Fully working with the rule no explicit any

This commit is contained in:
2020-11-25 09:27:42 +05:30
parent 84535ca9bb
commit b583b90756
27 changed files with 223 additions and 155 deletions

View File

@ -2,7 +2,7 @@ import uuid
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from functools import reduce from functools import reduce
from typing import List, Optional from typing import List
import barker.schemas.master as schemas import barker.schemas.master as schemas
@ -41,9 +41,10 @@ def effective_date(d: str = None) -> date:
@router.post("", response_model=schemas.ModifierCategory) @router.post("", response_model=schemas.ModifierCategory)
def save( def save(
data: schemas.ModifierCategoryIn, data: schemas.ModifierCategoryIn,
date_: date = Depends(effective_date),
db: Session = Depends(get_db), db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["modifiers"]), user: UserToken = Security(get_user, scopes=["modifiers"]),
): ) -> schemas.ModifierCategory:
try: try:
item = ModifierCategory( item = ModifierCategory(
name=data.name, name=data.name,
@ -54,7 +55,7 @@ def save(
db.add(item) db.add(item)
add_products(item, data.menu_categories, db) add_products(item, data.menu_categories, db)
db.commit() db.commit()
return modifier_category_info(item, db=db) return modifier_category_info(item, date_, db=db)
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.rollback() db.rollback()
raise HTTPException( raise HTTPException(
@ -73,7 +74,7 @@ def update(
date_: date = Depends(effective_date), date_: date = Depends(effective_date),
db: Session = Depends(get_db), db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["modifiers"]), user: UserToken = Security(get_user, scopes=["modifiers"]),
): ) -> schemas.ModifierCategory:
try: try:
item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first() item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first()
item.name = data.name item.name = data.name
@ -94,17 +95,18 @@ def update(
raise raise
@router.delete("/{id_}") @router.delete("/{id_}", response_model=schemas.ModifierCategoryIn)
def delete( def delete(
id_: uuid.UUID, id_: uuid.UUID,
date_: date = Depends(effective_date),
db: Session = Depends(get_db), db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["modifiers"]), user: UserToken = Security(get_user, scopes=["modifiers"]),
): ) -> schemas.ModifierCategoryIn:
try: try:
item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first() item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first()
db.delete(item) db.delete(item)
db.commit() db.commit()
return modifier_category_info(None, db=db) return modifier_category_blank(date_, db=db)
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.rollback() db.rollback()
raise HTTPException( raise HTTPException(
@ -116,12 +118,13 @@ def delete(
raise raise
@router.get("") @router.get("", response_model=schemas.ModifierCategoryIn)
def show_blank( def show_blank(
db: Session = Depends(get_db), db: Session = Depends(get_db),
date_: date = Depends(effective_date),
user: UserToken = Security(get_user, scopes=["modifiers"]), user: UserToken = Security(get_user, scopes=["modifiers"]),
): ) -> schemas.ModifierCategoryIn:
return modifier_category_info(None, db=db) return modifier_category_info(date_, db=db)
@router.get("/list") @router.get("/list")
@ -178,6 +181,7 @@ def show_list(
@router.get("/for-product/{id_}") @router.get("/for-product/{id_}")
def for_product( def for_product(
id_: uuid.UUID, id_: uuid.UUID,
date_: date = Depends(effective_date),
db: Session = Depends(get_db), db: Session = Depends(get_db),
user: UserToken = Security(get_user), user: UserToken = Security(get_user),
): ):
@ -201,18 +205,18 @@ def for_product(
] ]
@router.get("/{id_}") @router.get("/{id_}", response_model=schemas.ModifierCategory)
def show_id( def show_id(
id_: uuid.UUID, id_: uuid.UUID,
date_: date = Depends(effective_date), date_: date = Depends(effective_date),
db: Session = Depends(get_db), db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["modifiers"]), user: UserToken = Security(get_user, scopes=["modifiers"]),
): ) -> schemas.ModifierCategory:
item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first() item: ModifierCategory = db.query(ModifierCategory).filter(ModifierCategory.id == id_).first()
return modifier_category_info(item, date_, db=db) return modifier_category_info(item, date_, db=db)
def modifier_category_info(item: Optional[ModifierCategory], date_: date, db: Session): def modifier_category_info(item: ModifierCategory, date_: date, db: Session) -> schemas.ModifierCategory:
menu_categories = ( menu_categories = (
db.query(MenuCategory) db.query(MenuCategory)
.join(MenuCategory.products) .join(MenuCategory.products)
@ -235,48 +239,71 @@ def modifier_category_info(item: Optional[ModifierCategory], date_: date, db: Se
) )
.all() .all()
) )
if item is None:
return {
"name": "",
"minimum": 0,
"maximum": 0,
"isActive": True,
"menuCategories": [
{
"id": mc.id,
"name": mc.name,
"enabled": False,
"products": [{"id": p.id, "name": p.name, "enabled": False} for p in mc.products],
}
for mc in menu_categories
],
"sortOrder": item.sort_order,
}
products = [p.id for p in item.products] products = [p.id for p in item.products]
return { return schemas.ModifierCategory(
"id": item.id, id=item.id,
"name": item.name, name=item.name,
"minimum": item.minimum, minimum=item.minimum,
"maximum": item.maximum, maximum=item.maximum,
"isActive": item.is_active, isActive=item.is_active,
"menuCategories": [ menuCategories=[
{ schemas.MenuCategoryLink(
"id": mc.id, id=mc.id,
"name": mc.name, name=mc.name,
"enabled": False, enabled=False,
"products": [ products=[
{ schemas.ProductLink(
"id": p.id, id=p.id,
"name": p.name, name=p.name,
"enabled": True if p.product_id in products else False, enabled=True if p.product_id in products else False,
} )
for p in mc.products for p in mc.products
], ],
} )
for mc in menu_categories for mc in menu_categories
], ],
"sortOrder": item.sort_order, sortOrder=item.sort_order,
} )
def modifier_category_blank(date_: date, db: Session) -> schemas.ModifierCategoryIn:
menu_categories = (
db.query(MenuCategory)
.join(MenuCategory.products)
.filter(
and_(
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= date_,
),
)
)
.order_by(MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name)
.options(
joinedload(MenuCategory.products, innerjoin=True),
contains_eager(MenuCategory.products),
)
.all()
)
return schemas.ModifierCategoryIn(
name="",
minimum=0,
maximum=0,
isActive=True,
menuCategories=[
schemas.MenuCategoryLink(
id=mc.id,
name=mc.name,
enabled=False,
products=[schemas.ProductLink(id=p.id, name=p.name, enabled=False) for p in mc.products],
)
for mc in menu_categories
],
)
def add_products(modifier_category: ModifierCategory, menu_categories: List[schemas.MenuCategoryLink], db: Session): def add_products(modifier_category: ModifierCategory, menu_categories: List[schemas.MenuCategoryLink], db: Session):

View File

@ -38,6 +38,6 @@ module.exports = {
"@typescript-eslint/lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], "@typescript-eslint/lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
"class-methods-use-this": ["error", {"exceptMethods": ["disconnect", "displayFn", "transform"]}], "class-methods-use-this": ["error", {"exceptMethods": ["disconnect", "displayFn", "transform"]}],
"import/order": ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}, "newlines-between": "always"}], "import/order": ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}, "newlines-between": "always"}],
"@typescript-eslint/no-explicit-any": "off", // Disabled for now, but needed to transition to strict compiling "@typescript-eslint/no-explicit-any": "error",
} }
}; };

