diff --git a/frontend/src/api-service/ApiConfig.ts b/frontend/src/api-service/ApiConfig.ts index df8da4096..9b301768d 100644 --- a/frontend/src/api-service/ApiConfig.ts +++ b/frontend/src/api-service/ApiConfig.ts @@ -55,7 +55,9 @@ const ApiConfig = { areaOfUseSpzList: `${oracleServerHost}/api/area-of-use/spz-list/vegetation-code`, - parentTreeByVegCode: `${oracleServerHost}/api/parent-trees/vegetation-codes/{vegCode}` + parentTreeByVegCode: `${oracleServerHost}/api/parent-trees/vegetation-codes/{vegCode}`, + + seedlotFromOracleDbBySeedlotNumber: `${oracleServerHost}/api/seedlot/{seedlotNumber}` }; export default ApiConfig; diff --git a/frontend/src/api-service/seedlotAPI.ts b/frontend/src/api-service/seedlotAPI.ts index 24217771d..3a4c9ae49 100644 --- a/frontend/src/api-service/seedlotAPI.ts +++ b/frontend/src/api-service/seedlotAPI.ts @@ -72,3 +72,8 @@ export const getAClassSeedlotFullForm = (seedlotNumber: string) => { const url = `${ApiConfig.seedlots}/${seedlotNumber}/a-class-full-form`; return api.get(url).then((res) => res.data as SeedlotAClassFullResponseType); }; + +export const getSeedlotFromOracleDbBySeedlotNumber = (seedlotNumber: string) => { + const url = ApiConfig.seedlotFromOracleDbBySeedlotNumber.replace('{seedlotNumber}', seedlotNumber); + return api.get(url); +}; diff --git a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx index bef78b0ec..9361c5fe0 100644 --- a/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx +++ b/frontend/src/components/SeedlotRegistrationSteps/OwnershipStep/index.tsx @@ -18,6 +18,7 @@ import { EmptyMultiOptObj } from '../../../shared-constants/shared-constants'; import { THREE_HALF_HOURS, THREE_HOURS } from '../../../config/TimeUnits'; import { getMultiOptList } from '../../../utils/MultiOptionsUtils'; import getFundingSources from '../../../api-service/fundingSourcesAPI'; +import { getSeedlotFromOracleDbBySeedlotNumber } from '../../../api-service/seedlotAPI'; import TitleAccordion from '../../TitleAccordion'; import ScrollToTop from '../../ScrollToTop'; import SingleOwnerInfo from './SingleOwnerInfo'; @@ -53,7 +54,8 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { setStepData, defaultClientNumber: defaultAgency, defaultCode, - isFormSubmitted + isFormSubmitted, + seedlotNumber } = useContext(ClassAContext); const [accordionControls, setAccordionControls] = useState({}); @@ -107,6 +109,12 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { cacheTime: THREE_HALF_HOURS }); + const getSeedlotBySeedlotNumberQuery = useQuery( + ['get-seedlot-by-seedlotNumber', seedlotNumber], + () => getSeedlotFromOracleDbBySeedlotNumber(seedlotNumber ?? ''), + { enabled: !!seedlotNumber } + ); + // Set default method of payment for the first owner. useEffect(() => { if (methodsOfPaymentQuery.status === 'success') { @@ -143,6 +151,8 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { const getFcQuery = (clientNumber: string): ForestClientType | undefined => qc.getQueryData(['forest-clients', clientNumber]); + const originalSeedQty = getSeedlotBySeedlotNumberQuery.data?.data?.originalSeedQty ?? 0; + return (
@@ -209,7 +219,7 @@ const OwnershipStep = ({ isReview }: OwnershipStepProps) => { checkPortionSum={ (updtEntry: SingleOwnerForm, id: number) => checkPortionSum(updtEntry, id) } - readOnly={isFormSubmitted} + readOnly={isFormSubmitted || originalSeedQty > 0} isReview={isReview} /> diff --git a/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx b/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx index a7f45fb1f..f47855484 100644 --- a/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx +++ b/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx @@ -13,7 +13,7 @@ import { } from '@carbon/icons-react'; import { Beforeunload } from 'react-beforeunload'; -import { getSeedlotById, putAClassSeedlotProgress } from '../../../api-service/seedlotAPI'; +import { getSeedlotById, putAClassSeedlotProgress, getSeedlotFromOracleDbBySeedlotNumber } from '../../../api-service/seedlotAPI'; import { THREE_HALF_HOURS, THREE_HOURS } from '../../../config/TimeUnits'; import getVegCodes from '../../../api-service/vegetationCodeAPI'; import Breadcrumbs from '../../../components/Breadcrumbs'; @@ -105,6 +105,12 @@ const SeedlotReviewContent = () => { refetchOnMount: true }); + const getSeedlotBySeedlotNumberQuery = useQuery({ + queryKey: ['seedlot-by-seedlotNumber', seedlotNumber], + queryFn: () => getSeedlotFromOracleDbBySeedlotNumber(seedlotNumber ?? ''), + enabled: seedlotQuery.isFetched + }); + useEffect(() => { const { status } = seedlotQuery; @@ -550,6 +556,8 @@ const SeedlotReviewContent = () => { className="edit-save-btn" renderIcon={isReadMode ? Edit : Save} onClick={handleEditSaveBtn} + disabled={getSeedlotBySeedlotNumberQuery.isSuccess + && getSeedlotBySeedlotNumberQuery.data.data.originalSeedQty > 0} > {isReadMode ? 'Edit seedlot' : 'Save edit'} diff --git a/oracle-api/config/application-local.yml b/oracle-api/config/application-local.yml new file mode 100644 index 000000000..8a5b3d520 --- /dev/null +++ b/oracle-api/config/application-local.yml @@ -0,0 +1,5 @@ +spring: + datasource: + url: jdbc:oracle:thin:@tcp://localhost:1521/dbdock_01 + username: THE + password: default diff --git a/oracle-api/src/main/java/ca/bc/gov/oracleapi/endpoint/SeedlotEndpoint.java b/oracle-api/src/main/java/ca/bc/gov/oracleapi/endpoint/SeedlotEndpoint.java new file mode 100644 index 000000000..4932dd289 --- /dev/null +++ b/oracle-api/src/main/java/ca/bc/gov/oracleapi/endpoint/SeedlotEndpoint.java @@ -0,0 +1,73 @@ +package ca.bc.gov.oracleapi.endpoint; + +import ca.bc.gov.oracleapi.config.SparLog; +import ca.bc.gov.oracleapi.entity.Seedlot; +import ca.bc.gov.oracleapi.repository.SeedlotRepository; +import ca.bc.gov.oracleapi.security.RoleAccessConfig; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** This class exposes seedlot resources API. */ +@RestController +@RequestMapping("/api/seedlot") +@Tag(name = "seedlot", description = "Resource to retrieve seedlot") +public class SeedlotEndpoint { + + private SeedlotRepository seedlotRepository; + + SeedlotEndpoint(SeedlotRepository seedlotRepository) { + this.seedlotRepository = seedlotRepository; + } + + /** + * Retrieve a seedlot by seedlot number. + * + * @return A record of {@link Seedlot}. + */ + @GetMapping( + path = "/{seedlotNumber}", + produces = "application/json") + @Operation( + summary = "Retrieve a seedlot by seedlot number", + description = "Retrieve a seedlot by seedlot number ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "Returns a record of seedlot", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Seedlot.class))), + @ApiResponse( + responseCode = "401", + description = "Access token is missing or invalid", + content = @Content(schema = @Schema(implementation = Void.class))) + }) + @RoleAccessConfig({"SPAR_TSC_ADMIN", "SPAR_MINISTRY_ORCHARD", "SPAR_NONMINISTRY_ORCHARD"}) + public Seedlot getSeedlotBySeedlotNumber( + @PathVariable + @Parameter( + name = "seedlotNumber", + in = ParameterIn.PATH, + description = "Identifier of the seedlot.") + String seedlotNumber + ) { + SparLog.info("Fetch a seedlot by seedlot number"); + SparLog.info(seedlotNumber); + Seedlot seedlot = seedlotRepository.findSeedlotBySeedlotNumber(seedlotNumber); + SparLog.info("{} valid seedlot found.", seedlot); + + return seedlot; + } +} diff --git a/oracle-api/src/main/java/ca/bc/gov/oracleapi/entity/Seedlot.java b/oracle-api/src/main/java/ca/bc/gov/oracleapi/entity/Seedlot.java new file mode 100644 index 000000000..1b534ba7b --- /dev/null +++ b/oracle-api/src/main/java/ca/bc/gov/oracleapi/entity/Seedlot.java @@ -0,0 +1,29 @@ +package ca.bc.gov.oracleapi.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +/** This class presents a funding source to an agency seedlot owner. */ +@Getter +@Setter +@Entity +@Table(name = "SEEDLOT") +@Schema(description = "Represents a partial seedlot object in the database") +public class Seedlot { + + @Id + @Column(name = "SEEDLOT_NUMBER") + @Schema(description = "The number of a seedlot", example = "16258") + private Long seedlotNumber; + + @Column(name = "ORIGINAL_SEED_QTY") + @Schema( + description = "The original quantity of seeds in the seedlot", + example = "1") + private Long originalSeedQty; +} diff --git a/oracle-api/src/main/java/ca/bc/gov/oracleapi/repository/SeedlotRepository.java b/oracle-api/src/main/java/ca/bc/gov/oracleapi/repository/SeedlotRepository.java new file mode 100644 index 000000000..ae354807c --- /dev/null +++ b/oracle-api/src/main/java/ca/bc/gov/oracleapi/repository/SeedlotRepository.java @@ -0,0 +1,13 @@ +package ca.bc.gov.oracleapi.repository; + +import ca.bc.gov.oracleapi.entity.Seedlot; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +/** This interface enables the funding source entity to be retrieved from the database. */ +public interface SeedlotRepository extends JpaRepository { + + @Query("SELECT s FROM Seedlot s WHERE s.seedlotNumber = :seedlotNumber") + Seedlot findSeedlotBySeedlotNumber(@Param("seedlotNumber") String seedlotNumber); +}