Skip to content

Commit

Permalink
PSP-5492 Update Historical # and Type on Property Details (Property A…
Browse files Browse the repository at this point in the history
…ttributes) (#4016)

* Update frontend service calls to API

* Add new type code dropdown

* Update historical numbers - frontend

* Save the list of historical file numbers

* Clear associated fields when historical file number type is changed

* Controller, service and repository changes for updating historical numbers

* Test updates

* Update form validation rules

* Test corrections

* Fix merge conflicts

* Lint fixes

* CR feedback

* Fix model mappings

* Fix lookup table key
  • Loading branch information
asanchezr authored May 14, 2024
1 parent 2dd69a6 commit 5b76e6d
Show file tree
Hide file tree
Showing 22 changed files with 503 additions and 116 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using MapsterMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Pims.Api.Models.Concepts.Property;
using Pims.Api.Policies;
using Pims.Api.Services;
using Pims.Core.Extensions;
using Pims.Core.Json;
using Pims.Dal.Security;
using Swashbuckle.AspNetCore.Annotations;
Expand All @@ -25,6 +28,7 @@ public class HistoricalNumberController : ControllerBase
#region Variables
private readonly IPropertyService _propertyService;
private readonly IMapper _mapper;
private readonly ILogger _logger;
#endregion

#region Constructors
Expand All @@ -35,10 +39,11 @@ public class HistoricalNumberController : ControllerBase
/// <param name="propertyService"></param>
/// <param name="mapper"></param>
///
public HistoricalNumberController(IPropertyService propertyService, IMapper mapper)
public HistoricalNumberController(IPropertyService propertyService, IMapper mapper, ILogger<HistoricalNumberController> logger)
{
_propertyService = propertyService;
_mapper = mapper;
_logger = logger;
}

/// <summary>
Expand All @@ -47,14 +52,45 @@ public HistoricalNumberController(IPropertyService propertyService, IMapper mapp
[HttpGet("{propertyId}/historicalNumbers")]
[HasPermission(Permissions.PropertyView)]
[Produces("application/json")]
[ProducesResponseType(typeof(HistoricalFileNumberModel), 200)]
[ProducesResponseType(typeof(IEnumerable<HistoricalFileNumberModel>), 200)]
[SwaggerOperation(Tags = new[] { "property" })]
[TypeFilter(typeof(NullJsonResultFilter))]
public IActionResult GetHistoricalNumbersForPropertyId(long propertyId)
{
_logger.LogInformation(
"Request received by Controller: {Controller}, Action: {ControllerAction}, User: {User}, DateTime: {DateTime}",
nameof(HistoricalNumberController),
nameof(GetHistoricalNumbersForPropertyId),
User.GetUsername(),
DateTime.Now);

var historicalNumbers = _propertyService.GetHistoricalNumbersForPropertyId(propertyId);
return new JsonResult(_mapper.Map<List<HistoricalFileNumberModel>>(historicalNumbers));
}

/// <summary>
/// Updates the list of historic numbers for a given property id.
/// </summary>
[HttpPut("{propertyId}/historicalNumbers")]
[HasPermission(Permissions.PropertyEdit)]
[Produces("application/json")]
[ProducesResponseType(typeof(IEnumerable<HistoricalFileNumberModel>), 200)]
[SwaggerOperation(Tags = new[] { "property" })]
[TypeFilter(typeof(NullJsonResultFilter))]
public IActionResult UpdateHistoricalNumbers(long propertyId, IEnumerable<HistoricalFileNumberModel> historicalNumbers)
{
_logger.LogInformation(
"Request received by Controller: {Controller}, Action: {ControllerAction}, User: {User}, DateTime: {DateTime}",
nameof(HistoricalNumberController),
nameof(UpdateHistoricalNumbers),
User.GetUsername(),
DateTime.Now);

var historicalEntities = _mapper.Map<IEnumerable<Dal.Entities.PimsHistoricalFileNumber>>(historicalNumbers);
var updatedEntities = _propertyService.UpdateHistoricalFileNumbers(propertyId, historicalEntities);

return new JsonResult(_mapper.Map<IEnumerable<HistoricalFileNumberModel>>(updatedEntities));
}
#endregion
}
}
2 changes: 2 additions & 0 deletions source/backend/api/Controllers/LookupController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public IActionResult GetAll()
var dispositionChecklistItemStatusTypes = _mapper.Map<Model.LookupModel[]>(_lookupRepository.GetAllDispositionChecklistItemStatusTypes());
var dispositionChecklistItemTypes = _mapper.Map<Model.LookupModel[]>(_lookupRepository.GetAllDispositionChecklistItemTypes());
var dispositionChecklistSectionTypes = _mapper.Map<Model.LookupModel[]>(_lookupRepository.GetAllDispositionChecklistSectionTypes());
var historicalNumberTypes = _mapper.Map<Model.LookupModel[]>(_lookupRepository.GetAllHistoricalNumberTypes());

