Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Commit

Permalink
Exclude export qbo ui (#245)
Browse files Browse the repository at this point in the history
exclude export UI with toggle
  • Loading branch information
anishfyle authored Mar 2, 2023
1 parent 3345b55 commit 17015ee
Show file tree
Hide file tree
Showing 30 changed files with 1,665 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ describe('AdvancedSettingModel', () => {
memoStructure: new FormControl(['Fyle']),
searchOption: new FormControl([]),
emails: new FormControl([]),
addedEmail: new FormControl([])
addedEmail: new FormControl([]),
skipExport: new FormControl(true)
});

const advancedSettingPayload:AdvancedSettingPost = {
Expand Down
22 changes: 22 additions & 0 deletions src/app/core/models/db/expense-group.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ export interface ExpenseGroupList {
expenses: Expense[];
}

export interface SkipExportList {
updated_at: Date;
employee: [string, string];
expenseType: 'Credit Card' | 'Reimbursable';
claim_number: string;
}

export type SkipExportLog = {
employee_name: string;
employee_email: string;
claim_number: string;
updated_at: Date;
fund_source: string;
};

export type SkipExportLogResponse = {
count: number;
next: string;
previous: string;
results: SkipExportLog[];
};

export type ExportableExpenseGroup = {
exportable_expense_group_ids: number[];
};
24 changes: 24 additions & 0 deletions src/app/core/models/enum/enum.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,27 @@ export enum ZeroStatePage {
dashboard_error = 'dashboard_error',
custom_mapping = 'custom_mapping'
}

export enum Operator {
IsNull = "isnull",
IExact = "iexact",
IContains = "icontains",
LessThan = "lt",
LessThanOrEqual = "lte"
}

export const JoinOptions = {
AND: { value: 'AND' },
OR: { value: 'OR' }
};

export enum JoinOption {
AND = "AND",
OR = "OR"
}

export enum CustomOperatorOption {
Is = "iexact",
IsEmpty = "is_empty",
IsNotEmpty = "is_not_empty"
}
67 changes: 67 additions & 0 deletions src/app/core/models/misc/skip-export.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { JoinOption, Operator } from "../enum/enum.model";

export type SkipExport = {
condition: string;
custom_field_type: any;
operator: Operator.IsNull | Operator.IExact | Operator.IContains | Operator.LessThan | Operator.LessThanOrEqual;
values: string[];
rank: number;
join_by: JoinOption.AND | JoinOption.OR | null;
is_custom: boolean;
};

export type ExpenseFilterResponse = {
count: number;
results: SkipExport[];
};

export type ConditionField = {
field_name: string;
type: string;
is_custom: boolean;
};

export function constructPayload1(valueField: {
condition1: ConditionField,
operator1: SkipExport['operator'],
value1: string[]
join_by?: SkipExport['join_by']
}, valueOption1: any[]): SkipExport {
return {
condition: valueField.condition1.field_name,
operator: valueField.operator1,
values:
valueField.condition1.type === 'DATE' ||
valueField.operator1 === 'isnull' || valueField.condition1.field_name === 'report_title'
? valueField.value1
: valueOption1,
rank: 1,
join_by: valueField.join_by ? valueField.join_by : null,
is_custom: valueField.condition1.is_custom,
custom_field_type: valueField.condition1.is_custom
? valueField.condition1.type
: null
};
}

export function constructPayload2(valueField: {
condition2: ConditionField,
operator2: SkipExport['operator'],
value2: string[]
}, valueOption2: any[]): SkipExport {
return {
condition: valueField.condition2.field_name,
operator: valueField.operator2,
values:
valueField.condition2.type === 'DATE' ||
valueField.operator2 === 'isnull' || valueField.condition2.field_name === 'report_title'
? valueField.value2
: valueOption2,
rank: 2,
join_by: null,
is_custom: valueField.condition2.is_custom,
custom_field_type: valueField.condition2.is_custom
? valueField.condition2.type
: null
};
}
25 changes: 25 additions & 0 deletions src/app/core/services/configuration/advanced-setting.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Cacheable, CacheBuster } from 'ts-cacheable';
import { AdvancedSettingGet, AdvancedSettingPost, AdvancedSettingWorkspaceSchedulePost } from '../../models/configuration/advanced-setting.model';
import { WorkspaceSchedule, WorkspaceScheduleEmailOptions } from '../../models/db/workspace-schedule.model';
import { ExpenseFilterResponse, SkipExport } from '../../models/misc/skip-export.model';
import { ApiService } from '../core/api.service';
import { WorkspaceService } from '../workspace/workspace.service';

const advancedSettingsCache$ = new Subject<void>();
const skipExportCache = new Subject<void>();

@Injectable({
providedIn: 'root'
})
Expand All @@ -31,6 +35,27 @@ export class AdvancedSettingService {
return this.apiService.put(`/v2/workspaces/${this.workspaceService.getWorkspaceId()}/advanced_configurations/`, exportSettingsPayload);
}

@Cacheable({
cacheBusterObserver: skipExportCache
})
getExpenseFilter(): Observable<ExpenseFilterResponse> {
return this.apiService.get(`/workspaces/${this.workspaceService.getWorkspaceId()}/fyle/expense_filters/`, {});
}

@CacheBuster({
cacheBusterNotifier: skipExportCache
})
postExpenseFilter(skipExport: SkipExport): Observable<SkipExport> {
return this.apiService.post(`/workspaces/${this.workspaceService.getWorkspaceId()}/fyle/expense_filters/`, skipExport);
}

@CacheBuster({
cacheBusterNotifier: skipExportCache
})
deleteExpenseFilter(rank: number): Observable<SkipExport> {
return this.apiService.delete(`/workspaces/${this.workspaceService.getWorkspaceId()}/fyle/expense_filters/?rank=${rank}`);
}

getWorkspaceAdmins(): Observable<[WorkspaceScheduleEmailOptions]> {
return this.apiService.get(`/workspaces/${this.workspaceService.getWorkspaceId()}/admins/`, {});
}
Expand Down
19 changes: 18 additions & 1 deletion src/app/core/services/export-log/export-log.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Cacheable } from 'ts-cacheable';
import { ExpenseGroupSetting } from '../../models/db/expense-group-setting.model';
import { ExpenseGroup, ExpenseGroupDescription, ExpenseGroupResponse } from '../../models/db/expense-group.model';
import { ExpenseGroup, ExpenseGroupDescription, ExpenseGroupResponse, SkipExportLogResponse } from '../../models/db/expense-group.model';
import { FyleReferenceType } from '../../models/enum/enum.model';
import { SelectedDateFilter } from '../../models/misc/date-filter.model';
import { ApiService } from '../core/api.service';
Expand Down Expand Up @@ -52,6 +52,23 @@ export class ExportLogService {
return this.apiService.get(`/workspaces/${this.workspaceId}/fyle/expense_group_settings/`, {});
}

