Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: qbd direct team testing fixes #1095

Merged
merged 5 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class QbdDirectAdvancedSettingsModel extends AdvancedSettingsModel {
}

static defaultTopMemoOptions(): string[] {
return ["employee_name", "Expense/Report ID"];
return ["employee_name", "expense_key"];
}

static topMemoExpenseKeyNameConversion(keys: string[]): string[] {
Expand All @@ -52,7 +52,7 @@ export class QbdDirectAdvancedSettingsModel extends AdvancedSettingsModel {

return new FormGroup({
expenseMemoStructure: new FormControl(advancedSettings?.line_level_memo_structure && advancedSettings?.line_level_memo_structure.length > 0 ? this.formatMemoStructure(this.defaultMemoFields(), advancedSettings?.line_level_memo_structure) : this.defaultMemoFields(), Validators.required),
topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? this.topMemoExpenseKeyNameConversion(advancedSettings?.top_level_memo_structure) : this.defaultTopMemoOptions(), Validators.required),
topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? advancedSettings?.top_level_memo_structure : this.defaultTopMemoOptions(), Validators.required),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix static method using instance reference.

The static analysis tool correctly identified use of this in a static context.

-topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? advancedSettings?.top_level_memo_structure : this.defaultTopMemoOptions(), Validators.required),
+topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? advancedSettings?.top_level_memo_structure : QbdDirectAdvancedSettingsModel.defaultTopMemoOptions(), Validators.required),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? advancedSettings?.top_level_memo_structure : this.defaultTopMemoOptions(), Validators.required),
topMemoStructure: new FormControl(advancedSettings?.top_level_memo_structure && advancedSettings?.top_level_memo_structure.length > 0 ? advancedSettings?.top_level_memo_structure : QbdDirectAdvancedSettingsModel.defaultTopMemoOptions(), Validators.required),
🧰 Tools
🪛 Biome (1.9.4)

[error] 55-55: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

