Refactor: Moved various Schemas into their files

Feature: Complex voucher locking where specific types of vouchers and accounts can be locked and the locks can also be times to expire after some time.
Highest index takes priority
This commit is contained in:
2021-09-10 13:21:43 +05:30
parent 350edf7126
commit 2820813cb6
44 changed files with 1220 additions and 578 deletions

View File

@ -0,0 +1,16 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs';
import { LockInfo } from './lock-info';
export class LockDataSource extends DataSource<LockInfo> {
constructor(private data: Observable<LockInfo[]>) {
super();
}
connect(): Observable<LockInfo[]> {
return this.data;
}
disconnect() {}
}

View File

@ -1,17 +1,23 @@
export class LockDate {
days?: number;
date?: string;
}
export class LockInfo {
lockOlder: boolean;
olderRolling?: boolean;
olderDays?: number;
olderDate?: string;
lockNewer: boolean;
newerRolling?: boolean;
newerDays?: number;
newerDate?: string;
id?: string;
validFrom?: string;
validTill?: string;
voucherTypes: { id: number; name: string }[];
accountTypes: { id: number; name: string }[];
start: LockDate;
finish: LockDate;
index: number;
public constructor(init?: Partial<LockInfo>) {
this.lockOlder = false;
this.lockNewer = false;
this.voucherTypes = [];
this.accountTypes = [];
this.index = 0;
this.start = new LockDate();
this.finish = new LockDate();
Object.assign(this, init);
}
}

View File