var codes = new List<object>();
codes.AddRange(areaUnitTypes);
Expand Down Expand Up @@ -220,6 +221,7 @@ public IActionResult GetAll()
codes.AddRange(dispositionChecklistItemStatusTypes);
codes.AddRange(dispositionChecklistItemTypes);
codes.AddRange(dispositionChecklistSectionTypes);
codes.AddRange(historicalNumberTypes);

var response = new JsonResult(codes);

Expand Down
2 changes: 2 additions & 0 deletions source/backend/api/Services/IPropertyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,7 @@ public interface IPropertyService
void UpdateLocation(PimsProperty acquisitionProperty, ref PimsProperty propertyToUpdate, IEnumerable<UserOverrideCode> overrideCodes);

IList<PimsHistoricalFileNumber> GetHistoricalNumbersForPropertyId(long propertyId);

IList<PimsHistoricalFileNumber> UpdateHistoricalFileNumbers(long propertyId, IEnumerable<PimsHistoricalFileNumber> pimsHistoricalNumbers);
}
}
12 changes: 12 additions & 0 deletions source/backend/api/Services/PropertyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,18 @@ public IList<PimsHistoricalFileNumber> GetHistoricalNumbersForPropertyId(long pr
return _historicalNumberRepository.GetAllByPropertyId(propertyId);
}

public IList<PimsHistoricalFileNumber> UpdateHistoricalFileNumbers(long propertyId, IEnumerable<PimsHistoricalFileNumber> pimsHistoricalNumbers)
{

_logger.LogInformation("Updating historical numbers for property with id {id}", propertyId);
_user.ThrowIfNotAuthorized(Permissions.PropertyEdit);

_historicalNumberRepository.UpdateHistoricalFileNumbers(propertyId, pimsHistoricalNumbers);
_historicalNumberRepository.CommitTransaction();

return GetHistoricalNumbersForPropertyId(propertyId);
}

private Point TransformCoordinates(Geometry location)
{
// return property spatial location in lat/long (4326)
Expand Down
8 changes: 8 additions & 0 deletions source/backend/dal/Repositories/HistoricNumberRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pims.Dal.Entities;
using Pims.Dal.Helpers.Extensions;

namespace Pims.Dal.Repositories
{
Expand Down Expand Up @@ -42,6 +43,13 @@ public IList<PimsHistoricalFileNumber> GetAllByPropertyId(long propertyId)
.ToList();
return historicalFileNumbers;
}

public IList<PimsHistoricalFileNumber> UpdateHistoricalFileNumbers(long propertyId, IEnumerable<PimsHistoricalFileNumber> pimsHistoricalNumbers)
{
using var scope = Logger.QueryScope();
Context.UpdateChild<PimsProperty, long, PimsHistoricalFileNumber, long>(l => l.PimsHistoricalFileNumbers, propertyId, pimsHistoricalNumbers.ToArray());
return GetAllByPropertyId(propertyId);
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ namespace Pims.Dal.Repositories
public interface IHistoricalNumberRepository : IRepository<PimsHistoricalFileNumber>
{
IList<PimsHistoricalFileNumber> GetAllByPropertyId(long propertyId);

IList<PimsHistoricalFileNumber> UpdateHistoricalFileNumbers(long propertyId, IEnumerable<PimsHistoricalFileNumber> pimsHistoricalNumbers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,7 @@ public interface ILookupRepository : IRepository
IEnumerable<PimsDspChklstItemType> GetAllDispositionChecklistItemTypes();

IEnumerable<PimsDspChklstSectionType> GetAllDispositionChecklistSectionTypes();

IEnumerable<PimsHistoricalFileNumberType> GetAllHistoricalNumberTypes();
}
}
5 changes: 5 additions & 0 deletions source/backend/dal/Repositories/LookupRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ public IEnumerable<PimsDspChklstSectionType> GetAllDispositionChecklistSectionTy
return Context.PimsDspChklstSectionTypes.AsNoTracking().ToArray();
}

public IEnumerable<PimsHistoricalFileNumberType> GetAllHistoricalNumberTypes()
{
return Context.PimsHistoricalFileNumberTypes.AsNoTracking().ToArray();
}

#endregion
}
}
1 change: 1 addition & 0 deletions source/frontend/src/constants/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export const DISPOSITION_INITIATING_BRANCH_TYPES = 'PimsDspInitiatingBranchType'
export const DISPOSITION_TEAM_PROFILE_TYPES = 'PimsDspFlTeamProfileType';
export const DISPOSITION_FUNDING_TYPES = 'PimsDispositionFundingType';
export const DISPOSITION_OFFER_STATUS_TYPES = 'PimsDispositionOfferStatusType';
export const HISTORICAL_NUMBER_TYPES = 'PimsHistoricalFileNumberType';