@Cacheable()
getSkippedExpenses(limit: number, offset: number, selectedDateFilter: SelectedDateFilter | null): Observable<SkipExportLogResponse> {
const params: any = {
limit,
offset,
is_skipped: 'true'
};

if (selectedDateFilter) {
const startDate = selectedDateFilter.startDate.toLocaleDateString().split('/');
const endDate = selectedDateFilter.endDate.toLocaleDateString().split('/');
params.start_date = `${startDate[2]}-${startDate[1]}-${startDate[0]}T00:00:00`;
params.end_date = `${endDate[2]}-${endDate[1]}-${endDate[0]}T23:59:59`;
}
return this.apiService.get(`/workspaces/${this.workspaceId}/fyle/expenses/`, params);
}

generateExportTypeAndId(expenseGroup: ExpenseGroup) {
let exportRedirection = null;
let exportType = null;
Expand Down
32 changes: 32 additions & 0 deletions src/app/core/services/misc/mapping.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { MappingSettingResponse } from '../../models/db/mapping-setting.model';
import { EmployeeMapping, EmployeeMappingPost } from '../../models/db/employee-mapping.model';
import { DestinationAttribute } from '../../models/db/destination-attribute.model';
import { mappingSettingPayload, postMappingSettingResponse } from './mapping.service.fixture';
import { ConditionField } from '../../models/misc/skip-export.model';

describe('MappingService', () => {
let service: MappingService;
Expand Down Expand Up @@ -142,6 +143,37 @@ describe('MappingService', () => {
req.flush(response);
});

it('should return the correct Fyle custom fields', () => {
const expectedFields: ConditionField[] = [
{
"field_name": "Team",
"type": "SELECT",
"is_custom": true
},
{
"field_name": "Project",
"type": "SELECT",
"is_custom": true
}
];

service.getFyleCustomFields().subscribe(fields => {
expect(fields).toEqual(expectedFields);
expect(fields.length).toEqual(2);

const firstField = fields[0];
expect(firstField).toEqual(expectedFields[0]);

const secondField = fields[1];
expect(secondField).toEqual(expectedFields[1]);
});

const req = httpMock.expectOne(`${API_BASE_URL}/workspaces/${workspace_id}/fyle/custom_fields/`);
expect(req.request.method).toEqual('GET');
req.flush(expectedFields);
});


it('getMappings() service check', () => {
const response={
"count": 125,
Expand Down
5 changes: 5 additions & 0 deletions src/app/core/services/misc/mapping.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ExtendedExpenseAttributeResponse } from '../../models/db/expense-attrib
import { MappingSetting, MappingSettingPost, MappingSettingResponse } from '../../models/db/mapping-setting.model';
import { MappingPost, MappingStats } from '../../models/db/mapping.model';
import { EmployeeFieldMapping, MappingState } from '../../models/enum/enum.model';
import { ConditionField } from '../../models/misc/skip-export.model';
import { ExpenseField } from '../../models/misc/expense-field.model';
import { ApiService } from '../core/api.service';
import { WorkspaceService } from '../workspace/workspace.service';
Expand Down Expand Up @@ -84,6 +85,10 @@ export class MappingService {
return this.apiService.get(`/workspaces/${this.workspaceId}/fyle/expense_fields/`, {});
}

getFyleCustomFields(): Observable<ConditionField[]> {
return this.apiService.get(`/workspaces/${this.workspaceId}/fyle/custom_fields/`, {});
}

getMappings(mappingState: MappingState, limit: number, offset: number, alphabetsFilter: string[], sourceType: string, destinationType: string): Observable<ExtendedExpenseAttributeResponse> {
const params: any = {
limit,
Expand Down
98 changes: 97 additions & 1 deletion src/app/integration/main/export-log/export-log.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
<div class="page-header">
<nav mat-tab-nav-bar>
<a mat-tab-link (click)="changeState('COMPLETE')" [active]="state==='COMPLETE'">
Complete
</a>
<a mat-tab-link (click)="changeState('SKIP')" [active]="state==='SKIP'">
Skipped
</a>
</nav>
</div>
<div class="configuration-main">
<div class="configuration-main--bg-top"></div>
<div class="configuration-main--bg-bottom"></div>
</div>
<div class="export-log">
<div class="export-log--content">
<div class="export-log--content" *ngIf="state==='COMPLETE'">
<!-- header -->
<div class="export-log--header-section">
<div fxLayout="row" class="export-log--simple-search" [class.active]="selectedDateFilter">
Expand Down Expand Up @@ -83,4 +93,90 @@ <h5 class="export-log--done-text align-center">Done</h5>

<app-paginator *ngIf="expenseGroups.filteredData.length" [page]="PaginatorPage.EXPORT_LOG" [totalCount]="totalCount" [limit]="limit" [offset]="offset" (pageChangeEvent)="getExpenseGroups($event)"></app-paginator>
</div>

<!-- For Skipped Tab -->
<div class="export-log--content" *ngIf="state==='SKIP'">


<!-- header -->
<div class="export-log--header-section">
<div fxLayout="row" class="export-log--simple-search" [class.active]="selectedDateFilterSkipExport">
<app-simple-text-search *ngIf="isLoading || totalSkipCount || selectedDateFilterSkipExport" [page]="SimpleSearchPage.EXPORT_LOG" [searchType]="SimpleSearchType.TEXT_FIELD" [form]="skipExportLogForm" [placeholder]="'Search by Employee Name or Reference ID'" [showBackgroundColor]="false"></app-simple-text-search>

<div *ngIf="isLoading && !totalCount">
<mat-form-field floatLabel="always" appearance="outline" class="export-log--date-filter">
<mat-select placeholder="Select date range"></mat-select>
<div class="mat-select-arrow-closed"></div>
</mat-form-field>
</div>

<div *ngIf="!isLoading && (totalSkipCount || selectedDateFilterSkipExport)">
<mat-form-field [formGroup]="skipExportLogForm" floatLabel="always" appearance="outline" class="export-log--date-filter">
<mat-select placeholder="Select date range" formControlName="dateRange">
<mat-select-trigger *ngIf="selectedDateFilterSkipExport" class="export-log--selected-date">
{{ selectedDateFilterSkipExport.startDate | date: 'MMM dd, yyyy' }} - {{ selectedDateFilterSkipExport.endDate | date: 'MMM dd, yyyy' }}
<img src="assets/images/svgs/actions/clear-date-filter.svg" (click)="clearDateFilterSkipExport()" class="export-log--clear-date-filter" width="12px" height="12px">
</mat-select-trigger>
<mat-option class="export-log--date-select-option" *ngFor="let dateOption of dateOptions" [value]="dateOption">
<div>
<div class="sub-text-color">{{ dateOption.dateRange }}</div>
<div class="export-log--date-range">{{ dateOption.startDate | date: 'MMM dd, yyyy' }} - {{ dateOption.endDate | date: 'MMM dd, yyyy' }}</div>
</div>
</mat-option>
<mat-option class="export-log--date-select-option" [value]="''" (click)="picker.open();">
<div fxLayout="row" fxLayoutAlign="space-between center">
<div class="sub-text-color">Custom dates</div>
<img src="assets/images/svgs/actions/arrow-right-export-log.svg" class="configuration--icon">
</div>
</mat-option>
</mat-select>
<div *ngIf="!selectedDateFilterSkipExport" class="mat-select-arrow-closed"></div>
</mat-form-field>
<div class="export-log--date-field">
<mat-form-field floatLabel="always" appearance="outline" class="configuration--form-field export-log--date-filter">
<mat-date-range-input [formGroup]="skipExportLogForm" [rangePicker]="picker">
<input matStartDate formControlName="start" placeholder="Start date">
<input (dateChange)="dateFilterHandlerSkipExport()" matEndDate formControlName="end" placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker>
<mat-date-range-picker-actions>
<div class="export-log--cancel-btn pointer" matDateRangePickerCancel>
<h5 class="export-log--cancel-btn-text align-center">Cancel</h5>
</div>
<div class="export-log--done pointer" matDateRangePickerApply>
<h5 class="export-log--done-text align-center">Done</h5>
</div>
</mat-date-range-picker-actions>
</mat-date-range-picker>
</mat-form-field>
</div>
</div>
</div>
</div>
<!-- header -->



<!-- shimmers -->
<div *ngIf="isLoading">
<app-skip-export-log-table [displayedColumns]="skippedExpenseColumns" [skipExport]="emptySkipExportList"></app-skip-export-log-table>
<div *ngFor="let i of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" fxLayout="row" fxLayoutAlign="start center" class="skeleton-loader-list-view--row-box">
<div class="skeleton-loader-list-view--init-box-section">
<div class="shimmers skeleton-loader-list-view--init-box-export-log"></div>
</div>
<div class="skeleton-loader-list-view--end-box-section">
<div class="shimmers skeleton-loader-list-view--end-box-export-log"></div>
</div>
</div>
</div>
<!-- shimmers -->

<div *ngIf="!isLoading" class="export-log--table">
<app-skip-export-log-table *ngIf="totalSkipCount || selectedDateFilterSkipExport" [displayedColumns]="skippedExpenseColumns" [skipExport]="skipExport"></app-skip-export-log-table>
<app-zero-state-with-illustration [page]="ZeroStatePage.export_log" [form]="skipExportLogForm" [data]="skipExport" [searchTerm]="'Employee Name or Expense ID'" [dateFilter]="selectedDateFilterSkipExport"></app-zero-state-with-illustration>
</div>

<app-paginator *ngIf="skipExport.filteredData.length" [page]="PaginatorPage.EXPORT_LOG" [totalCount]="totalSkipCount" [limit]="limit" [offset]="offset" (pageChangeEvent)="getSkipExportLog($event)"></app-paginator>
</div>
</div>
Loading

0 comments on commit 17015ee

Please sign in to comment.