@ -8,10 +8,10 @@ import { SettingsService } from './settings.service';
@Injectable({
providedIn: 'root',
})
export class LockInformationResolver implements Resolve<LockInfo> {
export class LockInformationResolver implements Resolve<[LockInfo]> {
constructor(private ser: SettingsService) {}
resolve(): Observable<LockInfo> {
resolve(): Observable<[LockInfo]> {
return this.ser.getLockInformation();
}
}

View File

@ -2,11 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AccountTypeResolver } from '../account/account-type-resolver.service';
import { AuthGuard } from '../auth/auth-guard.service';
import { LockInformationResolver } from './lock-information-resolver.service';
import { MaintenanceResolver } from './maintenance-resolver.service';
import { SettingsComponent } from './settings.component';
import { VoucherTypeResolver } from './voucher-type-resolver.service';
const settingsRoutes: Routes = [
{
@ -19,6 +21,8 @@ const settingsRoutes: Routes = [
resolve: {
maintenance: MaintenanceResolver,
lockInformation: LockInformationResolver,
accountTypes: AccountTypeResolver,
voucherTypes: VoucherTypeResolver,
},
runGuardsAndResolvers: 'always',
},

View File

@ -2,7 +2,7 @@
<mat-tab label="Lock Dates">
<mat-card>
<mat-card-content>
<form [formGroup]="lockDatesForm" fxLayout="column">
<form [formGroup]="lockInfoForm" fxLayout="column">
<div
fxLayout="row"
fxLayoutAlign="space-around start"
@ -11,21 +11,13 @@
fxLayoutGap.lt-md="0px"
>
<mat-checkbox formControlName="lockOlder" fxFlex="20">Lock Older</mat-checkbox>
<mat-checkbox
formControlName="olderRolling"
*ngIf="lockInformation.lockOlder"
fxFlex="20"
>
<mat-checkbox formControlName="olderRolling" *ngIf="lockOlder" fxFlex="20">
Is Rolling
</mat-checkbox>
<mat-form-field
fxFlex="60"
*ngIf="lockInformation.lockOlder && !lockInformation.olderRolling"
>
<mat-form-field fxFlex="60" *ngIf="lockOlder && !olderRolling">
<input
matInput
[matDatepicker]="olderDate"
(focus)="olderDate.open()"
placeholder="Date"
formControlName="olderDate"
autocomplete="off"
@ -33,10 +25,7 @@
<mat-datepicker-toggle matSuffix [for]="olderDate"></mat-datepicker-toggle>
<mat-datepicker #olderDate></mat-datepicker>
</mat-form-field>
<mat-form-field
fxFlex="60"
*ngIf="lockInformation.lockOlder && lockInformation.olderRolling"
>
<mat-form-field fxFlex="60" *ngIf="lockOlder && olderRolling">
<input matInput placeholder="Days" formControlName="olderDays" autocomplete="off" />
</mat-form-field>
</div>
@ -48,21 +37,13 @@
fxLayoutGap.lt-md="0px"
>
<mat-checkbox formControlName="lockNewer" fxFlex="20">Lock Newer</mat-checkbox>
<mat-checkbox
formControlName="newerRolling"
*ngIf="lockInformation.lockNewer"
fxFlex="20"
>
<mat-checkbox formControlName="newerRolling" *ngIf="lockNewer" fxFlex="20">
Is Rolling
</mat-checkbox>
<mat-form-field
fxFlex="60"
*ngIf="lockInformation.lockNewer && !lockInformation.newerRolling"
>
<mat-form-field fxFlex="60" *ngIf="lockNewer && !newerRolling">
<input
matInput
[matDatepicker]="newerDate"
(focus)="newerDate.open()"
placehnewer="Date"
formControlName="newerDate"
autocomplete="off"
@ -70,18 +51,152 @@
<mat-datepicker-toggle matSuffix [for]="newerDate"></mat-datepicker-toggle>
<mat-datepicker #newerDate></mat-datepicker>
</mat-form-field>
<mat-form-field
fxFlex="60"
*ngIf="lockInformation.lockNewer && lockInformation.newerRolling"
>
<mat-form-field fxFlex="60" *ngIf="lockNewer && newerRolling">
<input matInput placehnewer="Days" formControlName="newerDays" autocomplete="off" />
</mat-form-field>
</div>
<div
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<div formArrayName="accountTypes">
<h3>Account Types</h3>
<div
fxLayout="row"
*ngFor="let at of accountTypes; index as i"
[formGroupName]="i"
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<mat-checkbox formControlName="accountType" fxFlex>{{ at.name }}</mat-checkbox>
</div>
</div>
<div formArrayName="voucherTypes">
<h3>Voucher Types</h3>
<div
fxLayout="row"
*ngFor="let vt of voucherTypes; index as j"
[formGroupName]="j"
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<mat-checkbox formControlName="voucherType" fxFlex>{{ vt.name }}</mat-checkbox>
</div>
</div>
</div>
<div
fxLayout="row"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
fxLayoutAlign="space-around start"
>
<mat-form-field fxFlex="30%">
<input
matInput
[matDatepicker]="validFrom"
placeholder="Valid From"
formControlName="validFrom"
autocomplete="off"
/>
<mat-datepicker-toggle matSuffix [for]="validFrom"></mat-datepicker-toggle>
<mat-datepicker #validFrom></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="30%">
<input
matInput
[matDatepicker]="validTill"
placeholder="Valid Till"
formControlName="validTill"
autocomplete="off"
/>
<mat-datepicker-toggle matSuffix [for]="validTill"></mat-datepicker-toggle>
<mat-datepicker #validTill></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="20">
<mat-label>Index</mat-label>
<input
type="text"
matInput
placeholder="Index"
formControlName="index"
autocomplete="off"
/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="saveLock()">Add</button>
</div>
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
<!-- Index Column -->
<ng-container matColumnDef="index">
<mat-header-cell *matHeaderCellDef>Index</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row.index }}</mat-cell>
</ng-container>
<!-- Validity Column -->
<ng-container matColumnDef="validity">
<mat-header-cell *matHeaderCellDef>Validity</mat-header-cell>
<mat-cell *matCellDef="let row"
>{{ !!row.validFrom ? row.validFrom : '\u221E' }} -
{{ !!row.validTill ? row.validTill : '\u221E' }}</mat-cell
>
</ng-container>
<!-- Voucher Types Column -->
<ng-container matColumnDef="voucherTypes">
<mat-header-cell *matHeaderCellDef>Voucher Types</mat-header-cell>
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let vt of row.voucherTypes">{{ vt.name }}</li>
</ul>
</mat-cell>
</ng-container>
<!-- Account Types Column -->
<ng-container matColumnDef="accountTypes">
<mat-header-cell *matHeaderCellDef>Account Types</mat-header-cell>
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let at of row.accountTypes">{{ at.name }}</li>
</ul>
</mat-cell>
</ng-container>
<!-- Lock Column -->
<ng-container matColumnDef="lock">
<mat-header-cell *matHeaderCellDef>Lock Dates</mat-header-cell>
<mat-cell *matCellDef="let row"
>{{ !!row.start.days ? row.start.days : ''
}}{{ !!row.start.date ? row.start.date : ''
}}{{ !row.start.days && !row.start.date ? '\u221E' : '' }} -
{{ !!row.finish.days ? row.finish.days : ''
}}{{ !!row.finish.date ? row.finish.date : ''
}}{{ !row.finish.days && !row.finish.date ? '\u221E' : '' }}</mat-cell
>
</ng-container>
<!-- Action Column -->
<ng-container matColumnDef="action">
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
<mat-cell *matCellDef="let row" class="center">
<button mat-icon-button tabindex="-1" color="warn" (click)="deleteLock(row)">
<mat-icon>delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</form>
<mat-card-actions>
<button mat-raised-button color="primary" (click)="setLockDates()">Set</button>
<button mat-raised-button color="warn" (click)="clearLockDates()">Delete</button>
</mat-card-actions>
</mat-card-content>
</mat-card>
</mat-tab>

View File

@ -1,19 +1,21 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { Observable, of as observableOf } from 'rxjs';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { AuthService } from '../auth/auth.service';
import { AccountType } from '../core/account-type';
import { Product } from '../core/product';
import { ToasterService } from '../core/toaster.service';
import { ProductService } from '../product/product.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { LockDataSource } from './lock-datasource';
import { LockInfo } from './lock-info';
import { SettingsService } from './settings.service';
@ -23,8 +25,18 @@ import { SettingsService } from './settings.service';
styleUrls: ['./settings.component.css'],
})
export class SettingsComponent implements OnInit {
lockDatesForm: FormGroup;
lockInformation: LockInfo;
public lockObservable = new BehaviorSubject<LockInfo[]>([]);
dataSource: LockDataSource = new LockDataSource(this.lockObservable);
accountTypes: AccountType[] = [];
voucherTypes: AccountType[] = [];
lockInfoForm: FormGroup;
lockInformation: LockInfo[];
lockOlder: boolean = false;
olderRolling: boolean = false;
lockNewer: boolean = false;
newerRolling: boolean = false;
rebaseDataForm: FormGroup;
@ -34,6 +46,8 @@ export class SettingsComponent implements OnInit {
maintenance: { enabled: boolean; user: string };
displayedColumns = ['index', 'validity', 'voucherTypes', 'accountTypes', 'lock', 'action'];
version: string;
constructor(
@ -48,17 +62,22 @@ export class SettingsComponent implements OnInit {
) {
const startDate = moment().date(1);
const finishDate = moment().date(startDate.daysInMonth());
this.lockDatesForm = this.fb.group({
lockOlder: null,
olderRolling: null,
olderDate: startDate,
this.lockInfoForm = this.fb.group({
validFrom: '',
validTill: '',
index: 0,
lockOlder: '',
olderRolling: '',
olderDate: finishDate,
olderDays: 0,
lockNewer: null,
newerRolling: null,
lockNewer: '',
newerRolling: '',
newerDate: finishDate,
newerDays: 0,
accountTypes: this.fb.array([]),
voucherTypes: this.fb.array([]),
});
this.lockInformation = [];
this.rebaseDataForm = this.fb.group({
date: moment(),
});
@ -70,12 +89,6 @@ export class SettingsComponent implements OnInit {
quantity: 0,
});
this.listenToLockForm();
this.lockInformation = {
lockOlder: false,
olderRolling: false,
lockNewer: false,
newerRolling: false,
};
// Listen to Product Changes
this.products = (this.resetStockForm.get('product') as FormControl).valueChanges.pipe(
startWith(''),
@ -92,65 +105,103 @@ export class SettingsComponent implements OnInit {
this.route.data.subscribe((value) => {
const data = value as {
maintenance: { enabled: boolean; user: string };
lockInformation: LockInfo;
lockInformation: [LockInfo];
accountTypes: AccountType[];
voucherTypes: AccountType[];
};
this.accountTypes = data.accountTypes;
this.voucherTypes = data.voucherTypes;
this.showLockInformation(data.lockInformation);
this.maintenance = data.maintenance;
this.lockInformation = data.lockInformation;
this.lockDatesForm.patchValue({
lockOlder: this.lockInformation.lockOlder,
olderRolling: this.lockInformation.olderRolling,
olderDate: this.lockInformation.olderDate,
olderDays: this.lockInformation.olderDays,
lockNewer: this.lockInformation.lockNewer,
newerRolling: this.lockInformation.newerRolling,
newerDate: this.lockInformation.newerDate,
newerDays: this.lockInformation.newerDays,
});
});
}
showLockInformation(info: LockInfo[]) {
this.lockInformation = info;
this.lockInfoForm.setControl(
'accountTypes',
this.fb.array(
this.accountTypes.map((x) =>
this.fb.group({
accountType: false,
}),
),
),
);
this.lockInfoForm.setControl(
'voucherTypes',
this.fb.array(
this.voucherTypes.map((x) =>
this.fb.group({
voucherType: false,
}),
),
),
);
this.dataSource = new LockDataSource(this.lockObservable);
this.lockObservable.next(this.lockInformation);
}
listenToLockForm() {
(this.lockDatesForm.get('lockOlder') as FormControl).valueChanges.subscribe((x) => {
this.lockInformation.lockOlder = x;
(this.lockInfoForm.get('lockOlder') as FormControl).valueChanges.subscribe((x) => {
this.lockOlder = x;
});
(this.lockDatesForm.get('lockNewer') as FormControl).valueChanges.subscribe((x) => {
this.lockInformation.lockNewer = x;
(this.lockInfoForm.get('lockNewer') as FormControl).valueChanges.subscribe((x) => {
this.lockNewer = x;
});
(this.lockDatesForm.get('olderRolling') as FormControl).valueChanges.subscribe((x) => {
this.lockInformation.olderRolling = x;
(this.lockInfoForm.get('olderRolling') as FormControl).valueChanges.subscribe((x) => {
this.olderRolling = x;
});
(this.lockDatesForm.get('newerRolling') as FormControl).valueChanges.subscribe((x) => {
this.lockInformation.newerRolling = x;
(this.lockInfoForm.get('newerRolling') as FormControl).valueChanges.subscribe((x) => {
this.newerRolling = x;
});
}
clearLockDates() {
this.ser.deleteLockInformation().subscribe((x) => {
this.lockInformation = x;
saveLock() {
const item = new LockInfo();
if (this.lockOlder && this.olderRolling) {
item.start.days = +(this.lockInfoForm.get('olderDays') as FormControl).value;
} else if (this.lockOlder && !this.olderRolling) {
item.start.date = moment((this.lockInfoForm.get('olderDate') as FormControl).value).format(
'DD-MMM-YYYY',
);
}
if (this.lockNewer && this.newerRolling) {
item.finish.days = +(this.lockInfoForm.get('newerDays') as FormControl).value;
}
if (this.lockNewer && !this.newerRolling) {
item.finish.date = moment((this.lockInfoForm.get('newerDate') as FormControl).value).format(
'DD-MMM-YYYY',
);
}
const atArray = this.lockInfoForm.get('accountTypes') as FormArray;
this.accountTypes.forEach((at, index) => {
if (atArray.controls[index].value.accountType) {
item.accountTypes.push({ id: at.id, name: at.name });
}
});
}
setLockDates() {
if (this.lockInformation.lockOlder && this.lockInformation.olderRolling) {
this.lockInformation.olderDays = +(this.lockDatesForm.get('olderDays') as FormControl).value;
const vtArray = this.lockInfoForm.get('voucherTypes') as FormArray;
this.voucherTypes.forEach((vt, index) => {
if (vtArray.controls[index].value.voucherType) {
item.voucherTypes.push({ id: vt.id, name: vt.name });
}
});
const validFrom = (this.lockInfoForm.get('validFrom') as FormControl).value;
if (validFrom) {
item.validFrom = moment(validFrom).format('DD-MMM-YYYY');
}
if (this.lockInformation.lockOlder && !this.lockInformation.olderRolling) {
this.lockInformation.olderDate = moment(
(this.lockDatesForm.get('olderDate') as FormControl).value,
).format('DD-MMM-YYYY');
const validTill = (this.lockInfoForm.get('validTill') as FormControl).value;
if (validTill) {
item.validTill = moment(validTill).format('DD-MMM-YYYY');
}
if (this.lockInformation.lockNewer && this.lockInformation.newerRolling) {
this.lockInformation.newerDays = +(this.lockDatesForm.get('newerDays') as FormControl).value;
}
if (this.lockInformation.lockOlder && !this.lockInformation.newerRolling) {
this.lockInformation.newerDate = moment(
(this.lockDatesForm.get('newerDate') as FormControl).value,
).format('DD-MMM-YYYY');
}
this.ser.setLockInformation(this.lockInformation).subscribe(
item.index = +(this.lockInfoForm.get('index') as FormControl).value;
this.ser.setLockInformation(item).subscribe(
(result) => {
this.lockInformation = result;
this.showLockInformation(result);
this.toaster.show('Success', 'Lock information Updated');
},
(error) => {
@ -159,6 +210,12 @@ export class SettingsComponent implements OnInit {
);
}
deleteLock(row: LockInfo) {
this.ser.deleteLockInformation(row.id as string).subscribe((x) => {
this.showLockInformation(x);
});
}
confirmRebase(): void {
const rebaseDate = moment((this.rebaseDataForm.get('date') as FormControl).value).format(
'DD-MMM-YYYY',

View File

@ -14,31 +14,31 @@ const serviceName = 'SettingsService';
export class SettingsService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
getLockInformation(): Observable<LockInfo> {
getLockInformation(): Observable<[LockInfo]> {
const url = '/api/lock-information';
return this.http
.get<LockInfo>(url)
.pipe(
catchError(this.log.handleError(serviceName, 'getLockInformation')),
) as Observable<LockInfo>;
.get<[LockInfo]>(url)
.pipe(catchError(this.log.handleError(serviceName, 'getLockInformation'))) as Observable<
[LockInfo]
>;
}
setLockInformation(lockInformation: LockInfo): Observable<LockInfo> {
setLockInformation(lockInformation: LockInfo): Observable<LockInfo[]> {
const url = '/api/lock-information';
return this.http
.post<LockInfo>(url, lockInformation)
.pipe(
catchError(this.log.handleError(serviceName, 'setLockInformation')),
) as Observable<LockInfo>;
.post<LockInfo[]>(url, lockInformation)
.pipe(catchError(this.log.handleError(serviceName, 'setLockInformation'))) as Observable<
LockInfo[]
>;
}
deleteLockInformation(): Observable<LockInfo> {
deleteLockInformation(id: string): Observable<LockInfo[]> {
const url = '/api/lock-information';
return this.http
.delete<LockInfo>(url)
.pipe(
catchError(this.log.handleError(serviceName, 'deleteLockInformation')),
) as Observable<LockInfo>;
.delete<LockInfo[]>(`${url}/${id}`)
.pipe(catchError(this.log.handleError(serviceName, 'deleteLockInformation'))) as Observable<
LockInfo[]
>;
}
resetStock(

View File

@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { AccountType } from '../core/account-type';
import { VoucherTypeService } from './voucher-type.service';
@Injectable({
providedIn: 'root',
})
export class VoucherTypeResolver implements Resolve<AccountType[]> {
constructor(private ser: VoucherTypeService) {}
resolve(): Observable<AccountType[]> {
return this.ser.list();
}
}

View File

@ -0,0 +1,23 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { catchError } from 'rxjs/operators';
import { AccountType } from '../core/account-type';
import { ErrorLoggerService } from '../core/error-logger.service';
const url = '/api/voucher-types';
const serviceName = 'VoucherTypeService';
@Injectable({
providedIn: 'root',
})
export class VoucherTypeService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
list(): Observable<AccountType[]> {
return this.http
.get<AccountType[]>(url)
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<AccountType[]>;
}
}