// TODO: PSP-4395 This should all be removed from this and moved to the useApi* hooks.
// Auth Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ import { server } from '@/mocks/msw/server';
import { getUserMock } from '@/mocks/user.mock';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { prettyFormatUTCDate } from '@/utils';
import { act, cleanup, render, RenderOptions, userEvent, screen } from '@/utils/test-utils';
import { RenderOptions, act, cleanup, render, userEvent } from '@/utils/test-utils';

import DispositionView, { IDispositionViewProps } from './DispositionView';
import { useApiProperties } from '@/hooks/pims-api/useApiProperties';
import { useHistoricalNumberRepository } from '@/hooks/repositories/useHistoricalNumberRepository';
import { useProjectProvider } from '@/hooks/repositories/useProjectProvider';
import { useLtsa } from '@/hooks/useLtsa';
import { ApiGen_Base_Page } from '@/models/api/generated/ApiGen_Base_Page';
import { ApiGen_Concepts_Property } from '@/models/api/generated/ApiGen_Concepts_Property';
import { vi } from 'vitest';
import { useLtsa } from '@/hooks/useLtsa';
import { useProjectProvider } from '@/hooks/repositories/useProjectProvider';
import { createRef } from 'react';
import { HttpResponse, http } from 'msw';
import { useHistoricalNumberRepository } from '@/hooks/repositories/useHistoricalNumberRepository';
import { createRef } from 'react';
import { vi } from 'vitest';
import DispositionView, { IDispositionViewProps } from './DispositionView';

// mock auth library

Expand Down Expand Up @@ -176,6 +176,13 @@ describe('DispositionView component', () => {
loading: false,
status: 200,
},
updatePropertyHistoricalNumbers: {
error: null,
response: [],
execute: vi.fn().mockResolvedValue([]),
loading: false,
status: 200,
},
});