exportSchedule: new FormControl(advancedSettings?.schedule_is_enabled ? advancedSettings?.schedule_is_enabled : false),
email: new FormControl(advancedSettings?.emails_selected ? advancedSettings?.emails_selected : null),
exportScheduleFrequency: new FormControl(advancedSettings?.schedule_is_enabled ? advancedSettings?.interval_hours : 1),
Expand All @@ -65,11 +65,6 @@ export class QbdDirectAdvancedSettingsModel extends AdvancedSettingsModel {

static constructPayload (advancedSettingForm: FormGroup, adminEmails: EmailOption[]): QbdDirectAdvancedSettingsPost {

const topMemo: string[] = advancedSettingForm.controls.topMemoStructure.value;

const index = topMemo.indexOf('Expense/Report ID');
topMemo[index] = 'expense_key';

const allSelectedEmails: EmailOption[] = advancedSettingForm.get('email')?.value;

const selectedEmailsEmails = allSelectedEmails?.filter((email: EmailOption) => adminEmails.includes(email));
Expand All @@ -80,7 +75,7 @@ export class QbdDirectAdvancedSettingsModel extends AdvancedSettingsModel {

const advancedSettingPayload: QbdDirectAdvancedSettingsPost = {
line_level_memo_structure: advancedSettingForm.get('expenseMemoStructure')?.value ? this.formatMemoStructure(this.defaultMemoFields(), memo) : [],
top_level_memo_structure: advancedSettingForm.get('topMemoStructure')?.value ? topMemo : null,
top_level_memo_structure: advancedSettingForm.get('topMemoStructure')?.value ? advancedSettingForm.get('topMemoStructure')?.value : null,
schedule_is_enabled: advancedSettingForm.get('exportSchedule')?.value ? advancedSettingForm.get('exportSchedule')?.value : false,
emails_selected: advancedSettingForm.get('exportSchedule')?.value ? selectedEmailsEmails : [],
interval_hours: advancedSettingForm.get('exportSchedule')?.value ? advancedSettingForm.get('exportScheduleFrequency')?.value : null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
[options]="reimbursableExpenseGroupingFieldOptions"
[iconPath]="'question-square-outline'"
[placeholder]="'Select expense grouping'"
[isDisableTextRequired]="isReimbursableExportGroupDisabled()"
[isDisabled]="isReimbursableExportGroupDisabled()"
[formControllerName]="'reimbursableExportGroup'">
</app-configuration-select-field>
</div>
Expand Down Expand Up @@ -239,7 +241,7 @@
[iconPath]="'question-square-outline'"
[placeholder]="'Select expense grouping'"
[isDisableTextRequired]="true"
[isDisabled]="exportSettingsForm.get('creditCardExportType')?.value === QBDCorporateCreditCardExpensesObject.CREDIT_CARD_PURCHASE || exportSettingsForm.get('creditCardExportType')?.value === QBDCorporateCreditCardExpensesObject.JOURNAL_ENTRY ? true : false"
[isDisabled]="isCccExportGroupDisabled()"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Inconsistency in disable text behavior between credit card and reimbursable export groups

The code inspection reveals that the reimbursable export group dynamically sets both isDisabled and isDisableTextRequired based on isReimbursableExportGroupDisabled(), while the credit card export group has isDisableTextRequired hardcoded to true. This inconsistency should be fixed to maintain a uniform behavior:

-                        [isDisableTextRequired]="true"
+                        [isDisableTextRequired]="isCccExportGroupDisabled()"

The implementation of both disable methods shows they handle different but valid business cases:

  • isCccExportGroupDisabled() - disables when credit card type is "CREDIT_CARD_PURCHASE" or when using an Accounts Payable account
  • isReimbursableExportGroupDisabled() - disables for Accounts Payable accounts with Journal Entry export type
🔗 Analysis chain

Verify consistency between credit card and reimbursable export group controls

The change to use isCccExportGroupDisabled() aligns with the pattern used for reimbursable exports. However, there's an inconsistency in how isDisableTextRequired is handled between the two sections.

Consider making the credit card export group's isDisableTextRequired dynamic to match the reimbursable section:

-                        [isDisableTextRequired]="true"
+                        [isDisableTextRequired]="isCccExportGroupDisabled()"

Let's verify the implementation of the disable method:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation of isCccExportGroupDisabled method
ast-grep --pattern 'isCccExportGroupDisabled() {
  $$$
}'

Length of output: 59


Script:

#!/bin/bash
# Let's try a different pattern to find the method implementation
ast-grep --pattern 'isCccExportGroupDisabled'

# Also search with ripgrep for more context
rg "isCccExportGroupDisabled" -A 5

Length of output: 2242


Script:

#!/bin/bash
# Let's find the reimbursable export group implementation for comparison
rg "isReimbursableExportGroupDisabled" -A 5

# Also search for any other configuration-select-field usage with isDisableTextRequired
rg "isDisableTextRequired" -B 2 -A 2

Length of output: 10525

[formControllerName]="'creditCardExportGroup'">
</app-configuration-select-field>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ export class QbdDirectExportSettingsComponent implements OnInit{
return true;
}

isCccExportGroupDisabled(): boolean {
if (this.exportSettingsForm.get('creditCardExportType')?.value === QBDCorporateCreditCardExpensesObject.CREDIT_CARD_PURCHASE) {
return true;
}
if (this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.value?.detail.account_type === 'AccountsPayable') {
return true;
}
return false;

}

isReimbursableExportGroupDisabled(): boolean {
if (this.exportSettingsForm.controls.defaultReimbursableAccountsPayableAccountName.value?.detail.account_type === 'AccountsPayable' && this.exportSettingsForm.controls.reimbursableExportType.value === QbdDirectReimbursableExpensesObject.JOURNAL_ENTRY) {
return true;
}
return false;
}

reimbursableAccpuntOptions(): DestinationAttribute[] {
if (this.exportSettingsForm.controls.employeeMapping.value === EmployeeFieldMapping.EMPLOYEE) {
return this.destinationOptionsWatcher(['Bank', 'CreditCard', 'OtherCurrentLiability', 'LongTermLiability'], this.destinationAccounts);
Expand Down Expand Up @@ -182,9 +200,10 @@ export class QbdDirectExportSettingsComponent implements OnInit{

cccExportTypeWatcher(): void {
this.exportSettingsForm.controls.creditCardExportType.valueChanges.subscribe((creditCardExportTypeValue) => {
if (creditCardExportTypeValue === QBDCorporateCreditCardExpensesObject.CREDIT_CARD_PURCHASE) {
this.exportSettingsForm.controls.creditCardExportGroup.patchValue(QbdDirectExportSettingModel.expenseGroupingFieldOptions()[1].value);
this.exportSettingsForm.controls.creditCardExportGroup.disable();
this.creditCardExpenseGroupingFieldOptions = [QbdDirectExportSettingModel.expenseGroupingFieldOptions()[1]];
}
});
}

Expand All @@ -211,6 +230,45 @@ export class QbdDirectExportSettingsComponent implements OnInit{
});
}

defaultAccountsPayableAccountWatcher() {
this.exportSettingsForm.controls.employeeMapping.valueChanges.subscribe((employeeMapping) => {
if (employeeMapping === EmployeeFieldMapping.EMPLOYEE) {
if (this.exportSettingsForm.controls.defaultReimbursableAccountsPayableAccountName.value.detail.account_type === 'AccountsPayable') {
this.exportSettingsForm.controls.defaultReimbursableAccountsPayableAccountName.patchValue(null);
if (this.exportSettingsForm.controls.nameInJE.value === EmployeeFieldMapping.EMPLOYEE && this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.value.detail.account_type === 'AccountsPayable') {
this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.patchValue(null);
}
}
}
});
}

defaultCCCAccountsPayableAccountWatcher() {
this.exportSettingsForm.controls.nameInJE.valueChanges.subscribe((nameInJE) => {
if (nameInJE === EmployeeFieldMapping.EMPLOYEE && this.exportSettingsForm.controls.employeeMapping.value === EmployeeFieldMapping.EMPLOYEE && this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.value.detail.account_type === 'AccountsPayable') {
this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.patchValue(null);
}
});
}
Comment on lines +246 to +252
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and code readability

Similar to the previous method, this one needs improvements in error handling and null safety.

Consider this safer implementation:

 defaultCCCAccountsPayableAccountWatcher() {
-  this.exportSettingsForm.controls.nameInJE.valueChanges.subscribe((nameInJE) => {
-    if (nameInJE === EmployeeFieldMapping.EMPLOYEE && this.exportSettingsForm.controls.employeeMapping.value === EmployeeFieldMapping.EMPLOYEE && this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.value.detail.account_type === 'AccountsPayable') {
-      this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.patchValue(null);
+  this.exportSettingsForm.controls.nameInJE.valueChanges.subscribe({
+    next: (nameInJE) => {
+      const isEmployeeMapping = this.exportSettingsForm.controls.employeeMapping.value === EmployeeFieldMapping.EMPLOYEE;
+      const cccAccount = this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.value?.detail;
+      
+      if (nameInJE === EmployeeFieldMapping.EMPLOYEE && 
+          isEmployeeMapping && 
+          cccAccount?.account_type === QbdDirectAccountType.ACCOUNTS_PAYABLE) {
+        this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.patchValue(null);
+      }
+    },
+    error: (error) => {
+      console.error('Error in nameInJE subscription:', error);
    }
   });
 }

Committable suggestion skipped: line range outside the PR's diff.


cccExportGroupingWatcher() {
this.exportSettingsForm.controls.defaultCCCAccountsPayableAccountName.valueChanges.subscribe((defaultCCCAccountsPayableAccountNameValue: QbdDirectDestinationAttribute) => {
if (defaultCCCAccountsPayableAccountNameValue?.detail?.account_type === 'AccountsPayable') {
this.exportSettingsForm.controls.creditCardExportGroup.patchValue(QbdDirectExportSettingModel.expenseGroupingFieldOptions()[1].value);
this.exportSettingsForm.controls.creditCardExportGroup.disable();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's have an else case to creditCardExportGroup.enable() it back

});
}

reimburesmentExpenseGroupingWatcher() {
this.exportSettingsForm.controls.defaultReimbursableAccountsPayableAccountName.valueChanges.subscribe((defaultReimbursableAccountsPayableAccountNameValue: QbdDirectDestinationAttribute) => {
if (defaultReimbursableAccountsPayableAccountNameValue?.detail?.account_type === 'AccountsPayable') {
this.exportSettingsForm.controls.reimbursableExportGroup.patchValue(QbdDirectExportSettingModel.expenseGroupingFieldOptions()[1].value);
this.exportSettingsForm.controls.reimbursableExportGroup.disable();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's have an else case to reimbursableExportGroup.enable() it back

});
}

destinationOptionsWatcher(detailAccountType: string[], destinationOptions: QbdDirectDestinationAttribute[]): DestinationAttribute[] {
return destinationOptions.filter((account: QbdDirectDestinationAttribute) => detailAccountType.includes(account.detail.account_type));
}
Expand All @@ -224,6 +282,14 @@ export class QbdDirectExportSettingsComponent implements OnInit{
this.reimbursableExpenseGroupWatcher();

this.cccExpenseGroupWatcher();

this.defaultAccountsPayableAccountWatcher();

this.defaultCCCAccountsPayableAccountWatcher();
Comment on lines +289 to +292
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent memory leaks and improve watcher organization

The watchers are correctly integrated, but there are two concerns:

  1. Subscriptions aren't being cleaned up, which could lead to memory leaks
  2. Related watchers could be grouped together for better maintainability

Consider these improvements:

  1. Add subscription management:
private subscriptions: Subscription[] = [];

private exportsettingsWatcher(): void {
  // Group related watchers
  // Credit card related
  this.subscriptions.push(this.cccExportTypeWatcher());
  this.subscriptions.push(this.cccExpenseGroupWatcher());
  this.subscriptions.push(this.defaultCCCAccountsPayableAccountWatcher());

  // Employee mapping related
  this.subscriptions.push(this.employeeMappingWatcher());
  this.subscriptions.push(this.defaultAccountsPayableAccountWatcher());

  // Reimbursable related
  this.subscriptions.push(this.reimbursableExpenseGroupWatcher());
}

ngOnDestroy() {
  this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
  1. Update watcher methods to return their subscriptions:
defaultAccountsPayableAccountWatcher(): Subscription {
  return this.exportSettingsForm.controls.employeeMapping.valueChanges.subscribe({
    // ... existing implementation
  });
}


this.cccExportGroupingWatcher();

this.reimburesmentExpenseGroupingWatcher();
}

private setupCCCExpenseGroupingDateOptions(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h5 class="!tw-text-text-muted tw-text-14-px !tw-font-400 !tw-leading-4" [innerH
(onChange)="onMultiSelectChange()">
<ng-template let-value pTemplate="selectedItems" >
<div *ngIf="form.get(formControllerName)?.value?.length>0" class="tw-flex">
<p *ngFor="let name of value;let i = index">{{ name | titlecase | snakeCaseToSpaceCase }}<span *ngIf="i !== value?.length-1">,&nbsp;</span></p>
<p *ngFor="let name of value;let i = index">{{ getMemo(name) }}<span *ngIf="i !== value?.length-1">,&nbsp;</span></p>
</div>
<div *ngIf="form.get(formControllerName)?.value?.length === 0" class="tw-text-placeholder">
{{ placeholder }}
Expand All @@ -26,7 +26,7 @@ <h5 class="!tw-text-text-muted tw-text-14-px !tw-font-400 !tw-leading-4" [innerH
<p class="tw-text-sub-text-color tw-text-14-px">{{ gens | titlecase | snakeCaseToSpaceCase }}</p>
</div> -->
<div>
<p class="tw-text-14-px">{{ memo | titlecase | snakeCaseToSpaceCase }}</p>
<p class="tw-text-14-px">{{ getMemo(memo) }}</p>
</div>
</ng-template>
</p-multiSelect>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { TitleCasePipe } from '@angular/common';
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { brandingConfig } from 'src/app/branding/branding-config';
import { SnakeCaseToSpaceCasePipe } from 'src/app/shared/pipes/snake-case-to-space-case.pipe';

@Component({
selector: 'app-configuration-multi-select',
Expand Down Expand Up @@ -49,6 +51,10 @@ export class ConfigurationMultiSelectComponent implements OnInit {
this.changeInMultiSelect.emit();
}

getMemo(memo: string): string {
return memo === 'expense_key' ? 'Expense/Report Id' : new SnakeCaseToSpaceCasePipe().transform(new TitleCasePipe().transform(memo));
}

// DragStart(memo: string) {
// This.currentlyDragging = memo;
// }
Expand Down
Loading