diff --git a/backend/src/main/java/ca/bc/gov/backendstartapi/exception/ClientIdForbiddenException.java b/backend/src/main/java/ca/bc/gov/backendstartapi/exception/ClientIdForbiddenException.java index 17ea71fca..18c92026a 100644 --- a/backend/src/main/java/ca/bc/gov/backendstartapi/exception/ClientIdForbiddenException.java +++ b/backend/src/main/java/ca/bc/gov/backendstartapi/exception/ClientIdForbiddenException.java @@ -9,6 +9,6 @@ public class ClientIdForbiddenException extends ResponseStatusException { public ClientIdForbiddenException() { - super(HttpStatus.FORBIDDEN); + super(HttpStatus.FORBIDDEN, "No access due to client ID"); } } diff --git a/backend/src/main/java/ca/bc/gov/backendstartapi/service/SeedlotService.java b/backend/src/main/java/ca/bc/gov/backendstartapi/service/SeedlotService.java index e00ca6965..224e49048 100644 --- a/backend/src/main/java/ca/bc/gov/backendstartapi/service/SeedlotService.java +++ b/backend/src/main/java/ca/bc/gov/backendstartapi/service/SeedlotService.java @@ -60,6 +60,7 @@ import ca.bc.gov.backendstartapi.security.LoggedUserService; import ca.bc.gov.backendstartapi.security.UserInfo; import jakarta.transaction.Transactional; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -244,6 +245,7 @@ public Optional> getSeedlotByClientId( * @throws SeedlotNotFoundException in case of errors. */ public SeedlotDto getSingleSeedlotInfo(@NonNull String seedlotNumber) { + SparLog.info("Retrieving information for Seedlot number {}", seedlotNumber); Seedlot seedlotEntity = @@ -251,6 +253,14 @@ public SeedlotDto getSingleSeedlotInfo(@NonNull String seedlotNumber) { SparLog.info("Seedlot number {} found", seedlotNumber); + String clientId = seedlotEntity.getApplicantClientNumber(); + Optional userInfo = loggedUserService.getLoggedUserInfo(); + + if (userInfo.isPresent() && !userInfo.get().clientIds().contains(clientId)) { + SparLog.info("User has no access to seedlot {}, request denied.", seedlotNumber); + throw new ClientIdForbiddenException(); + } + SeedlotDto seedlotDto = new SeedlotDto(); seedlotDto.setSeedlot(seedlotEntity); @@ -523,6 +533,10 @@ public SeedlotAclassFormDto getAclassSeedlotFormInfo(@NonNull String seedlotNumb List ownershipStep = seedlotOwnerQuantityRepository.findAllBySeedlot_id(seedlotInfo.getId()).stream() + .filter( + owner -> + owner.getOriginalPercentageOwned() != null + && owner.getOriginalPercentageOwned().compareTo(BigDecimal.ZERO) > 0) .map( owner -> new SeedlotFormOwnershipDto( diff --git a/backend/src/test/java/ca/bc/gov/backendstartapi/service/SeedlotServiceTest.java b/backend/src/test/java/ca/bc/gov/backendstartapi/service/SeedlotServiceTest.java index 4fee5228e..46f28fe61 100644 --- a/backend/src/test/java/ca/bc/gov/backendstartapi/service/SeedlotServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/backendstartapi/service/SeedlotServiceTest.java @@ -457,8 +457,6 @@ void findAclassSeedlotFormFullDataSuccessTest() { parentTreeDto.pollenCount(), audit); - List parentTreeData = List.of(spt); - ParentTreeGeneticQualityDto sptgqDto = createParentTreeGenQuaDto(); SeedlotParentTreeGeneticQuality sptgq = @@ -504,11 +502,15 @@ void findAclassSeedlotFormFullDataSuccessTest() { SeedlotOwnerQuantity seedlotOwners = new SeedlotOwnerQuantity(seedlotEntity, onwerNumber, ownerLoc); + BigDecimal orginalPercOwned = new BigDecimal(100); + seedlotOwners.setOriginalPercentageOwned(orginalPercOwned); seedlotOwners.setMethodOfPayment(new MethodOfPaymentEntity(methodOfPayment, "", null)); List seedlotOrchards = List.of(new SeedlotOrchard(seedlotEntity, true, orchardId)); + List parentTreeData = List.of(spt); + when(seedlotRepository.findById(seedlotNumber)).thenReturn(Optional.of(seedlotEntity)); when(seedlotParentTreeService.getAllSeedlotParentTree(seedlotNumber)) .thenReturn(parentTreeData); @@ -561,7 +563,7 @@ void findAclassSeedlotFormFullDataSuccessTest() { null, null, null, null, null, null, null, null, List.of(0)), List.of( new SeedlotFormOwnershipDto( - onwerNumber, ownerLoc, null, null, null, methodOfPayment, null)), + onwerNumber, ownerLoc, orginalPercOwned, null, null, methodOfPayment, null)), new SeedlotFormInterimDto(null, null, null, null, null, null), new SeedlotFormOrchardDto( orchardId, null, null, null, null, null, null, null, null, null), diff --git a/frontend/src/components/ApplicantAgencyFields/definitions.ts b/frontend/src/components/ApplicantAgencyFields/definitions.ts index 6948b371d..c39d1d443 100644 --- a/frontend/src/components/ApplicantAgencyFields/definitions.ts +++ b/frontend/src/components/ApplicantAgencyFields/definitions.ts @@ -8,7 +8,6 @@ interface ApplicantAgencyFieldsProps { agency: OptionsInputType; locationCode: StringInputType; fieldsProps: AgencyTextPropsType; - agencyOptions: Array; setAgencyAndCode: Function; defaultAgency?: MultiOptionsObj; defaultCode?: string; diff --git a/frontend/src/components/LotApplicantAndInfoForm/index.tsx b/frontend/src/components/LotApplicantAndInfoForm/index.tsx index 06ea2688e..1d5bfd165 100644 --- a/frontend/src/components/LotApplicantAndInfoForm/index.tsx +++ b/frontend/src/components/LotApplicantAndInfoForm/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { @@ -12,9 +12,11 @@ import validator from 'validator'; import Subtitle from '../Subtitle'; import Divider from '../Divider'; import ApplicantAgencyFields from '../ApplicantAgencyFields'; -import getApplicantAgenciesOptions from '../../api-service/applicantAgenciesAPI'; import { BooleanInputType, OptionsInputType, StringInputType } from '../../types/FormInputType'; import { EmptyBooleanInputType } from '../../shared-constants/shared-constants'; +import AuthContext from '../../contexts/AuthContext'; +import { getForestClientByNumberOrAcronym } from '../../api-service/forestClientsAPI'; +import { THREE_HALF_HOURS, THREE_HOURS } from '../../config/TimeUnits'; import SeedlotInformation from './SeedlotInformation'; import { FormProps } from './definitions'; @@ -36,11 +38,36 @@ const LotApplicantAndInfoForm = ({ seedlotFormData, setSeedlotFormData }: FormProps) => { - const applicantAgencyQuery = useQuery({ - queryKey: ['applicant-agencies'], - enabled: !isEdit, - queryFn: () => getApplicantAgenciesOptions() - }); + const { selectedClientRoles } = useContext(AuthContext); + + const defaultApplicantAgencyQuery = useQuery( + { + queryKey: ['forest-clients', selectedClientRoles?.clientId], + queryFn: () => getForestClientByNumberOrAcronym(selectedClientRoles?.clientId!), + enabled: !isEdit && !isReview && !!selectedClientRoles?.clientId, + staleTime: THREE_HOURS, + cacheTime: THREE_HALF_HOURS + } + ); + + useEffect(() => { + // Pre-fill the applicant acronym based on user selected role + if (defaultApplicantAgencyQuery.status === 'success' && !seedlotFormData?.client.value.code && setSeedlotFormData) { + const forestClient = defaultApplicantAgencyQuery.data; + + setSeedlotFormData((prevForm) => ({ + ...prevForm, + client: { + ...prevForm.client, + value: { + code: forestClient.clientNumber, + description: forestClient.clientName, + label: forestClient.acronym + } + } + })); + } + }, [defaultApplicantAgencyQuery.status]); const handleEmail = (value: string) => { const isEmailInvalid = !validator.isEmail(value); @@ -89,7 +116,6 @@ const LotApplicantAndInfoForm = ({ : vegLotLocationCode } fieldsProps={agencyFieldsProp} - agencyOptions={isEdit ? [] : applicantAgencyQuery.data ?? []} setAgencyAndCode={ ( _isDefault: BooleanInputType, diff --git a/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/data.ts b/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/data.ts deleted file mode 100644 index 18ea33b2c..000000000 --- a/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/data.ts +++ /dev/null @@ -1,7 +0,0 @@ -const AgencyOptions: Array = [ - '0032 - Strong Seeds Orchard - SSO', - '0035 - Weak Seeds Orchard - WSO', - '0038 - Okay Seeds Orchard - OSO' -]; - -export default AgencyOptions; diff --git a/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/index.tsx index 74a404f4a..8d3c5338d 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/CollectionStep/index.tsx @@ -5,7 +5,6 @@ import { Column, Row, TextInput, - NumberInput, CheckboxGroup, Checkbox, DatePickerInput, @@ -46,7 +45,6 @@ const CollectionStep = ({ isReview }: CollectionStepProps) => { setStepData, defaultAgencyObj: defaultAgency, defaultCode, - agencyOptions, isFormSubmitted } = useContext(ClassAContext); @@ -157,7 +155,6 @@ const CollectionStep = ({ isReview }: CollectionStepProps) => { agency={state.collectorAgency} locationCode={state.locationCode} fieldsProps={agencyFieldsProps} - agencyOptions={agencyOptions} defaultAgency={defaultAgency} defaultCode={defaultCode} setAgencyAndCode={ diff --git a/frontend/src/components/SeedlotRegistrationSteps/ExtractionAndStorageStep/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/ExtractionAndStorageStep/index.tsx index 18e75ae71..ddf317f2e 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/ExtractionAndStorageStep/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/ExtractionAndStorageStep/index.tsx @@ -42,7 +42,6 @@ const ExtractionAndStorage = ( const { allStepData: { extractionStorageStep: state }, setStepData, - agencyOptions, isFormSubmitted } = useContext(ClassAContext); @@ -115,7 +114,6 @@ const ExtractionAndStorage = ( agency={state.extraction.agency} locationCode={state.extraction.locationCode} fieldsProps={extractorAgencyFields} - agencyOptions={agencyOptions} defaultAgency={defaultAgency} defaultCode={defaultCode} setAgencyAndCode={( @@ -205,7 +203,6 @@ const ExtractionAndStorage = ( agency={state.seedStorage.agency} locationCode={state.seedStorage.locationCode} fieldsProps={storageAgencyFields} - agencyOptions={agencyOptions} defaultAgency={defaultAgency} defaultCode={defaultCode} setAgencyAndCode={( diff --git a/frontend/src/components/SeedlotRegistrationSteps/InterimStep/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/InterimStep/index.tsx index d6b199b32..fbfaa0215 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/InterimStep/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/InterimStep/index.tsx @@ -42,7 +42,6 @@ const InterimStep = ({ isReview }:InterimStepProps) => { allStepData: { collectionStep: { collectorAgency } }, allStepData: { collectionStep: { locationCode: collectorCode } }, setStepData, - agencyOptions, isFormSubmitted } = useContext(ClassAContext); @@ -160,7 +159,6 @@ const InterimStep = ({ isReview }:InterimStepProps) => { agency={state.agencyName} locationCode={state.locationCode} fieldsProps={agencyFieldsProps} - agencyOptions={agencyOptions} defaultAgency={collectorAgency.value} defaultCode={collectorCode.value} setAgencyAndCode={( diff --git a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/SingleOwnerInfo/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/SingleOwnerInfo/index.tsx index 87332d359..bd3b2fa3a 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/SingleOwnerInfo/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/SingleOwnerInfo/index.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { UseQueryResult } from '@tanstack/react-query'; import { - NumberInput, TextInput, FlexGrid, Column, @@ -20,10 +19,7 @@ import ComboBoxEvent from '../../../../types/ComboBoxEvent'; import { validatePerc } from '../utils'; -import { - SingleOwnerForm, - NumStepperVal -} from '../definitions'; +import { SingleOwnerForm } from '../definitions'; import { inputText, DEFAULT_INDEX, agencyFieldsProps } from '../constants'; import { FilterObj, filterInput } from '../../../../utils/FilterUtils'; @@ -32,7 +28,6 @@ import './styles.scss'; interface SingleOwnerInfoProps { ownerInfo: SingleOwnerForm, deleteAnOwner: Function, - agencyOptions: Array, defaultAgency: MultiOptionsObj, defaultCode: string, fundingSourcesQuery: UseQueryResult, @@ -44,7 +39,7 @@ interface SingleOwnerInfoProps { } const SingleOwnerInfo = ({ - ownerInfo, agencyOptions, defaultAgency, defaultCode, fundingSourcesQuery, + ownerInfo, defaultAgency, defaultCode, fundingSourcesQuery, methodsOfPaymentQuery, deleteAnOwner, checkPortionSum, setState, readOnly, isReview }: SingleOwnerInfoProps) => { const [ownerPortionInvalidText, setOwnerPortionInvalidText] = useState( @@ -91,7 +86,6 @@ const SingleOwnerInfo = ({ const handleReservedAndSurplus = (field: string, value: string) => { const clonedState = structuredClone(ownerInfo); const isReserved = field === 'reservedPerc'; - console.log(value); // First validate the format of what is set on the value and // get the correct error message @@ -152,7 +146,6 @@ const SingleOwnerInfo = ({ agency={ownerInfo.ownerAgency} locationCode={ownerInfo.ownerCode} fieldsProps={agencyFieldsProps} - agencyOptions={agencyOptions} defaultAgency={defaultAgency} defaultCode={defaultCode} setAgencyAndCode={ diff --git a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx index e5554d721..47e2295d8 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx @@ -49,7 +49,6 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { setStepData, defaultAgencyObj: defaultAgency, defaultCode, - agencyOptions, isFormSubmitted } = useContext(ClassAContext); @@ -173,7 +172,6 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { > void, defaultAgencyObj: MultiOptionsObj, defaultCode: string, - agencyOptions: MultiOptionsObj[], isFormSubmitted: boolean, isFormIncomplete: boolean, handleSaveBtn: () => void, @@ -86,7 +85,6 @@ const ClassAContext = createContext({ setStep: (delta: number) => { }, defaultAgencyObj: EmptyMultiOptObj, defaultCode: '', - agencyOptions: [] as MultiOptionsObj[], isFormSubmitted: false, isFormIncomplete: true, handleSaveBtn: () => { }, diff --git a/frontend/src/views/Seedlot/ContextContainerClassA/index.tsx b/frontend/src/views/Seedlot/ContextContainerClassA/index.tsx index b92a883a3..52cef4464 100644 --- a/frontend/src/views/Seedlot/ContextContainerClassA/index.tsx +++ b/frontend/src/views/Seedlot/ContextContainerClassA/index.tsx @@ -16,7 +16,6 @@ import { } from '../../../api-service/seedlotAPI'; import getVegCodes from '../../../api-service/vegetationCodeAPI'; import { getForestClientByNumberOrAcronym } from '../../../api-service/forestClientsAPI'; -import getApplicantAgenciesOptions from '../../../api-service/applicantAgenciesAPI'; import getFundingSources from '../../../api-service/fundingSourcesAPI'; import getMethodsOfPayment from '../../../api-service/methodsOfPaymentAPI'; import getGameticMethodology from '../../../api-service/gameticMethodologyAPI'; @@ -256,11 +255,6 @@ const ContextContainerClassA = ({ children }: props) => { const getDefaultLocationCode = (): string => (seedlotQuery.data?.seedlot.applicantLocationCode ?? ''); - const applicantAgencyQuery = useQuery({ - queryKey: ['applicant-agencies'], - queryFn: getApplicantAgenciesOptions - }); - const updateStepStatus = ( stepName: keyof ProgressIndicatorConfig, stepStatusObj: ProgressStepStatus @@ -742,7 +736,6 @@ const ContextContainerClassA = ({ children }: props) => { setStep, defaultAgencyObj: getAgencyObj(), defaultCode: getDefaultLocationCode(), - agencyOptions: applicantAgencyQuery.data ?? [], isFormSubmitted, isFormIncomplete, handleSaveBtn, @@ -782,7 +775,7 @@ const ContextContainerClassA = ({ children }: props) => { [ seedlotNumber, calculatedValues, allStepData, seedlotQuery.status, vegCodeQuery.status, formStep, forestClientQuery.status, - applicantAgencyQuery.status, isFormSubmitted, isFormIncomplete, + isFormSubmitted, isFormIncomplete, saveStatus, saveDescription, lastSaveTimestamp, allStepCompleted, progressStatus, submitSeedlot, saveProgress.status, getAllSeedlotInfoQuery.status, methodsOfPaymentQuery.status, orchardQuery.status, gameticMethodologyQuery.status,