history.replace(`/mapview/sidebar/disposition/1`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { ApiGen_Concepts_DispositionFile } from '@/models/api/generated/ApiGen_C
import { prettyFormatUTCDate } from '@/utils/dateUtils';
import { act, render, RenderOptions } from '@/utils/test-utils';

import DispositionHeader, { IDispositionHeaderProps } from './DispositionHeader';
import { http, HttpResponse } from 'msw';
import { useHistoricalNumberRepository } from '@/hooks/repositories/useHistoricalNumberRepository';
import { http, HttpResponse } from 'msw';
import DispositionHeader, { IDispositionHeaderProps } from './DispositionHeader';

vi.mock('@/hooks/repositories/useHistoricalNumberRepository');
vi.mocked(useHistoricalNumberRepository).mockReturnValue({
Expand All @@ -18,6 +18,13 @@ vi.mocked(useHistoricalNumberRepository).mockReturnValue({
loading: false,
status: 200,
},
updatePropertyHistoricalNumbers: {
error: null,
response: [],
execute: vi.fn().mockResolvedValue([]),
loading: false,
status: 200,
},
});

describe('DispositionHeader component', () => {
Expand Down Expand Up @@ -74,7 +81,10 @@ describe('DispositionHeader component', () => {

it('renders the file number and name concatenated', async () => {
const testDispositionFile = mockDispositionFileResponse();
const { getByText } = await setup({ dispositionFile: testDispositionFile, lastUpdatedBy: null });
const { getByText } = await setup({
dispositionFile: testDispositionFile,
lastUpdatedBy: null,
});

expect(getByText('File:')).toBeVisible();
expect(getByText(/FILE_NUMBER 3A8F46B/)).toBeVisible();
Expand Down Expand Up @@ -108,7 +118,10 @@ describe('DispositionHeader component', () => {
isDisabled: false,
},
};
const { getByText } = await setup({ dispositionFile: testDispositionFile, lastUpdatedBy: null });
const { getByText } = await setup({
dispositionFile: testDispositionFile,
lastUpdatedBy: null,
});

expect(getByText('Status:')).toBeVisible();
expect(getByText(/mock file status/i)).toBeVisible();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { FieldArray, useFormikContext } from 'formik';
import React from 'react';
import { Col, Row } from 'react-bootstrap';

import { LinkButton, RemoveButton } from '@/components/common/buttons';
import { Input, Select } from '@/components/common/form';
import { SectionField } from '@/components/common/Section/SectionField';
import * as API from '@/constants/API';
import useLookupCodeHelpers from '@/hooks/useLookupCodeHelpers';
import { exists } from '@/utils';

import { HistoricalNumberForm, UpdatePropertyDetailsFormModel } from './models';

export interface IUpdateHistoricalNumbersSubFormProps {
propertyId: number;
}

export const UpdateHistoricalNumbersSubForm: React.FC<IUpdateHistoricalNumbersSubFormProps> = ({
propertyId,
}) => {
const { values, setFieldValue } = useFormikContext<UpdatePropertyDetailsFormModel>();
const { getOptionsByType } = useLookupCodeHelpers();

// (sort alpha; exceptions: 1. Other at end, and 2. PS second in list)
// The order is set via displayOrder in the DB
const historicalNumberTypes = getOptionsByType(API.HISTORICAL_NUMBER_TYPES);

return (
<FieldArray
name="historicalNumbers"
render={arrayHelpers => (
<>
{values.historicalNumbers?.map((hn, index) => (
<React.Fragment key={`property-historical-${index}`}>
<Row className="py-3" data-testid={`historical-number-row-${index}`}>
<Col xs="auto" xl="5">
<Input field={`historicalNumbers.${index}.historicalNumber`} />
</Col>
<Col xs="auto" xl="5" className="pl-0">
<Select
data-testid={`select-historical-type-${index}`}
placeholder="Select type..."
field={`historicalNumbers.${index}.historicalNumberType`}
options={historicalNumberTypes}
value={hn.historicalNumberType}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
const selected = e?.target?.value;
if (exists(selected) && selected !== 'OTHER') {
// clear associated field when historical file # type changes
setFieldValue(`historicalNumbers.${index}.otherHistoricalNumberType`, '');
}
}}
/>
</Col>
<Col xs="auto" xl="2" className="pl-0">
<RemoveButton
dataTestId={`historical-number-remove-button-${index}`}
onRemove={() => {
arrayHelpers.remove(index);
}}
/>
</Col>
</Row>
{values.historicalNumbers[index]?.historicalNumberType === 'OTHER' && (
<SectionField label="Describe other" required>
<Input field={`historicalNumbers.${index}.otherHistoricalNumberType`} required />
</SectionField>
)}
</React.Fragment>
))}
<LinkButton
data-testid="add-historical-number"
onClick={() => {
const hn = new HistoricalNumberForm();
hn.propertyId = propertyId;
arrayHelpers.push(hn);
}}
>
+ Add historical file #
</LinkButton>
</>
)}
/>
);
};

export default UpdateHistoricalNumbersSubForm;
Loading

0 comments on commit 5b76e6d

Please sign in to comment.