diff --git a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.html b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.html index 1848744bd..913a8e7bb 100644 --- a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.html +++ b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.html @@ -1,7 +1,7 @@

{{ 'labels.heading.Capture Client Image' | translate }}

- + diff --git a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.scss b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.scss index e69de29bb..613b35c1b 100644 --- a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.scss +++ b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.scss @@ -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; +} diff --git a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.ts b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.ts index 6f3cbf994..480435fbb 100644 --- a/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.ts +++ b/src/app/clients/clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component.ts @@ -54,8 +54,13 @@ export class CaptureImageDialogComponent implements AfterViewInit, OnDestroy { */ startCamera() { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + const videoConstraints: MediaTrackConstraints = { + width: { ideal: 640 }, + height: { ideal: 480 }, + facingMode: 'user' + }; navigator.mediaDevices - .getUserMedia({ video: true }) + .getUserMedia({ video: videoConstraints }) .then((stream: MediaStream) => { this.renderer.setProperty(this.video.nativeElement, 'srcObject', stream); 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. + * Uses actual video dimensions to prevent aspect ratio distortion. * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL for details. */ capture() { this.isCaptured = true; 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); } /** diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts index 7c42a0b1b..cf5ffab06 100644 --- a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts @@ -255,6 +255,12 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges { 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; if (!allowAttributeOverrides.repaymentEvery) { this.loansAccountTermsForm.controls.repaymentEvery.disable(); @@ -517,7 +523,8 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges { multiDisburseLoan: [false], interestRateFrequencyType: [''], balloonRepaymentAmount: [''], - interestRecognitionOnDisbursementDate: [false] + interestRecognitionOnDisbursementDate: [false], + allowFullTermForTranche: [false] }); } @@ -699,4 +706,12 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges { 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; + } } diff --git a/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.html b/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.html index aec252581..3a05bed23 100644 --- a/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.html +++ b/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.html @@ -4,6 +4,11 @@ {{ loanDetails.maxOutstandingLoanBalance | formatNumber }}
+
+ {{ 'labels.inputs.Allow full term for each tranche' | translate }}: + {{ loanDetails.allowFullTermForTranche | yesNo }} +
+

{{ 'labels.heading.Loan Tranche Details' | translate }}

diff --git a/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.ts b/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.ts index cea55e50b..136ada550 100644 --- a/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.ts +++ b/src/app/loans/loans-view/loan-tranche-details/loan-tranche-details.component.ts @@ -27,6 +27,7 @@ import { import { MatTooltip } from '@angular/material/tooltip'; import { DateFormatPipe } from '../../../pipes/date-format.pipe'; import { FormatNumberPipe } from '../../../pipes/format-number.pipe'; +import { YesnoPipe } from '../../../pipes/yesno.pipe'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @Component({ @@ -49,7 +50,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatRowDef, MatRow, DateFormatPipe, - FormatNumberPipe + FormatNumberPipe, + YesnoPipe ] }) export class LoanTrancheDetailsComponent implements OnInit { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts index 65378474b..c52374d9d 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts @@ -146,6 +146,7 @@ export class LoanProductSettingsStepComponent implements OnInit { multiDisburseLoan: this.loanProductsTemplate.multiDisburseLoan, maxTrancheCount: this.loanProductsTemplate.maxTrancheCount, outstandingLoanBalance: this.loanProductsTemplate.outstandingLoanBalance, + allowFullTermForTranche: this.loanProductsTemplate.allowFullTermForTranche, enableDownPayment: this.loanProductsTemplate.enableDownPayment, enableInstallmentLevelDelinquency: this.loanProductsTemplate.enableInstallmentLevelDelinquency, loanScheduleType: this.loanProductsTemplate.loanScheduleType.code, @@ -314,6 +315,7 @@ export class LoanProductSettingsStepComponent implements OnInit { isInterestRecalculationEnabled: [false], holdGuaranteeFunds: [false], multiDisburseLoan: [false], + allowFullTermForTranche: [false], allowAttributeConfiguration: [true], allowPartialPeriodInterestCalculation: [false], allowAttributeOverrides: this.formBuilder.group({ @@ -553,7 +555,10 @@ export class LoanProductSettingsStepComponent implements OnInit { } else { this.loanProductSettingsForm.removeControl('maxTrancheCount'); 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.isAdvancedTransactionProcessingStrategy = false; this.loanProductSettingsForm.removeControl('chargeOffBehaviour'); + this.loanProductSettingsForm.patchValue({ allowFullTermForTranche: false }); } else { // Only Advanced Payment Allocation Strategy this.transactionProcessingStrategyDataBase.some((cn: CodeName) => { diff --git a/src/app/products/loan-products/models/loan-product.model.ts b/src/app/products/loan-products/models/loan-product.model.ts index 9ad7c3c83..d66039b64 100644 --- a/src/app/products/loan-products/models/loan-product.model.ts +++ b/src/app/products/loan-products/models/loan-product.model.ts @@ -97,6 +97,7 @@ export interface LoanProduct { creditAllocationAllocationTypes: OptionData[]; multiDisburseLoan: boolean; maxTrancheCount: number; + allowFullTermForTranche: boolean; disallowExpectedDisbursements: boolean; allowApprovedDisbursedAmountsOverApplied: boolean; overAppliedNumber: number; diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json index 65dc2d853..33ee0a706 100644 --- a/src/assets/translations/en-US.json +++ b/src/assets/translations/en-US.json @@ -1599,6 +1599,7 @@ "Enable Dormancy Tracking": "Enable Dormancy Tracking", "Enable Down Payment": "Enable Down Payment", "Enable Multiple Disbursals": "Enable Multiple Disbursals", + "Allow full term for each tranche": "Allow full term for each tranche", "Enabled": "Enabled", "End Date": "End Date", "End Point": "End Point", @@ -3566,6 +3567,7 @@ "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 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 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.",