import { Component } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; import { AuthService } from '../../auth/auth.service'; import { ReceivePaymentItem } from '../../core/receive-payment-item'; import { Table } from '../../core/table'; import { ToasterService } from '../../core/toaster.service'; import { SaleCategoryService } from '../../sale-category/sale-category.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { TableService } from '../../tables/table.service'; import { BillTypeComponent } from '../bill-type/bill-type.component'; import { BillService } from '../bill.service'; import { BillSelectionItem } from '../bills/bill-selection-item'; import { VoucherType } from '../bills/voucher-type'; import { CustomerDiscountsService } from '../discount/customer-discounts.service'; import { DiscountComponent } from '../discount/discount.component'; import { ReasonComponent } from '../reason/reason.component'; import { ReceivePaymentComponent } from '../receive-payment/receive-payment.component'; import { SplitBillComponent } from '../split-bill/split-bill.component'; import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component'; @Component({ selector: 'app-sales-home', templateUrl: './sales-home.component.html', styleUrls: ['./sales-home.component.css'], }) export class SalesHomeComponent { constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, private auth: AuthService, private toaster: ToasterService, private saleCategoryService: SaleCategoryService, private tableService: TableService, private customerDiscountsService: CustomerDiscountsService, private bs: BillService, ) {} printKotAllowed(): boolean { if (!this.auth.allowed('print-kot')) { return false; } if (!this.bs.bill.id) { return true; } if (this.bs.bill.voucherType !== VoucherType.Kot) { return false; } return true; } printKot() { if (!this.printKotAllowed()) { return; } let guestBookId = null; if (this.route.snapshot.queryParamMap.has('guest')) { guestBookId = this.route.snapshot.queryParamMap.get('guest'); } this.bs.printKot(guestBookId).subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (result) => { this.toaster.show('Error', result); }, ); } discountAllowed(): boolean { return this.auth.allowed('discount'); } showDiscount(): Observable { if (this.discountAllowed()) { const dialogRef = this.dialog.open(DiscountComponent, { // width: '750px', data: this.customerDiscountsService.list(this.bs.bill.customer?.id), }); return dialogRef.afterClosed(); } return observableOf(false); } discount(): void { this.showDiscount().subscribe((result: boolean | { id: string; name: string; discount: number }[]) => { if (result) { this.bs.discount(result as { id: string; name: string; discount: number }[]); } }); } billTypeDialog() { if (this.bs.bill.voucherType !== VoucherType.Kot) { return observableOf(this.bs.bill.voucherType); } return this.dialog.open(BillTypeComponent).afterClosed(); } confirmTableDialog(table: Table): Observable { return this.dialog .open(ConfirmDialogComponent, { width: '250px', data: { title: 'Select Table?', content: 'Are you sure?' }, }) .afterClosed() .pipe(map((x: boolean) => (x ? table : x))); } confirmCancelDialog(reason: string): Observable { return this.dialog .open(ConfirmDialogComponent, { width: '250px', data: { title: 'Cancel Bill?', content: 'Are you sure?' }, }) .afterClosed() .pipe(map((x: boolean) => (x ? reason : x))); } printBillAllowed(): boolean { if (!this.auth.allowed('print-bill')) { return false; } if (!this.bs.bill.id) { return true; } if (this.bs.bill.voucherType !== VoucherType.Kot && !this.auth.allowed('edit-printed-bill')) { return false; } if (this.bs.bill.voucherType === VoucherType.Void) { return false; } return true; } printBill() { if (!this.printBillAllowed()) { return; } let guestBookId: string | null = null; if (this.route.snapshot.queryParamMap.has('guest')) { guestBookId = this.route.snapshot.queryParamMap.get('guest'); } this.showDiscount() .pipe( tap((result: boolean | { id: string; name: string; discount: number }[]) => { if (result) { this.bs.discount(result as { id: string; name: string; discount: number }[]); } else if (this.discountAllowed()) { throw new Error('Discount Selection Cancelled'); } }), switchMap(() => this.billTypeDialog()), switchMap((x) => { if (x) { return observableOf(x); } throw new Error('No Bill Type Chosen'); }), switchMap((x: VoucherType) => this.bs.printBill(guestBookId, x as VoucherType)), ) .subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (result) => { this.toaster.show('Error', result); }, ); } receivePaymentAllowed(): boolean { if (!this.auth.allowed('settle-bill')) { return false; } if (!this.bs.bill.id) { return false; } if (this.bs.bill.voucherType === VoucherType.Kot) { return false; } if (this.bs.bill.voucherType === VoucherType.Void) { return false; } return true; } receivePayment() { if (!this.receivePaymentAllowed()) { return; } const amount = this.bs.amountVal; const type = this.bs.bill.voucherType; this.dialog .open(ReceivePaymentComponent, { data: { type, amount, }, }) .afterClosed() .pipe( switchMap((value: undefined | { choices: ReceivePaymentItem[]; reason: string }) => { if (value === undefined) { throw new Error('Receive Payment Choices / Reason not selected'); } return this.bs.receivePayment(value); }), ) .subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (value) => { this.toaster.show('Error', value); }, ); } moveTableAllowed(): boolean { if (!this.auth.allowed('move-table') && !this.auth.allowed('merge-tables')) { return false; } if (!this.bs.bill.id) { return false; } return true; } moveTable() { if (!this.moveTableAllowed()) { return; } const canMergeTables = this.auth.allowed('merge-tables'); this.showChooseTableDialog(canMergeTables) .pipe( tap((x) => { if (!x) { throw new Error('Please choose a table'); } }), map((x) => x as Table), switchMap((x: Table) => this.confirmTableDialog(x)), tap((x) => { if (!x) { throw new Error('Move Table Cancelled'); } }), map((x) => x as Table), switchMap((x: Table) => (x.status ? this.bs.mergeTable(x) : this.bs.moveTable(x))), ) .subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (x) => { this.toaster.show('Error', x); }, ); } cancelBillAllowed(): boolean { if (!this.auth.allowed('void-bill')) { return false; } if (!this.bs.bill.id) { return false; } return true; } cancelBill() { if (!this.cancelBillAllowed()) { return; } this.dialog .open(ReasonComponent, { // width: '750px' data: { title: 'Cancel Reason', reasons: [ 'Discount', 'Printing fault', 'Item changed', 'Quantity reduced', 'Costing bill for party', 'Cashier mistake', 'Management free sale', ], }, }) .afterClosed() .pipe( switchMap((x: boolean | string) => { if (x) { return this.confirmCancelDialog(x as string); } throw new Error('Please choose a reason to cancel the bill'); }), switchMap((x: boolean | string) => { if (x) { return this.bs.cancelBill(x as string); } throw new Error('You chose not to cancel the bill'); }), ) .subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (x) => { this.toaster.show('Error', x); }, ); } splitBillAllowed(): boolean { if (!this.auth.allowed('split-bill')) { return false; } if (!this.bs.bill.id) { return false; } return true; } showBillSplitChoices(): Observable<{ id: string; name: string; selected: boolean }[] | undefined | false> { return this.dialog .open(SplitBillComponent, { data: this.saleCategoryService .list() .pipe(map((x) => x.map((y) => ({ id: y.id, name: y.name, selected: false })))), }) .afterClosed(); } showChooseTableDialog(canChooseRunning: boolean): Observable
{ return this.dialog .open(TablesDialogComponent, { data: { list: this.tableService.running(), canChooseRunning, }, }) .afterClosed(); } splitBill() { if (!this.splitBillAllowed()) { return; } const obs = this.bs.selection.isEmpty() ? this.splitBillWithChoice() : this.splitBillWithSelection(); let inventories: string[]; obs .pipe( tap((x) => (inventories = x)), switchMap(() => this.showChooseTableDialog(false)), tap((x) => { if (!x) { throw new Error('Please choose a table'); } }), map((x) => x as Table), switchMap((x: Table) => this.confirmTableDialog(x)), tap((x) => { if (!x) { throw new Error('Bill Split Cancelled'); } }), map((x) => x as Table), switchMap((x: Table) => this.bs.splitBill(inventories, x)), ) .subscribe( () => { this.toaster.show('Success', ''); this.router.navigate(['/sales']); }, (x) => { this.toaster.show('Error', x); }, ); } splitBillWithChoice() { let inventories: string[]; return this.showBillSplitChoices().pipe( tap((x) => { if (x === undefined || x === false) { throw new Error('Cancelled'); } }), map((x) => (x as { id: string; name: string; selected: boolean }[]).filter((y) => y.selected).map((y) => y.id)), tap((x: string[]) => { const sel = this.bs.getInventories(x); if (sel.keep.length === 0 || sel.move.length === 0) { throw new Error('This will move either All or None of the items. Cancelled'); } inventories = sel.move; }), map(() => inventories), ); } splitBillWithSelection() { const inventories: string[] = this.bs.selection.selected.map( (x: string) => (JSON.parse(x) as BillSelectionItem).inventoryId as string, ); return observableOf(inventories); } }