Skip to content
Draft

PO-1917 #2287

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6808a35
Adding Minor Creditor account details page
louisbriggs Feb 3, 2026
25d4344
Merge branch 'master' into PO-1924
louisbriggs Feb 3, 2026
633bfb5
Updating link to minor creditor from acc. enq. search results
louisbriggs Feb 4, 2026
0560ae8
Removing unnecessary non-breaking spaces.
louisbriggs Feb 4, 2026
abbce1d
Merge branch 'master' into PO-1924
louisbriggs Feb 4, 2026
ea9c045
Reducing width of content on request payment card confirmation screen.
louisbriggs Feb 4, 2026
e74da33
Merge branch 'master' into PO-1924
louisbriggs Feb 4, 2026
970d47d
Adding logic for individual minor creditor name.
louisbriggs Feb 4, 2026
38f6c53
Moved Banner Messages to a new shared component for all acount summar…
louisbriggs Feb 4, 2026
bb23b08
Moving accoutn summary title and banner to a shared component.
louisbriggs Feb 5, 2026
df1e1f3
Consolidating transformation functions for account header summary
louisbriggs Feb 6, 2026
e522be4
Merge branch 'master' into PO-1924
louisbriggs Feb 6, 2026
8d84e9e
Adding account type specific ids to header fields.
louisbriggs Feb 9, 2026
9f00efc
Using account type specific naming in functions to reduce code duplic…
louisbriggs Feb 9, 2026
1e9df6a
Addressing sonarqube issues.
louisbriggs Feb 9, 2026
cd45432
Merge branch 'master' into PO-1924
louisbriggs Feb 9, 2026
a90cccf
Adding input IDs for account summary header and banner messages. fixi…
louisbriggs Feb 9, 2026
4aae59f
add minor creditor account header tests and intercepts
jonathanDuffy Feb 10, 2026
d55743c
add account button id updated
jonathanDuffy Feb 10, 2026
875c397
finish Minor Creditor Account Header tests
jonathanDuffy Feb 10, 2026
f99595d
Merge branch 'master' into PO-1924
louisbriggs Feb 10, 2026
7f9c618
Adding new param to function call.
louisbriggs Feb 10, 2026
ebfbb6b
Updating moj alert dismissed binding.
louisbriggs Feb 10, 2026
90b94a9
update addNoteButton selectors
jonathanDuffy Feb 11, 2026
cca4770
Merge branch 'master' into PO-1924
jonathanDuffy Feb 11, 2026
b645d1a
Merge branch 'master' into PO-1924
louisbriggs Feb 11, 2026
70338b1
Updating unit tests to use vitest
louisbriggs Feb 11, 2026
aae73e7
Adding at a glance tab to minor creditor account page
louisbriggs Feb 26, 2026
3675537
Merge branch 'master' into PO-1917
louisbriggs Feb 26, 2026
b32b01c
Adding abstract class for account summaries
louisbriggs Feb 26, 2026
d108a2e
Merge branch 'master' into PO-1917
louisbriggs Feb 26, 2026
8a19e61
Merge branch 'master' into PO-1917
louisbriggs Feb 26, 2026
f6248ad
Fixing method ordering issues
louisbriggs Feb 26, 2026
e5f73bb
Merge branch 'master' into PO-1917
louisbriggs Feb 27, 2026
96ca317
Converting components to use abstract class.
louisbriggs Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/constants/fines-permissions.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const FINES_PERMISSIONS: IFinesPermissions = {
'add-account-activity-notes': 8,
'amend-payment-terms': 9,
'enter-enforcement': 10,
'add-remove-payment-hold': 12,
consolidate: 13,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<app-fines-acc-summary-header
[accountStore]="accountStore"
id="defendant"
[hasAddAccountActivityNotePermission]="hasPermission('add-account-activity-notes')"
[hasAddAccountActivityNotePermission]="hasPermission(finesPermissions['add-account-activity-notes'])"
(refreshPage)="refreshPage()"
(navigateToAddAccountNotePage)="navigateToAddAccountNotePage()"
></app-fines-acc-summary-header>
Expand Down Expand Up @@ -149,7 +149,7 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
@if (tabAtAGlance$ | async; as tabData) {
<app-fines-acc-defendant-details-at-a-glance-tab
[tabData]="tabData"
[hasAccountMaintenencePermission]="hasPermission('account-maintenance')"
[hasAccountMaintenencePermission]="hasPermission(finesPermissions['account-maintenance'])"
(addComments)="navigateToAddCommentsPage()"
[style]="tabContentStyles"
></app-fines-acc-defendant-details-at-a-glance-tab>
Expand All @@ -159,7 +159,7 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
@if (tabDefendant$ | async; as tabData) {
<app-fines-acc-defendant-details-defendant-tab
[tabData]="tabData"
[hasAccountMaintenencePermission]="hasPermission('account-maintenance')"
[hasAccountMaintenencePermission]="hasPermission(finesPermissions['account-maintenance'])"
(addComments)="navigateToAddCommentsPage()"
[style]="tabContentStyles"
(changeDefendantDetails)="navigateToAmendPartyDetailsPage($event)"
Expand All @@ -171,7 +171,7 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
@if (tabParentOrGuardian$ | async; as tabData) {
<app-fines-acc-defendant-details-parent-or-guardian-tab
[tabData]="tabData"
[hasAccountMaintenencePermission]="hasPermission('account-maintenance')"
[hasAccountMaintenencePermission]="hasPermission(finesPermissions['account-maintenance'])"
(addComments)="navigateToAddCommentsPage()"
[style]="tabContentStyles"
(changeParentOrGuardianDetails)="navigateToAmendPartyDetailsPage($event)"
Expand All @@ -191,7 +191,7 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
@if (tabPaymentTerms$ | async; as tabData) {
<app-fines-acc-defendant-details-payment-terms-tab
[tabData]="tabData"
[hasAmendPaymentTermsPermission]="hasPermission('amend-payment-terms')"
[hasAmendPaymentTermsPermission]="hasPermission(finesPermissions['amend-payment-terms'])"
[style]="tabContentStyles"
(changePaymentTerms)="navigateToAmendPaymentTermsPage()"
(requestPaymentCard)="navigateToRequestPaymentCardPage()"
Expand All @@ -203,8 +203,8 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
<app-fines-acc-defendant-details-enforcement-tab
[tabData]="tabData"
[isCompanyAccount]="accountData.party_details.organisation_flag"
[hasAccountMaintenancePermission]="hasPermission('account-maintenance')"
[hasEnterEnforcementPermission]="hasPermission('enter-enforcement')"
[hasAccountMaintenancePermission]="hasPermission(finesPermissions['account-maintenance'])"
[hasEnterEnforcementPermission]="hasPermission(finesPermissions['enter-enforcement'])"
></app-fines-acc-defendant-details-enforcement-tab>
}
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IFinesAccMinorCreditorAccountTabsCacheMap } from '../interfaces/fines-acc-minor-creditor-account-tabs-cache-map.interface';

export const FINES_ACC_MINOR_CREDITOR_ACCOUNT_TABS_CACHE_MAP: IFinesAccMinorCreditorAccountTabsCacheMap = {
'at-a-glance': 'minorCreditorAccountAtAGlanceCache$',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<div class="govuk-grid-column-full">
<div class="govuk-grid-row">
<div class="govuk-grid-column-one-third">
<h2 [class]="style.heading">Minor creditor</h2>
<hr [class]="style.hr" />

@if (tabData.party.organisation_flag) {
<h3 [class]="style.key">Company Name</h3>
<p [class]="style.value">
{{ tabData.party.organisation_details?.organisation_name }}
</p>

@if (tabData.party.organisation_details?.organisation_aliases) {
<h3 [class]="style.key">Aliases</h3>
<p [class]="style.value">
@for (alias of tabData.party.organisation_details?.organisation_aliases; track alias) {
{{ alias.organisation_name }}<br />
}
</p>
}
} @else {
<h3 [class]="style.key">Name</h3>
<p [class]="style.value">
{{ tabData.party.individual_details?.title }}
{{ tabData.party.individual_details?.forenames }}
{{ tabData.party.individual_details?.surname | uppercase }}
</p>

@if (tabData.party.individual_details?.individual_aliases) {
<h3 [class]="style.key">Aliases</h3>
<p [class]="style.value">
@for (alias of tabData.party.individual_details?.individual_aliases; track alias) {
{{ alias.forenames }} {{ alias.surname | uppercase }}<br />
}
</p>
}

@if (tabData.party.individual_details && tabData.party.individual_details.date_of_birth) {
<h3 [class]="style.key">Date of birth</h3>
<p [class]="style.value">
{{ tabData.party.individual_details.date_of_birth | dateFormat: 'dd/MM/yyyy' : 'dd MMMM yyyy' }}
</p>
}
}

<h3 [class]="style.key">Address</h3>
<p [class]="style.value">
{{ tabData.address.address_line_1 }}
@if (tabData.address.address_line_2) {
<br />
{{ tabData.address.address_line_2 }}
}
@if (tabData.address.address_line_3) {
<br />
{{ tabData.address.address_line_3 }}
}
@if (tabData.address.address_line_4) {
<br />
{{ tabData.address.address_line_4 }}
}
@if (tabData.address.address_line_5) {
<br />
{{ tabData.address.address_line_5 }}
}
<br />
{{ tabData.address.postcode | uppercase }}
</p>

@if (!tabData.party.organisation_flag) {
<h3 [class]="style.key">National Insurance Number</h3>
<p [class]="style.value">
@if (tabData.party.individual_details?.national_insurance_number) {
{{ tabData.party.individual_details?.national_insurance_number! | nationalInsurance }}
} @else {
<app-fines-not-provided></app-fines-not-provided>
}
</p>
}
</div>

@if (tabData.defendant) {
<div class="govuk-grid-column-one-third">
<h2 [class]="style.heading">Defendant account</h2>
<hr [class]="style.hr" />

<h3 [class]="style.key">Defendant account</h3>
<p [class]="style.value">{{ tabData.defendant.account_number }}</p>

<h3 [class]="style.key">Defendant Name</h3>
<p [class]="style.value">{{ tabData.defendant.name }}</p>

<h3 [class]="style.key">Hearing Date</h3>
<p [class]="style.value">{{ tabData.defendant.hearing_date | dateFormat: 'dd/MM/yyyy' : 'dd MMMM yyyy' }}</p>
</div>
}

<div class="govuk-grid-column-one-third">
<h2 [class]="style.heading">Payout status</h2>
<hr [class]="style.hr" />
@if (tabData.payment.is_bacs) {
<opal-lib-moj-badge badgeId="is_bacs-badge" badgeClasses="moj-badge--blue govuk-!-margin-bottom-2"
>Provided</opal-lib-moj-badge
>
} @else {
<opal-lib-moj-badge badgeId="is_bacs-badge" badgeClasses="moj-badge--red govuk-!-margin-bottom-2"
>Not provided</opal-lib-moj-badge
>
}

@if (hasAddRemovePaymentHoldPermission) {
@if (tabData.payment.hold_payment) {
<p [class]="style.value">
<a
tabindex="0"
class="govuk-link govuk-link--no-visited-state"
(click)="handleRemovePaymentHold()"
(keyup.enter)="handleRemovePaymentHold()"
(keyup.space)="handleRemovePaymentHold()"
role="button"
>Remove payment hold</a
>
</p>
} @else {
<p [class]="style.value">
<a
tabindex="0"
class="govuk-link govuk-link--no-visited-state"
(click)="handleAddPaymentHold()"
(keyup.enter)="handleAddPaymentHold()"
(keyup.space)="handleAddPaymentHold()"
role="button"
>Add payment hold</a
>
</p>
}
}
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { FinesAccMinorCreditorDetailsAtAGlanceTabComponent } from './fines-acc-minor-creditor-details-at-a-glance-tab.component';
import { OPAL_FINES_ACCOUNT_MINOR_CREDITOR_AT_A_GLANCE_WITH_DEFENDANT_MOCK } from '@app/flows/fines/services/opal-fines-service/mocks/opal-fines-account-minor-creditor-at-a-glance-with-defendant.mock';
import { beforeEach, describe, expect, it, vi } from 'vitest';

describe('FinesAccMinorCreditorDetailsAtAGlanceTabComponent', () => {
let component: FinesAccMinorCreditorDetailsAtAGlanceTabComponent;
let fixture: ComponentFixture<FinesAccMinorCreditorDetailsAtAGlanceTabComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FinesAccMinorCreditorDetailsAtAGlanceTabComponent],
}).compileComponents();

fixture = TestBed.createComponent(FinesAccMinorCreditorDetailsAtAGlanceTabComponent);
component = fixture.componentInstance;
component.tabData = OPAL_FINES_ACCOUNT_MINOR_CREDITOR_AT_A_GLANCE_WITH_DEFENDANT_MOCK;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should emit addPaymentHold event when handleAddPaymentHold is called', () => {
vi.spyOn(component.addPaymentHold, 'emit');
component.handleAddPaymentHold();
expect(component.addPaymentHold.emit).toHaveBeenCalled();
});

it('should emit removePaymentHold event when handleRemovePaymentHold is called', () => {
vi.spyOn(component.removePaymentHold, 'emit');
component.handleRemovePaymentHold();
expect(component.removePaymentHold.emit).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { UpperCasePipe } from '@angular/common';
import { MojBadgeComponent } from '@hmcts/opal-frontend-common/components/moj/moj-badge';
import { FINES_MAC_LANGUAGE_PREFERENCES_OPTIONS } from '../../../fines-mac/fines-mac-language-preferences/constants/fines-mac-language-preferences-options';
import { IFinesAccSummaryTabsContentStyles } from '../../fines-acc-defendant-details/interfaces/fines-acc-summary-tabs-content-styles.interface';
import { FINES_ACC_SUMMARY_TABS_CONTENT_STYLES } from '../../constants/fines-acc-summary-tabs-content-styles.constant';
import { FINES_ACC_DEBTOR_TYPES } from '../../constants/fines-acc-debtor-types.constant';
import { NationalInsurancePipe } from '@hmcts/opal-frontend-common/pipes/national-insurance';
import { FinesNotProvidedComponent } from '../../../components/fines-not-provided/fines-not-provided.component';
import { DateFormatPipe } from '@hmcts/opal-frontend-common/pipes/date-format';
import { IOpalFinesAccountMinorCreditorAtAGlance } from '@app/flows/fines/services/opal-fines-service/interfaces/opal-fines-account-minor-creditor-at-a-glance.interface';
@Component({
selector: 'app-fines-acc-minor-creditor-details-at-a-glance-tab',
imports: [UpperCasePipe, MojBadgeComponent, NationalInsurancePipe, FinesNotProvidedComponent, DateFormatPipe],
templateUrl: './fines-acc-minor-creditor-details-at-a-glance-tab.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FinesAccMinorCreditorDetailsAtAGlanceTabComponent {
@Input({ required: true }) tabData!: IOpalFinesAccountMinorCreditorAtAGlance;
@Input() hasAddRemovePaymentHoldPermission: boolean = false;
@Input() style: IFinesAccSummaryTabsContentStyles = FINES_ACC_SUMMARY_TABS_CONTENT_STYLES;
@Output() addPaymentHold = new EventEmitter<void>();
@Output() removePaymentHold = new EventEmitter<void>();
public readonly languages = FINES_MAC_LANGUAGE_PREFERENCES_OPTIONS;
public readonly debtorTypes = FINES_ACC_DEBTOR_TYPES;

/**
* Emits an event to indicate that the user wants to add a payment hold.
*/
public handleAddPaymentHold(): void {
this.addPaymentHold.emit();
}

/**
* Emits an event to indicate that the user wants to remove a payment hold.
*/
public handleRemovePaymentHold(): void {
this.removePaymentHold.emit();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<app-fines-acc-summary-header
[accountStore]="accountStore"
id="minor-creditor"
[hasAddAccountActivityNotePermission]="hasPermission('add-account-activity-notes')"
[hasAddAccountActivityNotePermission]="hasPermission(finesPermissions['add-account-activity-notes'])"
(refreshPage)="refreshPage()"
(navigateToAddAccountNotePage)="navigateToAddAccountNotePage()"
></app-fines-acc-summary-header>
Expand Down Expand Up @@ -71,3 +71,17 @@ <h2 opal-lib-custom-account-information-item-label>Business Unit:</h2>
></li>
</opal-lib-moj-sub-navigation>
</div>

@switch (activeTab) {
@case ('at-a-glance') {
@if (tabAtAGlance$ | async; as tabData) {
<app-fines-acc-minor-creditor-details-at-a-glance-tab
[tabData]="tabData"
[hasAddRemovePaymentHoldPermission]="hasPermission(finesPermissions['add-remove-payment-hold'])"
(addPaymentHold)="navigateToAddPaymentHoldPage()"
(removePaymentHold)="navigateToRemovePaymentHoldPage()"
[style]="tabContentStyles"
></app-fines-acc-minor-creditor-details-at-a-glance-tab>
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ import { FinesAccPayloadService } from '../services/fines-acc-payload.service';
import { MOCK_FINES_ACCOUNT_STATE } from '../mocks/fines-acc-state.mock';
import { FINES_ACC_MINOR_CREDITOR_ROUTING_PATHS } from '../routing/constants/fines-acc-minor-creditor-routing-paths.constant';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { OPAL_FINES_ACCOUNT_MINOR_CREDITOR_AT_A_GLANCE_WITH_DEFENDANT_MOCK } from '../../services/opal-fines-service/mocks/opal-fines-account-minor-creditor-at-a-glance-with-defendant.mock';

describe('FinesAccMinorCreditorDetailsComponent', () => {
let component: FinesAccMinorCreditorDetailsComponent;
let fixture: ComponentFixture<FinesAccMinorCreditorDetailsComponent>;
let routerSpy: Pick<Router, 'navigate'>;
let activatedRouteStub: Partial<ActivatedRoute>;
let mockOpalFinesService: Pick<OpalFines, 'getMinorCreditorAccountHeadingData' | 'clearCache' | 'getResult'>;
let mockOpalFinesService: Pick<
OpalFines,
'getMinorCreditorAccountHeadingData' | 'getMinorCreditorAccountAtAGlance' | 'clearCache' | 'getResult'
>;
let mockPayloadService: Pick<FinesAccPayloadService, 'transformAccountHeaderForStore' | 'transformPayload'>;

beforeEach(async () => {
Expand Down Expand Up @@ -49,6 +53,9 @@ describe('FinesAccMinorCreditorDetailsComponent', () => {
.mockReturnValue(of(structuredClone(FINES_ACC_MINOR_CREDITOR_DETAILS_HEADER_MOCK))),
clearCache: vi.fn(),
getResult: vi.fn(),
getMinorCreditorAccountAtAGlance: vi
.fn()
.mockReturnValue(of(structuredClone(OPAL_FINES_ACCOUNT_MINOR_CREDITOR_AT_A_GLANCE_WITH_DEFENDANT_MOCK))),
};

await TestBed.configureTestingModule({
Expand Down Expand Up @@ -113,4 +120,26 @@ describe('FinesAccMinorCreditorDetailsComponent', () => {
relativeTo: component['activatedRoute'],
});
});

it('should call router.navigate when navigateToAddPaymentHoldPage is called', () => {
vi.spyOn(component['permissionsService'], 'hasBusinessUnitPermissionAccess').mockReturnValue(true);
component.navigateToAddPaymentHoldPage();
expect(routerSpy.navigate).toHaveBeenCalledWith(
[`../${FINES_ACC_MINOR_CREDITOR_ROUTING_PATHS.children['payment-hold']}/add`],
{
relativeTo: component['activatedRoute'],
},
);
});

it('should call router.navigate when navigateToRemovePaymentHoldPage is called', () => {
vi.spyOn(component['permissionsService'], 'hasBusinessUnitPermissionAccess').mockReturnValue(true);
component.navigateToRemovePaymentHoldPage();
expect(routerSpy.navigate).toHaveBeenCalledWith(
[`../${FINES_ACC_MINOR_CREDITOR_ROUTING_PATHS.children['payment-hold']}/remove`],
{
relativeTo: component['activatedRoute'],
},
);
});
});
Loading