diff --git a/src/app/core/models/intacct/intacct-configuration/import-settings.model.ts b/src/app/core/models/intacct/intacct-configuration/import-settings.model.ts index 11da3dfda3..ccd407e185 100644 --- a/src/app/core/models/intacct/intacct-configuration/import-settings.model.ts +++ b/src/app/core/models/intacct/intacct-configuration/import-settings.model.ts @@ -9,7 +9,8 @@ const emptyDestinationAttribute = { id: null, name: null }; export type Configuration = { import_vendors_as_merchants: boolean, import_categories: boolean, - import_tax_codes: boolean + import_tax_codes: boolean, + import_code_fields: string[] } export type ImportSettingGeneralMapping = { @@ -93,11 +94,22 @@ export class ImportSettings { isCategoryImportEnabled = filteredExpenseFieldArray.filter((field: MappingSetting) => field.source_field === 'CATEGORY' && field.import_to_fyle).length > 0 ? true : false; } + // Import_code_field value construction + const importCodeFields = importSettingsForm.value.importCodeFields; + + const importCodeFieldArray = importCodeFields.filter((field: { import_code: any; }) => field.import_code).map((value: { source_field: any; }) => { + return value.source_field; + }); + + const finalimportCodeFieldArray: string[] = importSettingsForm.value.importCodeField.filter((value: string) => value!=='COST_CODE' && value !== 'COST_TYPE').concat(importCodeFieldArray); + + // Actual Payload const importSettingPayload: ImportSettingPost = { configurations: { import_categories: isCategoryImportEnabled, import_tax_codes: importSettingsForm.value.importTaxCodes ? importSettingsForm.value.importTaxCodes : false, - import_vendors_as_merchants: importSettingsForm.value.importVendorAsMerchant ? importSettingsForm.value.importVendorAsMerchant : false + import_vendors_as_merchants: importSettingsForm.value.importVendorAsMerchant ? importSettingsForm.value.importVendorAsMerchant : false, + import_code_fields: finalimportCodeFieldArray }, general_mappings: { default_tax_code: importSettingsForm.value.importTaxCodes ? { diff --git a/src/app/core/services/common/mapping.service.ts b/src/app/core/services/common/mapping.service.ts index 3288a0e400..120567da84 100644 --- a/src/app/core/services/common/mapping.service.ts +++ b/src/app/core/services/common/mapping.service.ts @@ -180,7 +180,7 @@ export class MappingService { }; if (value) { - if (appName && ([AppName.SAGE300, AppName.QBO] as string[]).includes(appName)) { + if (appName && ([AppName.SAGE300, AppName.QBO, AppName.INTACCT] as string[]).includes(appName)) { params.value = value; } else { params.value__icontains = value; diff --git a/src/app/core/services/si/si-configuration/si-import-setting.service.ts b/src/app/core/services/si/si-configuration/si-import-setting.service.ts index 8a9ffc402a..03ec0eab8b 100644 --- a/src/app/core/services/si/si-configuration/si-import-setting.service.ts +++ b/src/app/core/services/si/si-configuration/si-import-setting.service.ts @@ -22,4 +22,9 @@ export class SiImportSettingService { postImportSettings(importSettingsPayload: ImportSettingPost): Observable { return this.apiService.put(`/v2/workspaces/${this.workspaceService.getWorkspaceId()}/import_settings/`, importSettingsPayload); } + + getImportCodeFieldConfig() { + const workspaceId = this.workspaceService.getWorkspaceId(); + return this.apiService.get(`/v2/workspaces/${workspaceId}/import_settings/import_code_fields_config/`, {}); + } } diff --git a/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.html b/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.html index 3ffe41ac85..7e8b365270 100644 --- a/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.html +++ b/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.html @@ -7,5 +7,6 @@ [showAutoMapEmployee]="showAutoMapEmployee" [appName]="appName" [isCategoryMappingGeneric]="false" + [isMultiLineOption]="acceptedCodeField.includes(destinationField) && brandingConfig.brandId !== 'co' ? true : false" (triggerAutoMapEmployee)="triggerAutoMapEmployees()"> diff --git a/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.ts b/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.ts index 09fa641565..47a3a58413 100644 --- a/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.ts +++ b/src/app/integrations/intacct/intacct-main/intacct-mapping/intacct-base-mapping/intacct-base-mapping.component.ts @@ -1,10 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { forkJoin } from 'rxjs'; +import { brandingConfig } from 'src/app/branding/branding-config'; import { IntacctConfiguration } from 'src/app/core/models/db/configuration.model'; import { DestinationAttribute } from 'src/app/core/models/db/destination-attribute.model'; import { MappingSetting } from 'src/app/core/models/db/mapping-setting.model'; -import { AccountingDisplayName, AccountingField, AppName, FyleField, IntacctCategoryDestination, IntacctCorporateCreditCardExpensesObject, IntacctReimbursableExpensesObject, ToastSeverity } from 'src/app/core/models/enum/enum.model'; +import { AccountingDisplayName, AccountingField, AppName, FyleField, IntacctCategoryDestination, IntacctCorporateCreditCardExpensesObject, IntacctReimbursableExpensesObject, MappingSourceField, SageIntacctField, ToastSeverity } from 'src/app/core/models/enum/enum.model'; import { IntegrationsToastService } from 'src/app/core/services/common/integrations-toast.service'; import { MappingService } from 'src/app/core/services/common/mapping.service'; import { WorkspaceService } from 'src/app/core/services/common/workspace.service'; @@ -37,6 +38,10 @@ export class IntacctBaseMappingComponent implements OnInit { cccExpenseObject: IntacctCorporateCreditCardExpensesObject | null; + acceptedCodeField: string[] = [SageIntacctField.ACCOUNT, SageIntacctField.DEPARTMENT, MappingSourceField.PROJECT]; + + brandingConfig = brandingConfig; + constructor( private route: ActivatedRoute, private mappingService: MappingService, diff --git a/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.html b/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.html index 47fb2890a3..5430063729 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.html +++ b/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.html @@ -167,12 +167,23 @@
- +

- {{ item.value }} + {{ value?.code }}: + {{ value?.value }}

+ +
+ + {{options.value}} + +

{{ options.code ? options.code : '--'}}

+
+
diff --git a/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.ts b/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.ts index a1353c1229..b7c3c75c33 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.ts +++ b/src/app/integrations/intacct/intacct-shared/intacct-advanced-settings/intacct-advanced-settings.component.ts @@ -19,6 +19,7 @@ import { environment } from 'src/environments/environment'; import { AdvancedSettingsModel } from 'src/app/core/models/common/advanced-settings.model'; import { SkipExportComponent } from 'src/app/shared/components/si/helper/skip-export/skip-export.component'; import { SelectFormOption } from 'src/app/core/models/common/select-form-option.model'; +import { DestinationAttribute } from 'src/app/core/models/db/destination-attribute.model'; @Component({ selector: 'app-intacct-advanced-settings', @@ -132,6 +133,10 @@ export class IntacctAdvancedSettingsComponent implements OnInit { this.router.navigate([`/integrations/intacct/onboarding/import_settings`]); } + isOverflowing(element: any, mapping: DestinationAttribute): string { + return element.offsetWidth < element.scrollWidth ? mapping.value : ''; + } + refreshDimensions(isRefresh: boolean) { this.mappingService.refreshSageIntacctDimensions().subscribe(); this.mappingService.refreshFyleDimensions().subscribe(); diff --git a/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.html b/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.html index 6d67535e8f..367bc3c322 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.html +++ b/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.html @@ -52,6 +52,7 @@ [placeholder]="'Select GL account'" [formControllerName]="'glAccount'" [isAdvanceSearchEnabled]="true" + [isMultiLineOption]="isMultiLineOption" (searchOptionsDropdown)="searchOptionsDropdown($event)">
@@ -163,6 +164,7 @@ [placeholder]="'Select expense payment type'" [formControllerName]="'cccExpensePaymentType'" [isAdvanceSearchEnabled]="true" + [isMultiLineOption]="isMultiLineOption" (searchOptionsDropdown)="searchOptionsDropdown($event)">
@@ -195,6 +197,7 @@ [placeholder]="'Select GL account'" [formControllerName]="'creditCard'" [isAdvanceSearchEnabled]="true" + [isMultiLineOption]="isMultiLineOption" (searchOptionsDropdown)="searchOptionsDropdown($event)">
diff --git a/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.ts b/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.ts index de3649a05b..f2906b9807 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.ts +++ b/src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.ts @@ -154,6 +154,8 @@ export class IntacctExportSettingsComponent implements OnInit { brandingContent = brandingContent; + isMultiLineOption: boolean; + constructor( private router: Router, private exportSettingService: SiExportSettingService, @@ -649,6 +651,7 @@ export class IntacctExportSettingsComponent implements OnInit { this.destinationOptions.CCC_EXPENSE_PAYMENT_TYPE = response[1].results.filter((attr: IntacctDestinationAttribute) => !attr.detail.is_reimbursable); this.destinationOptions.VENDOR = response[2].results; this.destinationOptions.CHARGE_CARD = response[3].results; + this.isMultiLineOption = brandingConfig.brandId !== 'co' ? true : false; this.getSettingsAndSetupForm(); }); diff --git a/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.html b/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.html index ee4bf93545..4533382266 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.html +++ b/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.html @@ -43,7 +43,7 @@
- +
@@ -132,7 +132,7 @@
+

{{brandingConfig.brandName}} Fields should be unique

@@ -195,6 +195,48 @@
+
+
+
+

+ Configure import method +

+

+ You have the option to import either just the names of your values or both names and code, based on how your employees identify these values. +

+
+
+ + +
+
+
+
+
+
+ + +
+
+
+ + diff --git a/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.ts b/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.ts index a5001e776c..2dd704db71 100644 --- a/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.ts +++ b/src/app/integrations/intacct/intacct-shared/intacct-import-settings/intacct-import-settings.component.ts @@ -2,14 +2,17 @@ import { Component, Inject, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { RxwebValidators } from '@rxweb/reactive-form-validators'; +import { InputSwitchChangeEvent } from 'primeng/inputswitch'; import { forkJoin } from 'rxjs'; -import { brandingConfig, brandingKbArticles } from 'src/app/branding/branding-config'; -import { IntacctCategoryDestination, ConfigurationCta, IntacctOnboardingState, IntacctUpdateEvent, Page, ProgressPhase, ToastSeverity, MappingSourceField, AppName, TrackingApp } from 'src/app/core/models/enum/enum.model'; +import { brandingConfig, brandingFeatureConfig, brandingKbArticles } from 'src/app/branding/branding-config'; +import { ImportSettingsModel } from 'src/app/core/models/common/import-settings.model'; +import { IntacctCategoryDestination, ConfigurationCta, IntacctOnboardingState, IntacctUpdateEvent, Page, ProgressPhase, ToastSeverity, MappingSourceField, AppName, TrackingApp, DefaultImportFields, SageIntacctField } from 'src/app/core/models/enum/enum.model'; import { IntacctDestinationAttribute } from 'src/app/core/models/intacct/db/destination-attribute.model'; import { ExpenseField } from 'src/app/core/models/intacct/db/expense-field.model'; import { LocationEntityMapping } from 'src/app/core/models/intacct/db/location-entity-mapping.model'; import { DependentFieldSetting, ImportSettingGet, ImportSettingPost, ImportSettings, MappingSetting } from 'src/app/core/models/intacct/intacct-configuration/import-settings.model'; import { Org } from 'src/app/core/models/org/org.model'; +import { HelperService } from 'src/app/core/services/common/helper.service'; import { IntegrationsToastService } from 'src/app/core/services/common/integrations-toast.service'; import { StorageService } from 'src/app/core/services/common/storage.service'; import { TrackingService } from 'src/app/core/services/integration/tracking.service'; @@ -87,10 +90,69 @@ export class IntacctImportSettingsComponent implements OnInit { isDialogVisible: boolean = false; + importCodeField: FormGroup[] = []; + + acceptedImportCodeField: string[] = [SageIntacctField.ACCOUNT, SageIntacctField.DEPARTMENT, MappingSourceField.PROJECT, IntacctCategoryDestination.EXPENSE_TYPE]; + existingFields: string[] = ['employee id', 'organisation name', 'employee name', 'employee email', 'expense date', 'expense date', 'expense id', 'report id', 'employee id', 'department', 'state', 'reporter', 'report', 'purpose', 'vendor', 'category', 'category code', 'mileage distance', 'mileage unit', 'flight from city', 'flight to city', 'flight from date', 'flight to date', 'flight from class', 'flight to class', 'hotel checkin', 'hotel checkout', 'hotel location', 'hotel breakfast', 'currency', 'amount', 'foreign currency', 'foreign amount', 'tax', 'approver', 'project', 'billable', 'cost center', 'cost center code', 'approved on', 'reimbursable', 'receipts', 'paid date', 'expense created date']; readonly brandingConfig = brandingConfig; + readonly brandingFeatureConfig = brandingFeatureConfig; + + intacctImportCodeConfig: any; + + importCodeSelectorOptions: Record = { + "ACCOUNT": [ + { + label: 'Import Codes + Names', + value: true, + subLabel: 'Example: 4567: Meals & Entertainment' + }, + { + label: 'Import Names only', + value: false, + subLabel: 'Example: Meals & Entertainment' + } + ], + "EXPENSE_TYPE": [ + { + label: 'Import Codes + Names', + value: true, + subLabel: 'Example: 4567: Meals & Entertainment' + }, + { + label: 'Import Names only', + value: false, + subLabel: 'Example: Meals & Entertainment' + } + ], + "DEPARTMENT": [ + { + label: 'Import Codes + Names', + value: true, + subLabel: 'Example: 24: Finance' + }, + { + label: 'Import Names only', + value: false, + subLabel: 'Example: Finance' + } + ], + "PROJECT": [ + { + label: 'Import Codes + Names', + value: true, + subLabel: 'Example: 12-00-201: PCL Construction' + }, + { + label: 'Import Names only', + value: false, + subLabel: 'Example: PCL Construction' + } + ] + }; + constructor( private router: Router, private mappingService: SiMappingsService, @@ -101,13 +163,62 @@ export class IntacctImportSettingsComponent implements OnInit { private toastService: IntegrationsToastService, private trackingService: TrackingService, private storageService: StorageService, - private workspaceService: SiWorkspaceService + private workspaceService: SiWorkspaceService, + public helper: HelperService ) { } get expenseFieldsGetter() { return this.importSettingsForm.get('expenseFields') as FormArray; } + get importCodeFieldGetter() { + return this.importSettingsForm.get('importCodeFields') as FormArray; + } + + addImportCodeField(event: any, sourceField: string) { + // Get the reference to the FormArray from the form + const importCodeFieldsArray = this.importSettingsForm.get('importCodeFields') as FormArray; + + if (sourceField === IntacctCategoryDestination.GL_ACCOUNT) { + sourceField = IntacctCategoryDestination.ACCOUNT; + } + + if (event.checked && this.acceptedImportCodeField.includes(sourceField)) { + // Create a new FormGroup + const value = this.formBuilder.group({ + source_field: [sourceField], + import_code: [ImportSettingsModel.getImportCodeField(this.importSettings.configurations.import_code_fields, sourceField, this.intacctImportCodeConfig), Validators.required] + }); + + // Push the new FormGroup into the FormArray + importCodeFieldsArray.push(value); + } else { + // Find the index of the FormGroup to be removed + const index = importCodeFieldsArray.controls.findIndex(control => control?.get('source_field')?.value === sourceField); + + // If found, remove the FormGroup from the FormArray + if (index !== -1) { + importCodeFieldsArray.removeAt(index); + } + } + } + + getFormGroup(control: AbstractControl): FormGroup { + return control as FormGroup; + } + + getDestinationField(destinationField: string): string { + const lastChar = destinationField.slice(-1).toLowerCase(); + const lastTwoChars = destinationField.slice(-2).toLowerCase(); + + if (lastChar === 'y') { + return destinationField.slice(0, -1) + 'ies'; + } else if (['s', 'x', 'z'].includes(lastChar) || ['sh', 'ch'].includes(lastTwoChars)) { + return destinationField + 'es'; + } + return destinationField + 's'; + } + refreshDimensions(isRefresh: boolean) { this.mappingService.refreshSageIntacctDimensions().subscribe(); this.mappingService.refreshFyleDimensions().subscribe(); @@ -115,6 +226,9 @@ export class IntacctImportSettingsComponent implements OnInit { } removeFilter(expenseField: AbstractControl) { + if ((expenseField as FormGroup).controls.import_to_fyle.value) { + this.addImportCodeField({checked: false}, (expenseField as FormGroup).controls.destination_field.value); + } (expenseField as FormGroup).controls.source_field.patchValue(''); (expenseField as FormGroup).controls.import_to_fyle.patchValue(false); event?.stopPropagation(); @@ -418,6 +532,7 @@ export class IntacctImportSettingsComponent implements OnInit { } private initializeForm(importSettings: ImportSettingGet): void { + const expenseFieldFormArray: FormGroup[] = []; this.importSettingsForm = this.formBuilder.group({ importVendorAsMerchant: [importSettings.configurations.import_vendors_as_merchants || null], importCategories: [importSettings.configurations.import_categories || null], @@ -429,12 +544,18 @@ export class IntacctImportSettingsComponent implements OnInit { isDependentImportEnabled: [importSettings.dependent_field_settings?.is_import_enabled || false], sageIntacctTaxCodes: [(this.sageIntacctTaxGroup?.find(taxGroup => taxGroup.destination_id === this.importSettings?.general_mappings?.default_tax_code?.id)) || null, importSettings.configurations.import_tax_codes ? [Validators.required] : []], expenseFields: this.formBuilder.array(this.constructFormArray()), - searchOption: [''] + searchOption: [''], + importCodeField: [importSettings.configurations.import_code_fields], + importCodeFields: this.formBuilder.array(this.importCodeField) }); if (this.importSettingsForm.controls.costCodes.value && this.importSettingsForm.controls.costTypes.value && this.dependentFieldSettings?.is_import_enabled) { this.fyleFields = this.fyleFields.filter(field => !field.is_dependent); } + if (this.importSettings.configurations.import_code_fields.length > 0 && !this.importSettings.configurations.import_code_fields.includes(this.intacctCategoryDestination) && this.intacctImportCodeConfig[this.intacctCategoryDestination] && this.importSettings.configurations.import_categories) { + this.addImportCodeField({checked: true}, this.intacctCategoryDestination); + } + // Disable toggle for expense fields that are dependent const expenseFields = this.importSettingsForm.get('expenseFields') as FormArray; @@ -461,6 +582,7 @@ export class IntacctImportSettingsComponent implements OnInit { const importSettingsObservable = this.importSettingService.getImportSettings(); const configuration = this.mappingService.getConfiguration(); const locationEntity = this.connectorService.getLocationEntityMapping(); + const importCodeFieldConfig = this.importSettingService.getImportCodeFieldConfig(); forkJoin([ sageIntacctFieldsObservable, @@ -468,9 +590,10 @@ export class IntacctImportSettingsComponent implements OnInit { groupedAttributesObservable, importSettingsObservable, configuration, - locationEntity + locationEntity, + importCodeFieldConfig ]).subscribe( - ([sageIntacctFields, fyleFields, groupedAttributesResponse, importSettings, configuration, locationEntity]) => { + ([sageIntacctFields, fyleFields, groupedAttributesResponse, importSettings, configuration, locationEntity, importCodeFieldConfig]) => { this.dependentFieldSettings = importSettings.dependent_field_settings; this.isImportTaxVisible = this.showImportTax(locationEntity); this.sageIntacctFields = sageIntacctFields.map(field => { @@ -486,6 +609,8 @@ export class IntacctImportSettingsComponent implements OnInit { const mappingSettings: MappingSetting[] = this.importSettings.mapping_settings; + this.intacctImportCodeConfig = importCodeFieldConfig; + for (const setting of mappingSettings) { const { source_field, destination_field, import_to_fyle } = setting; if (source_field === 'PROJECT' && destination_field === 'PROJECT' && import_to_fyle === true) { @@ -514,6 +639,7 @@ export class IntacctImportSettingsComponent implements OnInit { } else { this.intacctCategoryDestination = IntacctCategoryDestination.GL_ACCOUNT; } + this.initializeForm(importSettings); } ); @@ -547,6 +673,9 @@ export class IntacctImportSettingsComponent implements OnInit { if (!event.checked && formGroup.value.source_field === MappingSourceField.PROJECT && this.costCodeFieldOption[0].attribute_type !== 'custom_field' && this.costTypeFieldOption[0].attribute_type !== 'custom_field') { this.showDependentFieldWarning = true; } + if (formGroup.value.source_field) { + this.addImportCodeField(event, formGroup.value.destination_field); + } } save(): void { diff --git a/src/app/shared/components/si/helper/skip-export/skip-export.component.ts b/src/app/shared/components/si/helper/skip-export/skip-export.component.ts index dd2678166b..840c5b055e 100644 --- a/src/app/shared/components/si/helper/skip-export/skip-export.component.ts +++ b/src/app/shared/components/si/helper/skip-export/skip-export.component.ts @@ -309,7 +309,7 @@ export class SkipExportComponent implements OnInit { } const valueField = this.skipExportForm.getRawValue(); - if (this.showAddButton && this.expenseFilters.length > 1) { + if (this.showAddButton && this.expenseFilters?.length > 1) { this.advancedSettingsService .deleteExpenseFilter(this.expenseFilters[1].rank) .subscribe((skipExport1: SkipExport) => {