diff --git a/src/app/core/mock-data/expense-field.data.ts b/src/app/core/mock-data/expense-field.data.ts index 7f514e0bdc..30ddbb76b8 100644 --- a/src/app/core/mock-data/expense-field.data.ts +++ b/src/app/core/mock-data/expense-field.data.ts @@ -4,6 +4,28 @@ import { PlatformApiResponse } from '../models/platform/platform-api-response.mo import { PlatformExpenseField } from '../models/platform/platform-expense-field.model'; import { ExpenseField } from '../models/v1/expense-field.model'; +export const mockExpenseData: ExpenseField[] = deepFreeze([ + { + code: null, + column_name: 'boolean_column2', + created_at: new Date('2024-11-03T12:12:33.265861+00:00'), + default_value: null, + field_name: 'testttt', + id: 1259223, + is_custom: true, + is_enabled: false, // Disabled field + is_mandatory: false, + options: [], + org_category_ids: [147791], + org_id: 'orsO0VW86WLQ', + placeholder: 'testttt', + seq: 1, + type: 'BOOLEAN', + updated_at: new Date('2024-11-03T12:53:23.450820+00:00'), + parent_field_id: null, + }, +]); + export const expenseFieldResponse: ExpenseField[] = deepFreeze([ { id: 2, diff --git a/src/app/core/services/custom-inputs.service.spec.ts b/src/app/core/services/custom-inputs.service.spec.ts index 541d7882dd..806f2b2620 100644 --- a/src/app/core/services/custom-inputs.service.spec.ts +++ b/src/app/core/services/custom-inputs.service.spec.ts @@ -14,6 +14,8 @@ import { } from '../test-data/custom-inputs.spec.data'; import { CustomInputsService } from './custom-inputs.service'; import { expensesWithDependentFields } from '../mock-data/dependent-field-expenses.data'; +import { CustomInput } from '../models/custom-input.model'; +import { mockExpenseData } from '../mock-data/expense-field.data'; describe('CustomInputsService', () => { let customInputsService: CustomInputsService; @@ -281,10 +283,31 @@ describe('CustomInputsService', () => { it('should fill custom properties', (done) => { authService.getEou.and.resolveTo(authRespone); spenderPlatformV1ApiService.get.and.returnValue(of(platformApiResponse)); - const result = customInputsService.fillCustomProperties(orgCatId, customProperties, false); + const result = customInputsService.fillCustomProperties(orgCatId, customProperties); result.subscribe((res) => { expect(res).toEqual(filledCustomProperties); done(); }); }); + + it('should append "(Deleted)" to field name when custom input is disabled', (done) => { + const orgCategoryId = 147791; + + const customProperties: CustomInput[] = []; + + // Mock getAllinView method to return an observable with the mock data + spyOn(customInputsService, 'getAll').and.returnValue(of(mockExpenseData)); + + // Mock filterByCategory to return the mock object as the filtered result + spyOn(customInputsService, 'filterByCategory').and.callFake((inputs, id) => { + return inputs.filter((input) => input.org_category_ids.includes(id as number)); + }); + + // Call fillCustomProperties and verify results + customInputsService.fillCustomProperties(orgCategoryId, customProperties).subscribe((result) => { + expect(result.length).toBe(1); + expect(result[0].name).toBe('testttt (Deleted)'); // Check for "(Deleted)" + done(); + }); + }); }); diff --git a/src/app/core/services/custom-inputs.service.ts b/src/app/core/services/custom-inputs.service.ts index 23feaa7311..635b574228 100644 --- a/src/app/core/services/custom-inputs.service.ts +++ b/src/app/core/services/custom-inputs.service.ts @@ -12,6 +12,7 @@ import { PlatformApiResponse } from '../models/platform/platform-api-response.mo import { PlatformExpenseField } from '../models/platform/platform-expense-field.model'; import { ExpenseFieldsService } from './expense-fields.service'; import { CustomInput } from '../models/custom-input.model'; + const customInputssCacheBuster$ = new Subject(); @Injectable({ @@ -29,14 +30,14 @@ export class CustomInputsService { @Cacheable({ cacheBusterObserver: customInputssCacheBuster$, }) - getAll(active: boolean): Observable { + getAll(active?: boolean): Observable { return from(this.authService.getEou()).pipe( switchMap((eou) => this.spenderPlatformV1ApiService.get>('/expense_fields', { params: { org_id: `eq.${eou.ou.org_id}`, - is_enabled: `eq.${active}`, is_custom: 'eq.true', + ...(active !== undefined && { is_enabled: `eq.${active}` }), // Only add is_enabled if active is specified }, }) ), @@ -62,62 +63,81 @@ export class CustomInputsService { return 0; } - fillCustomProperties( - orgCategoryId: number, - customProperties: Partial[], - active: boolean - ): Observable { - return this.getAll(active).pipe( + fillCustomProperties(orgCategoryId: number, customProperties: Partial[]): Observable { + return this.getAll().pipe( + // Call getAll without any arguments map((allCustomInputs) => allCustomInputs.filter((customInput) => customInput.type !== 'DEPENDENT_SELECT')), map((allCustomInputs) => { const customInputs = this.filterByCategory(allCustomInputs, orgCategoryId); - // this should be by rank eventually + // Sort custom inputs by rank customInputs.sort(this.sortByRank); const filledCustomProperties: CustomField[] = []; - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let i = 0; i < customInputs.length; i++) { - const customInput = customInputs[i]; + + // Iterate through custom inputs and process each one + for (const customInput of customInputs) { + const fieldName = + customInput.is_enabled === false ? `${customInput.field_name} (Deleted)` : customInput.field_name; + + // Initialize the property object const property = { - name: customInput.field_name, + name: fieldName, value: null, type: customInput.type, mandatory: customInput.is_mandatory, options: customInput.options, }; - // defaults for types + + // Set default values based on the custom input type if (customInput.type === 'BOOLEAN') { property.value = false; } - - this.setSelectMultiselectValue(customInput, property); - if (customInput.type === 'USER_LIST') { property.value = []; } + // Handle select/multiselect values + this.setSelectMultiselectValue(customInput, property); + + // Check if a value exists in `customProperties` and assign it if (customProperties) { - // see if value is available - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let j = 0; j < customProperties.length; j++) { - if (customProperties[j].name === customInput.field_name) { - this.setCustomPropertyValue(property, customProperties, j); - break; - } + const matchingCustomPropertyIndex = customProperties.findIndex((cp) => cp.name === customInput.field_name); + if (matchingCustomPropertyIndex >= 0) { + this.setCustomPropertyValue(property, customProperties, matchingCustomPropertyIndex); } } - filledCustomProperties.push({ - ...property, - displayValue: this.getCustomPropertyDisplayValue(property), - }); - } + // Add the property to filledCustomProperties based on new logic + if (this.shouldIncludeProperty(customInput, property)) { + filledCustomProperties.push({ + ...property, + displayValue: this.getCustomPropertyDisplayValue(property), + }); + } + } return filledCustomProperties; }) ); } + /** + * Determines if a custom property should be included based on its enabled status and value. + * @param customInput - The custom input field. + * @param property - The custom field property. + * @returns A boolean indicating whether the property should be included. + */ + + shouldIncludeProperty(customInput: ExpenseField, property: CustomField): boolean { + return ( + customInput.is_enabled || + (!customInput.is_enabled && + property.value !== null && + property.value !== undefined && + this.getCustomPropertyDisplayValue(property) !== '-') + ); + } + setCustomPropertyValue(property: CustomField, customProperties: Partial[], index: number): void { if (property.type === 'DATE' && customProperties[index].value) { property.value = new Date(customProperties[index].value); diff --git a/src/app/fyle/view-expense/view-expense.page.spec.ts b/src/app/fyle/view-expense/view-expense.page.spec.ts index 5be6eaf745..1c22b3961f 100644 --- a/src/app/fyle/view-expense/view-expense.page.spec.ts +++ b/src/app/fyle/view-expense/view-expense.page.spec.ts @@ -538,8 +538,7 @@ describe('ViewExpensePage', () => { expect(customProperties).toEqual(filledCustomProperties); expect(customInputsService.fillCustomProperties).toHaveBeenCalledOnceWith( expenseData.category_id, - expenseData.custom_fields as Partial[], - true + expenseData.custom_fields as Partial[] ); done(); }); diff --git a/src/app/fyle/view-expense/view-expense.page.ts b/src/app/fyle/view-expense/view-expense.page.ts index 464451fc7f..60ff7682ea 100644 --- a/src/app/fyle/view-expense/view-expense.page.ts +++ b/src/app/fyle/view-expense/view-expense.page.ts @@ -286,8 +286,7 @@ export class ViewExpensePage { concatMap((expense) => this.customInputsService.fillCustomProperties( expense.category_id, - expense.custom_fields as Partial[], - true + expense.custom_fields as Partial[] ) ), shareReplay(1) diff --git a/src/app/fyle/view-mileage/view-mileage.page.spec.ts b/src/app/fyle/view-mileage/view-mileage.page.spec.ts index 92fcb8d3df..623c775292 100644 --- a/src/app/fyle/view-mileage/view-mileage.page.spec.ts +++ b/src/app/fyle/view-mileage/view-mileage.page.spec.ts @@ -713,8 +713,7 @@ describe('ViewMileagePage', () => { expect(data).toEqual(mockfilledCustomProperties); expect(customInputsService.fillCustomProperties).toHaveBeenCalledOnceWith( mileageExpense.category_id, - mileageExpense.custom_fields as Partial[], - true + mileageExpense.custom_fields as Partial[] ); expect(customInputsService.getCustomPropertyDisplayValue).toHaveBeenCalledTimes( mockfilledCustomProperties.length diff --git a/src/app/fyle/view-mileage/view-mileage.page.ts b/src/app/fyle/view-mileage/view-mileage.page.ts index 94e857f70f..d8348cb9ec 100644 --- a/src/app/fyle/view-mileage/view-mileage.page.ts +++ b/src/app/fyle/view-mileage/view-mileage.page.ts @@ -378,8 +378,7 @@ export class ViewMileagePage { switchMap((expense) => this.customInputsService.fillCustomProperties( expense.category_id, - expense.custom_fields as Partial[], - true + expense.custom_fields as Partial[] ) ), map((customProperties) => diff --git a/src/app/fyle/view-per-diem/view-per-diem.page.spec.ts b/src/app/fyle/view-per-diem/view-per-diem.page.spec.ts index b654f7303d..35a6a73dd5 100644 --- a/src/app/fyle/view-per-diem/view-per-diem.page.spec.ts +++ b/src/app/fyle/view-per-diem/view-per-diem.page.spec.ts @@ -464,8 +464,7 @@ describe('ViewPerDiemPage', () => { component.perDiemCustomFields$.subscribe((perDiemCustomFields) => { expect(customInputsService.fillCustomProperties).toHaveBeenCalledOnceWith( perDiemExpense.category_id, - perDiemExpense.custom_fields as Partial[], - true + perDiemExpense.custom_fields as Partial[] ); // Called twice because of the two custom fields expect(customInputsService.getCustomPropertyDisplayValue).toHaveBeenCalledTimes(2); diff --git a/src/app/fyle/view-per-diem/view-per-diem.page.ts b/src/app/fyle/view-per-diem/view-per-diem.page.ts index e0f833c75e..50afaea0e5 100644 --- a/src/app/fyle/view-per-diem/view-per-diem.page.ts +++ b/src/app/fyle/view-per-diem/view-per-diem.page.ts @@ -259,8 +259,7 @@ export class ViewPerDiemPage { switchMap((expense) => this.customInputsService.fillCustomProperties( expense.category_id, - expense.custom_fields as Partial[], - true + expense.custom_fields as Partial[] ) ), map((customProperties) =>