View File

@ -89,7 +89,7 @@ export class AuthService {
formData.append('otp', otp); formData.append('otp', otp);
formData.append('grant_type', 'password'); formData.append('grant_type', 'password');
return this.http return this.http
.post<any>(loginUrl, formData) .post<{ access_token: string; token_type: string }>(loginUrl, formData)
.pipe(map((u) => u.access_token)) .pipe(map((u) => u.access_token))
.pipe(map((u) => AuthService.parseJwt(u))) .pipe(map((u) => AuthService.parseJwt(u)))
.pipe( .pipe(
@ -118,7 +118,7 @@ export class AuthService {
refreshToken() { refreshToken() {
return this.http return this.http
.post<any>(refreshUrl, {}) .post<{ access_token: string; token_type: string }>(refreshUrl, {})
.pipe(map((u) => u.access_token)) .pipe(map((u) => u.access_token))
.pipe(map((u) => AuthService.parseJwt(u))) .pipe(map((u) => AuthService.parseJwt(u)))
.pipe( .pipe(

View File

@ -70,14 +70,16 @@ export class CashierReportComponent implements OnInit {
} }
print() { print() {
this.ser.print(this.info.cashier.id as string, this.info.startDate, this.info.finishDate).subscribe( this.ser
() => { .print(this.info.cashier.id as string, this.info.startDate, this.info.finishDate)
this.toaster.show('', 'Successfully Printed'); .subscribe(
}, () => {
(error) => { this.toaster.show('', 'Successfully Printed');
this.toaster.show('Error', error.error); },
}, (error) => {
); this.toaster.show('Error', error.error);
},
);
} }
getInfo(): CashierReport { getInfo(): CashierReport {

View File

@ -3,9 +3,8 @@ import { Product } from './product';
import { Tax } from './tax'; import { Tax } from './tax';
export class BillViewItem { export class BillViewItem {
id: string; id: string | undefined;
isKot: boolean; isKot: boolean;
oldKot: boolean;
info: string; info: string;
kotId: string; kotId: string;
@ -20,10 +19,16 @@ export class BillViewItem {
tax: Tax; tax: Tax;
modifiers: Modifier[]; modifiers: Modifier[];
public get isOldKot(): boolean {
return this.isKot && this.id !== undefined;
}
public get isNewKot(): boolean {
return this.isKot && this.id === undefined;
}
public constructor(init?: Partial<BillViewItem>) { public constructor(init?: Partial<BillViewItem>) {
this.id = '';
this.isKot = true; this.isKot = true;
this.oldKot = false;
this.info = ''; this.info = '';
this.kotId = ''; this.kotId = '';
this.product = new Product(); this.product = new Product();

View File

@ -19,7 +19,7 @@ export class ErrorLoggerService {
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars, class-methods-use-this // eslint-disable-next-line @typescript-eslint/no-unused-vars, class-methods-use-this
public handleError<T>(serviceName = 'error-logger', operation = 'operation', result?: T) { public handleError<T>(serviceName = 'error-logger', operation = 'operation', result?: T) {
return (error: any): Observable<T> => { return (error: unknown): Observable<T> => {
ErrorLoggerService.log(serviceName, `${operation} failed: ${error}`); ErrorLoggerService.log(serviceName, `${operation} failed: ${error}`);
return throwError(error); return throwError(error);
}; };

View File

@ -19,7 +19,7 @@ export class ErrorInterceptor implements HttpInterceptor {
private toaster: ToasterService, private toaster: ToasterService,
) {} ) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe( return next.handle(request).pipe(
catchError((err) => { catchError((err) => {
// We don't want to refresh token for some requests like login or refresh token itself // We don't want to refresh token for some requests like login or refresh token itself

View File

@ -10,7 +10,7 @@ export class JwtInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {} constructor(private authService: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// add authorization header with jwt token if available // add authorization header with jwt token if available
// We use this line to debug token refreshing // We use this line to debug token refreshing

View File

@ -1,6 +1,7 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatSelectChange } from '@angular/material/select';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@ -74,7 +75,7 @@ export class HeaderFooterComponent implements OnInit {
return item; return item;
} }
show(val: any) { show(val: MatSelectChange) {
this.router.navigate(['/header-footer', val.value]); this.router.navigate(['/header-footer', val.value]);
} }
} }

View File

@ -4,6 +4,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatTreeNestedDataSource } from '@angular/material/tree'; import { MatTreeNestedDataSource } from '@angular/material/tree';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { map } from 'rxjs/operators';
import { MenuCategory } from '../../core/menu-category'; import { MenuCategory } from '../../core/menu-category';
import { ModifierCategory } from '../../core/modifier-category'; import { ModifierCategory } from '../../core/modifier-category';
@ -11,6 +12,7 @@ import { Product } from '../../core/product';
import { ToasterService } from '../../core/toaster.service'; import { ToasterService } from '../../core/toaster.service';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { ModifierCategoryService } from '../modifier-category.service'; import { ModifierCategoryService } from '../modifier-category.service';
import { NodeItem } from '../node-item';
@Component({ @Component({
selector: 'app-role-detail', selector: 'app-role-detail',
@ -21,8 +23,8 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef; @ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
form: FormGroup; form: FormGroup;
item: ModifierCategory = new ModifierCategory(); item: ModifierCategory = new ModifierCategory();
treeControl = new NestedTreeControl<any>((node) => node.products); treeControl = new NestedTreeControl<NodeItem>((node: NodeItem) => node.children);
dataSource = new MatTreeNestedDataSource<any>(); dataSource = new MatTreeNestedDataSource<NodeItem>();
products: Map<string, Product> = new Map<string, Product>(); products: Map<string, Product> = new Map<string, Product>();
productsOfMenuCategory: Map<string, Product[]> = new Map<string, Product[]>(); productsOfMenuCategory: Map<string, Product[]> = new Map<string, Product[]>();
@ -45,15 +47,26 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
} }
ngOnInit() { ngOnInit() {
this.route.data.subscribe((value) => { this.route.data
const data = value as { item: ModifierCategory }; .pipe(
this.showItem(data.item); map((value) => {
}); const data = value as { item: ModifierCategory };
const tree: NodeItem[] = data.item.menuCategories.map((x: MenuCategory) => ({
id: x.id as string,
name: x.name,
children: x.products.map((y: Product) => ({ id: y.id as string, name: y.name })),
}));
return { item: data.item, tree };
}),
)
.subscribe((data) => {
this.showItem(data.item, data.tree);
});
} }
showItem(item: ModifierCategory) { showItem(item: ModifierCategory, tree: NodeItem[]) {
this.item = item; this.item = item;
this.dataSource.data = item.menuCategories; this.dataSource.data = tree;
this.form.patchValue({ this.form.patchValue({
name: this.item.name || '', name: this.item.name || '',
minimum: `${this.item.minimum}`, minimum: `${this.item.minimum}`,
@ -133,22 +146,22 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
return this.item; return this.item;
} }
hasChild = (_: number, node: any) => !!node.products && node.products.length > 0; hasChild = (_: number, node: NodeItem) => !!node.children && node.children.length > 0;
isProductSelected(node: Product) { isProductSelected(node: NodeItem) {
return (this.products.get(node.id as string) as Product).enabled; return (this.products.get(node.id) as Product).enabled;
} }
isMenuCategorySelected(node: MenuCategory) { isMenuCategorySelected(node: NodeItem) {
return (this.productsOfMenuCategory.get(node.id as string) as Product[]).reduce( return (this.productsOfMenuCategory.get(node.id) as Product[]).reduce(
(acc: boolean, current: Product) => acc && current.enabled, (acc: boolean, current: Product) => acc && current.enabled,
true, true,
); );
} }
isMenuCategoryPartiallySelected(node: MenuCategory) { isMenuCategoryPartiallySelected(node: NodeItem) {
const total = (this.productsOfMenuCategory.get(node.id as string) as Product[]).length; const total = (this.productsOfMenuCategory.get(node.id) as Product[]).length;
const ticked = (this.productsOfMenuCategory.get(node.id as string) as Product[]).reduce( const ticked = (this.productsOfMenuCategory.get(node.id) as Product[]).reduce(
(acc, current) => { (acc, current) => {
if (current.enabled) { if (current.enabled) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -161,12 +174,12 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
return ticked > 0 && ticked < total; return ticked > 0 && ticked < total;
} }
toggleProductSelection(node: Product) { toggleProductSelection(node: NodeItem) {
(this.products.get(node.id as string) as Product).enabled = !(this.products.get(node.id as string) as Product) (this.products.get(node.id) as Product).enabled = !(this.products.get(node.id) as Product)
.enabled; .enabled;
} }
toggleMenuCategorySelection(node: MenuCategory) { toggleMenuCategorySelection(node: NodeItem) {
const sel = !this.isMenuCategorySelected(node); const sel = !this.isMenuCategorySelected(node);
(this.productsOfMenuCategory.get(node.id as string) as Product[]).forEach((p) => { (this.productsOfMenuCategory.get(node.id as string) as Product[]).forEach((p) => {
p.enabled = sel; p.enabled = sel;

View File

@ -0,0 +1,5 @@
export interface NodeItem {
id: string;
name: string;
children?: NodeItem[];
}

View File

@ -1,5 +1,6 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
@ -34,7 +35,7 @@ export class ModifierListComponent implements OnInit {
}); });
} }
filterOn(val: any) { filterOn(val: MatSelectChange) {
this.filter.next(val.value); this.filter.next(val.value);
} }

View File

@ -20,9 +20,9 @@ export class BillTypeComponent {
} }
accept(): void { accept(): void {
if (this.selected === 'REGULAR_BILL') this.dialogRef.close(VoucherType.Bill) if (this.selected === 'REGULAR_BILL') this.dialogRef.close(VoucherType.Bill);
else if (this.selected === 'STAFF') this.dialogRef.close(VoucherType.Staff) else if (this.selected === 'STAFF') this.dialogRef.close(VoucherType.Staff);
else if (this.selected === 'NO_CHARGE') this.dialogRef.close(VoucherType.NoCharge) else if (this.selected === 'NO_CHARGE') this.dialogRef.close(VoucherType.NoCharge);
else this.dialogRef.close(); else this.dialogRef.close();
} }
} }

View File

@ -21,14 +21,14 @@ import { ModifiersComponent } from './modifiers/modifiers.component';
@Injectable() @Injectable()
export class BillService { export class BillService {
public dataObs; public dataObs: BehaviorSubject<BillViewItem[]>;
public data: any[]; public data: BillViewItem[];
public bill: Bill = new Bill(); public bill: Bill = new Bill();
public netAmount: BehaviorSubject<number>; public netAmount: BehaviorSubject<number>;
public discountAmount: BehaviorSubject<number>; public discountAmount: BehaviorSubject<number>;
public taxAmount: BehaviorSubject<number>; public taxAmount: BehaviorSubject<number>;
public amount: BehaviorSubject<number>; public amount: BehaviorSubject<number>;
public selection = new SelectionModel<any>(true, []); public selection = new SelectionModel<BillViewItem>(true, []);
constructor( constructor(
private dialog: MatDialog, private dialog: MatDialog,
@ -37,7 +37,7 @@ export class BillService {
private modifierCategoryService: ModifierCategoryService, private modifierCategoryService: ModifierCategoryService,
) { ) {
this.data = []; this.data = [];
this.dataObs = new BehaviorSubject<any[]>(this.data); this.dataObs = new BehaviorSubject<BillViewItem[]>(this.data);
this.netAmount = new BehaviorSubject(0); this.netAmount = new BehaviorSubject(0);
this.discountAmount = new BehaviorSubject(0); this.discountAmount = new BehaviorSubject(0);
this.taxAmount = new BehaviorSubject(0); this.taxAmount = new BehaviorSubject(0);
@ -50,7 +50,6 @@ export class BillService {
new BillViewItem({ new BillViewItem({
id: k.id, id: k.id,
isKot: true, isKot: true,
oldKot: true,
info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `, info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `,
}), }),
...k.inventories.map( ...k.inventories.map(
@ -74,7 +73,7 @@ export class BillService {
), ),
]); ]);
this.data = view.reduce((a, c) => a.concat(c), []); this.data = view.reduce((a, c) => a.concat(c), []);
this.data.push({ isKot: true, newKot: true, info: '== New Kot ==' }); this.data.push(new BillViewItem({ isKot: true, info: '== New Kot ==' }));
this.dataObs.next(this.data); this.dataObs.next(this.data);
this.updateAmounts(); this.updateAmounts();
} }
@ -101,7 +100,7 @@ export class BillService {
if (old !== undefined) { if (old !== undefined) {
old.quantity += quantity; old.quantity += quantity;
} else { } else {
const item = { const item = new BillViewItem({
isKot: false, isKot: false,
product, product,
productId: product.id, productId: product.id,
@ -113,11 +112,11 @@ export class BillService {
taxRate: product.tax.rate, taxRate: product.tax.rate,
tax: product.tax, tax: product.tax,
modifiers: [], modifiers: [],
}; });
this.data.push(item); this.data.push(item);
this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => { this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => {
if ( if (
result.reduce((a: any, c: ModifierCategory) => { result.reduce((a: number, c: ModifierCategory) => {
return a + c.minimum; return a + c.minimum;
}, 0) }, 0)
) { ) {
@ -129,14 +128,14 @@ export class BillService {
this.updateAmounts(); this.updateAmounts();
} }
showModifier(item: any): void { showModifier(item: BillViewItem): void {
// [routerLink]="['/sales', 'modifiers', item.id]" // [routerLink]="['/sales', 'modifiers', item.id]"
const dialogRef = this.dialog.open(ModifiersComponent, { const dialogRef = this.dialog.open(ModifiersComponent, {
position: { position: {
top: '10vh', top: '10vh',
}, },
data: { data: {
list: this.modifierCategoryService.listForProduct(item.productId), list: this.modifierCategoryService.listForProduct(item.productId as string),
selected: item.modifiers, selected: item.modifiers,
}, },
}); });
@ -148,20 +147,23 @@ export class BillService {
}); });
} }
addOne(item: any): void { addOne(item: BillViewItem): void {
item.quantity += 1; item.quantity += 1;
this.dataObs.next(this.data); this.dataObs.next(this.data);
this.updateAmounts(); this.updateAmounts();
} }
quantity(item: any, quantity: number): void { quantity(item: BillViewItem, quantity: number): void {
item.quantity = quantity; item.quantity = quantity;
this.dataObs.next(this.data); this.dataObs.next(this.data);
this.updateAmounts(); this.updateAmounts();
} }
subtractOne(item: any, canEdit: boolean): void { subtractOne(item: BillViewItem, canEdit: boolean): void {
if (item.quantity > 1 || (canEdit && this.minimum(item.productId, item.isHappyHour) >= 1)) { if (
item.quantity > 1 ||
(canEdit && this.minimum(item.productId as string, item.isHappyHour) >= 1)
) {
item.quantity -= 1; item.quantity -= 1;
this.dataObs.next(this.data); this.dataObs.next(this.data);
this.updateAmounts(); this.updateAmounts();
@ -170,13 +172,13 @@ export class BillService {
} }
} }
removeItem(item: any): void { removeItem(item: BillViewItem): void {
this.data.splice(this.data.indexOf(item), 1); this.data.splice(this.data.indexOf(item), 1);
this.dataObs.next(this.data); this.dataObs.next(this.data);
this.updateAmounts(); this.updateAmounts();
} }
modifier(item: any): void { modifier(item: BillViewItem): void {
this.showModifier(item); this.showModifier(item);
} }
@ -230,7 +232,7 @@ export class BillService {
const item = JSON.parse(JSON.stringify(this.bill)); const item = JSON.parse(JSON.stringify(this.bill));
item.kots.forEach((k: Kot) => { item.kots.forEach((k: Kot) => {
k.inventories.forEach((i: Inventory) => { k.inventories.forEach((i: Inventory) => {
i.discount = this.data.find((x) => !x.isKot && x.id === i.id).discount; i.discount = (this.data.find((x) => !x.isKot && x.id === i.id) as BillViewItem).discount;
}); });
}); });
item.kots.push(this.getKot()); item.kots.push(this.getKot());
@ -273,7 +275,10 @@ export class BillService {
math.round( math.round(
this.data this.data
.filter((x) => !x.isKot) .filter((x) => !x.isKot)
.reduce((ca: number, c: any) => ca + (c.isHappyHour ? 0 : c.price) * c.quantity, 0), .reduce(
(ca: number, c: BillViewItem) => ca + (c.isHappyHour ? 0 : c.price) * c.quantity,
0,
),
), ),
); );
this.discountAmount.next( this.discountAmount.next(
@ -281,7 +286,7 @@ export class BillService {
this.data this.data
.filter((x) => !x.isKot) .filter((x) => !x.isKot)
.reduce( .reduce(
(ca: number, c: Inventory) => (ca: number, c: BillViewItem) =>
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * c.discount, ca + (c.isHappyHour ? 0 : c.price) * c.quantity * c.discount,
0, 0,
), ),
@ -292,7 +297,7 @@ export class BillService {
this.data this.data
.filter((x) => !x.isKot) .filter((x) => !x.isKot)
.reduce( .reduce(
(ca: number, c: Inventory) => (ca: number, c: BillViewItem) =>
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate, ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * c.taxRate,
0, 0,
), ),
@ -303,7 +308,7 @@ export class BillService {
this.data this.data
.filter((x) => !x.isKot) .filter((x) => !x.isKot)
.reduce( .reduce(
(ca: number, c: Inventory) => (ca: number, c: BillViewItem) =>
ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate), ca + (c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate),
0, 0,
), ),
@ -327,7 +332,9 @@ export class BillService {
} }
splitBill(table: Table): Observable<boolean> { splitBill(table: Table): Observable<boolean> {
const inventoriesToMove: string[] = this.selection.selected.map((x: any) => x.id); const inventoriesToMove: string[] = this.selection.selected.map(
(x: BillViewItem) => x.id as string,
);
return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table); return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table);
} }
} }

View File

@ -16,12 +16,12 @@ export class Bill {
kotId: string; kotId: string;
table: Table; table: Table;
guest: GuestBook; guest: GuestBook;
settlements: any[]; // settlements: any[];
voidReason: string; voidReason: string;
voucherType: string; voucherType: string;
serial: number; serial: number;
kots: Kot[]; kots: Kot[];
reprints: any[]; // reprints: any[];
get dateTip(): string { get dateTip(): string {
return this.date; return this.date;
@ -46,12 +46,12 @@ export class Bill {
this.kotId = ''; this.kotId = '';
this.table = new Table(); this.table = new Table();
this.guest = new GuestBook(); this.guest = new GuestBook();
this.settlements = []; // this.settlements = [];
this.voidReason = ''; this.voidReason = '';
this.voucherType = ''; this.voucherType = '';
this.serial = 0; this.serial = 0;
this.kots = []; this.kots = [];
this.reprints = []; // this.reprints = [];
Object.assign(this, init); Object.assign(this, init);
} }
} }

View File

@ -1,12 +1,14 @@
import { DataSource } from '@angular/cdk/collections'; import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
export class BillsDataSource extends DataSource<any> { import { BillViewItem } from '../../core/bill-view-item';
constructor(private data: Observable<any[]>) {
export class BillsDataSource extends DataSource<BillViewItem> {
constructor(private data: Observable<BillViewItem[]>) {
super(); super();
} }
connect(): Observable<any[]> { connect(): Observable<BillViewItem[]> {
return this.data; return this.data;
} }

View File

@ -52,7 +52,7 @@
<ng-container matColumnDef="select"> <ng-container matColumnDef="select">
<mat-cell *matCellDef="let row"> <mat-cell *matCellDef="let row">
<mat-checkbox <mat-checkbox
*ngIf="row.oldKot" *ngIf="row.isOldKot"
(change)="$event ? masterToggle(row) : null" (change)="$event ? masterToggle(row) : null"
[checked]="bs.selection.hasValue() && isAllSelected(row)" [checked]="bs.selection.hasValue() && isAllSelected(row)"
[indeterminate]="isAnySelected(row)" [indeterminate]="isAnySelected(row)"
@ -70,7 +70,7 @@
</ng-container> </ng-container>
<!-- Info Column --> <!-- Info Column -->
<ng-container matColumnDef="info"> <ng-container matColumnDef="info">
<mat-cell *matCellDef="let row" [class.blue800]="row.newKot"> <mat-cell *matCellDef="let row" [class.blue800]="row.isNewKot">
<span> <span>
{{ row.info }} {{ row.info }}
</span> </span>
@ -123,7 +123,12 @@
> >
<mat-icon class="del">assignment</mat-icon> <mat-icon class="del">assignment</mat-icon>
</button> </button>
<button mat-icon-button (click)="moveKot(row)" [disabled]="row.newKot" *ngIf="row.isKot"> <button
mat-icon-button
(click)="moveKot(row)"
[disabled]="row.isKot && !row.id"
*ngIf="row.isKot"
>
<mat-icon class="del">open_in_new</mat-icon> <mat-icon class="del">open_in_new</mat-icon>
</button> </button>
</mat-cell> </mat-cell>
@ -166,8 +171,8 @@
<mat-header-row *matHeaderRowDef="['table-title', 'table-details']"></mat-header-row> <mat-header-row *matHeaderRowDef="['table-title', 'table-details']"></mat-header-row>
<mat-row <mat-row
*matRowDef="let row; columns: displayedColumns" *matRowDef="let row; columns: displayedColumns"
[class.blue400]="row.oldKot" [class.blue400]="row.isOldKot"
[class.blue800]="row.newKot" [class.blue800]="row.isNewKot"
[class.red100]="row.isPrinted" [class.red100]="row.isPrinted"
></mat-row> ></mat-row>
<mat-footer-row *matFooterRowDef="['net-title', 'net-amount']"></mat-footer-row> <mat-footer-row *matFooterRowDef="['net-title', 'net-amount']"></mat-footer-row>

View File

@ -5,6 +5,7 @@ import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { AuthService } from '../../auth/auth.service'; import { AuthService } from '../../auth/auth.service';
import { BillViewItem } from '../../core/bill-view-item';
import { Table } from '../../core/table'; import { Table } from '../../core/table';
import { ToasterService } from '../../core/toaster.service'; import { ToasterService } from '../../core/toaster.service';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
@ -68,7 +69,7 @@ export class BillsComponent implements OnInit {
isAllSelected(kot: Kot) { isAllSelected(kot: Kot) {
return this.bs.data return this.bs.data
.filter((x) => x.kotId === kot.id) .filter((x) => x.kotId === kot.id)
.reduce((p: boolean, c: any) => p && this.bs.selection.isSelected(c), true); .reduce((p: boolean, c: BillViewItem) => p && this.bs.selection.isSelected(c), true);
} }
isAnySelected(kot: Kot) { isAnySelected(kot: Kot) {
@ -76,7 +77,7 @@ export class BillsComponent implements OnInit {
let found = 0; let found = 0;
this.bs.data this.bs.data
.filter((x) => x.kotId === kot.id) .filter((x) => x.kotId === kot.id)
.forEach((c: any) => { .forEach((c: BillViewItem) => {
total += 1; total += 1;
if (this.bs.selection.isSelected(c)) { if (this.bs.selection.isSelected(c)) {
found += 1; found += 1;
@ -94,11 +95,11 @@ export class BillsComponent implements OnInit {
); );
} }
addOne(item: any): void { addOne(item: BillViewItem): void {
this.bs.addOne(item); this.bs.addOne(item);
} }
quantity(item: any): void { quantity(item: BillViewItem): void {
const dialogRef = this.dialog.open(QuantityComponent, { const dialogRef = this.dialog.open(QuantityComponent, {
// width: '750px', // width: '750px',
data: item.quantity, data: item.quantity,
@ -123,16 +124,16 @@ export class BillsComponent implements OnInit {
}); });
} }
subtractOne(item: any): void { subtractOne(item: BillViewItem): void {
const canEdit = this.auth.allowed('edit-printed-product'); const canEdit = this.auth.allowed('edit-printed-product');
this.bs.subtractOne(item, canEdit); this.bs.subtractOne(item, canEdit);
} }
removeItem(item: any): void { removeItem(item: BillViewItem): void {
this.bs.removeItem(item); this.bs.removeItem(item);
} }
modifier(item: any): void { modifier(item: BillViewItem): void {
this.bs.modifier(item); this.bs.modifier(item);
} }
@ -202,7 +203,7 @@ export class BillsComponent implements OnInit {
); );
} }
rowQuantityDisabled(row: any) { rowQuantityDisabled(row: BillViewItem) {
if (!row.isPrinted) { if (!row.isPrinted) {
return false; return false;
} }

View File

@ -11,7 +11,7 @@ import { DiscountDataSource } from './discount-datasource';
styleUrls: ['./discount.component.css'], styleUrls: ['./discount.component.css'],
}) })
export class DiscountComponent { export class DiscountComponent {
list: any[] = []; list: { name: string; discount: number }[] = [];
form: FormGroup; form: FormGroup;
dataSource: DiscountDataSource = new DiscountDataSource([]); dataSource: DiscountDataSource = new DiscountDataSource([]);
@ -20,12 +20,12 @@ export class DiscountComponent {
constructor( constructor(
public dialogRef: MatDialogRef<DiscountComponent>, public dialogRef: MatDialogRef<DiscountComponent>,
private fb: FormBuilder, private fb: FormBuilder,
@Inject(MAT_DIALOG_DATA) public data: Observable<any[]>, @Inject(MAT_DIALOG_DATA) public data: Observable<{ name: string; discount: number }[]>,
) { ) {
this.form = this.fb.group({ this.form = this.fb.group({
discounts: '', discounts: '',
}); });
this.data.subscribe((list: any[]) => { this.data.subscribe((list: { name: string; discount: number }[]) => {
this.list = list; this.list = list;
this.form.setControl( this.form.setControl(
'discounts', 'discounts',

View File

@ -249,7 +249,7 @@ export class SalesHomeComponent {
} }
const amount = this.bs.amountVal(); const amount = this.bs.amountVal();
const type = this.bs.type(); const type = this.bs.type();
let obs: any; let obs: Observable<boolean>;
if (type === 'NO_CHARGE' || type === 'STAFF') { if (type === 'NO_CHARGE' || type === 'STAFF') {
obs = this.receivePaymentWithReason(type, amount); obs = this.receivePaymentWithReason(type, amount);
} else { } else {

View File

@ -1,6 +1,7 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormGroup } from '@angular/forms'; import { AbstractControl, FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatSelectChange } from '@angular/material/select';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@ -127,7 +128,7 @@ export class SectionPrinterComponent implements OnInit {
return this.list; return this.list;
} }
show(val: any) { show(val: MatSelectChange) {
this.router.navigate(['/section-printers', val.value]); this.router.navigate(['/section-printers', val.value]);
} }
} }

View File

@ -4,7 +4,8 @@ import { Pipe, PipeTransform } from '@angular/core';
name: 'clear', name: 'clear',
}) })
export class ClearPipe implements PipeTransform { export class ClearPipe implements PipeTransform {
transform(value: any): any { transform(value: string | null): string {
if (value === null) return '';
return value === '₹ 0.00' || value === '0.00' ? '' : value; return value === '₹ 0.00' || value === '0.00' ? '' : value;
} }
} }

View File

@ -9,6 +9,6 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
export class ConfirmDialogComponent { export class ConfirmDialogComponent {
constructor( constructor(
public dialogRef: MatDialogRef<ConfirmDialogComponent>, public dialogRef: MatDialogRef<ConfirmDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: { title: string; content: string },
) {} ) {}
} }

View File

@ -9,7 +9,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
export class ImageDialogComponent { export class ImageDialogComponent {
constructor( constructor(
public dialogRef: MatDialogRef<ImageDialogComponent>, public dialogRef: MatDialogRef<ImageDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: string,
) {} ) {}
close(): void { close(): void {

View File

@ -5,16 +5,6 @@ import { evaluate, round } from 'mathjs';
providedIn: 'root', providedIn: 'root',
}) })
export class MathService { export class MathService {
// eslint-disable-next-line class-methods-use-this
journalAmount(amount: string = '', debit: number): any {
const val = this.parseAmount(amount, 2);
let newDebit = debit;
if (val < 0) {
newDebit *= -1;
}
return { debit: newDebit, amount: Math.abs(val) };
}
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
parseAmount(amount: string = '', rounding: number = 2): number { parseAmount(amount: string = '', rounding: number = 2): number {
const cleaned = `${amount}`.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), ''); const cleaned = `${amount}`.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), '');

View File

@ -0,0 +1,3 @@
export interface ToCsvType {
[column: string]: string | number | boolean | null;
}

View File

@ -1,15 +1,19 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ToCsvType } from './to-csv-type';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class ToCsvService { export class ToCsvService {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
toCsv(headers: any, data: any[]): string { toCsv(headers: { [display: string]: string }, data: unknown[]): string {
const header = Object.keys(headers); const header = Object.keys(headers);
const replacer = (key: string, value: string | number | null) => (value === null ? '' : value); const replacer = (key: string, value: string | number | null) => (value === null ? '' : value);
const csv = data.map((row) => const csv = data.map((row) =>
header.map((fieldName) => JSON.stringify(row[headers[fieldName]], replacer)).join(','), header
.map((fieldName) => JSON.stringify((row as ToCsvType)[headers[fieldName]], replacer))
.join(','),
); );
csv.unshift(header.join(',')); csv.unshift(header.join(','));
return csv.join('\r\n'); return csv.join('\r\n');