Move / Merge KOT Done.

We need to check if it is the only kot and raise an error if it is.
Split Bill Done
This commit is contained in:
Amritanshu
2019-08-18 01:05:59 +05:30
parent dcaf23b390
commit e697631cd4
15 changed files with 539 additions and 295 deletions

View File

@ -10,18 +10,19 @@ import { ModifierCategory } from '../core/modifier-category';
import { Bill, Inventory, Kot, PrintType } from './bills/bill';
import { VoucherService } from './bills/voucher.service';
import { ToasterService } from '../core/toaster.service';
import { tap } from 'rxjs/operators';
import { Table } from '../core/table';
import { SelectionModel } from "@angular/cdk/collections";
@Injectable()
export class BillService {
public dataObs;
public data;
public data: any[];
private bill;
public netAmount: BehaviorSubject<number>;
public discountAmount: BehaviorSubject<number>;
public taxAmount: BehaviorSubject<number>;
public amount: BehaviorSubject<number>;
public selection = new SelectionModel<any>(true, []);
constructor(
private dialog: MatDialog,
@ -41,12 +42,14 @@ export class BillService {
this.bill = bill;
const view = this.bill.kots.map(k => {
return [{
id: k.id,
isKot: true,
oldKot: true,
info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `
}, ...k.inventories.map(i => {
return {
id: i.id,
kotId: k.id,
isKot: false,
product: i.product,
productId: i.product.id,
@ -178,7 +181,7 @@ export class BillService {
});
}
printKot(guest_book_id: string): Observable<Bill> {
printKot(guest_book_id: string): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
const newKot = this.getKot();
if (newKot.inventories.length == 0) {
@ -188,7 +191,7 @@ export class BillService {
return this.ser.saveOrUpdate(item, PrintType.Kot, guest_book_id, true);
}
printBill(guest_book_id: string, printType: PrintType): Observable<Bill> {
printBill(guest_book_id: string, printType: PrintType): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
item.kots.forEach(k => {
k.inventories.forEach(i => {
@ -211,8 +214,16 @@ export class BillService {
return this.ser.moveTable(this.bill.id, table);
}
moveKot(id: string, table: Table): Observable<boolean> {
return this.ser.moveKot(this.bill.id, id, table);
mergeTable(table: Table): Observable<boolean> {
return this.ser.mergeTable(this.bill.id, table);
}
moveKot(kotId: string, table: Table): Observable<boolean> {
return this.ser.moveKotToNewTable(this.bill.id, kotId, table);
}
mergeKot(kotId: string, table: Table): Observable<boolean> {
return this.ser.mergeKotWithOldBill(this.bill.id, kotId, table);
}
voidBill(reason: string): Observable<boolean> {
@ -249,4 +260,9 @@ export class BillService {
, 0)
, 0));
}
splitBill(table: Table): Observable<boolean> {
const inventoriesToMove: string[] = this.selection.selected.map((x:any)=> x.id)
return this.ser.splitBill(this.bill.id, inventoriesToMove, table);
}
}

View File

@ -13,6 +13,10 @@ table {
width: 100%;
}
.mat-column-select{
flex: 0 0 50px;
}
.grey900 {
background-color: #1b5e20;
color: #ffffff;

View File

@ -8,6 +8,22 @@
</mat-card-title-group>
<mat-card-content>
<table mat-table #table [dataSource]="dataSource" aria-label="Elements" class="mat-elevation-z8">
<!-- Checkbox Column -->
<ng-container matColumnDef="select">
<mat-cell *matCellDef="let row">
<mat-checkbox *ngIf="row.oldKot"
(change)="$event ? masterToggle(row) : null"
[checked]="bs.selection.hasValue() && isAllSelected(row)"
[indeterminate]="isAnySelected(row)">
</mat-checkbox>
<mat-checkbox *ngIf="!row.isKot" [disabled]="!row.id"
(click)="$event.stopPropagation()"
(change)="$event ? bs.selection.toggle(row) : null"
[checked]="bs.selection.isSelected(row)">
</mat-checkbox>
</mat-cell>
<mat-footer-cell *matFooterCellDef class="grey900">Amount</mat-footer-cell>
</ng-container>
<!-- Info Column -->
<ng-container matColumnDef="info">
<mat-cell *matCellDef="let row" [class.blue800]="row.newKot">
@ -18,7 +34,7 @@
<li *ngFor="let m of row.modifiers">{{m.name}}</li>
</ul>
</mat-cell>
<mat-footer-cell *matFooterCellDef class="grey900 bold">Amount</mat-footer-cell>
<mat-footer-cell *matFooterCellDef class="grey900 bold"></mat-footer-cell>
</ng-container>
<!-- Quantity Column -->
<ng-container matColumnDef="quantity">
@ -39,7 +55,7 @@
<button mat-icon-button (click)="modifier(row)" [disabled]="row.isPrinted" *ngIf="!row.isKot">
<mat-icon class="del">assignment</mat-icon>
</button>
<button mat-icon-button (click)="modifier(row)" [disabled]="row.newKot" *ngIf="row.isKot">
<button mat-icon-button (click)="moveKot(row)" [disabled]="row.newKot" *ngIf="row.isKot">
<mat-icon class="del">open_in_new</mat-icon>
</button>
</mat-cell>

View File

@ -12,6 +12,8 @@ import { map, switchMap } from 'rxjs/operators';
import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component';
import { TableService } from '../../tables/table.service';
import { ToasterService } from '../../core/toaster.service';
import { AuthService } from "../../auth/auth.service";
import { SelectionModel } from "@angular/cdk/collections";
@Component({
selector: 'app-bills',
@ -22,13 +24,14 @@ export class BillsComponent implements OnInit {
dataSource: BillsDataSource;
item: Bill;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns: string[] = ['info', 'quantity'];
displayedColumns: string[] = ['select', 'info', 'quantity'];
constructor(
private route: ActivatedRoute,
private router: Router,
private dialog: MatDialog,
private toaster: ToasterService,
private auth: AuthService,
private bs: BillService,
private tSer: TableService
) {
@ -43,6 +46,38 @@ export class BillsComponent implements OnInit {
this.dataSource = new BillsDataSource(this.bs.dataObs);
}
isAllSelected(kot: Kot) {
return this.bs.data.filter(
x => x.kotId === kot.id
).reduce(
(p: boolean, c: any) => p && this.bs.selection.isSelected(c)
, true
);
}
isAnySelected(kot: Kot) {
let total: number = 0,
found: number = 0;
this.bs.data.filter(
x => x.kotId === kot.id
).forEach((c: any) => {
total += 1;
if (this.bs.selection.isSelected(c)) {
found += 1;
}
});
return found > 0 && found < total;
}
masterToggle(kot: Kot) {
const isAllSelected = this.isAllSelected(kot);
this.bs.data.filter(
x => x.kotId === kot.id
).forEach(
row => isAllSelected ? this.bs.selection.deselect(row) : this.bs.selection.select(row)
);
}
addOne(item: any): void {
this.bs.addOne(item);
}
@ -72,4 +107,48 @@ export class BillsComponent implements OnInit {
modifier(item: any): void {
this.bs.modifier(item);
}
confirmMoveKotDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
return this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Move KOT?', content: 'Are you sure?'}
}).afterClosed().pipe(
map ((x: boolean) => ({table: table, confirmed: x}))
);
}
moveKot(kot: Kot) {
const canMergeTables = this.auth.hasPermission("Merge Tables");
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: canMergeTables
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmMoveKotDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!value.confirmed) {
return throwError('Please confirm move');
} else if (value.table.status) {
return this.bs.mergeKot(kot.id, value.table);
} else {
return this.bs.moveKot(kot.id, value.table);
}
}
)
).subscribe((x) => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
this.toaster.show('Error', x);
});
}
}

View File

@ -11,6 +11,8 @@ const httpOptions = {
};
const url = '/v1/vouchers';
const urlMoveTable = '/v1/move-table';
const urlMoveKot = '/v1/move-kot';
const serviceName = 'VoucherService';
@Injectable({providedIn: 'root'})
@ -47,29 +49,29 @@ export class VoucherService {
);
}
save(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
save(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('p', printType).set('u', updateTable.toString())};
if (guest_book_id !== null) {
options.params = options.params.set('g', guest_book_id);
}
return <Observable<Bill>>this.http.post<Bill>(`${url}/new`, voucher, options)
return <Observable<boolean>>this.http.post<boolean>(`${url}/new`, voucher, options)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
update(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('p', printType).set('u', updateTable.toString())};
if (guest_book_id !== null) {
options.params = options.params.set('g', guest_book_id);
}
return <Observable<Bill>>this.http.put<Bill>(`${url}/${voucher.id}`, voucher, options)
return <Observable<boolean>>this.http.put<boolean>(`${url}/${voucher.id}`, voucher, options)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);
}
saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<boolean> {
if (!voucher.id) {
return this.save(voucher, printType, guest_book_id, updateTable);
} else {
@ -78,26 +80,75 @@ export class VoucherService {
}
receivePayment(id: string, amounts: { id: string; name: string; amount: number }[], updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, amounts, options)
.pipe(
catchError(this.log.handleError(serviceName, 'receivePayment'))
);
}
moveTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move-table', '')};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {table: {id: table.id}}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'moveTable'))
);
const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, amounts, options
).pipe(
catchError(this.log.handleError(serviceName, 'receivePayment'))
);
}
voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('void-bill', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {reason: reason}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'voidBill'))
);
const options = {params: new HttpParams().set('void-bill', '').set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, {reason: reason}, options
).pipe(
catchError(this.log.handleError(serviceName, 'voidBill'))
);
}
moveTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveTable, {
voucher: {id: id},
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'moveTable'))
);
}
mergeTable(id: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('merge', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveTable, {
voucher: {id: id},
table: {id: table.id},
newVoucher: {id: table.voucherId}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'mergeTable'))
);
}
moveKotToNewTable(id: string, kotId: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('move', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveKot, {
voucher: {id: id},
kot: {id: kotId},
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'moveKotToNewTable'))
);
}
mergeKotWithOldBill(id: string, kotId: string, table: Table): Observable<boolean> {
const options = {params: new HttpParams().set('merge', '')};
return <Observable<boolean>>this.http.post<boolean>(urlMoveKot, {
voucher: {id: id},
kot: {id: kotId},
table: {id: table.id},
newVoucher: {id: table.voucherId}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'mergeKotWithOldBill'))
);
}
splitBill(id: string, inventoriesToMove: string[], table: Table) {
const options = {params: new HttpParams().set('split-bill', '').set('u', 'true')};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {
voucher: {id: id},
inventories: inventoriesToMove,
table: {id: table.id}
}, options).pipe(
catchError(this.log.handleError(serviceName, 'splitBill'))
);
}
}

View File

@ -24,10 +24,7 @@
<mat-card fxLayout="column" class="square-button" matRipple (click)="voidBill()">
<h3 class="item-name">Void Bill</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">
<h3 class="item-name">Move KOT</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">
<mat-card fxLayout="column" class="square-button" matRipple (click)="splitBill()">
<h3 class="item-name">Split Bill</h3>
</mat-card>
</div>

View File

@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog } from '@angular/material';
import { concatMap, map, switchMap, tap } from 'rxjs/operators';
import { iif, Observable, of as observableOf, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { Observable, of as observableOf, throwError } from 'rxjs';
import { BillService } from '../bill.service';
import { ToasterService } from '../../core/toaster.service';
import { DiscountComponent } from '../discount/discount.component';
@ -70,13 +70,11 @@ export class SalesHomeComponent implements OnInit {
}
discountDialog (canGiveDiscount: boolean): Observable<any> {
let discObs = null;
if (canGiveDiscount) {
return discObs = this.showDiscount();
return this.showDiscount();
} else {
return discObs = observableOf('');
return observableOf('');
}
}
billTypeDialog() {
@ -89,10 +87,10 @@ export class SalesHomeComponent implements OnInit {
);
}
confirmMoveTableDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
confirmTableDialog(table: Table): Observable<{table: Table, confirmed: boolean}> {
return this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Move Table?', content: 'Are you sure?'}
data: {title: 'Select Table?', content: 'Are you sure?'}
}).afterClosed().pipe(
map ((x: boolean) => ({table: table, confirmed: x}))
);
@ -114,20 +112,19 @@ export class SalesHomeComponent implements OnInit {
guestBookId = this.route.snapshot.queryParamMap.get('guest');
}
this.discountDialog(canGiveDiscount).pipe(
concatMap(() => this.billTypeDialog())
).pipe(
concatMap(
(x: boolean | PrintType) => iif(
() => !!x,
this.bs.printBill(guestBookId, x as PrintType),
throwError(x)
)
),
switchMap(() => this.billTypeDialog()),
switchMap((x: boolean | PrintType) => {
if (!!x) {
return this.bs.printBill(guestBookId, x as PrintType);
} else {
return throwError(x);
}
}),
).subscribe(() => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
() => {
this.toaster.show('Error', 'No Bill Type Chosen');
});
}
@ -153,29 +150,32 @@ export class SalesHomeComponent implements OnInit {
}
moveTable() {
const canMergeTables = this.auth.hasPermission("Merge Tables");
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: false
canChooseRunning: canMergeTables
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmMoveTableDialog(x as Table);
if (!x) {
return this.confirmTableDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!!value.confirmed) {
return this.bs.moveTable(value.table);
} else {
if (!value.confirmed) {
return throwError('Please confirm move');
} else if (value.table.status) {
return this.bs.mergeTable(value.table);
} else {
return this.bs.moveTable(value.table);
}
}
)
).subscribe((x) => {
).subscribe(() => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
@ -210,4 +210,35 @@ export class SalesHomeComponent implements OnInit {
this.toaster.show('Error', x);
});
}
splitBill() {
this.dialog.open(TablesDialogComponent, {
// width: '750px',
data: {
list: this.tSer.running(),
canChooseRunning: false
}
}).afterClosed().pipe(
switchMap((x: boolean | Table) => {
if (!!x) {
return this.confirmTableDialog(x as Table);
} else {
return throwError('Please choose a table');
}
}),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
if (!value.confirmed) {
return throwError('Please confirm split');
} else {
return this.bs.splitBill(value.table);
}
})
).subscribe((x) => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
this.toaster.show('Error', x);
});
}
}

View File

@ -5,6 +5,7 @@ import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
@ -57,6 +58,7 @@ import { VoidReasonComponent } from './void-reason/void-reason.component';
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatChipsModule,
MatDialogModule,
MatDividerModule,