Merge branch 'dev' into angular20

This commit is contained in:
Felix van Hove
2025-12-08 11:05:39 +01:00
committed by GitHub
9 changed files with 66 additions and 7 deletions
@@ -1,7 +1,7 @@
<div class="layout-column gap-2px"> <div class="layout-column gap-2px">
<h1 mat-dialog-title align="center">{{ 'labels.heading.Capture Client Image' | translate }}</h1> <h1 mat-dialog-title align="center">{{ 'labels.heading.Capture Client Image' | translate }}</h1>
<video #video width="540" height="480" autoplay></video> <video #video class="capture-video" autoplay></video>
<!-- Using a class will break renderer changes --> <!-- Using a class will break renderer changes -->
<canvas #canvas [ngStyle]="{ display: 'none' }"></canvas> <canvas #canvas [ngStyle]="{ display: 'none' }"></canvas>
@@ -0,0 +1,10 @@
@use '../../../../../assets/styles/colours' as colors;
.capture-video {
width: 100%;
max-width: 640px;
height: auto;
object-fit: contain;
border-radius: 4px;
background-color: colors.$black;
}
@@ -54,8 +54,13 @@ export class CaptureImageDialogComponent implements AfterViewInit, OnDestroy {
*/ */
startCamera() { startCamera() {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const videoConstraints: MediaTrackConstraints = {
width: { ideal: 640 },
height: { ideal: 480 },
facingMode: 'user'
};
navigator.mediaDevices navigator.mediaDevices
.getUserMedia({ video: true }) .getUserMedia({ video: videoConstraints })
.then((stream: MediaStream) => { .then((stream: MediaStream) => {
this.renderer.setProperty(this.video.nativeElement, 'srcObject', stream); this.renderer.setProperty(this.video.nativeElement, 'srcObject', stream);
this.video.nativeElement.play(); this.video.nativeElement.play();
@@ -95,13 +100,26 @@ export class CaptureImageDialogComponent implements AfterViewInit, OnDestroy {
/** /**
* Captures the image by drawing video state on canvas, then converts canvas state to data URL. * Captures the image by drawing video state on canvas, then converts canvas state to data URL.
* Uses actual video dimensions to prevent aspect ratio distortion.
* See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL for details. * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL for details.
*/ */
capture() { capture() {
this.isCaptured = true; this.isCaptured = true;
this.video.nativeElement.pause(); this.video.nativeElement.pause();
this.canvas.nativeElement.getContext('2d').drawImage(this.video.nativeElement, 0, 0, 150, 150);
this.clientImageDataURL = this.canvas.nativeElement.toDataURL(); // Get actual video stream dimensions to preserve aspect ratio
const videoWidth = this.video.nativeElement.videoWidth;
const videoHeight = this.video.nativeElement.videoHeight;
// Set canvas dimensions to match video stream
this.canvas.nativeElement.width = videoWidth;
this.canvas.nativeElement.height = videoHeight;
// Draw the video frame at full resolution without distortion
this.canvas.nativeElement.getContext('2d').drawImage(this.video.nativeElement, 0, 0, videoWidth, videoHeight);
// Convert to data URL with JPEG format for smaller file size
this.clientImageDataURL = this.canvas.nativeElement.toDataURL('image/jpeg', 0.9);
} }
/** /**
@@ -255,6 +255,12 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges {
this.loansAccountTermsForm.addControl('enableDownPayment', new UntypedFormControl(enableDownPayment)); this.loansAccountTermsForm.addControl('enableDownPayment', new UntypedFormControl(enableDownPayment));
} }
if (this.isFullTermTrancheEditable()) {
const allowFullTermForTranche =
this.loansAccountTermsData.allowFullTermForTranche ?? this.loanProduct?.allowFullTermForTranche ?? false;
this.loansAccountTermsForm.patchValue({ allowFullTermForTranche });
}
const allowAttributeOverrides = this.loansAccountTermsData.product.allowAttributeOverrides; const allowAttributeOverrides = this.loansAccountTermsData.product.allowAttributeOverrides;
if (!allowAttributeOverrides.repaymentEvery) { if (!allowAttributeOverrides.repaymentEvery) {
this.loansAccountTermsForm.controls.repaymentEvery.disable(); this.loansAccountTermsForm.controls.repaymentEvery.disable();
@@ -517,7 +523,8 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges {
multiDisburseLoan: [false], multiDisburseLoan: [false],
interestRateFrequencyType: [''], interestRateFrequencyType: [''],
balloonRepaymentAmount: [''], balloonRepaymentAmount: [''],
interestRecognitionOnDisbursementDate: [false] interestRecognitionOnDisbursementDate: [false],
allowFullTermForTranche: [false]
}); });
} }
@@ -699,4 +706,12 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges {
collateral: this.collateralDataSource collateral: this.collateralDataSource
}; };
} }
/**
* Check if full term tranche option should be editable at loan level.
* Only available if the loan product has it enabled and is PROGRESSIVE schedule type.
*/
isFullTermTrancheEditable(): boolean {
return this.isProgressive && this.loanProduct?.allowFullTermForTranche === true;
}
} }
@@ -4,6 +4,11 @@
<span class="flex-60">{{ loanDetails.maxOutstandingLoanBalance | formatNumber }}</span> <span class="flex-60">{{ loanDetails.maxOutstandingLoanBalance | formatNumber }}</span>
</div> </div>
<div class="flex-fill" *ngIf="loanDetails.allowFullTermForTranche">
<span class="flex-40">{{ 'labels.inputs.Allow full term for each tranche' | translate }}:</span>
<span class="flex-60">{{ loanDetails.allowFullTermForTranche | yesNo }}</span>
</div>
<h3>{{ 'labels.heading.Loan Tranche Details' | translate }}</h3> <h3>{{ 'labels.heading.Loan Tranche Details' | translate }}</h3>
<div class="layout-row margin-t layout-xs-column layout-align-end gap-2percent"> <div class="layout-row margin-t layout-xs-column layout-align-end gap-2percent">
@@ -27,6 +27,7 @@ import {
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
import { DateFormatPipe } from '../../../pipes/date-format.pipe'; import { DateFormatPipe } from '../../../pipes/date-format.pipe';
import { FormatNumberPipe } from '../../../pipes/format-number.pipe'; import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
import { YesnoPipe } from '../../../pipes/yesno.pipe';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({ @Component({
@@ -49,7 +50,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
MatRowDef, MatRowDef,
MatRow, MatRow,
DateFormatPipe, DateFormatPipe,
FormatNumberPipe FormatNumberPipe,
YesnoPipe
] ]
}) })
export class LoanTrancheDetailsComponent implements OnInit { export class LoanTrancheDetailsComponent implements OnInit {
@@ -146,6 +146,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
multiDisburseLoan: this.loanProductsTemplate.multiDisburseLoan, multiDisburseLoan: this.loanProductsTemplate.multiDisburseLoan,
maxTrancheCount: this.loanProductsTemplate.maxTrancheCount, maxTrancheCount: this.loanProductsTemplate.maxTrancheCount,
outstandingLoanBalance: this.loanProductsTemplate.outstandingLoanBalance, outstandingLoanBalance: this.loanProductsTemplate.outstandingLoanBalance,
allowFullTermForTranche: this.loanProductsTemplate.allowFullTermForTranche,
enableDownPayment: this.loanProductsTemplate.enableDownPayment, enableDownPayment: this.loanProductsTemplate.enableDownPayment,
enableInstallmentLevelDelinquency: this.loanProductsTemplate.enableInstallmentLevelDelinquency, enableInstallmentLevelDelinquency: this.loanProductsTemplate.enableInstallmentLevelDelinquency,
loanScheduleType: this.loanProductsTemplate.loanScheduleType.code, loanScheduleType: this.loanProductsTemplate.loanScheduleType.code,
@@ -314,6 +315,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
isInterestRecalculationEnabled: [false], isInterestRecalculationEnabled: [false],
holdGuaranteeFunds: [false], holdGuaranteeFunds: [false],
multiDisburseLoan: [false], multiDisburseLoan: [false],
allowFullTermForTranche: [false],
allowAttributeConfiguration: [true], allowAttributeConfiguration: [true],
allowPartialPeriodInterestCalculation: [false], allowPartialPeriodInterestCalculation: [false],
allowAttributeOverrides: this.formBuilder.group({ allowAttributeOverrides: this.formBuilder.group({
@@ -553,7 +555,10 @@ export class LoanProductSettingsStepComponent implements OnInit {
} else { } else {
this.loanProductSettingsForm.removeControl('maxTrancheCount'); this.loanProductSettingsForm.removeControl('maxTrancheCount');
this.loanProductSettingsForm.removeControl('outstandingLoanBalance'); this.loanProductSettingsForm.removeControl('outstandingLoanBalance');
this.loanProductSettingsForm.patchValue({ disallowExpectedDisbursements: false }); this.loanProductSettingsForm.patchValue({
disallowExpectedDisbursements: false,
allowFullTermForTranche: false
});
} }
}); });
@@ -647,6 +652,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
this.advancedTransactionProcessingStrategyDisabled = false; this.advancedTransactionProcessingStrategyDisabled = false;
this.isAdvancedTransactionProcessingStrategy = false; this.isAdvancedTransactionProcessingStrategy = false;
this.loanProductSettingsForm.removeControl('chargeOffBehaviour'); this.loanProductSettingsForm.removeControl('chargeOffBehaviour');
this.loanProductSettingsForm.patchValue({ allowFullTermForTranche: false });
} else { } else {
// Only Advanced Payment Allocation Strategy // Only Advanced Payment Allocation Strategy
this.transactionProcessingStrategyDataBase.some((cn: CodeName) => { this.transactionProcessingStrategyDataBase.some((cn: CodeName) => {
@@ -97,6 +97,7 @@ export interface LoanProduct {
creditAllocationAllocationTypes: OptionData[]; creditAllocationAllocationTypes: OptionData[];
multiDisburseLoan: boolean; multiDisburseLoan: boolean;
maxTrancheCount: number; maxTrancheCount: number;
allowFullTermForTranche: boolean;
disallowExpectedDisbursements: boolean; disallowExpectedDisbursements: boolean;
allowApprovedDisbursedAmountsOverApplied: boolean; allowApprovedDisbursedAmountsOverApplied: boolean;
overAppliedNumber: number; overAppliedNumber: number;
+2
View File
@@ -1599,6 +1599,7 @@
"Enable Dormancy Tracking": "Enable Dormancy Tracking", "Enable Dormancy Tracking": "Enable Dormancy Tracking",
"Enable Down Payment": "Enable Down Payment", "Enable Down Payment": "Enable Down Payment",
"Enable Multiple Disbursals": "Enable Multiple Disbursals", "Enable Multiple Disbursals": "Enable Multiple Disbursals",
"Allow full term for each tranche": "Allow full term for each tranche",
"Enabled": "Enabled", "Enabled": "Enabled",
"End Date": "End Date", "End Date": "End Date",
"End Point": "End Point", "End Point": "End Point",
@@ -3566,6 +3567,7 @@
"Keyboard Shortcuts": "Keyboard Shortcuts", "Keyboard Shortcuts": "Keyboard Shortcuts",
"Leave this checkbox checked if the loan has Down Payment": "Leave this checkbox checked if the loan has Down Payment, A Down Payment is a sum a buyer pays upfront when purchasing a good. It represents a percentage of the total purchase price, and the balance is usually financed", "Leave this checkbox checked if the loan has Down Payment": "Leave this checkbox checked if the loan has Down Payment, A Down Payment is a sum a buyer pays upfront when purchasing a good. It represents a percentage of the total purchase price, and the balance is usually financed",
"Leave this checkbox unchecked": "Leave this checkbox unchecked if the loan is a single disburse loan. Check this checkbox if the loan is a multi disburse loan. See additional fields for additional information required for this type of loan.", "Leave this checkbox unchecked": "Leave this checkbox unchecked if the loan is a single disburse loan. Check this checkbox if the loan is a multi disburse loan. See additional fields for additional information required for this type of loan.",
"Allow full term length for each tranche disbursement": "When enabled, each tranche disbursement will follow the full loan term schedule instead of fitting within the remaining term. Only available for PROGRESSIVE loan schedule type.",
"Loan Account": "Loan Account", "Loan Account": "Loan Account",
"Loan Product can be used to apply for Topup Loans": "If selected, the Loan Product can be used to apply for Top Up Loans.", "Loan Product can be used to apply for Topup Loans": "If selected, the Loan Product can be used to apply for Top Up Loans.",
"Loan products may be assigned": "Loan products may be assigned to a fund set up by your financial institution. If available, the fund field can be used for tracking and reporting on groups of loans.", "Loan products may be assigned": "Loan products may be assigned to a fund set up by your financial institution. If available, the fund field can be used for tracking and reporting on groups of loans.",