Added feature: Split bill by Sales Category if no item is selected.
This commit is contained in:
parent
6257313792
commit
41d4ef1200
|
@ -89,6 +89,7 @@ export class BillService {
|
||||||
this.updateTable = updateTable;
|
this.updateTable = updateTable;
|
||||||
bill.kots.push(new Kot());
|
bill.kots.push(new Kot());
|
||||||
this.bill = bill;
|
this.bill = bill;
|
||||||
|
this.selection.clear();
|
||||||
this.displayBill();
|
this.displayBill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,11 +332,22 @@ export class BillService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
splitBill(table: Table): Observable<boolean> {
|
splitBill(inventories: string[], table: Table): Observable<boolean> {
|
||||||
const inventoriesToMove: string[] = this.selection.selected.map(
|
return this.ser.splitBill(this.bill.id as string, inventories, table, this.updateTable);
|
||||||
(x: string) => (JSON.parse(x) as BillSelectionItem).inventoryId as string,
|
}
|
||||||
);
|
|
||||||
return this.ser.splitBill(this.bill.id as string, inventoriesToMove, table, this.updateTable);
|
public getInventories(saleCategories: string[]): { move: string[]; keep: string[] } {
|
||||||
|
const data: { move: string[]; keep: string[] } = { move: [], keep: [] };
|
||||||
|
for (const kot of this.bill.kots) {
|
||||||
|
for (const inv of kot.inventories) {
|
||||||
|
if (saleCategories.indexOf(inv.product.saleCategory?.id as string) === -1) {
|
||||||
|
data.keep.push(inv.id as string);
|
||||||
|
} else {
|
||||||
|
data.move.push(inv.id as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private happyHourItemsBalanced(): boolean {
|
private happyHourItemsBalanced(): boolean {
|
||||||
|
|
|
@ -13,10 +13,12 @@ import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dial
|
||||||
import { TableService } from '../../tables/table.service';
|
import { TableService } from '../../tables/table.service';
|
||||||
import { BillTypeComponent } from '../bill-type/bill-type.component';
|
import { BillTypeComponent } from '../bill-type/bill-type.component';
|
||||||
import { BillService } from '../bill.service';
|
import { BillService } from '../bill.service';
|
||||||
|
import { BillSelectionItem } from '../bills/bill-selection-item';
|
||||||
import { VoucherType } from '../bills/voucher-type';
|
import { VoucherType } from '../bills/voucher-type';
|
||||||
import { DiscountComponent } from '../discount/discount.component';
|
import { DiscountComponent } from '../discount/discount.component';
|
||||||
import { ReasonComponent } from '../reason/reason.component';
|
import { ReasonComponent } from '../reason/reason.component';
|
||||||
import { ReceivePaymentComponent } from '../receive-payment/receive-payment.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';
|
import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -100,17 +102,17 @@ export class SalesHomeComponent {
|
||||||
return this.dialog.open(BillTypeComponent).afterClosed();
|
return this.dialog.open(BillTypeComponent).afterClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmTableDialog(table: Table): Observable<{ table: Table; confirmed: boolean }> {
|
confirmTableDialog(table: Table): Observable<Table | boolean> {
|
||||||
return this.dialog
|
return this.dialog
|
||||||
.open(ConfirmDialogComponent, {
|
.open(ConfirmDialogComponent, {
|
||||||
width: '250px',
|
width: '250px',
|
||||||
data: { title: 'Select Table?', content: 'Are you sure?' },
|
data: { title: 'Select Table?', content: 'Are you sure?' },
|
||||||
})
|
})
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(map((x: boolean) => ({ table, confirmed: x })));
|
.pipe(map((x: boolean) => (x ? table : x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmVoidDialog(reason: string): Observable<boolean | string> {
|
confirmVoidDialog(reason: string): Observable<string | boolean> {
|
||||||
return this.dialog
|
return this.dialog
|
||||||
.open(ConfirmDialogComponent, {
|
.open(ConfirmDialogComponent, {
|
||||||
width: '250px',
|
width: '250px',
|
||||||
|
@ -237,32 +239,22 @@ export class SalesHomeComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const canMergeTables = this.auth.allowed('merge-tables');
|
const canMergeTables = this.auth.allowed('merge-tables');
|
||||||
this.dialog
|
this.showChooseTableDialog(canMergeTables)
|
||||||
.open(TablesDialogComponent, {
|
|
||||||
// width: '750px',
|
|
||||||
data: {
|
|
||||||
list: this.tSer.running(),
|
|
||||||
canChooseRunning: canMergeTables,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.afterClosed()
|
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((x: boolean | Table) => {
|
tap((x) => {
|
||||||
if (x) {
|
if (!x) {
|
||||||
return this.confirmTableDialog(x as Table);
|
throw new Error('Please choose a table');
|
||||||
}
|
}
|
||||||
throw new Error('Please choose a table');
|
|
||||||
}),
|
}),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
map((x) => x as Table),
|
||||||
switchMap((value: { table: Table; confirmed: boolean }, index: number) => {
|
switchMap((x: Table) => this.confirmTableDialog(x)),
|
||||||
if (!value.confirmed) {
|
tap((x) => {
|
||||||
throw new Error('Please confirm move');
|
if (!x) {
|
||||||
|
throw new Error('Move Table Cancelled');
|
||||||
}
|
}
|
||||||
if (value.table.status) {
|
|
||||||
return this.bs.mergeTable(value.table);
|
|
||||||
}
|
|
||||||
return this.bs.moveTable(value.table);
|
|
||||||
}),
|
}),
|
||||||
|
map((x) => x as Table),
|
||||||
|
switchMap((x: Table) => (x.status ? this.bs.mergeTable(x) : this.bs.moveTable(x))),
|
||||||
)
|
)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => {
|
() => {
|
||||||
|
@ -341,33 +333,56 @@ export class SalesHomeComponent {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
splitBill() {
|
showBillSplitChoices(): Observable<
|
||||||
if (!this.splitBillAllowed()) {
|
{ id: string; name: string; selected: boolean }[] | undefined | false
|
||||||
return;
|
> {
|
||||||
}
|
return this.dialog
|
||||||
this.dialog
|
.open(SplitBillComponent, {
|
||||||
|
data: this.mcSer
|
||||||
|
.list()
|
||||||
|
.pipe(map((x) => x.map((y) => ({ id: y.id, name: y.name, selected: false })))),
|
||||||
|
})
|
||||||
|
.afterClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
showChooseTableDialog(canChooseRunning: boolean): Observable<Table | undefined | null> {
|
||||||
|
return this.dialog
|
||||||
.open(TablesDialogComponent, {
|
.open(TablesDialogComponent, {
|
||||||
// width: '750px',
|
// width: '750px',
|
||||||
data: {
|
data: {
|
||||||
list: this.tSer.running(),
|
list: this.tSer.running(),
|
||||||
canChooseRunning: false,
|
canChooseRunning,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.afterClosed()
|
.afterClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
splitBill() {
|
||||||
|
if (!this.splitBillAllowed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const obs = this.bs.selection.isEmpty()
|
||||||
|
? this.splitBillWithChoice()
|
||||||
|
: this.splitBillWithSelection();
|
||||||
|
let inventories: string[];
|
||||||
|
obs
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((x: boolean | Table) => {
|
tap((x) => (inventories = x)),
|
||||||
if (x) {
|
switchMap((x) => this.showChooseTableDialog(false)),
|
||||||
return this.confirmTableDialog(x as Table);
|
tap((x) => {
|
||||||
|
if (!x) {
|
||||||
|
throw new Error('Please choose a table');
|
||||||
}
|
}
|
||||||
throw new Error('Please choose a table');
|
|
||||||
}),
|
}),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
map((x) => x as Table),
|
||||||
switchMap((value: { table: Table; confirmed: boolean }, index: number) => {
|
switchMap((x: Table) => this.confirmTableDialog(x)),
|
||||||
if (!value.confirmed) {
|
tap((x) => {
|
||||||
throw new Error('Please confirm split');
|
if (!x) {
|
||||||
|
throw new Error('Bill Split Cancelled');
|
||||||
}
|
}
|
||||||
return this.bs.splitBill(value.table);
|
|
||||||
}),
|
}),
|
||||||
|
map((x) => x as Table),
|
||||||
|
switchMap((x: Table) => this.bs.splitBill(inventories, x)),
|
||||||
)
|
)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => {
|
() => {
|
||||||
|
@ -379,4 +394,35 @@ export class SalesHomeComponent {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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((x) => inventories),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
splitBillWithSelection() {
|
||||||
|
const inventories: string[] = this.bs.selection.selected.map(
|
||||||
|
(x: string) => (JSON.parse(x) as BillSelectionItem).inventoryId as string,
|
||||||
|
);
|
||||||
|
return observableOf(inventories);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { ReasonComponent } from './reason/reason.component';
|
||||||
import { ReceivePaymentComponent } from './receive-payment/receive-payment.component';
|
import { ReceivePaymentComponent } from './receive-payment/receive-payment.component';
|
||||||
import { RunningTablesComponent } from './running-tables/running-tables.component';
|
import { RunningTablesComponent } from './running-tables/running-tables.component';
|
||||||
import { SalesRoutingModule } from './sales-routing.module';
|
import { SalesRoutingModule } from './sales-routing.module';
|
||||||
|
import { SplitBillComponent } from './split-bill/split-bill.component';
|
||||||
import { TablesDialogComponent } from './tables-dialog/tables-dialog.component';
|
import { TablesDialogComponent } from './tables-dialog/tables-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -51,6 +52,7 @@ import { TablesDialogComponent } from './tables-dialog/tables-dialog.component';
|
||||||
QuantityComponent,
|
QuantityComponent,
|
||||||
ReceivePaymentComponent,
|
ReceivePaymentComponent,
|
||||||
RunningTablesComponent,
|
RunningTablesComponent,
|
||||||
|
SplitBillComponent,
|
||||||
SalesHomeComponent,
|
SalesHomeComponent,
|
||||||
TablesDialogComponent,
|
TablesDialogComponent,
|
||||||
ReasonComponent,
|
ReasonComponent,
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<h2 mat-dialog-title>Select Sale Categories</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<form [formGroup]="form" fxLayout="column">
|
||||||
|
<div formArrayName="saleCategories">
|
||||||
|
<div
|
||||||
|
fxLayout="row"
|
||||||
|
*ngFor="let sc of list; index as i"
|
||||||
|
[formGroupName]="i"
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayoutAlign="space-around start"
|
||||||
|
fxLayout.lt-md="column"
|
||||||
|
fxLayoutGap="20px"
|
||||||
|
fxLayoutGap.lt-md="0px"
|
||||||
|
>
|
||||||
|
<mat-checkbox formControlName="saleCategory" fxFlex>{{ sc.name }}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button [mat-dialog-close]="false">Cancel</button>
|
||||||
|
<button mat-button (click)="accept()" color="primary">Ok</button>
|
||||||
|
</mat-dialog-actions>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SplitBillComponent } from './split-bill.component';
|
||||||
|
|
||||||
|
describe('SplitBillComponent', () => {
|
||||||
|
let component: SplitBillComponent;
|
||||||
|
let fixture: ComponentFixture<SplitBillComponent>;
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SplitBillComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SplitBillComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-split-bill',
|
||||||
|
templateUrl: './split-bill.component.html',
|
||||||
|
styleUrls: ['./split-bill.component.css'],
|
||||||
|
})
|
||||||
|
export class SplitBillComponent {
|
||||||
|
list: { id: string; name: string; selected: boolean }[] = [];
|
||||||
|
form: FormGroup;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<SplitBillComponent>,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: Observable<{ id: string; name: string; selected: boolean }[]>,
|
||||||
|
) {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
saleCategories: this.fb.array([]),
|
||||||
|
});
|
||||||
|
this.data.subscribe((list: { id: string; name: string; selected: boolean }[]) => {
|
||||||
|
this.list = list;
|
||||||
|
this.form.setControl(
|
||||||
|
'saleCategories',
|
||||||
|
this.fb.array(
|
||||||
|
this.list.map((x) =>
|
||||||
|
this.fb.group({
|
||||||
|
saleCategory: x.selected,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
accept(): void {
|
||||||
|
const array = this.form.get('saleCategories') as FormArray;
|
||||||
|
this.list.forEach((item, index) => {
|
||||||
|
item.selected = array.controls[index].value.saleCategory;
|
||||||
|
});
|
||||||
|
this.dialogRef.close(this.list);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue