Merge branch 'dev' into angular20
This commit is contained in:
+1
-1
@@ -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>
|
||||||
|
|||||||
+10
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
+21
-3
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+16
-1
@@ -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 {
|
||||||
|
|||||||
+7
-1
@@ -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;
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
Reference in New Issue
Block a user