Feature: Lazy loading

Lazy loaded everything
TODO: The cash flow module when clicking on sub-links, it reloads the whole page, it needs to be diagnosed and fixed, this problem also exists in the other modules
TODO: Rename folders and modules such as account to accounts to match the url
This commit is contained in:
Amritanshu
2019-06-12 19:34:50 +05:30
parent fea48e1a3e
commit 72044476a8
184 changed files with 786 additions and 664 deletions

View File

@ -0,0 +1,9 @@
export class AccountType {
id: number;
name: string;
balanceSheet: boolean;
debit: boolean;
cashFlowClassification: string;
order: number;
showInList: boolean;
}

View File

@ -0,0 +1,15 @@
import {inject, TestBed} from '@angular/core/testing';
import {AccountService} from './account.service';
describe('AccountService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AccountService]
});
});
it('should be created', inject([AccountService], (service: AccountService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,97 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/internal/Observable';
import {catchError} from 'rxjs/operators';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {Account} from './account';
import {ErrorLoggerService} from './error-logger.service';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const url = '/api/accounts';
const serviceName = 'AccountService';
@Injectable({providedIn: 'root'})
export class AccountService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {
}
get(id: string): Observable<Account> {
const getUrl: string = (id === null) ? `${url}/new` : `${url}/${id}`;
return <Observable<Account>>this.http.get<Account>(getUrl)
.pipe(
catchError(this.log.handleError(serviceName, `get id=${id}`))
);
}
list(): Observable<Account[]> {
const options = {params: new HttpParams().set('l', '')};
return <Observable<Account[]>>this.http.get<Account[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
paymentAutocomplete(term: string): Observable<Account[]> {
const options = {params: new HttpParams().set('q', term).set('t', '1')};
return <Observable<Account[]>>this.http.get<Account[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
receiptAutocomplete(term: string): Observable<Account[]> {
const options = {params: new HttpParams().set('q', term).set('t', '1')};
return <Observable<Account[]>>this.http.get<Account[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
save(account: Account): Observable<Account> {
return <Observable<Account>>this.http.post<Account>(`${url}/new`, account, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(account: Account): Observable<Account> {
return <Observable<Account>>this.http.put<Account>(`${url}/${account.id}`, account, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);
}
saveOrUpdate(account: Account): Observable<Account> {
if (!account.id) {
return this.save(account);
} else {
return this.update(account);
}
}
delete(id: string): Observable<Account> {
return <Observable<Account>>this.http.delete<Account>(`${url}/${id}`, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'delete'))
);
}
autocomplete(term: string): Observable<Account[]> {
const options = {params: new HttpParams().set('q', term)};
return <Observable<Account[]>>this.http.get<Account[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'autocomplete'))
);
}
balance(id: string, date: string): Observable<any> {
const options = {params: new HttpParams().set('b', 'true').set('d', date)};
return <Observable<any>>this.http.get<any>(`${url}/${id}`, options)
.pipe(
catchError(this.log.handleError(serviceName, 'balance'))
);
}
}

View File

@ -0,0 +1,18 @@
import {AccountType} from './account-type';
import {CostCentre} from './cost-centre';
export class Account {
id: string;
code: number;
name: string;
type: AccountType;
isActive: boolean;
isReconcilable: boolean;
isStarred: boolean;
isFixture: boolean;
costCentre: CostCentre;
public constructor(init?: Partial<Account>) {
Object.assign(this, init);
}
}

View File

@ -0,0 +1,24 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/internal/Observable';
import {catchError} from 'rxjs/operators';
import {HttpClient, HttpParams} from '@angular/common/http';
import {Batch} from './voucher';
import {ErrorLoggerService} from './error-logger.service';
const url = '/api/batch';
const serviceName = 'BatchService';
@Injectable({providedIn: 'root'})
export class BatchService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {
}
autocomplete(date: string, term: string): Observable<Batch[]> {
const options = {params: new HttpParams().set('t', term).set('d', date)};
return <Observable<Batch[]>>this.http.get<Batch[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'autocomplete'))
);
}
}

View File

@ -0,0 +1,5 @@
export class CostCentre {
id: string;
name: string;
isFixture: boolean;
}

View File

@ -1,61 +1,61 @@
<mat-toolbar class="fixed-to-top">
<span><a routerLink="/">HnG / TGB</a></span>
<mat-menu #voucherMenu="matMenu">
<a mat-menu-item routerLink="/Journal">Journal</a>
<a mat-menu-item routerLink="/Purchase">Purchase</a>
<a mat-menu-item routerLink="/Return">Purchase Return</a>
<a mat-menu-item routerLink="/Payment">Payment</a>
<a mat-menu-item routerLink="/Receipt">Receipt</a>
<a mat-menu-item routerLink="/Issue">Issue</a>
<a mat-menu-item routerLink="/journal">Journal</a>
<a mat-menu-item routerLink="/purchase">Purchase</a>
<a mat-menu-item routerLink="/return">Purchase Return</a>
<a mat-menu-item routerLink="/payment">Payment</a>
<a mat-menu-item routerLink="/receipt">Receipt</a>
<a mat-menu-item routerLink="/issue">Issue</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="voucherMenu">
Voucher Entry
</button>
<mat-menu #reportMenu="matMenu">
<a mat-menu-item routerLink="/Ledger">Display Ledger</a>
<a mat-menu-item href="/Reconcile">Ledger Reconcilliation</a>
<a mat-menu-item routerLink="/CashFlow">Cash Flow</a>
<a mat-menu-item routerLink="/Daybook">Day Book</a>
<a mat-menu-item routerLink="/TrialBalance">Trial Balance</a>
<a mat-menu-item routerLink="/ProfitLoss">Profit and Loss</a>
<a mat-menu-item routerLink="/BalanceSheet">Balance Sheet</a>
<a mat-menu-item routerLink="/NetTransactions">Net Transactions</a>
<a mat-menu-item routerLink="/Unposted">UnPosted Entries</a>
<a mat-menu-item routerLink="/ledger">Display Ledger</a>
<a mat-menu-item href="/reconcile">Ledger Reconcilliation</a>
<a mat-menu-item routerLink="/cash-flow">Cash Flow</a>
<a mat-menu-item routerLink="/daybook">Day Book</a>
<a mat-menu-item routerLink="/trial-balance">Trial Balance</a>
<a mat-menu-item routerLink="/profit-loss">Profit and Loss</a>
<a mat-menu-item routerLink="/balance-sheet">Balance Sheet</a>
<a mat-menu-item routerLink="/net-transactions">Net Transactions</a>
<a mat-menu-item routerLink="/unposted">UnPosted Entries</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="reportMenu">
Reports
</button>
<mat-menu #productReportMenu="matMenu">
<a mat-menu-item routerLink="/ProductLedger">Product Ledger</a>
<a mat-menu-item routerLink="/RawMaterialCost">Raw Material Cost</a>
<a mat-menu-item routerLink="/PurchaseEntries">Purchase Entries</a>
<a mat-menu-item routerLink="/Purchases">Purchases</a>
<a mat-menu-item routerLink="/ClosingStock">Closing Stock</a>
<a mat-menu-item routerLink="/StockMovement">Stock Movement</a>
<a mat-menu-item routerLink="/product-ledger">Product Ledger</a>
<a mat-menu-item routerLink="/raw-material-cost">Raw Material Cost</a>
<a mat-menu-item routerLink="/purchase-entries">Purchase Entries</a>
<a mat-menu-item routerLink="/purchases">Purchases</a>
<a mat-menu-item routerLink="/closing-stock">Closing Stock</a>
<a mat-menu-item routerLink="/stock-movement">Stock Movement</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="productReportMenu">
Product Reports
</button>
<mat-menu #employeeMenu="matMenu">
<a mat-menu-item routerLink="/Employees">Employees</a>
<a mat-menu-item routerLink="/Attendance">Attendance</a>
<a mat-menu-item routerLink="/EmployeeAttendance">Employee Attendance</a>
<a mat-menu-item routerLink="/EmployeeFunctions">Employee Functions</a>
<a mat-menu-item routerLink="/EmployeeBenefits">Employee Benefits (Esi / Pf)</a>
<a mat-menu-item routerLink="/Incentive">Incentive</a>
<a mat-menu-item routerLink="/employees">Employees</a>
<a mat-menu-item routerLink="/attendance">Attendance</a>
<a mat-menu-item routerLink="/employee-attendance">Employee Attendance</a>
<a mat-menu-item routerLink="/employee-functions">Employee Functions</a>
<a mat-menu-item routerLink="/employee-benefits">Employee Benefits (Esi / Pf)</a>
<a mat-menu-item routerLink="/incentive">Incentive</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="employeeMenu">
Employees
</button>
<mat-menu #masterMenu="matMenu">
<a mat-menu-item routerLink="/Accounts">Accounts</a>
<a mat-menu-item routerLink="/CostCentres">Cost Centres</a>
<a mat-menu-item routerLink="/Products">Products</a>
<a mat-menu-item routerLink="/ProductGroups">Product Groups</a>
<a mat-menu-item routerLink="/accounts">Accounts</a>
<a mat-menu-item routerLink="/cost-centres">Cost Centres</a>
<a mat-menu-item routerLink="/products">Products</a>
<a mat-menu-item routerLink="/product-groups">Product Groups</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="masterMenu">
Masters
@ -65,11 +65,11 @@
<mat-menu #userMenu="matMenu">
<a mat-menu-item routerLink="/logout">Logout {{nameObject | async}}</a>
<a mat-menu-item routerLink="/User/{{nameObject | async}}">Change Password</a>
<a mat-menu-item routerLink="/Users">Users</a>
<a mat-menu-item routerLink="/Groups">Groups</a>
<a mat-menu-item routerLink="/Clients">Clients</a>
<a mat-menu-item routerLink="/Settings">Settings</a>
<a mat-menu-item routerLink="/users/{{nameObject | async}}">Change Password</a>
<a mat-menu-item routerLink="/users">Users</a>
<a mat-menu-item routerLink="/groups">Groups</a>
<a mat-menu-item routerLink="/clients">Clients</a>
<a mat-menu-item routerLink="/settings">Settings</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="userMenu" *ngIf="nameObject | async as name">
<mat-icon>account_box</mat-icon>

View File

@ -0,0 +1,5 @@
export class ProductGroup {
id: string;
name: string;
isFixture: boolean;
}

View File

@ -0,0 +1,20 @@
import {Account} from './account';
import {ProductGroup} from './product-group';
export class Product {
id: string;
code: number;
name: string;
units: string;
fraction: number;
fractionUnits: string;
productYield: number;
price: number;
salePrice: number;
isActive: boolean;
isFixture: boolean;
isPurchased: boolean;
isSold: boolean;
productGroup: ProductGroup;
account: Account;
}

View File

@ -0,0 +1,19 @@
export class User {
id: string;
name: string;
password: string;
lockedOut: boolean;
groups: UserGroup[];
perms: string[];
isAuthenticated: boolean;
public constructor(init?: Partial<User>) {
Object.assign(this, init);
}
}
export class UserGroup {
id: string;
name: string;
enabled: boolean;
}

View File

@ -0,0 +1,15 @@
import {inject, TestBed} from '@angular/core/testing';
import {VoucherService} from './voucher.service';
describe('VoucherService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [VoucherService]
});
});
it('should be created', inject([VoucherService], (service: VoucherService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,97 @@
import {Injectable} from '@angular/core';
import {catchError} from 'rxjs/operators';
import {Observable} from 'rxjs/internal/Observable';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {ErrorLoggerService} from './error-logger.service';
import {Voucher} from './voucher';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const url = '/api/voucher';
const serviceName = 'VoucherService';
@Injectable({
providedIn: 'root'
})
export class VoucherService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {
}
get(id: string): Observable<Voucher> {
const options = {params: new HttpParams()};
return <Observable<Voucher>>this.http.get<Voucher>(`${url}/${id}`, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
getOfType(type: string, account?: string): Observable<Voucher> {
const options = {params: new HttpParams().set('t', type)};
if (account !== undefined && account !== null) {
options.params = options.params.set('a', account);
}
return <Observable<Voucher>>this.http.get<Voucher>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
getIncentive(date: string): Observable<Voucher> {
const options = {params: new HttpParams().set('t', 'Service Charge').set('d', date)};
return <Observable<Voucher>>this.http.get<Voucher>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
post(id: string): Observable<Voucher> {
const options = {params: new HttpParams().set('p', '')};
return <Observable<Voucher>>this.http.post<Voucher>(`${url}/${id}`, {}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
delete(id: string): Observable<Voucher> {
return <Observable<Voucher>>this.http.delete<Voucher>(`${url}/${id}`, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'delete'))
);
}
save(voucher: Voucher): Observable<Voucher> {
const options = {
params: new HttpParams().set('t', voucher.type)
};
const fd = new FormData();
voucher.files.filter(x => !x.id).forEach((file, index) => {
fd.append('f' + index, this.dataURLtoBlob(file.resized));
fd.append('t' + index, this.dataURLtoBlob(file.thumbnail));
});
voucher.files = voucher.files.filter(x => x.id);
fd.append('model', JSON.stringify(voucher));
const saveUrl: string = (voucher.id === undefined || voucher.id === null) ? url : `${url}/${voucher.id}`;
return <Observable<Voucher>>this.http.post<Voucher>(saveUrl, fd, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
dataURLtoBlob(dataURL) {
const re = /^data:([\w/\-\.]+);\w+,(.*)$/;
const m = dataURL.match(re);
const mimeString = m[1];
const byteString = atob(m[2]);
const ab = new ArrayBuffer(byteString.length);
const dw = new DataView(ab);
let i;
for (i = 0; i < byteString.length; i++) {
dw.setUint8(i, byteString.charCodeAt(i));
}
return new Blob([ab], {type: mimeString});
}
}

View File

@ -0,0 +1,83 @@
import {Account} from './account';
import {User} from './user';
import {CostCentre} from './cost-centre';
import {Product} from './product';
export class Voucher {
id: string;
date: string;
type: string;
posted: boolean;
narration: string;
incentive?: number;
journals: Journal[];
inventories: Inventory[];
employeeBenefits: EmployeeBenefit[];
incentives: Incentive[];
files: DbFile[];
creationDate: string;
lastEditDate: string;
user: User;
isStarred: boolean;
poster: string;
reconcileDate: string;
}
export class Journal {
id: string;
debit: number;
amount: number;
account: Account;
costCentre: CostCentre;
public constructor(init?: Partial<Journal>) {
Object.assign(this, init);
}
}
export class EmployeeBenefit {
grossSalary: number;
daysWorked: number;
esiEmployee: number;
pfEmployee: number;
esiEmployer: number;
pfEmployer: number;
journal: Journal; // Should be employee as we need designation
}
export class Incentive {
id: string;
name: string;
designation: string;
department: string;
daysWorked: number;
points: number;
}
export class Inventory {
id: string;
quantity: number;
rate: number;
tax: number;
discount: number;
amount: number;
product: Product;
batch: Batch;
}
export class Batch {
id: string;
name: string;
quantityRemaining: number;
tax: number;
discount: number;
rate: number;
product: Product;
}
export class DbFile {
id: string;
resized: string;
thumbnail: string;
}