From 3320b01cd8239915c01f75fe4ad65c21e2737c8d Mon Sep 17 00:00:00 2001 From: Sue Tairaku <42981334+stairaku@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:12:06 -0800 Subject: [PATCH 01/44] PSP-9565: Changes on automation test set to accomodate GHA (#4516) * data changes * Testing browsers * changing .net 6.0 to .net 8.0 * Deleting EdgeDriver nuget package * Changes on webdriver config * Update integration-test.yml * Changes on automation test set - IS94 * Changes to adapt GHA * Changing checklist button to explicit path * Correcting a research file element --- .../Data/PIMS_Testing_Data.xlsx | Bin 120532 -> 120517 bytes .../Features/SmokeTest.feature | 28 +++---- .../Features/SmokeTest.feature.cs | 79 +++++++++++++++++- .../PIMS.Tests.Automation.csproj | 2 +- .../PageObjects/AcquisitionChecklist.cs | 2 +- .../PageObjects/AcquisitionDetails.cs | 10 ++- .../PageObjects/AcquisitionStakeholders.cs | 1 + .../PageObjects/Contacts.cs | 2 +- .../PageObjects/DispositionFileDetails.cs | 2 +- .../PageObjects/LeaseDeposits.cs | 2 +- .../PageObjects/LeaseDetails.cs | 3 +- .../PageObjects/ResearchFiles.cs | 3 +- 12 files changed, 106 insertions(+), 28 deletions(-) diff --git a/testing/PIMS.Tests.Automation/Data/PIMS_Testing_Data.xlsx b/testing/PIMS.Tests.Automation/Data/PIMS_Testing_Data.xlsx index e52276c98bb2c8466a6c9204da91b828064e62ac..99f5922af45d13f2d20bf49bee887a83845f5825 100644 GIT binary patch delta 30719 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 UA%KBxg31>Fl+b~lAFW{jAN0~>N&o-= delta 30738 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 kyYN0p{yq%=U|^ZxU|?wf)%dQ?K@~ diff --git a/testing/PIMS.Tests.Automation/Features/SmokeTest.feature b/testing/PIMS.Tests.Automation/Features/SmokeTest.feature index 33f22066b5..c238203ebe 100644 --- a/testing/PIMS.Tests.Automation/Features/SmokeTest.feature +++ b/testing/PIMS.Tests.Automation/Features/SmokeTest.feature @@ -58,20 +58,20 @@ Scenario: 11. Acquisition File Agreements Tab When I create Agreements within an Acquisition File Then A new Acquisition file is created successfully -#Scenario: 12. Acquisition File Compensation Tab -# Given I create a new Acquisition File from row number 18 -# When I add additional information to the Acquisition File Details -# And I add Properties to the Acquisition File -# And I create Stakeholders within an Acquisition File -# And I create Compensation Requisition within an Acquisition File -# Then A new Acquisition file is created successfully - -#Scenario: 13. Disposition Checklist Tab -# Given I create a new Disposition File from row number 1 -# When I insert Checklist information to an Disposition File -# Then Disposition File's Checklist has been saved successfully - -Scenario: 12. Disposition Offers and Sale Tab +Scenario: 12. Acquisition File Compensation Tab + Given I create a new Acquisition File from row number 18 + When I add additional information to the Acquisition File Details + And I add Properties to the Acquisition File + And I create Stakeholders within an Acquisition File + And I create Compensation Requisition within an Acquisition File + Then A new Acquisition file is created successfully + +Scenario: 13. Disposition Checklist Tab + Given I create a new Disposition File from row number 1 + When I insert Checklist information to an Disposition File + Then Disposition File's Checklist has been saved successfully + +Scenario: 14. Disposition Offers and Sale Tab Given I create a new Disposition File from row number 1 When I create Appraisal, Assessment, Offers and Sales Details within a Disposition File Then A new Disposition file is created successfully diff --git a/testing/PIMS.Tests.Automation/Features/SmokeTest.feature.cs b/testing/PIMS.Tests.Automation/Features/SmokeTest.feature.cs index 1d7b148a53..58e8ad07e9 100644 --- a/testing/PIMS.Tests.Automation/Features/SmokeTest.feature.cs +++ b/testing/PIMS.Tests.Automation/Features/SmokeTest.feature.cs @@ -423,14 +423,85 @@ public void _11_AcquisitionFileAgreementsTab() this.ScenarioCleanup(); } - [Xunit.SkippableFactAttribute(DisplayName="12. Disposition Offers and Sale Tab")] + [Xunit.SkippableFactAttribute(DisplayName="12. Acquisition File Compensation Tab")] [Xunit.TraitAttribute("FeatureTitle", "SmokeTest")] - [Xunit.TraitAttribute("Description", "12. Disposition Offers and Sale Tab")] - public void _12_DispositionOffersAndSaleTab() + [Xunit.TraitAttribute("Description", "12. Acquisition File Compensation Tab")] + public void _12_AcquisitionFileCompensationTab() { string[] tagsOfScenario = ((string[])(null)); System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("12. Disposition Offers and Sale Tab", null, tagsOfScenario, argumentsOfScenario, featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("12. Acquisition File Compensation Tab", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 61 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 62 + testRunner.Given("I create a new Acquisition File from row number 18", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 63 + testRunner.When("I add additional information to the Acquisition File Details", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 64 + testRunner.And("I add Properties to the Acquisition File", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 65 + testRunner.And("I create Stakeholders within an Acquisition File", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 66 + testRunner.And("I create Compensation Requisition within an Acquisition File", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 67 + testRunner.Then("A new Acquisition file is created successfully", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + + [Xunit.SkippableFactAttribute(DisplayName="13. Disposition Checklist Tab")] + [Xunit.TraitAttribute("FeatureTitle", "SmokeTest")] + [Xunit.TraitAttribute("Description", "13. Disposition Checklist Tab")] + public void _13_DispositionChecklistTab() + { + string[] tagsOfScenario = ((string[])(null)); + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("13. Disposition Checklist Tab", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 69 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 70 + testRunner.Given("I create a new Disposition File from row number 1", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 71 + testRunner.When("I insert Checklist information to an Disposition File", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 72 + testRunner.Then("Disposition File\'s Checklist has been saved successfully", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + + [Xunit.SkippableFactAttribute(DisplayName="14. Disposition Offers and Sale Tab")] + [Xunit.TraitAttribute("FeatureTitle", "SmokeTest")] + [Xunit.TraitAttribute("Description", "14. Disposition Offers and Sale Tab")] + public void _14_DispositionOffersAndSaleTab() + { + string[] tagsOfScenario = ((string[])(null)); + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("14. Disposition Offers and Sale Tab", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 74 this.ScenarioInitialize(scenarioInfo); #line hidden diff --git a/testing/PIMS.Tests.Automation/PIMS.Tests.Automation.csproj b/testing/PIMS.Tests.Automation/PIMS.Tests.Automation.csproj index 519880bf28..188e903a38 100644 --- a/testing/PIMS.Tests.Automation/PIMS.Tests.Automation.csproj +++ b/testing/PIMS.Tests.Automation/PIMS.Tests.Automation.csproj @@ -1,7 +1,7 @@  - net8.0 + net6.0 enable enable 484766ab-9711-4242-858c-ae7c826b3074 diff --git a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionChecklist.cs b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionChecklist.cs index 37b9e5dfb7..8d7d797e90 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionChecklist.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionChecklist.cs @@ -7,7 +7,7 @@ public class AcquisitionChecklist : PageObjectBase { private By checklistLinkTab = By.XPath("//a[contains(text(),'Checklist')]"); - private By checklistEditBttn = By.CssSelector("button[title='Edit checklist']"); + private By checklistEditBttn = By.XPath("//body/div[@id='root']/div[2]/div[2]/div[1]/div[1]/div[1]/div[3]/div[2]/div[1]/div[2]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/button[@title='Edit checklist']"); private By checklistInfo = By.XPath("//div/em[contains(text(),'This checklist was last updated')]"); //Checklist View Elements diff --git a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionDetails.cs b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionDetails.cs index 18cafbdd1a..eef5753e25 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionDetails.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionDetails.cs @@ -7,7 +7,7 @@ namespace PIMS.Tests.Automation.PageObjects public class AcquisitionDetails : PageObjectBase { //Acquisition Files Menu Elements - private readonly By menuAcquisitionButton = By.CssSelector("div[data-testid='nav-tooltip-acquisition'] a"); + private readonly By menuAcquisitionButton = By.XPath("//body/div[@id='root']/div[2]/div[1]/div[1]/div[@data-testid='nav-tooltip-acquisition']/a"); private readonly By createAcquisitionFileButton = By.XPath("//a[contains(text(),'Create an Acquisition File')]"); private readonly By acquisitionFileSummaryBttn = By.CssSelector("div[data-testid='menu-item-row-0'] div button[title='File Details']"); @@ -42,6 +42,7 @@ public class AcquisitionDetails : PageObjectBase private By acquisitionFileProjectContent = By.XPath("//div[@class='collapse show']/div/div/label[contains(text(),'Ministry project')]/parent::div/following-sibling::div"); private By acquisitionFileProjectProductLabel = By.XPath("//label[contains(text(),'Product')]"); private By acquisitionFileProjectProductSelect = By.Id("input-product"); + private By acquicistionFileProjectProductOptions = By.CssSelector("select[id='input-product'] option"); private By acquisitionFileProjectProductContent = By.XPath("//label[contains(text(),'Product')]/parent::div/following-sibling::div"); private By acquisitionFileProjectFundingLabel = By.XPath("//label[contains(text(),'Funding')]"); private By acquisitionFileProjectFundingInput = By.Id("input-fundingTypeCode"); @@ -140,8 +141,9 @@ public void NavigateToCreateNewAcquisitionFile() public void NavigateToFileSummary() { - WaitUntilVisible(acquisitionFileSummaryBttn); - FocusAndClick(acquisitionFileSummaryBttn); + Wait(); + if (webDriver.FindElements(acquisitionFileSummaryBttn).Count() > 0) + FocusAndClick(acquisitionFileSummaryBttn); } public void NavigateToFileDetailsTab() @@ -193,6 +195,8 @@ public void UpdateAcquisitionFile(AcquisitionFile acquisition) { WaitUntilClickable(acquisitionFileProjectProductSelect); webDriver.FindElement(acquisitionFileProjectProductSelect).Click(); + + Wait(2000); ChooseSpecificSelectOption(acquisitionFileProjectProductSelect, acquisition.AcquisitionProjProductCode + " " + acquisition.AcquisitionProjProduct); } diff --git a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionStakeholders.cs b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionStakeholders.cs index 58193aa052..01715ac394 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/AcquisitionStakeholders.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/AcquisitionStakeholders.cs @@ -186,6 +186,7 @@ public void VerifyStakeholderInitEditForm() public void VerifyInterestStakeholderViewForm(AcquisitionStakeholder interest) { + Wait(); AssertTrueIsDisplayed(stakeholderInterestsSubtitle); AssertTrueIsDisplayed(stakeholderInterestsEditBttn); AssertTrueIsDisplayed(stakeholderInterestTable); diff --git a/testing/PIMS.Tests.Automation/PageObjects/Contacts.cs b/testing/PIMS.Tests.Automation/PageObjects/Contacts.cs index b0f5b5d20a..244e2f1b5f 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/Contacts.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/Contacts.cs @@ -6,7 +6,7 @@ namespace PIMS.Tests.Automation.PageObjects public class Contacts : PageObjectBase { //Contact Menu Elements - private By menuContactsLink = By.CssSelector("div[data-testid='nav-tooltip-contacts'] a"); + private By menuContactsLink = By.XPath("//body/div[@id='root']/div[2]/div[1]/div[1]/div[@data-testid='nav-tooltip-contacts']/a"); private By createContactLink = By.XPath("//a[contains(text(),'Add a Contact')]"); private By createContactButton = By.XPath("//div[contains(text(),'Add new contact')]/parent::button"); diff --git a/testing/PIMS.Tests.Automation/PageObjects/DispositionFileDetails.cs b/testing/PIMS.Tests.Automation/PageObjects/DispositionFileDetails.cs index 024aa717e7..7ce009a761 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/DispositionFileDetails.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/DispositionFileDetails.cs @@ -7,7 +7,7 @@ namespace PIMS.Tests.Automation.PageObjects public class DispositionFileDetails : PageObjectBase { //Disposition Files Menu Elements - private By menuDispositionButton = By.CssSelector("div[data-testid='nav-tooltip-disposition'] a"); + private By menuDispositionButton = By.XPath("//body/div[@id='root']/div[2]/div[1]/div[1]/div[@data-testid='nav-tooltip-disposition']/a"); private By createDispositionFileButton = By.XPath("//a[contains(text(),'Create a Disposition File')]"); private By dispositionFileSummaryBttn = By.XPath("//div[contains(text(),'File Summary')]"); diff --git a/testing/PIMS.Tests.Automation/PageObjects/LeaseDeposits.cs b/testing/PIMS.Tests.Automation/PageObjects/LeaseDeposits.cs index 220fefb6e1..344eb84087 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/LeaseDeposits.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/LeaseDeposits.cs @@ -168,7 +168,7 @@ public void AddNotes(string notes) public void DeleteFirstReturn() { - WaitUntilClickable(licenseDepositReturn1stRowDeleteBttn); + Wait(); FocusAndClick(licenseDepositReturn1stRowDeleteBttn); WaitUntilVisible(licenseDepositModal); diff --git a/testing/PIMS.Tests.Automation/PageObjects/LeaseDetails.cs b/testing/PIMS.Tests.Automation/PageObjects/LeaseDetails.cs index 0ca54f42b3..22925855ee 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/LeaseDetails.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/LeaseDetails.cs @@ -7,7 +7,7 @@ namespace PIMS.Tests.Automation.PageObjects public class LeaseDetails : PageObjectBase { //Main Menu links Elements - private readonly By menuManagementButton = By.CssSelector("div[data-testid='nav-tooltip-leases&licences'] a"); + private readonly By menuManagementButton = By.XPath("//body/div[@id='root']/div[2]/div[1]/div[1]/div[@data-testid='nav-tooltip-leases&licences']/a"); private readonly By createLicenseButton = By.XPath("//a[contains(text(),'Create a Lease/Licence File')]"); //File Details Edit Icon @@ -273,6 +273,7 @@ public void CreateMinimumLicenseDetails(Lease lease) { foreach (string purpose in lease.LeasePurpose) { + webDriver.FindElement(licenseDetailsPurposeLabel).Click(); FocusAndClick(licenseDetailsPurposeMultiselector); Wait(5000); diff --git a/testing/PIMS.Tests.Automation/PageObjects/ResearchFiles.cs b/testing/PIMS.Tests.Automation/PageObjects/ResearchFiles.cs index 425aa92afb..2f08d5e915 100644 --- a/testing/PIMS.Tests.Automation/PageObjects/ResearchFiles.cs +++ b/testing/PIMS.Tests.Automation/PageObjects/ResearchFiles.cs @@ -1,12 +1,13 @@ using OpenQA.Selenium; using PIMS.Tests.Automation.Classes; +using System.ComponentModel; namespace PIMS.Tests.Automation.PageObjects { public class ResearchFiles : PageObjectBase { //Research File Menu options - private readonly By menuResearchButton = By.CssSelector("div[data-testid='nav-tooltip-research'] a"); + private readonly By menuResearchButton = By.XPath("//body/div[@id='root']/div[2]/div[1]/div[1]/div[@data-testid='nav-tooltip-research']/a"); private readonly By createResearchFileButton = By.XPath("//a[contains(text(),'Create a Research File')]"); //File Details Tab Element From 623b443a57ea4ed77215bf288e562fc300ba9b49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 01:12:30 +0000 Subject: [PATCH 02/44] CI: Bump version to v5.7.0-94.21 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index bfc4c90bd1..a5dcdb267d 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-94.20 + 5.7.0-94.21 5.7.0.94 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 519b458d94..6bd75d0d67 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-94.20", + "version": "5.7.0-94.21", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 2ebb2e66eb625257a22e59820ce35cc4f00f70e0 Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:17:28 -0800 Subject: [PATCH 03/44] psp-9500 Scheduler microservice (#4483) * split off geoserver functionality into a microservice - update docker and ci/cd to support. * psp-9500 scheduler microservice. * change service scope * Readme updates. * github action corrections. --------- Co-authored-by: Smith --- .github/workflows/api-dotnetcore.yml | 2 +- .github/workflows/ci-cd-pims-dev.yml | 13 +- .github/workflows/deploy-prod-start.yml | 2 + .github/workflows/retag-dev-to-test.yml | 6 + .github/workflows/retag-test-to-uat-argo.yml | 6 + .github/workflows/retag-test-to-uat.yml | 3 + .github/workflows/uat_hotfix.yml | 4 + .github/workflows/uat_hotfix_argo.yml | 8 + .github/workflows/uat_pre_release_hotfix.yml | 5 +- .../workflows/uat_pre_release_hotfix_argo.yml | 11 +- docker-compose.yml | 40 ++ source/backend/Dockerfile.scheduler | 33 ++ .../Controllers/ChecklistController.cs | 4 +- .../Documents/DocumentQueueController.cs | 64 ++++ source/backend/{ => api}/README.md | 0 .../Repositories/Cdogs/CdogsBaseRepository.cs | 2 +- .../Repositories/Mayan/MayanBaseRepository.cs | 2 +- .../api/Services/DocumentFileService.cs | 3 +- .../api/Services/DocumentGenerationService.cs | 1 + .../api/Services/DocumentQueueService.cs | 42 +++ .../backend/api/Services/DocumentService.cs | 3 +- .../api/Services/DocumentSyncService.cs | 1 + .../api/Services/FinancialCodeService.cs | 6 +- .../api/Services/FormDocumentService.cs | 1 + .../api/Services/IDocumentQueueService.cs | 14 + source/backend/api/Services/LeaseService.cs | 6 +- source/backend/api/Services/NoteService.cs | 6 +- .../api/Services/OrganizationService.cs | 4 +- source/backend/api/Services/PersonService.cs | 4 +- source/backend/api/Services/ProjectService.cs | 4 +- source/backend/api/Startup.cs | 1 + .../CodeTypes/DocumentQueueStatusTypes.cs | 25 ++ .../DocumentQueue/DocumentQueueMap.cs | 36 ++ .../DocumentQueue/DocumentQueueModel.cs | 67 ++++ source/backend/core.api/Pims.Core.Api.csproj | 1 + .../RestCommon/BaseRestRepository.cs | 2 +- .../RestCommon/IRestRespository.cs | 2 +- .../{api => core.api}/Services/BaseService.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 1 + .../Repositories/DocumentQueueRepository.cs | 100 +++++ .../Interfaces/IDocumentQueueRepository.cs | 20 + source/backend/dal/Services/BaseService.cs | 52 --- .../Models/AccessRequestFilter.cs | 0 .../Models/AcquisitionFileExportModel.cs | 0 .../Models/AcquisitionFilter.cs | 0 .../Models/AcquisitionReportFilterModel.cs | 0 .../Models/AutocompletionRequestModel.cs | 0 .../{dal => entities}/Models/ContactFilter.cs | 0 .../Models/DispositionFileExportModel.cs | 0 .../Models/DispositionFilter.cs | 0 .../entities/Models/DocumentQueueFilter.cs | 42 +++ .../Models/EnvironmentModel.cs | 0 .../{dal => entities}/Models/LastUpdateBy.cs | 0 .../{dal => entities}/Models/LeaseFilter.cs | 0 .../Models/OrganizationFilter.cs | 0 .../{dal => entities}/Models/PageFilter.cs | 0 .../{dal => entities}/Models/Paged{TModel}.cs | 0 .../{dal => entities}/Models/ProjectFilter.cs | 0 .../Models/PropertyFilter.cs | 0 .../Models/PropertyFilterCriteria.cs | 0 .../Models/PropertyOwnershipState.cs | 0 .../Models/ResearchFilter.cs | 0 .../{dal => entities}/Models/UserFilter.cs | 0 .../entities/Partials/DocumentQueue.cs | 15 + source/backend/entrypoint.scheduler.sh | 4 + source/backend/proxy/README.md | 32 +- source/backend/scheduler/.dockerignore | 18 + source/backend/scheduler/.editorconfig | 257 +++++++++++++ source/backend/scheduler/.gitignore | 55 +++ .../scheduler/Configuration/PimsOptions.cs | 21 ++ .../scheduler/Configuration/ProgramOptions.cs | 51 +++ .../Controllers/PimsSchedulerController.cs | 64 ++++ .../backend/scheduler/Directory.Build.props | 40 ++ .../backend/scheduler/Pims.Scheduler.csproj | 63 ++++ .../HangfireDashboardAuthorizationFilter.cs | 40 ++ source/backend/scheduler/Program.cs | 84 +++++ .../scheduler/Properties/launchSettings.json | 27 ++ source/backend/scheduler/README.md | 33 ++ .../Interfaces/IPimsDocumentRepository.cs | 16 + .../Repositories/PimsBaseRepository.cs | 52 +++ .../scheduler/Repositories/PimsRepository.cs | 55 +++ source/backend/scheduler/Scheduler.sln | 132 +++++++ .../scheduler/Scheduler/IJobRescheduler.cs | 11 + .../scheduler/Scheduler/JobRescheduler.cs | 59 +++ .../scheduler/Scheduler/JobScheduleOption.cs | 28 ++ .../scheduler/Scheduler/JobScheduleOptions.cs | 12 + .../Services/DocumentQueueService.cs | 29 ++ .../Interfaces/IDocumentQueueService.cs | 9 + source/backend/scheduler/Startup.cs | 350 ++++++++++++++++++ .../scheduler/appsettings.Development.json | 38 ++ .../backend/scheduler/appsettings.Docker.json | 51 +++ .../backend/scheduler/appsettings.Local.json | 51 +++ .../backend/scheduler/appsettings.Test.json | 29 ++ source/backend/scheduler/appsettings.Uat.json | 29 ++ source/backend/scheduler/appsettings.json | 132 +++++++ source/backend/scheduler/omnisharp.json | 5 + source/backend/scheduler/tests/.editorconfig | 72 ++++ source/backend/scheduler/tests/.gitignore | 49 +++ .../scheduler/tests/Directory.Build.props | 9 + tools/cicd/scripts/gen-env-files.sh | 28 ++ 100 files changed, 2587 insertions(+), 114 deletions(-) create mode 100644 source/backend/Dockerfile.scheduler create mode 100644 source/backend/api/Areas/Documents/DocumentQueueController.cs rename source/backend/{ => api}/README.md (100%) create mode 100644 source/backend/api/Services/DocumentQueueService.cs create mode 100644 source/backend/api/Services/IDocumentQueueService.cs create mode 100644 source/backend/apimodels/CodeTypes/DocumentQueueStatusTypes.cs create mode 100644 source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueMap.cs create mode 100644 source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueModel.cs rename source/backend/{api => core.api}/Repositories/RestCommon/BaseRestRepository.cs (99%) rename source/backend/{api => core.api}/Repositories/RestCommon/IRestRespository.cs (95%) rename source/backend/{api => core.api}/Services/BaseService.cs (97%) create mode 100644 source/backend/dal/Repositories/DocumentQueueRepository.cs create mode 100644 source/backend/dal/Repositories/Interfaces/IDocumentQueueRepository.cs delete mode 100644 source/backend/dal/Services/BaseService.cs rename source/backend/{dal => entities}/Models/AccessRequestFilter.cs (100%) rename source/backend/{dal => entities}/Models/AcquisitionFileExportModel.cs (100%) rename source/backend/{dal => entities}/Models/AcquisitionFilter.cs (100%) rename source/backend/{dal => entities}/Models/AcquisitionReportFilterModel.cs (100%) rename source/backend/{dal => entities}/Models/AutocompletionRequestModel.cs (100%) rename source/backend/{dal => entities}/Models/ContactFilter.cs (100%) rename source/backend/{dal => entities}/Models/DispositionFileExportModel.cs (100%) rename source/backend/{dal => entities}/Models/DispositionFilter.cs (100%) create mode 100644 source/backend/entities/Models/DocumentQueueFilter.cs rename source/backend/{dal => entities}/Models/EnvironmentModel.cs (100%) rename source/backend/{dal => entities}/Models/LastUpdateBy.cs (100%) rename source/backend/{dal => entities}/Models/LeaseFilter.cs (100%) rename source/backend/{dal => entities}/Models/OrganizationFilter.cs (100%) rename source/backend/{dal => entities}/Models/PageFilter.cs (100%) rename source/backend/{dal => entities}/Models/Paged{TModel}.cs (100%) rename source/backend/{dal => entities}/Models/ProjectFilter.cs (100%) rename source/backend/{dal => entities}/Models/PropertyFilter.cs (100%) rename source/backend/{dal => entities}/Models/PropertyFilterCriteria.cs (100%) rename source/backend/{dal => entities}/Models/PropertyOwnershipState.cs (100%) rename source/backend/{dal => entities}/Models/ResearchFilter.cs (100%) rename source/backend/{dal => entities}/Models/UserFilter.cs (100%) create mode 100644 source/backend/entities/Partials/DocumentQueue.cs create mode 100644 source/backend/entrypoint.scheduler.sh create mode 100644 source/backend/scheduler/.dockerignore create mode 100644 source/backend/scheduler/.editorconfig create mode 100644 source/backend/scheduler/.gitignore create mode 100644 source/backend/scheduler/Configuration/PimsOptions.cs create mode 100644 source/backend/scheduler/Configuration/ProgramOptions.cs create mode 100644 source/backend/scheduler/Controllers/PimsSchedulerController.cs create mode 100644 source/backend/scheduler/Directory.Build.props create mode 100644 source/backend/scheduler/Pims.Scheduler.csproj create mode 100644 source/backend/scheduler/Policies/HangfireDashboardAuthorizationFilter.cs create mode 100644 source/backend/scheduler/Program.cs create mode 100644 source/backend/scheduler/Properties/launchSettings.json create mode 100644 source/backend/scheduler/README.md create mode 100644 source/backend/scheduler/Repositories/Interfaces/IPimsDocumentRepository.cs create mode 100644 source/backend/scheduler/Repositories/PimsBaseRepository.cs create mode 100644 source/backend/scheduler/Repositories/PimsRepository.cs create mode 100644 source/backend/scheduler/Scheduler.sln create mode 100644 source/backend/scheduler/Scheduler/IJobRescheduler.cs create mode 100644 source/backend/scheduler/Scheduler/JobRescheduler.cs create mode 100644 source/backend/scheduler/Scheduler/JobScheduleOption.cs create mode 100644 source/backend/scheduler/Scheduler/JobScheduleOptions.cs create mode 100644 source/backend/scheduler/Services/DocumentQueueService.cs create mode 100644 source/backend/scheduler/Services/Interfaces/IDocumentQueueService.cs create mode 100644 source/backend/scheduler/Startup.cs create mode 100644 source/backend/scheduler/appsettings.Development.json create mode 100644 source/backend/scheduler/appsettings.Docker.json create mode 100644 source/backend/scheduler/appsettings.Local.json create mode 100644 source/backend/scheduler/appsettings.Test.json create mode 100644 source/backend/scheduler/appsettings.Uat.json create mode 100644 source/backend/scheduler/appsettings.json create mode 100644 source/backend/scheduler/omnisharp.json create mode 100644 source/backend/scheduler/tests/.editorconfig create mode 100644 source/backend/scheduler/tests/.gitignore create mode 100644 source/backend/scheduler/tests/Directory.Build.props diff --git a/.github/workflows/api-dotnetcore.yml b/.github/workflows/api-dotnetcore.yml index 0249fc5f72..7344a24221 100644 --- a/.github/workflows/api-dotnetcore.yml +++ b/.github/workflows/api-dotnetcore.yml @@ -28,7 +28,7 @@ jobs: if: ${{ needs.check-changes.outputs.backend == 'true' }} strategy: matrix: - services: [{directory: ./source/backend/api, solution: 'Pims.sln'}, {directory: ./source/backend/proxy, solution: 'Proxy.sln'}] + services: [{directory: ./source/backend/api, solution: 'Pims.sln'}, {directory: ./source/backend/proxy, solution: 'Proxy.sln'}, {directory: ./source/backend/scheduler, solution: 'Scheduler.sln'}] env: working-directory: ${{ matrix.services.directory }} solution-name: ${{ matrix.services.solution }} diff --git a/.github/workflows/ci-cd-pims-dev.yml b/.github/workflows/ci-cd-pims-dev.yml index 9a343c2439..871a435175 100644 --- a/.github/workflows/ci-cd-pims-dev.yml +++ b/.github/workflows/ci-cd-pims-dev.yml @@ -27,13 +27,10 @@ env: DEPLOYMENT_NAMESPACE: "3cd915-dev" on: - pull_request_target: - branches: [dev] - types: [closed] + workflow_dispatch: jobs: ci-cd-start-notification: - if: github.event.pull_request.merged == true name: CI-CD Start Notification to Teams Channel runs-on: ubuntu-latest steps: @@ -83,6 +80,7 @@ jobs: run: | ./openshift/4.0/player.sh build api -apply ./openshift/4.0/player.sh build proxy -apply + ./openshift/4.0/player.sh build scheduler -apply deploy: name: Deploy to OpenShift @@ -116,6 +114,12 @@ jobs: oc tag pims-proxy:latest-$DESTINATION pims-proxy:$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION + - name: Deploy scheduler microservice + shell: bash + run: | + oc tag pims-scheduler:latest-$DESTINATION pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION - name: Deploy mayan shell: bash run: | @@ -193,7 +197,6 @@ jobs: name: CI-CD End Notification to Teams Channel runs-on: ubuntu-latest needs: keycloak-sync - if: ${{ always() && github.event.pull_request.merged == true }} steps: - name: check workflow status uses: martialonline/workflow-status@v4 diff --git a/.github/workflows/deploy-prod-start.yml b/.github/workflows/deploy-prod-start.yml index e72d6c11a7..80865d36c2 100644 --- a/.github/workflows/deploy-prod-start.yml +++ b/.github/workflows/deploy-prod-start.yml @@ -72,6 +72,8 @@ jobs: run: | [[ -z ${{github.event.inputs.OVERRIDE_VERSION}} ]] && RELEASE_VERSION=${{steps.previoustag.outputs.tag}}-master || RELEASE_VERSION=${{github.event.inputs.OVERRIDE_VERSION}}-master RELEASE_TAG=$RELEASE_VERSION ./openshift/4.0/player.sh deploy api $DESTINATION -apply + RELEASE_TAG=$RELEASE_VERSION ./openshift/4.0/player.sh deploy proxy $DESTINATION -apply + RELEASE_TAG=$RELEASE_VERSION ./openshift/4.0/player.sh deploy scheduler $DESTINATION -apply RELEASE_TAG=$RELEASE_VERSION ./openshift/4.0/player.sh deploy app $DESTINATION -apply oc tag mayan-bcgov:$RELEASE_VERSION mayan-bcgov:master diff --git a/.github/workflows/retag-dev-to-test.yml b/.github/workflows/retag-dev-to-test.yml index 89d979e77e..fcf314150e 100644 --- a/.github/workflows/retag-dev-to-test.yml +++ b/.github/workflows/retag-dev-to-test.yml @@ -77,6 +77,12 @@ jobs: oc tag pims-proxy:$RELEASE_TAG pims-proxy:$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION + - name: Deploy scheduler microservice + shell: bash + run: | + oc tag pims-scheduler:$RELEASE_TAG pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION - name: Deploy mayan shell: bash run: | diff --git a/.github/workflows/retag-test-to-uat-argo.yml b/.github/workflows/retag-test-to-uat-argo.yml index 963b21dbf3..24a224a8a2 100644 --- a/.github/workflows/retag-test-to-uat-argo.yml +++ b/.github/workflows/retag-test-to-uat-argo.yml @@ -78,6 +78,12 @@ jobs: oc tag pims-proxy:$RELEASE_TAG pims-proxy:$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION + - name: Deploy scheduler microservice + shell: bash + run: | + oc tag pims-scheduler:$RELEASE_TAG pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION - name: Deploy mayan shell: bash run: | diff --git a/.github/workflows/retag-test-to-uat.yml b/.github/workflows/retag-test-to-uat.yml index 065219d3e7..902161f187 100644 --- a/.github/workflows/retag-test-to-uat.yml +++ b/.github/workflows/retag-test-to-uat.yml @@ -63,6 +63,8 @@ jobs: - name: call scripts to deploy api and frontend run: | ./openshift/4.0/player.sh deploy api $DESTINATION -apply + ./openshift/4.0/player.sh deploy proxy $DESTINATION -apply + ./openshift/4.0/player.sh deploy scheduler $DESTINATION -apply ./openshift/4.0/player.sh deploy app $DESTINATION -apply oc tag mayan-bcgov:test mayan-bcgov:$DESTINATION # the proxy can only be deployed via DEPLOYMENTS (ArgoCD way) @@ -162,6 +164,7 @@ jobs: oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master oc tag pims-proxy:uat pims-proxy:v${VERSION}-master + oc tag pims-scheduler:uat pims-scheduler:v${VERSION}-master oc tag mayan-bcgov:uat mayan-bcgov:v${VERSION}-master ci-cd-end-notification: diff --git a/.github/workflows/uat_hotfix.yml b/.github/workflows/uat_hotfix.yml index 366df8bd37..08fc02883b 100644 --- a/.github/workflows/uat_hotfix.yml +++ b/.github/workflows/uat_hotfix.yml @@ -81,6 +81,7 @@ jobs: run: | ./openshift/4.0/player.sh build api -apply ./openshift/4.0/player.sh build proxy -apply + ./openshift/4.0/player.sh build scheduler -apply deploy: name: Deploy frontend and api to OpenShift @@ -100,6 +101,7 @@ jobs: run: | ./openshift/4.0/player.sh deploy api $DESTINATION -apply ./openshift/4.0/player.sh deploy proxy $DESTINATION -apply + ./openshift/4.0/player.sh deploy scheduler $DESTINATION -apply ./openshift/4.0/player.sh deploy app $DESTINATION -apply # the command: @@ -188,6 +190,8 @@ jobs: VERSION=$(make version) oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master + oc tag pims-api:uat pims-proxy:v${VERSION}-master + oc tag pims-api:uat pims-scheduler:v${VERSION}-master ci-cd-end-notification: if: always() diff --git a/.github/workflows/uat_hotfix_argo.yml b/.github/workflows/uat_hotfix_argo.yml index 6b656b22b9..ff3e0826da 100644 --- a/.github/workflows/uat_hotfix_argo.yml +++ b/.github/workflows/uat_hotfix_argo.yml @@ -82,6 +82,7 @@ jobs: run: | ./openshift/4.0/player.sh build api -apply ./openshift/4.0/player.sh build proxy -apply + ./openshift/4.0/player.sh build scheduler -apply deploy: name: Deploy to OpenShift @@ -115,6 +116,12 @@ jobs: oc tag pims-proxy:latest-$DESTINATION pims-proxy:$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION + - name: Deploy scheduler microservice + shell: bash + run: | + oc tag pims-scheduler:latest-$DESTINATION pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION # the command: # 1) creates an openshift job with generated name to avoid name conflict, substituting the variables in the template. @@ -203,6 +210,7 @@ jobs: oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master oc tag pims-proxy:uat pims-proxy:v${VERSION}-master + oc tag pims-scheduler:uat pims-scheduler:v${VERSION}-master oc tag mayan-bcgov:uat mayan-bcgov:v${VERSION}-master ci-cd-end-notification: diff --git a/.github/workflows/uat_pre_release_hotfix.yml b/.github/workflows/uat_pre_release_hotfix.yml index 2713184f32..c1de9fedfa 100644 --- a/.github/workflows/uat_pre_release_hotfix.yml +++ b/.github/workflows/uat_pre_release_hotfix.yml @@ -107,6 +107,7 @@ jobs: run: | OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build api -apply OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build proxy -apply + OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build scheduler -apply deploy: name: Deploy frontend and api to OpenShift @@ -126,6 +127,7 @@ jobs: run: | RELEASE_TAG=latest-${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh deploy api $DESTINATION -apply RELEASE_TAG=latest-${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh deploy proxy $DESTINATION -apply + RELEASE_TAG=latest-${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh deploy scheduler $DESTINATION -apply RELEASE_TAG=latest-${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh deploy app $DESTINATION -apply # the command: @@ -214,7 +216,8 @@ jobs: VERSION=$(make version) oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master - oc tag pims-proxy:uat pims-api:v${VERSION}-master + oc tag pims-proxy:uat pims-proxy:v${VERSION}-master + oc tag pims-scheduler:uat pims-scheduler:v${VERSION}-master ci-cd-end-notification: if: always() diff --git a/.github/workflows/uat_pre_release_hotfix_argo.yml b/.github/workflows/uat_pre_release_hotfix_argo.yml index 6fb30a0591..ead0665cee 100644 --- a/.github/workflows/uat_pre_release_hotfix_argo.yml +++ b/.github/workflows/uat_pre_release_hotfix_argo.yml @@ -108,6 +108,7 @@ jobs: run: | OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build api -apply OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build proxy -apply + OC_JOB_NAME=${{github.event.inputs.HOTFIX_BRANCH}} && ./openshift/4.0/player.sh build scheduler -apply deploy: name: Deploy to OpenShift @@ -144,6 +145,13 @@ jobs: oc tag pims-proxy:$RELEASE_TAG pims-proxy:$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION + - name: Deploy scheduler microservice + shell: bash + run: | + export RELEASE_TAG=latest-${{github.event.inputs.HOTFIX_BRANCH}} + oc tag pims-scheduler:$RELEASE_TAG pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION # the command: # 1) creates an openshift job with generated name to avoid name conflict, substituting the variables in the template. @@ -231,7 +239,8 @@ jobs: VERSION=$(make version) oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master - oc tag pims-proxy:uat pims-api:v${VERSION}-master + oc tag pims-proxy:uat pims-proxy:v${VERSION}-master + oc tag pims-scheduler:uat pims-scheduler:v${VERSION}-master ci-cd-end-notification: if: always() diff --git a/docker-compose.yml b/docker-compose.yml index da85dba690..58ce6b72e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,6 +92,44 @@ services: networks: - psp + ####################### Scheduler ####################### + scheduler: + profiles: + - all + - psp + restart: on-failure:1 + container_name: psp-scheduler + build: + context: source/backend + dockerfile: Dockerfile.scheduler + args: + BUILD_CONFIGURATION: Debug + env_file: source/backend/scheduler/.env + environment: + - ASPNETCORE_ENVIRONMENT=Docker + ports: + - ${SCHEDULER_HTTPS_PORT:-8058}:443 + - ${SCHEDULER_HTTP_PORT:-8057}:8080 + depends_on: + - scheduler-redis + networks: + - psp + + ####################### Redis ####################### + scheduler-redis: + image: redis:6.2-alpine + profiles: + - all + - psp + restart: on-failure:1 + ports: + - 6379:6379 + networks: + - psp + command: redis-server --save 20 1 --loglevel warning + volumes: + - redis:/data + ####################### Frontend ####################### frontend: profiles: @@ -199,3 +237,5 @@ volumes: name: psp-api-db-rhel-data frontend-node-cache: name: psp-frontend-node-cache + redis-scheduler: + driver: local diff --git a/source/backend/Dockerfile.scheduler b/source/backend/Dockerfile.scheduler new file mode 100644 index 0000000000..847c0fa060 --- /dev/null +++ b/source/backend/Dockerfile.scheduler @@ -0,0 +1,33 @@ +ARG BUILD_CONFIGURATION=Release +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5001 5000 + +# Copy csproj and restore as distinct layers +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY scheduler scheduler/ +COPY scheduler/Directory.Build.props ./ +COPY core core/ +COPY apimodels apimodels/ +COPY entities entities/ +COPY core.api core.api/ +COPY keycloak keycloak/ +COPY scheduler/*.csproj scheduler/ + +RUN dotnet restore scheduler/Scheduler.sln +ENV PATH="$PATH:/root/.dotnet/tools" +# Copy everything else and build +WORKDIR /src/scheduler +RUN dotnet build "Pims.Scheduler.csproj" -c "$BUILD_CONFIGURATION" -o /app/build + +FROM build AS publish +RUN dotnet publish "Pims.Scheduler.csproj" -c "$BUILD_CONFIGURATION" -o /app/publish + +# Runtime image +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +COPY entrypoint.scheduler.sh . +RUN chmod +x /app/entrypoint.scheduler.sh +ENTRYPOINT ["/app/entrypoint.scheduler.sh"] diff --git a/source/backend/api/Areas/Disposition/Controllers/ChecklistController.cs b/source/backend/api/Areas/Disposition/Controllers/ChecklistController.cs index af993497d6..c15cbcb0dd 100644 --- a/source/backend/api/Areas/Disposition/Controllers/ChecklistController.cs +++ b/source/backend/api/Areas/Disposition/Controllers/ChecklistController.cs @@ -2,11 +2,11 @@ using MapsterMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.Concepts.DispositionFile; using Pims.Api.Models.Concepts.File; -using Pims.Core.Api.Policies; using Pims.Api.Services; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Policies; using Pims.Core.Json; using Pims.Core.Security; using Swashbuckle.AspNetCore.Annotations; diff --git a/source/backend/api/Areas/Documents/DocumentQueueController.cs b/source/backend/api/Areas/Documents/DocumentQueueController.cs new file mode 100644 index 0000000000..9fead6a65e --- /dev/null +++ b/source/backend/api/Areas/Documents/DocumentQueueController.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using MapsterMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Pims.Api.Services; +using Pims.Core.Api.Policies; +using Pims.Core.Json; +using Pims.Core.Security; +using Pims.Dal.Entities.Models; +using Swashbuckle.AspNetCore.Annotations; + +namespace Pims.Api.Controllers +{ + ///

+ /// DocumentQueueController class, provides endpoints to handle document queue requests. + /// + [Authorize] + [ApiController] + [ApiVersion("1.0")] + [Route("v{version:apiVersion}/documents/queue")] + [Route("/documents")] + public class DocumentQueueController : ControllerBase + { + #region Variables + private readonly IDocumentQueueService _documentQueueService; + private readonly IMapper _mapper; + #endregion + + #region Constructors + + /// + /// Creates a new instance of a DocumentQueueController class. + /// + /// + /// + public DocumentQueueController(IDocumentQueueService documentQueueService, IMapper mapper) + { + _documentQueueService = documentQueueService; + _mapper = mapper; + } + #endregion + + #region Endpoints + + /// + /// Search for Document Queue items via filter. + /// + /// + [HttpGet("search")] + [HasPermission(Permissions.SystemAdmin)] + [Produces("application/json")] + [ProducesResponseType(typeof(List), 200)] + [SwaggerOperation(Tags = new[] { "document-types" })] + [TypeFilter(typeof(NullJsonResultFilter))] + public IActionResult GetDocumentTypes([FromBody] DocumentQueueFilter filter) + { + var queuedDocuments = _documentQueueService.SearchDocumentQueue(filter); + var documentQueueModels = _mapper.Map>(queuedDocuments); + return new JsonResult(documentQueueModels); + } + + #endregion + } +} diff --git a/source/backend/README.md b/source/backend/api/README.md similarity index 100% rename from source/backend/README.md rename to source/backend/api/README.md diff --git a/source/backend/api/Repositories/Cdogs/CdogsBaseRepository.cs b/source/backend/api/Repositories/Cdogs/CdogsBaseRepository.cs index f8df726c8e..d1d192b1d5 100644 --- a/source/backend/api/Repositories/Cdogs/CdogsBaseRepository.cs +++ b/source/backend/api/Repositories/Cdogs/CdogsBaseRepository.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Pims.Api.Models.Config; -using Pims.Api.Repositories.Rest; +using Pims.Core.Api.Repositories.Rest; namespace Pims.Api.Repositories.Cdogs { diff --git a/source/backend/api/Repositories/Mayan/MayanBaseRepository.cs b/source/backend/api/Repositories/Mayan/MayanBaseRepository.cs index 4b7dfca7c9..ab06e068fc 100644 --- a/source/backend/api/Repositories/Mayan/MayanBaseRepository.cs +++ b/source/backend/api/Repositories/Mayan/MayanBaseRepository.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Pims.Api.Models.Config; -using Pims.Api.Repositories.Rest; +using Pims.Core.Api.Repositories.Rest; namespace Pims.Api.Repositories.Mayan { diff --git a/source/backend/api/Services/DocumentFileService.cs b/source/backend/api/Services/DocumentFileService.cs index f87910eb59..098e3e794c 100644 --- a/source/backend/api/Services/DocumentFileService.cs +++ b/source/backend/api/Services/DocumentFileService.cs @@ -5,11 +5,12 @@ using MapsterMapper; using Microsoft.Extensions.Logging; using Pims.Api.Constants; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.CodeTypes; using Pims.Api.Models.Concepts.Document; using Pims.Api.Models.Requests.Document.Upload; using Pims.Api.Models.Requests.Http; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Services; using Pims.Core.Extensions; using Pims.Core.Security; using Pims.Dal.Entities; diff --git a/source/backend/api/Services/DocumentGenerationService.cs b/source/backend/api/Services/DocumentGenerationService.cs index 2f9aef6ebb..a22e49b1ed 100644 --- a/source/backend/api/Services/DocumentGenerationService.cs +++ b/source/backend/api/Services/DocumentGenerationService.cs @@ -14,6 +14,7 @@ using Pims.Api.Models.Requests.Http; using Pims.Api.Repositories.Cdogs; using Pims.Av; +using Pims.Core.Api.Services; using Pims.Core.Extensions; using Pims.Core.Http.Configuration; using Pims.Core.Security; diff --git a/source/backend/api/Services/DocumentQueueService.cs b/source/backend/api/Services/DocumentQueueService.cs new file mode 100644 index 0000000000..4ffb2c4363 --- /dev/null +++ b/source/backend/api/Services/DocumentQueueService.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Pims.Core.Api.Services; +using Pims.Core.Extensions; +using Pims.Core.Http.Configuration; +using Pims.Core.Security; +using Pims.Dal.Entities; +using Pims.Dal.Entities.Models; +using Pims.Dal.Repositories; + +namespace Pims.Api.Services +{ + /// + /// DocumentQueueService implementation provides document queue managing capabilities. + /// + public class DocumentQueueService : BaseService, IDocumentQueueService + { + private readonly IDocumentQueueRepository documentQueueRepository; + private readonly IOptionsMonitor keycloakOptions; + + public DocumentQueueService( + ClaimsPrincipal user, + ILogger logger, + IDocumentQueueRepository documentQueueRepository, + IOptionsMonitor options) + : base(user, logger) + { + this.documentQueueRepository = documentQueueRepository; + this.keycloakOptions = options; + } + + public IEnumerable SearchDocumentQueue(DocumentQueueFilter filter) + { + this.Logger.LogInformation("Retrieving queued PIMS documents using filter {filter}", filter); + this.User.ThrowIfNotAuthorizedOrServiceAccount(Permissions.SystemAdmin, this.keycloakOptions); + + return documentQueueRepository.GetAllByFilter(filter); + } + } +} diff --git a/source/backend/api/Services/DocumentService.cs b/source/backend/api/Services/DocumentService.cs index 72cf9b7008..333fdb1c2b 100644 --- a/source/backend/api/Services/DocumentService.cs +++ b/source/backend/api/Services/DocumentService.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Pims.Core.Api.Exceptions; using Pims.Api.Models; using Pims.Api.Models.CodeTypes; using Pims.Api.Models.Concepts.Document; @@ -23,6 +22,8 @@ using Pims.Api.Models.Requests.Http; using Pims.Api.Repositories.Mayan; using Pims.Av; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Services; using Pims.Core.Exceptions; using Pims.Core.Extensions; using Pims.Core.Http.Configuration; diff --git a/source/backend/api/Services/DocumentSyncService.cs b/source/backend/api/Services/DocumentSyncService.cs index ae7030ef0a..fca48277a9 100644 --- a/source/backend/api/Services/DocumentSyncService.cs +++ b/source/backend/api/Services/DocumentSyncService.cs @@ -12,6 +12,7 @@ using Pims.Api.Models.PimsSync; using Pims.Api.Models.Requests.Http; using Pims.Api.Repositories.Mayan; +using Pims.Core.Api.Services; using Pims.Core.Extensions; using Pims.Core.Http.Configuration; using Pims.Core.Security; diff --git a/source/backend/api/Services/FinancialCodeService.cs b/source/backend/api/Services/FinancialCodeService.cs index 01ca8f84b0..d8ebe83ff0 100644 --- a/source/backend/api/Services/FinancialCodeService.cs +++ b/source/backend/api/Services/FinancialCodeService.cs @@ -3,13 +3,13 @@ using MapsterMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.Concepts.FinancialCode; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Services; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { diff --git a/source/backend/api/Services/FormDocumentService.cs b/source/backend/api/Services/FormDocumentService.cs index dc56329c7e..00cab505bd 100644 --- a/source/backend/api/Services/FormDocumentService.cs +++ b/source/backend/api/Services/FormDocumentService.cs @@ -8,6 +8,7 @@ using Pims.Api.Models.Concepts.Document; using Pims.Api.Models.Requests.Document.Upload; using Pims.Api.Models.Requests.Http; +using Pims.Core.Api.Services; using Pims.Core.Extensions; using Pims.Core.Security; using Pims.Dal.Entities; diff --git a/source/backend/api/Services/IDocumentQueueService.cs b/source/backend/api/Services/IDocumentQueueService.cs new file mode 100644 index 0000000000..b1f3c09206 --- /dev/null +++ b/source/backend/api/Services/IDocumentQueueService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Pims.Dal.Entities; +using Pims.Dal.Entities.Models; + +namespace Pims.Api.Services +{ + /// + /// IDocumentService interface, defines the functionality for document queue services. + /// + public interface IDocumentQueueService + { + public IEnumerable SearchDocumentQueue(DocumentQueueFilter filter); + } +} diff --git a/source/backend/api/Services/LeaseService.cs b/source/backend/api/Services/LeaseService.cs index 2a4e7a3fce..bf53885351 100644 --- a/source/backend/api/Services/LeaseService.cs +++ b/source/backend/api/Services/LeaseService.cs @@ -5,17 +5,17 @@ using System.Security.Claims; using System.Text; using Microsoft.Extensions.Logging; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.CodeTypes; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Services; using Pims.Core.Exceptions; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; using Pims.Dal.Entities.Extensions; using Pims.Dal.Entities.Models; using Pims.Dal.Exceptions; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; using static Pims.Dal.Entities.PimsLeaseStatusType; namespace Pims.Api.Services diff --git a/source/backend/api/Services/NoteService.cs b/source/backend/api/Services/NoteService.cs index 2c5554c606..2e27221339 100644 --- a/source/backend/api/Services/NoteService.cs +++ b/source/backend/api/Services/NoteService.cs @@ -5,13 +5,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Pims.Api.Constants; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.Concepts.Note; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Services; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { diff --git a/source/backend/api/Services/OrganizationService.cs b/source/backend/api/Services/OrganizationService.cs index a3c9b23a52..3613e4da69 100644 --- a/source/backend/api/Services/OrganizationService.cs +++ b/source/backend/api/Services/OrganizationService.cs @@ -1,11 +1,11 @@ using System.Security.Claims; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Pims.Core.Api.Services; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { diff --git a/source/backend/api/Services/PersonService.cs b/source/backend/api/Services/PersonService.cs index 52eee6b61b..3314e8b09e 100644 --- a/source/backend/api/Services/PersonService.cs +++ b/source/backend/api/Services/PersonService.cs @@ -1,11 +1,11 @@ using System.Security.Claims; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Pims.Core.Api.Services; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { diff --git a/source/backend/api/Services/ProjectService.cs b/source/backend/api/Services/ProjectService.cs index 96022d2212..4519051c1d 100644 --- a/source/backend/api/Services/ProjectService.cs +++ b/source/backend/api/Services/ProjectService.cs @@ -4,14 +4,14 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Pims.Core.Api.Services; using Pims.Core.Exceptions; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; using Pims.Dal.Entities.Models; using Pims.Dal.Exceptions; -using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { diff --git a/source/backend/api/Startup.cs b/source/backend/api/Startup.cs index b093c5fb9d..ba3ab2fb7f 100644 --- a/source/backend/api/Startup.cs +++ b/source/backend/api/Startup.cs @@ -522,6 +522,7 @@ private static void AddPimsApiServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); } diff --git a/source/backend/apimodels/CodeTypes/DocumentQueueStatusTypes.cs b/source/backend/apimodels/CodeTypes/DocumentQueueStatusTypes.cs new file mode 100644 index 0000000000..3b38b2899d --- /dev/null +++ b/source/backend/apimodels/CodeTypes/DocumentQueueStatusTypes.cs @@ -0,0 +1,25 @@ +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace Pims.Api.Models.CodeTypes +{ + [JsonConverter(typeof(JsonStringEnumMemberConverter))] + public enum DocumentQueueStatusTypes + { + + [EnumMember(Value = "MAYAN_ERROR")] + MAYAN_ERROR, + + [EnumMember(Value = "PENDING")] + PENDING, + + [EnumMember(Value = "PIMS_ERROR")] + PIMS_ERROR, + + [EnumMember(Value = "PROCESSING")] + PROCESSING, + + [EnumMember(Value = "SUCCESS")] + SUCCESS, + } +} diff --git a/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueMap.cs b/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueMap.cs new file mode 100644 index 0000000000..1a98ba0d07 --- /dev/null +++ b/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueMap.cs @@ -0,0 +1,36 @@ +using Mapster; +using Pims.Api.Models.Base; +using Entity = Pims.Dal.Entities; + +namespace Pims.Api.Models.Concepts.Document +{ + public class DocumentQueueMap : IRegister + { + public void Register(TypeAdapterConfig config) + { + config.NewConfig() + .Map(dest => dest.Id, src => src.DocumentQueueId) + .Map(dest => dest.DocumentExternalId, src => src.DocumentExternalId) + .Map(dest => dest.DocumentId, src => src.DocumentId) + .Map(dest => dest.DocumentQueueStatusType, src => src.DocumentQueueStatusTypeCodeNavigation) + .Map(dest => dest.DataSourceTypeCode, src => src.DataSourceTypeCodeNavigation) + .Map(dest => dest.DocumentProcessStartTimestamp, src => src.DocProcessStartDt) + .Map(dest => dest.DocumentProcessEndTimestamp, src => src.DocProcessEndDt) + .Map(dest => dest.DocumentProcessRetries, src => src.DocProcessRetries) + .Map(dest => dest.Document, src => src.Document) + .Inherits(); + + config.NewConfig() + .Map(dest => dest.DocumentQueueId, src => src.Id) + .Map(dest => dest.DocumentExternalId, src => src.DocumentExternalId) + .Map(dest => dest.DocumentId, src => src.DocumentId) + .Map(dest => dest.DocumentQueueStatusTypeCode, src => src.DocumentQueueStatusType.Id) + .Map(dest => dest.DataSourceTypeCode, src => src.DataSourceTypeCode.Id) + .Map(dest => dest.DocProcessStartDt, src => src.DocumentProcessStartTimestamp) + .Map(dest => dest.DocProcessEndDt, src => src.DocumentProcessEndTimestamp) + .Map(dest => dest.DocProcessRetries, src => src.DocumentProcessRetries) + .Map(dest => dest.Document, src => src.Document) + .Inherits(); + } + } +} diff --git a/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueModel.cs b/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueModel.cs new file mode 100644 index 0000000000..73d553f74d --- /dev/null +++ b/source/backend/apimodels/Models/Concepts/DocumentQueue/DocumentQueueModel.cs @@ -0,0 +1,67 @@ +using System; +using Pims.Api.Models.Base; + +namespace Pims.Api.Models.Concepts.Document +{ + /// + /// DocumentQueueModel class, provides a model to represent document associated to entities. + /// + public class DocumentQueueModel : BaseAuditModel + { + + #region Properties + + /// + /// get/set - Document Queue Id. + /// + public long Id { get; set; } + + /// + /// get/set - The document id within PIMS. + /// + public long? DocumentId { get; set; } + + /// + /// get/set - The original identifier in the source system. + /// + public long? DocumentExternalId { get; set; } + + /// + /// get/set - The document queue status type. + /// + public CodeTypeModel DocumentQueueStatusType { get; set; } + + /// + /// get/set - The document source type. + /// + public CodeTypeModel DataSourceTypeCode { get; set; } + + /// + /// get/set - When the processing of the document began. + /// + public DateTime? DocumentProcessStartTimestamp { get; set; } + + /// + /// get/set - When the processing of the document ended. + /// + public DateTime? DocumentProcessEndTimestamp { get; set; } + + /// + /// get/set - The number of times an attempt was made to process this document. + /// + public int? DocumentProcessRetries { get; set; } + + /// + /// get/set - The latest error from mayan, if processing failed. + /// + public string MayanError { get; set; } + + /// + /// get/set - The actual document, represented as a byte[]. + /// + public byte[] Document { get; set; } + + + #endregion + } +} diff --git a/source/backend/core.api/Pims.Core.Api.csproj b/source/backend/core.api/Pims.Core.Api.csproj index 2e34c312af..70c947b9d1 100644 --- a/source/backend/core.api/Pims.Core.Api.csproj +++ b/source/backend/core.api/Pims.Core.Api.csproj @@ -22,6 +22,7 @@ + diff --git a/source/backend/api/Repositories/RestCommon/BaseRestRepository.cs b/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs similarity index 99% rename from source/backend/api/Repositories/RestCommon/BaseRestRepository.cs rename to source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs index 2b5559b8fe..214d359f9b 100644 --- a/source/backend/api/Repositories/RestCommon/BaseRestRepository.cs +++ b/source/backend/core.api/Repositories/RestCommon/BaseRestRepository.cs @@ -14,7 +14,7 @@ using Pims.Api.Models.CodeTypes; using Pims.Api.Models.Requests.Http; -namespace Pims.Api.Repositories.Rest +namespace Pims.Core.Api.Repositories.Rest { /// /// BaseRestRepository provides common methods to interact with Rest-ful external interfaces. diff --git a/source/backend/api/Repositories/RestCommon/IRestRespository.cs b/source/backend/core.api/Repositories/RestCommon/IRestRespository.cs similarity index 95% rename from source/backend/api/Repositories/RestCommon/IRestRespository.cs rename to source/backend/core.api/Repositories/RestCommon/IRestRespository.cs index 3229499989..da802e813e 100644 --- a/source/backend/api/Repositories/RestCommon/IRestRespository.cs +++ b/source/backend/core.api/Repositories/RestCommon/IRestRespository.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Pims.Api.Models.Requests.Http; -namespace Pims.Api.Repositories.Rest +namespace Pims.Core.Api.Repositories.Rest { /// /// IRestRespository interface, defines common functionality among Rest-ful Interfaces. diff --git a/source/backend/api/Services/BaseService.cs b/source/backend/core.api/Services/BaseService.cs similarity index 97% rename from source/backend/api/Services/BaseService.cs rename to source/backend/core.api/Services/BaseService.cs index 6a83bb3d8b..10174a96ec 100644 --- a/source/backend/api/Services/BaseService.cs +++ b/source/backend/core.api/Services/BaseService.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using Microsoft.Extensions.Logging; -namespace Pims.Api.Services +namespace Pims.Core.Api.Services { /// /// BaseService abstract class, provides a generic service layer to perform business logic. diff --git a/source/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs b/source/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs index 75511b9f23..267afba4f5 100644 --- a/source/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs +++ b/source/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs @@ -84,6 +84,7 @@ public static IServiceCollection AddPimsDalRepositories(this IServiceCollection repositories.AddScoped(); repositories.AddScoped(); repositories.AddScoped(); + repositories.AddScoped(); return repositories; } diff --git a/source/backend/dal/Repositories/DocumentQueueRepository.cs b/source/backend/dal/Repositories/DocumentQueueRepository.cs new file mode 100644 index 0000000000..fa2472427a --- /dev/null +++ b/source/backend/dal/Repositories/DocumentQueueRepository.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Microsoft.Extensions.Logging; +using Pims.Core.Extensions; +using Pims.Dal.Entities; +using Pims.Dal.Entities.Models; + +namespace Pims.Dal.Repositories +{ + /// + /// DocumentQueueRepository class, provides a repository to interact with queued documents within the datasource. + /// + public class DocumentQueueRepository : BaseRepository, IDocumentQueueRepository + { + #region Constructors + + /// + /// Creates a new instance of a DocumentQueueRepository, and initializes it with the specified arguments. + /// + /// + /// + /// + public DocumentQueueRepository( + PimsContext dbContext, + ClaimsPrincipal user, + ILogger logger) + : base(dbContext, user, logger) + { + } + #endregion + + #region Methods + + /// + /// Updates the queued document in the database. + /// + /// + /// + public PimsDocumentQueue Update(PimsDocumentQueue queuedDocument) + { + queuedDocument.ThrowIfNull(nameof(queuedDocument)); + + queuedDocument = Context.Update(queuedDocument).Entity; + return queuedDocument; + } + + /// + /// Deletes the passed queued document from the database. Note, removing a queued document does not delete the imported document. + /// + /// + /// + public bool Delete(PimsDocumentQueue queuedDocument) + { + queuedDocument.ThrowIfNull(nameof(queuedDocument)); + + Context.Remove(queuedDocument); + return true; + } + + /// + /// Return a list of documents, filtered by the specified arguments. + /// + /// + /// + public IEnumerable GetAllByFilter(DocumentQueueFilter filter) + { + var query = Context.PimsDocumentQueues.Where(q => true); + + if (filter.DataSourceTypeCode != null) + { + query.Where(d => d.DataSourceTypeCode == filter.DataSourceTypeCode); + } + if (filter.DocumentQueueStatusTypeCode != null) + { + query.Where(d => d.DocumentQueueStatusTypeCode == filter.DocumentQueueStatusTypeCode); + } + if (filter.DocProcessStartDate != null) + { + query.Where(d => d.DocProcessStartDt >= filter.DocProcessStartDate); + } + if (filter.DocProcessEndDate != null) + { + query.Where(d => d.DocProcessEndDt <= filter.DocProcessEndDate); + } + return query.ToList(); + } + + public int DocumentQueueCount(PimsDocumentQueueStatusType pimsDocumentQueueStatusType) + { + if (pimsDocumentQueueStatusType == null) + { + Context.PimsDocumentQueues.Count(); + } + return Context.PimsDocumentQueues.Count(d => d.DocumentQueueStatusTypeCode == pimsDocumentQueueStatusType.DocumentQueueStatusTypeCode); + } + + #endregion + } +} diff --git a/source/backend/dal/Repositories/Interfaces/IDocumentQueueRepository.cs b/source/backend/dal/Repositories/Interfaces/IDocumentQueueRepository.cs new file mode 100644 index 0000000000..45c14d19ac --- /dev/null +++ b/source/backend/dal/Repositories/Interfaces/IDocumentQueueRepository.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Pims.Dal.Entities; +using Pims.Dal.Entities.Models; + +namespace Pims.Dal.Repositories +{ + /// + /// IDocumentQueueRepository interface, provides functions to interact with queued documents within the datasource. + /// + public interface IDocumentQueueRepository : IRepository + { + IEnumerable GetAllByFilter(DocumentQueueFilter filter); + + PimsDocumentQueue Update(PimsDocumentQueue queuedDocument); + + bool Delete(PimsDocumentQueue queuedDocument); + + int DocumentQueueCount(PimsDocumentQueueStatusType pimsDocumentQueueStatusType); + } +} diff --git a/source/backend/dal/Services/BaseService.cs b/source/backend/dal/Services/BaseService.cs deleted file mode 100644 index 920366878a..0000000000 --- a/source/backend/dal/Services/BaseService.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Security.Claims; -using Microsoft.Extensions.Logging; - -namespace Pims.Dal.Services -{ - /// - /// BaseService abstract class, provides a generic service layer to perform business logic. - /// It can access the datastore via available repositories. - /// - public abstract class BaseService - { - #region Properties - - /// - /// get - The current user accessing the service. - /// - protected ClaimsPrincipal User { get; } - - /// - /// get - The logger. - /// - protected ILogger Logger { get; } - - #endregion - - #region Constructors - - /// - /// Creates a new instance of a BaseService class, and initializes it with the specified arguments. - /// - /// - /// - protected BaseService(ClaimsPrincipal user, ILogger logger) - { - this.User = user; - this.Logger = logger; - } - #endregion - - #region Methods - - /// - /// Provides a way to fetch the user within the assembly. - /// - /// - internal ClaimsPrincipal GetUser() - { - return this.User; - } - #endregion - } -} diff --git a/source/backend/dal/Models/AccessRequestFilter.cs b/source/backend/entities/Models/AccessRequestFilter.cs similarity index 100% rename from source/backend/dal/Models/AccessRequestFilter.cs rename to source/backend/entities/Models/AccessRequestFilter.cs diff --git a/source/backend/dal/Models/AcquisitionFileExportModel.cs b/source/backend/entities/Models/AcquisitionFileExportModel.cs similarity index 100% rename from source/backend/dal/Models/AcquisitionFileExportModel.cs rename to source/backend/entities/Models/AcquisitionFileExportModel.cs diff --git a/source/backend/dal/Models/AcquisitionFilter.cs b/source/backend/entities/Models/AcquisitionFilter.cs similarity index 100% rename from source/backend/dal/Models/AcquisitionFilter.cs rename to source/backend/entities/Models/AcquisitionFilter.cs diff --git a/source/backend/dal/Models/AcquisitionReportFilterModel.cs b/source/backend/entities/Models/AcquisitionReportFilterModel.cs similarity index 100% rename from source/backend/dal/Models/AcquisitionReportFilterModel.cs rename to source/backend/entities/Models/AcquisitionReportFilterModel.cs diff --git a/source/backend/dal/Models/AutocompletionRequestModel.cs b/source/backend/entities/Models/AutocompletionRequestModel.cs similarity index 100% rename from source/backend/dal/Models/AutocompletionRequestModel.cs rename to source/backend/entities/Models/AutocompletionRequestModel.cs diff --git a/source/backend/dal/Models/ContactFilter.cs b/source/backend/entities/Models/ContactFilter.cs similarity index 100% rename from source/backend/dal/Models/ContactFilter.cs rename to source/backend/entities/Models/ContactFilter.cs diff --git a/source/backend/dal/Models/DispositionFileExportModel.cs b/source/backend/entities/Models/DispositionFileExportModel.cs similarity index 100% rename from source/backend/dal/Models/DispositionFileExportModel.cs rename to source/backend/entities/Models/DispositionFileExportModel.cs diff --git a/source/backend/dal/Models/DispositionFilter.cs b/source/backend/entities/Models/DispositionFilter.cs similarity index 100% rename from source/backend/dal/Models/DispositionFilter.cs rename to source/backend/entities/Models/DispositionFilter.cs diff --git a/source/backend/entities/Models/DocumentQueueFilter.cs b/source/backend/entities/Models/DocumentQueueFilter.cs new file mode 100644 index 0000000000..c9d43c5e61 --- /dev/null +++ b/source/backend/entities/Models/DocumentQueueFilter.cs @@ -0,0 +1,42 @@ +using System; + +namespace Pims.Dal.Entities.Models +{ + public class DocumentQueueFilter : PageFilter + { + #region Properties + + /// + /// get/set - The source system for this document. + /// + public string DataSourceTypeCode { get; set; } + + /// + /// get/set - The status of the document in the queue, such as 'Pending'. + /// + public string DocumentQueueStatusTypeCode { get; set; } + + /// + /// get/set - The date/time that processing of the document started. + /// + public DateTime? DocProcessStartDate { get; set; } + + /// + /// get/set - The date/time that processing of the document ended. + /// + public DateTime? DocProcessEndDate { get; set; } + + #endregion + + #region Constructors + + /// + /// Creates a new instance of a DocumentQueueFilter class. + /// + public DocumentQueueFilter() + { + } + + #endregion + } +} diff --git a/source/backend/dal/Models/EnvironmentModel.cs b/source/backend/entities/Models/EnvironmentModel.cs similarity index 100% rename from source/backend/dal/Models/EnvironmentModel.cs rename to source/backend/entities/Models/EnvironmentModel.cs diff --git a/source/backend/dal/Models/LastUpdateBy.cs b/source/backend/entities/Models/LastUpdateBy.cs similarity index 100% rename from source/backend/dal/Models/LastUpdateBy.cs rename to source/backend/entities/Models/LastUpdateBy.cs diff --git a/source/backend/dal/Models/LeaseFilter.cs b/source/backend/entities/Models/LeaseFilter.cs similarity index 100% rename from source/backend/dal/Models/LeaseFilter.cs rename to source/backend/entities/Models/LeaseFilter.cs diff --git a/source/backend/dal/Models/OrganizationFilter.cs b/source/backend/entities/Models/OrganizationFilter.cs similarity index 100% rename from source/backend/dal/Models/OrganizationFilter.cs rename to source/backend/entities/Models/OrganizationFilter.cs diff --git a/source/backend/dal/Models/PageFilter.cs b/source/backend/entities/Models/PageFilter.cs similarity index 100% rename from source/backend/dal/Models/PageFilter.cs rename to source/backend/entities/Models/PageFilter.cs diff --git a/source/backend/dal/Models/Paged{TModel}.cs b/source/backend/entities/Models/Paged{TModel}.cs similarity index 100% rename from source/backend/dal/Models/Paged{TModel}.cs rename to source/backend/entities/Models/Paged{TModel}.cs diff --git a/source/backend/dal/Models/ProjectFilter.cs b/source/backend/entities/Models/ProjectFilter.cs similarity index 100% rename from source/backend/dal/Models/ProjectFilter.cs rename to source/backend/entities/Models/ProjectFilter.cs diff --git a/source/backend/dal/Models/PropertyFilter.cs b/source/backend/entities/Models/PropertyFilter.cs similarity index 100% rename from source/backend/dal/Models/PropertyFilter.cs rename to source/backend/entities/Models/PropertyFilter.cs diff --git a/source/backend/dal/Models/PropertyFilterCriteria.cs b/source/backend/entities/Models/PropertyFilterCriteria.cs similarity index 100% rename from source/backend/dal/Models/PropertyFilterCriteria.cs rename to source/backend/entities/Models/PropertyFilterCriteria.cs diff --git a/source/backend/dal/Models/PropertyOwnershipState.cs b/source/backend/entities/Models/PropertyOwnershipState.cs similarity index 100% rename from source/backend/dal/Models/PropertyOwnershipState.cs rename to source/backend/entities/Models/PropertyOwnershipState.cs diff --git a/source/backend/dal/Models/ResearchFilter.cs b/source/backend/entities/Models/ResearchFilter.cs similarity index 100% rename from source/backend/dal/Models/ResearchFilter.cs rename to source/backend/entities/Models/ResearchFilter.cs diff --git a/source/backend/dal/Models/UserFilter.cs b/source/backend/entities/Models/UserFilter.cs similarity index 100% rename from source/backend/dal/Models/UserFilter.cs rename to source/backend/entities/Models/UserFilter.cs diff --git a/source/backend/entities/Partials/DocumentQueue.cs b/source/backend/entities/Partials/DocumentQueue.cs new file mode 100644 index 0000000000..5ddae49be5 --- /dev/null +++ b/source/backend/entities/Partials/DocumentQueue.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Pims.Dal.Entities +{ + /// + /// PimsDocumentQueue class, provides an entity for the datamodel to manage document entities. + /// + public partial class PimsDocumentQueue : StandardIdentityBaseAppEntity, IBaseAppEntity + { + #region Properties + [NotMapped] + public override long Internal_Id { get => this.DocumentQueueId; set => this.DocumentQueueId = value; } + #endregion + } +} diff --git a/source/backend/entrypoint.scheduler.sh b/source/backend/entrypoint.scheduler.sh new file mode 100644 index 0000000000..21a3da7bef --- /dev/null +++ b/source/backend/entrypoint.scheduler.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# export MAIL_SERVER_URL=`route -n | grep "UG" | grep -v "UGH" | cut -f 10 -d " "` +# export MAIL_SERVER_PORT=1025 +dotnet Pims.Scheduler.dll diff --git a/source/backend/proxy/README.md b/source/backend/proxy/README.md index 3875eae4b6..5146d9580a 100644 --- a/source/backend/proxy/README.md +++ b/source/backend/proxy/README.md @@ -1,10 +1,6 @@ -# PIMS RESTful API - .NET CORE +# PIMS PROXY API -The PIMS API provides an RESTful interface to interact with the configured data-source. - -The API is configured to run in a Docker container and has the following dependencies with other containers; database, keycloak. - -For more information refer to documentation [here](https://github.com/bcgov/PSP/wiki/api/API.md). +The PROXY API provides a keycloak-authenticated passthrough to the PIMS geoserver instance (via service account credentials). To run the API locally you will need to create the appropriate environment variable `.env` files. You can do this through using the prebuilt scripts [here](../../scripts/README.md). @@ -28,27 +24,3 @@ ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=http://*:8080 ASPNETCORE_FORWARDEDHEADERS_ENABLED=true ``` - -## Running Locally - -to run the API locally with vscode, comment out the following lines, and add the `ConnectionStrings__PIMS` value in your `.env` file; - -```conf -# ASPNETCORE_ENVIRONMENT=Development -# ASPNETCORE_URLS=http://*:8080 -``` - -This is so that the `/.vscode/launch.json` configured environment variables are used instead. Specifically it will run with the following; - -```json -{ - "configurations": [{ - ... - "env": { - "ASPNETCORE_ENVIRONMENT": "Local", - "ASPNETCORE_URLS": "http://*:5002" - } - ... - }] -} -``` diff --git a/source/backend/scheduler/.dockerignore b/source/backend/scheduler/.dockerignore new file mode 100644 index 0000000000..7ed9d732a6 --- /dev/null +++ b/source/backend/scheduler/.dockerignore @@ -0,0 +1,18 @@ +.vs/ +.env + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +**/[Bb]in/ +**/[Oo]bj/ +**/[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn diff --git a/source/backend/scheduler/.editorconfig b/source/backend/scheduler/.editorconfig new file mode 100644 index 0000000000..01b3182133 --- /dev/null +++ b/source/backend/scheduler/.editorconfig @@ -0,0 +1,257 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +end_of_line = lf +charset = utf-8 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +indent_size = 2 + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[*.env] +insert_final_newline = false + +[{Makefile,**.mk}] +# Use tabs for indentation (Makefiles require tabs) +indent_style = tab + +[*.cs] +indent_size = 4 + +# IDE0005: Using directive is unnecessary. +dotnet_diagnostic.IDE0005.severity = warning +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = warning +# CA1816: Call GC.SuppressFinalize correctly +dotnet_diagnostic.CA1816.severity = error +# CA1063: Implement IDisposable correctly +dotnet_diagnostic.CA1063.severity = error +# CA2007: Do not directly await a Task +dotnet_diagnostic.CA2007.severity = none +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = none +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = none +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = error +# CA1508: Avoid dead conditional code +dotnet_diagnostic.CA1508.severity = none #TODO: Requires further analysis +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = none # #TODO: Requires further analysis +# CA1801: Review unused parameters +dotnet_diagnostic.CA1801.severity = none # #TODO: Requires further analysis +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = warning +# CS1591: Missing XML comment +dotnet_diagnostic.CS1591.severity = none +# CS0108 X member hides inherited member Y. Use the new keyword if hiding was intended. +dotnet_diagnostic.CS0108.severity = error +# SA1400 Element X should declare an access modifier +dotnet_diagnostic.SA1400.severity = error +# SA1314 Type parameter names should begin with T +dotnet_diagnostic.SA1314.severity = error + +# SonarQube +# S4487 Unread "private" fields should be removed. +dotnet_diagnostic.S4487.severity = warning +# S927 Parameter names should match base declaration and other partial definitions +dotnet_diagnostic.S927.severity = error +# S1006 Remove the default parameter value to match the signature of overridden method +dotnet_diagnostic.S1006.severity = error +# S3928 The parameter name '{param}' is not declared in the argument list +dotnet_diagnostic.S3928.severity = error +# S1481 Remove the unused local variable +dotnet_diagnostic.S1481.severity = warning +# S1116 Remove empty statement +dotnet_diagnostic.S1116.severity = warning +# S1118 Utility classes should not have public constructors +dotnet_diagnostic.S1118.severity = error +# S3925 "ISerializable" should be implemented correctly +dotnet_diagnostic.S3925.severity = error +# S4457 Parameter validation in "async"/"await" methods should be wrapped +dotnet_diagnostic.S4457.severity = error +# S3442 "abstract" classes should not have "public" constructors +dotnet_diagnostic.S3442.severity = error +# S125 Remove commented out code +dotnet_diagnostic.S125.severity = warning +# S4136 Method overloads should be adjacent +dotnet_diagnostic.S4136.severity = none +# S1128 Remove this unnecessary 'using' +dotnet_diagnostic.S1128.severity = warning +# S3358 Extract this nested ternary operation into an independent statement. +dotnet_diagnostic.S3358.severity = warning +# SA1617 Void return value should not be documented +dotnet_diagnostic.SA1617.severity = error +# SA1122 Use string.Empty for empty strings +dotnet_diagnostic.SA1122.severity = error + +# StyleCop +# SA1600 Elements should be documented +dotnet_diagnostic.SA1600.severity = none +# SA1200 Using directive should appear within a namespace declaration +dotnet_diagnostic.SA1200.severity = none +# SA1633 The file header is missing or not located at the top of the file. +dotnet_diagnostic.SA1633.severity = none +# SA1642 Constructor summary documentation should begin with standard text +dotnet_diagnostic.SA1642.severity = none +# SA1614 Element parameter documentation should have text +dotnet_diagnostic.SA1614.severity = none +# SA1101 Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none +# SA1616 Element return value documentation should have text +dotnet_diagnostic.SA1616.severity = none +# SA1623 The property's documentation summary text should begin with: 'Gets or sets' +dotnet_diagnostic.SA1623.severity = none +# SA1309 Field should not begin with an underscore +dotnet_diagnostic.SA1309.severity = none +# SA1513 Closing brace should be followed by an empty line +dotnet_diagnostic.SA1505.severity = none +# SA1505 Opening brace should be followed by an empty line +dotnet_diagnostic.SA1513.severity = none +# SA1124 Do not use regions +dotnet_diagnostic.SA1124.severity = none +# SA1139 Use literal suffix notation +dotnet_diagnostic.SA1139.severity = none +# SA1501 Statement should not be on a single line. +dotnet_diagnostic.SA1501.severity = none +# SA1000 The keyword 'new' should be followed by a space +dotnet_diagnostic.SA1000.severity = none + +# TODO: Fix these +# SA1622 Generic type parameter documentation should have text. // TODO: This should get added +dotnet_diagnostic.SA1622.severity = none +# SA1618 The documentation for type parameter 'T' is missing. // TODO: This should be an error +dotnet_diagnostic.SA1618.severity = none +# SA1402 File may only contain a single type. // TODO: This should be enabled +dotnet_diagnostic.SA1402.severity = none +# SA1649 File name should match first type name. // TODO: This should be an error || LTSA needs to be skiped +dotnet_diagnostic.SA1649.severity = none +# CA2254 The logging message template should not vary between calls. // TODO: requires code changes +dotnet_diagnostic.CA2254.severity = none +# SA1129 Do not use default value type constructor. +dotnet_diagnostic.SA1129.severity = none +# S1135 Complete the task associated to this 'TODO' comment +dotnet_diagnostic.S1135.severity = none +## + +# SA1611: The documentation for parameter X is missing. // Not necessary unless it proves value +dotnet_diagnostic.SA1611.severity = none +# SA1615 Element return value should be documented // Not necessary unless it proves value +dotnet_diagnostic.SA1615.severity = none +# SA1201 A constructor should not follow a property +dotnet_diagnostic.SA1201.severity = none +# SA1602 Enumeration items should be documented. // Not necessary unless it proves value +dotnet_diagnostic.SA1602.severity = none + +# SA0001 XML comment analysis is disabled due to project configuration +dotnet_diagnostic.SA0001.severity = none + +# --- Set to error before running formatter --- +# dotnet format --severity error --exclude entities/ef/** --exclude entities/PimsBaseContext.cs +# SA1515 Single-line comment should be preceded by blank line. +dotnet_diagnostic.SA1515.severity = warning +# SA1516 Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = warning +# SA1208 Using directive for 'X' should appear before directive 'Y' +dotnet_diagnostic.SA1208.severity = warning +# SA1507 Code should not contain multiple blank lines in a row +dotnet_diagnostic.SA1507.severity = warning +# SA1629 Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = warning +# SA1121 Use built-in type alias +dotnet_diagnostic.SA1121.severity = warning +# SA1413 Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = warning +# SA1128 Put constructor initializers on their own line +dotnet_diagnostic.SA1128.severity = warning +# SA1514 Element documentation header should be preceded by blank line +dotnet_diagnostic.SA1514.severity = warning +# SA1502 Element should not be on a single line +dotnet_diagnostic.SA1502.severity = warning +# S1128 Remove this unnecessary 'using' +dotnet_diagnostic.S1128.severity = warning +# SA1517 Code should not contain blank lines at start of file +dotnet_diagnostic.SA1517.severity = warning +# S1128 Remove this unnecessary 'using' +dotnet_diagnostic.S1128.severity = warning +# SA1508 A closing brace should not be preceded by a blank line. +dotnet_diagnostic.SA1508.severity = warning +# SA1009 Closing parenthesis should be followed by a space +dotnet_diagnostic.SA1009.severity = warning +# SA1210 Using directives should be ordered alphabetically by the namespaces +dotnet_diagnostic.SA1210.severity = warning +# SA1610 Property documentation should have value text +dotnet_diagnostic.SA1610.severity = warning +# CS8019: Using directive is unnecessary. +dotnet_diagnostic.CS8019.severity = warning +# SA1005 Single line comment should begin with a space. +dotnet_diagnostic.SA1005.severity = warning +# SA1411 Attribute constructor should not use unnecessary parenthesis +dotnet_diagnostic.SA1411.severity = warning +# SA1519 Braces should not be omitted from multi-line child statement +dotnet_diagnostic.SA1519.severity = warning +# SA1202 'public' members should come before 'private' members +dotnet_diagnostic.SA1202.severity = warning +# SA1204 Static members should appear before non-static members. +dotnet_diagnostic.SA1204.severity = warning +# SA1127 Generic type constraints should be on their own line +dotnet_diagnostic.SA1127.severity = warning +# SA1520 Use braces consistently +dotnet_diagnostic.SA1520.severity = warning +# SA1028 Code should not contain trailing whitespace. +dotnet_diagnostic.SA1028.severity = warning +# SA1512 Single-line comments should not be followed by blank line. +dotnet_diagnostic.SA1512.severity = warning +# SA1500 Braces for multi-line statements should not share line +dotnet_diagnostic.SA1500.severity = warning +# SA1119 Statement should not use unnecessary parenthesis. +dotnet_diagnostic.SA1119.severity = warning +# CA1847 Use 'string.Contains(char)' instead of 'string.Contains(string)' when searching for a single character +dotnet_diagnostic.CA1847.severity = warning +# SA1107 Code should not contain multiple statements on one line +dotnet_diagnostic.SA1107.severity = warning +# IDE0005 Using directive is unnecessary. +dotnet_diagnostic.IDE0005.severity = warning +# SA1518 Code should not contain blank lines at the end of the file +dotnet_diagnostic.SA1518.severity = warning +# SA1510 'else' statement should not be preceded by a blank line. +dotnet_diagnostic.SA1510.severity = warning +# SA1116 The parameters should begin on the line after the declaration. +dotnet_diagnostic.SA1116.severity = warning +# SA1137 Elements should have the same indentation +dotnet_diagnostic.SA1137.severity = warning +# SA1117 The parameters should all be placed on the same line or each parameter should be placed on its own line. +dotnet_diagnostic.SA1117.severity = warning +# SA1115 The parameter should begin on the line after the previous parameter. +dotnet_diagnostic.SA1115.severity = warning +# SA1612 The parameter 'id' does not exist. +dotnet_diagnostic.SA1612.severity = warning +# SA1025 Code should not contain multiple whitespace characters in a row. +dotnet_diagnostic.SA1025.severity = warning +# IDE0052 Private member X can be removed as the value assigned to it is never read. +dotnet_diagnostic.IDE0052.severity = warning +# SA1214 Readonly fields should appear before non-readonly fields. +dotnet_diagnostic.SA1214.severity = warning +# SA1300 Element X should begin with an uppercase letter +dotnet_diagnostic.SA1300.severity = warning + +# Entity Framework files +[**{entities/ef/*,PIMSContext,PimsBaseContext}.cs] +# CS8019: Using directive is unnecessary. +dotnet_diagnostic.CS8019.severity = none +# SA1502 Element should not be on a single line +dotnet_diagnostic.SA1502.severity = none +# SA1516 Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none +# SonarQube +# S1128 Remove this unnecessary 'using' +dotnet_diagnostic.S1128.severity = none +# S3251 Supply an implementation for this partial method. +dotnet_diagnostic.S3251.severity = none \ No newline at end of file diff --git a/source/backend/scheduler/.gitignore b/source/backend/scheduler/.gitignore new file mode 100644 index 0000000000..7f9566aaa2 --- /dev/null +++ b/source/backend/scheduler/.gitignore @@ -0,0 +1,55 @@ +# Environment variables +.env +connectionstrings.*json +.vscode/settings.json + +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +TestResults/ + +# Rider +.idea + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +**/[Dd]ebug/ +**/[Dd]ebugPublic/ +**/[Rr]elease/ +**/[Rr]eleases/ +**/x64/ +**/x86/ +**/build/ +**/bld/ +**/[Bb]in/ +**/[Oo]bj/ +**/[Oo]ut/ +**/msbuild.log +**/msbuild.err +**/msbuild.wrn + +# Visual Studio 2015 +.vs/ + +# SonarQube +.sonarqube/ + +# NET Core Healthchecks UI +healthchecksdb +healthchecksdb-shm +healthchecksdb-wal diff --git a/source/backend/scheduler/Configuration/PimsOptions.cs b/source/backend/scheduler/Configuration/PimsOptions.cs new file mode 100644 index 0000000000..eeedc9d212 --- /dev/null +++ b/source/backend/scheduler/Configuration/PimsOptions.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace Pims.Scheduler.Http.Configuration +{ + /// + /// PimsOptions class, provides a way to store connection information for the PIMS application. + /// + public class PimsOptions + { + #region Properties + + /// + /// get/set - the internal Uri of the pims server. + /// + [Required(ErrorMessage = "Configuration 'Uri' is required.")] + public string Uri { get; set; } + + public string Environment { get; set; } + #endregion + } +} diff --git a/source/backend/scheduler/Configuration/ProgramOptions.cs b/source/backend/scheduler/Configuration/ProgramOptions.cs new file mode 100644 index 0000000000..ce2c0d06c0 --- /dev/null +++ b/source/backend/scheduler/Configuration/ProgramOptions.cs @@ -0,0 +1,51 @@ +using System.Globalization; +using System.Linq; +using CommandLine; +using Pims.Core.Extensions; + +namespace Pims.Api.Configuration +{ + /// + /// ProgramOptions class, provides a way to parse command line arguments for the PIMS API program. + /// + public class ProgramOptions + { + #region Properties + + /// + /// get/set - ASP NET Core environment. + /// + [Option('e', "environment", Required = false, HelpText = "ASPNETCORE_ENVIRONMENT")] + public string Environment { get; set; } + + /// + /// get/set - ASP NET Core URLs. + /// + [Option('u', "urls", Required = false, HelpText = "ASPNETCORE_URLS")] + public string Urls { get; set; } + + /// + /// get/set - ASP NET Core HTTPS port. + /// + [Option('p', "port", Required = false, HelpText = "ASPNETCORE_HTTPS_PORT")] + public int? HttpsPort { get; set; } + #endregion + + #region Methods + + /// + /// Return an array of arguments of valid option values. + /// + /// + public string[] ToArgs() + { + return new[] + { + this.Urls, + this.Environment, + this.HttpsPort?.ToString(CultureInfo.InvariantCulture), + }.NotNull().ToArray(); + } + #endregion + } +} diff --git a/source/backend/scheduler/Controllers/PimsSchedulerController.cs b/source/backend/scheduler/Controllers/PimsSchedulerController.cs new file mode 100644 index 0000000000..8074e9a6d5 --- /dev/null +++ b/source/backend/scheduler/Controllers/PimsSchedulerController.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Pims.Core.Api.Policies; +using Pims.Core.Extensions; +using Pims.Core.Security; +using Pims.Scheduler.Services; + +namespace Pims.Scheduler.Controllers +{ + /// + /// DocumentSchedulerController class, allows a caller to create a job to process pending PIMS documents. + /// + [Authorize] + [ApiController] + [Route("v{version:apiVersion}/documents")] + [Route("/documents")] + public class DocumentSchedulerController : ControllerBase + { + #region Variables + + private readonly ILogger _logger; + private readonly IDocumentQueueService _documentQueueService; + private readonly IBackgroundJobClient _backgroundJobClient; + #endregion + + #region Constructors + + /// + /// Creates a new instance of a PimsGeoserverController class. + /// + public DocumentSchedulerController(ILogger logger, IDocumentQueueService documentQueueService, IBackgroundJobClient backgroundJobClient) + { + _logger = logger; + _documentQueueService = documentQueueService; + _backgroundJobClient = backgroundJobClient; + } + #endregion + + #region Endpoints + + [Route("queued")] + [HasPermission(Permissions.SystemAdmin)] + public Task ProcessQueuedDocuments() + { + _logger.LogInformation( + "Request received by Controller: {Controller}, Action: {ControllerAction}, User: {User}, DateTime: {DateTime}", + nameof(DocumentSchedulerController), + nameof(ProcessQueuedDocuments), + User.GetUsername(), + DateTime.Now); + + // TODO: this is a placeholder only + _backgroundJobClient.Enqueue(() => _documentQueueService.UploadQueuedDocuments()); + + return Task.CompletedTask; + } + + #endregion + } +} diff --git a/source/backend/scheduler/Directory.Build.props b/source/backend/scheduler/Directory.Build.props new file mode 100644 index 0000000000..d9a8909ec7 --- /dev/null +++ b/source/backend/scheduler/Directory.Build.props @@ -0,0 +1,40 @@ + + + net8.0 + 11.0 + Minimum + 4 + true + true + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + all + runtime; build; native; contentfiles; analyzers; + + + + + + diff --git a/source/backend/scheduler/Pims.Scheduler.csproj b/source/backend/scheduler/Pims.Scheduler.csproj new file mode 100644 index 0000000000..e4f4993886 --- /dev/null +++ b/source/backend/scheduler/Pims.Scheduler.csproj @@ -0,0 +1,63 @@ + + + + true + 16BC0468-78F6-4C91-87DA-7403C919E646 + net8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + diff --git a/source/backend/scheduler/Policies/HangfireDashboardAuthorizationFilter.cs b/source/backend/scheduler/Policies/HangfireDashboardAuthorizationFilter.cs new file mode 100644 index 0000000000..2117b5f9e0 --- /dev/null +++ b/source/backend/scheduler/Policies/HangfireDashboardAuthorizationFilter.cs @@ -0,0 +1,40 @@ +using System.Linq; +using Hangfire.Dashboard; +using Microsoft.Extensions.Options; +using Pims.Core.Extensions; +using Pims.Core.Security; +using Pims.Keycloak.Configuration; + +namespace Pims.Scheduler.Policies +{ + public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter + { + #region Variables + private readonly Permissions[] _permissions; + private readonly IOptionsMonitor _keycloakOptions; + #endregion + + #region Constructors + + /// + /// Creates a new instance of a PermissionFilter class, initializes it with the specified permission. + /// This will ensure the user has the specified permission. + /// + /// + /// + public HangfireDashboardAuthorizationFilter(IOptionsMonitor options, Permissions permission) + { + _permissions = new[] { permission }; + _keycloakOptions = options; + } + #endregion + + public bool Authorize(DashboardContext context) + { + var httpContext = context.GetHttpContext(); + var hasRole = httpContext.User.HasPermission(_permissions.ToArray()); + var isServiceAccount = httpContext.User.IsServiceAccount(_keycloakOptions); + return !hasRole && !isServiceAccount; + } + } +} diff --git a/source/backend/scheduler/Program.cs b/source/backend/scheduler/Program.cs new file mode 100644 index 0000000000..485337ce60 --- /dev/null +++ b/source/backend/scheduler/Program.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using CommandLine; +using CommandLine.Text; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Pims.Api.Configuration; +using Serilog; + +namespace Pims.Scheduler +{ + /// + /// Program class, provides the main program starting point for the scheduling application. + /// + [ExcludeFromCodeCoverage] + public static class Program + { + /// + /// The primary entry point for the application. + /// + /// + public static void Main(string[] args) + { + var results = Parser.Default.ParseArguments(args); + + results.WithParsed((options) => + { + var builder = CreateWebHostBuilder(options); + builder.Build().Run(); + }) + .WithNotParsed((errors) => + { + var helpText = HelpText.AutoBuild( + results, + h => { return HelpText.DefaultParsingErrorsHandler(results, h); }, + e => e); + Console.WriteLine(helpText); + }); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + DotNetEnv.Env.Load(); + return Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((hostingContext, config) => + { + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + config.AddEnvironmentVariables(); + config.AddCommandLine(args); + }); + } + + /// + /// Create a default configuration and setup for a web application. + /// + /// + /// + private static IHostBuilder CreateWebHostBuilder(ProgramOptions options) + { + var args = options.ToArgs(); + DotNetEnv.Env.Load(); + var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + var config = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddCommandLine(args) + .Build(); + + return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webHostBuilder => + webHostBuilder.ConfigureAppConfiguration((hostingContext, config) => + { + config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + config.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true); + config.AddEnvironmentVariables(); + config.AddCommandLine(args); + }) + .UseUrls(config.GetValue("ASPNETCORE_URLS")) + .UseStartup() + .UseKestrel(options => + { + options.Limits.MaxRequestBodySize = 524288000; // 500MB + })).UseSerilog(); + } + } +} diff --git a/source/backend/scheduler/Properties/launchSettings.json b/source/backend/scheduler/Properties/launchSettings.json new file mode 100644 index 0000000000..129dea4ff0 --- /dev/null +++ b/source/backend/scheduler/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:8057;https://localhost:8058", + "sslPort": 44342 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "Pims.Scheduler": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local", + "URLS": "http://localhost:8057;https://localhost:8058" + } + } + } +} diff --git a/source/backend/scheduler/README.md b/source/backend/scheduler/README.md new file mode 100644 index 0000000000..94532a28c9 --- /dev/null +++ b/source/backend/scheduler/README.md @@ -0,0 +1,33 @@ +# PIMS scheduler + +The PROXY scheduler provides a keycloak-authenticated hangfire instance. + +To run the API locally you will need to create the appropriate environment variable `.env` files. You can do this through using the prebuilt scripts [here](../../scripts/README.md). + +## Hangfire + +1. All serilog logging is piped into hangfire and visible in the job dashboard. +2. All recurring hangfire jobs must be registered in code (currently within Startup.cs). They can then be controlled in the appsettings files or by env. +3. Hangfire is dependent on redis to store job state. This is handled by a separate container with storage in local docker and OS. +4. See best practices for hangfire jobs, but all jobs should be written such that they can continue at any point if the are interupted. + +## API Environment Variables + +The current environment is initialized through the environment variable `ASPNETCORE_ENVIRONMENT`. + +When running the solution it applies the configuration setting in the following order; + +> NOTE: When the environment is Development it will look for your _User Secrets_ file. + +1. appsettings.json +2. appsettings.`[environment]`.json +3. User Secrets `(if environment=Development)` +4. Environment Variables + +To run the solution with docker-compose create a `.env` file within the `/api` directory and populate with the following; + +```conf +ASPNETCORE_ENVIRONMENT=Development +ASPNETCORE_URLS=http://*:8080 +ASPNETCORE_FORWARDEDHEADERS_ENABLED=true +``` diff --git a/source/backend/scheduler/Repositories/Interfaces/IPimsDocumentRepository.cs b/source/backend/scheduler/Repositories/Interfaces/IPimsDocumentRepository.cs new file mode 100644 index 0000000000..7505e3576d --- /dev/null +++ b/source/backend/scheduler/Repositories/Interfaces/IPimsDocumentRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Pims.Api.Models.Concepts.Document; +using Pims.Api.Models.Requests.Http; +using Pims.Dal.Entities.Models; + +namespace Pims.Scheduler.Repositories.Pims +{ + /// + /// IPimsDocumentQueueRepository interface, defines the functionality for a pims repository. + /// + public interface IPimsDocumentQueueRepository + { + Task>> SearchQueuedDocumentsAsync(DocumentQueueFilter filter); + } +} diff --git a/source/backend/scheduler/Repositories/PimsBaseRepository.cs b/source/backend/scheduler/Repositories/PimsBaseRepository.cs new file mode 100644 index 0000000000..6cdccd99cc --- /dev/null +++ b/source/backend/scheduler/Repositories/PimsBaseRepository.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using Microsoft.Extensions.Logging; +using Pims.Core.Api.Repositories.Rest; + +namespace Pims.Scheduler.Repositories +{ + public abstract class PimsBaseRepository : BaseRestRepository + { + + /// + /// Initializes a new instance of the class. + /// + /// Injected Logger Provider. + /// Injected Httpclient factory. + protected PimsBaseRepository( + ILogger logger, + IHttpClientFactory httpClientFactory) + : base(logger, httpClientFactory) + { + } + + public override void AddAuthentication(HttpClient client, string authenticationToken = null) + { + if (!string.IsNullOrEmpty(authenticationToken)) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationToken.Split(" ")[1]); + } + } + + protected Dictionary GenerateQueryParams(string ordering = "", int? page = null, int? pageSize = null) + { + Dictionary queryParams = new(); + + if (!string.IsNullOrEmpty(ordering)) + { + queryParams["ordering"] = ordering; + } + if (page.HasValue) + { + queryParams["page"] = page.ToString(); + } + if (pageSize.HasValue) + { + queryParams["page_size"] = pageSize.ToString(); + } + + return queryParams; + } + } +} diff --git a/source/backend/scheduler/Repositories/PimsRepository.cs b/source/backend/scheduler/Repositories/PimsRepository.cs new file mode 100644 index 0000000000..f6b2b061c6 --- /dev/null +++ b/source/backend/scheduler/Repositories/PimsRepository.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Pims.Api.Models.Concepts.Document; +using Pims.Api.Models.Requests.Http; +using Pims.Core.Http; +using Pims.Dal.Entities.Models; +using Pims.Scheduler.Http.Configuration; + +namespace Pims.Scheduler.Repositories.Pims +{ + /// + /// PimsDocumentQueueRepository provides document access from the PIMS document queue api. + /// + public class PimsDocumentQueueRepository : PimsBaseRepository, IPimsDocumentQueueRepository + { + private readonly IOpenIdConnectRequestClient _authRepository; + private readonly IOptionsMonitor _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// Injected Logger Provider. + /// Injected Httpclient factory. + /// Injected repository that handles authentication. + /// The injected configuration provider. + public PimsDocumentQueueRepository( + ILogger logger, + IHttpClientFactory httpClientFactory, + IOpenIdConnectRequestClient authRepository, + IOptionsMonitor configuration) + : base(logger, httpClientFactory) + { + _authRepository = authRepository; + _configuration = configuration; + } + + public async Task>> SearchQueuedDocumentsAsync(DocumentQueueFilter filter) + { + _logger.LogDebug("Getting filtered list of queued documents by {filter}", filter); + + string authenticationToken = await _authRepository.RequestAccessToken(); + + Uri endpoint = new($"{_configuration.CurrentValue.Uri}/documents/queue/search"); + + var response = await GetAsync>(endpoint, authenticationToken); + _logger.LogDebug($"Retrieved list of queued documents based on {filter} ", filter); + + return response; + } + } +} diff --git a/source/backend/scheduler/Scheduler.sln b/source/backend/scheduler/Scheduler.sln new file mode 100644 index 0000000000..9175bf6124 --- /dev/null +++ b/source/backend/scheduler/Scheduler.sln @@ -0,0 +1,132 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33110.190 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Scheduler", "Pims.Scheduler.csproj", "{16BC0468-78F6-4C91-87DA-7403C919E646}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{8D3E4CB2-41D6-4AA6-B9E4-CFCAB0E2F5BC}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .editorconfig = .editorconfig + .gitignore = .gitignore + Dockerfile = Dockerfile + Dockerfile.bak = Dockerfile.bak + entrypoint.sh = entrypoint.sh + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{5237F8A4-67F5-4751-B8B2-B93A06791480}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F256F2A5-0DBF-4137-A7D6-21F08111BD4A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit", "unit", "{3D70B211-74A8-484C-9B86-B0A2835C71E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Core.Api", "..\core.api\Pims.Core.Api.csproj", "{89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Core", "..\core\Pims.Core.csproj", "{AC8F04FF-3164-41FB-9EDF-E468B8B77837}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Keycloak", "..\keycloak\Pims.Keycloak.csproj", "{970903E9-BC53-436F-BA77-C62349546425}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Api.Models", "..\apimodels\Pims.Api.Models.csproj", "{58C42283-68DA-477F-915D-C67597543546}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pims.Dal.Entities", "..\entities\Pims.Dal.Entities.csproj", "{1C724CD5-CD24-46CD-835A-A83F673F97B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|x64.ActiveCfg = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|x64.Build.0 = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|x86.ActiveCfg = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Debug|x86.Build.0 = Debug|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|Any CPU.Build.0 = Release|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|x64.ActiveCfg = Release|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|x64.Build.0 = Release|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|x86.ActiveCfg = Release|Any CPU + {16BC0468-78F6-4C91-87DA-7403C919E646}.Release|x86.Build.0 = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|x64.Build.0 = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Debug|x86.Build.0 = Debug|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|Any CPU.Build.0 = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|x64.ActiveCfg = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|x64.Build.0 = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|x86.ActiveCfg = Release|Any CPU + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8}.Release|x86.Build.0 = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|x64.ActiveCfg = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|x64.Build.0 = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|x86.ActiveCfg = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Debug|x86.Build.0 = Debug|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|Any CPU.Build.0 = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|x64.ActiveCfg = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|x64.Build.0 = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|x86.ActiveCfg = Release|Any CPU + {AC8F04FF-3164-41FB-9EDF-E468B8B77837}.Release|x86.Build.0 = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|Any CPU.Build.0 = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|x64.ActiveCfg = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|x64.Build.0 = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|x86.ActiveCfg = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Debug|x86.Build.0 = Debug|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|Any CPU.ActiveCfg = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|Any CPU.Build.0 = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|x64.ActiveCfg = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|x64.Build.0 = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|x86.ActiveCfg = Release|Any CPU + {970903E9-BC53-436F-BA77-C62349546425}.Release|x86.Build.0 = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|x64.ActiveCfg = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|x64.Build.0 = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|x86.ActiveCfg = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Debug|x86.Build.0 = Debug|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|Any CPU.Build.0 = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|x64.ActiveCfg = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|x64.Build.0 = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|x86.ActiveCfg = Release|Any CPU + {58C42283-68DA-477F-915D-C67597543546}.Release|x86.Build.0 = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|x64.Build.0 = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|x86.ActiveCfg = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Debug|x86.Build.0 = Debug|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|Any CPU.Build.0 = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|x64.ActiveCfg = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|x64.Build.0 = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|x86.ActiveCfg = Release|Any CPU + {1C724CD5-CD24-46CD-835A-A83F673F97B5}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3D70B211-74A8-484C-9B86-B0A2835C71E7} = {F256F2A5-0DBF-4137-A7D6-21F08111BD4A} + {89A99CC5-ADFB-4FC2-9136-7B0029EEA2D8} = {5237F8A4-67F5-4751-B8B2-B93A06791480} + {AC8F04FF-3164-41FB-9EDF-E468B8B77837} = {5237F8A4-67F5-4751-B8B2-B93A06791480} + {970903E9-BC53-436F-BA77-C62349546425} = {5237F8A4-67F5-4751-B8B2-B93A06791480} + {58C42283-68DA-477F-915D-C67597543546} = {5237F8A4-67F5-4751-B8B2-B93A06791480} + {1C724CD5-CD24-46CD-835A-A83F673F97B5} = {5237F8A4-67F5-4751-B8B2-B93A06791480} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3433C5DD-DC49-4A96-A1AE-90C1A1EBA87C} + EndGlobalSection +EndGlobal diff --git a/source/backend/scheduler/Scheduler/IJobRescheduler.cs b/source/backend/scheduler/Scheduler/IJobRescheduler.cs new file mode 100644 index 0000000000..f1acaa8866 --- /dev/null +++ b/source/backend/scheduler/Scheduler/IJobRescheduler.cs @@ -0,0 +1,11 @@ +namespace Pims.Scheduler.Rescheduler +{ + public interface IJobRescheduler + { + /// + /// Load from configuration and override the schdule set via code. + /// If configuration is null, it will get from the singleton instance of JobConfiguration + /// + void LoadSchedules(JobScheduleOptions options); + } +} diff --git a/source/backend/scheduler/Scheduler/JobRescheduler.cs b/source/backend/scheduler/Scheduler/JobRescheduler.cs new file mode 100644 index 0000000000..791942711c --- /dev/null +++ b/source/backend/scheduler/Scheduler/JobRescheduler.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using CsvHelper.Configuration; +using Hangfire; +using Hangfire.Storage; + +namespace Pims.Scheduler.Rescheduler +{ + public class JobRescheduler : IJobRescheduler + { + private readonly JobStorage _jobStorage; + private readonly IRecurringJobManager _recurringJobManager; + + public JobRescheduler(JobStorage jobStorage, IRecurringJobManager recurringJobManager) + { + _jobStorage = jobStorage; + _recurringJobManager = recurringJobManager; + } + + public void LoadSchedules(JobScheduleOptions options) + { + var storageConnection = _jobStorage.GetConnection(); + + foreach (var scheduling in options.Schedules) + { + var recurringJob = storageConnection.GetRecurringJobs().FirstOrDefault(j => j.Id == scheduling.JobId); + if (recurringJob == null) + { + continue; + } + + if (!scheduling.IsEnabled) + { + _recurringJobManager.RemoveIfExists(scheduling.JobId); + continue; + } + + var timezoneId = scheduling.TimeZoneId ?? recurringJob.TimeZoneId ?? TimeZoneInfo.Local.Id; + var timezone = TimeZoneInfo.FindSystemTimeZoneById(timezoneId); + if (timezone == null) + { + throw new ConfigurationException($"Unable to find TimeZoneInfo : {timezoneId}"); + } + + var cron = scheduling.Cron; + if (cron == null) + { + throw new ConfigurationException($"Cron is required"); + } + + _recurringJobManager.AddOrUpdate( + recurringJob.Id, + recurringJob.Job, + scheduling.Cron, + new RecurringJobOptions() { TimeZone = timezone }); + } + } + } +} diff --git a/source/backend/scheduler/Scheduler/JobScheduleOption.cs b/source/backend/scheduler/Scheduler/JobScheduleOption.cs new file mode 100644 index 0000000000..07bb74c470 --- /dev/null +++ b/source/backend/scheduler/Scheduler/JobScheduleOption.cs @@ -0,0 +1,28 @@ +namespace Pims.Scheduler.Rescheduler +{ + /// + /// JobSchedule. + /// + public class JobScheduleOption + { + /// + /// Recurring JobId. + /// + public string JobId { get; set; } = default!; + + /// + /// Cron Expression. + /// + public string Cron { get; set; } = default!; + + /// + /// If enabled == false, the job will be removed. + /// + public bool IsEnabled { get; set; } = true; + + /// + /// TimezoneId. + /// + public string TimeZoneId { get; set; } + } +} diff --git a/source/backend/scheduler/Scheduler/JobScheduleOptions.cs b/source/backend/scheduler/Scheduler/JobScheduleOptions.cs new file mode 100644 index 0000000000..726e40462d --- /dev/null +++ b/source/backend/scheduler/Scheduler/JobScheduleOptions.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Pims.Scheduler.Rescheduler +{ + public class JobScheduleOptions + { + /// + /// Schedules. This will override the runtime registration defined in Jobs.Registry module. + /// + public List Schedules { get; set; } = new List(); + } +} diff --git a/source/backend/scheduler/Services/DocumentQueueService.cs b/source/backend/scheduler/Services/DocumentQueueService.cs new file mode 100644 index 0000000000..6e2c2a01c3 --- /dev/null +++ b/source/backend/scheduler/Services/DocumentQueueService.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Pims.Api.Models.CodeTypes; +using Pims.Core.Api.Services; +using Pims.Scheduler.Repositories.Pims; + +namespace Pims.Scheduler.Services +{ + public class DocumentQueueService : BaseService, IDocumentQueueService + { + private readonly ILogger _logger; + private readonly IPimsDocumentQueueRepository _pimsDocumentQueueRepository; + + public DocumentQueueService( + ILogger logger, + IPimsDocumentQueueRepository pimsDocumentQueueRepository) + : base(null, logger) + { + _logger = logger; + _pimsDocumentQueueRepository = pimsDocumentQueueRepository; + } + + public async Task UploadQueuedDocuments() + { + var queuedDocuments = await _pimsDocumentQueueRepository.SearchQueuedDocumentsAsync(new Dal.Entities.Models.DocumentQueueFilter() { Quantity = 50, DocumentQueueStatusTypeCode = DocumentQueueStatusTypes.PENDING.ToString() }); + _logger.LogInformation("retrieved {queuedDocuments} documents", queuedDocuments?.Payload?.Count); + } + } +} diff --git a/source/backend/scheduler/Services/Interfaces/IDocumentQueueService.cs b/source/backend/scheduler/Services/Interfaces/IDocumentQueueService.cs new file mode 100644 index 0000000000..8820b3913b --- /dev/null +++ b/source/backend/scheduler/Services/Interfaces/IDocumentQueueService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Pims.Scheduler.Services +{ + public interface IDocumentQueueService + { + public Task UploadQueuedDocuments(); + } +} diff --git a/source/backend/scheduler/Startup.cs b/source/backend/scheduler/Startup.cs new file mode 100644 index 0000000000..86b192e7c7 --- /dev/null +++ b/source/backend/scheduler/Startup.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IdentityModel.Tokens.Jwt; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Claims; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Hangfire; +using Hangfire.Console; +using Hangfire.Console.Extensions; +using Hangfire.Redis.StackExchange; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using Pims.Core.Api.Exceptions; +using Pims.Core.Api.Handlers; +using Pims.Core.Api.Helpers; +using Pims.Core.Api.Middleware; +using Pims.Core.Converters; +using Pims.Core.Http; +using Pims.Core.Json; +using Pims.Core.Security; +using Pims.Keycloak.Configuration; +using Pims.Scheduler.Http.Configuration; +using Pims.Scheduler.Policies; +using Pims.Scheduler.Repositories.Pims; +using Pims.Scheduler.Rescheduler; +using Pims.Scheduler.Services; +using Prometheus; +using StackExchange.Redis; + +namespace Pims.Scheduler +{ + /// + /// Startup class, provides a way to startup the .netcore RESTful API and configure it. + /// + [ExcludeFromCodeCoverage] + public class Startup + { + #region Properties + + /// + /// get - The application configuration settings. + /// + public IConfiguration Configuration { get; } + + /// + /// get/set - The environment settings for the application. + /// + public IWebHostEnvironment Environment { get; } + #endregion + + #region Constructors + + /// + /// Creates a new instances of a Startup class. + /// + /// + /// + public Startup(IConfiguration configuration, IWebHostEnvironment env) + { + this.Configuration = configuration; + this.Environment = env; + } + #endregion + + #region Methods + + /// + /// This method gets called by the runtime. Use this method to add services to the container. + /// + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddScoped(); + services.AddSingleton(); + + services.AddSerilogging(this.Configuration); + var jsonSerializerOptions = this.Configuration.GenerateJsonSerializerOptions(); + services.Configure(options => + { + options.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.DefaultIgnoreCondition = jsonSerializerOptions.DefaultIgnoreCondition; + options.PropertyNameCaseInsensitive = jsonSerializerOptions.PropertyNameCaseInsensitive; + options.PropertyNamingPolicy = jsonSerializerOptions.PropertyNamingPolicy; + options.WriteIndented = jsonSerializerOptions.WriteIndented; + options.Converters.Add(new JsonStringEnumMemberConverter()); + options.Converters.Add(new Int32ToStringJsonConverter()); + }); + services.Configure(this.Configuration.GetSection("Keycloak")); + services.Configure(this.Configuration.GetSection("OpenIdConnect")); + services.Configure(this.Configuration.GetSection("Keycloak")); + services.Configure(this.Configuration.GetSection("Pims:Environment")); + services.AddOptions(); + services.AddApiVersioning(options => + { + options.ReportApiVersions = true; + options.AssumeDefaultVersionWhenUnspecified = true; + options.ApiVersionReader = new HeaderApiVersionReader("api-version"); + }); + + services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.JsonSerializerOptions.DefaultIgnoreCondition = jsonSerializerOptions.DefaultIgnoreCondition; + options.JsonSerializerOptions.PropertyNameCaseInsensitive = jsonSerializerOptions.PropertyNameCaseInsensitive; + options.JsonSerializerOptions.PropertyNamingPolicy = jsonSerializerOptions.PropertyNamingPolicy; + options.JsonSerializerOptions.WriteIndented = jsonSerializerOptions.WriteIndented; + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + options.JsonSerializerOptions.Converters.Add(new Int32ToStringJsonConverter()); + options.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter()); + }); + + services.AddMvcCore() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.DefaultIgnoreCondition = jsonSerializerOptions.DefaultIgnoreCondition; + options.JsonSerializerOptions.PropertyNameCaseInsensitive = jsonSerializerOptions.PropertyNameCaseInsensitive; + options.JsonSerializerOptions.PropertyNamingPolicy = jsonSerializerOptions.PropertyNamingPolicy; + options.JsonSerializerOptions.WriteIndented = jsonSerializerOptions.WriteIndented; + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + options.JsonSerializerOptions.Converters.Add(new Int32ToStringJsonConverter()); + options.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter()); + }); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + var key = Encoding.ASCII.GetBytes(Configuration["Keycloak:Secret"]); + options.RequireHttpsMetadata = false; + options.Authority = Configuration["OpenIdConnect:Authority"]; + options.Audience = Configuration["Keycloak:Audience"]; + options.SaveToken = true; + options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() + { + ValidateIssuerSigningKey = true, + ValidateIssuer = false, + ValidateAudience = false, + ValidAlgorithms = new List() { "RS256" }, + }; + if (key.Length > 0) + { + options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(key); + } + + options.Events = new JwtBearerEvents() + { + OnTokenValidated = context => + { + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + context.NoResult(); + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + throw new AuthenticationException("Failed to authenticate", context.Exception); + }, + OnForbidden = context => + { + return Task.CompletedTask; + }, + }; + }); + + services.AddHttpClient(); + services.AddTransient(); + services.AddHttpClient("Pims.Api.Logging").AddHttpMessageHandler(); + services.AddHttpContextAccessor(); + + services.AddTransient(s => s.GetService()?.HttpContext?.User); + services.AddResponseCaching(); + services.AddMemoryCache(); + + // Export metrics from all HTTP clients registered in services + services.UseHttpClientMetrics(); + + services.AddHealthChecks().ForwardToPrometheus(); + + services.Configure(Configuration.GetSection(nameof(OpenApiInfo))); + services.AddSwaggerGen(options => + { + options.EnableAnnotations(false, true); + options.CustomSchemaIds(o => o.FullName); + options.OperationFilter(); + options.DocumentFilter(); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Name = "Authorization", + In = ParameterLocation.Header, + Description = "Please enter into field the word 'Bearer' following by space and JWT", + Type = SecuritySchemeType.ApiKey, + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer", + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + }, + new List() + }, + }); + + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + options.IncludeXmlComments(xmlPath); + }); + + ConnectionMultiplexer redisConnection = ConnectionMultiplexer.Connect(Configuration.GetConnectionString("Redis")); + services.AddHangfire(options => + { + options.UseSimpleAssemblyNameTypeSerializer().UseRecommendedSerializerSettings().UseRedisStorage(redisConnection).UseSerilogLogProvider().UseConsole(); + }); + services.AddHangfireServer(); + services.AddHangfireConsoleExtensions(); + + services.Configure(options => + { + options.ForwardedHeaders = ForwardedHeaders.All; + options.AllowedHosts = this.Configuration.GetValue("AllowedHosts")?.Split(';').ToList(); + }); + } + + /// + /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// + /// + /// + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMetricServer(); + app.UseHttpMetrics(); + + if (!env.IsProduction()) + { + app.UseDeveloperExceptionPage(); + } + + var baseUrl = this.Configuration.GetValue("BaseUrl"); + app.UsePathBase(baseUrl); + app.UseForwardedHeaders(); + + app.UseMiddleware(); + app.UseMiddleware(); + app.UseMiddleware(); + + app.UseRouting(); + app.UseCors(); + + // Exception handler middleware that changes HTTP response codes must be registered after UseHttpMetrics() + // in order to ensure that prometheus-net reports the correct HTTP response status code. + app.UseHttpMetrics(); + + // Set responses secure headers. + ConfigureSecureHeaders(app); + + app.UseResponseCaching(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseHangfireDashboard(options: new DashboardOptions + { + Authorization = new[] { new HangfireDashboardAuthorizationFilter(app.ApplicationServices.GetRequiredService>(), Permissions.SystemAdmin) }, + }); + + var healthPort = this.Configuration.GetValue("HealthChecks:Port"); + app.UseHealthChecks(this.Configuration.GetValue("HealthChecks:LivePath"), healthPort, new HealthCheckOptions + { + Predicate = r => r.Name.Contains("SqlServer"), + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + }); + app.UseHealthChecks(this.Configuration.GetValue("HealthChecks:ReadyPath"), healthPort, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("services") && !r.Tags.Contains("external"), + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + }); + + app.UseEndpoints(config => + { + config.MapControllers(); + config.MapHangfireDashboard(); + + // Enable the /metrics page to export Prometheus metrics + config.MapMetrics(); + }); + + ScheduleHangfireJobs(app.ApplicationServices); + } + + private void ScheduleHangfireJobs(IServiceProvider services) + { + // provide default definition of all jobs. + RecurringJob.AddOrUpdate(nameof(DocumentQueueService.UploadQueuedDocuments), x => x.UploadQueuedDocuments(), Cron.Hourly); + + // override scheduled jobs with configuration. + JobScheduleOptions jobOptions = this.Configuration.GetSection("JobOptions").Get(); + services.GetService().LoadSchedules(jobOptions); + } + + /// + /// Configures the app to to use content security policies. + /// + /// The application builder provider. + private static void ConfigureSecureHeaders(IApplicationBuilder app) + { + app.Use( + async (context, next) => + { + context.Response.Headers.Add("Strict-Transport-Security", "max-age=86400; includeSubDomains"); + context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); + context.Response.Headers.Add("X-XSS-Protection", "1"); + context.Response.Headers.Add("X-Frame-Options", " DENY"); + await next().ConfigureAwait(true); + }); + } + #endregion + } +} diff --git a/source/backend/scheduler/appsettings.Development.json b/source/backend/scheduler/appsettings.Development.json new file mode 100644 index 0000000000..7b96433f86 --- /dev/null +++ b/source/backend/scheduler/appsettings.Development.json @@ -0,0 +1,38 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "System": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error" + } + } + }, + "Pims": { + "Environment": { + "Uri": "http://pims-api-dev:8080", + "Name": "Development" + }, + "Notifications": { + "ThrowExceptions": true + } + }, + "OpenIdConnect": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard" + }, + "Keycloak": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard", + "Audience": "property-services-project-api-dev-only-4700", + "Client": "property-services-project-api-dev-only-4700", + "ServiceAccount": { + "Environment": "dev", + "Integration": "4699" + } + }, + "ConnectionStrings": { + "Redis": "scheduler-redis:6379" + } +} diff --git a/source/backend/scheduler/appsettings.Docker.json b/source/backend/scheduler/appsettings.Docker.json new file mode 100644 index 0000000000..b14ef17c6e --- /dev/null +++ b/source/backend/scheduler/appsettings.Docker.json @@ -0,0 +1,51 @@ +{ + "HealthChecks": { + "Port": "5004" + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error", + "Pims.Api.Handlers": "Trace" + } + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "System": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error" + } + } + }, + "Pims": { + "Environment": { + "Uri": "http://pims-api:8080", + "Name": "Local" + }, + "Notifications": { + "ThrowExceptions": true + } + }, + "OpenIdConnect": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard" + }, + "Keycloak": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard", + "Audience": "property-services-project-api-dev-only-4700", + "Client": "property-services-project-api-dev-only-4700", + "ServiceAccount": { + "Environment": "dev", + "Integration": "4699" + } + }, + "ConnectionStrings": { + "Redis": "scheduler-redis:6379" + } +} diff --git a/source/backend/scheduler/appsettings.Local.json b/source/backend/scheduler/appsettings.Local.json new file mode 100644 index 0000000000..eb5a828660 --- /dev/null +++ b/source/backend/scheduler/appsettings.Local.json @@ -0,0 +1,51 @@ +{ + "HealthChecks": { + "Port": "5004" + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error", + "Pims.Api.Handlers": "Trace" + } + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "System": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error" + } + } + }, + "Pims": { + "Environment": { + "Uri": "http://localhost:5000", + "Name": "Local" + }, + "Notifications": { + "ThrowExceptions": true + } + }, + "OpenIdConnect": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard" + }, + "Keycloak": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard", + "Audience": "property-services-project-api-dev-only-4700", + "Client": "property-services-project-api-dev-only-4700", + "ServiceAccount": { + "Environment": "dev", + "Integration": "4699" + } + }, + "ConnectionStrings": { + "Redis": "localhost:6379" + } +} diff --git a/source/backend/scheduler/appsettings.Test.json b/source/backend/scheduler/appsettings.Test.json new file mode 100644 index 0000000000..736444393b --- /dev/null +++ b/source/backend/scheduler/appsettings.Test.json @@ -0,0 +1,29 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } + }, + "OpenIdConnect": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard" + }, + "Keycloak": { + "Authority": "https://dev.loginproxy.gov.bc.ca/auth/realms/standard", + "ServiceAccount": { + "Environment": "dev" + } + }, + "Pims": { + "Environment": { + "Uri": "http://pims-api-test:8080", + "Name": "Testing" + } + }, + "ConnectionStrings": { + "Redis": "scheduler-redis:6379" + } +} diff --git a/source/backend/scheduler/appsettings.Uat.json b/source/backend/scheduler/appsettings.Uat.json new file mode 100644 index 0000000000..279ae8c1a7 --- /dev/null +++ b/source/backend/scheduler/appsettings.Uat.json @@ -0,0 +1,29 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } + }, + "OpenIdConnect": { + "Authority": "https://test.loginproxy.gov.bc.ca/auth/realms/standard" + }, + "Keycloak": { + "Authority": "https://test.loginproxy.gov.bc.ca/auth/realms/standard", + "ServiceAccount": { + "Environment": "test" + } + }, + "Pims": { + "Environment": { + "Uri": "http://pims-api-uat:8080", + "Name": "Testing" + } + }, + "ConnectionStrings": { + "Redis": "scheduler-redis:6379" + } +} diff --git a/source/backend/scheduler/appsettings.json b/source/backend/scheduler/appsettings.json new file mode 100644 index 0000000000..8bf86b8d30 --- /dev/null +++ b/source/backend/scheduler/appsettings.json @@ -0,0 +1,132 @@ +{ + "BaseUrl": "/scheduler", + "HealthChecks": { + "Port": "8080", + "LivePath": "/health/live", + "ReadyPath": "/health/ready", + "ApiMetrics": { + "Enabled": true, + "Period": 1 + } + }, + "Swagger": { + "RoutePrefix": "scheduler-docs", + "RouteTemplate": "/scheduler-docs/swagger/{documentname}/swagger.json", + "EndpointPath": "/scheduler-docs/swagger/{0}/swagger.json" + }, + "https_port": 443, + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System.Net.Http.HttpClient": "Error", + "HealthChecks.UI": "Error" + } + }, + "WriteTo": [ + { + "Name": "Hangfire", + "Args": { + "restrictedToMinimumLevel": "Debug" + } + } + ], + "Enrich": [ + "FromLogContext", + "WithMachineName", + "WithThreadId", + "WithHangfireContext" + ] + }, + "Hangfire": { + "Dashboard": { + "AppPath": "/", + "StatsPollingInterval": 2000 + }, + "Server": { + "HeartbeatInterval": "00:00:30", + "Queues": [ "default" ], + "SchedulePollingInterval": "00:00:15", + "ServerCheckInterval": "00:05:00", + "ServerName": null, + "ServerTimeout": "00:05:00", + "ShutdownTimeout": "00:00:15", + "WorkerCount": 20 + } + }, + "JobOptions": { + "Schedules": [ + { + "JobId": "UploadQueuedDocuments", + "IsEnabled": true, + "Cron": "*/5 * * * *" + } + ] + }, + "AllowedHosts": "*", + "ContentSecurityPolicy": { + "Base": "'none'", + "DefaultSource": "'none'", + "ScriptSource": "'none'", + "ConnectSource": "'none'", + "ImageSource": "'none'", + "StyleSource": "'none'", + "FormAction": "'none'", + "FontSource": "'none'", + "FrameSource": "'none'", + "FrameAncestors": "'none'" + }, + "Keycloak": { + "Authority": "https://loginproxy.gov.bc.ca/auth/realms/standard", + "Audience": "property-services-project-api-4380", + "Secret": "[USE SECRETS]", + "Client": "property-services-project-api-4380", + "ServiceAccount": { + "Authority": "https://loginproxy.gov.bc.ca/auth/realms/standard", + "Audience": "service-account-team-1295-4381", + "Secret": "[USE SECRETS]", + "Client": "service-account-team-1295-4381", + "API": "https://api.loginproxy.gov.bc.ca/api/v1", + "Integration": "4379", + "Environment": "prod" + } + }, + "OpenIdConnect": { + "Authority": "https://loginproxy.gov.bc.ca/auth/realms/standard", + "Login": "/protocol/openid-connect/auth", + "Logout": "/protocol/openid-connect/logout", + "Register": "/protocol/openid-connect/registrations", + "Token": "/protocol/openid-connect/token", + "TokenIntrospect": "/protocol/openid-connect/token/introspect", + "UserInfo": "/protocol/openid-connect/userinfo" + }, + "OpenApiInfo": { + "Description": "PSP Scheduler service", + "Title": "PIMS scheduler", + "Contact": { + "Email": "support@pims.gov.bc.ca", + "Name": "Support" + }, + "License": { + "Name": "APACHE", + "Url": "https://github.com/bcgov/PSP/blob/dev/LICENSE" + } + }, + "Serialization": { + "Json": { + "PropertyNameCaseInsensitive": true, + "PropertyNamingPolicy": "CamelCase", + "IgnoreNullValues": true + } + }, + "Pims": { + "Environment": { + "Uri": "http://pims-api-prod:8080" + } + }, + "ConnectionStrings": { + "Redis": "scheduler-redis:6379" + } +} diff --git a/source/backend/scheduler/omnisharp.json b/source/backend/scheduler/omnisharp.json new file mode 100644 index 0000000000..8982c08dc5 --- /dev/null +++ b/source/backend/scheduler/omnisharp.json @@ -0,0 +1,5 @@ +{ + "RoslynExtensionsOptions": { + "enableAnalyzersSupport": true + } +} diff --git a/source/backend/scheduler/tests/.editorconfig b/source/backend/scheduler/tests/.editorconfig new file mode 100644 index 0000000000..3c1051119d --- /dev/null +++ b/source/backend/scheduler/tests/.editorconfig @@ -0,0 +1,72 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +end_of_line = lf +charset = utf-8 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +indent_size = 2 + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[*.env] +insert_final_newline = false + +[{Makefile,**.mk}] +# Use tabs for indentation (Makefiles require tabs) +indent_style = tab + +[*.cs] +indent_size = 4 + + +# Test files +# SA1515 Single-line comment should be preceded by blank line +dotnet_diagnostic.SA1515.severity = none +# SA1513: Closing brace should be followed by blank line +dotnet_diagnostic.SA1513.severity = none +# SA1633 The file header is missing or not located at the top of the file +dotnet_diagnostic.SA1633.severity = none +# SA1200 Using directive should appear within a namespace declaration +dotnet_diagnostic.SA1200.severity = none +# SA1124 Do not use regions +dotnet_diagnostic.SA1124.severity = none +# SA1201 A constructor should not follow a property +dotnet_diagnostic.SA1201.severity = none +# SA1309 Field 'X' should not begin with an underscore +dotnet_diagnostic.SA1309.severity = none +# SA1117 The parameters should all be placed on the same line or each parameter should be placed on its own line. +dotnet_diagnostic.SA1117.severity = none + +# -- Set to 'error' before running formatter +# dotnet format --severity error --exclude entities/ef/** --exclude entities/PimsBaseContext.cs +# SA1208: Using directive for X should appear before directive for Y +dotnet_diagnostic.SA1208.severity = warning +# SA1121 Use built-in type alias +dotnet_diagnostic.SA1121.severity = warning +# SA1413 Use trailing comma in multi-line initializers. +dotnet_diagnostic.SA1413.severity = warning +# SA1122 Use string.Empty for empty strings +dotnet_diagnostic.SA1122.severity = warning +# SA1518 Code should not contain blank lines at the end of the file. +dotnet_diagnostic.SA1518.severity = warning +# SA1101 Prefix local calls with this +dotnet_diagnostic.SA1101.severity = warning +# SA1507 Code should not contain multiple blank lines in a row +dotnet_diagnostic.SA1507.severity = warning +# SA1127 Generic type constraints should be on their own line +dotnet_diagnostic.SA1127.severity = warning +# SA1002 Semicolons should be followed by a space. +dotnet_diagnostic.SA1002.severity = warning +# SA1009 Closing parenthesis should not be preceded by a space. +dotnet_diagnostic.SA1009.severity = warning +# SA1508 A closing brace should not be preceded by a blank line +dotnet_diagnostic.SA1508.severity = warning +# SA1005 Single line comment should begin with a space. +dotnet_diagnostic.SA1005.severity = warning \ No newline at end of file diff --git a/source/backend/scheduler/tests/.gitignore b/source/backend/scheduler/tests/.gitignore new file mode 100644 index 0000000000..5923334c0e --- /dev/null +++ b/source/backend/scheduler/tests/.gitignore @@ -0,0 +1,49 @@ +# Environment variables +.env +# Build +.obj + +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Rider +.idea + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio 2015 +.vs/ + +# SonarQube +.sonarqube/ diff --git a/source/backend/scheduler/tests/Directory.Build.props b/source/backend/scheduler/tests/Directory.Build.props new file mode 100644 index 0000000000..4357d5e289 --- /dev/null +++ b/source/backend/scheduler/tests/Directory.Build.props @@ -0,0 +1,9 @@ + + + net8.0 + 9.0 + + + + + diff --git a/tools/cicd/scripts/gen-env-files.sh b/tools/cicd/scripts/gen-env-files.sh index eec4ebed37..9e6c5a602c 100755 --- a/tools/cicd/scripts/gen-env-files.sh +++ b/tools/cicd/scripts/gen-env-files.sh @@ -60,6 +60,34 @@ Cdogs__ServiceClientId= Cdogs__ServiceClientSecret=" >> ./source/backend/api/.env fi +# Proxy +if test -f "./source/backend/proxy/.env"; then + echo "./source/backend/proxy/.env exists" +else +echo \ +"ASPNETCORE_ENVIRONMENT=Docker +ASPNETCORE_URLS=http://*:5002 + +TZ=America/Los_Angeles + +Keycloak__Secret= +Keycloak__ServiceAccount__Secret=" >> ./source/backend/proxy/.env +fi + +# Scheduler +if test -f "./source/backend/scheduler/.env"; then + echo "./source/backend/scheduler/.env exists" +else +echo \ +"ASPNETCORE_ENVIRONMENT=Docker +ASPNETCORE_URLS=http://*:8057 + +TZ=America/Los_Angeles + +Keycloak__Secret= +Keycloak__ServiceAccount__Secret=" >> ./source/backend/scheduler/.env +fi + # DAL DB migration if test -f "./source/backend/dal/.env"; then echo "./source/backend/dal/.env exists" From 2fe0a65208db5b46e7c1c2ee14cc9d59227dd2e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 04:17:51 +0000 Subject: [PATCH 04/44] CI: Bump version to v5.7.0-94.22 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index a5dcdb267d..e92b4316e0 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-94.21 + 5.7.0-94.22 5.7.0.94 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 6bd75d0d67..f27b065948 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-94.21", + "version": "5.7.0-94.22", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 503faf552377685322861d706daeeb7179c3aa78 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Thu, 5 Dec 2024 14:44:34 -0800 Subject: [PATCH 05/44] Bump version to IS95 (#4518) --- source/backend/api/Pims.Api.csproj | 4 ++-- source/frontend/package-lock.json | 4 ++-- source/frontend/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index e92b4316e0..5be3058cef 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,8 +2,8 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-94.22 - 5.7.0.94 + 5.7.0-95.0 + 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} net8.0 diff --git a/source/frontend/package-lock.json b/source/frontend/package-lock.json index e92f9a5885..c7a32606b0 100644 --- a/source/frontend/package-lock.json +++ b/source/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "5.7.0-94.0", + "version": "5.7.0-95.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "5.7.0-94.0", + "version": "5.7.0-95.0", "dependencies": { "@bcgov/bc-sans": "1.0.1", "@bcgov/design-tokens": "3.0.0-rc1", diff --git a/source/frontend/package.json b/source/frontend/package.json index f27b065948..c6e2230513 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-94.22", + "version": "5.7.0-95.0", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From a95fc629f1601009b6213f8fb97b60400587c4c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:44:59 +0000 Subject: [PATCH 06/44] CI: Bump version to v5.7.0-95.1 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 5be3058cef..8be5a7914e 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.0 + 5.7.0-95.1 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index c6e2230513..d3a9221b01 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.0", + "version": "5.7.0-95.1", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From b5d9fa730112b87d434ca3b9055bc26d87562c40 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 6 Dec 2024 12:17:46 -0700 Subject: [PATCH 07/44] =?UTF-8?q?PSP-9569=20:=20H=20120=20document-=20Gene?= =?UTF-8?q?rated=20document=20shows=20full=20names,=20inste=E2=80=A6=20(#4?= =?UTF-8?q?519)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Herrera --- ...nsationRequisitionDetailView.test.tsx.snap | 12 +++-- ...eCompensationRequisitionForm.test.tsx.snap | 12 +++-- .../src/mocks/acquisitionFiles.mock.ts | 54 +++++++++++++++++-- .../src/models/generate/GenerateProject.ts | 6 +-- .../GenerateAcquisitionFile.test.ts | 13 +++++ 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap b/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap index 660b7f0316..9f20af92f5 100644 --- a/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap @@ -816,7 +816,9 @@ exports[`Compensation Detail View Component > renders as expected 1`] = `
+ > + FERRIES +
renders as expected 1`] = `
+ > + SURVEY +
renders as expected 1`] = `
+ > + HWY PLAN +
renders as expected 1`]
+ > + FERRIES +
renders as expected 1`]
+ > + SURVEY +
renders as expected 1`]
+ > + HWY PLAN +
{ expect(file.file_name).toBe(''); expect(file.file_number).toBe(''); }); + it('Can generate a file with no primary owner', () => { const acqFile = mockAcquisitionFileResponse(1, 'test', 1); acqFile.acquisitionFileOwners = acqFile?.acquisitionFileOwners @@ -30,6 +31,18 @@ describe('GenerateFile tests', () => { expect(file.primary_owner?.owner_string).toBe(''); }); + it('Can generate a file with a project', () => { + const acqFile = mockAcquisitionFileResponse(1, 'test', 1); + const file = new Api_GenerateAcquisitionFile({ + file: acqFile, + interestHolders: [], + }); + expect(file.project).not.toBeNull(); + expect(file.project.cost_type).toBe('HWY PLAN'); + expect(file.project.business_function).toBe('FERRIES'); + expect(file.project.work_activity).toBe('SURVEY'); + }); + it('Can generate a file with a primary owner', () => { const acqFile = mockAcquisitionFileResponse(1, 'test', 1); const file = new Api_GenerateAcquisitionFile({ From bf7f5e2ce8363c93998ae3211b681e863d87ab7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:18:12 +0000 Subject: [PATCH 08/44] CI: Bump version to v5.7.0-95.2 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 8be5a7914e..a59be2f6e6 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.1 + 5.7.0-95.2 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index d3a9221b01..4304032bf4 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.1", + "version": "5.7.0-95.2", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 6c79bc35fa35f82a8ad1f82d845ef561d9156d29 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Fri, 6 Dec 2024 11:23:58 -0800 Subject: [PATCH 09/44] Github action fixes (#4520) --- .github/workflows/ci-cd-pims-dev.yml | 10 +++++----- .github/workflows/retag-test-to-uat-argo.yml | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-cd-pims-dev.yml b/.github/workflows/ci-cd-pims-dev.yml index 871a435175..6cfb198229 100644 --- a/.github/workflows/ci-cd-pims-dev.yml +++ b/.github/workflows/ci-cd-pims-dev.yml @@ -115,11 +115,11 @@ jobs: oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-proxy-$DESTINATION oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-proxy-$DESTINATION - name: Deploy scheduler microservice - shell: bash - run: | - oc tag pims-scheduler:latest-$DESTINATION pims-scheduler:$DESTINATION - oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION - oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION + shell: bash + run: | + oc tag pims-scheduler:latest-$DESTINATION pims-scheduler:$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout restart deployment/pims-scheduler-$DESTINATION + oc -n $DEPLOYMENT_NAMESPACE rollout status --timeout=600s deployment/pims-scheduler-$DESTINATION - name: Deploy mayan shell: bash run: | diff --git a/.github/workflows/retag-test-to-uat-argo.yml b/.github/workflows/retag-test-to-uat-argo.yml index 24a224a8a2..37229edaaf 100644 --- a/.github/workflows/retag-test-to-uat-argo.yml +++ b/.github/workflows/retag-test-to-uat-argo.yml @@ -178,6 +178,7 @@ jobs: oc tag pims-app:uat pims-app:v${VERSION}-master oc tag pims-api:uat pims-api:v${VERSION}-master oc tag pims-proxy:uat pims-proxy:v${VERSION}-master + oc tag pims-scheduler:uat pims-scheduler:v${VERSION}-master oc tag mayan-bcgov:uat mayan-bcgov:v${VERSION}-master ci-cd-end-notification: From 069d2dfd1c44a2894d79fe39f51070469702a51e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:24:24 +0000 Subject: [PATCH 10/44] CI: Bump version to v5.7.0-95.3 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index a59be2f6e6..98f5891794 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.2 + 5.7.0-95.3 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 4304032bf4..f63c4bac23 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.2", + "version": "5.7.0-95.3", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 310729b8576686ca12fd39bc2bee35995ad0a148 Mon Sep 17 00:00:00 2001 From: Sue Tairaku <42981334+stairaku@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:49:56 -0800 Subject: [PATCH 11/44] Lease Historical file alignment fix (#4521) Co-authored-by: Alejandro Sanchez --- .../src/features/mapSideBar/lease/common/LeaseHeader.tsx | 2 +- .../lease/common/__snapshots__/LeaseHeader.test.tsx.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx b/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx index b52f7ec2ab..066c2cc083 100644 --- a/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx +++ b/source/frontend/src/features/mapSideBar/lease/common/LeaseHeader.tsx @@ -119,7 +119,7 @@ export const LeaseHeader: React.FC = ({ lease, lastUpdatedBy export default LeaseHeader; const HistoricalRow = styled(Row)` - margin-right: 1rem; + margin-right: -1.25rem; `; const Container = styled.div` diff --git a/source/frontend/src/features/mapSideBar/lease/common/__snapshots__/LeaseHeader.test.tsx.snap b/source/frontend/src/features/mapSideBar/lease/common/__snapshots__/LeaseHeader.test.tsx.snap index 0ccc122371..9c526b463a 100644 --- a/source/frontend/src/features/mapSideBar/lease/common/__snapshots__/LeaseHeader.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/lease/common/__snapshots__/LeaseHeader.test.tsx.snap @@ -236,7 +236,7 @@ exports[`LeaseHeader component > renders as expected when no data is provided 1` } .c4 { - margin-right: 1rem; + margin-right: -1.25rem; } .c0 { From cb54fec322f6bf25396d32317a59d0c1a7c61050 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:50:20 +0000 Subject: [PATCH 12/44] CI: Bump version to v5.7.0-95.4 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 98f5891794..505e0c6096 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.3 + 5.7.0-95.4 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index f63c4bac23..07e2578f51 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.3", + "version": "5.7.0-95.4", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 13d66bb209bfe38f7bcb7eaa782c4247eb74ab2c Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 9 Dec 2024 10:55:51 -0800 Subject: [PATCH 13/44] PSP-9517 Remove "cannot determine" region dropdown from User Management screen under Admin Tools (#4522) * Remove "Cannot determine" region from user management page * Test updates --- .../admin/users/ManageUsersPage.test.tsx | 143 +- .../ManageUsersPage.test.tsx.snap | 2214 ++++++++++++++++- .../admin/users/components/UsersFilterBar.tsx | 4 +- 3 files changed, 2298 insertions(+), 63 deletions(-) diff --git a/source/frontend/src/features/admin/users/ManageUsersPage.test.tsx b/source/frontend/src/features/admin/users/ManageUsersPage.test.tsx index 0587d72272..0900690215 100644 --- a/source/frontend/src/features/admin/users/ManageUsersPage.test.tsx +++ b/source/frontend/src/features/admin/users/ManageUsersPage.test.tsx @@ -1,32 +1,22 @@ -import { useKeycloak } from '@react-keycloak/web'; -import { fireEvent, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { createMemoryHistory } from 'history'; -import { act } from 'react-dom/test-utils'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import fileDownload from 'js-file-download'; import * as actionTypes from '@/constants/actionTypes'; import * as API from '@/constants/API'; +import { mockLookups } from '@/mocks/lookups.mock'; import { getMockPagedUsers } from '@/mocks/user.mock'; -import { ILookupCode, lookupCodesSlice } from '@/store/slices/lookupCodes'; +import { lookupCodesSlice } from '@/store/slices/lookupCodes'; import { networkSlice } from '@/store/slices/network/networkSlice'; import { prettyFormatDateTime } from '@/utils'; -import { fillInput, render, userEvent } from '@/utils/test-utils'; +import { act, fillInput, render, userEvent, waitForElementToBeRemoved } from '@/utils/test-utils'; import { ManageUsersPage } from './ManageUsersPage'; const history = createMemoryHistory(); history.push('admin'); -const mockStore = configureMockStore([thunk]); - -const lCodes = { - lookupCodes: [ - { id: 1, name: 'roleVal', code: '', isDisabled: false, type: API.ROLE_TYPES }, - { id: 2, name: 'disabledRole', code: '', isDisabled: true, type: API.ROLE_TYPES }, - ] as ILookupCode[], -}; + const mockAxios = new MockAdapter(axios); vi.mock('react-router-dom', async importOriginal => { @@ -36,88 +26,123 @@ vi.mock('react-router-dom', async importOriginal => { useRouteMatch: () => ({ url: '/admin', path: '/admin' }), }; }); -const getStore = (includeDate?: boolean) => - mockStore({ - [lookupCodesSlice.name]: lCodes, - [networkSlice.name]: { - [actionTypes.GET_USERS]: { - isFetching: false, + +vi.mock('js-file-download', () => { + return { + __esModule: true, + default: vi.fn(), + }; +}); + +describe('Manage Users Component', () => { + let store: any; + + const setup = () => { + const component = render(, { + store: store, + }); + return { ...component }; + }; + + beforeEach(() => { + mockAxios.onPost().reply(200, getMockPagedUsers()); + store = { + [lookupCodesSlice.name]: { lookupCodes: mockLookups }, + [networkSlice.name]: { + [actionTypes.GET_USERS]: { + isFetching: false, + }, }, - }, + }; }); -describe('Manage Users Component', () => { afterEach(() => { vi.restoreAllMocks(); mockAxios.reset(); }); - const testRender = (store: any) => { - mockAxios.onPost().reply(200, getMockPagedUsers()); - const component = render(, { store: store }); - return { ...component }; - }; it('Snapshot matches', async () => { - const { container, getByTitle } = testRender(getStore()); + const { asFragment, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); - expect(container.firstChild).toMatchSnapshot(); + expect(asFragment()).toMatchSnapshot(); }); it('Table row count is 5', async () => { - const { container, getByTitle } = testRender(getStore()); + const { container, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); const rows = container.querySelectorAll('.tbody .tr'); expect(rows.length).toBe(5); }); it('displays enabled roles', async () => { - const { queryByText, getByTitle } = testRender(getStore()); + const { queryByText, getByTitle } = setup(); + await waitForElementToBeRemoved(getByTitle('table-loading')); + expect(queryByText('Acquisition functional')).toBeVisible(); + }); + + it('does not display disabled roles', async () => { + store = { + ...store, + [lookupCodesSlice.name]: { + lookupCodes: [ + ...mockLookups, + { + id: 9999, + key: 'foo-bar', + name: 'Disabled role', + description: 'A sample role that is disabled in PIMS', + isDisabled: true, + isPublic: true, + displayOrder: 0, + type: API.ROLE_TYPES, + rowVersion: 1, + }, + ], + }, + }; + const { queryByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); - expect(queryByText('roleVal')).toBeVisible(); + expect(queryByText('Disabled role')).toBeNull(); }); - it('Does not display disabled roles', async () => { - const { queryByText, getByTitle } = testRender(getStore()); + it(`does not display "Cannot determine" region`, async () => { + const { queryByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); - expect(queryByText('disabledRole')).toBeNull(); + expect(queryByText('Cannot determine')).toBeNull(); }); - it('Displays the correct last login time', async () => { - const { getByText, getByTitle } = testRender(getStore(true)); + it('displays the correct last login time', async () => { + const { getByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); expect(getByText(prettyFormatDateTime('2022-06-08T23:24:56.163'))).toBeVisible(); }); - it.skip('downloads data when excel icon clicked', async () => { - const { getByTestId, getByTitle } = testRender(getStore()); + it('triggers a file download when excel icon is clicked', async () => { + const { getByTestId, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); const excelIcon = getByTestId('excel-icon'); mockAxios.onGet().reply(200); await act(async () => { - fireEvent.click(excelIcon); - }); - await waitFor(() => { - expect(mockAxios.history.get.length).toBe(1); + userEvent.click(excelIcon); }); + expect(fileDownload).toHaveBeenCalled(); }); it('can search for users', async () => { - const { container, getByTitle } = testRender(getStore()); + const { container, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); await fillInput(container, 'firstName', 'testUserFirst1'); const searchButton = container.querySelector('#search-button'); mockAxios.onPost().reply(200); await act(async () => { - fireEvent.click(searchButton!); - }); - await waitFor(() => { - expect(mockAxios.history.post.length).toBe(1); + userEvent.click(searchButton!); }); + expect(mockAxios.history.post.length).toBe(1); }); it('search reset works as expected', async () => { - const { getByTitle, getAllByPlaceholderText, container } = testRender(getStore()); + const { getByTitle, getAllByPlaceholderText, container } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); @@ -133,7 +158,7 @@ describe('Manage Users Component', () => { }); it('each row contains a link to the access request details page', async () => { - const { getByText, getByTitle } = testRender(getStore()); + const { getByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); @@ -143,7 +168,7 @@ describe('Manage Users Component', () => { describe('access request actions', () => { it('Enable menu button is disabled', async () => { - const { getAllByText, getByTitle } = testRender(getStore()); + const { getAllByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); @@ -152,7 +177,7 @@ describe('Manage Users Component', () => { }); it('Disable menu button is enabled', async () => { - const { getAllByText, getByTitle } = testRender(getStore()); + const { getAllByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); @@ -163,7 +188,7 @@ describe('Manage Users Component', () => { it('Disable action submits a request', async () => { mockAxios.onPut().reply(200, {}); - const { getAllByText, getByTitle } = testRender(getStore()); + const { getAllByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); @@ -172,15 +197,13 @@ describe('Manage Users Component', () => { userEvent.click(disableButton); }); - await waitFor(() => { - expect(mockAxios.history.put[0].url).toEqual( - '/keycloak/users/e81274eb-a007-4f2e-ada3-2817efcdb0a6', - ); - }); + expect(mockAxios.history.put[0].url).toEqual( + '/keycloak/users/e81274eb-a007-4f2e-ada3-2817efcdb0a6', + ); }); it('Open menu button is enabled', async () => { - const { getAllByText, getByTitle } = testRender(getStore()); + const { getAllByText, getByTitle } = setup(); await waitForElementToBeRemoved(getByTitle('table-loading')); diff --git a/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap b/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap index 9163106748..c94c04c413 100644 --- a/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap +++ b/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap @@ -1,7 +1,2217 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Manage Users Component > Snapshot matches 1`] = ` + +
+
+ .c4.btn { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + padding: 0.4rem 1.2rem; + border: 0.2rem solid transparent; + border-radius: 0.4rem; + text-align: center; + -webkit-text-decoration: none; + text-decoration: none; + font-size: 1.8rem; + font-family: 'BCSans','Noto Sans',Verdana,Arial,sans-serif; + font-weight: 700; + -webkit-letter-spacing: 0.1rem; + -moz-letter-spacing: 0.1rem; + -ms-letter-spacing: 0.1rem; + letter-spacing: 0.1rem; + cursor: pointer; +} + +.c4.btn .Button__value { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c4.btn:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + opacity: 0.8; +} + +.c4.btn:focus { + outline-width: 2px; + outline-style: solid; + outline-color: #2E5DD7; + outline-offset: 2px; + box-shadow: none; +} + +.c4.btn.btn-primary { + color: #FFFFFF; + background-color: #013366; +} + +.c4.btn.btn-primary:hover, +.c4.btn.btn-primary:active, +.c4.btn.btn-primary:focus { + background-color: #1E5189; +} + +.c4.btn.btn-secondary { + color: #013366; + background: none; + border-color: #013366; +} + +.c4.btn.btn-secondary:hover, +.c4.btn.btn-secondary:active, +.c4.btn.btn-secondary:focus { + color: #FFFFFF; + background-color: #013366; +} + +.c4.btn.btn-info { + color: #9F9D9C; + border: none; + background: none; + padding-left: 0.6rem; + padding-right: 0.6rem; +} + +.c4.btn.btn-info:hover, +.c4.btn.btn-info:active, +.c4.btn.btn-info:focus { + color: var(--surface-color-primary-button-hover); + background: none; +} + +.c4.btn.btn-light { + color: #FFFFFF; + background-color: #606060; + border: none; +} + +.c4.btn.btn-light:hover, +.c4.btn.btn-light:active, +.c4.btn.btn-light:focus { + color: #FFFFFF; + background-color: #606060; +} + +.c4.btn.btn-dark { + color: #FFFFFF; + background-color: #474543; + border: none; +} + +.c4.btn.btn-dark:hover, +.c4.btn.btn-dark:active, +.c4.btn.btn-dark:focus { + color: #FFFFFF; + background-color: #474543; +} + +.c4.btn.btn-danger { + color: #FFFFFF; + background-color: #CE3E39; +} + +.c4.btn.btn-danger:hover, +.c4.btn.btn-danger:active, +.c4.btn.btn-danger:focus { + color: #FFFFFF; + background-color: #CE3E39; +} + +.c4.btn.btn-warning { + color: #FFFFFF; + background-color: #FCBA19; + border-color: #FCBA19; +} + +.c4.btn.btn-warning:hover, +.c4.btn.btn-warning:active, +.c4.btn.btn-warning:focus { + color: #FFFFFF; + border-color: #FCBA19; + background-color: #FCBA19; +} + +.c4.btn.btn-link { + font-size: 1.6rem; + font-weight: 400; + color: var(--surface-color-primary-button-default); + background: none; + border: none; + -webkit-text-decoration: none; + text-decoration: none; + min-height: 2.5rem; + line-height: 3rem; + -webkit-box-pack: left; + -webkit-justify-content: left; + -ms-flex-pack: left; + justify-content: left; + -webkit-letter-spacing: unset; + -moz-letter-spacing: unset; + -ms-letter-spacing: unset; + letter-spacing: unset; + text-align: left; + padding: 0; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c4.btn.btn-link:hover, +.c4.btn.btn-link:active, +.c4.btn.btn-link:focus { + color: var(--surface-color-primary-button-hover); + -webkit-text-decoration: underline; + text-decoration: underline; + border: none; + background: none; + box-shadow: none; + outline: none; +} + +.c4.btn.btn-link:disabled, +.c4.btn.btn-link.disabled { + color: #9F9D9C; + background: none; + pointer-events: none; +} + +.c4.btn:disabled, +.c4.btn:disabled:hover { + color: #9F9D9C; + background-color: #EDEBE9; + box-shadow: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + cursor: not-allowed; +} + +.c4.Button .Button__icon { + margin-right: 1.6rem; +} + +.c4.Button--icon-only:focus { + outline: none; +} + +.c4.Button--icon-only .Button__icon { + margin-right: 0; +} + +.c5.c5.btn { + background-color: unset; + border: none; +} + +.c5.c5.btn:hover, +.c5.c5.btn:focus, +.c5.c5.btn:active { + background-color: unset; + outline: none; + box-shadow: none; +} + +.c5.c5.btn svg { + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.c5.c5.btn svg:hover { + -webkit-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; +} + +.c5.c5.btn.btn-primary svg { + color: #013366; +} + +.c5.c5.btn.btn-primary svg:hover { + color: #013366; +} + +.c5.c5.btn.btn-light svg { + color: var(--surface-color-primary-button-default); +} + +.c5.c5.btn.btn-light svg:hover { + color: #CE3E39; +} + +.c5.c5.btn.btn-info svg { + color: var(--surface-color-primary-button-default); +} + +.c5.c5.btn.btn-info svg:hover { + color: var(--surface-color-primary-button-hover); +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + margin-left: 0.5rem; +} + +.c10 { + width: 1.6rem; + height: 1.6rem; +} + +.c9 { + width: 1.6rem; + height: 1.6rem; +} + +.c11 { + margin-top: 0.3rem; +} + +.c12 { + min-width: 5rem; + max-width: 5rem; + margin-left: 1rem; + margin-right: 1rem; + text-align: center; + padding: 0; +} + +.c12:disabled { + background: white; +} + +.c3 { + background-color: #d9eaf7; + border-radius: 0.5rem; +} + +.c0 { + background-color: #fff; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0; +} + +.c6 { + padding: 1.6rem 3.2rem; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + overflow-y: auto; + overflow-x: auto; +} + +.c7 { + padding: 1.5rem; +} + +.c1 { + padding: 1rem 2rem; +} + +.c2 { + text-align: left; +} +
+ class="c0 c1 users-management-page container-fluid" + > +

+ User Management +

+
+
+
+
+
+ Search By: +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + Show active users only + +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ Active +
+ + + + + + +
+
+
+
+
+ IDIR/BCeID +
+ + + + + + +
+
+
+
+
+ First name +
+ + + + + + +
+
+
+
+
+ Last name +
+ + + + + + +
+
+
+
+
+ Email +
+ + + + + + +
+
+
+
+
+ Position +
+ + + + + + +
+
+
+
+
+ User Type +
+
+
+
+ Roles +
+
+
+
+ MoTI region(s) +
+
+
+
+ Last login +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + +
+ +
+ Devin +
+
+ Smith +
+
+ devin.smith@gov.bc.ca +
+
+ pos +
+
+
+ Functional, System Administrator +
+
+
+ Jun 14, 2022 12:24 pm +
+
+ +
+
+
+
+
+
+ + + + + + + + + + +
+ +
+ Manuel +
+
+ Rodriguez +
+
+ manuel.1.rodriguez@gov.bc.ca +
+
+
+
+ Functional, System Administrator +
+
+
+ Jun 8, 2022 12:59 pm +
+
+ +
+
+
+
+
+
+ + + + + + + + + + +
+ +
+ Devashish +
+
+ Bhargava +
+
+ devashish.bhargava@gov.bc.ca +
+
+ Business Analyst +
+
+
+ System Administrator, Functional +
+
+
+ Jun 8, 2022 01:39 pm +
+
+ +
+
+
+
+
+
+ + + + + + + + + + +
+ +
+ Brennan +
+
+ Neitz +
+
+ brennan.neitz@gov.bc.ca +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + +
+ +
+ Alejandro +
+
+ Sanchez +
+
+ alejandro.sanchez@gov.bc.ca +
+
+
+
+
+
+ Jun 8, 2022 04:24 pm +
+
+ +
+
+
+
+
+
+
+ + 1 - 10 of 42 + +
+
+
+ +
+
+ +
+
+
+
+ `; diff --git a/source/frontend/src/features/admin/users/components/UsersFilterBar.tsx b/source/frontend/src/features/admin/users/components/UsersFilterBar.tsx index 9cb0ebb3ca..30bba0935d 100644 --- a/source/frontend/src/features/admin/users/components/UsersFilterBar.tsx +++ b/source/frontend/src/features/admin/users/components/UsersFilterBar.tsx @@ -20,7 +20,9 @@ interface IProps { export const UsersFilterBar: React.FC> = ({ values, onChange }) => { const { getOptionsByType } = useLookupCodeHelpers(); const roles = getOptionsByType(API.ROLE_TYPES); - const regions = getOptionsByType(API.REGION_TYPES); + const regions = getOptionsByType(API.REGION_TYPES).filter( + region => region.label !== 'Cannot determine', + ); return ( From 66081f62aab490954dbdc0d2ef75afd1668ba62d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:56:16 +0000 Subject: [PATCH 14/44] CI: Bump version to v5.7.0-95.5 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 505e0c6096..895f65768d 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.4 + 5.7.0-95.5 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 07e2578f51..c9bb32af8f 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.4", + "version": "5.7.0-95.5", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From e1ab3f16daa67ce7bea9804d8cdf6a1d0fe3cd58 Mon Sep 17 00:00:00 2001 From: Sue Tairaku <42981334+stairaku@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:58:05 -0800 Subject: [PATCH 15/44] Adding requested tooltip on View and Update forms (#4523) --- .../detail/PropertyDetailsTabView.tsx | 7 ++++-- .../update/UpdatePropertyDetailsForm.tsx | 5 ++++- ...datePropertyDetailsContainer.test.tsx.snap | 22 +++++++++++++++++++ .../UpdatePropertyDetailsForm.test.tsx.snap | 22 +++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/detail/PropertyDetailsTabView.tsx b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/detail/PropertyDetailsTabView.tsx index 0d5298555e..2ab10c650d 100644 --- a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/detail/PropertyDetailsTabView.tsx +++ b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/detail/PropertyDetailsTabView.tsx @@ -124,7 +124,10 @@ export const PropertyDetailsTabView: React.FunctionComponent {property?.isALR ? 'Yes' : 'No'} - + {booleanToYesNoUnknownString(property?.isRwyBeltDomPatent)} {property?.propertyType?.description} @@ -155,7 +158,7 @@ export const PropertyDetailsTabView: React.FunctionComponent - + {pphStatusTypeCodeDesc ?? 'Unknown'} {isHighwayRoad && ( diff --git a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/UpdatePropertyDetailsForm.tsx b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/UpdatePropertyDetailsForm.tsx index fe6f2e5b1f..4686392099 100644 --- a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/UpdatePropertyDetailsForm.tsx +++ b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/UpdatePropertyDetailsForm.tsx @@ -226,7 +226,10 @@ export const UpdatePropertyDetailsForm: React.FunctionComponent< {values.isALR ? 'Yes' : 'No'} - + diff --git a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/__snapshots__/UpdatePropertyDetailsContainer.test.tsx.snap b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/__snapshots__/UpdatePropertyDetailsContainer.test.tsx.snap index 9bf2c14ce3..ab1d0544c1 100644 --- a/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/__snapshots__/UpdatePropertyDetailsContainer.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/property/tabs/propertyDetails/update/__snapshots__/UpdatePropertyDetailsContainer.test.tsx.snap @@ -1468,6 +1468,28 @@ exports[`UpdatePropertyDetailsContainer component > renders as expected 1`] = ` class="c5" > Railway belt / Dominion patent: + + + + + + +
renders as expected 1`] = ` class="c4" > Railway belt / Dominion patent: + + + + + + +
Date: Mon, 9 Dec 2024 19:58:33 +0000 Subject: [PATCH 16/44] CI: Bump version to v5.7.0-95.6 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 895f65768d..00a19dcc2b 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.5 + 5.7.0-95.6 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index c9bb32af8f..9b096d3c6c 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.5", + "version": "5.7.0-95.6", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 5e4f8967cec9dc183eb026de2b0893b415eb528e Mon Sep 17 00:00:00 2001 From: Doug Filteau Date: Mon, 9 Dec 2024 13:14:11 -0800 Subject: [PATCH 17/44] IS-95.00 Database Schema (#4511) * IS-95.00 Database Schema PSP_PIMS | Development Sprint: S95.00 | Design Sprint: 94 | Date: 2024-Dec-03 - Altered tables: - PIMS_ACQUISITION_FILE - PIMS_ACQUISITION_FILE_HIST - PIMS_COMPENSATION_REQUISITION - PIMS_COMPENSATION_REQUISITION_HIST - PIMS_EXPROPRIATION_PAYMENT - PIMS_EXPROPRIATION_PAYMENT_HIST - Added build script: - 007_PSP_PIMS_COMPENSATION_REQUISITION_HIST_Add.sql - Altered build script: - 007_PSP_PIMS_ACQUISITION_FILE_HIST_Add.sql - Altered test script: - 043_PIMS_ACQUISITION_FILE.sql - Requires additional metadata to meet standards * Fixed alter down * Aligning to LATEST --------- Co-authored-by: Manuel Rodriguez Co-authored-by: Alejandro Sanchez --- .../Alter Down/002_PSP_PIMS_Alter_Down.sql | 982 +- .../010_DML_PIMS_STATIC_VARIABLE_VERSION.sql | 2 +- .../999_DML_Delete_FILE_NUMBER_Alter_Down.sql | 63 - .../Alter Down/Schema Compare.zip | Bin 3082282 -> 3082516 bytes .../PSP_PIMS_LATEST/Alter Down/master.sql | 13 +- .../Alter Up/002_PSP_PIMS_Alter_Up.sql | 986 +- .../010_DML_PIMS_STATIC_VARIABLE_VERSION.sql | 2 +- .../Alter Up/Schema Compare.zip | Bin 3082282 -> 3082479 bytes .../PSP_PIMS_LATEST/Alter Up/master.sql | 13 +- .../Build/002_PSP_PIMS_Build.sql | 277 +- ...007_PSP_PIMS_ACQUISITION_FILE_HIST_Add.sql | 3 +- ...PIMS_COMPENSATION_REQUISITION_HIST_Add.sql | 10 + .../Build/020_DML_PIMS_STATIC_VARIABLE.sql | 2 +- .../PSP_PIMS_LATEST/Drop/01_PSP_PIMS_Drop.sql | 172 +- .../PSP_PIMS_LATEST/PIMS PDM S94.00.png | Bin 6573943 -> 0 bytes .../PSP_PIMS_LATEST/PIMS PDM S95.00.png | Bin 0 -> 9699697 bytes ...nge Log.txt => PIMS S95.00 Change Log.txt} | 16 + .../Test Data/031_PIMS_ACQUISITION_FILE.sql | 20 +- .../Test Data/043_PIMS_ACQUISITION_FILE.sql | 32 +- .../Alter Down/002_PSP_PIMS_Alter_Down.sql | 995 + .../010_DML_PIMS_STATIC_VARIABLE_VERSION.sql} | 31 +- .../Alter Down/Schema Compare.zip | Bin 0 -> 3082516 bytes .../PSP_PIMS_S95_00/Alter Down/master.sql | 44 + .../Alter Up/002_PSP_PIMS_Alter_Up.sql | 937 + .../010_DML_PIMS_STATIC_VARIABLE_VERSION.sql | 50 + .../Alter Up/Schema Compare.zip | Bin 0 -> 3082479 bytes .../PSP_PIMS_S95_00/Alter Up/master.sql | 44 + .../Build/000_PSP_PIMS_ETL_SCHEMA_Build.sql | 38 + .../Build/001_PSP_PIMS_PMBC_SCHEMA_Build.sql | 43 + .../Build/002_PSP_PIMS_Build.sql | 41480 ++++++++++++++++ .../Build/003_PSP_PIMS_ETL_Tables_Build.sql | 322 + .../Build/004_PSP_PIMS_PMBC_Tables_Build.sql | 111 + .../005_PSP_PIMS_PROPERTY_SPATIAL_IDX.sql | 65 + .../Build/006_PSP_PIMS_PMBC_INDICES.sql | 81 + ...007_PSP_PIMS_ACQUISITION_FILE_HIST_Add.sql | 11 + ...PIMS_COMPENSATION_REQUISITION_HIST_Add.sql | 10 + .../Build/007_PSP_PIMS_LEASE_HIST_Add.sql | 15 + ...07_PSP_PIMS_PROPERTY_ACTIVITY_HIST_Add.sql | 20 + .../Build/007_PSP_PIMS_PROPERTY_HIST_Add.sql | 24 + ...008_PSP_PIMS_PROPERTY_ACTIVITY_Trigger.sql | 36 + .../009_DML_Set_Concurrency_Control_NULL.sql | 303 + .../Build/020_DML_PIMS_STATIC_VARIABLE.sql | 19 + .../Build/030_DML_PIMS_ADDRESS_USAGE_TYPE.sql | 29 + .../Build/031_DML_PIMS_AREA_UNIT_TYPE.sql | 17 + .../032_DML_PIMS_PROPERTY_TENURE_TYPE.sql | 38 + .../040_DML_PIMS_CONTACT_METHOD_TYPE.sql | 20 + .../Build/041_DML_PIMS_ORGANIZATION_TYPE.sql | 22 + ...42_DML_PIMS_ACCESS_REQUEST_STATUS_TYPE.sql | 20 + .../Build/044_DML_PIMS_PROPERTY_TYPE.sql | 26 + .../Build/045_DML_PIMS_COUNTRY.sql | 20 + .../Build/046_DML_PIMS_PROVINCE_STATE.sql | 81 + .../Build/047_DML_PIMS_REGION.sql | 21 + .../Build/048_DML_PIMS_DISTRICT.sql | 26 + .../Build/049_DML_PIMS_DATA_SOURCE_TYPE.sql | 47 + .../050_DML_PIMS_ORG_IDENTIFIER_TYPE.sql | 17 + .../Build/051_DML_PIMS_ROLE.sql | 20 + .../052_DML_PIMS_PROPERTY_STATUS_TYPE.sql | 20 + .../053_DML_PIMS_LEASE_PAY_RVBL_TYPE.sql | 16 + .../Build/054_DML_PIMS_LEASE_LICENSE_TYPE.sql | 46 + .../Build/056_DML_PIMS_LEASE_PURPOSE_TYPE.sql | 92 + .../Build/057_DML_PIMS_LEASE_PROGRAM_TYPE.sql | 44 + .../058_DML_PIMS_LEASE_PMT_FREQ_TYPE.sql | 23 + .../061_DML_PIMS_SURPLUS_DECLARATION_TYPE.sql | 17 + ...062_DML_PIMS_PROPERTY_IMPROVEMENT_TYPE.sql | 16 + .../064_DML_PIMS_SECURITY_DEPOSIT_TYPE.sql | 16 + .../Build/065_DML_PIMS_LESSOR_TYPE.sql | 16 + .../066_DML_PIMS_LEASE_INITIATOR_TYPE.sql | 16 + ...067_DML_PIMS_LEASE_RESPONSIBILITY_TYPE.sql | 16 + .../068_DML_PIMS_LEASE_PERIOD_STATUS_TYPE.sql | 16 + ...070_DML_PIMS_LEASE_PAYMENT_STATUS_TYPE.sql | 17 + ...071_DML_PIMS_LEASE_PAYMENT_METHOD_TYPE.sql | 20 + .../Build/072_DML_PIMS_INSURANCE_TYPE.sql | 21 + .../Build/073_DML_PIMS_LEASE_STATUS_TYPE.sql | 23 + .../Build/074_DML_PIMS_CLAIM.sql | 331 + .../Build/075_DML_PIMS_ROLE_CLAIM.sql | 406 + .../Build/076_DML_PIMS_ORGANIZATION.sql | 36 + .../Build/077_DML_PIMS_TENANT.sql | 13 + .../078_DML_PIMS_PROPERTY_ANOMALY_TYPE.sql | 27 + .../Build/079_DML_PIMS_PROPERTY_ROAD_TYPE.sql | 36 + .../Build/081_DML_PIMS_VOLUMETRIC_TYPE.sql | 16 + .../Build/082_DML_PIMS_VOLUME_UNIT_TYPE.sql | 16 + ...083_DML_PIMS_RESEARCH_FILE_STATUS_TYPE.sql | 20 + .../084_DML_PIMS_REQUEST_SOURCE_TYPE.sql | 24 + .../085_DML_PIMS_RESEARCH_PURPOSE_TYPE.sql | 19 + ...86_DML_PIMS_PROP_RESEARCH_PURPOSE_TYPE.sql | 36 + .../Build/087_DML_PIMS_PPH_STATUS_TYPE.sql | 19 + .../088_DML_PIMS_DOCUMENT_STATUS_TYPE.sql | 24 + ..._DML_PIMS_ACQUISITION_FILE_STATUS_TYPE.sql | 21 + .../Build/090_DML_PIMS_ACQUISITION_TYPE.sql | 19 + .../091_DML_PIMS_ACQUISITION_FUNDING_TYPE.sql | 25 + ...092_DML_PIMS_ACQ_PHYS_FILE_STATUS_TYPE.sql | 17 + .../093_DML_PIMS_ACQ_FL_TEAM_PROFILE_TYPE.sql | 20 + .../Build/095_DML_PIMS_SURVEY_PLAN_TYPE.sql | 44 + .../Build/096_DML_PIMS_LAND_SURVEYOR_TYPE.sql | 17 + .../Build/098_DML_PIMS_FENCE_TYPE.sql | 21 + .../Build/099_DML_PIMS_LETTER_TYPE.sql | 15 + .../Build/101_DML_PIMS_COST_TYPE_CODE.sql | 810 + .../102_DML_PIMS_CHART_OF_ACCOUNTS_CODE.sql | 814 + .../103_DML_PIMS_BUSINESS_FUNCTION_CODE.sql | 53 + .../104_DML_PIMS_YEARLY_FINANCIAL_CODE.sql | 87 + .../105_DML_PIMS_RESPONSIBILITY_CODE.sql | 286 + ...6_DML_SET_PIMS_RESPONSIBILITY_CODE_SEQ.sql | 18 + .../107_DML_PIMS_FINANCIAL_ACTIVITY_CODE.sql | 43 + .../Build/108_DML_PIMS_WORK_ACTIVITY_CODE.sql | 351 + .../109_DML_PIMS_LEASE_STAKEHOLDER_TYPE.sql | 21 + .../110_DML_PIMS_PROJECT_STATUS_TYPE.sql | 20 + .../111_DML_PIMS_BUSINESS_FUNCTION_CODE.sql | 58 + .../Build/112_DML_PIMS_COST_TYPE_CODE.sql | 816 + .../Build/113_DML_PIMS_WORK_ACTIVITY_CODE.sql | 19 + .../114_DML_PIMS_CONSULTATION_STATUS_TYPE.sql | 18 + .../Build/115_DML_PIMS_CONSULTATION_TYPE.sql | 50 + .../Build/116_DML_PIMS_TAKE_TYPE.sql | 18 + .../Build/117_DML_PIMS_TAKE_STATUS_TYPE.sql | 17 + .../118_DML_PIMS_TAKE_SITE_CONTAM_TYPE.sql | 17 + .../120_DML_PIMS_ACQ_CHKLST_SECTION_TYPE.sql | 20 + .../121_DML_PIMS_ACQ_CHKLST_ITEM_TYPE.sql | 61 + .../122_DML_PIMS_PROJECT_PERSON_ROLE_TYPE.sql | 19 + .../Build/123_DML_PIMS_FORM_TYPE.sql | 26 + .../Build/124_DML_PIMS_AGREEMENT_TYPE.sql | 18 + .../Build/125_DML_PIMS_USER_TYPE.sql | 16 + .../Build/126_DML_PIMS_H120_CATEGORY.sql | 45 + .../127_DML_PIMS_DOCUMENT_FORMAT_TYPE.sql | 33 + .../Build/128_DML_PIMS_LAND_ACT_TYPE.sql | 25 + .../129_DML_PIMS_DOCUMENT_CATEGORY_TYPE.sql | 20 + ...DML_PIMS_INTEREST_HOLDER_INTEREST_TYPE.sql | 56 + .../131_DML_PIMS_INTEREST_HOLDER_TYPE.sql | 17 + .../Build/132_DML_PIMS_PAYMENT_ITEM_TYPE.sql | 25 + .../133_DML_PIMS_PROPERTY_PURPOSE_TYPE.sql | 40 + ...ML_PIMS_PROP_MGMT_ACTIVITY_STATUS_TYPE.sql | 19 + .../135_DML_PIMS_PROP_MGMT_ACTIVITY_TYPE.sql | 38 + ...36_DML_PIMS_PROP_MGMT_ACTIVITY_SUBTYPE.sql | 92 + .../137_DML_PIMS_AGREEMENT_STATUS_TYPE.sql | 17 + ..._DML_PIMS_DISPOSITION_FILE_STATUS_TYPE.sql | 20 + .../139_DML_PIMS_DISPOSITION_STATUS_TYPE.sql | 20 + .../Build/140_DML_PIMS_DISPOSITION_TYPE.sql | 18 + ...L_PIMS_DISPOSITION_INITIATING_DOC_TYPE.sql | 18 + ...142_DML_PIMS_DSP_PHYS_FILE_STATUS_TYPE.sql | 17 + ...43_DML_PIMS_DSP_INITIATING_BRANCH_TYPE.sql | 19 + .../144_DML_PIMS_DSP_FL_TEAM_PROFILE_TYPE.sql | 21 + .../145_DML_PIMS_DISPOSITION_FUNDING_TYPE.sql | 25 + .../147_DML_PIMS_DSP_CHKLST_SECTION_TYPE.sql | 19 + .../148_DML_PIMS_DSP_CHKLST_ITEM_TYPE.sql | 59 + ...DML_PIMS_DISPOSITION_OFFER_STATUS_TYPE.sql | 19 + .../150_DML_PIMS_PROPERTY_OPERATION_TYPE.sql | 16 + ...1_DML_PIMS_HISTORICAL_FILE_NUMBER_TYPE.sql | 21 + ...152_DML_PIMS_LEASE_CHKLST_SECTION_TYPE.sql | 19 + .../153_DML_PIMS_LEASE_CHKLST_ITEM_TYPE.sql | 54 + .../154_DML_PIMS_CHKLST_ITEM_STATUS_TYPE.sql | 17 + ...5_DML_PIMS_LEASE_PAYMENT_CATEGORY_TYPE.sql | 17 + ...156_DML_PIMS_CONSULTATION_OUTCOME_TYPE.sql | 19 + .../157_DML_PIMS_SUBFILE_INTEREST_TYPE.sql | 47 + ...58_DML_PIMS_DOCUMENT_QUEUE_STATUS_TYPE.sql | 31 + .../PSP_PIMS_S95_00/Drop/01_PSP_PIMS_Drop.sql | 19368 ++++++++ .../PIMS S95.00 Change Log.txt | 2116 + .../Test Data/003_DML_LEASE_TESTING.sql | 72 + .../Test Data/004_DML_LEASE_LOAD.sql | 90 + .../Test Data/005_DML_LEASE_PROPERTY.sql | 1323 + .../006_DML_LEASE_PROPERTY_TENURE_TYPE.sql | 1296 + .../Test Data/007_DML_PROPERTY_LEASE.sql | 331 + .../Test Data/007_PIMS_USER_TEST_DATA.sql | 6 + .../008_CONTACTS_ORGANIZATION_TEST_DATA.sql | 51 + .../Test Data/009_CONTACTS_TEST_DATA.sql | 8 + .../Test Data/010_INSURANCE_TEST_DATA.sql | 35 + .../Test Data/011_LEASE_TEST_DATA.sql | 156 + .../012_SECURITY_DEPOSIT_TEST_DATA.sql | 28 + .../013_SECURITY_DEPOSIT_RETURN_TEST_DATA.sql | 26 + ...CURITY_DEPOSIT_RETURN_HOLDER_TEST_DATA.sql | 17 + .../015_SURPLUS_DECLERATION_TEST_DATA.sql | 18 + .../Test Data/016_TENANT_TEST_DATA.sql | 113 + .../018_PIMS_ACCESS_REQUEST_TEST_DATA.sql | 6 + .../Test Data/019_PMBC_TEST_DATA.sql | 2308 + .../Test Data/020_PIMS_RESEARCH_FILE.sql | 12 + .../021_PIMS_PROPERTY_RESEARCH_FILE.sql | 8 + .../022_PIMS_RESEARCH_FILE_PURPOSE.sql | 7 + ...23_PIMS_PRF_PROP_RESEARCH_PURPOSE_TYPE.sql | 7 + .../Test Data/026_PIMS_NOTE.sql | 57 + .../Test Data/027_PIMS_DOCUMENT_TYP.sql | 7 + .../Test Data/028_PIMS_DOCUMENT.sql | 7 + .../Test Data/031_PIMS_ACQUISITION_FILE.sql | 22 + .../Test Data/032_PIMS_ACQUISITION_OWNER.sql | 12 + .../033_PIMS_PROPERTY_ACQUISITION_FILE.sql | 53 + .../034_PIMS_ACQUISITION_FILE_TEAM.sql | 7 + .../Test Data/038_PIMS_USER.sql | 276 + .../Test Data/039_PIMS_PROJECT.sql | 7 + .../Test Data/040_PIMS_PRODUCT.sql | 40 + .../Test Data/041_PIMS_LEASE_PERIOD.sql | 57 + .../Test Data/042_PIMS_LEASE_PAYMENT.sql | 58 + .../Test Data/043_PIMS_ACQUISITION_FILE.sql | 16 + .../Test Data/044_PIMS_ACQUISITION_OWNER.sql | 6 + .../Test Data/045_INTEREST_HOLDER.sql | 8 + .../Test Data/047_PIMS_PERSON.sql | 18 + .../048_PIMS_COMPENSATION_REQUISITION.sql | 13 + .../Test Data/049_PIMS_PROP_PROP_PURPOSE.sql | 5 + .../Test Data/050_PIMS_PROPERTY_CONTACT.sql | 44 + .../Test Data/051_PIMS_PROPERTY_ACTIVITY.sql | 14 + .../052_PIMS_PROPERTY_ACTIVITY_INVOICE.sql | 14 + .../053_PIMS_PROP_ACT_MIN_CONTACT.sql | 16 + .../054_PIMS_PROP_ACT_INVOLVED_PARTY.sql | 16 + .../Test Data/055_PIMS_PROP_PROP_ACTIVITY.sql | 36 + .../056_PIMS_PROP_ACT_MIN_CONTACT.sql | 16 + .../Test Data/057_PIMS_PROJECT_PRODUCT.sql | 39 + .../Test Data/058_PIMS_TAKE.sql | 20 + .../Test Data/059_PIMS_AGREEMENT.sql | 313 + .../Test Data/060_PIMS_DISPOSITION_FILE.sql | 20 + .../061_PIMS_DISPOSITION_FILE_PROPERTY.sql | 13 + .../062_PIMS_DISPOSITION_FILE_TEAM.sql | 15 + .../063_PIMS_DISPOSITION_FILE_NOTE.sql | 17 + .../064_PIMS_DISPOSITION_FILE_DOCUMENT.sql | 12 + .../Test Data/065_PIMS_DISPOSITION_OFFER.sql | 32 + .../068_PIMS_DSP_PURCH_SOLICITOR.sql | 27 + .../Test Data/069_PIMS_DSP_PURCH_AGENT.sql | 27 + .../070_PIMS_DISPOSITION_CHECKLIST_ITEM.sql | 18 + .../Test Data/071_PIMS_PROPERTY_CONTACT.sql | 27 + .../Test Data/072_PIMS_DISPOSITION_SALE.sql | 17 + .../073_PIMS_DISPOSITION_PURCHASER.sql | 27 + .../Test Data/074_PIMS_PROPERTY_OPERATION.sql | 43 + .../075_PIMS_HISTORICAL_PIMS_FILE_NUMBER.sql | 107 + .../076_PIMS_PIMS_LEASE_CHECKLIST_ITEM.sql | 107 + .../077_PIMS_PROP_ACQ_FL_COMP_REQ.sql | 61 + .../078_PIMS_AQUISITION_CHECKLIST_ITEM.sql | 58 + .../079_PIMS_LEASE_LEASE_PURPOSE.sql | 32 + .../Test Data/080_PIMS_LEASE_CONSULTATION.sql | 31 + 222 files changed, 82095 insertions(+), 1127 deletions(-) delete mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/999_DML_Delete_FILE_NUMBER_Alter_Down.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Build/007_PSP_PIMS_COMPENSATION_REQUISITION_HIST_Add.sql delete mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/PIMS PDM S94.00.png create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/PIMS PDM S95.00.png rename source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/{PIMS S94.00 Change Log.txt => PIMS S95.00 Change Log.txt} (99%) create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Down/002_PSP_PIMS_Alter_Down.sql rename source/database/mssql/scripts/dbscripts/{PSP_PIMS_LATEST/Alter Up/998_DML_Migrate_Acq_File_Suffix_Alter_Up.sql => PSP_PIMS_S95_00/Alter Down/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql} (60%) create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Down/Schema Compare.zip create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Down/master.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Up/002_PSP_PIMS_Alter_Up.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Up/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Up/Schema Compare.zip create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Alter Up/master.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/000_PSP_PIMS_ETL_SCHEMA_Build.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/001_PSP_PIMS_PMBC_SCHEMA_Build.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/002_PSP_PIMS_Build.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/003_PSP_PIMS_ETL_Tables_Build.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/004_PSP_PIMS_PMBC_Tables_Build.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/005_PSP_PIMS_PROPERTY_SPATIAL_IDX.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/006_PSP_PIMS_PMBC_INDICES.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/007_PSP_PIMS_ACQUISITION_FILE_HIST_Add.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/007_PSP_PIMS_COMPENSATION_REQUISITION_HIST_Add.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/007_PSP_PIMS_LEASE_HIST_Add.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/007_PSP_PIMS_PROPERTY_ACTIVITY_HIST_Add.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/007_PSP_PIMS_PROPERTY_HIST_Add.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/008_PSP_PIMS_PROPERTY_ACTIVITY_Trigger.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/009_DML_Set_Concurrency_Control_NULL.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/020_DML_PIMS_STATIC_VARIABLE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/030_DML_PIMS_ADDRESS_USAGE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/031_DML_PIMS_AREA_UNIT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/032_DML_PIMS_PROPERTY_TENURE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/040_DML_PIMS_CONTACT_METHOD_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/041_DML_PIMS_ORGANIZATION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/042_DML_PIMS_ACCESS_REQUEST_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/044_DML_PIMS_PROPERTY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/045_DML_PIMS_COUNTRY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/046_DML_PIMS_PROVINCE_STATE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/047_DML_PIMS_REGION.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/048_DML_PIMS_DISTRICT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/049_DML_PIMS_DATA_SOURCE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/050_DML_PIMS_ORG_IDENTIFIER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/051_DML_PIMS_ROLE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/052_DML_PIMS_PROPERTY_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/053_DML_PIMS_LEASE_PAY_RVBL_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/054_DML_PIMS_LEASE_LICENSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/056_DML_PIMS_LEASE_PURPOSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/057_DML_PIMS_LEASE_PROGRAM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/058_DML_PIMS_LEASE_PMT_FREQ_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/061_DML_PIMS_SURPLUS_DECLARATION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/062_DML_PIMS_PROPERTY_IMPROVEMENT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/064_DML_PIMS_SECURITY_DEPOSIT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/065_DML_PIMS_LESSOR_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/066_DML_PIMS_LEASE_INITIATOR_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/067_DML_PIMS_LEASE_RESPONSIBILITY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/068_DML_PIMS_LEASE_PERIOD_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/070_DML_PIMS_LEASE_PAYMENT_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/071_DML_PIMS_LEASE_PAYMENT_METHOD_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/072_DML_PIMS_INSURANCE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/073_DML_PIMS_LEASE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/074_DML_PIMS_CLAIM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/075_DML_PIMS_ROLE_CLAIM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/076_DML_PIMS_ORGANIZATION.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/077_DML_PIMS_TENANT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/078_DML_PIMS_PROPERTY_ANOMALY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/079_DML_PIMS_PROPERTY_ROAD_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/081_DML_PIMS_VOLUMETRIC_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/082_DML_PIMS_VOLUME_UNIT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/083_DML_PIMS_RESEARCH_FILE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/084_DML_PIMS_REQUEST_SOURCE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/085_DML_PIMS_RESEARCH_PURPOSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/086_DML_PIMS_PROP_RESEARCH_PURPOSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/087_DML_PIMS_PPH_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/088_DML_PIMS_DOCUMENT_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/089_DML_PIMS_ACQUISITION_FILE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/090_DML_PIMS_ACQUISITION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/091_DML_PIMS_ACQUISITION_FUNDING_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/092_DML_PIMS_ACQ_PHYS_FILE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/093_DML_PIMS_ACQ_FL_TEAM_PROFILE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/095_DML_PIMS_SURVEY_PLAN_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/096_DML_PIMS_LAND_SURVEYOR_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/098_DML_PIMS_FENCE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/099_DML_PIMS_LETTER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/101_DML_PIMS_COST_TYPE_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/102_DML_PIMS_CHART_OF_ACCOUNTS_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/103_DML_PIMS_BUSINESS_FUNCTION_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/104_DML_PIMS_YEARLY_FINANCIAL_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/105_DML_PIMS_RESPONSIBILITY_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/106_DML_SET_PIMS_RESPONSIBILITY_CODE_SEQ.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/107_DML_PIMS_FINANCIAL_ACTIVITY_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/108_DML_PIMS_WORK_ACTIVITY_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/109_DML_PIMS_LEASE_STAKEHOLDER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/110_DML_PIMS_PROJECT_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/111_DML_PIMS_BUSINESS_FUNCTION_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/112_DML_PIMS_COST_TYPE_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/113_DML_PIMS_WORK_ACTIVITY_CODE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/114_DML_PIMS_CONSULTATION_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/115_DML_PIMS_CONSULTATION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/116_DML_PIMS_TAKE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/117_DML_PIMS_TAKE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/118_DML_PIMS_TAKE_SITE_CONTAM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/120_DML_PIMS_ACQ_CHKLST_SECTION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/121_DML_PIMS_ACQ_CHKLST_ITEM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/122_DML_PIMS_PROJECT_PERSON_ROLE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/123_DML_PIMS_FORM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/124_DML_PIMS_AGREEMENT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/125_DML_PIMS_USER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/126_DML_PIMS_H120_CATEGORY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/127_DML_PIMS_DOCUMENT_FORMAT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/128_DML_PIMS_LAND_ACT_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/129_DML_PIMS_DOCUMENT_CATEGORY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/130_DML_PIMS_INTEREST_HOLDER_INTEREST_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/131_DML_PIMS_INTEREST_HOLDER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/132_DML_PIMS_PAYMENT_ITEM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/133_DML_PIMS_PROPERTY_PURPOSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/134_DML_PIMS_PROP_MGMT_ACTIVITY_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/135_DML_PIMS_PROP_MGMT_ACTIVITY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/136_DML_PIMS_PROP_MGMT_ACTIVITY_SUBTYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/137_DML_PIMS_AGREEMENT_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/138_DML_PIMS_DISPOSITION_FILE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/139_DML_PIMS_DISPOSITION_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/140_DML_PIMS_DISPOSITION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/141_DML_PIMS_DISPOSITION_INITIATING_DOC_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/142_DML_PIMS_DSP_PHYS_FILE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/143_DML_PIMS_DSP_INITIATING_BRANCH_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/144_DML_PIMS_DSP_FL_TEAM_PROFILE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/145_DML_PIMS_DISPOSITION_FUNDING_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/147_DML_PIMS_DSP_CHKLST_SECTION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/148_DML_PIMS_DSP_CHKLST_ITEM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/149_DML_PIMS_DISPOSITION_OFFER_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/150_DML_PIMS_PROPERTY_OPERATION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/151_DML_PIMS_HISTORICAL_FILE_NUMBER_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/152_DML_PIMS_LEASE_CHKLST_SECTION_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/153_DML_PIMS_LEASE_CHKLST_ITEM_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/154_DML_PIMS_CHKLST_ITEM_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/155_DML_PIMS_LEASE_PAYMENT_CATEGORY_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/156_DML_PIMS_CONSULTATION_OUTCOME_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/157_DML_PIMS_SUBFILE_INTEREST_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Build/158_DML_PIMS_DOCUMENT_QUEUE_STATUS_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Drop/01_PSP_PIMS_Drop.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/PIMS S95.00 Change Log.txt create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/003_DML_LEASE_TESTING.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/004_DML_LEASE_LOAD.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/005_DML_LEASE_PROPERTY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/006_DML_LEASE_PROPERTY_TENURE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/007_DML_PROPERTY_LEASE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/007_PIMS_USER_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/008_CONTACTS_ORGANIZATION_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/009_CONTACTS_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/010_INSURANCE_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/011_LEASE_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/012_SECURITY_DEPOSIT_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/013_SECURITY_DEPOSIT_RETURN_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/014_SECURITY_DEPOSIT_RETURN_HOLDER_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/015_SURPLUS_DECLERATION_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/016_TENANT_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/018_PIMS_ACCESS_REQUEST_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/019_PMBC_TEST_DATA.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/020_PIMS_RESEARCH_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/021_PIMS_PROPERTY_RESEARCH_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/022_PIMS_RESEARCH_FILE_PURPOSE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/023_PIMS_PRF_PROP_RESEARCH_PURPOSE_TYPE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/026_PIMS_NOTE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/027_PIMS_DOCUMENT_TYP.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/028_PIMS_DOCUMENT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/031_PIMS_ACQUISITION_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/032_PIMS_ACQUISITION_OWNER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/033_PIMS_PROPERTY_ACQUISITION_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/034_PIMS_ACQUISITION_FILE_TEAM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/038_PIMS_USER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/039_PIMS_PROJECT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/040_PIMS_PRODUCT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/041_PIMS_LEASE_PERIOD.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/042_PIMS_LEASE_PAYMENT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/043_PIMS_ACQUISITION_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/044_PIMS_ACQUISITION_OWNER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/045_INTEREST_HOLDER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/047_PIMS_PERSON.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/048_PIMS_COMPENSATION_REQUISITION.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/049_PIMS_PROP_PROP_PURPOSE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/050_PIMS_PROPERTY_CONTACT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/051_PIMS_PROPERTY_ACTIVITY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/052_PIMS_PROPERTY_ACTIVITY_INVOICE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/053_PIMS_PROP_ACT_MIN_CONTACT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/054_PIMS_PROP_ACT_INVOLVED_PARTY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/055_PIMS_PROP_PROP_ACTIVITY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/056_PIMS_PROP_ACT_MIN_CONTACT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/057_PIMS_PROJECT_PRODUCT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/058_PIMS_TAKE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/059_PIMS_AGREEMENT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/060_PIMS_DISPOSITION_FILE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/061_PIMS_DISPOSITION_FILE_PROPERTY.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/062_PIMS_DISPOSITION_FILE_TEAM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/063_PIMS_DISPOSITION_FILE_NOTE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/064_PIMS_DISPOSITION_FILE_DOCUMENT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/065_PIMS_DISPOSITION_OFFER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/068_PIMS_DSP_PURCH_SOLICITOR.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/069_PIMS_DSP_PURCH_AGENT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/070_PIMS_DISPOSITION_CHECKLIST_ITEM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/071_PIMS_PROPERTY_CONTACT.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/072_PIMS_DISPOSITION_SALE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/073_PIMS_DISPOSITION_PURCHASER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/074_PIMS_PROPERTY_OPERATION.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/075_PIMS_HISTORICAL_PIMS_FILE_NUMBER.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/076_PIMS_PIMS_LEASE_CHECKLIST_ITEM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/077_PIMS_PROP_ACQ_FL_COMP_REQ.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/078_PIMS_AQUISITION_CHECKLIST_ITEM.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/079_PIMS_LEASE_LEASE_PURPOSE.sql create mode 100644 source/database/mssql/scripts/dbscripts/PSP_PIMS_S95_00/Test Data/080_PIMS_LEASE_CONSULTATION.sql diff --git a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/002_PSP_PIMS_Alter_Down.sql b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/002_PSP_PIMS_Alter_Down.sql index 0777d3e3cf..74decace2c 100644 --- a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/002_PSP_PIMS_Alter_Down.sql +++ b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/002_PSP_PIMS_Alter_Down.sql @@ -1,8 +1,8 @@ --- Script generated by Aqua Data Studio Schema Synchronization for MS SQL Server 2016 on Wed Nov 27 11:36:04 PST 2024 +-- Script generated by Aqua Data Studio Schema Synchronization for MS SQL Server 2016 on Mon Dec 02 19:25:07 PST 2024 -- Execute this script on: --- PSP_PIMS_S94_00/dbo - This database/schema will be modified +-- PSP_PIMS_S95_00/dbo - This database/schema will be modified -- to synchronize it with MS SQL Server 2016: --- PSP_PIMS_S93_00/dbo +-- PSP_PIMS_S94_00/dbo -- We recommend backing up the database prior to executing the script. @@ -15,200 +15,457 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_PRJPRD_I_S_U_TR -PRINT N'Drop trigger dbo.PIMS_PRJPRD_I_S_U_TR' +-- Drop trigger dbo.PIMS_EXPPMT_A_S_IUD_TR +PRINT N'Drop trigger dbo.PIMS_EXPPMT_A_S_IUD_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_PRJPRD_I_S_U_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_EXPPMT_A_S_IUD_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_DSPDOC_A_S_IUD_TR -PRINT N'Drop trigger dbo.PIMS_DSPDOC_A_S_IUD_TR' +-- Drop trigger dbo.PIMS_ACQNFL_I_S_U_TR +PRINT N'Drop trigger dbo.PIMS_ACQNFL_I_S_U_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DSPDOC_A_S_IUD_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_I_S_U_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_PRPRAT_I_S_I_TR -PRINT N'Drop trigger dbo.PIMS_PRPRAT_I_S_I_TR' +-- Drop trigger dbo.PIMS_CMPREQ_I_S_U_TR +PRINT N'Drop trigger dbo.PIMS_CMPREQ_I_S_U_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_PRPRAT_I_S_I_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_CMPREQ_I_S_U_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_DSPDOC_I_S_U_TR -PRINT N'Drop trigger dbo.PIMS_DSPDOC_I_S_U_TR' +-- Drop trigger dbo.PIMS_EXPPMT_I_S_U_TR +PRINT N'Drop trigger dbo.PIMS_EXPPMT_I_S_U_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DSPDOC_I_S_U_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_EXPPMT_I_S_U_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_DOCQUE_I_S_U_TR -PRINT N'Drop trigger dbo.PIMS_DOCQUE_I_S_U_TR' +-- Drop trigger dbo.PIMS_ACQNFL_A_S_IUD_TR +PRINT N'Drop trigger dbo.PIMS_ACQNFL_A_S_IUD_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DOCQUE_I_S_U_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_A_S_IUD_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_DSPDOC_I_S_I_TR -PRINT N'Drop trigger dbo.PIMS_DSPDOC_I_S_I_TR' +-- Drop trigger dbo.PIMS_CMPREQ_A_S_IUD_TR +PRINT N'Drop trigger dbo.PIMS_CMPREQ_A_S_IUD_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DSPDOC_I_S_I_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_CMPREQ_A_S_IUD_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_ACQNFL_I_S_U_TR -PRINT N'Drop trigger dbo.PIMS_ACQNFL_I_S_U_TR' +-- Drop trigger dbo.PIMS_CMPREQ_I_S_I_TR +PRINT N'Drop trigger dbo.PIMS_CMPREQ_I_S_I_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_I_S_U_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_CMPREQ_I_S_I_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_PRJPRD_I_S_I_TR -PRINT N'Drop trigger dbo.PIMS_PRJPRD_I_S_I_TR' +-- Drop trigger dbo.PIMS_EXPPMT_I_S_I_TR +PRINT N'Drop trigger dbo.PIMS_EXPPMT_I_S_I_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_PRJPRD_I_S_I_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_EXPPMT_I_S_I_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_DOCQUE_A_S_IUD_TR -PRINT N'Drop trigger dbo.PIMS_DOCQUE_A_S_IUD_TR' +-- Drop trigger dbo.PIMS_ACQNFL_I_S_I_TR +PRINT N'Drop trigger dbo.PIMS_ACQNFL_I_S_I_TR' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DOCQUE_A_S_IUD_TR] +DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_I_S_I_TR] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_PRPRAT_I_S_U_TR -PRINT N'Drop trigger dbo.PIMS_PRPRAT_I_S_U_TR' +-- Alter table dbo.PIMS_ACQUISITION_FILE +PRINT N'Alter table dbo.PIMS_ACQUISITION_FILE' +GO +ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] + ADD [FILE_NUMBER] nvarchar(18) NOT NULL DEFAULT 999999 GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_PRPRAT_I_S_U_TR] + +EXEC sp_addextendedproperty + @name = N'MS_Description', @value = N'Formatted file number assigned to the acquisition file. Format follows YY-XXXXXX-ZZ where YY = MoTI region number, XXXXXX = generated integer sequence number, and ZZ = file suffix number (defaulting to ''01'')' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_ACQUISITION_FILE', + @level2type = N'Column', @level2name = N'FILE_NUMBER' GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop trigger dbo.PIMS_ACQNFL_A_S_IUD_TR -PRINT N'Drop trigger dbo.PIMS_ACQNFL_A_S_IUD_TR' +-- Drop dynamically-named default constraint +PRINT N'Drop dynamically-named default constraint' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_A_S_IUD_TR] +DECLARE @sqlQry VARCHAR(1000) +DECLARE @defName VARCHAR(100) +SET @defName = (SELECT obj.NAME + FROM SYSOBJECTS obj INNER JOIN + SYSCOLUMNS col on obj.ID = col.CDEFAULT INNER JOIN + SYSOBJECTS tbl on col.ID = tbl.ID + WHERE obj.XTYPE = 'D' + AND tbl.NAME = 'PIMS_ACQUISITION_FILE' + AND col.NAME = 'FILE_NUMBER') +SET @sqlQry = 'ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] DROP CONSTRAINT IF EXISTS [' + @defName + ']' +EXEC (@sqlQry) GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Drop index dbo.ACQNFL_FILE_NO_IDX -PRINT N'Drop index dbo.ACQNFL_FILE_NO_IDX' +-- Alter table dbo.PIMS_EXPROPRIATION_PAYMENT +PRINT N'Alter table dbo.PIMS_EXPROPRIATION_PAYMENT' GO -DROP INDEX IF EXISTS [dbo].[PIMS_ACQUISITION_FILE].[ACQNFL_FILE_NO_IDX] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'EXPROPRIATION_PAYMENT_ID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Drop trigger dbo.PIMS_DOCQUE_I_S_I_TR -PRINT N'Drop trigger dbo.PIMS_DOCQUE_I_S_I_TR' +--ALTER TABLE [dbo].[PIMS_EXPROPRIATION_PAYMENT] ADD DEFAULT 999999 FOR [ACQUISITION_FILE_ID] +--GO +--IF @@ERROR <> 0 SET NOEXEC ON +--GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'ACQUISITION_FILE_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_DOCQUE_I_S_I_TR] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'ACQUISITION_OWNER_ID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Drop trigger dbo.PIMS_PRJPRD_A_S_IUD_TR -PRINT N'Drop trigger dbo.PIMS_PRJPRD_A_S_IUD_TR' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'INTEREST_HOLDER_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_PRJPRD_A_S_IUD_TR] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'EXPROPRIATING_AUTHORITY' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Drop trigger dbo.PIMS_ACQNFL_I_S_I_TR -PRINT N'Drop trigger dbo.PIMS_ACQNFL_I_S_I_TR' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'CONCURRENCY_CONTROL_NUMBER' GO -DROP TRIGGER IF EXISTS [dbo].[PIMS_ACQNFL_I_S_I_TR] +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_CREATE_TIMESTAMP' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Drop check constraint dbo.ACQNFL_SUBFILE_INTEREST_TYPE_CODE_TCC -PRINT N'Drop check constraint dbo.ACQNFL_SUBFILE_INTEREST_TYPE_CODE_TCC' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_CREATE_USERID' GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] - DROP CONSTRAINT IF EXISTS [ACQNFL_SUBFILE_INTEREST_TYPE_CODE_TCC] +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_CREATE_USER_GUID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Alter table dbo.PIMS_ACQUISITION_FILE -PRINT N'Alter table dbo.PIMS_ACQUISITION_FILE' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_CREATE_USER_DIRECTORY' +GO +IF @@ERROR <> 0 SET NOEXEC ON GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] ADD CONSTRAINT [ACQNFL_FILE_NUMBER_DEF] DEFAULT ('') FOR [FILE_NUMBER] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_TIMESTAMP' GO IF @@ERROR <> 0 SET NOEXEC ON GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] - DROP CONSTRAINT IF EXISTS [ACQNFL_FILE_NO_SUFFIX_DEF] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USERID' GO IF @@ERROR <> 0 SET NOEXEC ON GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] - DROP COLUMN IF EXISTS [FILE_NO_SUFFIX] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USER_GUID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Alter table dbo.PIMS_DOCUMENT -PRINT N'Alter table dbo.PIMS_DOCUMENT' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USER_DIRECTORY' GO -UPDATE [dbo].[PIMS_DOCUMENT] SET [MAYAN_ID] = ((-1)) WHERE [MAYAN_ID] IS NULL +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'DB_CREATE_TIMESTAMP' GO IF @@ERROR <> 0 SET NOEXEC ON GO -ALTER TABLE [dbo].[PIMS_DOCUMENT] ALTER COLUMN [MAYAN_ID] bigint NOT NULL +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'DB_CREATE_USERID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Alter table dbo.PIMS_DOCUMENT_QUEUE -PRINT N'Alter table dbo.PIMS_DOCUMENT_QUEUE' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'DB_LAST_UPDATE_TIMESTAMP' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_EXPROPRIATION_PAYMENT', + @level2type = N'Column', @level2name = N'DB_LAST_UPDATE_USERID' GO -ALTER TABLE [dbo].[PIMS_DOCUMENT_QUEUE] - DROP COLUMN IF EXISTS [FILE_NAME] +IF @@ERROR <> 0 SET NOEXEC ON +GO +ALTER TABLE [dbo].[PIMS_EXPROPRIATION_PAYMENT] + DROP COLUMN IF EXISTS [ADV_PMT_SERVED_DT] GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Alter table dbo.PIMS_ACQUISITION_FILE_HIST -PRINT N'Alter table dbo.PIMS_ACQUISITION_FILE_HIST' +-- Alter table dbo.PIMS_COMPENSATION_REQUISITION +PRINT N'Alter table dbo.PIMS_COMPENSATION_REQUISITION' GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE_HIST] - DROP COLUMN IF EXISTS [FILE_NO_SUFFIX] +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'COMPENSATION_REQUISITION_ID' GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Alter table dbo.PIMS_DOCUMENT_QUEUE_HIST -PRINT N'Alter table dbo.PIMS_DOCUMENT_QUEUE_HIST' +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'ACQUISITION_OWNER_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'INTEREST_HOLDER_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'ACQUISITION_FILE_TEAM_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'CHART_OF_ACCOUNTS_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'RESPONSIBILITY_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'YEARLY_FINANCIAL_ID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'CONCURRENCY_CONTROL_NUMBER' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_CREATE_TIMESTAMP' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_CREATE_USERID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_CREATE_USER_GUID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_CREATE_USER_DIRECTORY' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_TIMESTAMP' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USERID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USER_GUID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'APP_LAST_UPDATE_USER_DIRECTORY' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'DB_CREATE_TIMESTAMP' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'DB_CREATE_USERID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'DB_LAST_UPDATE_TIMESTAMP' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +EXEC sp_dropextendedproperty + @name = N'MS_Description' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'DB_LAST_UPDATE_USERID' +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +ALTER TABLE [dbo].[PIMS_COMPENSATION_REQUISITION] + ADD [ADV_PMT_SERVED_DT] date NULL GO -ALTER TABLE [dbo].[PIMS_DOCUMENT_QUEUE_HIST] - DROP COLUMN IF EXISTS [FILE_NAME] +EXEC sp_addextendedproperty + @name = N'MS_Description', @value = N'Date that the advanced payment was made.' , + @level0type = N'Schema', @level0name = N'dbo', + @level1type = N'Table', @level1name = N'PIMS_COMPENSATION_REQUISITION', + @level2type = N'Column', @level2name = N'ADV_PMT_SERVED_DT' GO IF @@ERROR <> 0 SET NOEXEC ON GO +--GO --- Alter table dbo.PIMS_DOCUMENT_HIST -PRINT N'Alter table dbo.PIMS_DOCUMENT_HIST' +-- Alter table dbo.PIMS_ACQUISITION_FILE_HIST +PRINT N'Alter table dbo.PIMS_ACQUISITION_FILE_HIST' GO -ALTER TABLE [dbo].[PIMS_DOCUMENT_HIST] ALTER COLUMN [MAYAN_ID] bigint NOT NULL +UPDATE [dbo].[PIMS_ACQUISITION_FILE_HIST] +SET FILE_NUMBER = '' + , CONCURRENCY_CONTROL_NUMBER = CONCURRENCY_CONTROL_NUMBER + 1 +WHERE FILE_NUMBER IS NULL GO IF @@ERROR <> 0 SET NOEXEC ON GO -ALTER TABLE [dbo].[PIMS_DOCUMENT_HIST] ADD DEFAULT '' FOR [MAYAN_ID] +ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE_HIST] + ALTER COLUMN [FILE_NUMBER] nvarchar(18) NOT NULL GO IF @@ERROR <> 0 SET NOEXEC ON GO @@ -223,20 +480,19 @@ SET @defName = (SELECT obj.NAME SYSCOLUMNS col on obj.ID = col.CDEFAULT INNER JOIN SYSOBJECTS tbl on col.ID = tbl.ID WHERE obj.XTYPE = 'D' - AND tbl.NAME = 'PIMS_DOCUMENT_HIST' - AND col.NAME = 'MAYAN_ID') -SET @sqlQry = 'ALTER TABLE [dbo].[PIMS_DOCUMENT_HIST] DROP CONSTRAINT IF EXISTS [' + @defName + ']' + AND tbl.NAME = 'PIMS_ACQUISITION_FILE_HIST' + AND col.NAME = 'FILE_NUMBER') +SET @sqlQry = 'ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE_HIST] DROP CONSTRAINT IF EXISTS [' + @defName + ']' EXEC (@sqlQry) GO IF @@ERROR <> 0 SET NOEXEC ON GO - --- Create index dbo.ACQNFL_FILE_NUMBER_IDX -PRINT N'Create index dbo.ACQNFL_FILE_NUMBER_IDX' +-- Alter table dbo.PIMS_EXPROPRIATION_PAYMENT_HIST +PRINT N'Alter table dbo.PIMS_EXPROPRIATION_PAYMENT_HIST' GO -CREATE NONCLUSTERED INDEX [ACQNFL_FILE_NUMBER_IDX] - ON [dbo].[PIMS_ACQUISITION_FILE]([FILE_NUMBER]) +ALTER TABLE [dbo].[PIMS_EXPROPRIATION_PAYMENT_HIST] + DROP COLUMN IF EXISTS [ADV_PMT_SERVED_DT] GO IF @@ERROR <> 0 SET NOEXEC ON GO @@ -263,6 +519,7 @@ BEGIN TRY "SUBFILE_INTEREST_TYPE_CODE", "FILE_NAME", "FILE_NO", + "FILE_NO_SUFFIX", "FILE_NUMBER", "LEGACY_FILE_NUMBER", "LEGACY_STAKEHOLDER", @@ -295,6 +552,7 @@ BEGIN TRY "SUBFILE_INTEREST_TYPE_CODE", "FILE_NAME", "FILE_NO", + "FILE_NO_SUFFIX", "FILE_NUMBER", "LEGACY_FILE_NUMBER", "LEGACY_STAKEHOLDER", @@ -327,24 +585,49 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_PRJPRD_A_S_IUD_TR -PRINT N'Create trigger dbo.PIMS_PRJPRD_A_S_IUD_TR' +-- Create trigger dbo.PIMS_EXPPMT_I_S_I_TR +PRINT N'Create trigger dbo.PIMS_EXPPMT_I_S_I_TR' GO -CREATE TRIGGER [dbo].[PIMS_PRJPRD_A_S_IUD_TR] ON PIMS_PROJECT_PRODUCT FOR INSERT, UPDATE, DELETE AS +CREATE TRIGGER [dbo].[PIMS_EXPPMT_I_S_I_TR] ON PIMS_EXPROPRIATION_PAYMENT INSTEAD OF INSERT AS SET NOCOUNT ON BEGIN TRY -DECLARE @curr_date datetime; -SET @curr_date = getutcdate(); - IF NOT EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) + IF NOT EXISTS(SELECT * FROM inserted) RETURN; - -- historical - IF EXISTS(SELECT * FROM deleted) - update PIMS_PROJECT_PRODUCT_HIST set END_DATE_HIST = @curr_date where PROJECT_PRODUCT_ID in (select PROJECT_PRODUCT_ID from deleted) and END_DATE_HIST is null; - IF EXISTS(SELECT * FROM inserted) - insert into PIMS_PROJECT_PRODUCT_HIST ([PROJECT_PRODUCT_ID], [PROJECT_ID], [PRODUCT_ID], [APP_CREATE_TIMESTAMP], [APP_CREATE_USER_DIRECTORY], [APP_CREATE_USER_GUID], [APP_CREATE_USERID], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USER_DIRECTORY], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USERID], [CONCURRENCY_CONTROL_NUMBER], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _PROJECT_PRODUCT_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) - select [PROJECT_PRODUCT_ID], [PROJECT_ID], [PRODUCT_ID], [APP_CREATE_TIMESTAMP], [APP_CREATE_USER_DIRECTORY], [APP_CREATE_USER_GUID], [APP_CREATE_USERID], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USER_DIRECTORY], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USERID], [CONCURRENCY_CONTROL_NUMBER], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_PROJECT_PRODUCT_H_ID_SEQ]) as [_PROJECT_PRODUCT_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; + insert into PIMS_EXPROPRIATION_PAYMENT ("EXPROPRIATION_PAYMENT_ID", + "ACQUISITION_FILE_ID", + "ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID", + "EXPROPRIATING_AUTHORITY", + "DESCRIPTION", + "IS_DISABLED", + "CONCURRENCY_CONTROL_NUMBER", + "APP_CREATE_TIMESTAMP", + "APP_CREATE_USERID", + "APP_CREATE_USER_GUID", + "APP_CREATE_USER_DIRECTORY", + "APP_LAST_UPDATE_TIMESTAMP", + "APP_LAST_UPDATE_USERID", + "APP_LAST_UPDATE_USER_GUID", + "APP_LAST_UPDATE_USER_DIRECTORY") + select "EXPROPRIATION_PAYMENT_ID", + "ACQUISITION_FILE_ID", + "ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID", + "EXPROPRIATING_AUTHORITY", + "DESCRIPTION", + "IS_DISABLED", + "CONCURRENCY_CONTROL_NUMBER", + "APP_CREATE_TIMESTAMP", + "APP_CREATE_USERID", + "APP_CREATE_USER_GUID", + "APP_CREATE_USER_DIRECTORY", + "APP_LAST_UPDATE_TIMESTAMP", + "APP_LAST_UPDATE_USERID", + "APP_LAST_UPDATE_USER_GUID", + "APP_LAST_UPDATE_USER_DIRECTORY" + from inserted; END TRY BEGIN CATCH @@ -356,32 +639,40 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_DOCQUE_I_S_I_TR -PRINT N'Create trigger dbo.PIMS_DOCQUE_I_S_I_TR' +-- Create trigger dbo.PIMS_CMPREQ_I_S_I_TR +PRINT N'Create trigger dbo.PIMS_CMPREQ_I_S_I_TR' GO -CREATE TRIGGER [dbo].[PIMS_DOCQUE_I_S_I_TR] ON PIMS_DOCUMENT_QUEUE INSTEAD OF INSERT AS +CREATE TRIGGER [dbo].[PIMS_CMPREQ_I_S_I_TR] ON PIMS_COMPENSATION_REQUISITION INSTEAD OF INSERT AS SET NOCOUNT ON BEGIN TRY IF NOT EXISTS(SELECT * FROM inserted) RETURN; - insert into PIMS_DOCUMENT_QUEUE ("DOCUMENT_QUEUE_ID", - "DOCUMENT_ID", - "DOCUMENT_QUEUE_STATUS_TYPE_CODE", - "DATA_SOURCE_TYPE_CODE", - "PROPERTY_ACTIVITY_DOCUMENT_ID", - "ACQUISITION_FILE_DOCUMENT_ID", - "RESEARCH_FILE_DOCUMENT_ID", - "LEASE_DOCUMENT_ID", - "DISPOSITION_FILE_DOCUMENT_ID", - "DOCUMENT_EXTERNAL_ID", - "DOCUMENT_METADATA", - "DOC_PROCESS_START_DT", - "DOC_PROCESS_END_DT", - "DOC_PROCESS_RETRIES", - "MAYAN_ERROR", - "DOCUMENT", + insert into PIMS_COMPENSATION_REQUISITION ("COMPENSATION_REQUISITION_ID", + "ACQUISITION_FILE_ID", + "LEASE_ID", + "ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID", + "ACQUISITION_FILE_TEAM_ID", + "CHART_OF_ACCOUNTS_ID", + "RESPONSIBILITY_ID", + "YEARLY_FINANCIAL_ID", + "ALTERNATE_PROJECT_ID", + "LEGACY_PAYEE", + "IS_DRAFT", + "IS_PAYMENT_IN_TRUST", + "GST_NUMBER", + "FISCAL_YEAR", + "AGREEMENT_DT", + "EXPROP_NOTICE_SERVED_DT", + "EXPROP_VESTING_DT", + "GENERATION_DT", + "FINALIZED_DATE", + "ADV_PMT_SERVED_DT", + "SPECIAL_INSTRUCTION", + "DETAILED_REMARKS", + "IS_DISABLED", "CONCURRENCY_CONTROL_NUMBER", "APP_CREATE_TIMESTAMP", "APP_CREATE_USERID", @@ -391,22 +682,30 @@ BEGIN TRY "APP_LAST_UPDATE_USERID", "APP_LAST_UPDATE_USER_GUID", "APP_LAST_UPDATE_USER_DIRECTORY") - select "DOCUMENT_QUEUE_ID", - "DOCUMENT_ID", - "DOCUMENT_QUEUE_STATUS_TYPE_CODE", - "DATA_SOURCE_TYPE_CODE", - "PROPERTY_ACTIVITY_DOCUMENT_ID", - "ACQUISITION_FILE_DOCUMENT_ID", - "RESEARCH_FILE_DOCUMENT_ID", - "LEASE_DOCUMENT_ID", - "DISPOSITION_FILE_DOCUMENT_ID", - "DOCUMENT_EXTERNAL_ID", - "DOCUMENT_METADATA", - "DOC_PROCESS_START_DT", - "DOC_PROCESS_END_DT", - "DOC_PROCESS_RETRIES", - "MAYAN_ERROR", - "DOCUMENT", + select "COMPENSATION_REQUISITION_ID", + "ACQUISITION_FILE_ID", + "LEASE_ID", + "ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID", + "ACQUISITION_FILE_TEAM_ID", + "CHART_OF_ACCOUNTS_ID", + "RESPONSIBILITY_ID", + "YEARLY_FINANCIAL_ID", + "ALTERNATE_PROJECT_ID", + "LEGACY_PAYEE", + "IS_DRAFT", + "IS_PAYMENT_IN_TRUST", + "GST_NUMBER", + "FISCAL_YEAR", + "AGREEMENT_DT", + "EXPROP_NOTICE_SERVED_DT", + "EXPROP_VESTING_DT", + "GENERATION_DT", + "FINALIZED_DATE", + "ADV_PMT_SERVED_DT", + "SPECIAL_INSTRUCTION", + "DETAILED_REMARKS", + "IS_DISABLED", "CONCURRENCY_CONTROL_NUMBER", "APP_CREATE_TIMESTAMP", "APP_CREATE_USERID", @@ -428,10 +727,10 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_ACQNFL_A_S_IUD_TR -PRINT N'Create trigger dbo.PIMS_ACQNFL_A_S_IUD_TR' +-- Create trigger dbo.PIMS_CMPREQ_A_S_IUD_TR +PRINT N'Create trigger dbo.PIMS_CMPREQ_A_S_IUD_TR' GO -CREATE TRIGGER [dbo].[PIMS_ACQNFL_A_S_IUD_TR] ON PIMS_ACQUISITION_FILE FOR INSERT, UPDATE, DELETE AS +CREATE TRIGGER [dbo].[PIMS_CMPREQ_A_S_IUD_TR] ON PIMS_COMPENSATION_REQUISITION FOR INSERT, UPDATE, DELETE AS SET NOCOUNT ON BEGIN TRY DECLARE @curr_date datetime; @@ -441,51 +740,11 @@ SET @curr_date = getutcdate(); -- historical IF EXISTS(SELECT * FROM deleted) - update PIMS_ACQUISITION_FILE_HIST set END_DATE_HIST = @curr_date where ACQUISITION_FILE_ID in (select ACQUISITION_FILE_ID from deleted) and END_DATE_HIST is null; + update PIMS_COMPENSATION_REQUISITION_HIST set END_DATE_HIST = @curr_date where COMPENSATION_REQUISITION_ID in (select COMPENSATION_REQUISITION_ID from deleted) and END_DATE_HIST is null; IF EXISTS(SELECT * FROM inserted) - insert into PIMS_ACQUISITION_FILE_HIST ([ACQUISITION_FILE_ID], [PRNT_ACQUISITION_FILE_ID], [PROJECT_ID], [PRODUCT_ID], [ACQUISITION_FILE_STATUS_TYPE_CODE], [ACQUISITION_TYPE_CODE], [ACQUISITION_FUNDING_TYPE_CODE], [ACQ_PHYS_FILE_STATUS_TYPE_CODE], [REGION_CODE], [SUBFILE_INTEREST_TYPE_CODE], [FILE_NAME], [FILE_NO], [FILE_NUMBER], [LEGACY_FILE_NUMBER], [LEGACY_STAKEHOLDER], [FUNDING_OTHER], [ASSIGNED_DATE], [DELIVERY_DATE], [PAIMS_ACQUISITION_FILE_ID], [TOTAL_ALLOWABLE_COMPENSATION], [OTHER_SUBFILE_INTEREST_TYPE], [EST_COMPLETION_DT], [POSSESSION_DT], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _ACQUISITION_FILE_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) - select [ACQUISITION_FILE_ID], [PRNT_ACQUISITION_FILE_ID], [PROJECT_ID], [PRODUCT_ID], [ACQUISITION_FILE_STATUS_TYPE_CODE], [ACQUISITION_TYPE_CODE], [ACQUISITION_FUNDING_TYPE_CODE], [ACQ_PHYS_FILE_STATUS_TYPE_CODE], [REGION_CODE], [SUBFILE_INTEREST_TYPE_CODE], [FILE_NAME], [FILE_NO], [FILE_NUMBER], [LEGACY_FILE_NUMBER], [LEGACY_STAKEHOLDER], [FUNDING_OTHER], [ASSIGNED_DATE], [DELIVERY_DATE], [PAIMS_ACQUISITION_FILE_ID], [TOTAL_ALLOWABLE_COMPENSATION], [OTHER_SUBFILE_INTEREST_TYPE], [EST_COMPLETION_DT], [POSSESSION_DT], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_ACQUISITION_FILE_H_ID_SEQ]) as [_ACQUISITION_FILE_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; - -END TRY -BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - EXEC pims_error_handling -END CATCH; - -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Create trigger dbo.PIMS_PRPRAT_I_S_U_TR -PRINT N'Create trigger dbo.PIMS_PRPRAT_I_S_U_TR' -GO -CREATE TRIGGER [dbo].[PIMS_PRPRAT_I_S_U_TR] ON PIMS_PROP_PROP_ANOMALY_TYPE INSTEAD OF UPDATE AS -SET NOCOUNT ON -BEGIN TRY - IF NOT EXISTS(SELECT * FROM deleted) - RETURN; - - -- validate concurrency control - if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.PROP_PROP_ANOMALY_TYPE_ID = deleted.PROP_PROP_ANOMALY_TYPE_ID) - raiserror('CONCURRENCY FAILURE.',16,1) - - - -- update statement - update PIMS_PROP_PROP_ANOMALY_TYPE - set "PROP_PROP_ANOMALY_TYPE_ID" = inserted."PROP_PROP_ANOMALY_TYPE_ID", - "PROPERTY_ID" = inserted."PROPERTY_ID", - "PROPERTY_ANOMALY_TYPE_CODE" = inserted."PROPERTY_ANOMALY_TYPE_CODE", - "APP_LAST_UPDATE_TIMESTAMP" = inserted."APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID" = inserted."APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID" = inserted."APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER" - , DB_LAST_UPDATE_TIMESTAMP = getutcdate() - , DB_LAST_UPDATE_USERID = user_name() - from PIMS_PROP_PROP_ANOMALY_TYPE - inner join inserted - on (PIMS_PROP_PROP_ANOMALY_TYPE.PROP_PROP_ANOMALY_TYPE_ID = inserted.PROP_PROP_ANOMALY_TYPE_ID); + insert into PIMS_COMPENSATION_REQUISITION_HIST ([COMPENSATION_REQUISITION_ID], [ACQUISITION_FILE_ID], [LEASE_ID], [ACQUISITION_OWNER_ID], [INTEREST_HOLDER_ID], [ACQUISITION_FILE_TEAM_ID], [CHART_OF_ACCOUNTS_ID], [RESPONSIBILITY_ID], [YEARLY_FINANCIAL_ID], [ALTERNATE_PROJECT_ID], [LEGACY_PAYEE], [IS_DRAFT], [IS_PAYMENT_IN_TRUST], [GST_NUMBER], [FISCAL_YEAR], [AGREEMENT_DT], [EXPROP_NOTICE_SERVED_DT], [EXPROP_VESTING_DT], [GENERATION_DT], [FINALIZED_DATE], [ADV_PMT_SERVED_DT], [SPECIAL_INSTRUCTION], [DETAILED_REMARKS], [IS_DISABLED], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _COMPENSATION_REQUISITION_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) + select [COMPENSATION_REQUISITION_ID], [ACQUISITION_FILE_ID], [LEASE_ID], [ACQUISITION_OWNER_ID], [INTEREST_HOLDER_ID], [ACQUISITION_FILE_TEAM_ID], [CHART_OF_ACCOUNTS_ID], [RESPONSIBILITY_ID], [YEARLY_FINANCIAL_ID], [ALTERNATE_PROJECT_ID], [LEGACY_PAYEE], [IS_DRAFT], [IS_PAYMENT_IN_TRUST], [GST_NUMBER], [FISCAL_YEAR], [AGREEMENT_DT], [EXPROP_NOTICE_SERVED_DT], [EXPROP_VESTING_DT], [GENERATION_DT], [FINALIZED_DATE], [ADV_PMT_SERVED_DT], [SPECIAL_INSTRUCTION], [DETAILED_REMARKS], [IS_DISABLED], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_COMPENSATION_REQUISITION_H_ID_SEQ]) as [_COMPENSATION_REQUISITION_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; END TRY BEGIN CATCH @@ -497,10 +756,10 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_DOCQUE_A_S_IUD_TR -PRINT N'Create trigger dbo.PIMS_DOCQUE_A_S_IUD_TR' +-- Create trigger dbo.PIMS_ACQNFL_A_S_IUD_TR +PRINT N'Create trigger dbo.PIMS_ACQNFL_A_S_IUD_TR' GO -CREATE TRIGGER [dbo].[PIMS_DOCQUE_A_S_IUD_TR] ON PIMS_DOCUMENT_QUEUE FOR INSERT, UPDATE, DELETE AS +CREATE TRIGGER [dbo].[PIMS_ACQNFL_A_S_IUD_TR] ON PIMS_ACQUISITION_FILE FOR INSERT, UPDATE, DELETE AS SET NOCOUNT ON BEGIN TRY DECLARE @curr_date datetime; @@ -510,57 +769,11 @@ SET @curr_date = getutcdate(); -- historical IF EXISTS(SELECT * FROM deleted) - update PIMS_DOCUMENT_QUEUE_HIST set END_DATE_HIST = @curr_date where DOCUMENT_QUEUE_ID in (select DOCUMENT_QUEUE_ID from deleted) and END_DATE_HIST is null; + update PIMS_ACQUISITION_FILE_HIST set END_DATE_HIST = @curr_date where ACQUISITION_FILE_ID in (select ACQUISITION_FILE_ID from deleted) and END_DATE_HIST is null; IF EXISTS(SELECT * FROM inserted) - insert into PIMS_DOCUMENT_QUEUE_HIST ([DOCUMENT_QUEUE_ID], [DOCUMENT_ID], [DOCUMENT_QUEUE_STATUS_TYPE_CODE], [DATA_SOURCE_TYPE_CODE], [PROPERTY_ACTIVITY_DOCUMENT_ID], [ACQUISITION_FILE_DOCUMENT_ID], [RESEARCH_FILE_DOCUMENT_ID], [LEASE_DOCUMENT_ID], [DISPOSITION_FILE_DOCUMENT_ID], [DOCUMENT_EXTERNAL_ID], [DOCUMENT_METADATA], [DOC_PROCESS_START_DT], [DOC_PROCESS_END_DT], [DOC_PROCESS_RETRIES], [MAYAN_ERROR], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _DOCUMENT_QUEUE_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) - select [DOCUMENT_QUEUE_ID], [DOCUMENT_ID], [DOCUMENT_QUEUE_STATUS_TYPE_CODE], [DATA_SOURCE_TYPE_CODE], [PROPERTY_ACTIVITY_DOCUMENT_ID], [ACQUISITION_FILE_DOCUMENT_ID], [RESEARCH_FILE_DOCUMENT_ID], [LEASE_DOCUMENT_ID], [DISPOSITION_FILE_DOCUMENT_ID], [DOCUMENT_EXTERNAL_ID], [DOCUMENT_METADATA], [DOC_PROCESS_START_DT], [DOC_PROCESS_END_DT], [DOC_PROCESS_RETRIES], [MAYAN_ERROR], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_DOCUMENT_QUEUE_H_ID_SEQ]) as [_DOCUMENT_QUEUE_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; - -END TRY -BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - EXEC pims_error_handling -END CATCH; - -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Create trigger dbo.PIMS_PRJPRD_I_S_I_TR -PRINT N'Create trigger dbo.PIMS_PRJPRD_I_S_I_TR' -GO -CREATE TRIGGER [dbo].[PIMS_PRJPRD_I_S_I_TR] ON PIMS_PROJECT_PRODUCT INSTEAD OF INSERT AS -SET NOCOUNT ON -BEGIN TRY - IF NOT EXISTS(SELECT * FROM inserted) - RETURN; - - - insert into PIMS_PROJECT_PRODUCT ("PROJECT_PRODUCT_ID", - "PROJECT_ID", - "PRODUCT_ID", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER") - select "PROJECT_PRODUCT_ID", - "PROJECT_ID", - "PRODUCT_ID", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" - from inserted; + insert into PIMS_ACQUISITION_FILE_HIST ([ACQUISITION_FILE_ID], [PRNT_ACQUISITION_FILE_ID], [PROJECT_ID], [PRODUCT_ID], [ACQUISITION_FILE_STATUS_TYPE_CODE], [ACQUISITION_TYPE_CODE], [ACQUISITION_FUNDING_TYPE_CODE], [ACQ_PHYS_FILE_STATUS_TYPE_CODE], [REGION_CODE], [SUBFILE_INTEREST_TYPE_CODE], [FILE_NAME], [FILE_NO], [FILE_NO_SUFFIX], [FILE_NUMBER], [LEGACY_FILE_NUMBER], [LEGACY_STAKEHOLDER], [FUNDING_OTHER], [ASSIGNED_DATE], [DELIVERY_DATE], [PAIMS_ACQUISITION_FILE_ID], [TOTAL_ALLOWABLE_COMPENSATION], [OTHER_SUBFILE_INTEREST_TYPE], [EST_COMPLETION_DT], [POSSESSION_DT], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _ACQUISITION_FILE_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) + select [ACQUISITION_FILE_ID], [PRNT_ACQUISITION_FILE_ID], [PROJECT_ID], [PRODUCT_ID], [ACQUISITION_FILE_STATUS_TYPE_CODE], [ACQUISITION_TYPE_CODE], [ACQUISITION_FUNDING_TYPE_CODE], [ACQ_PHYS_FILE_STATUS_TYPE_CODE], [REGION_CODE], [SUBFILE_INTEREST_TYPE_CODE], [FILE_NAME], [FILE_NO], [FILE_NO_SUFFIX], [FILE_NUMBER], [LEGACY_FILE_NUMBER], [LEGACY_STAKEHOLDER], [FUNDING_OTHER], [ASSIGNED_DATE], [DELIVERY_DATE], [PAIMS_ACQUISITION_FILE_ID], [TOTAL_ALLOWABLE_COMPENSATION], [OTHER_SUBFILE_INTEREST_TYPE], [EST_COMPLETION_DT], [POSSESSION_DT], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_ACQUISITION_FILE_H_ID_SEQ]) as [_ACQUISITION_FILE_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; END TRY BEGIN CATCH @@ -572,45 +785,29 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_ACQNFL_I_S_U_TR -PRINT N'Create trigger dbo.PIMS_ACQNFL_I_S_U_TR' +-- Create trigger dbo.PIMS_EXPPMT_I_S_U_TR +PRINT N'Create trigger dbo.PIMS_EXPPMT_I_S_U_TR' GO -CREATE TRIGGER [dbo].[PIMS_ACQNFL_I_S_U_TR] ON PIMS_ACQUISITION_FILE INSTEAD OF UPDATE AS +CREATE TRIGGER [dbo].[PIMS_EXPPMT_I_S_U_TR] ON PIMS_EXPROPRIATION_PAYMENT INSTEAD OF UPDATE AS SET NOCOUNT ON BEGIN TRY IF NOT EXISTS(SELECT * FROM deleted) RETURN; -- validate concurrency control - if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.ACQUISITION_FILE_ID = deleted.ACQUISITION_FILE_ID) + if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.EXPROPRIATION_PAYMENT_ID = deleted.EXPROPRIATION_PAYMENT_ID) raiserror('CONCURRENCY FAILURE.',16,1) -- update statement - update PIMS_ACQUISITION_FILE - set "ACQUISITION_FILE_ID" = inserted."ACQUISITION_FILE_ID", - "PRNT_ACQUISITION_FILE_ID" = inserted."PRNT_ACQUISITION_FILE_ID", - "PROJECT_ID" = inserted."PROJECT_ID", - "PRODUCT_ID" = inserted."PRODUCT_ID", - "ACQUISITION_FILE_STATUS_TYPE_CODE" = inserted."ACQUISITION_FILE_STATUS_TYPE_CODE", - "ACQUISITION_TYPE_CODE" = inserted."ACQUISITION_TYPE_CODE", - "ACQUISITION_FUNDING_TYPE_CODE" = inserted."ACQUISITION_FUNDING_TYPE_CODE", - "ACQ_PHYS_FILE_STATUS_TYPE_CODE" = inserted."ACQ_PHYS_FILE_STATUS_TYPE_CODE", - "REGION_CODE" = inserted."REGION_CODE", - "SUBFILE_INTEREST_TYPE_CODE" = inserted."SUBFILE_INTEREST_TYPE_CODE", - "FILE_NAME" = inserted."FILE_NAME", - "FILE_NO" = inserted."FILE_NO", - "FILE_NUMBER" = inserted."FILE_NUMBER", - "LEGACY_FILE_NUMBER" = inserted."LEGACY_FILE_NUMBER", - "LEGACY_STAKEHOLDER" = inserted."LEGACY_STAKEHOLDER", - "FUNDING_OTHER" = inserted."FUNDING_OTHER", - "ASSIGNED_DATE" = inserted."ASSIGNED_DATE", - "DELIVERY_DATE" = inserted."DELIVERY_DATE", - "PAIMS_ACQUISITION_FILE_ID" = inserted."PAIMS_ACQUISITION_FILE_ID", - "TOTAL_ALLOWABLE_COMPENSATION" = inserted."TOTAL_ALLOWABLE_COMPENSATION", - "OTHER_SUBFILE_INTEREST_TYPE" = inserted."OTHER_SUBFILE_INTEREST_TYPE", - "EST_COMPLETION_DT" = inserted."EST_COMPLETION_DT", - "POSSESSION_DT" = inserted."POSSESSION_DT", + update PIMS_EXPROPRIATION_PAYMENT + set "EXPROPRIATION_PAYMENT_ID" = inserted."EXPROPRIATION_PAYMENT_ID", + "ACQUISITION_FILE_ID" = inserted."ACQUISITION_FILE_ID", + "ACQUISITION_OWNER_ID" = inserted."ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID" = inserted."INTEREST_HOLDER_ID", + "EXPROPRIATING_AUTHORITY" = inserted."EXPROPRIATING_AUTHORITY", + "DESCRIPTION" = inserted."DESCRIPTION", + "IS_DISABLED" = inserted."IS_DISABLED", "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER", "APP_LAST_UPDATE_TIMESTAMP" = inserted."APP_LAST_UPDATE_TIMESTAMP", "APP_LAST_UPDATE_USERID" = inserted."APP_LAST_UPDATE_USERID", @@ -618,55 +815,9 @@ BEGIN TRY "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY" , DB_LAST_UPDATE_TIMESTAMP = getutcdate() , DB_LAST_UPDATE_USERID = user_name() - from PIMS_ACQUISITION_FILE + from PIMS_EXPROPRIATION_PAYMENT inner join inserted - on (PIMS_ACQUISITION_FILE.ACQUISITION_FILE_ID = inserted.ACQUISITION_FILE_ID); - -END TRY -BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - EXEC pims_error_handling -END CATCH; - -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Create trigger dbo.PIMS_DSPDOC_I_S_I_TR -PRINT N'Create trigger dbo.PIMS_DSPDOC_I_S_I_TR' -GO -CREATE TRIGGER [dbo].[PIMS_DSPDOC_I_S_I_TR] ON PIMS_DISPOSITION_FILE_DOCUMENT INSTEAD OF INSERT AS -SET NOCOUNT ON -BEGIN TRY - IF NOT EXISTS(SELECT * FROM inserted) - RETURN; - - - insert into PIMS_DISPOSITION_FILE_DOCUMENT ("DISPOSITION_FILE_DOCUMENT_ID", - "DISPOSITION_FILE_ID", - "DOCUMENT_ID", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER") - select "DISPOSITION_FILE_DOCUMENT_ID", - "DISPOSITION_FILE_ID", - "DOCUMENT_ID", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" - from inserted; + on (PIMS_EXPROPRIATION_PAYMENT.EXPROPRIATION_PAYMENT_ID = inserted.EXPROPRIATION_PAYMENT_ID); END TRY BEGIN CATCH @@ -678,38 +829,46 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_DOCQUE_I_S_U_TR -PRINT N'Create trigger dbo.PIMS_DOCQUE_I_S_U_TR' +-- Create trigger dbo.PIMS_CMPREQ_I_S_U_TR +PRINT N'Create trigger dbo.PIMS_CMPREQ_I_S_U_TR' GO -CREATE TRIGGER [dbo].[PIMS_DOCQUE_I_S_U_TR] ON PIMS_DOCUMENT_QUEUE INSTEAD OF UPDATE AS +CREATE TRIGGER [dbo].[PIMS_CMPREQ_I_S_U_TR] ON PIMS_COMPENSATION_REQUISITION INSTEAD OF UPDATE AS SET NOCOUNT ON BEGIN TRY IF NOT EXISTS(SELECT * FROM deleted) RETURN; -- validate concurrency control - if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.DOCUMENT_QUEUE_ID = deleted.DOCUMENT_QUEUE_ID) + if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.COMPENSATION_REQUISITION_ID = deleted.COMPENSATION_REQUISITION_ID) raiserror('CONCURRENCY FAILURE.',16,1) -- update statement - update PIMS_DOCUMENT_QUEUE - set "DOCUMENT_QUEUE_ID" = inserted."DOCUMENT_QUEUE_ID", - "DOCUMENT_ID" = inserted."DOCUMENT_ID", - "DOCUMENT_QUEUE_STATUS_TYPE_CODE" = inserted."DOCUMENT_QUEUE_STATUS_TYPE_CODE", - "DATA_SOURCE_TYPE_CODE" = inserted."DATA_SOURCE_TYPE_CODE", - "PROPERTY_ACTIVITY_DOCUMENT_ID" = inserted."PROPERTY_ACTIVITY_DOCUMENT_ID", - "ACQUISITION_FILE_DOCUMENT_ID" = inserted."ACQUISITION_FILE_DOCUMENT_ID", - "RESEARCH_FILE_DOCUMENT_ID" = inserted."RESEARCH_FILE_DOCUMENT_ID", - "LEASE_DOCUMENT_ID" = inserted."LEASE_DOCUMENT_ID", - "DISPOSITION_FILE_DOCUMENT_ID" = inserted."DISPOSITION_FILE_DOCUMENT_ID", - "DOCUMENT_EXTERNAL_ID" = inserted."DOCUMENT_EXTERNAL_ID", - "DOCUMENT_METADATA" = inserted."DOCUMENT_METADATA", - "DOC_PROCESS_START_DT" = inserted."DOC_PROCESS_START_DT", - "DOC_PROCESS_END_DT" = inserted."DOC_PROCESS_END_DT", - "DOC_PROCESS_RETRIES" = inserted."DOC_PROCESS_RETRIES", - "MAYAN_ERROR" = inserted."MAYAN_ERROR", - "DOCUMENT" = inserted."DOCUMENT", + update PIMS_COMPENSATION_REQUISITION + set "COMPENSATION_REQUISITION_ID" = inserted."COMPENSATION_REQUISITION_ID", + "ACQUISITION_FILE_ID" = inserted."ACQUISITION_FILE_ID", + "LEASE_ID" = inserted."LEASE_ID", + "ACQUISITION_OWNER_ID" = inserted."ACQUISITION_OWNER_ID", + "INTEREST_HOLDER_ID" = inserted."INTEREST_HOLDER_ID", + "ACQUISITION_FILE_TEAM_ID" = inserted."ACQUISITION_FILE_TEAM_ID", + "CHART_OF_ACCOUNTS_ID" = inserted."CHART_OF_ACCOUNTS_ID", + "RESPONSIBILITY_ID" = inserted."RESPONSIBILITY_ID", + "YEARLY_FINANCIAL_ID" = inserted."YEARLY_FINANCIAL_ID", + "ALTERNATE_PROJECT_ID" = inserted."ALTERNATE_PROJECT_ID", + "LEGACY_PAYEE" = inserted."LEGACY_PAYEE", + "IS_DRAFT" = inserted."IS_DRAFT", + "IS_PAYMENT_IN_TRUST" = inserted."IS_PAYMENT_IN_TRUST", + "GST_NUMBER" = inserted."GST_NUMBER", + "FISCAL_YEAR" = inserted."FISCAL_YEAR", + "AGREEMENT_DT" = inserted."AGREEMENT_DT", + "EXPROP_NOTICE_SERVED_DT" = inserted."EXPROP_NOTICE_SERVED_DT", + "EXPROP_VESTING_DT" = inserted."EXPROP_VESTING_DT", + "GENERATION_DT" = inserted."GENERATION_DT", + "FINALIZED_DATE" = inserted."FINALIZED_DATE", + "ADV_PMT_SERVED_DT" = inserted."ADV_PMT_SERVED_DT", + "SPECIAL_INSTRUCTION" = inserted."SPECIAL_INSTRUCTION", + "DETAILED_REMARKS" = inserted."DETAILED_REMARKS", + "IS_DISABLED" = inserted."IS_DISABLED", "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER", "APP_LAST_UPDATE_TIMESTAMP" = inserted."APP_LAST_UPDATE_TIMESTAMP", "APP_LAST_UPDATE_USERID" = inserted."APP_LAST_UPDATE_USERID", @@ -717,9 +876,9 @@ BEGIN TRY "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY" , DB_LAST_UPDATE_TIMESTAMP = getutcdate() , DB_LAST_UPDATE_USERID = user_name() - from PIMS_DOCUMENT_QUEUE + from PIMS_COMPENSATION_REQUISITION inner join inserted - on (PIMS_DOCUMENT_QUEUE.DOCUMENT_QUEUE_ID = inserted.DOCUMENT_QUEUE_ID); + on (PIMS_COMPENSATION_REQUISITION.COMPENSATION_REQUISITION_ID = inserted.COMPENSATION_REQUISITION_ID); END TRY BEGIN CATCH @@ -731,81 +890,56 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_DSPDOC_I_S_U_TR -PRINT N'Create trigger dbo.PIMS_DSPDOC_I_S_U_TR' +-- Create trigger dbo.PIMS_ACQNFL_I_S_U_TR +PRINT N'Create trigger dbo.PIMS_ACQNFL_I_S_U_TR' GO -CREATE TRIGGER [dbo].[PIMS_DSPDOC_I_S_U_TR] ON PIMS_DISPOSITION_FILE_DOCUMENT INSTEAD OF UPDATE AS +CREATE TRIGGER [dbo].[PIMS_ACQNFL_I_S_U_TR] ON PIMS_ACQUISITION_FILE INSTEAD OF UPDATE AS SET NOCOUNT ON BEGIN TRY IF NOT EXISTS(SELECT * FROM deleted) RETURN; -- validate concurrency control - if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.DISPOSITION_FILE_DOCUMENT_ID = deleted.DISPOSITION_FILE_DOCUMENT_ID) + if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.ACQUISITION_FILE_ID = deleted.ACQUISITION_FILE_ID) raiserror('CONCURRENCY FAILURE.',16,1) -- update statement - update PIMS_DISPOSITION_FILE_DOCUMENT - set "DISPOSITION_FILE_DOCUMENT_ID" = inserted."DISPOSITION_FILE_DOCUMENT_ID", - "DISPOSITION_FILE_ID" = inserted."DISPOSITION_FILE_ID", - "DOCUMENT_ID" = inserted."DOCUMENT_ID", + update PIMS_ACQUISITION_FILE + set "ACQUISITION_FILE_ID" = inserted."ACQUISITION_FILE_ID", + "PRNT_ACQUISITION_FILE_ID" = inserted."PRNT_ACQUISITION_FILE_ID", + "PROJECT_ID" = inserted."PROJECT_ID", + "PRODUCT_ID" = inserted."PRODUCT_ID", + "ACQUISITION_FILE_STATUS_TYPE_CODE" = inserted."ACQUISITION_FILE_STATUS_TYPE_CODE", + "ACQUISITION_TYPE_CODE" = inserted."ACQUISITION_TYPE_CODE", + "ACQUISITION_FUNDING_TYPE_CODE" = inserted."ACQUISITION_FUNDING_TYPE_CODE", + "ACQ_PHYS_FILE_STATUS_TYPE_CODE" = inserted."ACQ_PHYS_FILE_STATUS_TYPE_CODE", + "REGION_CODE" = inserted."REGION_CODE", + "SUBFILE_INTEREST_TYPE_CODE" = inserted."SUBFILE_INTEREST_TYPE_CODE", + "FILE_NAME" = inserted."FILE_NAME", + "FILE_NO" = inserted."FILE_NO", + "FILE_NO_SUFFIX" = inserted."FILE_NO_SUFFIX", + "FILE_NUMBER" = inserted."FILE_NUMBER", + "LEGACY_FILE_NUMBER" = inserted."LEGACY_FILE_NUMBER", + "LEGACY_STAKEHOLDER" = inserted."LEGACY_STAKEHOLDER", + "FUNDING_OTHER" = inserted."FUNDING_OTHER", + "ASSIGNED_DATE" = inserted."ASSIGNED_DATE", + "DELIVERY_DATE" = inserted."DELIVERY_DATE", + "PAIMS_ACQUISITION_FILE_ID" = inserted."PAIMS_ACQUISITION_FILE_ID", + "TOTAL_ALLOWABLE_COMPENSATION" = inserted."TOTAL_ALLOWABLE_COMPENSATION", + "OTHER_SUBFILE_INTEREST_TYPE" = inserted."OTHER_SUBFILE_INTEREST_TYPE", + "EST_COMPLETION_DT" = inserted."EST_COMPLETION_DT", + "POSSESSION_DT" = inserted."POSSESSION_DT", + "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER", "APP_LAST_UPDATE_TIMESTAMP" = inserted."APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID" = inserted."APP_LAST_UPDATE_USER_GUID", "APP_LAST_UPDATE_USERID" = inserted."APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER" + "APP_LAST_UPDATE_USER_GUID" = inserted."APP_LAST_UPDATE_USER_GUID", + "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY" , DB_LAST_UPDATE_TIMESTAMP = getutcdate() , DB_LAST_UPDATE_USERID = user_name() - from PIMS_DISPOSITION_FILE_DOCUMENT + from PIMS_ACQUISITION_FILE inner join inserted - on (PIMS_DISPOSITION_FILE_DOCUMENT.DISPOSITION_FILE_DOCUMENT_ID = inserted.DISPOSITION_FILE_DOCUMENT_ID); - -END TRY -BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - EXEC pims_error_handling -END CATCH; - -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Create trigger dbo.PIMS_PRPRAT_I_S_I_TR -PRINT N'Create trigger dbo.PIMS_PRPRAT_I_S_I_TR' -GO -CREATE TRIGGER [dbo].[PIMS_PRPRAT_I_S_I_TR] ON PIMS_PROP_PROP_ANOMALY_TYPE INSTEAD OF INSERT AS -SET NOCOUNT ON -BEGIN TRY - IF NOT EXISTS(SELECT * FROM inserted) - RETURN; - - - insert into PIMS_PROP_PROP_ANOMALY_TYPE ("PROP_PROP_ANOMALY_TYPE_ID", - "PROPERTY_ID", - "PROPERTY_ANOMALY_TYPE_CODE", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER") - select "PROP_PROP_ANOMALY_TYPE_ID", - "PROPERTY_ID", - "PROPERTY_ANOMALY_TYPE_CODE", - "APP_CREATE_TIMESTAMP", - "APP_CREATE_USER_DIRECTORY", - "APP_CREATE_USER_GUID", - "APP_CREATE_USERID", - "APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" - from inserted; + on (PIMS_ACQUISITION_FILE.ACQUISITION_FILE_ID = inserted.ACQUISITION_FILE_ID); END TRY BEGIN CATCH @@ -817,10 +951,10 @@ GO IF @@ERROR <> 0 SET NOEXEC ON GO --- Create trigger dbo.PIMS_DSPDOC_A_S_IUD_TR -PRINT N'Create trigger dbo.PIMS_DSPDOC_A_S_IUD_TR' +-- Create trigger dbo.PIMS_EXPPMT_A_S_IUD_TR +PRINT N'Create trigger dbo.PIMS_EXPPMT_A_S_IUD_TR' GO -CREATE TRIGGER [dbo].[PIMS_DSPDOC_A_S_IUD_TR] ON PIMS_DISPOSITION_FILE_DOCUMENT FOR INSERT, UPDATE, DELETE AS +CREATE TRIGGER [dbo].[PIMS_EXPPMT_A_S_IUD_TR] ON PIMS_EXPROPRIATION_PAYMENT FOR INSERT, UPDATE, DELETE AS SET NOCOUNT ON BEGIN TRY DECLARE @curr_date datetime; @@ -830,51 +964,11 @@ SET @curr_date = getutcdate(); -- historical IF EXISTS(SELECT * FROM deleted) - update PIMS_DISPOSITION_FILE_DOCUMENT_HIST set END_DATE_HIST = @curr_date where DISPOSITION_FILE_DOCUMENT_ID in (select DISPOSITION_FILE_DOCUMENT_ID from deleted) and END_DATE_HIST is null; + update PIMS_EXPROPRIATION_PAYMENT_HIST set END_DATE_HIST = @curr_date where EXPROPRIATION_PAYMENT_ID in (select EXPROPRIATION_PAYMENT_ID from deleted) and END_DATE_HIST is null; IF EXISTS(SELECT * FROM inserted) - insert into PIMS_DISPOSITION_FILE_DOCUMENT_HIST ([DISPOSITION_FILE_DOCUMENT_ID], [DISPOSITION_FILE_ID], [DOCUMENT_ID], [APP_CREATE_TIMESTAMP], [APP_CREATE_USER_DIRECTORY], [APP_CREATE_USER_GUID], [APP_CREATE_USERID], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USER_DIRECTORY], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USERID], [CONCURRENCY_CONTROL_NUMBER], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _DISPOSITION_FILE_DOCUMENT_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) - select [DISPOSITION_FILE_DOCUMENT_ID], [DISPOSITION_FILE_ID], [DOCUMENT_ID], [APP_CREATE_TIMESTAMP], [APP_CREATE_USER_DIRECTORY], [APP_CREATE_USER_GUID], [APP_CREATE_USERID], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USER_DIRECTORY], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USERID], [CONCURRENCY_CONTROL_NUMBER], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_DISPOSITION_FILE_DOCUMENT_H_ID_SEQ]) as [_DISPOSITION_FILE_DOCUMENT_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; - -END TRY -BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - EXEC pims_error_handling -END CATCH; - -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Create trigger dbo.PIMS_PRJPRD_I_S_U_TR -PRINT N'Create trigger dbo.PIMS_PRJPRD_I_S_U_TR' -GO -CREATE TRIGGER [dbo].[PIMS_PRJPRD_I_S_U_TR] ON PIMS_PROJECT_PRODUCT INSTEAD OF UPDATE AS -SET NOCOUNT ON -BEGIN TRY - IF NOT EXISTS(SELECT * FROM deleted) - RETURN; - - -- validate concurrency control - if exists (select 1 from inserted, deleted where inserted.CONCURRENCY_CONTROL_NUMBER != deleted.CONCURRENCY_CONTROL_NUMBER+1 AND inserted.PROJECT_PRODUCT_ID = deleted.PROJECT_PRODUCT_ID) - raiserror('CONCURRENCY FAILURE.',16,1) - - - -- update statement - update PIMS_PROJECT_PRODUCT - set "PROJECT_PRODUCT_ID" = inserted."PROJECT_PRODUCT_ID", - "PROJECT_ID" = inserted."PROJECT_ID", - "PRODUCT_ID" = inserted."PRODUCT_ID", - "APP_LAST_UPDATE_TIMESTAMP" = inserted."APP_LAST_UPDATE_TIMESTAMP", - "APP_LAST_UPDATE_USER_DIRECTORY" = inserted."APP_LAST_UPDATE_USER_DIRECTORY", - "APP_LAST_UPDATE_USER_GUID" = inserted."APP_LAST_UPDATE_USER_GUID", - "APP_LAST_UPDATE_USERID" = inserted."APP_LAST_UPDATE_USERID", - "CONCURRENCY_CONTROL_NUMBER" = inserted."CONCURRENCY_CONTROL_NUMBER" - , DB_LAST_UPDATE_TIMESTAMP = getutcdate() - , DB_LAST_UPDATE_USERID = user_name() - from PIMS_PROJECT_PRODUCT - inner join inserted - on (PIMS_PROJECT_PRODUCT.PROJECT_PRODUCT_ID = inserted.PROJECT_PRODUCT_ID); + insert into PIMS_EXPROPRIATION_PAYMENT_HIST ([EXPROPRIATION_PAYMENT_ID], [ACQUISITION_FILE_ID], [ACQUISITION_OWNER_ID], [INTEREST_HOLDER_ID], [EXPROPRIATING_AUTHORITY], [DESCRIPTION], [IS_DISABLED], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], _EXPROPRIATION_PAYMENT_HIST_ID, END_DATE_HIST, EFFECTIVE_DATE_HIST) + select [EXPROPRIATION_PAYMENT_ID], [ACQUISITION_FILE_ID], [ACQUISITION_OWNER_ID], [INTEREST_HOLDER_ID], [EXPROPRIATING_AUTHORITY], [DESCRIPTION], [IS_DISABLED], [CONCURRENCY_CONTROL_NUMBER], [APP_CREATE_TIMESTAMP], [APP_CREATE_USERID], [APP_CREATE_USER_GUID], [APP_CREATE_USER_DIRECTORY], [APP_LAST_UPDATE_TIMESTAMP], [APP_LAST_UPDATE_USERID], [APP_LAST_UPDATE_USER_GUID], [APP_LAST_UPDATE_USER_DIRECTORY], [DB_CREATE_TIMESTAMP], [DB_CREATE_USERID], [DB_LAST_UPDATE_TIMESTAMP], [DB_LAST_UPDATE_USERID], (next value for [dbo].[PIMS_EXPROPRIATION_PAYMENT_H_ID_SEQ]) as [_EXPROPRIATION_PAYMENT_HIST_ID], null as [END_DATE_HIST], @curr_date as [EFFECTIVE_DATE_HIST] from inserted; END TRY BEGIN CATCH diff --git a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql index cede96119c..cc62290797 100644 --- a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql +++ b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/010_DML_PIMS_STATIC_VARIABLE_VERSION.sql @@ -18,7 +18,7 @@ GO -- Update the database version number. DECLARE @CurrVer NVARCHAR(100) -SET @CurrVer = N'93.00' +SET @CurrVer = N'94.00' UPDATE PIMS_STATIC_VARIABLE WITH (UPDLOCK, SERIALIZABLE) diff --git a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/999_DML_Delete_FILE_NUMBER_Alter_Down.sql b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/999_DML_Delete_FILE_NUMBER_Alter_Down.sql deleted file mode 100644 index e000fe8fb8..0000000000 --- a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/999_DML_Delete_FILE_NUMBER_Alter_Down.sql +++ /dev/null @@ -1,63 +0,0 @@ -/* ----------------------------------------------------------------------------- -Alter the data in the PIMS_ACQUISITION_FILE table. -. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -Author Date Comment ------------- ----------- ----------------------------------------------------- -Doug Filteau 2024-Nov-14 Initial version. ------------------------------------------------------------------------------ */ - -SET XACT_ABORT ON -GO -SET TRANSACTION ISOLATION LEVEL SERIALIZABLE -GO -BEGIN TRANSACTION -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Alter table dbo.PIMS_ACQUISITION_FILE -PRINT N'Alter table dbo.PIMS_ACQUISITION_FILE' -GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] - DROP CONSTRAINT IF EXISTS [ACQNFL_FILE_NO_SUFFIX_DEF] -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Drop FILE_NUM_SUFFIX from PIMS_ACQUISITION_FILE -PRINT N'Drop FILE_NUM_SUFFIX from PIMS_ACQUISITION_FILE' -GO -ALTER TABLE [dbo].[PIMS_ACQUISITION_FILE] - DROP COLUMN IF EXISTS [FILE_NO_SUFFIX] -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - --- Drop FILE_NUM_SUFFIX from PIMS_ACQUISITION_FILE_HIST -PRINT N'Drop FILE_NUM_SUFFIX from PIMS_ACQUISITION_FILE_HIST' -GO -ALTER TABLE PIMS_ACQUISITION_FILE_HIST - DROP COLUMN IF EXISTS FILE_NO_SUFFIX -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - -ALTER TABLE PIMS_ACQUISITION_FILE_HIST - ALTER COLUMN FILE_NUMBER NVARCHAR(18) NOT NULL; -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO - -COMMIT TRANSACTION -GO -IF @@ERROR <> 0 SET NOEXEC ON -GO -DECLARE @Success AS BIT -SET @Success = 1 -SET NOEXEC OFF -IF (@Success = 1) PRINT 'The database update succeeded' -ELSE BEGIN - IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION - PRINT 'The database update failed' -END -GO diff --git a/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/Schema Compare.zip b/source/database/mssql/scripts/dbscripts/PSP_PIMS_LATEST/Alter Down/Schema Compare.zip index 6e0e1eca83dac5f77c146d8b171c19604903e41e..115bb7b4e0ba5c49244d2bd0d682b9b3ed885a3d 100644 GIT binary patch delta 311204 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
_m<;_wi- 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

`Ber?|${i-`or zoQ=_zpe~Ng#U#kYU-Rbl#eSbMH!8b|hnjdkJJf-FaEMevOWq1p*JM}v)%i z+PsZ6y;~9#KPou3~OK05iKNO1N$e?K_Itr*lyRI&e#rL2f z+G{e#8TpzjKAIt;XCMR2N{hO!k|QwLpUaw6YY3D+Bd5Y<+M`I}d!K3Kx=4cUHAdCD z-&+!yp!gaNem;+J(BJ6WhBmgj*WFMnrQema=KMs%+qys)SYwd0vof$Qoc_f}V7)$l zMR2Cu$>y~~`*pz#$;nD%PMRU9O{SFMM99Hj*K$U}EqQ)xnAvP>J zNXW+M@V>e1@Rq8#3W%wg?IYRkQoaGQ>exlqT=%?2Jc;WQODyZ94z>h5^>0hzB4JbQ zAt4)Ok{yN}e&*^hm|Mk!yPZ&Jr5Z(%xkU$?_>Fy$N;L07O>~Q1A_@GawrY_`S~~Gq z=GhYY>K{}5y8J8F;a2{h9mFc)b7A;Gi@no#{Bg(L=W4Xi(jGF~L`l+K)1`z~apT$! zd0FNy>P*rFF|YA1GJmlt48b0G0Kd=sdUj6jb}G-)kFV0OIblU0-`bac&99klWX+J7 z-(4xbJ=i3_ED`GW$YHSyBbA(a4iEMMhb_|d?i4@U#?V97kru~|ThWOxRGQ^U^Jy&> zF(AIk!*UVA#-TV1Np)lk57<6CeaHxXXFXlve04cegSQ{bnZQ)37JEaVvMNFXi<5td zipVe`+i20S%>5(m00#1E_5;b`%1o6Y7t)$WXr?f%ypvx_Y>RO%jCGaOkbJ+M*c<%i z$D-|mAv}rlgS)rMhk&l&A7=8?6i0zs6qI3)tU2T+oVn_gD-BKMwNbsh8@RHUd(nvL z)ywNj)uDYH@hGK`e1!+H43H_txSsKRQ(p4A6c{D0Wc)hJj%R^eLgVVB3ctOh@on{I z&n)V?-I)XjU$S%Lh$Kk#VmwcFlK2rxVxv2^3jR@ag>vmQcV^b?~ zGuy|m`J5vE=p{9>O8i8y#Qk=@;H*Vmsixy!yQ}EbF5CC{C=boZT@(AW9#zpZ6jewt z7TpMdv>AJJ!bs{@F_@svqaV{;czSX2j3}de+cu2{hMh>PmZNZDzDNCtW`#JbuDyHw z;%@I~U4GPuxHOwr@SS-U2US6$gsFIuDUJ+GE#TrhdekBTavSpIGSonpIz|P>|llXO57T`j|IrdO6&Z*^82< z0k8Q5(D`I^W)#_35XZn|Rp^V+*S&W#c|Hc2!bR)km~$L&hi)os-_LwPt7SD^H3817 zL;ATRhLkG%F89;tDlcXyQJ-zS6?SaD7jGrlH=JuKVGzF_a3UM+V!T-c`xo9O)NNGM z_xaz1Kh)DMZeQmiPWi?fM{{3^Cu92hjQYN#{(AovPo6-#$x(ZZn`RK+VU3O~0d`H0?pIHLT#wq=ni!b^5l`MVJbI1q3?DSG3BVD7blwgLD4ZU) zcX~VM*kCO;5Ve!E2{av=64w56#okMU{bl=br6J1MjbZL@Jqndyn(u1__XWXM6c^p6 zdiR#z5SHIEpmwnN#hTKGBZFmG^7CcB28DM0oa$sVQe4bT5F`E%pQR~86`sP8z+GD{ z^jKD%`5d8NEDtNk)`})MIv%sN-0xMXe&p+fv%LgUaUxD}mzsR-DFrbdGN-hu z)i1pHWue?FnyYDPa&7J-GFGCvmKBX{s4;C~6q2h=9|4adX=XUv>-bFx(ng!00Om`s< zB;rXjUFqp@&&diGC`W@^XVP5^W2(-iJB#S~4@4`@F8t5&41%YCPWl|NGI4gve<4R@ zbmON7B`2f+2o^lFm5vbJCk~|dvz55~{Ht~~o$u*^$;lT!t#nxMlsw>EI~Dj};v;s7 zfAaxO!yWSINfE9MbR*|D0pDGGbM##+UC=3Ahjlt`xR?}h(jVRZ&*4vtAA|(4(@pp3 zoLZt%fXqzSm-- z@IOBl=fG!y)fffusM|PdzAMr&P!Dq*RCnSJ!EVv=tMe$EFwbrwN z6V4fGTkbNmVIJ_nIa>e_EVy|+0|ERw4?y-<0DM7wIB>K`iVzb7>zy;rJw@O^a8w+8 z;Sdr5kpSbL(*git!M|VSAb@X*gVEqE3cyM3&u#FGbV~N5 zT4GEAVw;MWPihl2z+ca?2XMeUze<;#71sc)bdDu}yXY%(rr@*pg8gj37d;i#7F>7A zB9;uo4Oh5#DSanG$`w3)THAjhm~aEHi&cfl@d68;qw`qhG99A;u-7>{00a|G74{Du zqcE`iIXdEM0B9}t9}s68_~toE0O(TAQ6PCLDdO5w@WiQID(;ih!3Sc&*WvgX;ER_J zj55FuSSQ(LPjV3d0inQS)Pd^tQfKG>w=-2Q2A5*~A73EDC2GKe7p^)5)PPB_Pv9r^ zWW=^+(FJ?@pX$PNOJ^6#Yq?djMJryFa8+?|$7^qKfxc)d872c+h|1#(eI zc~l7e94nw46CRq)L;%Ov0rV5YhFt6%-em(J%ih?K!c)fq3?~9-)4eQN5h3KP{{I400jwIylP4g4Nz*m@~ zD0+Mu^7pzH8iLOS^5LAI02mW~@9{;ezyXS+h?2*Uh*R-S8iRp3Fm-bBgPaaoe`*hy zIz^p2heIEP2X1K2jscGhy+{r*6bczR^{_u6i9em(A37{C5YAINC(NSZkkiD|f6C|K zkSC|Pv;h*l@MoF-$gz?M#NnNGl4s@UaQmWvl9Id&NJTTB`nd z#0e+dR}gIYQQbw*Zv#;xv|mBaa!j8fw;ui1C7uz3+gxKNL-@D;@r^$?ba<52e{K8^ zcnXb~9I^cx;&WkwVf+we z_{q<7+si!wkh>q~0hZ4x&-WXQ1*fw6@21%w9tG==r{}y)!XEH8&)t7&xqBDlc&?VC z4uQH+vcF8M=@8QSS9M-EfI~l>q3PNK>}Qdn7g{3%4H-&(ZiIR62=F$*hFtiNSraOB4bj8`mG}$1=tU=lQaPY~f6GPS z9<0DhOPK?T1wTl=Xq(eX@ijiE^SR0a$w5)!1Hw>Dcpn-aF1$z>dL5qTa7kvwFJUO; zoXI#`*)O>q&sZ-&zw4#8fv}T`&GOAP=BYqukj=|FJ$|ULUG*%HDUEi3<+k{$AhyL#omY z`t-Ek|3EO{4Ib=#|B~vphMrw`KD`>~4@HA3KEB*_09Qtd5iHKov$qgV0Pu205*jsr5)A03 z?0nco{xu$JHuS-1OMOm2@CSMcg;WAvIwg1xg$_@KvGe{*(7Y1LeoF8}VTo42z&Cs^ z$@qIVLyq8XfKHxM2moI+a7-&S>y&TB1Oqp`YyfB`Hr@a9Bd@xlEvL1hQcQmWy6DOH z35-Mtf;i3Ujfn{_DUcoduZ6$wLeJK7 zCp>`EYA=I;UL>DgDJc#sVq5?m5+fa$pDwx!l9I)6$omtDF-YM<~#Pr@?>;aSJ{D>4PbM zEVfHezj}`C03SfMF!WM`N0{EA=RbAD6CDBEMe%9*>CZZjKjpykN%S8Zn2ORL{-;Bd zJYc8icP?U1`^sN;=+8!g-_!u$1!i!dO@a`1^7Q7XlAmzY*8-@Wj8ck`fOiQ7T=XvZ z)+=B`&Ow#F_MCBmZ!QoKk*I!k#NwHh7e(4NPo8E`Gfp-YJM6r zvusL#HjO@Upc3nUs`c2K{%m9KgifO;phar*<+fz*LQi*2TL5=aa%?YpyK|0O{^TD% zNCEVb|Ct`h#{<-}VeDo1A&Q`HJ!hd49DW@A#R-CT9Q|2eeIhho1|W24@Z;4v@hWllC4{`mF$ zOZv0mr!OHuxo_g-(H-!J`cqX;AagDBrx)t~kYI16C-`ep9{3y_7=AswFSh;DbW}6l z^k;z+v^%wTwq^7Chaxe!1`&SE!Cz1n57*5E-d9o-xjYrFmASw^ zI*T1WDeE%zQe}p--6Oy>o)AZ{8qYarJL)bZOXR?8nZeR9g zKVdLs7(J&ofJ28H=3JJ`*NWk+*FNbBKzRVuSx1J`=al}`{>Fokwv)hx$c+5*g2!@7 znRHcsDtGTiZb0|Y?O&_E$m(`nr`H0)S@b7ADSg{StW8r-nJD`1XRYF9COmdEUrL=w z?qB1}R};aS^vqZLcBQs#4r&&FlnZCEhMA92w8A#(}P17Z8*$ zTcHgMTbRG%cFXv4?A4$JoLdfSwav4ggW$asj(bmh9=stXdN}LiUqtxb&4^HDSG^*h zIX%H5PJ3aVjpQg?I?`td<53+sf~&9|hh|Ch)2yOHd=A&P>YYYi2ktL1nneZIVV$b* zsBgCOIm6OK$wYY6BXG1pWuXGmp|@}KUGG#A`w`nHGVmp(_iBlWBKE!_ICQ4QKty-a zybH#Lv(>|S`>xw5PtsSlKhqhKvYmtWXa{P*?#P;_-^cuA)d9=(4tuz>97_H{g-@y7 zB1SOLRdF!%I#Kq(ou55dEj2jGz_8vjPQvFHwxh}Ux5z}8dw_1=Lkd#W5gBc;DU+F( z0R!v$x?~K5xPkZFgJh}vYL#L+({34{*Wu)`20XzFGFvSBT47d^yo|)zk39XWoAc^8 z9br_k5Q-krs&s#MfI?mrO=pw*T?$X_iV@94GZTCDBR|yI*WU8T%<}u+IJHpszp_kG zFJw(Euc0R&u|276Oy)MU)6gElfxXC2Y3K;pZmbV^IOWvACvE+~iL*`ebN@Ygl9uMJ zQnmqv)eH8o3;`p>+pZdHN^PZB>yIVx=55g4UGU~FGd0R6xz@td_v)+e2;x2!znF{6 zsD(JvG|%ncW8G&Swz#F^$A|YKpe9Jc4NvA%io8XgG{`=_leSND(|DRJxO)!<3Sio$ z*NHs#h!o}%hkg~O9fxvz&wVG9REj?Wyc$NKCg?jP2=PF~1wu%C+ zv59X}#>9}~eVgGTMi(>`rM2i0>UF`euQIDj5hlO?MQ=n}DP5A0P){jcunBYJf_VkY zA(SKqcqc}g-4qc@_TI7&(qM)36E=}(vtu?dEw7Q3SJuhjME5CsI}LhzxJJ}^bjMfz z3p(BOkc!BJizeC*3um8?imcQtv0T&R zLF^&zRt^xgjrb=i%H4oPaJ%n!3Sc?uC*Rld3jyoPJSbdTAo~|ihx3y}w@6u`C|PEaiPw4tMNo#`)82@ksmLVz z{!)yb=e-|UgsFSoo`3zjq>1;!IvpnB?+sR{l}+l4X-gg4@SWcE!<^4r)HoX!7BW)(N>aI$*@!7l)nBs_sV{FFekBavuQ6GH4@2|Qa=Suhs-(U{784?j z$nqsbvB~m%8Pz7e55ER!T?u!<9O<&B&gEK2D0xPn-~GU~aH(sekkYOUClMNIgp*v% zyU&f>?HlZl^3hrb6&9gpSkXt7?`x`LJAj0J*DuU+fb}31X>iQ&J~c^d*#HGsypn-K zeoRcm%mjD6pz>N*6|E$YfIjKxJTjL0I$V~>iE-uyS*WeP-f-HrD~rk#2WxxPDYZ>d zJu`FG!zmo`_bzd{*E}Z)G9j`H!Qmh#2112lo-oP6MFfXrGi?0`<=Hb|zP2QC56&80 zq2pTSYduON-6RU=!w;PgkdC6{-E?0q3{k^3Yn>_CZ%k*UTf3J&etA^4`UV~p4#Z{}l!?ai z`Dk0|hj5O$vo6Kqmj~WCg(UX|auYC`I`1*p;>_-hAYi>}1_yGB#vm6&I<6B&^IMU5 zai?5yPQ1zEE5C1kIEcHB$^$n)ve?hx{5aY1KA9?r}uPL<`dp9Cyi z+$Z{<+2n#su436+?cBa@k@x@^O^C+`Ow^U$f&%aW;WuayI5746mYn46~naw^w|rqKO_% zA2$`<)9X_D$u|4-$qy(eDGn^^B$?cY4^=i@sUvl&j=z)1*_Zz92a)C zg#Uv7b9des;tYPrpC-PlsYjNgexpViuTVf3zIxYD{18UN>Ix6j^ks;pcp*(|B)0@w(ZdB=Z# z1MQOIV2WF^_sz&zSHD2gLGaPQ2_D-%vHp@+Sf=Y^vEGb2d`CsLwA-qZ%qljAd1x=< zCG-vFZtSO+#T29Q`Rw=oP8BBM2D=8}Uk9#)XrCVPV5}D4cNnRMV$ZTWh#DiKbXN}K zZ%06j8;jyPId~Qrv$#q%36#T@*#){5B#)WP2N+nb!7odky+szS!_e%iBuU#C*v7Hp zttt#eFlcL|MquPDs+T9zwAJf-jg&>AQrx^dgYSue^l{hI$APHiX9d{bT?J`?^l@K+ z^l|;a)5qbfS7e9NHg?(13~1-GeEpe~;*6`!xSNWp@;iGRlp^d|Vd0008m7-G^<9&Y zTgtppZF#PcG#WlPDYS`@l2DfWpDn~Y_r)<_WGU-!^2YgMQ(}p4$+mV2VL`&*A~@0g zfVX?1Wz&k&)P&C+k~sh_%TI zi?H!upMP?dX(dQJhs8Gp8s;jtw-17 z8U4n*$Hk0V|C2LL{+)i;IDTD7j~0+KZbv8K>hxd~(p6}V*Y?l~Cwr?eD_U6^?g9b! zcM-R0H8GH1fZnI)u0kZgMQV7j^g9ilOX8Es6)lj2YH75dFg426yE1%s)oSd&89BnV zg%{CCzDtxRASRhfLnL0k31)x5%`8NM8I#nATd~lR@uC;nA3OBm^*b-3Z!_;&K$UoT z6)zN?O)cKiq{MU2Qkc~lc)y;%TmR?@Cx3dDM*1$+isMk=l)~Mz3got_FX-**Z(#>* zCiaGpla3R_O!BLF(sERHs=amu$ee}SV0HNAIJ>kn)b!YN;FmY6K%OO5HyEf zvr$W~uQi|ZM?O8ul&(;z`cQbrl;-PC&89C46@eKoaP_T)_c zVCQRLM}r>6Z<1*pra$BiVw-5m8-j<5di%smqld%}R6aZ_x6J9E^tT%dnCb8PsH zJa(Sn_xaCv;TsP&)F$te9d8=M%ICkB3B6X6iO&j%!x4oLmMOc;;iNJ?N^snz)V zdA6HB;|4at&#TiLjW{4hZs*Er^G6l(;&?065V5d&X}1KujD zD=MEoReHkP9CT%R*a$`5q2Lk6J+-RszS9U(VU1UQJ)lwi#VWhV;GwM#>PL6~Re=bV z7!21D|7?{j*K(ixp(HXi&K<$Jd&g0cftA(Dy4OUHO`OsKYme?ma%Zv4_3^CI!2*SL zC)CCDz!A;aCTnZ`Cs|BIdr~Dd8|JF+1M8I^Wu-C5jb?~xq`r82l810}kHKn@}s!fPlX>h9as z$Fe2<Q_-WI7^vtZJUhNa2v4OHb1H=L|!=ofV(0DCQ;mwz`OuRR3!c#CqjNSZkO`-pzZMXjcKIVNxyrdN| ze&rh^T2vO&4GFhC=tg3WrKu5z5aFI9wQuUd#8vkZN#{&-@Jf%G}2q6_~aJ8Ny%eLO)V;|zuRY?#o0-a2*wMw zRydg0R#9Z87SVdoQnR#3h?HAFm{m{`?MxKTPadRdK-*h1v~e>vqKK$mJB?@6O<7bZ zqwO;h(>{wk3sh}R2$tfSn|a9LnWfM#t+P3AASy0D{G;6cr@Z)l+ilrHEe7WP7DBXQ z?46HN^PAW=JCAa6M7ou!O1QhXHX}WI+BVFDg$I>gjor~024)t=M0tDT_ui)6XQ*?v zYwk2Vm~Y#tL5mtM#;ar%)KthAtrN7LT+V_vI|_@lnAjpv7-4D+?qkbBSx=LVf%iU{ zeMU=J2oHaydfq6kJ)jq&*r+6 z&!_W$R0$dd@YLG=?SG6-OVbKp=AAmx?#i7#{?8VvbqxEwp{wKG-&2Ma8XSA&hKlMM zpYdM%Rjg~CWVy~QGCY8Pw_oUtrypiK{+Ze*@JPpo>a3U-tA;m~y9AnNceym}gU5}) z=>7RUtS3Knoz1x}@?G)a(z<1TMkdWAHr(&nKG;1hWU9GoOuz9%G6E(q{p~PpsmJIz zx3NpYrbgHQtvlh=P5hr`H^j?# z(w%qOX~@L4;d?b?eMmkGdVa567g`R4L0wVqq1NY@BtcN|oFjJCvZ@ZlW{gTB|9u$F z%;FBqrOAjNp0Ep)Reo4Lz_(WK_WS zYKXYg#*;Jd*m+5n%x4_+xIMCaBvUBg9P*$dNB*E|u=Q@|%XU1_MgD*{H_a{%OPU}Y%&{M}CSf*uvBKGST+JhsyK zYv)HFvvNG_UC}aSd*(qwg|huox(w9!(fRTrc&@g604;(#Z#X7*yd%DAWH0&k6iUdB zWAZ|HR`!4C^0Yuyk#8q^X6s(j$-bAgPP(FI!k$@}K%xC=`g;)KiDFL3x7l&s?Dt6Z zFglocsfWGfKpfcJ(CJ?GjJz-M{KZsk;ctJJK7XM=f>ZLTFA4qacgnb|`V@KEa7ym9 zL7IiAD9#DAFOdp$MZkb_2eG{IUq{%d(2}XjY@BMZguaO7ovAb4eje?IB_b$EaRjp@ zV$DhRlGB}*?^RIK)Hcrcj<)|nE2BU`XCck4_>7!U2r@K`L^gX3gxxz$=(cF>olCwa zY$mjuv;Teczu)d_V#4iL8I84mG*Yqh` z!>U5Bo;7B>UoJ42*JBmZuvI^CPuFGsm8SkD?M8R)s;U>e{#(KLF}7}b%R(wEkC^_K zzV!Vqy-PPF{0`N@eZY;Mp0+OV$?IooK@)Sva?bot?*9_rKYcD;P- z;*%eL;wOwfnAU&!&CG*iiOxHxU96d0^yucKUI|vs#eMxgtcrL(rkmv+ul(}pHw7DS zP;ryDuFk02l4t{UtLTdt$*@WAie5AC(P5JtcOt2<$t(z)%o4rXyhO3o(C^cfv7$k| zf7gy@zdmRl(hVNr&x_a|dov;9(5TCQI|NV9F^E%uNBFC^4u}$kcJ4iju)Yv=&fmVBYW+cR=PH3^o%a~o3;35z4a2NK1y94xpWJ>zu)_Y9hJDavn(en$u2oh!Uo7~76+ zn4Rjnc)O|b;P{hU=eD2!VOYGU&DTt>M$B<|ga5{W^3tvs_Rnn$Cg0!}l`Opx4v+9J zywuy<&Z(|T|Hv+H2dPqz@K4+N;O8Q1w|-9xqt;h$=GXVVUvahHsGe6Wx^?i(7at$p zb7=jV8xuXxRrfSXx4M-%a7C|dyX>`l1!NRu=-n{~9yKHwbgMVdAMy&F+%BrJ>(Oq* zGwYGFu0EbolB|5h>;1sMyB1Rv+t0eczv|-yYqQ%1BLjcgHXJzg*XOlP z%!)g1eAgBKnQFeYd1tPZ;Fj$gTT^1K_m+;?np2 zn~gWZYRrQR6T3k$rNiL^SGT5xxpn$jIl-r?{L_Q}{bQeAE$n~x?*1Kpl61G<&lO!h z`!|RGYn#eD>(pg)PH6ZQ1Sk(%w1pHT^2YWDEv9XYICbNn;!LYL7` zT~~QUIDZiToYzN5wOkQGDtFRv2Y9aT*LUIR7v+&}o)+%@bgI&dW00Y$*uU(h&8PL( zKHaNba5j6(tSdn;x9f&GXh7uQsJ8ae5}mt2yS)t|c=68vdHt!d2VD>ND%_dzVzGacC#};^Y zf8X_cQ4f<8+a+#4^N27@_PuztOHg`-I#@lsD*E^!YZ1Kpf6X^0k*{m*C_ZrX#*n27 zd-v=(`H0Ym0`RvVxp9oAvGClOt7Bu*)F4K5Il{QlA%hHcrT*oitDXeZ-s&E?<#pJ( z9><1T5tffkdtCdvZ0dA@;p_6xS=l)%o{_#a7c1_4ouid_PkF+0uRb~FPg;Lz*jxVN zP;&J|wdLaHdZLx*CpW&-NV%}2e#~EeQDlD9ZfMz zxZJQAqA8_vFYvdF<86~Q5!}}?5>0q(Vn1P{Ld5mKO)q9l=M;8zvtQ>5KWzWeevFe_ zPVXgqr`k0*FL@K0xhQeeodL%J_fIkEHfPBGVvWXIn%05o9bBE36d6Ceap;4+3)h9e ztDt8^w_x{sdZ+uWB10#`A##$To$+PZ;8GJZa-#jmaofEAwqJi`sc66IMB-~(QJ*<_ z6{_a`pY9qA46(nE@jch=iu!1Kq&C_nKc0)TCO`kH$N%i8~ zVfV|v-hHsLow{=d_l{mu#}RttW5fBPJS~eSzVpsqAdY(d%T*g>G$TmFiGFdnB37aI zu8(~>IjP&vu;}z-;Sv zhOK(mSgU5489eWIr^@u^@Arxu9xr+v|KyZM{Hmi78XL_9=z4EIl`&}B+JvuBou+jT z*8cV?+gN;WX48=gkJ6rv&aHcwP#r&a-_yd=dDF)}e4RSp$!S*ex%Kmc)`X7YKh%GD zq_%xX=!X!~jLo8V>*t=i_47mlZ_tM9X)a%9{qit~7fwiW`FXL@>h9uuK?je{yj(of zVUT$8_qi|g-o%>)6TyP>d>XZJ912bPfo-I0LR1{OLcl_pu z0U?b^r}=M7hZ&Y-Ej(W^;C8|67{}B%A2%3(dga`xn{{&WML~p!6EO4DOOI-szCj6V zC-q$z#^E2!^Vo!xqgN}R|f~bxn=S>ZPJzGI)l{%JRkjNGQa8) zFUM%gJqZiI=nNPZznQYu!GyulC%S0ka-!u5vLn?|S9Jk~aZKjmRNl#8Nw>$?FGH z^#1-hMXNH?zwUK%kdw(r@ipxVjp2F$s`{sYkG*6+%Q11^>RYQL`|WvpZTQ;Rjq^oC zGtb>BH>|(9XyTKttJZ#ws*l_7cfiP0t&t;7jT-sX^@8vfC(fu|*vGQn?QNfizut3r zSo^srjs_pOQI536mfqYsaniSX^VgB?f-fW2ULSC%zGltxJC1Ruw$F0(EiX^IS#^ke ze7&aAoCofg%Nx=PPyAVtf7MrE%eTOq`JBoh`xc8tg^7Isf_r{hKXcYCUwLiJsjiRi zbnd_(7BS9nmwn)<`Zv$xO2@nH`;@lta(UJ3m4-7XL_P3IemPP70)3Zs6&*M5u1ug4dNpLQ_(`a8DNq{qaoTEAZ$yJILG+#r}%Ycgu$ z)x{GM7Y`DZU3dF6FZycIW#u%^y2|3D%m3Nwd^+wn(zNzR&6DU0v%??8X2-01Hrd^4 zf$gBMVcRY2)~Sqq&t2a+*r|^C?~xT*>&`1w^{TiW>gSRza(R81>b`F zMLl#K*Ku=qzP$B#L}Zd+Q}pZ!bD9)*1$iZ?@7H+`_lqZfirq$20 z8DceR;-uT>Y>&rtE4WRUr>zR9-*+!1>Ttrx+v1KJc*4Z-nNMeSR$tJw`}Zhcw z?}v_!I6FIg+)9^0QRRy)-yEy|eRrsPbau&NlhS$GyGDxaeiDIZ^E5)Xa}W7j4$isL zn6vSg+HgDN4(s|q6P;S^J)<_#NH3pPsy8o7#bEj~j_2Gn1D}RT) z+Y{M2@cO(S149(Ptg5QWm_H-AXop_l-oNvEUmoy0d)keTcjGVZd$2E|(`lXLEl+Mo zW#4Y!oO(Doc2ej0!)Cv`7L-~yvdaH^sPnvzKGA=v=k;~N`-`g~$a2?7dcHs(FG!vV zpx+AEAhM5UZ1cHE_L5ijVdC^e?kn1CkVGr&_sZqucN-NJ})m~_b$d>4j?LM(}6aoRvxhbDPxM$NqZIKTm-iY zbaL(87=;eXwLc(Z5g-|x-~$VQN*%LPJJ24*@|%6$dHY=Yf-YtA+ft?kvD;Pq?lRv` zzaU?xPj~E-=v%>5FhKW(Jv>QX-KI?6s_i8S(JdfGD3TEKkZM*NCccJYQ7}D zhlX39*2pW!(LkKtY~P!fOhtm$U6zaMh-WD}NPbA51)_%1)f||i61ey#Nh+l(>b$N4 zGgP9Z=OBsuP(t37Ld0p4X&WJxrVf&~#9KgZgG}Q4)(*Gn20=lp9UPd+t%Re)S-L&7 ze6NNMxZ1Wu8CM~)<#SCwvK8&*Ao)pNijjA%vv1357cHrkl3TA`x;tE>z1xCRLqTP7 z-W9_SA+M46lzh?B)8C;FBlDMfJi|o1-{0YYM4SYsffkp`i@V+5A&3@7U0G@fae&Tr z9dcfq_riq-_3NGWkmg-;`dxySIOw532gxG<)WsU$XrR37^0LcEIY@q!pasVaZF+?| zNTQOI?xT?o^wTCSdbm*z$7L??#NL#{WA_sN!?@b47RB)@aeVZ(9v1O9OTp7v0&_HLN2-~4|mY3b zhV;3HmJ$VQboe6U?fBF-CBBsEFuYZ1+8ucb=@|~Wtx5m^udnQB)6(JM`Ewa9;gwL> z_lF%ABZ|0ZwnIybKWQz1G*EDcgCR<)gnn&#+<|$IvhQ(+oL0RgT&{3)&NzIQQJYcR zrf{E%9j3Gv4sE#Vz-SI+?5(}{$5n^LGI{`{fznhQ;Rh-1L8Dyx(}H0ZqVCx1-f}oA zqqw-L^=*bj*c?T836?D5k8gczXweDjxv&tx;bi~(JpNYK>lut7=E`z z_F^I+8QwzA9Qh_WQ42oitB1g$uzl9@r zyAH1Xdx`H{a*UG}#SD|{N{T|v}3rpUA!fWRft26?agzjC}SHMt9l`5-6iz_tcQ8A&%jGnIrn z7Hz@TLI1u&1;a_0ulFb7k@J3v68)u(P z=6{lrv;L2~9R4=`bD35F5DnD1S*}%!;bFB^v?!_t4BE?QP7GOl`CI6sQT6)d7*u}v zUwJvFj_@VF^VNc5)<1Ea-;-pQM=tovPe+I))^SAsu`kgV(5B@5~ zS1CXEooL+^7gVj0wYES9T2p`idmRBt=W5Z&!9%V}@jn)4l|;J|~WXf~kTD74;+k zb85+GHB>fK;KXXC<}iU7cbPhJ86hx2KEnhitf*)bwMTTZqCOJq!rYLuezz~JE=jx~A34(BiX(VZ`gbXcXtiU)W&S?UUQ9BkG7S2)zw3Ts! z#cU>_ig5t_(UpX{q*!|xpw2oPI;b&J&;|L1LO&JiXy}q3RZ|vq$99b3@gU?{_ZA@z zDRI3VDSFC#cSDq0A~0pueJBKI$sRN<%X>Di-SLJ;mp%fI4}u?t*~jp>n-4`s6`_K) ztd%viDCcS}P$xoV&0$b^{w?OJpqdgz4MaQ^Xs1+&^uP<$UuBeiqILHV1^k!e%c2QO4}+h;ua5X z#Wx0NBPV-KYbksu3hYhTdRut|--4kh(2YlF zAwM~>mXPb@om2rOG=LH^`sLR8lU*$e1AeXCba> zYK-lhaRw~e3zjfP(T=f9>FrXSW(>lOAI($2gV{4khF8jwgvmJP9m{D{zl!|!fb#~; zCeb;p6_P&<#5K(&0X7wO0Gm~NfmzYG$)s7YD zq4+6O>$gbBU51?-2F3!bc^SzfQi|7VquN+#(NtCE2{c>&!rrP?y%T1i1plrC|B4tj zoxZ9?Ae8PaG#ikGvf>1;tSv?-p?ld)kVE$WbcP&@b(9?B94}bG+G6g%v8^scOY*_z zl^aQH$CQH2+i?xzbN$Mf*`R0`7(9)kD0K@tt=gmn?$Ab^XF_%4+9<-jx9g!tPqxuBF2v%g!Y(QSyBSoe(>^DGpvjqcK(OQXs_Bnt)CBkGlK2Z?K>XfFp zkMgd8QH0BBPr5(3*Yc3x=>3X4iZ$ za>zL;bP6kK>;gbtDk4$7DOx4;(1wykP%y0$?0VW~A+QolT3C80XQ3dDRld_AK>C-` z$PHyCDPd*faV35_u>AcUD6uY78gq*ITSf^$XBG=gP@Fzbo3fDn=)$VCc`;}$x=E9= zL;{nP$G5R+2hSSXi(ERs07o+?#iTo=1oj1I(yYtwr-8K^SZf&7)3u7$8@dFl$9V~6 zX+87(XkBu#Q4M_NU~};y#ZOUtYMAo)$r#))zijsEvg;^(0Sq}ap3;o7wZ|0u;1C zFo~5v#(3W3d%*7teCF0|ftG}?%UOYQS>8r)nV+5%sE@)n!S*fGfHMh4$=U=Ib3++Q zvt)uW>3Gp1&OcRFL;w6aDrntydgsy}Id6ylm=%fHec#*;7L@oh=|#+wY^;2Xhg6SNiwCVVU8<1ZznVexC!3@h> z9j8vG#aPV(rMKdsCsK1<%SQrdL&ILM!6{x!PQU?+_W`hMmK11&DiS$SICT1cKqt;2 z(H)WHXdPvgJRfW#10Be0w2m725o=S>0U$q{Pm*m=@)C|C`nZTQ3#ZEWAfU4sOVJx} zK$}AV9KBQuR7K{81-fY9ZjJ^0uLatK6>E3f**&hT1l;}s+`f)6#}=+&ikr*IZ`wE3 z_Xh9@=&sC#>ew|jKPd;MKJRRSHM*6;u_gc2leH79&4x0x+aM(wq3UcwEGsnZ2tZFJ z%Ruu|IP-9WPkF?MeMjifb=+1dhrJWezMVg!62%XLPW%E%v6d+8s9-*;DesShDTmUf zP}qIu3MR8c&*TEsEmH>SyqmKSSEW1i0R3&ZEP6lGCgnrt{hY2i-hzDK?b(m?PQZd% zG+L;Fw2nc*w%A)6>6q&ppzHYp2UIKOVMDah$3Q^DVNyVMRCr7)o6|KE^^l6Jmh;@nM($Geu0I|z1PALnjLT_DLhndLq_8%Hd7rPl9|vkIxQ7`}GpeGgzaDUA;)*r!JeZmG5N*By z!>&aU?DQ(m!}yr_gcE?HXchv+;HeBHv=B2K)sWC*wNfbiNU6Af{5`UY+Ckwn86&Ol z3zF)M`WFjK;Xp(i>0W?^Cw%I{>hlf7psx8f&H4A1lZYGmTs#jF&{RDLL+8pFP{1a>1kFT=!9dr2^zRc6>_fF5s_LRrtzkoOfpg>umC`_TP% zT!!^-*%g64%9;q1weMB>Bf%Fpc89THJn>_lMM39 zk9Ct&WyFPPaIxX?^Cos9i|BRd9}SvQtIb`D>sRW;nw#WqcK|dk=0-V7N6NN971spI zSS`NMW6Pzdzz>(>&(J6NcF4Is*8vS22eo4D$c3Z*n0DI8r#;scrNiG?W#&B^5LgN_ zyMW9CjOO0gSSoWXu0+p`M4UWeC&`W1Tr--TWy7^X8kXEZTw7AV0TsB!Qi`@gu{SVb z$y+3}LkAifepApD`E=r9x9UYV0sXlXjkdlGBg(CwLM`NcOE8VKB^|v5_##Ic{L$Z< z5QQ^=RU>%Q9<_W_Ma4%kOYrmxkg-ZAmC>1+&?)x63TT%9Z$h7c8@OFvq+A2UwdC5M z@O>(psMr~5Q|XSN6RYK3cYt}sm1ZX05iDRuY2O7@uO2e;eLc9Naf6cnjE=`Iqi_ot zlrDJ4aEiRS*uC=MO3)DLBSGJTtr0APv{7Rpt`Ty+CvadjYuY^^J?SMS>7v4WnAL%v zbtsSQv1GKB8QmM$m*jUrN2&zIWI6%zoyFBm8_ZQE|KiN5_hS|4eH=jZyzdJlSi48N z6E|;>D#=hQb3qb5n541~qzfJh%s3xV#eIQm+C!MdY93%(wC_VOB5?$5#HNRsy|&W! z&NsWqhrOE5&MDN8`u$y9=>xN$rd8($AUquJ`z0!^S*_W zC;~OYaXNJxO*&T#X;0*0PvrPdfEPHC=J`Z&2jL=3rKdJ`Ku%BK*!xfRHdxH>4QMlhPy(04Up=&4yW^j0E3sXZszBH`DTxSepmFf6USh8k# z5pwFFWWGyC$#x`zJ-HzCIiNC@qs?zXZqPceIVyZENMv>O{SfhtyI?;VzGbepB3DcK z>u}}}FF{eK^)xX4rC=j#b?ChU)Qe40afT@C6&$WKZs88Xxz)I`NTUqQ;(}Qz47W~C zk&zSs8Wuj8&RhdXEL2fNjnAQfz~4Hvnvwq+%rHhI-4*Hl$Hg8g-B_)dN1oknohk#gP9fxX;l zoJ-74_KhXi^&7#e1q_$;+eeql=O7mw$X)#Rz%Vk;j7-{L=9!foL>Jy+^2|csgK^GB z;D<9{WVe2&0wJ3Zu!B7J9+UY5H?CoX$YbJFQBH$^eWm}z=8b71ShWMHnYooKInET4 z$m(;`kDzadn1uF3&W!?PlzA4v&Ur+l4GW}b_Ux?-jex2wq*0m|xdzm11`7yf)OZTI zr|u^h_^dB-jc^rZ9i6f_?xb`sNGpJX)LIT~<`_Ir%sK9Hh z{=Co7qO4*CrmX#WmV!qJc^p~=%9+8z)exTjf;l>G`T|OYH_)@Mg5GHCSHUP&*-abl z&B()m_HtuIb_Gd2$y!H;zJctql_bCu`FwBLQ~Y}bGnn&t0sG;};_pCucb_B);n@tX zBC37F9fk8*&mVvedn`rA;Hu`yRpqB-%>OHRhZ)2BSS?}y#3VNI`yNa>1=MpuWv+ZS z)JYX9qVY{|#!YRSpn!VhNA@%BtR^to;FS#XLz5tw)$NBb{MvgEG{=MHd5n&~yq=s0 ztkI$`u)NEA4~s4`O$PmoGgL-d@42oxH&kdW6O(Qr(=C~!>zDUTiEuyqHze?qTkRI9 zCgh*kcY+^(L!q`eN=0CosYm_*Q1z<>_=_AYo9Tw3lNN8Ngc@B{wbOoZ+v6kcqGnRn z52=I%Tv^?=EDCx6WtHFYnf|8P&CT2l9Q&L{m})JsH$szV?icI-((D@?9+v&MchRC< z!0rrw4rA2Z1upVaCORVcC1L|~PKoD%Y1*mfgc%nC=KyvR3 z{Mn2`PSut2HPILz!9J>(jqi?ffFBKfW;$u5KFN1P9}-oSk*^|QK;@#LWE-BE^lw5| zha@S2LtyvZO0Q5NbkJi(Vkj%#M+xv}+cWWG$}QYZk!k}GeykqI$lrC0oLgr?379kU zJWEpKW+%3M(Rs&ud?3@vOJ<5GnXY_;L3qc|19meq#You_M) z2`TTv2IizQu@h;70O@KFifEGq&jRVF62n=Ayjhn1aW@E&Z?FFxWQ4%44w|BHH=ZUk zCU};ZmW$e}flr4JGQ4eSaEi7-jTp#U2M0-z9eam5pa*xAqV>roK6cq0 zr~&9>Zc;S-n70Noh1Ca}hPm0Z9}D}tqfc7kNUP;WIw(eyV9%6o)&x~QyqHk9u_DY3dN{tCqPigGK|Dtk z*q_&t{Fe`|;x_04<-{N~)PU%T{sr=^Q$)>%sIeVEphy2qG*OTq(Vo?!MS4J77bGPb zpj+Ju74$)mU=R09ZwL6XBcynJ)YzS{N(T2!VxYekuPL|a}5uPMUN%*^+; z9!-iIfa1qNkLfXvHyY>oKDyq-Z4hB6x1hN@jugSZpfNQBvo}L7v=)p_#TjJRG$h)S zlYSsh(UVkvzH52*WQ#H;|K){iRwL6!gq(m9Zj~GK z^Vdq{7@+qXBrZmImV_zVVMREw_9!2(+x3S*+z1dC!*H1AM#SkvI3wrHJnWU4wbo$& zuFW*CXe-PP-#QSM2>#Up*CKn`0HtXwO=&>9U|eA4?JM0Xw8@$<;>K>H(LNmr_8`@+ z4j}VNI*D>YwL5vrsMLZmM#=kls^mv=oG&{LoS#DO+A5&%%rK)~CN0c!H(4E<$k3)W zp^H3r^E%@SrezC8?AcB7dZMg-ylFVCA@Se!$xKZ$DIk^M@*(?4b~bDGEb0W}d50fXm9E&B(~(jl%i2bjOBqC~H3izHIVIv@^=GC)m%2Z^bwTlK~@g%w$eh z>rc?sBrz`$XVEhUu*erOph+E_9SQc3@0W_h!^w@Nd|g~FKo=Yd_K@#1KFAI(A^~nF z=prv1H-HAKDCmpn)4ul?(NF=we%fcgxOyRZrd|mxf>}2UuaGQn6i#3kI-RxB&vgdW-rF=P?g7sXnK%*CSvzuGK*0nuyLr9b zxG?yhl<$z{j9CVrTtDq7IW3Znl9`bDkMtmPawbX_pnd$xh0%-z|O=QOqEEzc62>k4cCS``Q) za*pviwchJ9$gC-0ktWk1LLIVY=Dw?AtcVaH9y&4{X4_9L1v6*8}Nc2Zl z8eQv2bU+@Sn4!;O&&vDcic@~M7^^Aev$xcxs%7WM;B7KEUO?6`r8FgN)wbVreWF;IDpClpXR3 zAm*@^?^*yr&-9_8Sp#9T`OqJ36AK3r?3blB4gkD&fDGPykRtZ7#O-z+YRMKWf41~| zkW>VF89WDpqU0bc3f7f_FdJ{1!2lgRf`*0;#w--7NmS7&8YK!Ol#ulhB9zrBn}-0@ zdK?o9`3ysdK-MvLOO&1$+3CqKml@Yv94ZyUe*f-a=f z3vo_Xw5Dz%Bi(T@>A*)foS!nrg7V$TB+nUHk0VUz6vJ_tuy1qh^m6h}8%&Ej3q3d2JP z_OqDG5TI|{$)p>gZy`i5YpV?n1-$NV8GKeK5zPv<2m|PfeKJs86CphF7)HQLphMuQ zOPsM#o%|?ZrC%rMH3wPfD(G)EjPo_wioUoyS`ZH8o=2o)bu>O#(E?>nfK~$k!X|tB zP5|DeTp8Z!39tbH9{WY*W)kmxTn0~^QtXc#a_(>WGlz`jlV>1P8H2}$Q&JIOtlk(J z0g67Jp-~$nFe@0%Nr0Mgo<@x`Qa3_jlZbJw16iM*n}faHC@5UDwFFCKU%`1?j21@{ z?8y;Dk)UAlA>aY9f zm5{o7R7XK!A;Yl!XQgbdlptFz6!(@;MMYJrkZNTu)M@#X4Xe#6{6QU(kj)mb`Fmj- zITbVE*y`U~=nqmk@F#OzRKk~=k{C8XFB8SKnx&7i&dF)ovKNmbFOH zrBXhOcsG@>akZqpLpjL%B23bS@P1J`4Qq2~8-9(S(M+ zr1=LosTz^x)qil*B#&665JyCxta!OW$5=wfdtcq@IgU&+w zujNmcxXz->-QYVPUD!hy@^(XF&Zp@_SBkS>JKgeV2?cMJu?_E4{-yQ_Xwyw-yH(0* zi3I$LniiOlrKRkRQ+nAa;?ztuV>eI~G=Rc>&uK#0mW$dYE#$0f6zL^aA<6KPCeCRxY zzOkaA-t!5_ADKt=Vr`qQoXJzs#+%SKxwcaLFxK&5$-=~$bS-h=Diu{gZ6{@Y^lv`l z&nkP{W|c1}ybxrE5hPxSG8YhiSY>xwed_*F+Ib1iB(@t0SV)Xw#ik!xwo>A}v#unT zz2-s|ffJtgAOUtLYY}E2rneYShF&x(*+*F;t+z6~lCzlT%c^^M?#hi3$R-zB>PSDT zQ9#y5XE-!;7QxwQVJV@V@-I^rIf)1xR+mISsxn2^_Xs22*gmav71ViJdPzgFHz$18{6 zND(PMw`WjYmv?teE2%yDxRmf?tpwEpJsf7BTiH+vzd~9WYAA0R9JUruQg+AbI(OIo z%|w)c9hjyQWtktRfd6|eC)`*)uw*%Sz${8aD4(tzic|14ZuGV!G@+SnAJMW37O%j> z#v4|E?9^BZR=ARgWG%DBr6-H%(TZ0!vlZM970v_8cg|Dx#0}1Yt3bi8M2gc9C9fjb zFE$znKZ&7x70-P^D_9SSRzouhTTS?|x+Z@$NDmQ72-+)^eOxh51^j5|)hh%h+(K=5 zr;ahmi~mA#EK6^*~`;l(ARd2CsuM z+}lJU?NHcrO(BI?vE=$#zv67dT%S7 zeNe9ZynJ>N-0X)La>GHCmjY{8^0^fx%7v+QTrPg9YS;vWYB}6JmQwtai2&AuW+nsl zUmgw3JESm$?YhU~ElYbL_(K!Rk{rJ3;uZcdyK&&e19?TCQP7`ULAta#fK{5TZ98#Fn-m%+dLjPD0d=}?5AMj8$8Rj4DQAIbx8Z>cz} zn1+h>5XRh%Uu77Jf5Z4b_P4SxE?S}RC8u&WYild{Ny`!aQyze;nQd3C2gcLe^!I;g z_@)d@UwxMW!-gh2ppu_LbWGEPD;n{`VBt_P6xKggDk=kTBY#gf)3Qlm;d>bQgBc@# zppsNrA6)bG%LJ{XAWkdQs;jHg6IY(+o8OgCY>bkgB59r$R=+J$UJhDJh8)&p279DsAz z*3#`J`m+uY({_V>#TGOa9o-8rd}i6IXro12Ag+*Yh@as`}>K3 ztoF?wcgRb!Fx7M6m&~`qdl{lQW zv9_D7sR>6r-S-V6H|pvDK#8`iEsNtehh$n`4N;K{F}YY$15ntRts;p!+kSw%aonVid$Rj{x6 ziB7;YTem?ZhycSxEH$Nd!1pVd>J z7eBp|Kob`IM-$+pycY7^rP2>Kf4@E)oidYk{ywx*MnsVqddE&N;mbO#d=f*sZta%f z??H}(?`cd3qet)`WqRJ>Htv@p@zE9zXVs0GV)^9ii-6g|AjcN;UgGBZVZ0sXwk4bzS z9T#*Kx0N`pfau3sjmI7>+Y*KHD_{dKvVdm6?OY|Kdkz!4znPcQJA!qE6Hz3?&pU@% zdT`Ezf1^uiC|rKUz9KO7aMC(@0_W~6mBLlg>@ASdq6@DAA?rd+5Pe)B=qM?dAss3t z{8(F4GYNfgO9on0rJ{w7ZdH+7&4rKH{OFt_AS+eUWKjmxoJBG0yUHG^^u&!rLyLi8 z`9OwJR1B@1(nm>vkLJqHn+P3%f%kux}Fyv_)?(W5yS+D=<^| zHcJNoMxLrSt}btrNOyQjlUgqUQVD%jQSF9^TwX zet}?0m|81mEN(#IPpwmwtg+W?wc=$@1;Y$f1vt9R^_{BpIKbPdCrf{N6AmV*JFicT zgzPO-tqclw+)%;_C?i5xhnmr0-RA4jo#-+9>)eX*PMEH2ykfl*qmgD!lAkS;|P~1~kq;glbn@rEHv8 zco7}F43*-`mnE*fLAbLTm^tbGXUTbwJN!arOKIRr_8*umTTX1Hmq+2+V>eaoyKQT3 zg2l02WW`k6Bu20vXs?~GwQm`W)2rdoFM+WWMQ)S;TXg*&T>0JP1&6&kO|VD{`pY#z zMYnLn&n?jX#GRscLYw?lu{YeZD*$Qh(~49=&3#lg(BBG-k4~D}Yf4TUT>NEe^#Q7$ zxSB{X-G7QcUU2It%lR-^wFi#leYm1O6+z*-4wU6I+#w9ohN)tAgu=T}qN#xtg->2g zRYQyKV%9|$CT&|U*_9^`mt_Q2!Z`0-iJ2CcRDz1UQ4+$nN=yve>>j{Qj+4Md_dw(# zE{vtV!K$hVKC*Yve3ErMjAF&(oHI8sU?Iw@fTn#tfHPNbGxB+9xip}RD&_5BZ z!XHki@B-w0pXkNf3$%_X`q3!@yt-);;~(EAMzThqA`6NFs4y>AHy(vQz$8n*djNxZ zmjn{f1-U;Yda({8n;rsc&|DG)uThh?QEMM!7K@5$6~Cub!xMMUS`(V7Y z7f%ntIsFh^g-ji979t7dI&Ppjx?5F^k?sv;ZIqX<3ct1WlyGKMHz@yH>T=|B5f%(F zn`vrNZ^)AOsfOXucbjTIoX$!8OeRUrGS8&S5N}pv5?-BZz+L~g)6m*1ICj@41RJwz z2tI55JtgsuJ867{GXat5y{bOACVXev(&@+=)`#37yCsxbVj^oJJHJJkiyAWknzmmG z)kEPqs@UN2zh_{}!^1Qhu1~^mo9Clmw)2IP2Fl(3$5;Y8dONMMY8W&ZuF~o&01%DP$t&$6`(NS>fg5}zm z#2{9`HH}%2POVC~>q}(t-mfsj;fYahV`tDo*E6Lwfx0T0aT5lt##flp(C;q8s#^%jX2BBwW)>p>?L&Z%P0gR!ZksWopfK_|a@FsT0n8Wp$zCIF2Z zRcvxs1Bs4(LZW-3@aIrc$jUN7wZDxu$&Z$}_8Yr*xDOrSKU_GDn+2{&?*G(cBt`l}i@DiAus;`K6SLNly?ScC(bS|5L=fu$_I&1{g30tQ zR{0l&O|4Z^Pratr9XE1iCs+Rv(ZjLfU${i$p=OVCI+Hg@I;fe^{~G%apJ>2W{XTat z75(4^an)q#-fC*fXyHeQ>}53)uB?^tb=s@x)X>9Af_wo=bNmCiHSY5XQwzI3!Sr3C zE{k2Kr-ltMB{wT~oKF``0p6;n6x153nV=hnYJRxkO5+RYaMPE-gT4^#50n*qyW6DwIpmYrH}TosnB+}3v{ z9f4A@ML|CxX$fw0sUg94OdedAulcMv=s5Cua4rr~zA70_Hj%j?HtjHB#oFjDPTHxm zgq}4=5F|gD6?Sa}nom~;H^W(?Mir9RH+6@W67&-@Cm4n$#?f;E@2)H5k@+}32n#}| z#84Slyx%=2s4F_}qlV3{`quO<^OL}-t5z!g)v)1& zu-`CbB=kXzTwy2lw#-xm-TV!)n#J`H&Dz=DK?Qv*GgT)4#-7K=`2+OT0W{q`2(KrT z1a28j0tqy5gjxu$WshqH;QgT_5UxUyccNMj$6?tV30AfIjXjNN*ZCm{=w=QKnI^$f z8Il9?7CL=E&RYBDe7oKsVrV^ltOhe=q|86=f!2I(Xk?{18Ud@do$IAX1rs2CCD0e zVFYVXL}BjYo$Ln?9}c!^X@~N1@#7&!6I2=Gw8Hh!z2o?a^$-c~3OR^Wyvs>PXhL4a z(jh~j?8!Vcw1BYXWE+ACs?ZX$=Y=dDez1ymRs9)RR!uQ<{DEW||G#O9dh{(Y_#5`! zqmSC4m?&%&!G1*MWM@|uPfs^fE=s@}x@57gso^v8bV2soD-!r7U12}gTCd-FxsVR8 zZ7P#seQv8^1F(zqK|6Q_5*Ae@fjw;}WPiIX zL?2+sA4y;t`k0aB9|^l!-D0DG(2up`nV-Fk=#pQklVAgl;cn6c1ED8tQ?686e0vmY z2P=3bDts8FmIQ>#NQL^kh{8p9(7q(i1lMtyl1WQ`}#(tmV-{w>MB{`M%KFEs7 z`=$nY(_|~8q*NFQ3D%9RjxqS@=jT@HKvq}|2~+&h3R6dg&1%>a>CKr|nkU%T6*i4-|@MZal^)VJ3$5mZ0 zR8X){;3y1c%mUXNN>RwclrO=O6#@VMfGc%OoS>dj2pU{09&_+h6hk3i!8&W&g3oTfG zX1>e{3eb0`lx%{uMyq?1r^;+)IPzsm;9vvz8TfZ?e|}Ruo)iI5%m{TV1%!o-tZhJ- zc!mTxS3MY~W{rs+3~P=>4g=aFm|)OooPJagJX=*Zesrgs2E3vy^E^$W}u zXJCC*(LvaS)vf7|Gs9BAt?yjgzR;9eEF&bGquq`aVv52z+Da(vtk9HI+;7`8BY#3o zd)-w~7{@Tbp_CQ}SEu0E;g)(2T)UcT3nke2Sc--hc%Q5L;rx|w|3uO|xFcCJxUIsw zb)*RPA36&VB}dnTnOSmXPJT}-rfx^I?*py_SL>o8^5>pC`(R4l^IGTV$>2l}aAG8* z)J^|n#1-^`JGObx)ggD66lbQY(H`dnjy*U*Uj-FA3W=1wASD!N533TkmX${$4Gg77 z>e7<7g$CBYLmhxHLdYZx>83$#OW8F1Aqka`zf9Wl-W`5?Y`r$+7Oj*9cMUYh5#yB? z{Qaud=5TL2fuk^-;T3T&QrJ}1@_6z=+uSe`&;`X#)}YoHY-ZJvz+00kvrti-Mh{#Q zv7C3sdX}Do!p|ED3S$^{gw7_}9pM)uHQ?xb2IR*2#6pex?$l6BIk#CCElO@jy~)g? ze$4WU&XszwhQ7M3?Mtk4S4pK(mjqeZ=qv%oyQR@Ui=7%naSdUD5P)8LY2d&k8b;(G zrHcj}(GcP&>YZUy@jIr0Y`oO1$(up&piXC@kk#9pNNiwPE7qG8X6Xd5K6hGSDk%D! zKm)}(345>-&X9!JPbGvTXQ5h}v(U+f#T8=&8s|?bKo5@eS7i?AnasnxnbWtm%3&@=~4>e!N{$Y=+N)96f#{Mb#C%1~qj`g8^0c7G|*7|po= zV@PusSO=UNsA-040`Ezl@j#LXKXo@n6T%v*kF#j_g#1yg9C zZoDS8$vz{2-{;c6EH_x8)+T8>;Ckm{)!X9jFm?vRq}PYhJ3p+Iig0JGuH^Vne|k78 zDBRe`RhYn_w% z;kgk?Js;>dPlN5 zpcr>()$sE-gIMdevpXmX{3C^qWbJ%&bB1zWLI(;@Q)n|rN5JxL!bMFRT8)(lruoEr zKu52KqY4V^>j}f>Sr4Hrt4SPBz#_OF*Mc3$3foD-n!C2bv`}bQE$TgMwsz^^1#nFt z2|V0e3p-=nB$0t5T9H~PZ#Z7?G;e_K94CPv_7-+!HMfz3jhdo`Hv7YP)7eMZm(|mc zj&a46W(o@3(>QHbMox(oS3q8D>ch$%KX1~ThtNm#-P*1%j%;b=s-p$I7y;e}lMCEI zdFBdEn*~ab|7Zc!Jth%Z>7sS;Bi5T@+EKAjHis|vg93LtB~c9brS#E48%T919w^jA z@X?XHgTX=^lil~XpvF-_;Rn3t!d%;)K2J$=MrP-=y5i^~v>rKigx4o}j^ecW%^SBj z6ul>+*1D7(@T>y#ufBvHg#=7)6ArmnoOSdY&2h4EwMU>XSbs?)p1cS-y0O9lvcVM+yz@f1oJA2U4RO9p@Ja6!Y4G zt)TLIX5YOp?CzZ3e=MKpIcMU`%$YN1<`uT@uEe}@szn=5Jw zRmC@lsv8$dL3cJMHouGW6&1@_;z`P5+H2L=>!tXQuA8f#D-l#I=J2Mn$I{t?RxsOOPwVO8BOi!<S?1;3X3_Z4T?EZ5 zou|djZ3BmC>kRG92-O$NMC`!N-#1Q!5x5!Z>owCTa7LlbHf9O#Mh0xyZ;N1R zDV+1kI`INJ;AstQx9zZ{rR0dHtOt@Gax4?`w0Pd@%Z@#^rTE_AQmUhfS0ncG|XN3VvZ(yl9I~BAW zn-v4)16rwxvF$7!So3z4^(f_Qt*bsYfO7w%+P-s#1htIS3oQ-Us#t|B-xvN@f>>hg zJ&c8V)8rDW1q+H()DZe=DTOw3SEFmTRMj?+J9A3}G^dLGm(f7|BrGalm*{JmwA)fO zy>%D3{DK_~%j|+;R^MF$vq|f!XecaYT>7onv)uLc9>6!yjML3IXvsI-ZtVC2OVu?< zOSYw}B20MHaiwRyrVbc?hPAZUgyZ+Lq_pJ~78E)rxwUmQZ+Laa9@-V2{X{JzubU!T zINL?Uqr&ZrN>tgh1@T0UmnSL`lo+^gvA5UUXIdbCQ&r=*-!N)Lf})C0S#koD<=|GR z3cE8wA>L6vh|KtL68--XbED?NZIgUUxXAhOuxdoL$-#8X*Jn`HPILA28fr`vg$lI{ zWq?&pYN)pCfs(eaYsIdOwQ^@JJ+UWS)`Qr+Q3SGMy%oWt z0;l!Cq@KQ00^Q$YRg+Q?Vi>c$6+XWFqqBC%Ctx3E_Y^KX7wRs1?Es^V^wZaCrO`n) zCpgQK6(cyvIvug9N_EoVz9`}OyE2+g)rjo}V86#Q(41}0wQ5O;HR})bp!^bantmBW z*0sMPR5)d90snxLftBzamW3qOAD<^jJqr1j72jSpgQ`NgSYPB%EH{AO5&z z3S&p@JI5i2bv%P6N3iOme^|XR4b$v?c2JHMmVza&MwQo$5 zdid;uv4LKgM*noIrV`-87AUR#DgFBnzX^JWom1MM`dSfVpP?$cFaHX2V~XDF-wM+= zUzW@U^LeUb zR%M9~tyPcl2&RCT!JWR=OU5Bg?q%(vH~5W;?t^P2$O?=5?SKQUBw}x8nv5!^{4Py6 z`olVmD)Se+`=Qe@-a8G@UIs=P+HlK6Zd@|4w6FOQGPerC!}|;TeDE;dtF?7F$A=MX-N;zCesfSst61&v()9=F-A;MW9Yy_~0T}xwEkAOFq+1W<*a;l)?Sq|sK^-^xkPF27uJ3#En`6ny6(kW8&#%`sOz#*Q1Bgvr8yyE zrNb3GO1hkXfO9NyxH7zGzx4%d(N`VN-mFF8m}i{Ak6jvz*wX&73URdmT4trg6R`cB z&|TZ84?1!FK%sqRjKczV&reeD@*zHcnm>*R&4-Q$w5S?~`l{~nh6mfKj8fDP7dhCW zRS7sQ%2stFT>y^R@nvXcZ`jMpk=pjszoSY5%bB3CVMit?B88Q(mf@8u6K>%+tN-!whFKQxx;E~mISycu}R)`SI5 zA<`y#j>~Fq5B`j)+We~5*up7_uEO%_Pepn29&!cltlm_y>&j9&O8=N{>rc;7XjZ~S30{th%gcVIwG!#~#&2%W; zibNzCk9?g9{1!4Yt$*5)QQJqHmtDCO&)vVG0ZVej<{y`z%~|>!g|krOm_Nb1SY2kS z-qsO~vGH4)BoATwV-yw3+>Vgot!5DW#>^QI`X-8_JeYfvijAq=K|q&F#(Qk^^!~l4 z?V?1-NbD8-8@UVGS0vMqTfZ>~y3m%~Ny`%3d}P zvYS2N0Dtyy9x)3q9qkv}0sF&IRkURo%IW9%;#FwLwIPS(CxB!lL+ z#^)DyMhg!_3u{Jl&RekV3y=UM8Z9kGy|Mic)-cS|nt{j*l{m|vxm#Bh)|i>Ts6=k4k2a1eiiZzm5vH9|c@Upzdpy<-4_7~`Y|X+~D4GgA{-)QakIe^p z;LEaF2TeE3)B+x^AVSq{E8(EGX{v?|YJ`ie-5NXaPob7$gB#g-@P$HPzAT&b9(GXk zRNtV@Yhy>A#p|0CXYNL8e?^ExbJo!xR3f81+menfdC>&a)yrX-6TJEDN!k&jF2mIV zl9KEUSWJ=~Ii_c>LYXa7Rq%n-UafL?ee=sGX=@ynX!<>IX)0!Qmaxc9b^TIMPIfH= zI_{>ck*S05McHwV~q3u;z1K5ZYC9rZVW({#jwUuL! zpHpKC)+ibXTlHN2;I>at*-=Oo(TvC}exc@Ot%c{Y{HYz8PxE>$RC(sB;UVjYP}BT% zfc;ynhT)sC6(qkPe;r|&A?rbCY;7-%02M)%sI8hRR6of~?fabC-$6}1XuMpSKWlZx&e1eME&cw5!g$SrLni{9c`ouOhsL>QQ<12UgFfOrfTYzM)qW+ z>u^E;xR*R|go#;mZg1OBO~1P7zugNg%Y@;lx3GtEJfqrGgs7K&v_(DcsTMVOGejLp zRSkMWhF@YyksW5$YJnh8CL-UOG1~MbwWK$j6}5$>nBD*8uV4g7TMg6>kWL98fsky5A-nl#MRYx-ddx@E8aP~3}TGm0(QnM?o9aPJ} zm2OK`d#`;h$|+CYj^Z}QMGI9!MV_-KQz;#H06FZ&e}Z3^#RF?Yom8`r0h@o-zA`oI zt?!Wdb}7ODZP7uRuE2;VYH6yA-~V-xA;V8|x|eFziKsgUO*ag^r0K_J{wytTTPo61 zF`}`ztARWFcXtwtzey%m)Z1lQm3{DqkJZAP*7u9;$=ef0IJ&?}M%%NZ3Jzr6)%GRl zwkhBS9l_~=<|LTxA&ao&UxqhiN3w`{q5LkWY>wM4ynw-fBZs;rzrx-!$bn30sQjbC z{BdX_6SR@0-rxqMhnwe~fbjG}s=sG4D#{6v{*!EXOeI7C5vpv4*cPq|d8+v?-?DC!8ScESBq z&qP#h+IDSsp~}=!LU4!u58+hXe=qbkSt3D?FL3BUIk7$d8U1!NvhuGm?G1bKTFw?9 z=e>>U^VShl;hK)xd+e#ZILn=79KiF)Z}#DeoM2L5f%{RsfFm-L3@O2Y+@{6zm z(#(JydPQcJPdh*y!L>X9J>CD2kh2dE(Tz78<@Z!VEeL}*p0fc#8hmE70&9Ix;UKKS zxPu^F(Pz7kDU{O1d*o7zGsfKx0qR|`6jXIPXhC&oQeD}{PXpBzA)%&AoZik^qT3x- zgbOWZXU55DTQJYMUek{DZt<3=s;`==ZsyRci*1$Wp!`y+l$;6G z9U~~4dU~>*^Li9z#tm)bb&TL5f~D*Bg-3`y&Y6pqR|lgAF&J!`w}bk&EyZ7Pl*nzn zqCeNq0{ZvoXzQO5!}&E?lVgNx>#ebgeA+&%qdGE?Q^yK>DB?JJC~+7EC|SV}N7YB_ z1a=?O^6AFwp_plVVW!pG1@xK3*#S(Mt!OILF*6%Fn$71ZFP1mU#ERX^CMJ)`^(r?x zgi1_CMuq0ub8&qsK5j;cgO-<1Kvnha9O{8Lmk9T?#Yv#<9FtJ34m;MO%pr7pV2x2I z&N*0==Gi~HCbLbM+bP2BPdo)h+wMrvt)~>Vh4#N{V*PTv5S?0#Ofk)w;!_@{y0cVs zCo7hc>qs86%{dK44Zq50Yj*#%B0|_3iZg&-{v|{0SzMuGDCJyCbN=~pIa**(Rc$}Y z)EY-IJb4I(Q+<(BU^Q&g|KQZbIg}vPSKwb&bRt#y*H%=00hK4PvC(wht_SM~!Ur8m z|LyjQUaR*aoErL4dvl&tOGl9E)2vA1K!jVqsE_~QyuP|qC8~AeKGxd3gSQU0#+o1T zeAHZ|!I&*Gck-0K1#8SUHFa{JgfH%{r%1s{Rg9Be%@NnOU1{NWTRD;06|Jfs>X3xR z*nE?=0gvdWmhmRmi98)1djZ`yd9WIZ2L`Oz`3uCh)$t;*Q%9(=g~?82-pXN)beOJ2 z&Y$emo@#>0)0tT-Ogd#S>1d9ODT~$gj7x+c@#7M-B(G7!@~+@+(%W<=KT6ewn`d_~ z#<8huw00g=?zz%34qc{hyj+1kz3U}dTxPxEWWi=%ajHWpt<>iE3P;p05t~KL`aR1l z5hV7L>Rg4s^mipNLpJuRqPlQ5G^t1EgjDERHBZ~=F)30DD85RBe-AG;%{9jU??eNA zy*e7Pa~zsI&cOpi^9`N-s2Q|c<*YNFM#v2f)o#{xUYs2xoTH+zVUFq;B#BqOd7wIn zz=vzC{@){rJIA9ExLf{G$Jf18uaq#vBFJolnb)EES7QmS`Z8_Kbw#jn-tKYe)mVPu zu^Fp_=EeHaF%m!N2127O*{tl8@X;?T8*vO|v}Xd5LKa!p_C&MH}w>O%pdWn=j*sI0$H0;)b(`|cLuCx_hz zwCs^m(D?_P$-wv8YaQG2w`YE#B{T=zh*MmI6LY;o1i{ANfg&6iExe0oRz9C{CLd;a zz=7w_aiD^=IO9xu7grp!j@x2vG}752?7G6)eT8Gg>>kATy2Sy$?9p|)t{X;1d)+bA z(>n_D)U4}{hZ1}BV`n2)aF1|a!tX9{BQ`8hr8ADvlI6p8zvLbHh zX!glWdx;pph(7t12UUiyGPJ&fsL>kU?cjW$c^D}xmg(xlv0_UKdJJK=135s! zV*Ol5LrvbhvCmd`A~TTur`gA4t1_#n=tAQs#9LQm^7V3#U|Ag-qg~nds@+x6!6=Yr^S$g_*9ipQ2*MZ8*S}U3p6MytmIH>*?tEb_#9T z&M_)#+H-V8(leqfjy{8u`tkoE>)VTZ;@^Ea#AwKW4pE(ka6kY{ z8iocrlIT*4zvd~DsaOzwlIeRv=Eo5-1=%3OT*&Q#%L^zd9?yH)hsD1jF1dw`n&fSW z`RGJL?FR9gE>U;B#N*AgrqDy7)k`QkKUYSVXX!5$HHAi6r%!mjhb$ovMtUwyA{>7O z@gc8>1|RRX+Gugto=)W0n6b*zE_k%ak;dy|g6wL1}k-KyL*re0eQ1BKCG|NBp z7^jMdve`Sx4*eeo__CJo2#bGZ5@-Gj7T*(#vZm~KEI6E+`W_}<@Q&z#@b}R2;-(B0 zzut2pF2d0TL!K}FnoV>rmwk9ojDaEfkUir$2L!OzuQ3MRJ)yhd`f}GKBj~6au3eM- zx3UQG*eksNx|V)nZ9bqU3O~B29;g)!wX=0j51f>l7q3*ML4BhsxTT)ZTwK+tMC3&A?5|) z6A_WVsJG=8MNOgY$9#bjTfBn73k`gEHa^IeoURHu+NuUed$EwOibleo=(QTwhYrD#Eosf1XX$im%@GWb;{-$H+BH`a_1Bvf^(< z|FrlH$*WpQ&^h0U!q@B{v||?vZxE)s%fVaJOcI z3ek0)z7i)fR)rv)!TTS)T|pvWTG}|L_U9?E(Q-OY-3h$#{0XU6PQ6`KpW+fV*_R39 zZnB8qu#gu+B=q57u4Zg*5mA?WMJQJHkusDFMQ6RIgJ!gMFwC zyI(d%#^Tc#sjj38m+=ddv+)QCubX8tCc(%VXw&>(RQ~X9;Gf~;5jEb7HCsSSzsAws zmdR-G9sV*;;!Y)_(RVAg=S`0_5_SGYS0lEwn0O$k#-f=qmoU3O*I7AgY?1j?+b&(M zbSkqtQ^uLGF+r}@OXHPh?8gdkGGlS$hc$;c!ZWng@eHH)OR|#mmCb~%-H^3In%%|} zJ`iWpn!`8kq(m(^=BnDri5$bE8Sngl!7;4eTAN)SZ?o`T0o#o2D5Df#hAJ$B`r^q> z6*T0PD;}*Zs}#>O)h0fj^bYMh9zj0Mg8uuk6hFy8Sq_ikLZ4tvu*wF|H}ttCUYwWR zW7>%LEAU``=y)*g-pPEbA6cv+6lH&ufo5#;d)hKTaP++&YIHeeL*Y!j(CyZta7S6u*_^WTbQ|`^C_WJvLB!vN$7UBjLbxcNnyNnrf;o+t;iFjL#h! zxsfBcnK5L~wpPR4O_cbEpSha>i*D;}&MMftnXxRaWWqjq#Hlv+B~2)1PVPrsnQ9QHs;M<}bf!a1pk1S7%Ii@l$g{z1+xEYKtjK_&>bwsA@PMO_`;c z(tt0X$P^85^P$oXBF#V?Swl@sHB*XDX6|#?^Y9W_IhGxX#=Bg{triut>^rY>)GRc| zdW7dShr{tkT7ryOc6p`PAuy=`%`aQHv0d-nJlUfP=(e1ubT6;w@bPU+;Nq_8l^1jO zJLZ&ob+xoJ=NS6@pM=9w?GLm3@kySLKf(%=GByV zimZcMIhJXGZ%4^1!OU4?yDy9Y>~n}MF}EMFg@)x@OR(a;8#QP2)DtkyG6;oh&Lb~(sk!;t zu#-zW+{o;*@f}TW^nj6OG}rF`7Kck@n6lcHl;Rs^J(|2q%|sz;G|?6ieX2wNIUzaQ zp{gq`sgXyoqNlrEa3l9mUHs}>FJkUd%8F$Tu9uKV@lo~=U*oPC`TiDd8LcBsKNiC> z{y?gy=0I5dxP*>d3(sMe!45>vjdOs$DlfGNE%cIr6I_+zppUyp|ELd`q;!MODM1o7%MEj}gB!vBebg*r zDav?5ClqL2P3DVLH}Uwo=M7Zr2B;g`{An1Lm0I zZo?wI;P&DtIotpAgz77JH<=qH$PI)Rl@fbqX08;nzTZp2CVE5Bke=>jAavJDd>}mJ zG4`1q2gnq0?Cpd%3NblZLYXJIlR=XOf2KsKxBI_G%5Y<5?B;6C^1YP!C~uN`IdZI1Ox!cgxx*>pVQ)Ip|%kd@p)Wr{~zRZ%K@qUEm zB=k95cN4VS(XpHq-1Fjn0;)0l?MDpt&*?@1ec8gN80y)K6A+zCqi2?*k53(#iIJom zRKJkH*{Y96hz9!j0MzE=298m%nd|(mS<)(Zyf_@LbP=|m{<%&5Wif@V)tQS^@FI4g zQhY@?GZ0$b_p-;^P>-y?-N|z&y!*MQ1H`B5c)2$Z%WMStf{4y}5Clc%PO*iRl?vA7 zq&s=G5^MaqRR;QJflmKaKhN3XkfWIb;)mw2HdU0qtoVvMdElA3Bf7^8^v5Ti^RrcV zBzCG&iIA^?s7JUGiY)VFsQAFienOK<52RTBm47Ad&R~dj4JO1642IaKw=z^5Ne&4v zTFwvu0(FAz!#{Cq0Bahe6c70H5GeRv$WaRRB19SLC@{L1ZN@t+@HM3FyOp+O67|`x zYG}Usa*# zik%D;e#(D$V)9>YAiIptS}+Zf zTuv=tgYZPU)_{T$c#f|&T6A>{r8wesquZ6ckx*dQMB9lw93fHjYodu-)uX#(eoZJ^ zju-v7BB0IKgPO#A+vEQFlb;Y*33AiccB-|+Z{5m+%nLbM3yL~)Q~}d!E5*IB_l?=d zf1sj{&hc(WSI!g<<~Ow=+6FHJ)`qRBBJf$YQ4t=27fyW!(*t}j2#>hJ2Jn8tA;e%0 zV|KNl2kDr<*cco#i0yifS$#cXv}z9Ng=K}JgjtBtR#SSjun``SRJu>u%C|juO3Y<{ z?KXBAC$mjhW{L+nAv(EQZR?K6-g@l$G@Wc$M02YlfYd4+MzM2}hswPb1qX{xr_D`3 zDSG0@tLEmw6GxM35oYXBUHF9Ktd!;~F&yoUpDw~kWm3Vl%{(&jQ)eykSR#vSETle< zsd{iRsMb1x^zk^z#F$Nr0JLbg1kH>f#_aUICr0;1+fMbouiGS!UDJmF!(E=OS7JUEhYl~zSU zU)*ttG~tAY8C$*)PSy(iwR8Ys`pIiUHxGpNyE^uD>9j0}jE!EZ4~@4jO2F`ju%h|r zBr6gv4t1PD#(6^57oBeJd{Y)-#{A0!ShI__QT!_nh?@Rx0KE?PWs-O{oIieF?p+M) zF*=h+8q^GZW9i_D zh+jj0bN1g)a$EGaDfBM#l*x^Sv8a!8%!`?a9dbQF110ENUr(~#9HSs}U9b!^V+ob% zn(yLhJejL%<$@@s_%zM7DA+!R#ecy{;Zs+}nz6ozeXZH*YMy2+vpHe*dCj4~rJ+P> z9gX33E5egpV^UfGy*Elmo3Zcu!Pe}3geN|Y(gItQq5@Cy(Gja?kVmwU$zt12O23tU z4_4VkXU{Mn37g#57W9U;_tC^&%CW#~eKnYyLT6nm-$Ul(ptB`0LhrRiDIfNep=3uU zw*pWvnFGDp--A5Km%Gci2C)7Z3D}}Fae}pJ%}sq*)VsBgE4p@~Of_Zk<7i_EZ39K| ze@f_-HcIhp9M@E+xiZR>InVMWSNg)%-e5*R`DL&+fNVik}?{ZVv_RcXCu^X1UFid|Yzt9>vHa z=r9_reMUC*AZPa$hCLc={$P2TJsya&YUlrPm^aHg=}CS}q@V*zGvlHRG-06~mEtF~ z9A52r?1(_`a@eY-8@2no#E-w@X~-^jBy2r1qj7#QY`sb&?atYDU*?kqJ=h5aZT3_K znlSgyaJIHZx|y>HoiJ14r}$8=b!RZ^zLl5-FFjSC#1%}*>=7A{nymNNjz1hR~a3dCv*b6 z+Dg>&J)rMVOD{4k+Q-hzl~rj3pJ7cWnIf)E)BPKcsH=0gn}nSc=S8+|VGmTHX)hUQ z%9@6G*|2P0qS|eVhF#bbq@e>Pl21R{7R&s3c=a5X9E`c}eUb$4*Na$aj`xC4htU$M z7%1WFYBuWCJ-m<4B`zK>WAVY;-oyg^t~bOUn##Hl#6+CX7t!8oeTV_>9P#tfK|a8B zJi?qgGT(&Fo#{ooo^E}i&400s7AIfyy`iYr5Bp(rym6~Jk>@VwR3G-eud<0S!J$n* zD0qPfdpK(48ZYuZ$mYVHzKPKBw59fJ_4f{$ZOoFk(v_LmAG(h1=V)J+*B?V|;vTPR zR92m1$HgD#N{%vIOmZtG5PC;r8TPSXlcewC=3hKQ{&EeZ0E6ic9aKfTJcmPtf} z?pMHCvocUp8>X(g%Q7@#3&+4ek4S=HskRrhLT(GMwU7H#@Ji*(AiUcuP3qHqP+3p>@aUrcG<|ooNW#BwqohYMD5;>fZlaY zWG1$-4ZTTcVR!}Kxv5Ydq7!Bq6fKJ&cUjJ(plePC30SYKH(kfXZhywXCpqV*ZI}PX z%4{>1-N{>>nq@Is=`Wm-qeesJqwbvK$L5Vzir;{J%u##$b5syZ>f=p5L$a#Z$83JM zw*p?K<_*NlLpWRf1V;PqRbn&HYAHHFNsCcZby`hEmqn)#OW*hAap60#^o`fKGSz0H z%r|4^#0zGGr&pRy>$lkJ=ggWfGC;?kfQi{LAw4I}V-jGp7%wjSYtfM*JCKV$#K2yZOme=d@b1O$x1)r2)s8L%6~kTNLf=b=WcO8)cG?1%bz;c zPo4tOpcgVpJm>yybUtDY3Tdiy7PtGoMEzqb5nuG)QfBN)45{-vL+a~SiE2`ae#-fT z8Rm_@zeU<`T?eaf+O`aa~^m^dA1uL?36 z+Yb{Ta!&5BWzp5A7%?|>suE`-iy*fc%m0L~2aXbOft?TeT<`L6&OQs#VqrRa`4gTp zyBr%kgE;NDGy{r0_?LnP&x8@CPsC0!+sDU;I+KZMWOvyKBd}0sy^9Z#gyqygdv2LY z#Ls`vgkG0AGL*c75tL<9{V}Ym3(^HOouQtQGTW57hx?GTY~|U|^|Yx9oz~n38=uBL z|EB$1&c!&LJwTb(Av}>WxDE%=+qHa(0Ap@>$ z=0ivC(Gu|JSX6w)d?Esu65k@SJrq^aIX*X?DD%k{p11&teodEvSySi|IW7dydcF)S z#}XD2HouEQJ1s2*wOr&wrog=3HRSdsp$qw8r6j^41NGmzjP8-VMNrsft%P=4OoXSu zMYHdxq3EEFuR3UpM0MZjLpD@}CD4(xQwEx{x!dU3Y3Zu{R|lh09_si$xqGyiWA0@@a|foJMrcpfu<>d6A=hSyme1k4zam`BOX; z+((AH=B%*hhD0r9*y?o4p*tmmK~cs%84VZvwhy`TCJ*XpF_$-(&g}<-r;><6c|N2= zaP2SXs{Td-zJB3TkD68;t4ANsLA{3PtQx&PNbGIPh!B18GU&MZT|&+JLKkK^zvDDj zkJVp=J%8?UVuG%+0;ozxGRl}0l=U@W6IT$M);A69J@l{;-PcJgDKwXaWSaSsYis;U zXj^701C801mBf6~!q|0yIJu;pv&0W}^~FWZbix&#n+{be9uo9wIuY{lj;oxmzBRhR zpJV$9JxSx~PF2UVDxYyIXrSaU1?x1-#hRtA!V2&((3gx?XCBUS?gV?fhq#$$Ai=!` zTeBMD>uwdgx!z@<4vyg*s9*TinN2B4ua;krGmLf7Wz< zlh#Y8f5WHBlCZ_uNQ~c}?d!a{iq%IqoHlp1)HAI%!7 z4~yp}n0e+F!pz@I*}Jnkns{t&?M3JFK8YHC7&DJ56Q*b@#^aE!M6fJlEA*YkLdx}l zL^I@!hBp({F9LO;Jt7i#$q05hLSz}y|w^0O1DZdfsS3ju}H?3cdeW=q*8&h-n7 zcOky-sp$^%`Xq^1@D)9y%A`1)iJl40Bzh)36MDa673F$CB75d!>Wriv+!VM^$TN0>viS}v9nb-*s7{f_T~g4gyEYH}q% z(nex;1L*1@0axxO#C_nX)qXMx>#L6+8T`-4UNUHqlq}yTSmOMy>_=X<*mE&q?F>wU zy22 zJa8Y;IurLn=&m*r>ft`zcDTGxDSqzd%%z`xqa}yIqLWO(_mB4z^XiWMknP)zL$QwR zC)SaZQ}R<6qX`%5gaQ2eNYw0}eq>T_-T~-{NtA&`EZ`vFeN`NO<{ghWoYhfubC|?$ zeFzIyHpRD0XuAI)=Fl5D4z59p%ok_Zhb8!K!H1;yb54^caje)S+j}9tA-?{@U#j?x zed8f`NTJjG$f;xRuMOV+LxFTxB;R?=<0uxKZgXhk8*~IMHy_^#I|?-y7t?C4wzQ4m z?pLwS@_cv&XN%ph6-Oc7W1ZA6504V**2y*2etrPi&2`$e9X5{KZa_tn=O%hsoIeId zSFlc)WW_b?JU+nc*L0EDBOU_n7l(NZ#;6LU%h& ztRR1P9RI8=iouf4m8+D(0)RSmx(D z(8b((7RBu7DWk<9;`--++7>9GQqK{UslB)M0e+FRn$G6oRftSAW1FgbTeI|YC|JaK z!v7d@9{T>OEs=ujqNxv_C#K?~+n$u=k8F3=>D%AoGM@}dq+fs{v&I~#VEyiRTC)Wg zQLs%@T*xitri`R2HK6dBueLF@j^c8}bJ)d;P~_c)?YfNdRnXPnfQ4K_1oPK=M3_x4 zDXm!fhs5KV-Tx`Fx`DN(f==Fl6kY*Wi^C|nL@b@ZE;}hP zb2C03@BCaQb|t}AApSrS2L!N!!T#jdvT4xBfUX8j+)T8TFC|Wj& zH?bGHnuCj`1rz=8^wLXDb9VTuGEjI#rvE$g-vwyAi8>4QvYE0ZQz>}xCI>wqpF^1B z{2Zu1H(w@+FKv4 z=U|81bk@@74Kkk$l;&TDqCMLsVDJqhTW{74pw8@(QRP_94Px`?aTCz5hcuxEY~W3z z`1_unnh}kbdX1jZw6?^Pl8{x|{^Wtr0N)LB#-m@S=tK;JiY*^Mm7?Cyei1)H4-2&(fKx@>+)nMzB%{6pY*-l zJ_cUm9l1oOBeAyrr;S`&qwYXg!*?8rB!W9s5&=i0e&MJfcIcBonM4r${`~Q3=$6jd z18XJ`xc=m9aT39Zu+7Edu*uu9Y|>k-mo8-j$VqtBJrpk1L;^mzN31T-+`3sj#qKs) zC%1LDr9?H$#Q@GS4g^+=#ftvu|K`|p^uF#^;IAN=ikyN6#I#uXA+$JGlb}0m2AHwVRRYL8 z>AL>2&*DBhkJOH@Epy18iOqxXh>5ySGJLW@sEj9qa(w6eEkdE^+cz;vO7!EuaAj2uFVrDC`yn}iueHX z40iJLsS8}7qq2^Blh9XYo3fR?=@y#!6uO*;Na*8Fi4r+J1M1IFG75)y&xl!f7l*zc zuL(6^MbC&?cdD`dsXWx-g3f{UzG;$>8PAnXg-dDA*ITAL!BA)GyhT!boiCK%bn+{pCLWYgrtI)5Vk2>4-f%nANf8jDWBu>b=$ z_C4N9`l#UZ=hw@br~ww za>sZ3rlx4$MOe8s*YNDSoGLy)Eq)8x+a7a3K-!}KV;236h{lh8w&?GDFuArm-rLC+ z|IrQDqjy9l6z`$PE}uPq59dDVoz%qQ=Qk$pT>5eiG?@fxJM}AYspLK3)W_vR!Tlm$ z6i-&ZFo4V%EURCbw-mnb`L5clYK?Mi;U`RieGLN1@0vCA$~bZ#3E2(WX|sDHqm{E+ z1)3deZfleUQ|PI)Zr*p0*!dq|3U>B^nCK* zyMDr9&4Q0a|2+5zu?a|CRYS!o?+3$d9*a$RB9d1>5%I4xj&T*SDdBh8-y(7K6D+{L zW+1sPS}v?nbbw`iLsYE@nX0@%%J4w)MpKgYH80v^2UFkMP-Kn#)QyOXRq2kQDRRRK(}hQA5imh zxCG4jL8L~0;3)rbGRlb6DO8HTr8LjL`0;hL&Sah4Sn5>H7tc>_MNses$9;v^(JE#I zk~`XIQQgYlg7`~15l6(aRP3Kc#JU~*6FRmmWBR{fn8=nAKR!3bW9)M&3(GQtL&c$m zvA-b830b9|FblO0F=Z}z>==4sFof12DcOVi#eiO#B<2Mv-+ap2W{wDnN zz2`>{{|Fn{4K12qpzL>qbuU&95+)%&E{5!4+~+7(1}^mv7G}jRO$kN@+eHbJ7i`SR zyWvh+%Ts~nnfrx60~S8Zll&h2gV==$+fl05I;LQQC(L=vz0(giV$H7ylK%WoeW>q| zE2G8!{ES)e=leoYOC2xq?h~1cm|U4)@!PPuWrCf`u6iBFvR(z+GV`**_QJ0k)F}(t z>NhG_H|n9eiyWl?Lj`hW!QTQqQtx=$ciMf92bt!#$7-wb^_j!J1DU0HkQ>V~2o}E^ zYGw#6O2Z%y3t_2-XvPG?;7H-OhivPdsI%D$pXf}`*XyjIN6%6vU;tH@HszqDrKbwy z$C84AOjt&_;6H@M^n+vV6eU=^$ZQlGCM?mwvp)v6K#3AN>gzSpD3McyT0pK*aCP;I YL53Lah}Ie|H!ORgtX@%TxC?s!2gY{O4*&oF delta 311142 zcmZ@g2{@GB*JjCbh*3Ju)%(x3;plRX9MUkpYMJx!2wRybi`Jgp&U4L+@%K=6Uti~~a2hk-FAwP6 ze)68!fQ?}Q$IZVV4K)r8T*N-Tw5=}{SLr6D0M&PGc9>*JkKP#It8Y;GqTu(S%O;k2 zx$VCmWR*q~zsiVwF{t$bH*i2@Ym2AJ;Yp`Qe81*y-f}$p^D)EnisOwAt=Q1e)hIRB zl1rPU4^PVc(7oC2`ZD}gzkA9lmhbd~qT=&j50(AHjdK6Ij4l}{^mj97oqcl0~g? zFmtIa%lCoDoBpoi9IcB#ilpQ}xD|cCxs^v{f0J4JkG!-$EN4Tu_gc4J6g{9ac{^!5 zHM3PE#6xClej)timUqtEe z;|ILNX3S~DWPRp~F&l!8jtTswh-m{wsSa#hqfB@;WIj=upfi~pMDglqM(D_K3=fUQ zL7kPGMeyfPd9d1vqCM7P$bO9eh=#v7$E1o|gWBO-SnaD)S&loienDalXPh<$?U4M& zcr$q8o!{@uFY7YvpTg(Q{_b#E`u59yf0?gCU2nj!Xuif*UDx~E{E0g&r`%SzM+b2yn z7nb$0S)b`jQJY2_(LdYWoF@V&`sUSopP2EERrge`ine7P&DMI9HM_xLGqSCL;Vj2{ zUiJgQImIt?#}xnm+FR6ik9#a6CFz*tKBCTToH;OA3M4GEMwqZHq0vpuqWBEvEo;R= zlGQA8E-cjv2q6#&zBd!v$q%BiZnVn|LAcw1WmSKGT6O z`=MYlj2&-SNhE+PJPd|?8mp2m35(jz3rf`WC_V}}vm zN7jGsYUqOkz%8_UfPz8^Qn$gdK>lYicWCfH8iC*_ruTOJqZTT$!SF+_mKc9pSQu@| ze)0+LevgnL!2-N2_;ICkYiY=~R_-C;n^2*`2 z5q4o6r7xCe=OZ222px+S zHCON7Vjih=(oFVaTu4f`Ra;?RN{jGZvv+uwxJ{eZ5e~_L69MOcGskD$d+2&*K%l+7 zPfjKfS0L`*kGPXDvWeZ8<7uk-yOO9HpJ4Hz=*L@L=&iWFd^oIR%xqc~ec)Lb`_T&I zN42PDzB?j1K5i*e9>2#_dp0^&wm#~PUX3o@VtvUmh6wD1ToH-aZeGqshf2QM#N^@k zd+0>Y-_Aa#d#2^%W+rQQ{%CQ=q+2Bjl<-le#z8bLC}XEtOUhlPTadvMyb?otME%>= zr$sJ{r8D^t%A-c|C2Mb9R}E!Jc5i3l?cJi7JMuMGaOVmidO4`{yKs;}$6Wh9pQF3*-r|P_hXen6P~C(cof| zGry9sLXTE`FZft8eyqc(GIz17=gL*jeace2 zo&g}Ws(Z4`xgUKTA9&{&{b^ZLaafkQe8cRcYO}=Q$U#G`y38Q) zvOlxp54zJ8W#2!ld$f?&;x5>A2Y>#}?E8m%W}1BbguQVqxZronreC1yV(|{kBYL&_ zR$FVAi;2#6@`Kcsd?Xzmf#F{yBROBOPLe^<+DNvB&Uca}i|Q6p+1QXS$tgh@+bOnK zWQ$>g^sIp2lQmGI0`@kjBN^F0V12v)$2YSdCy>@g2%9Y9?Ufq%0D^8#-uF4Pec$X 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

Ua7Z+b_JF`_yO#5=&Ub2~npF1K447&>h9A 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

Jul 10, 2025
@@ -1266,10 +1271,10 @@ exports[`AcquisitionView component > renders as expected 1`] = `

renders as expected 1`] = ` class="pr-0 text-left col-4" >
Test ACQ File
@@ -1309,7 +1314,7 @@ exports[`AcquisitionView component > renders as expected 1`] = ` class="pr-0 text-left col-4" >

legacy file number
@@ -1349,13 +1354,13 @@ exports[`AcquisitionView component > renders as expected 1`] = ` class="pr-0 text-left col-4" >
renders as expected 1`] = ` class="pr-0 text-left col-4" >
Consensual Agreement
@@ -1383,13 +1388,13 @@ exports[`AcquisitionView component > renders as expected 1`] = ` class="pr-0 text-left col-4" >
South Coast Region
@@ -1397,10 +1402,10 @@ exports[`AcquisitionView component > renders as expected 1`] = `

renders as expected 1`] = ` class="collapse show" >

Each property in this file should be owned by the owner(s) in this section

renders as expected 1`] = ` class="pr-0 text-left col-4" >
test representative comment
diff --git a/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionMenu.tsx b/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionMenu.tsx index 8e57d207c9..e23006e54d 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionMenu.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/common/AcquisitionMenu.tsx @@ -53,9 +53,9 @@ const AcquisitionMenu: React.FunctionComponent< className={cx('no-gutters', { selected: props.selectedIndex === index })} > {activeIndex ? ( - + {label} - + ) : ( handleClick(index)}> @@ -96,9 +96,9 @@ const AcquisitionMenu: React.FunctionComponent< {activeIndex ? ( - + {label} - + ) : ( {label} @@ -129,6 +129,11 @@ const StyledMenuWrapper = styled.div` color: ${props => props.theme.css.linkColor}; `; +const StyledMenuCol = styled(Col)` + min-height: 2.5rem; + line-height: 3rem; +`; + const StyledRow = styled(Row)` &.selected { font-weight: bold; diff --git a/source/frontend/src/features/mapSideBar/acquisition/common/__snapshots__/AcquisitionMenu.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/common/__snapshots__/AcquisitionMenu.test.tsx.snap index 30b69c0577..a6bee72b71 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/common/__snapshots__/AcquisitionMenu.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/common/__snapshots__/AcquisitionMenu.test.tsx.snap @@ -6,7 +6,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` class="Toastify" />
- .c6.btn { + .c7.btn { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -35,19 +35,19 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` cursor: pointer; } -.c6.btn .Button__value { +.c7.btn .Button__value { width: -webkit-max-content; width: -moz-max-content; width: max-content; } -.c6.btn:hover { +.c7.btn:hover { -webkit-text-decoration: underline; text-decoration: underline; opacity: 0.8; } -.c6.btn:focus { +.c7.btn:focus { outline-width: 2px; outline-style: solid; outline-color: #2E5DD7; @@ -55,31 +55,31 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` box-shadow: none; } -.c6.btn.btn-primary { +.c7.btn.btn-primary { color: #FFFFFF; background-color: #013366; } -.c6.btn.btn-primary:hover, -.c6.btn.btn-primary:active, -.c6.btn.btn-primary:focus { +.c7.btn.btn-primary:hover, +.c7.btn.btn-primary:active, +.c7.btn.btn-primary:focus { background-color: #1E5189; } -.c6.btn.btn-secondary { +.c7.btn.btn-secondary { color: #013366; background: none; border-color: #013366; } -.c6.btn.btn-secondary:hover, -.c6.btn.btn-secondary:active, -.c6.btn.btn-secondary:focus { +.c7.btn.btn-secondary:hover, +.c7.btn.btn-secondary:active, +.c7.btn.btn-secondary:focus { color: #FFFFFF; background-color: #013366; } -.c6.btn.btn-info { +.c7.btn.btn-info { color: #9F9D9C; border: none; background: none; @@ -87,66 +87,66 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` padding-right: 0.6rem; } -.c6.btn.btn-info:hover, -.c6.btn.btn-info:active, -.c6.btn.btn-info:focus { +.c7.btn.btn-info:hover, +.c7.btn.btn-info:active, +.c7.btn.btn-info:focus { color: var(--surface-color-primary-button-hover); background: none; } -.c6.btn.btn-light { +.c7.btn.btn-light { color: #FFFFFF; background-color: #606060; border: none; } -.c6.btn.btn-light:hover, -.c6.btn.btn-light:active, -.c6.btn.btn-light:focus { +.c7.btn.btn-light:hover, +.c7.btn.btn-light:active, +.c7.btn.btn-light:focus { color: #FFFFFF; background-color: #606060; } -.c6.btn.btn-dark { +.c7.btn.btn-dark { color: #FFFFFF; background-color: #474543; border: none; } -.c6.btn.btn-dark:hover, -.c6.btn.btn-dark:active, -.c6.btn.btn-dark:focus { +.c7.btn.btn-dark:hover, +.c7.btn.btn-dark:active, +.c7.btn.btn-dark:focus { color: #FFFFFF; background-color: #474543; } -.c6.btn.btn-danger { +.c7.btn.btn-danger { color: #FFFFFF; background-color: #CE3E39; } -.c6.btn.btn-danger:hover, -.c6.btn.btn-danger:active, -.c6.btn.btn-danger:focus { +.c7.btn.btn-danger:hover, +.c7.btn.btn-danger:active, +.c7.btn.btn-danger:focus { color: #FFFFFF; background-color: #CE3E39; } -.c6.btn.btn-warning { +.c7.btn.btn-warning { color: #FFFFFF; background-color: #FCBA19; border-color: #FCBA19; } -.c6.btn.btn-warning:hover, -.c6.btn.btn-warning:active, -.c6.btn.btn-warning:focus { +.c7.btn.btn-warning:hover, +.c7.btn.btn-warning:active, +.c7.btn.btn-warning:focus { color: #FFFFFF; border-color: #FCBA19; background-color: #FCBA19; } -.c6.btn.btn-link { +.c7.btn.btn-link { font-size: 1.6rem; font-weight: 400; color: var(--surface-color-primary-button-default); @@ -170,9 +170,9 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` text-decoration: underline; } -.c6.btn.btn-link:hover, -.c6.btn.btn-link:active, -.c6.btn.btn-link:focus { +.c7.btn.btn-link:hover, +.c7.btn.btn-link:active, +.c7.btn.btn-link:focus { color: var(--surface-color-primary-button-hover); -webkit-text-decoration: underline; text-decoration: underline; @@ -182,15 +182,15 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` outline: none; } -.c6.btn.btn-link:disabled, -.c6.btn.btn-link.disabled { +.c7.btn.btn-link:disabled, +.c7.btn.btn-link.disabled { color: #9F9D9C; background: none; pointer-events: none; } -.c6.btn:disabled, -.c6.btn:disabled:hover { +.c7.btn:disabled, +.c7.btn:disabled:hover { color: #9F9D9C; background-color: #EDEBE9; box-shadow: none; @@ -202,23 +202,23 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` cursor: not-allowed; } -.c6.Button .Button__icon { +.c7.Button .Button__icon { margin-right: 1.6rem; } -.c6.Button--icon-only:focus { +.c7.Button--icon-only:focus { outline: none; } -.c6.Button--icon-only .Button__icon { +.c7.Button--icon-only .Button__icon { margin-right: 0; } -.c4 { +.c5 { fill: #007bff; } -.c4:hover { +.c5:hover { fill: #0056b3; } @@ -230,6 +230,11 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` color: var(--surface-color-primary-button-default); } +.c2 { + min-height: 2.5rem; + line-height: 3rem; +} + .c1 { font-size: 1.4rem; font-weight: normal; @@ -246,7 +251,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` font-size: 1.4rem; } -.c5 { +.c6 { background-color: #fcba19; font-size: 1.5rem; border-radius: 50%; @@ -268,11 +273,11 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` align-items: center; } -.c5.selected { +.c6.selected { background-color: #FCBA19; } -.c2 { +.c3 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -289,7 +294,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` border-bottom: 1px solid #606060; } -.c3 { +.c4 { font-weight: bold; font-size: 1.6rem; color: #474543; @@ -304,7 +309,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` data-testid="menu-item-row-0" >
matches snapshot 1`] = `
Properties @@ -327,7 +332,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` type="button" > matches snapshot 1`] = ` class="pr-2 col-auto" >
1
@@ -369,7 +374,7 @@ exports[`AcquisitionMenu component > matches snapshot 1`] = ` class="col" >
PLMB
@@ -1342,13 +1347,13 @@ exports[`DispositionView component > renders as expected 1`] = ` class="pr-0 text-left col-5" >
Cannot determine
@@ -1356,10 +1361,10 @@ exports[`DispositionView component > renders as expected 1`] = `

renders as expected 1`] = ` class="pr-0 text-left col-5" >
{activeIndex ? ( - + {label} - + ) : ( handleClick(index)}> @@ -93,9 +93,9 @@ const DispositionMenu: React.FunctionComponent< {activeIndex ? ( - + {label} - + ) : ( {label} @@ -120,6 +120,11 @@ const StyledMenuWrapper = styled.div` color: ${props => props.theme.css.linkColor}; `; +const StyledMenuCol = styled(Col)` + min-height: 2.5rem; + line-height: 3rem; +`; + const StyledRow = styled(Row)` &.selected { font-weight: bold; diff --git a/source/frontend/src/features/mapSideBar/disposition/common/__snapshots__/DispositionMenu.test.tsx.snap b/source/frontend/src/features/mapSideBar/disposition/common/__snapshots__/DispositionMenu.test.tsx.snap index 9401f7a652..b23ce83742 100644 --- a/source/frontend/src/features/mapSideBar/disposition/common/__snapshots__/DispositionMenu.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/disposition/common/__snapshots__/DispositionMenu.test.tsx.snap @@ -6,7 +6,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` class="Toastify" />
- .c5.btn { + .c6.btn { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -35,19 +35,19 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` cursor: pointer; } -.c5.btn .Button__value { +.c6.btn .Button__value { width: -webkit-max-content; width: -moz-max-content; width: max-content; } -.c5.btn:hover { +.c6.btn:hover { -webkit-text-decoration: underline; text-decoration: underline; opacity: 0.8; } -.c5.btn:focus { +.c6.btn:focus { outline-width: 2px; outline-style: solid; outline-color: #2E5DD7; @@ -55,31 +55,31 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` box-shadow: none; } -.c5.btn.btn-primary { +.c6.btn.btn-primary { color: #FFFFFF; background-color: #013366; } -.c5.btn.btn-primary:hover, -.c5.btn.btn-primary:active, -.c5.btn.btn-primary:focus { +.c6.btn.btn-primary:hover, +.c6.btn.btn-primary:active, +.c6.btn.btn-primary:focus { background-color: #1E5189; } -.c5.btn.btn-secondary { +.c6.btn.btn-secondary { color: #013366; background: none; border-color: #013366; } -.c5.btn.btn-secondary:hover, -.c5.btn.btn-secondary:active, -.c5.btn.btn-secondary:focus { +.c6.btn.btn-secondary:hover, +.c6.btn.btn-secondary:active, +.c6.btn.btn-secondary:focus { color: #FFFFFF; background-color: #013366; } -.c5.btn.btn-info { +.c6.btn.btn-info { color: #9F9D9C; border: none; background: none; @@ -87,66 +87,66 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` padding-right: 0.6rem; } -.c5.btn.btn-info:hover, -.c5.btn.btn-info:active, -.c5.btn.btn-info:focus { +.c6.btn.btn-info:hover, +.c6.btn.btn-info:active, +.c6.btn.btn-info:focus { color: var(--surface-color-primary-button-hover); background: none; } -.c5.btn.btn-light { +.c6.btn.btn-light { color: #FFFFFF; background-color: #606060; border: none; } -.c5.btn.btn-light:hover, -.c5.btn.btn-light:active, -.c5.btn.btn-light:focus { +.c6.btn.btn-light:hover, +.c6.btn.btn-light:active, +.c6.btn.btn-light:focus { color: #FFFFFF; background-color: #606060; } -.c5.btn.btn-dark { +.c6.btn.btn-dark { color: #FFFFFF; background-color: #474543; border: none; } -.c5.btn.btn-dark:hover, -.c5.btn.btn-dark:active, -.c5.btn.btn-dark:focus { +.c6.btn.btn-dark:hover, +.c6.btn.btn-dark:active, +.c6.btn.btn-dark:focus { color: #FFFFFF; background-color: #474543; } -.c5.btn.btn-danger { +.c6.btn.btn-danger { color: #FFFFFF; background-color: #CE3E39; } -.c5.btn.btn-danger:hover, -.c5.btn.btn-danger:active, -.c5.btn.btn-danger:focus { +.c6.btn.btn-danger:hover, +.c6.btn.btn-danger:active, +.c6.btn.btn-danger:focus { color: #FFFFFF; background-color: #CE3E39; } -.c5.btn.btn-warning { +.c6.btn.btn-warning { color: #FFFFFF; background-color: #FCBA19; border-color: #FCBA19; } -.c5.btn.btn-warning:hover, -.c5.btn.btn-warning:active, -.c5.btn.btn-warning:focus { +.c6.btn.btn-warning:hover, +.c6.btn.btn-warning:active, +.c6.btn.btn-warning:focus { color: #FFFFFF; border-color: #FCBA19; background-color: #FCBA19; } -.c5.btn.btn-link { +.c6.btn.btn-link { font-size: 1.6rem; font-weight: 400; color: var(--surface-color-primary-button-default); @@ -170,9 +170,9 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` text-decoration: underline; } -.c5.btn.btn-link:hover, -.c5.btn.btn-link:active, -.c5.btn.btn-link:focus { +.c6.btn.btn-link:hover, +.c6.btn.btn-link:active, +.c6.btn.btn-link:focus { color: var(--surface-color-primary-button-hover); -webkit-text-decoration: underline; text-decoration: underline; @@ -182,15 +182,15 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` outline: none; } -.c5.btn.btn-link:disabled, -.c5.btn.btn-link.disabled { +.c6.btn.btn-link:disabled, +.c6.btn.btn-link.disabled { color: #9F9D9C; background: none; pointer-events: none; } -.c5.btn:disabled, -.c5.btn:disabled:hover { +.c6.btn:disabled, +.c6.btn:disabled:hover { color: #9F9D9C; background-color: #EDEBE9; box-shadow: none; @@ -202,15 +202,15 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` cursor: not-allowed; } -.c5.Button .Button__icon { +.c6.Button .Button__icon { margin-right: 1.6rem; } -.c5.Button--icon-only:focus { +.c6.Button--icon-only:focus { outline: none; } -.c5.Button--icon-only .Button__icon { +.c6.Button--icon-only .Button__icon { margin-right: 0; } @@ -222,6 +222,11 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` color: var(--surface-color-primary-button-default); } +.c2 { + min-height: 2.5rem; + line-height: 3rem; +} + .c1 { font-size: 1.4rem; font-weight: normal; @@ -238,7 +243,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` font-size: 1.4rem; } -.c4 { +.c5 { background-color: #fcba19; font-size: 1.5rem; border-radius: 50%; @@ -260,11 +265,11 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` align-items: center; } -.c4.selected { +.c5.selected { background-color: #FCBA19; } -.c2 { +.c3 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -281,7 +286,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` border-bottom: 1px solid #606060; } -.c3 { +.c4 { font-weight: bold; font-size: 1.6rem; color: #474543; @@ -296,7 +301,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` data-testid="menu-item-row-0" >
matches snapshot 1`] = `
Properties @@ -325,7 +330,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` class="pr-2 col-auto" >
1
@@ -334,7 +339,7 @@ exports[`DispositionMenu component > matches snapshot 1`] = ` class="col" >

renders as expected 1`] = ` class="pr-0 text-left col-4" >
No
@@ -1297,14 +1302,14 @@ exports[`ResearchContainer component > renders as expected 1`] = ` class="pr-0 text-left col-4" >

An expropriation note diff --git a/source/frontend/src/features/mapSideBar/research/common/ResearchMenu.tsx b/source/frontend/src/features/mapSideBar/research/common/ResearchMenu.tsx index d8acf4ae1d..5a551eb41e 100644 --- a/source/frontend/src/features/mapSideBar/research/common/ResearchMenu.tsx +++ b/source/frontend/src/features/mapSideBar/research/common/ResearchMenu.tsx @@ -40,9 +40,9 @@ const ResearchMenu: React.FunctionComponent< {props.selectedIndex === index ? ( - + {label} - + ) : ( handleClick(index)}> @@ -56,9 +56,9 @@ const ResearchMenu: React.FunctionComponent< {index === 0 && ( <> {props.selectedIndex === index ? ( - + {label} - + ) : ( handleClick(index)}> @@ -96,6 +96,11 @@ const StyledMenuWrapper = styled.div` color: ${({ theme }) => theme.css.linkColor}; `; +const StyledMenuCol = styled(Col)` + min-height: 2.5rem; + line-height: 3rem; +`; + const StyledRow = styled(Row)` &.selected { font-weight: bold; diff --git a/source/frontend/src/features/mapSideBar/research/common/__snapshots__/ResearchMenu.test.tsx.snap b/source/frontend/src/features/mapSideBar/research/common/__snapshots__/ResearchMenu.test.tsx.snap index 58b0128579..e8c6b08a55 100644 --- a/source/frontend/src/features/mapSideBar/research/common/__snapshots__/ResearchMenu.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/research/common/__snapshots__/ResearchMenu.test.tsx.snap @@ -6,7 +6,7 @@ exports[`ResearchMenu component > renders as expected when provided no research class="Toastify" />
- .c6.btn { + .c7.btn { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -35,19 +35,19 @@ exports[`ResearchMenu component > renders as expected when provided no research cursor: pointer; } -.c6.btn .Button__value { +.c7.btn .Button__value { width: -webkit-max-content; width: -moz-max-content; width: max-content; } -.c6.btn:hover { +.c7.btn:hover { -webkit-text-decoration: underline; text-decoration: underline; opacity: 0.8; } -.c6.btn:focus { +.c7.btn:focus { outline-width: 2px; outline-style: solid; outline-color: #2E5DD7; @@ -55,31 +55,31 @@ exports[`ResearchMenu component > renders as expected when provided no research box-shadow: none; } -.c6.btn.btn-primary { +.c7.btn.btn-primary { color: #FFFFFF; background-color: #013366; } -.c6.btn.btn-primary:hover, -.c6.btn.btn-primary:active, -.c6.btn.btn-primary:focus { +.c7.btn.btn-primary:hover, +.c7.btn.btn-primary:active, +.c7.btn.btn-primary:focus { background-color: #1E5189; } -.c6.btn.btn-secondary { +.c7.btn.btn-secondary { color: #013366; background: none; border-color: #013366; } -.c6.btn.btn-secondary:hover, -.c6.btn.btn-secondary:active, -.c6.btn.btn-secondary:focus { +.c7.btn.btn-secondary:hover, +.c7.btn.btn-secondary:active, +.c7.btn.btn-secondary:focus { color: #FFFFFF; background-color: #013366; } -.c6.btn.btn-info { +.c7.btn.btn-info { color: #9F9D9C; border: none; background: none; @@ -87,66 +87,66 @@ exports[`ResearchMenu component > renders as expected when provided no research padding-right: 0.6rem; } -.c6.btn.btn-info:hover, -.c6.btn.btn-info:active, -.c6.btn.btn-info:focus { +.c7.btn.btn-info:hover, +.c7.btn.btn-info:active, +.c7.btn.btn-info:focus { color: var(--surface-color-primary-button-hover); background: none; } -.c6.btn.btn-light { +.c7.btn.btn-light { color: #FFFFFF; background-color: #606060; border: none; } -.c6.btn.btn-light:hover, -.c6.btn.btn-light:active, -.c6.btn.btn-light:focus { +.c7.btn.btn-light:hover, +.c7.btn.btn-light:active, +.c7.btn.btn-light:focus { color: #FFFFFF; background-color: #606060; } -.c6.btn.btn-dark { +.c7.btn.btn-dark { color: #FFFFFF; background-color: #474543; border: none; } -.c6.btn.btn-dark:hover, -.c6.btn.btn-dark:active, -.c6.btn.btn-dark:focus { +.c7.btn.btn-dark:hover, +.c7.btn.btn-dark:active, +.c7.btn.btn-dark:focus { color: #FFFFFF; background-color: #474543; } -.c6.btn.btn-danger { +.c7.btn.btn-danger { color: #FFFFFF; background-color: #CE3E39; } -.c6.btn.btn-danger:hover, -.c6.btn.btn-danger:active, -.c6.btn.btn-danger:focus { +.c7.btn.btn-danger:hover, +.c7.btn.btn-danger:active, +.c7.btn.btn-danger:focus { color: #FFFFFF; background-color: #CE3E39; } -.c6.btn.btn-warning { +.c7.btn.btn-warning { color: #FFFFFF; background-color: #FCBA19; border-color: #FCBA19; } -.c6.btn.btn-warning:hover, -.c6.btn.btn-warning:active, -.c6.btn.btn-warning:focus { +.c7.btn.btn-warning:hover, +.c7.btn.btn-warning:active, +.c7.btn.btn-warning:focus { color: #FFFFFF; border-color: #FCBA19; background-color: #FCBA19; } -.c6.btn.btn-link { +.c7.btn.btn-link { font-size: 1.6rem; font-weight: 400; color: var(--surface-color-primary-button-default); @@ -170,9 +170,9 @@ exports[`ResearchMenu component > renders as expected when provided no research text-decoration: underline; } -.c6.btn.btn-link:hover, -.c6.btn.btn-link:active, -.c6.btn.btn-link:focus { +.c7.btn.btn-link:hover, +.c7.btn.btn-link:active, +.c7.btn.btn-link:focus { color: var(--surface-color-primary-button-hover); -webkit-text-decoration: underline; text-decoration: underline; @@ -182,15 +182,15 @@ exports[`ResearchMenu component > renders as expected when provided no research outline: none; } -.c6.btn.btn-link:disabled, -.c6.btn.btn-link.disabled { +.c7.btn.btn-link:disabled, +.c7.btn.btn-link.disabled { color: #9F9D9C; background: none; pointer-events: none; } -.c6.btn:disabled, -.c6.btn:disabled:hover { +.c7.btn:disabled, +.c7.btn:disabled:hover { color: #9F9D9C; background-color: #EDEBE9; box-shadow: none; @@ -202,23 +202,23 @@ exports[`ResearchMenu component > renders as expected when provided no research cursor: not-allowed; } -.c6.Button .Button__icon { +.c7.Button .Button__icon { margin-right: 1.6rem; } -.c6.Button--icon-only:focus { +.c7.Button--icon-only:focus { outline: none; } -.c6.Button--icon-only .Button__icon { +.c7.Button--icon-only .Button__icon { margin-right: 0; } -.c4 { +.c5 { fill: #007bff; } -.c4:hover { +.c5:hover { fill: #0056b3; } @@ -230,6 +230,11 @@ exports[`ResearchMenu component > renders as expected when provided no research color: var(--surface-color-primary-button-default); } +.c2 { + min-height: 2.5rem; + line-height: 3rem; +} + .c1 { font-size: 1.4rem; font-weight: normal; @@ -246,7 +251,7 @@ exports[`ResearchMenu component > renders as expected when provided no research font-size: 1.4rem; } -.c5 { +.c6 { background-color: #fcba19; font-size: 1.5rem; border-radius: 50%; @@ -268,11 +273,11 @@ exports[`ResearchMenu component > renders as expected when provided no research align-items: center; } -.c5.selected { +.c6.selected { background-color: #FCBA19; } -.c2 { +.c3 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -289,7 +294,7 @@ exports[`ResearchMenu component > renders as expected when provided no research border-bottom: 1px solid #606060; } -.c3 { +.c4 { font-weight: bold; font-size: 1.6rem; color: #474543; @@ -304,7 +309,7 @@ exports[`ResearchMenu component > renders as expected when provided no research data-testid="menu-item-row-0" >
renders as expected when provided no research
Properties @@ -327,7 +332,7 @@ exports[`ResearchMenu component > renders as expected when provided no research type="button" > renders as expected when provided no research class="pr-2 col-auto" >
1
@@ -369,7 +374,7 @@ exports[`ResearchMenu component > renders as expected when provided no research class="col" >
+ +
+
+
+
+
+
+`; + +exports[`SideNavbar display and logic > renders 2`] = ` +.c4.btn { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + padding: 0.4rem 1.2rem; + border: 0.2rem solid transparent; + border-radius: 0.4rem; + text-align: center; + -webkit-text-decoration: none; + text-decoration: none; + font-size: 1.8rem; + font-family: 'BCSans','Noto Sans',Verdana,Arial,sans-serif; + font-weight: 700; + -webkit-letter-spacing: 0.1rem; + -moz-letter-spacing: 0.1rem; + -ms-letter-spacing: 0.1rem; + letter-spacing: 0.1rem; + cursor: pointer; +} + +.c4.btn .Button__value { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c4.btn:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + opacity: 0.8; +} + +.c4.btn:focus { + outline-width: 2px; + outline-style: solid; + outline-color: #2E5DD7; + outline-offset: 2px; + box-shadow: none; +} + +.c4.btn.btn-primary { + color: #FFFFFF; + background-color: #013366; +} + +.c4.btn.btn-primary:hover, +.c4.btn.btn-primary:active, +.c4.btn.btn-primary:focus { + background-color: #1E5189; +} + +.c4.btn.btn-secondary { + color: #013366; + background: none; + border-color: #013366; +} + +.c4.btn.btn-secondary:hover, +.c4.btn.btn-secondary:active, +.c4.btn.btn-secondary:focus { + color: #FFFFFF; + background-color: #013366; +} + +.c4.btn.btn-info { + color: #9F9D9C; + border: none; + background: none; + padding-left: 0.6rem; + padding-right: 0.6rem; +} + +.c4.btn.btn-info:hover, +.c4.btn.btn-info:active, +.c4.btn.btn-info:focus { + color: var(--surface-color-primary-button-hover); + background: none; +} + +.c4.btn.btn-light { + color: #FFFFFF; + background-color: #606060; + border: none; +} + +.c4.btn.btn-light:hover, +.c4.btn.btn-light:active, +.c4.btn.btn-light:focus { + color: #FFFFFF; + background-color: #606060; +} + +.c4.btn.btn-dark { + color: #FFFFFF; + background-color: #474543; + border: none; +} + +.c4.btn.btn-dark:hover, +.c4.btn.btn-dark:active, +.c4.btn.btn-dark:focus { + color: #FFFFFF; + background-color: #474543; +} + +.c4.btn.btn-danger { + color: #FFFFFF; + background-color: #CE3E39; +} + +.c4.btn.btn-danger:hover, +.c4.btn.btn-danger:active, +.c4.btn.btn-danger:focus { + color: #FFFFFF; + background-color: #CE3E39; +} + +.c4.btn.btn-warning { + color: #FFFFFF; + background-color: #FCBA19; + border-color: #FCBA19; +} + +.c4.btn.btn-warning:hover, +.c4.btn.btn-warning:active, +.c4.btn.btn-warning:focus { + color: #FFFFFF; + border-color: #FCBA19; + background-color: #FCBA19; +} + +.c4.btn.btn-link { + font-size: 1.6rem; + font-weight: 400; + color: var(--surface-color-primary-button-default); + background: none; + border: none; + -webkit-text-decoration: none; + text-decoration: none; + min-height: 2.5rem; + line-height: 3rem; + -webkit-box-pack: left; + -webkit-justify-content: left; + -ms-flex-pack: left; + justify-content: left; + -webkit-letter-spacing: unset; + -moz-letter-spacing: unset; + -ms-letter-spacing: unset; + letter-spacing: unset; + text-align: left; + padding: 0; + -webkit-text-decoration: underline; + text-decoration: underline; +} + +.c4.btn.btn-link:hover, +.c4.btn.btn-link:active, +.c4.btn.btn-link:focus { + color: var(--surface-color-primary-button-hover); + -webkit-text-decoration: underline; + text-decoration: underline; + border: none; + background: none; + box-shadow: none; + outline: none; +} + +.c4.btn.btn-link:disabled, +.c4.btn.btn-link.disabled { + color: #9F9D9C; + background: none; + pointer-events: none; +} + +.c4.btn:disabled, +.c4.btn:disabled:hover { + color: #9F9D9C; + background-color: #EDEBE9; + box-shadow: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + cursor: not-allowed; +} + +.c4.Button .Button__icon { + margin-right: 1.6rem; +} + +.c4.Button--icon-only:focus { + outline: none; +} + +.c4.Button--icon-only .Button__icon { + margin-right: 0; +} + +.c5.btn.btn-light.Button { + padding: 0; + border: 0.1rem solid #9F9D9C; + background-color: white; + color: #474543; +} + +.c5.btn.btn-light.Button:focus, +.c5.btn.btn-light.Button:active { + background-color: white; + color: #474543; +} + +.c5.btn, +.c5 .Button__icon, +.c5 svg { + max-width: 2.4rem; + min-width: 2.4rem; + max-height: 2.4rem; + min-height: 2.4rem; + font-size: 1.4rem; +} + +.c1 { + height: calc( 100vh - 7.2rem - 4.4rem ); + overflow-y: auto; + overflow-x: hidden; + padding-top: 1.5rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + grid-area: iconbar; + background-color: #003366; + -webkit-transition: 0.5s width; + transition: 0.5s width; + width: 6rem; +} + +.c1 label { + color: white; + min-width: 8rem; +} + +.c1.expanded { + width: 16rem; +} + +.c1 .nav-item svg { min-width: 2.4rem; } @@ -328,12 +917,6 @@ exports[`SideNavbar display and logic > renders 1`] = ` fill: #f2f2f2; } -.c2 svg { - min-width: -webkit-max-content; - min-width: -moz-max-content; - min-width: max-content; -} - .c2:hover label { color: #8ec6ff; } @@ -356,6 +939,17 @@ exports[`SideNavbar display and logic > renders 1`] = ` align-items: center; } +.c3 svg { + fill: white; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; +} + +.c3.active svg { + fill: #fdb913; +} + @media (max-width:1225px) { .c6 { width: 32rem; diff --git a/source/frontend/src/components/layout/SideNavBar/styles.ts b/source/frontend/src/components/layout/SideNavBar/styles.ts index ae3b76e6df..a01ea22726 100644 --- a/source/frontend/src/components/layout/SideNavBar/styles.ts +++ b/source/frontend/src/components/layout/SideNavBar/styles.ts @@ -32,7 +32,6 @@ export const SideNavBar = styled.div` width: 16rem; } .nav-item svg { - fill: white; min-width: 2.4rem; } .to-bottom { From 2183180fae895396008050a08476f17cfa4be904 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:46:31 +0000 Subject: [PATCH 24/44] CI: Bump version to v5.7.0-95.10 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 86257bc20e..b8a338ea87 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.9 + 5.7.0-95.10 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 12256297db..ce5249250a 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.9", + "version": "5.7.0-95.10", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 38e8fcc6dc52c2c5fe07285ef3962c3f3751c1ea Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:58:28 -0800 Subject: [PATCH 25/44] Remove unecessary SetOriginalConcurrencyControlNumber method. (#4527) --- .../dal/Helpers/Extensions/DbContextExtensions.cs | 12 ------------ source/backend/dal/Repositories/UserRepository.cs | 4 ---- 2 files changed, 16 deletions(-) diff --git a/source/backend/dal/Helpers/Extensions/DbContextExtensions.cs b/source/backend/dal/Helpers/Extensions/DbContextExtensions.cs index aa8cc965b5..7e4ef89f23 100644 --- a/source/backend/dal/Helpers/Extensions/DbContextExtensions.cs +++ b/source/backend/dal/Helpers/Extensions/DbContextExtensions.cs @@ -8,18 +8,6 @@ namespace Pims.Dal.Helpers.Extensions ///

public static class DbContextExtensions { - /// - /// When manipulating entities it is necessary to reset the original value for 'ConcurrencyControlNumber' so that concurrency checking can occur. - /// - /// - /// - /// The original source entity from the database. - public static void SetOriginalConcurrencyControlNumber(this DbContext context, T source) - where T : IBaseEntity - { - context.Entry(source).OriginalValues[nameof(IBaseEntity.ConcurrencyControlNumber)] = source.ConcurrencyControlNumber; - } - /// /// Detach the entity from the context. /// diff --git a/source/backend/dal/Repositories/UserRepository.cs b/source/backend/dal/Repositories/UserRepository.cs index a280f14964..4e4902ffd1 100644 --- a/source/backend/dal/Repositories/UserRepository.cs +++ b/source/backend/dal/Repositories/UserRepository.cs @@ -458,9 +458,6 @@ public PimsUser UpdateWithoutSave(PimsUser update) user.IssueDate = DateTime.UtcNow; } - user.ConcurrencyControlNumber = update.ConcurrencyControlNumber; - this.Context.SetOriginalConcurrencyControlNumber(user); - var addRoles = update.PimsUserRoles.Except(user.PimsUserRoles, new UserRoleRoleIdComparer()); addRoles.ForEach(r => user.PimsUserRoles.Add(new PimsUserRole() { UserId = user.UserId, RoleId = r.RoleId })); var removeRoles = user.PimsUserRoles.Except(update.PimsUserRoles, new UserRoleRoleIdComparer()); @@ -511,7 +508,6 @@ public void Delete(PimsUser delete) .FirstOrDefault(u => u.UserId == delete.UserId) ?? throw new KeyNotFoundException(); user.ConcurrencyControlNumber = delete.ConcurrencyControlNumber; - this.Context.SetOriginalConcurrencyControlNumber(user); user.PimsUserRoles.ForEach(ur => this.Context.Remove(ur)); user.PimsUserRoles.Clear(); From f8248ad6e1d6b2bf0462b864a3fbab92682246cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:58:53 +0000 Subject: [PATCH 26/44] CI: Bump version to v5.7.0-95.11 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index b8a338ea87..f28845f3c5 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.10 + 5.7.0-95.11 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index ce5249250a..e47035d8ea 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.10", + "version": "5.7.0-95.11", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 127175e7ca2fa0103f48bb4cec7594b8484b7099 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 16 Dec 2024 14:42:15 -0800 Subject: [PATCH 27/44] PSP-9279 UI UX - Acquisition - Agreements & Expropriation - UI Button update for consistency (#4528) * PSP-9279 UI UX cleanup - make generate buttons consistent * Update snapshots * Test updates --- .../__snapshots__/SideNavbar.test.tsx.snap | 590 ------------------ .../LeasePages/details/LeaseDetailView.tsx | 22 +- .../details/LeaseDetailsView.test.tsx | 24 +- .../LeaseDetailsView.test.tsx.snap | 6 +- .../common/GenerateForm/GenerateFormView.tsx | 2 +- .../agreement/detail/AgreementView.test.tsx | 40 +- .../tabs/agreement/detail/AgreementView.tsx | 22 +- .../__snapshots__/AgreementView.test.tsx.snap | 22 +- .../form1/ExpropriationForm1.test.tsx | 16 +- .../form1/ExpropriationForm1.tsx | 6 +- .../ExpropriationForm1.test.tsx.snap | 17 +- .../form5/ExpropriationForm5.test.tsx | 16 +- .../form5/ExpropriationForm5.tsx | 6 +- .../ExpropriationForm5.test.tsx.snap | 17 +- .../details/ExpropriationForm8Details.tsx | 7 +- .../ExpropriationForm8Details.test.tsx.snap | 11 +- .../form9/ExpropriationForm9.test.tsx | 16 +- .../form9/ExpropriationForm9.tsx | 6 +- .../ExpropriationForm9.test.tsx.snap | 17 +- ...CompensationRequisitionDetailView.test.tsx | 25 +- .../CompensationRequisitionDetailView.tsx | 10 +- ...nsationRequisitionDetailView.test.tsx.snap | 11 +- 22 files changed, 215 insertions(+), 694 deletions(-) diff --git a/source/frontend/src/components/layout/SideNavBar/__snapshots__/SideNavbar.test.tsx.snap b/source/frontend/src/components/layout/SideNavBar/__snapshots__/SideNavbar.test.tsx.snap index 785e9ebc34..d5a7e41ddc 100644 --- a/source/frontend/src/components/layout/SideNavBar/__snapshots__/SideNavbar.test.tsx.snap +++ b/source/frontend/src/components/layout/SideNavBar/__snapshots__/SideNavbar.test.tsx.snap @@ -356,596 +356,6 @@ exports[`SideNavbar display and logic > renders 1`] = ` min-width: max-content; } -.c3.active svg { - fill: #FDB913; -} - -@media (max-width:1225px) { - .c6 { - width: 32rem; - } - - .c6.show { - -webkit-transform: translateX(32rem); - -ms-transform: translateX(32rem); - transform: translateX(32rem); - } -} - -
-
-
-
-
- - - - - - - - - - -
-
-
-
-
-
-`; - -exports[`SideNavbar display and logic > renders 2`] = ` -.c4.btn { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - padding: 0.4rem 1.2rem; - border: 0.2rem solid transparent; - border-radius: 0.4rem; - text-align: center; - -webkit-text-decoration: none; - text-decoration: none; - font-size: 1.8rem; - font-family: 'BCSans','Noto Sans',Verdana,Arial,sans-serif; - font-weight: 700; - -webkit-letter-spacing: 0.1rem; - -moz-letter-spacing: 0.1rem; - -ms-letter-spacing: 0.1rem; - letter-spacing: 0.1rem; - cursor: pointer; -} - -.c4.btn .Button__value { - width: -webkit-max-content; - width: -moz-max-content; - width: max-content; -} - -.c4.btn:hover { - -webkit-text-decoration: underline; - text-decoration: underline; - opacity: 0.8; -} - -.c4.btn:focus { - outline-width: 2px; - outline-style: solid; - outline-color: #2E5DD7; - outline-offset: 2px; - box-shadow: none; -} - -.c4.btn.btn-primary { - color: #FFFFFF; - background-color: #013366; -} - -.c4.btn.btn-primary:hover, -.c4.btn.btn-primary:active, -.c4.btn.btn-primary:focus { - background-color: #1E5189; -} - -.c4.btn.btn-secondary { - color: #013366; - background: none; - border-color: #013366; -} - -.c4.btn.btn-secondary:hover, -.c4.btn.btn-secondary:active, -.c4.btn.btn-secondary:focus { - color: #FFFFFF; - background-color: #013366; -} - -.c4.btn.btn-info { - color: #9F9D9C; - border: none; - background: none; - padding-left: 0.6rem; - padding-right: 0.6rem; -} - -.c4.btn.btn-info:hover, -.c4.btn.btn-info:active, -.c4.btn.btn-info:focus { - color: var(--surface-color-primary-button-hover); - background: none; -} - -.c4.btn.btn-light { - color: #FFFFFF; - background-color: #606060; - border: none; -} - -.c4.btn.btn-light:hover, -.c4.btn.btn-light:active, -.c4.btn.btn-light:focus { - color: #FFFFFF; - background-color: #606060; -} - -.c4.btn.btn-dark { - color: #FFFFFF; - background-color: #474543; - border: none; -} - -.c4.btn.btn-dark:hover, -.c4.btn.btn-dark:active, -.c4.btn.btn-dark:focus { - color: #FFFFFF; - background-color: #474543; -} - -.c4.btn.btn-danger { - color: #FFFFFF; - background-color: #CE3E39; -} - -.c4.btn.btn-danger:hover, -.c4.btn.btn-danger:active, -.c4.btn.btn-danger:focus { - color: #FFFFFF; - background-color: #CE3E39; -} - -.c4.btn.btn-warning { - color: #FFFFFF; - background-color: #FCBA19; - border-color: #FCBA19; -} - -.c4.btn.btn-warning:hover, -.c4.btn.btn-warning:active, -.c4.btn.btn-warning:focus { - color: #FFFFFF; - border-color: #FCBA19; - background-color: #FCBA19; -} - -.c4.btn.btn-link { - font-size: 1.6rem; - font-weight: 400; - color: var(--surface-color-primary-button-default); - background: none; - border: none; - -webkit-text-decoration: none; - text-decoration: none; - min-height: 2.5rem; - line-height: 3rem; - -webkit-box-pack: left; - -webkit-justify-content: left; - -ms-flex-pack: left; - justify-content: left; - -webkit-letter-spacing: unset; - -moz-letter-spacing: unset; - -ms-letter-spacing: unset; - letter-spacing: unset; - text-align: left; - padding: 0; - -webkit-text-decoration: underline; - text-decoration: underline; -} - -.c4.btn.btn-link:hover, -.c4.btn.btn-link:active, -.c4.btn.btn-link:focus { - color: var(--surface-color-primary-button-hover); - -webkit-text-decoration: underline; - text-decoration: underline; - border: none; - background: none; - box-shadow: none; - outline: none; -} - -.c4.btn.btn-link:disabled, -.c4.btn.btn-link.disabled { - color: #9F9D9C; - background: none; - pointer-events: none; -} - -.c4.btn:disabled, -.c4.btn:disabled:hover { - color: #9F9D9C; - background-color: #EDEBE9; - box-shadow: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - pointer-events: none; - cursor: not-allowed; -} - -.c4.Button .Button__icon { - margin-right: 1.6rem; -} - -.c4.Button--icon-only:focus { - outline: none; -} - -.c4.Button--icon-only .Button__icon { - margin-right: 0; -} - -.c5.btn.btn-light.Button { - padding: 0; - border: 0.1rem solid #9F9D9C; - background-color: white; - color: #474543; -} - -.c5.btn.btn-light.Button:focus, -.c5.btn.btn-light.Button:active { - background-color: white; - color: #474543; -} - -.c5.btn, -.c5 .Button__icon, -.c5 svg { - max-width: 2.4rem; - min-width: 2.4rem; - max-height: 2.4rem; - min-height: 2.4rem; - font-size: 1.4rem; -} - -.c1 { - height: calc( 100vh - 7.2rem - 4.4rem ); - overflow-y: auto; - overflow-x: hidden; - padding-top: 1.5rem; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - position: relative; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - grid-area: iconbar; - background-color: #003366; - -webkit-transition: 0.5s width; - transition: 0.5s width; - width: 6rem; -} - -.c1 label { - color: white; - min-width: 8rem; -} - -.c1.expanded { - width: 16rem; -} - -.c1 .nav-item svg { - min-width: 2.4rem; -} - -.c1 .to-bottom { - margin-top: auto; - margin-bottom: 1.6rem; -} - -.c1 .nav-link { - padding: 0.8rem 1.6rem; -} - -.c0 { - z-index: 1000; - position: relative; -} - -.c7 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - height: 100%; -} - -.c7 a { - font-size: 1.7rem; -} - -.c6 { - height: 100%; - overflow-y: auto; - position: absolute; - top: 0; - right: 0; - background-color: white; - z-index: -1; - width: 64rem; - padding: 0.8rem 1.6rem; - text-align: left; - -webkit-transition: -webkit-transform 0.3s ease-in-out; - -webkit-transition: transform 0.3s ease-in-out; - transition: transform 0.3s ease-in-out; - box-shadow: 0.3rem 0 0.4rem rgba(0,0,0,0.2); -} - -.c6.show { - -webkit-transform: translateX(64rem); - -ms-transform: translateX(64rem); - transform: translateX(64rem); -} - -.c2 { - width: 100%; - margin-bottom: 2rem; - fill: #f2f2f2; -} - -.c2:hover label { - color: #8ec6ff; -} - -.c2:hover svg { - fill: #8ec6ff; -} - -.c3 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c3 svg { - fill: white; - min-width: -webkit-max-content; - min-width: -moz-max-content; - min-width: max-content; -} - .c3.active svg { fill: #fdb913; } diff --git a/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailView.tsx b/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailView.tsx index fa08459e40..16c009e8bc 100644 --- a/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailView.tsx +++ b/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailView.tsx @@ -44,28 +44,18 @@ export const LeaseDetailView: React.FunctionComponent< {hasClaim(Claims.LEASE_VIEW) && exists(leaseTypeCode) && leaseTypeCode === ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA && ( - onGenerate(lease)}> - - Generate H1005(a) + onGenerate(lease)}> + + Generate H-1005(a) )} {hasClaim(Claims.LEASE_VIEW) && exists(leaseTypeCode) && leaseTypeCode === ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY && ( - onGenerate(lease)}> - - Generate H1005 + onGenerate(lease)}> + + Generate H-1005 )} diff --git a/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailsView.test.tsx b/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailsView.test.tsx index 8e8478a3e0..fea5179f9f 100644 --- a/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailsView.test.tsx +++ b/source/frontend/src/features/leases/detail/LeasePages/details/LeaseDetailsView.test.tsx @@ -133,11 +133,11 @@ describe('LeaseDetailView component', () => { }); it.each([ - [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H1005(a)'], - [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H1005'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H-1005(a)'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H-1005'], ])( 'does not render generation button if missing permissions - %s', - async (leaseTypeCode: string, buttonTitle: string) => { + async (leaseTypeCode: string, buttonText: string) => { setup({ props: { lease: { @@ -156,7 +156,7 @@ describe('LeaseDetailView component', () => { claims: [], }); - expect(screen.queryByText(buttonTitle)).toBeNull(); + expect(screen.queryByText(buttonText)).toBeNull(); }, ); @@ -178,15 +178,15 @@ describe('LeaseDetailView component', () => { }, }); - expect(screen.queryByText(/Generate H1005/i)).toBeNull(); + expect(screen.queryByText(/Generate H-1005/i)).toBeNull(); }); it.each([ - [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H1005(a)'], - [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H1005'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H-1005(a)'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H-1005'], ])( 'only renders generation button for specific lease types - %s', - async (leaseTypeCode: string, buttonTitle: string) => { + async (leaseTypeCode: string, buttonText: string) => { setup({ props: { lease: { @@ -204,13 +204,13 @@ describe('LeaseDetailView component', () => { }, }); - expect(await screen.findByTitle(buttonTitle)).toBeInTheDocument(); + expect(await screen.findByText(buttonText)).toBeInTheDocument(); }, ); it.each([ - [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H1005(a)'], - [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H1005'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LOOBCTFA, 'Generate H-1005(a)'], + [ApiGen_CodeTypes_LeaseLicenceTypes.LIPPUBHWY, 'Generate H-1005'], ])( 'calls onGenerate when generation button is clicked - %s', async (leaseTypeCode: string, buttonTitle: string) => { @@ -231,7 +231,7 @@ describe('LeaseDetailView component', () => { }, }); - const generateButton = await screen.findByTitle(buttonTitle); + const generateButton = await screen.findByText(buttonTitle); expect(generateButton).toBeInTheDocument(); await act(async () => userEvent.click(generateButton)); diff --git a/source/frontend/src/features/leases/detail/LeasePages/details/__snapshots__/LeaseDetailsView.test.tsx.snap b/source/frontend/src/features/leases/detail/LeasePages/details/__snapshots__/LeaseDetailsView.test.tsx.snap index 88e0c555eb..092cb7b767 100644 --- a/source/frontend/src/features/leases/detail/LeasePages/details/__snapshots__/LeaseDetailsView.test.tsx.snap +++ b/source/frontend/src/features/leases/detail/LeasePages/details/__snapshots__/LeaseDetailsView.test.tsx.snap @@ -292,6 +292,7 @@ exports[`LeaseDetailView component > renders minimally as expected 1`] = ` >
diff --git a/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/GenerateFormView.tsx b/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/GenerateFormView.tsx index fc9248f849..75906f58cc 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/GenerateFormView.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/GenerateFormView.tsx @@ -50,7 +50,7 @@ const GenerateFormView: React.FunctionComponent< onGenerateClick(entry.formType)} - title="Generate" + title="Download File" > {entry.text} diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.test.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.test.tsx index 23af780d01..e409d92f71 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.test.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.test.tsx @@ -1,10 +1,12 @@ +import { Claims } from '@/constants'; import { mockAgreementsResponse } from '@/mocks/agreements.mock'; import { mockLookups } from '@/mocks/index.mock'; import { lookupCodesSlice } from '@/store/slices/lookupCodes'; -import { render, RenderOptions } from '@/utils/test-utils'; +import { act, render, RenderOptions, screen, userEvent } from '@/utils/test-utils'; -import AgreementView, { IAgreementViewProps } from './AgreementView'; import AcquisitionFileStatusUpdateSolver from '../../fileDetails/detail/AcquisitionFileStatusUpdateSolver'; +import AgreementView, { IAgreementViewProps } from './AgreementView'; +import { ApiGen_CodeTypes_AcquisitionStatusTypes } from '@/models/api/generated/ApiGen_CodeTypes_AcquisitionStatusTypes'; // mock auth library @@ -43,6 +45,7 @@ describe('AgreementView component', () => { beforeEach(() => { mockViewProps.agreements = mockAgreementsResponse(); + mockViewProps.statusUpdateSolver = new AcquisitionFileStatusUpdateSolver(); }); afterEach(() => { @@ -54,8 +57,39 @@ describe('AgreementView component', () => { expect(asFragment()).toMatchSnapshot(); }); - it('renders the agreement type ', () => { + it('renders the agreement type', () => { const { getByText } = setup(); expect(getByText(/License Of Occupation/i)).toBeVisible(); }); + + it('calls onGenerate when generation button is clicked', async () => { + const { getAllByTitle } = setup(); + + const generateButtons = getAllByTitle(/Download File/i); + expect(generateButtons).toHaveLength(2); + await act(async () => userEvent.click(generateButtons[0])); + + expect(mockViewProps.onGenerate).toHaveBeenCalled(); + }); + + it('displays confirmation modal when Delete Agreement button is clicked', async () => { + mockViewProps.statusUpdateSolver = new AcquisitionFileStatusUpdateSolver({ + id: ApiGen_CodeTypes_AcquisitionStatusTypes.ACTIVE, + description: '', + displayOrder: 1, + isDisabled: false, + }); + + const { getAllByTitle } = setup({ + claims: [Claims.ACQUISITION_EDIT], + }); + + const removeButtons = getAllByTitle(/Delete Agreement/i); + expect(removeButtons).toHaveLength(2); + await act(async () => userEvent.click(removeButtons[0])); + + expect( + screen.getByText(/You have selected to delete this Agreement/i, { exact: false }), + ).toBeVisible(); + }); }); diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.tsx index cfe33029f8..2e16e2b9bb 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/AgreementView.tsx @@ -1,4 +1,4 @@ -import { FaMailBulk, FaPlus, FaTrash } from 'react-icons/fa'; +import { FaFileContract, FaPlus, FaTrash } from 'react-icons/fa'; import { useHistory, useRouteMatch } from 'react-router-dom'; import styled from 'styled-components'; @@ -69,12 +69,13 @@ export const AgreementView: React.FunctionComponent = ({ {exists(agreement.agreementType) && ( { onGenerate(agreement); }} > - - Generate + + {`Generate ${getAgreementFormName(agreement.agreementType.id)}`} {!statusUpdateSolver.canEditOrDeleteAgreement( @@ -200,6 +201,21 @@ export const AgreementView: React.FunctionComponent = ({ export default AgreementView; +function getAgreementFormName(agreementType: string | null): string { + switch (agreementType) { + case ApiGen_CodeTypes_AgreementTypes.H179A: + return 'H-179A'; + case ApiGen_CodeTypes_AgreementTypes.H179P: + return 'H-179P'; + case ApiGen_CodeTypes_AgreementTypes.H179T: + return 'H-179T'; + case ApiGen_CodeTypes_AgreementTypes.H0074: + return 'H-0074'; + default: + return ''; + } +} + export const StyledButtonContainer = styled.div` display: flex; flex-direction: row; diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/__snapshots__/AgreementView.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/__snapshots__/AgreementView.test.tsx.snap index 631ccb72f1..1184be4b0b 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/__snapshots__/AgreementView.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/agreement/detail/__snapshots__/AgreementView.test.tsx.snap @@ -392,6 +392,7 @@ exports[`AgreementView component > renders as expected 1`] = ` > renders as expected 1`] = ` > { }); it('validates form values before generating', async () => { - const { getByText } = await setup(); - await act(async () => userEvent.click(getByText('Generate'))); + const { getByText, getByTitle } = await setup(); + await act(async () => userEvent.click(getByTitle(/Download File/i))); expect(getByText('Expropriation authority is required')).toBeInTheDocument(); expect(getByText('At lease one impacted property is required')).toBeInTheDocument(); @@ -83,10 +83,10 @@ describe('Expropriation Form 1', () => { await act(async () => userEvent.paste(getNatureOfInterest(), 'foo')); await act(async () => userEvent.paste(getPurpose(), 'bar')); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).not.toBeCalled(); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); }); it(`clears the form when Cancel button is clicked`, async () => { @@ -122,9 +122,9 @@ describe('Expropriation Form 1', () => { await act(async () => userEvent.paste(getNatureOfInterest(), 'foo')); await act(async () => userEvent.paste(getPurpose(), 'bar')); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).toBeCalledWith(error); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).toHaveBeenCalledWith(error); }); }); diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/ExpropriationForm1.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/ExpropriationForm1.tsx index fb597c7cd1..c1fd6f27df 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/ExpropriationForm1.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/ExpropriationForm1.tsx @@ -1,6 +1,7 @@ import { Formik, FormikHelpers, FormikProps } from 'formik'; import { Fragment } from 'react'; import { Col, Row } from 'react-bootstrap'; +import { FaFileContract } from 'react-icons/fa'; import styled from 'styled-components'; import { Button } from '@/components/common/buttons'; @@ -101,7 +102,10 @@ export const ExpropriationForm1: React.FC = ({ - + diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/__snapshots__/ExpropriationForm1.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/__snapshots__/ExpropriationForm1.test.tsx.snap index 70dd8872eb..9ad25523cf 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/__snapshots__/ExpropriationForm1.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form1/__snapshots__/ExpropriationForm1.test.tsx.snap @@ -831,12 +831,27 @@ exports[`Expropriation Form 1 > matches snapshot 1`] = ` >
diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.test.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.test.tsx index 8a45adffb0..b8acb8b15c 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.test.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.test.tsx @@ -60,8 +60,8 @@ describe('Expropriation Form 1', () => { }); it('validates form values before generating', async () => { - const { getByText } = await setup(); - await act(async () => userEvent.click(getByText('Generate'))); + const { getByText, getByTitle } = await setup(); + await act(async () => userEvent.click(getByTitle(/Download File/i))); expect(getByText('Expropriation authority is required')).toBeInTheDocument(); expect(getByText('At lease one impacted property is required')).toBeInTheDocument(); @@ -77,10 +77,10 @@ describe('Expropriation Form 1', () => { // fill other form fields await act(async () => userEvent.click(getByTestId('selectrow-1'))); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).not.toBeCalled(); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); }); it(`clears the form when Cancel button is clicked`, async () => { @@ -105,9 +105,9 @@ describe('Expropriation Form 1', () => { // fill other form fields await act(async () => userEvent.click(getByTestId('selectrow-1'))); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).toBeCalledWith(error); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).toHaveBeenCalledWith(error); }); }); diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.tsx index b0d9d690c0..9b88065596 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/ExpropriationForm5.tsx @@ -1,6 +1,7 @@ import { Formik, FormikHelpers, FormikProps } from 'formik'; import React from 'react'; import { Col, Row } from 'react-bootstrap'; +import { FaFileContract } from 'react-icons/fa'; import styled from 'styled-components'; import { Button } from '@/components/common/buttons'; @@ -94,7 +95,10 @@ export const ExpropriationForm5: React.FC = ({ - + diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/__snapshots__/ExpropriationForm5.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/__snapshots__/ExpropriationForm5.test.tsx.snap index de73a2f81c..60e64c5842 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/__snapshots__/ExpropriationForm5.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form5/__snapshots__/ExpropriationForm5.test.tsx.snap @@ -753,12 +753,27 @@ exports[`Expropriation Form 1 > matches snapshot 1`] = ` >
diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/ExpropriationForm8Details.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/ExpropriationForm8Details.tsx index 940e2fc569..4d9d338000 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/ExpropriationForm8Details.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/ExpropriationForm8Details.tsx @@ -1,5 +1,5 @@ import { Col, Row } from 'react-bootstrap'; -import { FaExternalLinkAlt, FaMoneyCheck, FaTrash } from 'react-icons/fa'; +import { FaExternalLinkAlt, FaFileContract, FaTrash } from 'react-icons/fa'; import { useHistory, useRouteMatch } from 'react-router-dom'; import styled from 'styled-components'; @@ -51,11 +51,12 @@ export const ExpropriationForm8Details: React.FunctionComponent<
onGenerate(form8.id as number, acquisitionFileNumber)} > - - Generate + + Generate Form 8 {keycloak.hasClaim(Claims.ACQUISITION_EDIT) && ( diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/__snapshots__/ExpropriationForm8Details.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/__snapshots__/ExpropriationForm8Details.test.tsx.snap index a2b4d33637..02a2a8930d 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/__snapshots__/ExpropriationForm8Details.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form8/details/__snapshots__/ExpropriationForm8Details.test.tsx.snap @@ -340,6 +340,7 @@ exports[`Form 8 Detail View component > renders as expected 1`] = ` diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.test.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.test.tsx index e73016a842..4a7ff6aa1c 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.test.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.test.tsx @@ -62,8 +62,8 @@ describe('Expropriation Form 1', () => { }); it('validates form values before generating', async () => { - const { getByText } = await setup(); - await act(async () => userEvent.click(getByText('Generate'))); + const { getByText, getByTitle } = await setup(); + await act(async () => userEvent.click(getByTitle(/Download File/i))); expect(getByText('Expropriation authority is required')).toBeInTheDocument(); expect(getByText('At lease one impacted property is required')).toBeInTheDocument(); @@ -81,10 +81,10 @@ describe('Expropriation Form 1', () => { await act(async () => userEvent.click(getByTestId('selectrow-1'))); await act(async () => userEvent.paste(getRegisteredPlanNumbers(), 'testing')); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).not.toBeCalled(); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); }); it(`clears the form when Cancel button is clicked`, async () => { @@ -116,9 +116,9 @@ describe('Expropriation Form 1', () => { await act(async () => userEvent.click(getByTestId('selectrow-1'))); await act(async () => userEvent.paste(getRegisteredPlanNumbers(), 'testing')); - await act(async () => userEvent.click(getByText('Generate'))); + await act(async () => userEvent.click(getByTitle(/Download File/i))); - expect(onGenerate).toBeCalled(); - expect(onError).toBeCalledWith(error); + expect(onGenerate).toHaveBeenCalled(); + expect(onError).toHaveBeenCalledWith(error); }); }); diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.tsx index 7c86de1d67..6ff2b334d1 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/ExpropriationForm9.tsx @@ -1,6 +1,7 @@ import { Formik, FormikHelpers, FormikProps } from 'formik'; import React from 'react'; import { Col, Row } from 'react-bootstrap'; +import { FaFileContract } from 'react-icons/fa'; import styled from 'styled-components'; import { Button } from '@/components/common/buttons'; @@ -98,7 +99,10 @@ export const ExpropriationForm9: React.FC = ({ - + diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/__snapshots__/ExpropriationForm9.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/__snapshots__/ExpropriationForm9.test.tsx.snap index 64a2690433..7ef7e89233 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/__snapshots__/ExpropriationForm9.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/expropriation/form9/__snapshots__/ExpropriationForm9.test.tsx.snap @@ -792,12 +792,27 @@ exports[`Expropriation Form 1 > matches snapshot 1`] = ` > diff --git a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx index 22665b9b56..8a754a40d6 100644 --- a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx +++ b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx @@ -9,17 +9,18 @@ import { getMockApiDefaultCompensation, getMockCompensationPropertiesReq, } from '@/mocks/compensations.mock'; +import { ApiGen_CodeTypes_AcquisitionStatusTypes } from '@/models/api/generated/ApiGen_CodeTypes_AcquisitionStatusTypes'; +import { ApiGen_CodeTypes_FileTypes } from '@/models/api/generated/ApiGen_CodeTypes_FileTypes'; import { ApiGen_Concepts_CompensationRequisition } from '@/models/api/generated/ApiGen_Concepts_CompensationRequisition'; import { toTypeCodeNullable } from '@/utils/formUtils'; -import { act, render, RenderOptions, userEvent, waitFor } from '@/utils/test-utils'; +import { act, render, RenderOptions, userEvent, waitForEffects } from '@/utils/test-utils'; import CompensationRequisitionDetailView, { CompensationRequisitionDetailViewProps, } from './CompensationRequisitionDetailView'; -import { ApiGen_CodeTypes_AcquisitionStatusTypes } from '@/models/api/generated/ApiGen_CodeTypes_AcquisitionStatusTypes'; -import { ApiGen_CodeTypes_FileTypes } from '@/models/api/generated/ApiGen_CodeTypes_FileTypes'; const setEditMode = vi.fn(); +const onGenerate = vi.fn(); const history = createMemoryHistory(); @@ -39,7 +40,7 @@ describe('Compensation Detail View Component', () => { loading={renderOptions.props?.loading ?? false} setEditMode={setEditMode} clientConstant={renderOptions.props?.clientConstant ?? '034'} - onGenerate={vi.fn()} + onGenerate={onGenerate} compensationContactPerson={undefined} compensationContactOrganization={undefined} />, @@ -50,6 +51,8 @@ describe('Compensation Detail View Component', () => { }, ); + await waitForEffects(); + return { ...component, }; @@ -61,8 +64,7 @@ describe('Compensation Detail View Component', () => { it('renders as expected', async () => { const { asFragment } = await setup({}); - const fragment = await waitFor(() => asFragment()); - expect(fragment).toMatchSnapshot(); + expect(asFragment()).toMatchSnapshot(); }); it('Displays the Compensation Requisition Header Information with Draft Status', async () => { @@ -194,7 +196,7 @@ describe('Compensation Detail View Component', () => { }); it('displays the acquisition files properties selected', async () => { - const { findByText, findByTestId } = await setup({ + const { findByText } = await setup({ claims: [Claims.COMPENSATION_REQUISITION_EDIT], props: { compensation: getMockApiCompensationWithProperty(), @@ -250,4 +252,13 @@ describe('Compensation Detail View Component', () => { expect(queryByTestId('file-product')).toHaveTextContent('00048'); }); + + it('calls onGenerate when generation button is clicked', async () => { + const { getByTitle } = await setup({}); + + const generateButton = getByTitle(/Download File/i); + await act(async () => userEvent.click(generateButton)); + + expect(onGenerate).toHaveBeenCalled(); + }); }); diff --git a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.tsx b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.tsx index b093e47cc7..ffc4b1f8f5 100644 --- a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.tsx +++ b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.tsx @@ -1,7 +1,6 @@ -import { useEffect, useState } from 'react'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Col, Row } from 'react-bootstrap'; -import { FaExternalLinkAlt, FaMoneyCheck } from 'react-icons/fa'; +import { FaExternalLinkAlt, FaFileContract } from 'react-icons/fa'; import { Link } from 'react-router-dom'; import styled from 'styled-components'; @@ -253,12 +252,13 @@ export const CompensationRequisitionDetailView: React.FunctionComponent< /> )} { onGenerate(fileType, compensation); }} > - - Generate H120 + + Generate H-120 diff --git a/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap b/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap index 9f20af92f5..c75e9ce073 100644 --- a/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/compensation/detail/__snapshots__/CompensationRequisitionDetailView.test.tsx.snap @@ -496,6 +496,7 @@ exports[`Compensation Detail View Component > renders as expected 1`] = ` From 8209d25932645840f0f67146b4719bf251a9815d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:42:39 +0000 Subject: [PATCH 28/44] CI: Bump version to v5.7.0-95.12 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index f28845f3c5..a09cdf4c14 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.11 + 5.7.0-95.12 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index e47035d8ea..c9609e911a 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.11", + "version": "5.7.0-95.12", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From b964adc35f17b400fa4f41fa13f0bb46d3c71fbf Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:36:34 -0800 Subject: [PATCH 29/44] psp-9632 limit the default bounds when resetting. (#4533) Co-authored-by: Smith Co-authored-by: Alejandro Sanchez --- .../components/common/mapFSM/machineDefinition/mapMachine.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts index 40e41f403c..8d1b6d7fbb 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts @@ -185,7 +185,10 @@ const mapRequestStates = { any >; const filteredBounds = geoJSON(featureCollection).getBounds(); - const validBounds = filteredBounds.isValid() ? filteredBounds : defaultBounds; + const validBounds = + filteredBounds.isValid() && defaultBounds.contains(filteredBounds) // we should not be automatically setting the bounds outside the default bounds of british columbia. + ? filteredBounds + : defaultBounds; // if the current map bounds contain the bounds of the filtered properties, use the current map bounds. if ( From f21bc059b043179f221d6b19ba73550143263f20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:37:00 +0000 Subject: [PATCH 30/44] CI: Bump version to v5.7.0-95.13 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index a09cdf4c14..b51a843429 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.12 + 5.7.0-95.13 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index c9609e911a..d6bd701569 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.12", + "version": "5.7.0-95.13", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 9ea5bd9f18c355030e606e40a7623a4d189f2a0f Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:55:46 -0800 Subject: [PATCH 31/44] psp-9715 fix issues adding properties with no locations, and also override research file property updates. (#4530) Co-authored-by: Alejandro Sanchez --- .../api/Services/AcquisitionFileService.cs | 5 + .../api/Services/DispositionFileService.cs | 5 + source/backend/api/Services/LeaseService.cs | 5 + .../backend/api/Services/PropertyService.cs | 6 +- .../api/Services/ResearchFileService.cs | 5 + .../ResearchFilePropertyRepository.cs | 7 +- .../Services/AcquisitionFileServiceTest.cs | 40 ++++++++ .../Services/DispositionFileServiceTest.cs | 39 ++++++++ .../unit/api/Services/LeaseServiceTest.cs | 35 +++++++ .../api/Services/ResearchFileServiceTest.cs | 94 +++++++++++++++++++ 10 files changed, 233 insertions(+), 8 deletions(-) diff --git a/source/backend/api/Services/AcquisitionFileService.cs b/source/backend/api/Services/AcquisitionFileService.cs index 54e1c32a0b..8f23f5fdce 100644 --- a/source/backend/api/Services/AcquisitionFileService.cs +++ b/source/backend/api/Services/AcquisitionFileService.cs @@ -315,6 +315,11 @@ public PimsAcquisitionFile UpdateProperties(PimsAcquisitionFile acquisitionFile, // Check if the property is new or if it is being updated foreach (var incomingAcquisitionProperty in acquisitionFile.PimsPropertyAcquisitionFiles) { + var matchingProperty = currentFileProperties.FirstOrDefault(c => c.PropertyId == incomingAcquisitionProperty.PropertyId); + if (matchingProperty is not null && incomingAcquisitionProperty.Internal_Id == 0) + { + incomingAcquisitionProperty.Internal_Id = matchingProperty.Internal_Id; + } // If the property is not new, check if the name has been updated. if (incomingAcquisitionProperty.Internal_Id != 0) { diff --git a/source/backend/api/Services/DispositionFileService.cs b/source/backend/api/Services/DispositionFileService.cs index aded90176d..89b870e499 100644 --- a/source/backend/api/Services/DispositionFileService.cs +++ b/source/backend/api/Services/DispositionFileService.cs @@ -501,6 +501,11 @@ public PimsDispositionFile UpdateProperties(PimsDispositionFile dispositionFile, // Check if the property is new or if it is being updated foreach (var incomingDispositionProperty in dispositionFile.PimsDispositionFileProperties) { + var matchingProperty = currentFileProperties.FirstOrDefault(c => c.PropertyId == incomingDispositionProperty.PropertyId); + if (matchingProperty is not null && incomingDispositionProperty.Internal_Id == 0) + { + incomingDispositionProperty.Internal_Id = matchingProperty.Internal_Id; + } // If the property is not new, check if the name has been updated. if (incomingDispositionProperty.Internal_Id != 0) { diff --git a/source/backend/api/Services/LeaseService.cs b/source/backend/api/Services/LeaseService.cs index bf53885351..1fba211a92 100644 --- a/source/backend/api/Services/LeaseService.cs +++ b/source/backend/api/Services/LeaseService.cs @@ -256,6 +256,11 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr // Update marker locations in the context of this file foreach (var incomingLeaseProperty in leaseWithProperties.PimsPropertyLeases) { + var matchingProperty = currentFileProperties.FirstOrDefault(c => c.PropertyId == incomingLeaseProperty.PropertyId); + if (matchingProperty is not null && incomingLeaseProperty.Internal_Id == 0) + { + incomingLeaseProperty.Internal_Id = matchingProperty.Internal_Id; + } // If the property is not new, check if the marker location has been updated. if (incomingLeaseProperty.Internal_Id != 0) { diff --git a/source/backend/api/Services/PropertyService.cs b/source/backend/api/Services/PropertyService.cs index 4c0b502fb5..863b540e0a 100644 --- a/source/backend/api/Services/PropertyService.cs +++ b/source/backend/api/Services/PropertyService.cs @@ -107,7 +107,7 @@ public PimsProperty Update(PimsProperty property, bool commitTransaction = true) // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage var geom = property.Location; - if (geom.SRID != SpatialReference.BCALBERS) + if (geom != null && geom.SRID != SpatialReference.BCALBERS) { var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); property.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); @@ -371,7 +371,7 @@ public PimsProperty PopulateNewProperty(PimsProperty property, bool isOwned = fa // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage var geom = property.Location; - if (geom.SRID != SpatialReference.BCALBERS) + if (geom != null && geom.SRID != SpatialReference.BCALBERS) { var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); property.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); @@ -397,7 +397,7 @@ public void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty prope // convert spatial location from lat/long (4326) to BC Albers (3005) for database storage var geom = incomingProperty.Location; - if (geom.SRID != SpatialReference.BCALBERS) + if (geom != null && geom.SRID != SpatialReference.BCALBERS) { var newCoords = _coordinateService.TransformCoordinates(geom.SRID, SpatialReference.BCALBERS, geom.Coordinate); propertyToUpdate.Location = GeometryHelper.CreatePoint(newCoords, SpatialReference.BCALBERS); diff --git a/source/backend/api/Services/ResearchFileService.cs b/source/backend/api/Services/ResearchFileService.cs index ab8e10e58a..988a467b9f 100644 --- a/source/backend/api/Services/ResearchFileService.cs +++ b/source/backend/api/Services/ResearchFileService.cs @@ -115,6 +115,11 @@ public PimsResearchFile UpdateProperties(PimsResearchFile researchFile, IEnumera // Check if the property is new or if it is being updated foreach (var incomingResearchProperty in researchFile.PimsPropertyResearchFiles) { + var matchingProperty = currentFileProperties.FirstOrDefault(c => c.PropertyId == incomingResearchProperty.PropertyId); + if (matchingProperty is not null && incomingResearchProperty.Internal_Id == 0) + { + incomingResearchProperty.Internal_Id = matchingProperty.Internal_Id; + } // If the property is not new, check if the name has been updated. if (incomingResearchProperty.Internal_Id != 0) { diff --git a/source/backend/dal/Repositories/ResearchFilePropertyRepository.cs b/source/backend/dal/Repositories/ResearchFilePropertyRepository.cs index ddee4e3d41..826356f900 100644 --- a/source/backend/dal/Repositories/ResearchFilePropertyRepository.cs +++ b/source/backend/dal/Repositories/ResearchFilePropertyRepository.cs @@ -73,11 +73,8 @@ public void Delete(PimsPropertyResearchFile propertyResearchFile) public PimsPropertyResearchFile Update(PimsPropertyResearchFile propertyResearchFile) { - // Mark the property not to be changed it was being tracked. - if (propertyResearchFile.Property != null) - { - Context.Entry(propertyResearchFile.Property).State = EntityState.Unchanged; - } + // Do not allow this method to make any updates to the related property entity. + propertyResearchFile.Property = null; // Retrieve the existing property research purpose types for the property // Note: This is needed given the research file properties purpose types may not have the corresponging id, but corresponding code. diff --git a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs index e93f7aeb68..bea06de2d5 100644 --- a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs @@ -1382,6 +1382,46 @@ public void UpdateProperties_MatchProperties_Success() filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void UpdateProperties_MatchProperties_Success_NoInternalId() + { + // Arrange + var service = this.CreateAcquisitionServiceWithPermissions(Permissions.AcquisitionFileEdit, Permissions.PropertyAdd, Permissions.PropertyView); + + var acqFile = EntityHelper.CreateAcquisitionFile(); + acqFile.ConcurrencyControlNumber = 1; + + var property = EntityHelper.CreateProperty(1, regionCode: 1); + acqFile.PimsPropertyAcquisitionFiles = new List() { new PimsPropertyAcquisitionFile() { Internal_Id = 0, Property = property, PropertyId = 1 } }; + var propertyAcquisitionFiles = new List() { new PimsPropertyAcquisitionFile() { Internal_Id = 1, Property = property, PropertyId = 1 } }; + + var repository = this._helper.GetService>(); + repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); + repository.Setup(x => x.GetById(It.IsAny())).Returns(acqFile); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(property); + propertyRepository.Setup(x => x.GetPropertyRegion(It.IsAny())).Returns(1); + + var filePropertyRepository = this._helper.GetService>(); + filePropertyRepository.Setup(x => x.GetPropertiesByAcquisitionFileId(It.IsAny())).Returns(propertyAcquisitionFiles); + + var userRepository = this._helper.GetService>(); + userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); + + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + + var solver = this._helper.GetService>(); + solver.Setup(x => x.CanEditProperties(It.IsAny())).Returns(true); + + // Act + var response = service.UpdateProperties(acqFile, new List() { UserOverrideCode.AddLocationToProperty }); + + // Assert + var updatedProperty = response.PimsPropertyAcquisitionFiles.FirstOrDefault().Internal_Id.Should().Be(1); } diff --git a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs index b51dcb1ab8..f57b446a39 100644 --- a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs @@ -866,6 +866,7 @@ public void Update_Success() repository.Verify(x => x.Update(It.IsAny(), It.IsAny()), Times.Once); } + [Fact] public void Update_Success_FinalButAdmin() { @@ -1000,6 +1001,44 @@ public void UpdateProperties_MatchProperties_Success() propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } + [Fact] + public void UpdateProperties_MatchProperties_Success_NoInternalId() + { + // Arrange + var service = this.CreateDispositionServiceWithPermissions(Permissions.DispositionEdit, Permissions.PropertyAdd, Permissions.PropertyView); + + var dspFile = EntityHelper.CreateDispositionFile(); + dspFile.ConcurrencyControlNumber = 1; + + var property = EntityHelper.CreateProperty(1, regionCode: 1); + dspFile.PimsDispositionFileProperties = new List() { new PimsDispositionFileProperty() { Internal_Id = 0, Property = property, PropertyId = 1 } }; + var dispositionFileProperties = new List() { new PimsDispositionFileProperty() { Internal_Id = 1, Property = property, PropertyId = 1 } }; + + var repository = this._helper.GetService>(); + repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); + repository.Setup(x => x.GetById(It.IsAny())).Returns(dspFile); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(property); + propertyRepository.Setup(x => x.GetPropertyRegion(It.IsAny())).Returns(1); + + var filePropertyRepository = this._helper.GetService>(); + filePropertyRepository.Setup(x => x.GetPropertiesByDispositionFileId(It.IsAny())).Returns(dispositionFileProperties); + + var userRepository = this._helper.GetService>(); + userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); + + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); + + // Act + var updatedDispositionFile = service.UpdateProperties(dspFile, new List() { UserOverrideCode.AddLocationToProperty }); + + // Assert + var updatedProperty = updatedDispositionFile.PimsDispositionFileProperties.FirstOrDefault().Internal_Id.Should().Be(1); + } + [Fact] public void UpdateProperties_MatchProperties_NewProperty_UserOverride() { diff --git a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs index 206eedb102..8e37da010e 100644 --- a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs +++ b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs @@ -523,6 +523,41 @@ public void UpdateProperties_MatchProperties_Success() propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); } + [Fact] + public void UpdateProperties_MatchProperties_Success_NoInternalId() + { + // Arrange + var lease = EntityHelper.CreateLease(1); + + var property = EntityHelper.CreateProperty(1, regionCode: 1); + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); + var leaseRepository = this._helper.GetService>(); + var propertyLeaseRepository = this._helper.GetService>(); + var propertyRepository = this._helper.GetService>(); + var userRepository = this._helper.GetService>(); + var propertyLeases = new List() {new PimsPropertyLease() + { + Property = property, + PropertyId = 1, + Lease = lease, + Internal_Id = 1, + } }; + + propertyLeaseRepository.Setup(x => x.GetAllByLeaseId(It.IsAny())).Returns(propertyLeases); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(lease.PimsPropertyLeases.FirstOrDefault().Property); + leaseRepository.Setup(x => x.GetNoTracking(It.IsAny())).Returns(lease); + userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); + + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + + // Act + var updatedLease = service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); + + // Assert + var updatedProperty = updatedLease.PimsPropertyLeases.FirstOrDefault().Internal_Id.Should().Be(1); + } + [Fact] public void UpdateProperties_MatchProperties_NewProperty_Success() { diff --git a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs index 86f0bd29b3..a6267ad0a2 100644 --- a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs @@ -297,6 +297,100 @@ public void UpdateProperties_MatchProperties_PID_NewProperty_Success() propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Once); } + [Fact] + public void UpdateProperties_MatchProperties_PID_NoLocation() + { + // Arrange + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); + + var researchFile = EntityHelper.CreateResearchFile(); + researchFile.ConcurrencyControlNumber = 1; + + var property = EntityHelper.CreateProperty(12345); + property.Location.SRID = SpatialReference.WGS84; + researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Property = property, Location = null } }; + + PimsPropertyResearchFile updatedResearchFileProperty = null; + + var repository = this._helper.GetService>(); + repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); + repository.Setup(x => x.GetById(It.IsAny())).Returns(researchFile); + + var filePropertyRepository = this._helper.GetService>(); + filePropertyRepository.Setup(x => x.GetAllByResearchFileId(It.IsAny())).Returns(researchFile.PimsPropertyResearchFiles.ToList()); + filePropertyRepository.Setup(x => x.Add(It.IsAny())).Callback(x => updatedResearchFileProperty = x).Returns(researchFile.PimsPropertyResearchFiles.FirstOrDefault()); + + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new PimsProperty() + { + PropertyDataSourceEffectiveDate = DateOnly.FromDateTime(System.DateTime.Now), + PropertyDataSourceTypeCode = "PMBC", + PropertyTypeCode = "UNKNOWN", + PropertyStatusTypeCode = "UNKNOWN", + SurplusDeclarationTypeCode = "UNKNOWN", + RegionCode = 1 + }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Throws(); + + // Act + service.UpdateProperties(researchFile, new List()); + + // Assert + // since this is a new property, the following default fields should be set. + var updatedProperty = updatedResearchFileProperty.Property; + updatedProperty.Location.Should().BeNull(); + } + + [Fact] + public void UpdateProperties_MatchProperties_PID_NoInternalId() + { + // Arrange + var service = this.CreateResearchFileServiceWithPermissions(Permissions.ResearchFileEdit, Permissions.PropertyView, Permissions.PropertyAdd); + + var researchFile = EntityHelper.CreateResearchFile(); + researchFile.ConcurrencyControlNumber = 1; + + var property = EntityHelper.CreateProperty(12345); + property.Location.SRID = SpatialReference.WGS84; + researchFile.PimsPropertyResearchFiles = new List() { new PimsPropertyResearchFile() { Property = property, PropertyId = 1, Internal_Id = 0 } }; + var propertyResearchFiles = new List() { new PimsPropertyResearchFile() { Property = property, PropertyId = 1, Internal_Id = 1 } }; + + PimsPropertyResearchFile updatedResearchFileProperty = null; + + var repository = this._helper.GetService>(); + repository.Setup(x => x.GetRowVersion(It.IsAny())).Returns(1); + repository.Setup(x => x.GetById(It.IsAny())).Returns(researchFile); + + var filePropertyRepository = this._helper.GetService>(); + filePropertyRepository.Setup(x => x.GetAllByResearchFileId(It.IsAny())).Returns(propertyResearchFiles); + filePropertyRepository.Setup(x => x.Add(It.IsAny())).Callback(x => updatedResearchFileProperty = x).Returns(researchFile.PimsPropertyResearchFiles.FirstOrDefault()); + + var propertyService = this._helper.GetService>(); + propertyService.Setup(x => x.PopulateNewProperty(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new PimsProperty() + { + PropertyDataSourceEffectiveDate = DateOnly.FromDateTime(System.DateTime.Now), + PropertyDataSourceTypeCode = "PMBC", + PropertyTypeCode = "UNKNOWN", + PropertyStatusTypeCode = "UNKNOWN", + SurplusDeclarationTypeCode = "UNKNOWN", + RegionCode = 1 + }); + propertyService.Setup(x => x.PopulateNewFileProperty(It.IsAny())).Returns(x => x); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Throws(); + + // Act + var updatedResearchFile = service.UpdateProperties(researchFile, new List()); + + // Assert + // since this is a new property, the following default fields should be set. + var updatedProperty = updatedResearchFile.PimsPropertyResearchFiles.FirstOrDefault().Internal_Id.Should().Be(1); + } + [Fact] public void UpdateProperties_MatchProperties_PIN_NewProperty_Success() { From 2c4433fe46100a3b68c54646b4ff955fbb4b8b94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:56:09 +0000 Subject: [PATCH 32/44] CI: Bump version to v5.7.0-95.14 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index b51a843429..767881689a 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.13 + 5.7.0-95.14 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index d6bd701569..4f0b2afc80 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.13", + "version": "5.7.0-95.14", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From e652ff8410251b7e7fe03a41cf61049c24c4457c Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 16 Dec 2024 17:52:49 -0800 Subject: [PATCH 33/44] [CI] Fix build failures after ubuntu-latest upgrade to 24.04 (#4537) --- .github/workflows/api-dotnetcore.yml | 16 +++- .github/workflows/app-logging.yml | 87 +++++++++---------- .github/workflows/app-react.yml | 4 +- .github/workflows/ci-cd-pims-dev.yml | 16 ++-- .github/workflows/codecov-comment-pr.yml | 2 +- .github/workflows/credentials-comment-pr.yml | 2 +- .github/workflows/credentials-scan.yml | 2 +- .github/workflows/db-schma.yml | 2 +- .github/workflows/deploy-prod-end.yml | 4 +- .github/workflows/deploy-prod-start-argo.yml | 10 +-- .github/workflows/deploy-prod-start.yml | 13 ++- .github/workflows/image-scan-analysis.yml | 32 +++---- .github/workflows/integration-test.yml | 2 +- .github/workflows/keycloak-sync.yml | 2 +- .github/workflows/mayan-sync.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/retag-dev-to-test.yml | 19 ++-- .github/workflows/retag-test-to-uat-argo.yml | 14 +-- .github/workflows/retag-test-to-uat.yml | 14 +-- .github/workflows/tag.yml | 2 +- .github/workflows/uat_hotfix.yml | 18 ++-- .github/workflows/uat_hotfix_argo.yml | 18 ++-- .github/workflows/uat_pre_release_hotfix.yml | 22 ++--- .../workflows/uat_pre_release_hotfix_argo.yml | 22 ++--- .github/workflows/version.yml | 2 +- .github/workflows/zap-scan.yml | 2 +- 26 files changed, 167 insertions(+), 164 deletions(-) diff --git a/.github/workflows/api-dotnetcore.yml b/.github/workflows/api-dotnetcore.yml index 7344a24221..7201b33fbc 100644 --- a/.github/workflows/api-dotnetcore.yml +++ b/.github/workflows/api-dotnetcore.yml @@ -9,7 +9,7 @@ on: jobs: # JOB to run change detection check-changes: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # Set job outputs to values from filter step outputs: backend: ${{ steps.filter.outputs.backend }} @@ -24,11 +24,19 @@ jobs: build-backend: needs: check-changes - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: ${{ needs.check-changes.outputs.backend == 'true' }} strategy: matrix: - services: [{directory: ./source/backend/api, solution: 'Pims.sln'}, {directory: ./source/backend/proxy, solution: 'Proxy.sln'}, {directory: ./source/backend/scheduler, solution: 'Scheduler.sln'}] + services: + [ + { directory: ./source/backend/api, solution: "Pims.sln" }, + { directory: ./source/backend/proxy, solution: "Proxy.sln" }, + { + directory: ./source/backend/scheduler, + solution: "Scheduler.sln", + }, + ] env: working-directory: ${{ matrix.services.directory }} solution-name: ${{ matrix.services.solution }} @@ -147,7 +155,7 @@ jobs: sonarHostname: ${{secrets.SONAR_URL}} post-build: needs: build-backend - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./source/backend GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/app-logging.yml b/.github/workflows/app-logging.yml index da0d235d23..a98977ee74 100644 --- a/.github/workflows/app-logging.yml +++ b/.github/workflows/app-logging.yml @@ -6,14 +6,11 @@ on: pull_request: branches: [master, test, dev] - jobs: - build: - name: build-logging if: github.event.ref == 'refs/heads/master' || github.event.ref == 'refs/heads/test' || github.event.ref == 'refs/heads/dev' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: SLEEP_TIME: 60 STORAGE_TYPE: Amazon_S3 @@ -32,44 +29,44 @@ jobs: working-directory: ./openshift/4.0/templates/Logging steps: - - uses: actions/checkout@v4 - - name: Set ENV variable - run: | - if [[ ${{github.event.ref}} == 'refs/heads/test' ]]; then - echo "FRONTEND_APP_NAME=pims-app-test" >> $GITHUB_ENV - echo "API_NAME=pims-api-test" >> $GITHUB_ENV - elif [[ ${{github.event.ref}} == 'refs/heads/master' ]]; then - echo "FRONTEND_APP_NAME=pims-app-uat" >> $GITHUB_ENV - echo "API_NAME=pims-api-uat" >> $GITHUB_ENV - echo "PROJECT_NAMESPACE=3cd915-test" >> $GITHUB_ENV - else - echo "FRONTEND_APP_NAME=pims-app" >> $GITHUB_ENV - echo "API_NAME=pims-api" >> $GITHUB_ENV - fi - - name: Build the pims-logging docker-compose stack - run: docker-compose -f docker-compose.yml up -d - working-directory: ${{env.working-directory}} - - name: Sleep for 180 seconds - uses: jakejarvis/wait-action@master - with: - time: '180s' - - name: Check Extracted Logs - run: | - docker cp pims-logging:/logging/. . - exitcode=$(docker inspect pims-logging --format='{{.State.ExitCode}}') - if [[ "$(ls -A pims* 2>/dev/null | wc -l)" != "0" ]]; then - ls -A pims* && rm -f pims* - elif [[ $exitcode == 0 ]]; then - echo "Info: No log captured between sleep time" - else - echo "There's an error capturing pims logs" && exit 1 - fi - - name: Check running containers - run: docker ps -a - - name: Check pims-logging logs - if: always() - run: docker logs pims-logging - - name: Stop containers - if: always() - run: docker-compose -f "docker-compose.yml" down - working-directory: ${{env.working-directory}} + - uses: actions/checkout@v4 + - name: Set ENV variable + run: | + if [[ ${{github.event.ref}} == 'refs/heads/test' ]]; then + echo "FRONTEND_APP_NAME=pims-app-test" >> $GITHUB_ENV + echo "API_NAME=pims-api-test" >> $GITHUB_ENV + elif [[ ${{github.event.ref}} == 'refs/heads/master' ]]; then + echo "FRONTEND_APP_NAME=pims-app-uat" >> $GITHUB_ENV + echo "API_NAME=pims-api-uat" >> $GITHUB_ENV + echo "PROJECT_NAMESPACE=3cd915-test" >> $GITHUB_ENV + else + echo "FRONTEND_APP_NAME=pims-app" >> $GITHUB_ENV + echo "API_NAME=pims-api" >> $GITHUB_ENV + fi + - name: Build the pims-logging docker-compose stack + run: docker-compose -f docker-compose.yml up -d + working-directory: ${{env.working-directory}} + - name: Sleep for 180 seconds + uses: jakejarvis/wait-action@master + with: + time: "180s" + - name: Check Extracted Logs + run: | + docker cp pims-logging:/logging/. . + exitcode=$(docker inspect pims-logging --format='{{.State.ExitCode}}') + if [[ "$(ls -A pims* 2>/dev/null | wc -l)" != "0" ]]; then + ls -A pims* && rm -f pims* + elif [[ $exitcode == 0 ]]; then + echo "Info: No log captured between sleep time" + else + echo "There's an error capturing pims logs" && exit 1 + fi + - name: Check running containers + run: docker ps -a + - name: Check pims-logging logs + if: always() + run: docker logs pims-logging + - name: Stop containers + if: always() + run: docker-compose -f "docker-compose.yml" down + working-directory: ${{env.working-directory}} diff --git a/.github/workflows/app-react.yml b/.github/workflows/app-react.yml index 87453e04f3..7ec1afe7d7 100644 --- a/.github/workflows/app-react.yml +++ b/.github/workflows/app-react.yml @@ -9,7 +9,7 @@ on: jobs: # JOB to run change detection check-changes: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # Set job outputs to values from filter step outputs: frontend: ${{ steps.filter.outputs.frontend }} @@ -25,7 +25,7 @@ jobs: build-frontend: needs: check-changes if: ${{ needs.check-changes.outputs.frontend == 'true' }} - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: CI: true working-directory: ./source/frontend diff --git a/.github/workflows/ci-cd-pims-dev.yml b/.github/workflows/ci-cd-pims-dev.yml index 6cfb198229..fc879705e4 100644 --- a/.github/workflows/ci-cd-pims-dev.yml +++ b/.github/workflows/ci-cd-pims-dev.yml @@ -32,7 +32,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -46,7 +46,7 @@ jobs: build-frontend: name: Build frontend needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -65,7 +65,7 @@ jobs: build-api: name: Build api needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: deploy: name: Deploy to OpenShift needs: [build-frontend, build-api] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -132,7 +132,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -155,7 +155,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -177,7 +177,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -195,7 +195,7 @@ jobs: ci-cd-end-notification: name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: keycloak-sync steps: - name: check workflow status diff --git a/.github/workflows/codecov-comment-pr.yml b/.github/workflows/codecov-comment-pr.yml index e7b73d6545..987102e947 100644 --- a/.github/workflows/codecov-comment-pr.yml +++ b/.github/workflows/codecov-comment-pr.yml @@ -10,7 +10,7 @@ on: jobs: upload: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: > github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' diff --git a/.github/workflows/credentials-comment-pr.yml b/.github/workflows/credentials-comment-pr.yml index d214955d67..fb6a19921e 100644 --- a/.github/workflows/credentials-comment-pr.yml +++ b/.github/workflows/credentials-comment-pr.yml @@ -10,7 +10,7 @@ on: jobs: # this action will leave a comment in response to credential scans performed on pull requests on-completed: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: github.event.workflow_run.event == 'pull_request' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/credentials-scan.yml b/.github/workflows/credentials-scan.yml index 7ecfb6cfb5..409877e8ab 100644 --- a/.github/workflows/credentials-scan.yml +++ b/.github/workflows/credentials-scan.yml @@ -10,7 +10,7 @@ on: jobs: credentials-scan: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/db-schma.yml b/.github/workflows/db-schma.yml index 951b95de25..04fae0de53 100644 --- a/.github/workflows/db-schma.yml +++ b/.github/workflows/db-schma.yml @@ -11,7 +11,7 @@ on: jobs: build: name: db-schema - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read diff --git a/.github/workflows/deploy-prod-end.yml b/.github/workflows/deploy-prod-end.yml index 12d22b02d2..6842681833 100644 --- a/.github/workflows/deploy-prod-end.yml +++ b/.github/workflows/deploy-prod-end.yml @@ -11,7 +11,7 @@ on: jobs: maintenance-page: name: Hide the maintenance page - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -31,7 +31,7 @@ jobs: ci-cd-end-notification: name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: maintenance-page steps: - name: check workflow status diff --git a/.github/workflows/deploy-prod-start-argo.yml b/.github/workflows/deploy-prod-start-argo.yml index fc92bf5cbd..27694729d3 100644 --- a/.github/workflows/deploy-prod-start-argo.yml +++ b/.github/workflows/deploy-prod-start-argo.yml @@ -35,7 +35,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -48,7 +48,7 @@ jobs: deploy: name: Deploy frontend and api to OpenShift - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: ci-cd-start-notification steps: - name: Checkout Source Code @@ -94,7 +94,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -117,7 +117,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -139,7 +139,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 diff --git a/.github/workflows/deploy-prod-start.yml b/.github/workflows/deploy-prod-start.yml index 80865d36c2..f0a1170762 100644 --- a/.github/workflows/deploy-prod-start.yml +++ b/.github/workflows/deploy-prod-start.yml @@ -34,7 +34,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -47,7 +47,7 @@ jobs: deploy: name: Deploy frontend and api to OpenShift - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: ci-cd-start-notification steps: - name: Checkout Source Code @@ -84,7 +84,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -107,7 +107,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -125,11 +125,11 @@ jobs: oc process -f ./openshift/4.0/templates/jobs/mayan-sync.yaml -p NAMESPACE=3cd915-prod -p TOKEN_URL=https://loginproxy.gov.bc.ca:443/auth/realms/standard/protocol/openid-connect/token -p CLIENT_ID=property-services-project-api-4380 -p MAYAN_SYNC_URL=https://pims-app-3cd915-prod.apps.silver.devops.gov.bc.ca/documents/sync/documenttype -p KEYCLOAK_SECRET_NAME=pims-api-sso | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read TASK_NAME; oc wait --for=condition=succeeded taskruns/$TASK_NAME --timeout=80s) oc process -f ./openshift/4.0/templates/jobs/mayan-sync.yaml -p NAMESPACE=3cd915-prod -p TOKEN_URL=https://loginproxy.gov.bc.ca:443/auth/realms/standard/protocol/openid-connect/token -p CLIENT_ID=property-services-project-api-4380 -p MAYAN_SYNC_URL=https://pims-app-3cd915-prod.apps.silver.devops.gov.bc.ca/api/documents/sync/mayan -p KEYCLOAK_SECRET_NAME=pims-api-sso | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read TASK_NAME; oc wait --for=condition=succeeded taskruns/$TASK_NAME --timeout=80s) -## Call the tekton pipeline that executes the keycloak sync. Dependent on the pims-api being accessible. Can run in parallel with the mayan sync. + ## Call the tekton pipeline that executes the keycloak sync. Dependent on the pims-api being accessible. Can run in parallel with the mayan sync. keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -144,4 +144,3 @@ jobs: shell: bash run: | oc process -f ./openshift/4.0/templates/jobs/keycloak-sync-pipeline-run.yaml -p ASPNETCORE_ENVIRONMENT=$ASPNETCORE_ENVIRONMENT -p NAMESPACE=3cd915-prod -p BRANCH=$DESTINATION -p API_URL=http://pims-api:8080/api | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read PIPELINE_NAME; oc wait --for=condition=succeeded pipelineruns/$PIPELINE_NAME --timeout=500s) - diff --git a/.github/workflows/image-scan-analysis.yml b/.github/workflows/image-scan-analysis.yml index e31f88a92b..7d116332de 100644 --- a/.github/workflows/image-scan-analysis.yml +++ b/.github/workflows/image-scan-analysis.yml @@ -12,7 +12,7 @@ jobs: nodejs-base-image: name: nodejs-base-image if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -75,7 +75,7 @@ jobs: # this action will leave a comment in response to vulnerability scans performed on cotnainer image if: always() && needs.nodejs-base-image.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: nodejs-base-image - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./source/frontend image-name: nodejs-14-ubi8 @@ -145,7 +145,7 @@ jobs: nginx-base-image: name: nginx-base-image if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -205,7 +205,7 @@ jobs: # this action will leave a comment in response to vulnerability scans performed on cotnainer image if: always() && needs.nginx-base-image.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: nginx-base-image - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./source/frontend image-name: nginx-base @@ -276,7 +276,7 @@ jobs: if: always() && github.event.pull_request.head.repo.full_name == github.repository needs: [nodejs-base-image, nginx-base-image] name: pims-frontend - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -338,7 +338,7 @@ jobs: # this action will leave a comment in response to credential scans performed on pull requests if: always() && needs.build_frontend.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: build_frontend - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./source/frontend image-name: pims-app @@ -408,7 +408,7 @@ jobs: aspnet-runtime: name: aspnet-runtime if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -468,7 +468,7 @@ jobs: # this action will leave a comment in response to credential scans performed on pull requests if: always() && needs.aspnet-runtime.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: aspnet-runtime - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./openshift/4.0/templates/base-images/dotnet50 image-name: dotnet-aspnet @@ -538,7 +538,7 @@ jobs: dotnet-sdk: name: dotnet5-sdk if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -598,7 +598,7 @@ jobs: # this action will leave a comment in response to credential scans performed on pull requests if: always() && needs.dotnet-sdk.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: dotnet-sdk - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./openshift/4.0/templates/base-images/dotnet50 image-name: dotnet-sdk @@ -669,7 +669,7 @@ jobs: if: always() && github.event.pull_request.head.repo.full_name == github.repository needs: [aspnet-runtime, dotnet-sdk] name: pims-backend - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -729,7 +729,7 @@ jobs: # this action will leave a comment in response to credential scans performed on pull requests if: always() && needs.build_backend.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: build_backend - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./source/backend image-name: pims-api @@ -800,7 +800,7 @@ jobs: if: always() && github.event.pull_request.head.repo.full_name == github.repository needs: [build_frontend, build_backend] name: logging - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -861,7 +861,7 @@ jobs: # this action will leave a comment in response to Image scans performed on pull requests if: always() && needs.pims_logging.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: pims_logging - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./openshift/4.0/templates/jenkins-slaves/jenkins-slave-zap image-name: pims-logging @@ -930,7 +930,7 @@ jobs: jenkins-agent-dotnet: if: github.event.pull_request.head.repo.full_name == github.repository name: jenkins-agent-dotnet - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read @@ -992,7 +992,7 @@ jobs: # this action will leave a comment in response to credential scans performed on pull requests if: always() && needs.jenkins-agent-dotnet.result == 'failure' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: jenkins-agent-dotnet - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: working-directory: ./openshift/4.0/templates/jenkins-slaves/jenkins-slave-zap image-name: jenkins-agent-dotnet diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 3f6d9ce617..ae50f5e065 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -19,7 +19,7 @@ on: jobs: smoke-test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/keycloak-sync.yml b/.github/workflows/keycloak-sync.yml index c6033e1920..27cfc2a96a 100644 --- a/.github/workflows/keycloak-sync.yml +++ b/.github/workflows/keycloak-sync.yml @@ -13,7 +13,7 @@ on: jobs: sync-keycloak: name: Sync Keycloak - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 diff --git a/.github/workflows/mayan-sync.yml b/.github/workflows/mayan-sync.yml index 342e7a010f..008a3f9f1a 100644 --- a/.github/workflows/mayan-sync.yml +++ b/.github/workflows/mayan-sync.yml @@ -32,7 +32,7 @@ on: jobs: mayan-sync: name: mayan-sync - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 816bc26baf..8891eb641b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/retag-dev-to-test.yml b/.github/workflows/retag-dev-to-test.yml index fcf314150e..06b9cbeca2 100644 --- a/.github/workflows/retag-dev-to-test.yml +++ b/.github/workflows/retag-dev-to-test.yml @@ -34,7 +34,7 @@ on: workflow_dispatch jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -48,7 +48,7 @@ jobs: deploy: name: Retag/Deploy to OpenShift needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -95,7 +95,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -113,12 +113,12 @@ jobs: oc wait --for=condition=complete job/$JOB_NAME --timeout=120s oc get pods -o custom-columns=POD:.metadata.name --no-headers | grep -Eo $JOB_NAME-[^\s].* | (read POD_NAME; oc logs $POD_NAME) -## Call the mayan sync task three times, once for each mayan sync endpoint. The task will wait for the job to complete before exiting. -## Note: this depends on the mayan-sync configmap for the target namespace being up to date. + ## Call the mayan sync task three times, once for each mayan sync endpoint. The task will wait for the job to complete before exiting. + ## Note: this depends on the mayan-sync configmap for the target namespace being up to date. mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -136,11 +136,11 @@ jobs: oc process -f ./openshift/4.0/templates/jobs/mayan-sync.yaml -p NAMESPACE=3cd915-dev -p TOKEN_URL=https://dev.loginproxy.gov.bc.ca:443/auth/realms/standard/protocol/openid-connect/token -p CLIENT_ID=property-services-project-api-4380 -p MAYAN_SYNC_URL=https://pims-app-test-3cd915-dev.apps.silver.devops.gov.bc.ca:443/api/documents/sync/documenttype -p KEYCLOAK_SECRET_NAME=pims-api-sso-test | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read TASK_NAME; oc wait --for=condition=succeeded taskruns/$TASK_NAME --timeout=80s) oc process -f ./openshift/4.0/templates/jobs/mayan-sync.yaml -p NAMESPACE=3cd915-dev -p TOKEN_URL=https://dev.loginproxy.gov.bc.ca:443/auth/realms/standard/protocol/openid-connect/token -p CLIENT_ID=property-services-project-api-4380 -p MAYAN_SYNC_URL=https://pims-app-test-3cd915-dev.apps.silver.devops.gov.bc.ca:443/api/documents/sync/mayan -p KEYCLOAK_SECRET_NAME=pims-api-sso-test | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read TASK_NAME; oc wait --for=condition=succeeded taskruns/$TASK_NAME --timeout=80s) -## Call the tekton pipeline that executes the keycloak sync. Dependent on the pims-api being accessible. Can run in parallel with the mayan sync. + ## Call the tekton pipeline that executes the keycloak sync. Dependent on the pims-api being accessible. Can run in parallel with the mayan sync. keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -156,10 +156,9 @@ jobs: run: | oc process -f ./openshift/4.0/templates/jobs/keycloak-sync-pipeline-run.yaml -p ASPNETCORE_ENVIRONMENT=$ASPNETCORE_ENVIRONMENT -p NAMESPACE=$NAMESPACE_OVERRIDE -p BRANCH=dev -p KEYCLOAK_SECRET_NAME=pims-api-sso-test -p KEYCLOAK_SERVICE_ACCOUNT_SECRET_NAME=pims-api-sso-test -p API_URL=http://pims-api-test:8080/api | oc create -f - | grep -oP "(?<=\/)[^\s]*" | (read PIPELINE_NAME; oc wait --for=condition=succeeded pipelineruns/$PIPELINE_NAME --timeout=600s) - ci-cd-end-notification: name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: keycloak-sync steps: - name: check workflow status diff --git a/.github/workflows/retag-test-to-uat-argo.yml b/.github/workflows/retag-test-to-uat-argo.yml index 37229edaaf..6626fa8e0e 100644 --- a/.github/workflows/retag-test-to-uat-argo.yml +++ b/.github/workflows/retag-test-to-uat-argo.yml @@ -35,7 +35,7 @@ on: workflow_dispatch jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -49,7 +49,7 @@ jobs: deploy: name: Retag/Deploy to OpenShift needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -96,7 +96,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -119,7 +119,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -141,7 +141,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -160,7 +160,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -183,7 +183,7 @@ jobs: ci-cd-end-notification: name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [keycloak-sync, mayan-sync] if: always() steps: diff --git a/.github/workflows/retag-test-to-uat.yml b/.github/workflows/retag-test-to-uat.yml index 902161f187..6b12fdc362 100644 --- a/.github/workflows/retag-test-to-uat.yml +++ b/.github/workflows/retag-test-to-uat.yml @@ -35,7 +35,7 @@ on: workflow_dispatch jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -49,7 +49,7 @@ jobs: deploy: name: Retag/Deploy frontend and api to OpenShift needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -82,7 +82,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -105,7 +105,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -127,7 +127,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -146,7 +146,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -169,7 +169,7 @@ jobs: ci-cd-end-notification: name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [keycloak-sync, mayan-sync] if: always() steps: diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 408cf3a934..69d71ce36b 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -9,7 +9,7 @@ jobs: prepare-release: # this job will only run if the PR has been merged if: github.event.pull_request.merged == true - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/uat_hotfix.yml b/.github/workflows/uat_hotfix.yml index 08fc02883b..8301332c5f 100644 --- a/.github/workflows/uat_hotfix.yml +++ b/.github/workflows/uat_hotfix.yml @@ -33,7 +33,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -47,7 +47,7 @@ jobs: build-frontend: name: Build frontend needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -66,7 +66,7 @@ jobs: build-api: name: Build api needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -86,7 +86,7 @@ jobs: deploy: name: Deploy frontend and api to OpenShift needs: [build-frontend, build-api] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -111,7 +111,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -132,7 +132,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -154,7 +154,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -173,7 +173,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -196,7 +196,7 @@ jobs: ci-cd-end-notification: if: always() name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [mayan-sync, keycloak-sync] steps: - name: check workflow status diff --git a/.github/workflows/uat_hotfix_argo.yml b/.github/workflows/uat_hotfix_argo.yml index ff3e0826da..90b4c9832c 100644 --- a/.github/workflows/uat_hotfix_argo.yml +++ b/.github/workflows/uat_hotfix_argo.yml @@ -34,7 +34,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -48,7 +48,7 @@ jobs: build-frontend: name: Build frontend needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -67,7 +67,7 @@ jobs: build-api: name: Build api needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -87,7 +87,7 @@ jobs: deploy: name: Deploy to OpenShift needs: [build-frontend, build-api] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -130,7 +130,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -151,7 +151,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -173,7 +173,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -192,7 +192,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -216,7 +216,7 @@ jobs: ci-cd-end-notification: if: always() name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [mayan-sync, keycloak-sync] steps: - name: check workflow status diff --git a/.github/workflows/uat_pre_release_hotfix.yml b/.github/workflows/uat_pre_release_hotfix.yml index c1de9fedfa..b5895e9893 100644 --- a/.github/workflows/uat_pre_release_hotfix.yml +++ b/.github/workflows/uat_pre_release_hotfix.yml @@ -37,7 +37,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -51,7 +51,7 @@ jobs: create-builds: name: create builds needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -72,7 +72,7 @@ jobs: build-frontend: name: Build frontend needs: create-builds - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -92,7 +92,7 @@ jobs: build-api: name: Build api needs: create-builds - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -112,7 +112,7 @@ jobs: deploy: name: Deploy frontend and api to OpenShift needs: [build-frontend, build-api] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -137,7 +137,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -158,7 +158,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -180,7 +180,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -199,7 +199,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -222,7 +222,7 @@ jobs: ci-cd-end-notification: if: always() name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [mayan-sync, keycloak-sync] steps: - name: check workflow status @@ -241,7 +241,7 @@ jobs: if: always() name: cleanup builds needs: ci-cd-end-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 diff --git a/.github/workflows/uat_pre_release_hotfix_argo.yml b/.github/workflows/uat_pre_release_hotfix_argo.yml index ead0665cee..a8a3c2a026 100644 --- a/.github/workflows/uat_pre_release_hotfix_argo.yml +++ b/.github/workflows/uat_pre_release_hotfix_argo.yml @@ -38,7 +38,7 @@ on: jobs: ci-cd-start-notification: name: CI-CD Start Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Start notification to Teams Channel uses: dragos-cojocari/ms-teams-notification@v1.0.2 @@ -52,7 +52,7 @@ jobs: create-builds: name: create builds needs: ci-cd-start-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -73,7 +73,7 @@ jobs: build-frontend: name: Build frontend needs: create-builds - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -93,7 +93,7 @@ jobs: build-api: name: Build api needs: create-builds - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -113,7 +113,7 @@ jobs: deploy: name: Deploy to OpenShift needs: [build-frontend, build-api] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -160,7 +160,7 @@ jobs: database-upgrade: name: Upgrade database needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -181,7 +181,7 @@ jobs: mayan-sync: name: sync mayan needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -203,7 +203,7 @@ jobs: keycloak-sync: name: sync keycloak needs: database-upgrade - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -222,7 +222,7 @@ jobs: tag-release-image: name: Release Tag needs: [deploy] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 @@ -245,7 +245,7 @@ jobs: ci-cd-end-notification: if: always() name: CI-CD End Notification to Teams Channel - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [mayan-sync, keycloak-sync] steps: - name: check workflow status @@ -264,7 +264,7 @@ jobs: if: always() name: cleanup builds needs: ci-cd-end-notification - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout Source Code uses: actions/checkout@v4 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 5027fbb558..3596a124d1 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -9,7 +9,7 @@ jobs: bump-version: # this job will only run if the PR has been merged if: github.event.pull_request.merged == true - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/zap-scan.yml b/.github/workflows/zap-scan.yml index efaf794407..b8ff77f9cb 100644 --- a/.github/workflows/zap-scan.yml +++ b/.github/workflows/zap-scan.yml @@ -6,7 +6,7 @@ on: jobs: zap_scan: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: Scan the web application env: ZAP_REPORT: zap-report.xml From dea99afa77baefbf37342de3351650d83be7f01d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 01:53:17 +0000 Subject: [PATCH 34/44] CI: Bump version to v5.7.0-95.15 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 767881689a..a03029781d 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.14 + 5.7.0-95.15 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 4f0b2afc80..2af24e5a04 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.14", + "version": "5.7.0-95.15", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From ab9e7dcf410fea538eb5f592aef190a866c46aa8 Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:49:11 -0800 Subject: [PATCH 35/44] psp-9457 prevent disposed properties from being added to leases. (#4532) --- source/backend/api/Services/LeaseService.cs | 14 +- .../unit/api/Services/LeaseServiceTest.cs | 166 +++++++++++++++++- .../features/leases/add/AddLeaseYupSchema.ts | 14 +- .../SelectedPropertyRow.tsx | 4 + .../src/features/mapSideBar/shared/models.ts | 2 + 5 files changed, 188 insertions(+), 12 deletions(-) diff --git a/source/backend/api/Services/LeaseService.cs b/source/backend/api/Services/LeaseService.cs index 1fba211a92..14d550a1b7 100644 --- a/source/backend/api/Services/LeaseService.cs +++ b/source/backend/api/Services/LeaseService.cs @@ -232,12 +232,6 @@ public PimsLease Update(PimsLease lease, IEnumerable userOverr pimsUser.ThrowInvalidAccessToLeaseFile(lease.RegionCode); var currentFileProperties = _propertyLeaseRepository.GetAllByLeaseId(lease.LeaseId); - var newPropertiesAdded = lease.PimsPropertyLeases.Where(x => !currentFileProperties.Any(y => y.Internal_Id == x.Internal_Id)).ToList(); - - if (newPropertiesAdded.Any(x => x.Property.IsRetired.HasValue && x.Property.IsRetired.Value)) - { - throw new BusinessRuleViolationException("Retired property can not be selected."); - } if (currentLease.LeaseStatusTypeCode != lease.LeaseStatusTypeCode) { @@ -553,8 +547,11 @@ private PimsLease AssociatePropertyLeases(PimsLease lease, IEnumerable p.LeaseId != lease.Internal_Id); var isPropertyOnThisLease = existingPropertyLeases.Any(p => p.LeaseId == lease.Internal_Id); + var isDisposedOrRetired = existingPropertyLeases.Any(p => p.Property.IsRetired.HasValue && p.Property.IsRetired.Value) + || propertyWithAssociations?.PimsDispositionFileProperties?.Any(d => d.DispositionFile.DispositionFileStatusTypeCode == DispositionFileStatusTypes.COMPLETE.ToString()) == true; if (isPropertyOnOtherLease && !isPropertyOnThisLease && !userOverrides.Contains(UserOverrideCode.AddPropertyToInventory)) { @@ -573,6 +570,11 @@ private PimsLease AssociatePropertyLeases(PimsLease lease, IEnumerable x.PopulateNewFileProperty(It.IsAny()), Times.Never); } + [Fact] + public void Add_WithDisposedProperty_Should_Fail() + { + // Arrange + var lease = EntityHelper.CreateLease(1); + lease.RegionCode = 1; + var user = EntityHelper.CreateUser("Test"); + user.PimsRegionUsers.Add(new PimsRegionUser() { RegionCode = lease.RegionCode.Value }); + + PimsProperty newProperty = new PimsProperty() + { + PropertyId = 100, + Pid = 1000, + }; + + PimsProperty disposedProperty = new PimsProperty() + { + PropertyId = 100, + Pid = 1000, + PimsDispositionFileProperties = new List() + { + new PimsDispositionFileProperty() + { + DispositionFile = new PimsDispositionFile() + { + DispositionFileId = 1, + DispositionFileStatusTypeCode = DispositionFileStatusTypes.COMPLETE.ToString(), + }, + }, + }, + }; + + var service = this.CreateLeaseService(Permissions.LeaseAdd); + + var leaseRepository = this._helper.GetService>(); + leaseRepository.Setup(x => x.Add(It.IsAny())).Returns(lease); + + var propertyRepository = this._helper.GetService>(); + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(newProperty); + propertyRepository.Setup(x => x.GetAllAssociationsById(It.IsAny())).Returns(disposedProperty); + + var userRepository = this._helper.GetService>(); + userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(user); + + var propertyService = this._helper.GetService>(); + + // Act + Action act = () => service.Add(lease, new List()); + + // Assert + var ex = act.Should().Throw(); + ex.WithMessage("Disposed or retired properties may not be added to a Lease. Remove any disposed or retired properties before continuing."); + + leaseRepository.Verify(x => x.Add(It.IsAny()), Times.Never); + propertyService.Verify(x => x.PopulateNewFileProperty(It.IsAny()), Times.Never); + } + #endregion #region Properties @@ -464,7 +521,56 @@ public void UpdateProperties_Success() } [Fact] - public void UpdateProperties_WithRetiredProperty_Should_Fail() + public void UpdateProperties_WithDisposedProperty_Should_Fail() + { + // Arrange + var lease = EntityHelper.CreateLease(1); + + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); + var leaseRepository = this._helper.GetService>(); + var propertyLeaseRepository = this._helper.GetService>(); + var propertyRepository = this._helper.GetService>(); + var userRepository = this._helper.GetService>(); + + PimsProperty property = new PimsProperty() + { + PropertyId = 100, + Pid = 1, + }; + + PimsProperty disposedProperty = new PimsProperty() + { + PropertyId = 100, + Pid = 1000, + PimsDispositionFileProperties = new List() + { + new PimsDispositionFileProperty() + { + DispositionFile = new PimsDispositionFile() + { + DispositionFileId = 1, + DispositionFileStatusTypeCode = DispositionFileStatusTypes.COMPLETE.ToString(), + }, + }, + }, + }; + + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(property); + propertyRepository.Setup(x => x.GetAllAssociationsById(It.IsAny())).Returns(disposedProperty); + leaseRepository.Setup(x => x.GetNoTracking(It.IsAny())).Returns(lease); + leaseRepository.Setup(x => x.Get(It.IsAny())).Returns(EntityHelper.CreateLease(1)); + userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); + + // Act + Action act = () => service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); + + // Assert + var ex = act.Should().Throw(); + ex.WithMessage("Disposed or retired properties may not be added to a Lease. Remove any disposed or retired properties before continuing."); + } + + [Fact] + public void UpdateProperties_WithExistingDisposedProperty_Should_Pass() { // Arrange var lease = EntityHelper.CreateLease(1); @@ -479,10 +585,65 @@ public void UpdateProperties_WithRetiredProperty_Should_Fail() { PropertyId = 100, Pid = 1, - IsRetired = true, }; + PimsProperty disposedProperty = new PimsProperty() + { + PropertyId = 100, + Pid = 1000, + PimsDispositionFileProperties = new List() + { + new PimsDispositionFileProperty() + { + DispositionFile = new PimsDispositionFile() + { + DispositionFileId = 1, + DispositionFileStatusTypeCode = DispositionFileStatusTypes.COMPLETE.ToString(), + }, + }, + }, + }; + PimsPropertyLease propertyLease = new PimsPropertyLease() + { + Property = disposedProperty, + LeaseId = lease.LeaseId + }; + + propertyLeaseRepository.Setup(x => x.GetAllByPropertyId(It.IsAny())).Returns(new List() { propertyLease }); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(property); + propertyRepository.Setup(x => x.GetAllAssociationsById(It.IsAny())).Returns(disposedProperty); + leaseRepository.Setup(x => x.GetNoTracking(It.IsAny())).Returns(lease); + leaseRepository.Setup(x => x.Get(It.IsAny())).Returns(EntityHelper.CreateLease(1)); + userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); + + // Act + Action act = () => service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); + + // Assert + var ex = act.Should().NotThrow(); + leaseRepository.Verify(x => x.Update(lease, false), Times.Once); + } + + [Fact] + public void UpdateProperties_WithRetiredProperty_Should_Fail() + { + // Arrange + var lease = EntityHelper.CreateLease(1); + + var service = this.CreateLeaseService(Permissions.LeaseEdit, Permissions.PropertyAdd, Permissions.PropertyView); + var leaseRepository = this._helper.GetService>(); + var propertyLeaseRepository = this._helper.GetService>(); + var propertyRepository = this._helper.GetService>(); + var userRepository = this._helper.GetService>(); + + PimsProperty retiredProperty = new PimsProperty() + { + PropertyId = 100, + Pid = 1, + IsRetired = true, + }; + + propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(retiredProperty); leaseRepository.Setup(x => x.GetNoTracking(It.IsAny())).Returns(lease); leaseRepository.Setup(x => x.Get(It.IsAny())).Returns(EntityHelper.CreateLease(1)); userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); @@ -542,6 +703,7 @@ public void UpdateProperties_MatchProperties_Success_NoInternalId() Lease = lease, Internal_Id = 1, } }; + lease.PimsPropertyLeases = propertyLeases; propertyLeaseRepository.Setup(x => x.GetAllByLeaseId(It.IsAny())).Returns(propertyLeases); propertyRepository.Setup(x => x.GetByPid(It.IsAny(), true)).Returns(lease.PimsPropertyLeases.FirstOrDefault().Property); diff --git a/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts b/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts index cad9b25bbc..7ba438ac1f 100644 --- a/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts +++ b/source/frontend/src/features/leases/add/AddLeaseYupSchema.ts @@ -66,10 +66,16 @@ export const AddLeaseYupSchema = Yup.object().shape({ properties: Yup.array().of( Yup.object().shape({ name: Yup.string().max(250, 'Property name must be at most ${max} characters'), - isRetired: Yup.boolean().notOneOf( - [true], - 'Selected property is retired and can not be added to the file', - ), + property: Yup.object().shape({ + isRetired: Yup.boolean().notOneOf( + [true], + 'Selected property is retired and can not be added to the file', + ), + isDisposed: Yup.boolean().notOneOf( + [true], + 'Selected property is disposed and can not be added to the file', + ), + }), }), ), consultations: Yup.array().of( diff --git a/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx index d175720d2c..115e90d2f7 100644 --- a/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx @@ -66,6 +66,10 @@ export const SelectedPropertyRow: React.FunctionComponent diff --git a/source/frontend/src/features/mapSideBar/shared/models.ts b/source/frontend/src/features/mapSideBar/shared/models.ts index 4e36a0d369..fafebf9e2a 100644 --- a/source/frontend/src/features/mapSideBar/shared/models.ts +++ b/source/frontend/src/features/mapSideBar/shared/models.ts @@ -92,6 +92,7 @@ export class PropertyForm { public landArea?: number; public areaUnit?: AreaUnitTypes; public isRetired?: boolean; + public isDisposed?: boolean; public constructor(baseModel?: Partial) { Object.assign(this, baseModel); @@ -152,6 +153,7 @@ export class PropertyForm { ? enumFromValue(model?.pimsFeature?.properties?.PROPERTY_AREA_UNIT_TYPE_CODE, AreaUnitTypes) : AreaUnitTypes.SquareMeters, isRetired: model?.pimsFeature?.properties?.IS_RETIRED ?? false, + isDisposed: model?.pimsFeature?.properties?.IS_DISPOSED ?? false, legalDescription: model?.pimsFeature?.properties?.LAND_LEGAL_DESCRIPTION ?? model?.parcelFeature?.properties?.LEGAL_DESCRIPTION ?? From 8efee0ee84875bc58e1084f8ec4ccb55fbfeceb0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 05:49:37 +0000 Subject: [PATCH 36/44] CI: Bump version to v5.7.0-95.16 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index a03029781d..7b63f5073c 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.15 + 5.7.0-95.16 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 2af24e5a04..f72ef24647 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.15", + "version": "5.7.0-95.16", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 8ca90a8881d87fc8b4c5ad7b9b4e6e662ae029b9 Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:54:17 -0800 Subject: [PATCH 37/44] disposition override corrections - ensure save redirect functions. (#4536) Co-authored-by: Alejandro Sanchez --- .../mapSideBar/disposition/add/AddDispositionContainer.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/disposition/add/AddDispositionContainer.tsx b/source/frontend/src/features/mapSideBar/disposition/add/AddDispositionContainer.tsx index bd8bccf360..49d58d5f6a 100644 --- a/source/frontend/src/features/mapSideBar/disposition/add/AddDispositionContainer.tsx +++ b/source/frontend/src/features/mapSideBar/disposition/add/AddDispositionContainer.tsx @@ -159,6 +159,7 @@ const AddDispositionContainer: React.FC = ({ userOverrideCodes: UserOverrideCode[], ) => { try { + formikHelpers.setSubmitting(true); const dispositionFile = values.toApi(); const response = await addDispositionFileApi(dispositionFile, userOverrideCodes); @@ -183,7 +184,7 @@ const AddDispositionContainer: React.FC = ({ onSubmit={( values: DispositionFormModel, formikHelpers: FormikHelpers, - ) => + ) => { withUserOverride( (userOverrideCodes: UserOverrideCode[]) => handleSubmit(values, formikHelpers, userOverrideCodes), @@ -197,8 +198,8 @@ const AddDispositionContainer: React.FC = ({ }); setDisplayModal(true); }, - ) - } + ); + }} > ); }; From 56b6c5ea2e7f520d299ae0ab836e0dc5e8253e5b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:54:40 +0000 Subject: [PATCH 38/44] CI: Bump version to v5.7.0-95.17 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 7b63f5073c..eed067dfd3 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.16 + 5.7.0-95.17 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index f72ef24647..50399c9c6e 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.16", + "version": "5.7.0-95.17", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 2aa9ddbebadc61fbe010a760f9c5633c39246f98 Mon Sep 17 00:00:00 2001 From: devinleighsmith <41091511+devinleighsmith@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:47:03 -0800 Subject: [PATCH 39/44] psp-9530 allow research files to add retired properties. (#4534) * psp-9530 allow research files to add retired properties. * fix test compilation error. --------- Co-authored-by: Alejandro Sanchez --- .../backend/api/Services/IPropertyService.cs | 2 +- .../backend/api/Services/PropertyService.cs | 8 +++---- .../api/Services/ResearchFileService.cs | 4 ++-- .../Interfaces/IPropertyRepository.cs | 2 +- .../dal/Repositories/PropertyRepository.cs | 4 ++-- .../Services/AcquisitionFileServiceTest.cs | 8 +++---- .../Services/DispositionFileServiceTest.cs | 6 ++--- .../unit/api/Services/LeaseServiceTest.cs | 10 ++++---- .../unit/api/Services/PropertyServiceTest.cs | 24 +++++++++---------- .../api/Services/ResearchFileServiceTest.cs | 8 +++---- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/source/backend/api/Services/IPropertyService.cs b/source/backend/api/Services/IPropertyService.cs index 18a52c564b..cd7c81ab22 100644 --- a/source/backend/api/Services/IPropertyService.cs +++ b/source/backend/api/Services/IPropertyService.cs @@ -45,7 +45,7 @@ public interface IPropertyService PimsProperty PopulateNewProperty(PimsProperty property, bool isOwned = false, bool isPropertyOfInterest = true); - void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty propertyToUpdate, IEnumerable overrideCodes); + void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty propertyToUpdate, IEnumerable overrideCodes, bool allowRetired = false); T PopulateNewFileProperty(T fileProperty) where T : IFilePropertyEntity; diff --git a/source/backend/api/Services/PropertyService.cs b/source/backend/api/Services/PropertyService.cs index 863b540e0a..6028afcd99 100644 --- a/source/backend/api/Services/PropertyService.cs +++ b/source/backend/api/Services/PropertyService.cs @@ -6,17 +6,17 @@ using Microsoft.Extensions.Logging; using NetTopologySuite.Geometries; using Pims.Api.Constants; -using Pims.Core.Api.Exceptions; using Pims.Api.Models.CodeTypes; using Pims.Api.Models.Concepts.Property; +using Pims.Core.Api.Exceptions; using Pims.Core.Exceptions; using Pims.Core.Extensions; +using Pims.Core.Security; using Pims.Dal.Entities; using Pims.Dal.Exceptions; using Pims.Dal.Helpers; using Pims.Dal.Helpers.Extensions; using Pims.Dal.Repositories; -using Pims.Core.Security; namespace Pims.Api.Services { @@ -387,7 +387,7 @@ public PimsProperty PopulateNewProperty(PimsProperty property, bool isOwned = fa return property; } - public void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty propertyToUpdate, IEnumerable overrideCodes) + public void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty propertyToUpdate, IEnumerable overrideCodes, bool allowRetired = false) { if (propertyToUpdate.Location == null || propertyToUpdate.Boundary == null) { @@ -415,7 +415,7 @@ public void UpdateLocation(PimsProperty incomingProperty, ref PimsProperty prope if (needsUpdate) { - _propertyRepository.Update(propertyToUpdate, overrideLocation: true); + _propertyRepository.Update(propertyToUpdate, overrideLocation: true, allowRetired: allowRetired); } } else diff --git a/source/backend/api/Services/ResearchFileService.cs b/source/backend/api/Services/ResearchFileService.cs index 988a467b9f..f28025f757 100644 --- a/source/backend/api/Services/ResearchFileService.cs +++ b/source/backend/api/Services/ResearchFileService.cs @@ -209,7 +209,7 @@ private void MatchProperties(PimsResearchFile researchFile, IEnumerable long GetAllAssociationsCountById(long id); - PimsProperty Update(PimsProperty property, bool overrideLocation = false); + PimsProperty Update(PimsProperty property, bool overrideLocation = false, bool allowRetired = false); PimsProperty UpdatePropertyManagement(PimsProperty property); diff --git a/source/backend/dal/Repositories/PropertyRepository.cs b/source/backend/dal/Repositories/PropertyRepository.cs index 4b75be1251..50ec958dcb 100644 --- a/source/backend/dal/Repositories/PropertyRepository.cs +++ b/source/backend/dal/Repositories/PropertyRepository.cs @@ -310,7 +310,7 @@ public long GetAllAssociationsCountById(long id) /// The property to update. /// Whether to update the property spatial location with the incoming value. Defaults to false. /// The updated property. - public PimsProperty Update(PimsProperty property, bool overrideLocation = false) + public PimsProperty Update(PimsProperty property, bool overrideLocation = false, bool allowRetired = false) { property.ThrowIfNull(nameof(property)); @@ -320,7 +320,7 @@ public PimsProperty Update(PimsProperty property, bool overrideLocation = false) .FirstOrDefault(p => p.PropertyId == propertyId) ?? throw new KeyNotFoundException(); // prevent editing on retired properties - if (existingProperty.IsRetired.HasValue && existingProperty.IsRetired.Value) + if (existingProperty.IsRetired.HasValue && existingProperty.IsRetired.Value && !allowRetired) { throw new BusinessRuleViolationException("Retired records are referenced for historical purposes only and cannot be edited or deleted."); } diff --git a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs index bea06de2d5..5369e25725 100644 --- a/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/AcquisitionFileServiceTest.cs @@ -1369,7 +1369,7 @@ public void UpdateProperties_MatchProperties_Success() userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); var solver = this._helper.GetService>(); solver.Setup(x => x.CanEditProperties(It.IsAny())).Returns(true); @@ -1380,7 +1380,7 @@ public void UpdateProperties_MatchProperties_Success() // Assert filePropertyRepository.Verify(x => x.GetPropertiesByAcquisitionFileId(It.IsAny()), Times.Once); filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false), Times.Once); propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } @@ -1412,7 +1412,7 @@ public void UpdateProperties_MatchProperties_Success_NoInternalId() userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); var solver = this._helper.GetService>(); solver.Setup(x => x.CanEditProperties(It.IsAny())).Returns(true); @@ -1888,7 +1888,7 @@ public void UpdateProperties_WithProperty_SelectedForCompensation_Should_Fail() userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); var solver = this._helper.GetService>(); solver.Setup(x => x.CanEditProperties(It.IsAny())).Returns(true); diff --git a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs index f57b446a39..3672d5bf98 100644 --- a/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/DispositionFileServiceTest.cs @@ -988,7 +988,7 @@ public void UpdateProperties_MatchProperties_Success() userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act @@ -997,7 +997,7 @@ public void UpdateProperties_MatchProperties_Success() // Assert filePropertyRepository.Verify(x => x.GetPropertiesByDispositionFileId(It.IsAny()), Times.Once); filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false), Times.Once); propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } @@ -1029,7 +1029,7 @@ public void UpdateProperties_MatchProperties_Success_NoInternalId() userRepository.Setup(x => x.GetUserInfoByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser(1, Guid.NewGuid(), "Test", regionCode: 1)); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act diff --git a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs index 14f43e1e26..fccfdbaf4e 100644 --- a/source/backend/tests/unit/api/Services/LeaseServiceTest.cs +++ b/source/backend/tests/unit/api/Services/LeaseServiceTest.cs @@ -510,14 +510,14 @@ public void UpdateProperties_Success() userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); // Act var updatedLease = service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); // Assert leaseRepository.Verify(x => x.Update(lease, false), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false), Times.Once); } [Fact] @@ -674,14 +674,14 @@ public void UpdateProperties_MatchProperties_Success() userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); // Act var updatedLease = service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); // Assert leaseRepository.Verify(x => x.Update(lease, false), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false), Times.Once); } [Fact] @@ -711,7 +711,7 @@ public void UpdateProperties_MatchProperties_Success_NoInternalId() userRepository.Setup(x => x.GetByKeycloakUserId(It.IsAny())).Returns(EntityHelper.CreateUser("Test")); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), false)); // Act var updatedLease = service.Update(lease, new List() { UserOverrideCode.AddLocationToProperty }); diff --git a/source/backend/tests/unit/api/Services/PropertyServiceTest.cs b/source/backend/tests/unit/api/Services/PropertyServiceTest.cs index 3535ce0750..2de98b1cb1 100644 --- a/source/backend/tests/unit/api/Services/PropertyServiceTest.cs +++ b/source/backend/tests/unit/api/Services/PropertyServiceTest.cs @@ -119,7 +119,7 @@ public void Update_Property_No_Reprojection_Success() var service = this.CreatePropertyServiceWithPermissions(Permissions.PropertyView, Permissions.PropertyEdit); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(property); + repository.Setup(x => x.Update(It.IsAny(), It.IsAny(), false)).Returns(property); repository.Setup(x => x.GetById(It.IsAny())).Returns(property); var coordinateService = this._helper.GetService>(); coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())); @@ -133,7 +133,7 @@ public void Update_Property_No_Reprojection_Success() var updatedProperty = service.Update(newValues); // Assert - repository.Verify(x => x.Update(It.IsAny(), It.IsAny()), Times.Once); + repository.Verify(x => x.Update(It.IsAny(), It.IsAny(), false), Times.Once); coordinateService.Verify(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())); } @@ -145,7 +145,7 @@ public void Update_Property_With_Reprojection_Success() var service = this.CreatePropertyServiceWithPermissions(Permissions.PropertyView, Permissions.PropertyEdit); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(property); + repository.Setup(x => x.Update(It.IsAny(), It.IsAny(), false)).Returns(property); repository.Setup(x => x.GetById(It.IsAny())).Returns(property); var projected = new Coordinate(14000, 9200); @@ -162,7 +162,7 @@ public void Update_Property_With_Reprojection_Success() // Assert coordinateService.Verify(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())); - repository.Verify(x => x.Update(It.Is(p => p.Location.Coordinate.Equals(projected)), It.IsAny()), Times.Once); + repository.Verify(x => x.Update(It.Is(p => p.Location.Coordinate.Equals(projected)), It.IsAny(), false), Times.Once); } [Fact] @@ -175,7 +175,7 @@ public void Update_Property_KeyNotFound() var property = EntityHelper.CreateProperty(1); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(property, It.IsAny())).Throws(); + repository.Setup(x => x.Update(property, It.IsAny(), false)).Throws(); repository.Setup(x => x.GetById(It.IsAny())).Throws(); // Assert @@ -193,7 +193,7 @@ public void Update_Property_NoPermission() // Assert Assert.Throws(() => service.Update(property)); - repository.Verify(x => x.Update(It.IsAny(), It.IsAny()), Times.Never); + repository.Verify(x => x.Update(It.IsAny(), It.IsAny(), false), Times.Never); } #endregion @@ -208,7 +208,7 @@ public void UpdateLocation_Requires_UserOverride() var service = this.CreatePropertyServiceWithPermissions(Permissions.PropertyView, Permissions.PropertyEdit); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(property); + repository.Setup(x => x.Update(It.IsAny(), It.IsAny(), false)).Returns(property); var coordinateService = this._helper.GetService>(); coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new Coordinate(14000, 9200)); @@ -225,7 +225,7 @@ public void UpdateLocation_Requires_UserOverride() ex.Which.UserOverride.Should().Be(UserOverrideCode.AddLocationToProperty); coordinateService.Verify(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - repository.Verify(x => x.Update(It.IsAny(), It.IsAny()), Times.Never); + repository.Verify(x => x.Update(It.IsAny(), It.IsAny(), false), Times.Never); } [Fact] @@ -237,7 +237,7 @@ public void UpdateLocation_Success() var service = this.CreatePropertyServiceWithPermissions(Permissions.PropertyView, Permissions.PropertyEdit); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(property); + repository.Setup(x => x.Update(It.IsAny(), It.IsAny(), false)).Returns(property); var coordinateService = this._helper.GetService>(); coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())) @@ -252,7 +252,7 @@ public void UpdateLocation_Success() // Assert coordinateService.Verify(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - repository.Verify(x => x.Update(It.Is(p => p.Location.Coordinate.Equals(new Coordinate(14000, 9200))), It.IsAny()), Times.Once); + repository.Verify(x => x.Update(It.Is(p => p.Location.Coordinate.Equals(new Coordinate(14000, 9200))), It.IsAny(), false), Times.Once); } [Fact] @@ -265,7 +265,7 @@ public void UpdateLocation_Boundary_Success() var service = this.CreatePropertyServiceWithPermissions(Permissions.PropertyView, Permissions.PropertyEdit); var repository = this._helper.GetService>(); - repository.Setup(x => x.Update(It.IsAny(), It.IsAny())).Returns(property); + repository.Setup(x => x.Update(It.IsAny(), It.IsAny(), false)).Returns(property); var coordinateService = this._helper.GetService>(); coordinateService.Setup(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny())) @@ -291,7 +291,7 @@ public void UpdateLocation_Boundary_Success() // Assert coordinateService.Verify(x => x.TransformCoordinates(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); coordinateService.Verify(x => x.TransformGeometry(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - repository.Verify(x => x.Update(property, true), Times.Once); + repository.Verify(x => x.Update(property, true, false), Times.Once); property.Location.Coordinate.Should().Be(new Coordinate(14000, 9200)); property.Boundary.Should().BeOfType(); var updatedBoundary = property.Boundary as Polygon; diff --git a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs index a6267ad0a2..5ea2997992 100644 --- a/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs +++ b/source/backend/tests/unit/api/Services/ResearchFileServiceTest.cs @@ -194,7 +194,7 @@ public void UpdateProperties_MatchProperties_PID_Success() filePropertyRepository.Setup(x => x.GetAllByResearchFileId(It.IsAny())).Returns(researchFile.PimsPropertyResearchFiles.ToList()); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), true)); propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act @@ -202,7 +202,7 @@ public void UpdateProperties_MatchProperties_PID_Success() // Assert filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), true), Times.Once); propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } @@ -230,7 +230,7 @@ public void UpdateProperties_MatchProperties_PIN_Success() filePropertyRepository.Setup(x => x.GetAllByResearchFileId(It.IsAny())).Returns(researchFile.PimsPropertyResearchFiles.ToList()); var propertyService = this._helper.GetService>(); - propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>())); + propertyService.Setup(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), true)); propertyService.Setup(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny())); // Act @@ -238,7 +238,7 @@ public void UpdateProperties_MatchProperties_PIN_Success() // Assert filePropertyRepository.Verify(x => x.Update(It.IsAny()), Times.Once); - propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>()), Times.Once); + propertyService.Verify(x => x.UpdateLocation(It.IsAny(), ref It.Ref.IsAny, It.IsAny>(), true), Times.Once); propertyService.Verify(x => x.UpdateFilePropertyLocation(It.IsAny(), It.IsAny()), Times.Once); } From 769c835e967cc5ffff3404667fd6fdbaf0dcdd35 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:47:29 +0000 Subject: [PATCH 40/44] CI: Bump version to v5.7.0-95.18 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index eed067dfd3..5c4262e2de 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.17 + 5.7.0-95.18 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 50399c9c6e..6dafa62888 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.17", + "version": "5.7.0-95.18", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 491b29b1e2ed3051721f31859b569855a5c42885 Mon Sep 17 00:00:00 2001 From: Manuel Rodriguez Date: Wed, 18 Dec 2024 17:00:13 -0800 Subject: [PATCH 41/44] PSP-8573 | Updated list headers (#4531) * Updated list headers * Added icons * Fixed Typos * lint fixes --------- Co-authored-by: Alejandro Sanchez --- .../acquisition/list/AcquisitionListView.tsx | 7 ++- .../admin/access/ManageAccessRequestsPage.tsx | 11 ++-- .../ManageAccessRequestsPage.test.tsx.snap | 53 +++++++++++++++--- .../DocumentTemplateManagementView.tsx | 12 ++-- ...tTemplateManagementContainer.test.tsx.snap | 55 ++++++++++++++++--- .../list/FinancialCodeListView.tsx | 7 ++- .../features/admin/users/ManageUsersPage.tsx | 10 ++-- .../ManageUsersPage.test.tsx.snap | 53 +++++++++++++++--- .../contacts/list/ContactListPage.tsx | 11 ++-- .../ContactListView.test.tsx.snap | 36 +++++++++--- .../disposition/list/DispositionListView.tsx | 7 ++- .../DispositionListView.test.tsx.snap | 44 ++++++++++++--- .../features/leases/list/LeaseListView.tsx | 7 ++- .../__snapshots__/LeaseListView.test.tsx.snap | 36 +++++++++--- ...CompensationRequisitionDetailView.test.tsx | 8 +-- .../projects/list/ProjectListView.tsx | 8 ++- .../ProjectListView.test.tsx.snap | 41 +++++++++++--- .../research/list/ResearchListView.tsx | 7 ++- .../ResearchListView.test.tsx.snap | 44 ++++++++++++--- 19 files changed, 369 insertions(+), 88 deletions(-) diff --git a/source/frontend/src/features/acquisition/list/AcquisitionListView.tsx b/source/frontend/src/features/acquisition/list/AcquisitionListView.tsx index 85716defd9..1576349df6 100644 --- a/source/frontend/src/features/acquisition/list/AcquisitionListView.tsx +++ b/source/frontend/src/features/acquisition/list/AcquisitionListView.tsx @@ -5,8 +5,10 @@ import { FaFileExcel, FaPlus } from 'react-icons/fa'; import { useHistory } from 'react-router'; import { toast } from 'react-toastify'; +import AcquisitionFileIcon from '@/assets/images/acquisition-icon.svg?react'; import { StyledIconButton } from '@/components/common/buttons/IconButton'; import { StyledAddButton } from '@/components/common/styles'; +import * as CommonStyled from '@/components/common/styles'; import TooltipWrapper from '@/components/common/TooltipWrapper'; import Claims from '@/constants/claims'; import { useApiAcquisitionFile } from '@/hooks/pims-api/useApiAcquisitionFile'; @@ -95,7 +97,10 @@ export const AcquisitionListView: React.FunctionComponent< return ( - Acquisition Files + + + Acquisition Files + diff --git a/source/frontend/src/features/admin/access/ManageAccessRequestsPage.tsx b/source/frontend/src/features/admin/access/ManageAccessRequestsPage.tsx index a4ba79cd55..47adb558fb 100644 --- a/source/frontend/src/features/admin/access/ManageAccessRequestsPage.tsx +++ b/source/frontend/src/features/admin/access/ManageAccessRequestsPage.tsx @@ -2,6 +2,8 @@ import { useCallback, useEffect } from 'react'; import { toast } from 'react-toastify'; import styled from 'styled-components'; +import AdminIcon from '@/assets/images/admin-icon.svg?react'; +import * as CommonStyled from '@/components/common/styles'; import { Table } from '@/components/Table'; import { useApiAccessRequests } from '@/hooks/pims-api/useApiAccessRequests'; import { useSearch } from '@/hooks/useSearch'; @@ -48,7 +50,10 @@ const ManageAccessRequestsPage = () => { return ( - PIMS User Access Requests + + + PIMS User Access Requests + setFilter(accessRequestfilter)} @@ -81,8 +86,4 @@ const StyledContainer = styled(ScrollXYContainer)` height: 100%; `; -const StyledPageHeader = styled.h3` - text-align: left; -`; - export default ManageAccessRequestsPage; diff --git a/source/frontend/src/features/admin/access/__snapshots__/ManageAccessRequestsPage.test.tsx.snap b/source/frontend/src/features/admin/access/__snapshots__/ManageAccessRequestsPage.test.tsx.snap index cba7b19710..6ce04b1583 100644 --- a/source/frontend/src/features/admin/access/__snapshots__/ManageAccessRequestsPage.test.tsx.snap +++ b/source/frontend/src/features/admin/access/__snapshots__/ManageAccessRequestsPage.test.tsx.snap @@ -231,6 +231,16 @@ exports[`Manage access requests > Snapshot matches 1`] = ` background: white; } +.c2 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c0 { padding: 1.6rem 3.2rem; -webkit-box-flex: 1; @@ -251,18 +261,47 @@ exports[`Manage access requests > Snapshot matches 1`] = ` height: 100%; } -.c2 { - text-align: left; -} -
-

- PIMS User Access Requests -

+ + + + + + + + + + + + + PIMS User Access Requests + +
diff --git a/source/frontend/src/features/admin/document-template/DocumentTemplateManagementView.tsx b/source/frontend/src/features/admin/document-template/DocumentTemplateManagementView.tsx index a2c8545be8..6d1d1058ce 100644 --- a/source/frontend/src/features/admin/document-template/DocumentTemplateManagementView.tsx +++ b/source/frontend/src/features/admin/document-template/DocumentTemplateManagementView.tsx @@ -2,9 +2,11 @@ import { Col, Row } from 'react-bootstrap'; import Form from 'react-bootstrap/Form'; import styled from 'styled-components'; +import AdminIcon from '@/assets/images/admin-icon.svg?react'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; import { Scrollable as ScrollableBase } from '@/components/common/Scrollable/Scrollable'; import { Section } from '@/components/common/Section/Section'; +import * as CommonStyled from '@/components/common/styles'; import DocumentListContainer from '@/features/documents/list/DocumentListContainer'; import { ApiGen_CodeTypes_DocumentRelationType } from '@/models/api/generated/ApiGen_CodeTypes_DocumentRelationType'; import { ApiGen_Concepts_FormDocumentType } from '@/models/api/generated/ApiGen_Concepts_FormDocumentType'; @@ -28,8 +30,10 @@ export const DocumentTemplateManagementView: React.FunctionComponent< - - PIMS Document Template Management + + + PIMS Document Template Management +
Form Type: @@ -66,10 +70,6 @@ export const DocumentTemplateManagementView: React.FunctionComponent< export default DocumentTemplateManagementView; -const StyledPageHeader = styled.h3` - text-align: left; -`; - export const ListPage = styled.div` display: flex; flex-direction: column; diff --git a/source/frontend/src/features/admin/document-template/__snapshots__/DocumentTemplateManagementContainer.test.tsx.snap b/source/frontend/src/features/admin/document-template/__snapshots__/DocumentTemplateManagementContainer.test.tsx.snap index aa362a4775..3c1612fd1a 100644 --- a/source/frontend/src/features/admin/document-template/__snapshots__/DocumentTemplateManagementContainer.test.tsx.snap +++ b/source/frontend/src/features/admin/document-template/__snapshots__/DocumentTemplateManagementContainer.test.tsx.snap @@ -6,7 +6,17 @@ exports[`DocumentTemplateManagementContainer component > matches snapshot 1`] = class="Toastify" />
- .c1 { + .c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + +.c1 { width: 100%; -webkit-box-flex: 1; -webkit-flex-grow: 1; @@ -23,10 +33,6 @@ exports[`DocumentTemplateManagementContainer component > matches snapshot 1`] = border-radius: 0.5rem; } -.c3 { - text-align: left; -} - .c0 { display: -webkit-box; display: -webkit-flex; @@ -55,11 +61,44 @@ exports[`DocumentTemplateManagementContainer component > matches snapshot 1`] =
-

- PIMS Document Template Management -

+ + + + + + + + + + + + + PIMS Document Template Management + +
diff --git a/source/frontend/src/features/admin/financial-codes/list/FinancialCodeListView.tsx b/source/frontend/src/features/admin/financial-codes/list/FinancialCodeListView.tsx index 271f7b9907..ed1689a7ce 100644 --- a/source/frontend/src/features/admin/financial-codes/list/FinancialCodeListView.tsx +++ b/source/frontend/src/features/admin/financial-codes/list/FinancialCodeListView.tsx @@ -4,6 +4,8 @@ import { Col, Row } from 'react-bootstrap'; import { FaPlus } from 'react-icons/fa'; import { useHistory } from 'react-router'; +import AdminIcon from '@/assets/images/admin-icon.svg?react'; +import * as CommonStyled from '@/components/common/styles'; import { StyledAddButton } from '@/components/common/styles'; import { TableSort } from '@/components/Table/TableSort'; import { Roles } from '@/constants/roles'; @@ -97,7 +99,10 @@ export const FinancialCodeListView: React.FC = () => { return ( - Financial Codes + + + Financial Codes + diff --git a/source/frontend/src/features/admin/users/ManageUsersPage.tsx b/source/frontend/src/features/admin/users/ManageUsersPage.tsx index 265d702e13..7e4d88f9b9 100644 --- a/source/frontend/src/features/admin/users/ManageUsersPage.tsx +++ b/source/frontend/src/features/admin/users/ManageUsersPage.tsx @@ -5,7 +5,9 @@ import { Col, Row } from 'react-bootstrap'; import { FaFileExcel } from 'react-icons/fa'; import styled from 'styled-components'; +import AdminIcon from '@/assets/images/admin-icon.svg?react'; import { StyledIconButton } from '@/components/common/buttons'; +import * as CommonStyled from '@/components/common/styles'; import TooltipWrapper from '@/components/common/TooltipWrapper'; import { Table } from '@/components/Table'; import { useApiUsers } from '@/hooks/pims-api/useApiUsers'; @@ -75,7 +77,10 @@ export const ManageUsersPage = () => { return ( - User Management + + + User Management + @@ -117,7 +122,4 @@ const StyledPage = styled(Styled.ListView)` padding: 1rem 2rem; `; -const StyledPageHeader = styled.h3` - text-align: left; -`; export default ManageUsersPage; diff --git a/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap b/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap index c94c04c413..6288c47f75 100644 --- a/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap +++ b/source/frontend/src/features/admin/users/__snapshots__/ManageUsersPage.test.tsx.snap @@ -307,6 +307,16 @@ exports[`Manage Users Component > Snapshot matches 1`] = ` background: white; } +.c2 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c3 { background-color: #d9eaf7; border-radius: 0.5rem; @@ -346,18 +356,47 @@ exports[`Manage Users Component > Snapshot matches 1`] = ` padding: 1rem 2rem; } -.c2 { - text-align: left; -} -
-

- User Management -

+ + + + + + + + + + + + + User Management + +
diff --git a/source/frontend/src/features/contacts/list/ContactListPage.tsx b/source/frontend/src/features/contacts/list/ContactListPage.tsx index 9bf7c25e5d..154c755935 100644 --- a/source/frontend/src/features/contacts/list/ContactListPage.tsx +++ b/source/frontend/src/features/contacts/list/ContactListPage.tsx @@ -1,6 +1,8 @@ import styled from 'styled-components'; +import ContactIcon from '@/assets/images/contact-icon.svg?react'; import { Scrollable as ScrollableBase } from '@/components/common/Scrollable/Scrollable'; +import * as CommonStyled from '@/components/common/styles'; import ContactManagerView from '@/components/contact/ContactManagerView/ContactManagerView'; /** @@ -10,7 +12,10 @@ export const ContactListPage = () => { return ( - Contacts + + + Contacts + @@ -31,7 +36,3 @@ const StyledScrollable = styled(ScrollableBase)` padding: 1.6rem 3.2rem; width: 100%; `; - -const StyledPageHeader = styled.h3` - text-align: left; -`; diff --git a/source/frontend/src/features/contacts/list/__snapshots__/ContactListView.test.tsx.snap b/source/frontend/src/features/contacts/list/__snapshots__/ContactListView.test.tsx.snap index 78669bc126..4e065a7d53 100644 --- a/source/frontend/src/features/contacts/list/__snapshots__/ContactListView.test.tsx.snap +++ b/source/frontend/src/features/contacts/list/__snapshots__/ContactListView.test.tsx.snap @@ -343,6 +343,16 @@ exports[`Contact List View > matches snapshot 1`] = ` flex-wrap: nowrap; } +.c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c5.form-group { display: -webkit-box; display: -webkit-flex; @@ -478,10 +488,6 @@ exports[`Contact List View > matches snapshot 1`] = ` width: 100%; } -.c3 { - text-align: left; -} - @media only screen and (max-width:1199px) { .c4 { max-width: 50%; @@ -494,11 +500,27 @@ exports[`Contact List View > matches snapshot 1`] = `
-

- Contacts -

+ + + + + Contacts + +
= () => { return ( - Disposition Files + + + Disposition Files + diff --git a/source/frontend/src/features/disposition/list/__snapshots__/DispositionListView.test.tsx.snap b/source/frontend/src/features/disposition/list/__snapshots__/DispositionListView.test.tsx.snap index 13fcf4683e..7c6484df48 100644 --- a/source/frontend/src/features/disposition/list/__snapshots__/DispositionListView.test.tsx.snap +++ b/source/frontend/src/features/disposition/list/__snapshots__/DispositionListView.test.tsx.snap @@ -329,6 +329,16 @@ exports[`Disposition List View > matches snapshot 1`] = ` font-size: 2.4rem; } +.c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c5 { background-color: #d9eaf7; border-radius: 0.5rem; @@ -365,10 +375,6 @@ exports[`Disposition List View > matches snapshot 1`] = ` width: 100%; } -.c3 { - text-align: left; -} - .c4 { -webkit-align-items: center; -webkit-box-align: center; @@ -388,11 +394,35 @@ exports[`Disposition List View > matches snapshot 1`] = `
-

- Disposition Files -

+ + + Disposition file Icon + + + + + + Disposition Files + +
diff --git a/source/frontend/src/features/leases/list/LeaseListView.tsx b/source/frontend/src/features/leases/list/LeaseListView.tsx index e3673ec3ba..88b7f64d86 100644 --- a/source/frontend/src/features/leases/list/LeaseListView.tsx +++ b/source/frontend/src/features/leases/list/LeaseListView.tsx @@ -5,7 +5,9 @@ import { FaFileAlt, FaFileExcel, FaPlus } from 'react-icons/fa'; import { useHistory } from 'react-router'; import { toast } from 'react-toastify'; +import LeaseIcon from '@/assets/images/lease-icon.svg?react'; import { StyledIconButton } from '@/components/common/buttons'; +import * as CommonStyled from '@/components/common/styles'; import { StyledAddButton } from '@/components/common/styles'; import TooltipWrapper from '@/components/common/TooltipWrapper'; import Claims from '@/constants/claims'; @@ -80,7 +82,10 @@ export const LeaseListView: React.FunctionComponent - Leases & Licences + + + Leases & Licences + diff --git a/source/frontend/src/features/leases/list/__snapshots__/LeaseListView.test.tsx.snap b/source/frontend/src/features/leases/list/__snapshots__/LeaseListView.test.tsx.snap index f4cbb7b726..ee36b0f0a5 100644 --- a/source/frontend/src/features/leases/list/__snapshots__/LeaseListView.test.tsx.snap +++ b/source/frontend/src/features/leases/list/__snapshots__/LeaseListView.test.tsx.snap @@ -334,6 +334,16 @@ exports[`Lease and License List View > matches snapshot 1`] = ` height: 1.6rem; } +.c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c5 { background-color: #d9eaf7; border-radius: 0.5rem; @@ -385,10 +395,6 @@ exports[`Lease and License List View > matches snapshot 1`] = ` width: 100%; } -.c3 { - text-align: left; -} - .c4 { -webkit-align-items: center; -webkit-box-align: center; @@ -404,11 +410,27 @@ exports[`Lease and License List View > matches snapshot 1`] = `
-

- Leases & Licences -

+ + + + + Leases & Licences + +
diff --git a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx index 8a754a40d6..089b9c6728 100644 --- a/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx +++ b/source/frontend/src/features/mapSideBar/compensation/detail/CompensationRequisitionDetailView.test.tsx @@ -128,14 +128,14 @@ describe('Compensation Detail View Component', () => { }); it('Edit Compensation Button not displayed without claims when is in "Draft" status', async () => { - const acquistionFile = mockAcquisitionFileResponse(); + const acquisition = mockAcquisitionFileResponse(); const mockFinalCompensation = getMockApiDefaultCompensation(); const { queryByTitle } = await setup({ claims: [Claims.COMPENSATION_REQUISITION_VIEW], props: { file: { - ...acquistionFile, + ...acquisition, fileStatusTypeCode: toTypeCodeNullable(ApiGen_CodeTypes_AcquisitionStatusTypes.ACTIVE), }, compensation: { ...mockFinalCompensation, isDraft: true }, @@ -165,13 +165,13 @@ describe('Compensation Detail View Component', () => { }); it('User does not have the option to Edit Compensation when is in "FINAL" status', async () => { - const acquistionFile = mockAcquisitionFileResponse(); + const acquisition = mockAcquisitionFileResponse(); const mockFinalCompensation = getMockApiDefaultCompensation(); const { queryByTitle, getByTestId } = await setup({ claims: [Claims.COMPENSATION_REQUISITION_EDIT], props: { file: { - ...acquistionFile, + ...acquisition, fileStatusTypeCode: toTypeCodeNullable(ApiGen_CodeTypes_AcquisitionStatusTypes.COMPLT), }, compensation: { ...mockFinalCompensation, isDraft: false }, diff --git a/source/frontend/src/features/projects/list/ProjectListView.tsx b/source/frontend/src/features/projects/list/ProjectListView.tsx index 4af3fed367..a33fdbcb92 100644 --- a/source/frontend/src/features/projects/list/ProjectListView.tsx +++ b/source/frontend/src/features/projects/list/ProjectListView.tsx @@ -1,8 +1,9 @@ import { useCallback } from 'react'; import { Col, Row } from 'react-bootstrap'; -import { FaPlus } from 'react-icons/fa'; +import { FaBriefcase, FaPlus } from 'react-icons/fa'; import { useHistory } from 'react-router-dom'; +import * as CommonStyled from '@/components/common/styles'; import { StyledAddButton } from '@/components/common/styles'; import { Claims } from '@/constants/claims'; import { useApiProjects } from '@/hooks/pims-api/useApiProjects'; @@ -54,7 +55,10 @@ export const ProjectListView: React.FunctionComponent - Projects + + + Projects + diff --git a/source/frontend/src/features/projects/list/__snapshots__/ProjectListView.test.tsx.snap b/source/frontend/src/features/projects/list/__snapshots__/ProjectListView.test.tsx.snap index b402766ec5..15c65b1e6d 100644 --- a/source/frontend/src/features/projects/list/__snapshots__/ProjectListView.test.tsx.snap +++ b/source/frontend/src/features/projects/list/__snapshots__/ProjectListView.test.tsx.snap @@ -243,6 +243,16 @@ exports[`Project List View > matches snapshot 1`] = ` height: 1.6rem; } +.c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c5 { background-color: #d9eaf7; border-radius: 0.5rem; @@ -283,10 +293,6 @@ exports[`Project List View > matches snapshot 1`] = ` width: 100%; } -.c3 { - text-align: left; -} - .c4 { -webkit-align-items: center; -webkit-box-align: center; @@ -302,11 +308,32 @@ exports[`Project List View > matches snapshot 1`] = `
-

- Projects -

+ + + Project icon + + + + + Projects + +
diff --git a/source/frontend/src/features/research/list/ResearchListView.tsx b/source/frontend/src/features/research/list/ResearchListView.tsx index 0ad37f67ba..f694e567c7 100644 --- a/source/frontend/src/features/research/list/ResearchListView.tsx +++ b/source/frontend/src/features/research/list/ResearchListView.tsx @@ -2,9 +2,11 @@ import React, { useEffect } from 'react'; import { useCallback } from 'react'; import { Col, Row } from 'react-bootstrap'; import { FaPlus } from 'react-icons/fa'; +import { MdTopic } from 'react-icons/md'; import { useHistory } from 'react-router'; import { toast } from 'react-toastify'; +import * as CommonStyled from '@/components/common/styles'; import { StyledAddButton } from '@/components/common/styles'; import Claims from '@/constants/claims'; import { useApiResearchFile } from '@/hooks/pims-api/useApiResearchFile'; @@ -64,7 +66,10 @@ export const ResearchListView: React.FunctionComponent - Research Files + + + Research Files + diff --git a/source/frontend/src/features/research/list/__snapshots__/ResearchListView.test.tsx.snap b/source/frontend/src/features/research/list/__snapshots__/ResearchListView.test.tsx.snap index 981da56f7d..3d5d4b6b55 100644 --- a/source/frontend/src/features/research/list/__snapshots__/ResearchListView.test.tsx.snap +++ b/source/frontend/src/features/research/list/__snapshots__/ResearchListView.test.tsx.snap @@ -287,6 +287,16 @@ exports[`Research List View > matches snapshot 1`] = ` height: 1.6rem; } +.c3 { + color: #474543; + font-family: 'BCSans-Bold'; + font-size: 3.2rem; + border-bottom: solid 0.5rem var(--theme-blue-90); + width: 100%; + text-align: left; + margin-bottom: 2.4rem; +} + .c8 .input-group-prepend select { width: 16rem; } @@ -345,10 +355,6 @@ exports[`Research List View > matches snapshot 1`] = ` width: 100%; } -.c3 { - text-align: left; -} - .c4 { -webkit-align-items: center; -webkit-box-align: center; @@ -364,11 +370,35 @@ exports[`Research List View > matches snapshot 1`] = `
-

- Research Files -

+ + + Research file icon + + + + + + Research Files + +
From 5f48f1c24222d64ed24b4ef009cad7debbfb3720 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 01:00:36 +0000 Subject: [PATCH 42/44] CI: Bump version to v5.7.0-95.19 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index 5c4262e2de..a9fd9fa124 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.18 + 5.7.0-95.19 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 6dafa62888..7ab40e1397 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.18", + "version": "5.7.0-95.19", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", From 9b33fa7db1d5db845eb9f798757fe026d905bfb9 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 18 Dec 2024 18:14:01 -0700 Subject: [PATCH 43/44] =?UTF-8?q?PSP-9655=20:=20FT-REG:=20When=20creating?= =?UTF-8?q?=20a=20new=20file=20approximately=20after=204pm=20P=E2=80=A6=20?= =?UTF-8?q?(#4540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PSP-9655 : FT-REG: When creating a new file approximately after 4pm PST timezone, PIMS timestamps the file with the next day's date * - test updates * - test updates --------- Co-authored-by: Herrera Co-authored-by: Alejandro Sanchez --- .../__snapshots__/AcquisitionView.test.tsx.snap | 3 ++- .../tabs/__snapshots__/AcquisitionFileTabs.test.tsx.snap | 3 ++- .../fileDetails/detail/AcquisitionSummaryView.test.tsx | 3 ++- .../tabs/fileDetails/detail/AcquisitionSummaryView.tsx | 6 ++++-- .../__snapshots__/AcquisitionSummaryView.test.tsx.snap | 3 ++- .../tabs/fileDetails/update/UpdateAcquisitionForm.test.tsx | 7 +++++-- .../__snapshots__/UpdateAcquisitionForm.test.tsx.snap | 4 ++-- .../acquisition/tabs/fileDetails/update/models.ts | 3 ++- source/frontend/src/mocks/acquisitionFiles.mock.ts | 2 +- 9 files changed, 22 insertions(+), 12 deletions(-) diff --git a/source/frontend/src/features/mapSideBar/acquisition/__snapshots__/AcquisitionView.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/__snapshots__/AcquisitionView.test.tsx.snap index 006bbe89bb..436212a8bd 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/__snapshots__/AcquisitionView.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/__snapshots__/AcquisitionView.test.tsx.snap @@ -1166,8 +1166,9 @@ exports[`AcquisitionView component > renders as expected 1`] = `
- Jun 27, 2022 + Dec 17, 2024
matches snapshot 1`] = `
- Jun 27, 2022 + Dec 17, 2024
{ }); it('renders acquisition-related dates', async () => { - const { getByText } = setup( + const { getByText, getByTestId } = setup( { acquisitionFile: { ...mockAcquisitionFileResponse(), @@ -136,6 +136,7 @@ describe('AcquisitionSummaryView component', () => { await waitForEffects(); expect(getByText('Jan 10, 2030')).toBeVisible(); expect(getByText('Mar 10, 2035')).toBeVisible(); + expect(getByTestId('assigned-date')).toHaveTextContent('Dec 17, 2024'); }); it('renders owner solicitor information with primary contact', async () => { diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/detail/AcquisitionSummaryView.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/detail/AcquisitionSummaryView.tsx index d99db2373d..ab9b60622a 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/detail/AcquisitionSummaryView.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/detail/AcquisitionSummaryView.tsx @@ -14,7 +14,7 @@ import { InterestHolderType } from '@/constants/interestHolderTypes'; import { usePersonRepository } from '@/features/contacts/repositories/usePersonRepository'; import useKeycloakWrapper from '@/hooks/useKeycloakWrapper'; import { ApiGen_Concepts_AcquisitionFile } from '@/models/api/generated/ApiGen_Concepts_AcquisitionFile'; -import { exists, prettyFormatDate } from '@/utils'; +import { exists, prettyFormatDate, prettyFormatUTCDate } from '@/utils'; import { formatApiPersonNames } from '@/utils/personUtils'; import { cannotEditMessage } from '../../../common/constants'; @@ -93,7 +93,9 @@ const AcquisitionSummaryView: React.FC = ({ )}
- {prettyFormatDate(detail.assignedDate)} + + {prettyFormatUTCDate(detail.assignedDate)} + matches snapshot 1`] = `
- Jun 27, 2022 + Dec 17, 2024
{ utils.container.querySelector(`select[name="fileStatusTypeCode"]`) as HTMLSelectElement, getEstimatedCompletionDatePicker: () => utils.container.querySelector(`input[name="estimatedCompletionDate"]`) as HTMLInputElement, + getAssignedDatePicker: () => + utils.container.querySelector(`input[name="assignedDate"]`) as HTMLInputElement, getPossessionDatePicker: () => utils.container.querySelector(`input[name="possessionDate"]`) as HTMLInputElement, getTeamMemberProfileDropDownList: (index = 0) => @@ -164,11 +166,12 @@ describe('UpdateAcquisitionForm component', () => { expect(getByText('test representative comment')).toBeVisible(); }); - it('displays estimated completion and possession dates', async () => { - const { getEstimatedCompletionDatePicker, getPossessionDatePicker } = setup({ initialValues }); + it('displays estimated completion, assigned and possession dates', async () => { + const { getEstimatedCompletionDatePicker, getPossessionDatePicker, getAssignedDatePicker } = setup({ initialValues }); await act(async () => {}); expect(getEstimatedCompletionDatePicker()).toHaveValue('Jul 10, 2024'); expect(getPossessionDatePicker()).toHaveValue('Jul 10, 2025'); + expect(getAssignedDatePicker()).toHaveValue('Dec 17, 2024'); }); it('displays Individual type Owner with data', async () => { diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/__snapshots__/UpdateAcquisitionForm.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/__snapshots__/UpdateAcquisitionForm.test.tsx.snap index ef251aa553..60042f1871 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/__snapshots__/UpdateAcquisitionForm.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/__snapshots__/UpdateAcquisitionForm.test.tsx.snap @@ -860,7 +860,7 @@ exports[`UpdateAcquisitionForm component > Sub-interest files > renders as expec name="assignedDate" placeholder="MMM DD, YYYY" type="text" - value="Jun 27, 2022" + value="Dec 17, 2024" />
@@ -4554,7 +4554,7 @@ exports[`UpdateAcquisitionForm component > renders as expected 1`] = ` name="assignedDate" placeholder="MMM DD, YYYY" type="text" - value="Jun 27, 2022" + value="Dec 17, 2024" /> diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/models.ts b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/models.ts index dc2911336b..f3416a42f3 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/models.ts +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/fileDetails/update/models.ts @@ -5,6 +5,7 @@ import { ApiGen_Concepts_AcquisitionFile } from '@/models/api/generated/ApiGen_C import { ApiGen_Concepts_AcquisitionFileOwner } from '@/models/api/generated/ApiGen_Concepts_AcquisitionFileOwner'; import { ApiGen_Concepts_InterestHolder } from '@/models/api/generated/ApiGen_Concepts_InterestHolder'; import { getEmptyBaseAudit } from '@/models/defaultInitializers'; +import { formatUTCDateTime } from '@/utils'; import { fromTypeCode, toTypeCodeNullable } from '@/utils/formUtils'; import { exists, isValidId, isValidIsoDateTime } from '@/utils/utils'; @@ -120,7 +121,7 @@ export class UpdateAcquisitionSummaryFormModel newForm.legacyFileNumber = model.legacyFileNumber ?? undefined; newForm.fileName = model.fileName || ''; newForm.rowVersion = model.rowVersion ?? undefined; - newForm.assignedDate = model.assignedDate ?? undefined; + newForm.assignedDate = model.assignedDate ? formatUTCDateTime(model.assignedDate) : ''; newForm.deliveryDate = model.deliveryDate ?? undefined; newForm.estimatedCompletionDate = model.estimatedCompletionDate ?? undefined; newForm.possessionDate = model.possessionDate ?? undefined; diff --git a/source/frontend/src/mocks/acquisitionFiles.mock.ts b/source/frontend/src/mocks/acquisitionFiles.mock.ts index 1cf3082fe6..8cd2a38a04 100644 --- a/source/frontend/src/mocks/acquisitionFiles.mock.ts +++ b/source/frontend/src/mocks/acquisitionFiles.mock.ts @@ -99,7 +99,7 @@ export const mockAcquisitionFileResponse = ( }, productId: null, legacyFileNumber: 'legacy file number', - assignedDate: '2022-06-27T00:00:00', + assignedDate: '2024-12-18T02:04:53.18', deliveryDate: '2022-07-29T00:00:00', estimatedCompletionDate: '2024-07-10T00:00:00', possessionDate: '2025-07-10T00:00:00', From e6990dc29cd8a8e98b48986df6c101d59e3718a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 01:14:26 +0000 Subject: [PATCH 44/44] CI: Bump version to v5.7.0-95.20 --- source/backend/api/Pims.Api.csproj | 2 +- source/frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/backend/api/Pims.Api.csproj b/source/backend/api/Pims.Api.csproj index a9fd9fa124..79dcc9d373 100644 --- a/source/backend/api/Pims.Api.csproj +++ b/source/backend/api/Pims.Api.csproj @@ -2,7 +2,7 @@ 0ef6255f-9ea0-49ec-8c65-c172304b4926 - 5.7.0-95.19 + 5.7.0-95.20 5.7.0.95 true {16BC0468-78F6-4C91-87DA-7403C919E646} diff --git a/source/frontend/package.json b/source/frontend/package.json index 7ab40e1397..cf0713c1a2 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.7.0-95.19", + "version": "5.7.0-95.20", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1",