diff --git a/src/app/core/models/platform/v1/expense.model.ts b/src/app/core/models/platform/v1/expense.model.ts index 35e91f3730..4a8761d02a 100644 --- a/src/app/core/models/platform/v1/expense.model.ts +++ b/src/app/core/models/platform/v1/expense.model.ts @@ -17,6 +17,7 @@ import { ReportState } from '../platform-report.model'; import { Account } from './account.model'; import { CustomFields } from '../custom-fields.model'; import { CustomInput } from '../../custom-input.model'; +import { CommuteDetails } from './commute-details.model'; export interface Expense { // `activity_details` is not added on purpose @@ -109,6 +110,9 @@ export interface Expense { verifier_comments: string[]; report_last_paid_at: Date; report_last_approved_at: Date; + commute_deduction?: 'ONE_WAY' | 'ROUND_TRIP' | 'NO_DEDUCTION'; + commute_details?: CommuteDetails; + commute_details_id?: number; } export interface Employee { diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.html b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.html index 0b5816d3d9..2203bffffc 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.html +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.html @@ -150,6 +150,62 @@ + +
+ + + +
+
{{label}}
+
+ {{distance.toFixed(2) + ' ' + distanceUnit}} +
+
+ {{distance + ' ' + distanceUnit}} +
+
+ + Add Location +
+
+ check +
+
+
+ Select Commute Deduction. +
+
+
; + showCommuteDeductionField = false; + + commuteDetails: CommuteDetails; + + distanceUnit: string; + + commuteDeductionOptions: { label: string; value: string; distance: number }[]; + + initialDistance: number; + + previousCommuteDeductionType: string; + + previousRouteValue: { roundTrip: boolean; mileageLocations: Location[]; distance: number }; + private _isExpandedView = false; constructor( @@ -320,7 +340,9 @@ export class AddEditMileagePage implements OnInit { private categoriesService: CategoriesService, private orgSettingsService: OrgSettingsService, private platformHandlerService: PlatformHandlerService, - private storageService: StorageService + private storageService: StorageService, + private expenseService: ExpensesService, + private employeesService: EmployeesService ) {} get showSaveAndNext(): boolean { @@ -1274,6 +1296,47 @@ export class AddEditMileagePage implements OnInit { ); } + updateDistance(commuteDeductionType: string): void { + console.log('invoked'); + const distance = this.getFormValues().route?.distance; + if (distance > 0 && commuteDeductionType) { + const selectedCommuteDeduction = this.commuteDeductionOptions.find( + (option) => option.value === commuteDeductionType + ); + + if (this.previousCommuteDeductionType) { + // If there is a previous commute deduction type, add previosly deducted distance to the distance + const commuteDeduction = this.commuteDeductionOptions.find( + (option) => option.value === this.previousCommuteDeductionType + ); + this.initialDistance = distance + commuteDeduction.distance; + } else { + // User choosing the commute deduction type for the first time + this.initialDistance = distance; + } + + const commuteDeductedDistance = this.initialDistance - selectedCommuteDeduction.distance; + console.log(commuteDeductedDistance); + if (commuteDeductedDistance < 0) { + this.previousCommuteDeductionType = null; + this.fg.controls.route.patchValue({ distance: 0 }, { emitEvent: false }); + } else { + this.previousCommuteDeductionType = commuteDeductionType; + this.fg.controls.route.patchValue({ distance: 1 }, { emitEvent: false }); + console.log(this.fg.value); + console.log(commuteDeductedDistance); + } + } + } + + getCommuteDeductionOptions(distance: number): { label: string; value: string; distance: number }[] { + return [ + { label: 'One Way Distance', value: 'ONE_WAY', distance: distance || null }, + { label: 'Round Trip Distance', value: 'ROUND_TRIP', distance: distance ? distance * 2 : null }, + { label: 'No Deduction', value: 'NO_DEDUCTION', distance: 0 }, + ]; + } + ionViewWillEnter(): void { this.initClassObservables(); @@ -1285,6 +1348,7 @@ export class AddEditMileagePage implements OnInit { this.expenseStartTime = new Date().getTime(); this.fg = this.fb.group({ mileage_rate_name: [], + commuteDeduction: [], dateOfSpend: [, this.customDateValidator], route: [], paymentMode: [, Validators.required], @@ -1506,6 +1570,7 @@ export class AddEditMileagePage implements OnInit { recentValue: this.recentlyUsedValues$, recentProjects: this.recentlyUsedProjects$, recentCostCenters: this.recentlyUsedCostCenters$, + eou: from(this.authService.getEou()), }) ), take(1), @@ -1527,6 +1592,7 @@ export class AddEditMileagePage implements OnInit { recentValue, recentProjects, recentCostCenters, + eou, }) => { if (project) { this.selectedProject$.next(project); @@ -1560,6 +1626,76 @@ export class AddEditMileagePage implements OnInit { } }); + this.showCommuteDeductionField = + orgSettings.mileage?.allowed && + orgSettings.mileage.enabled && + orgSettings.commute_deduction_settings?.allowed && + orgSettings.commute_deduction_settings.enabled; + console.log('isCommuteenabled', this.showCommuteDeductionField); + + if (this.showCommuteDeductionField) { + this.distanceUnit = orgSettings.mileage?.unit === 'MILES' ? 'Miles' : 'km'; + this.fg.controls.commuteDeduction.valueChanges.subscribe((commuteDeductionType: string) => { + console.log(commuteDeductionType); + this.updateDistance(commuteDeductionType); + }); + + this.fg.controls.route.valueChanges.subscribe( + (route: { roundTrip: boolean; mileageLocations: Location[]; distance: number }) => { + if ( + this.previousRouteValue && + route && + (this.previousRouteValue.roundTrip !== route.roundTrip || + this.previousRouteValue.distance !== route.distance) + ) { + console.log('route invoke hua'); + this.updateDistance(this.getFormValues().commuteDeduction?.value); + this.previousRouteValue = route; + } + } + ); + + if (etxn?.tx?.id) { + /** + * If we are editing an expense, then + * 1. Fetch the expense details + * 2. Take the commute details from the expense, if present. + * 3. Setup the commute deduction field options. + * 4. Select the commute deduction field value, if present. + */ + this.expenseService.getExpenseById(etxn.tx.id).subscribe((expense) => { + this.commuteDetails = expense.commute_details || null; + this.distanceUnit = this.commuteDetails?.distance_unit || this.distanceUnit; + this.distanceUnit = this.distanceUnit === 'MILES' ? 'Miles' : 'km'; + this.commuteDeductionOptions = this.getCommuteDeductionOptions(this.commuteDetails?.distance); + if (expense.commute_deduction) { + this.fg.patchValue( + { + commuteDeduction: expense.commute_deduction, + }, + { emitEvent: false } + ); + } + }); + } else { + /** + * If we are creating a new expense, then + * 1. Fetch the commute details from the platform api. + * 2. Setup the commute deduction field options. + */ + this.employeesService + .getCommuteDetails(eou) + .pipe(map((response) => response?.data?.[0] || null)) + .subscribe((commuteDetailsResponse) => { + this.commuteDetails = commuteDetailsResponse?.commute_details || null; + this.distanceUnit = this.commuteDetails?.distance_unit || this.distanceUnit; + this.distanceUnit = this.distanceUnit === 'MILES' ? 'Miles' : 'km'; + this.commuteDeductionOptions = this.getCommuteDeductionOptions(this.commuteDetails?.distance); + console.log(this.commuteDeductionOptions); + }); + } + } + // Check if auto-fills is enabled const isAutofillsEnabled = orgSettings.org_expense_form_autofills && @@ -2701,4 +2837,34 @@ export class AddEditMileagePage implements OnInit { this.onPageExit$.next(null); this.onPageExit$.complete(); } + + async openCommuteDetailsModal(): Promise { + const commuteDetailsModal = await this.modalController.create({ + component: FySelectCommuteDetailsComponent, + componentProps: { + existingCommuteDetails: this.commuteDetails, + }, + mode: 'ios', + }); + + await commuteDetailsModal.present(); + + const { data } = (await commuteDetailsModal.onWillDismiss()) as OverlayResponse<{ action: string }>; + + // If the user edited or saved the commute details, refresh the page and show the toast message + if (data.action === 'save') { + return from(this.authService.getEou()) + .pipe( + concatMap((eou) => + this.employeesService.getCommuteDetails(eou).pipe(map((response) => response?.data?.[0] || null)) + ) + ) + .subscribe((commuteDetailsResponse) => { + this.commuteDetails = commuteDetailsResponse?.commute_details || null; + this.distanceUnit = this.commuteDetails?.distance_unit || this.distanceUnit; + this.distanceUnit = this.distanceUnit === 'MILES' ? 'Miles' : 'km'; + this.commuteDeductionOptions = this.getCommuteDeductionOptions(this.commuteDetails?.distance); + }); + } + } } diff --git a/src/app/shared/components/fy-select/fy-select.component.ts b/src/app/shared/components/fy-select/fy-select.component.ts index 4be76fab88..bc9c09f90a 100644 --- a/src/app/shared/components/fy-select/fy-select.component.ts +++ b/src/app/shared/components/fy-select/fy-select.component.ts @@ -97,7 +97,15 @@ export class FySelectComponent implements ControlValueAccessor { } async openModal() { - const cssClass = this.label === 'Payment Mode' ? 'payment-mode-modal' : 'fy-modal'; + let cssClass: string; + + if (this.label === 'Payment Mode') { + cssClass = 'payment-mode-modal'; + } else if (this.label === 'Commute Deduction') { + cssClass = 'add-location-modal'; + } else { + cssClass = 'fy-modal'; + } const selectionModal = await this.modalController.create({ component: FySelectModalComponent, diff --git a/src/global.scss b/src/global.scss index 57921d43c8..fe5e121a3a 100644 --- a/src/global.scss +++ b/src/global.scss @@ -1002,6 +1002,15 @@ ion-modal.payment-mode-modal { } } +ion-modal.add-location-modal { + &::part(content) { + border-radius: 16px 16px 0 0 !important; + position: absolute; + max-height: 40%; + bottom: 0; + } +} + .mat-chip.mat-standard-chip { background-color: $pure-white; border: 1px solid $grey-lighter;