Save Bill Works

This commit is contained in:
Amritanshu
2019-07-13 21:32:18 +05:30
parent bcad4cdae3
commit 7d06a2f961
43 changed files with 660 additions and 349 deletions

View File

@ -1,49 +1,78 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material';
import { BehaviorSubject } from "rxjs";
import { BehaviorSubject } from 'rxjs';
import { Product } from '../core/product';
import { ModifiersComponent } from './modifiers/modifiers.component';
import { ModifierCategoryService } from '../modifier-categories/modifier-category.service';
import { ModifierCategory } from "../core/modifier-category";
import { ModifierCategory } from '../core/modifier-category';
import { Bill, Inventory, Kot, PrintType } from './bills/bill';
import {VoucherService} from './bills/voucher.service';
@Injectable()
export class BillService {
public dataObs;
public data;
private bill;
constructor(
private dialog: MatDialog,
private ser: VoucherService,
private modifierCategoryService: ModifierCategoryService
) {
this.data = [];
this.dataObs = new BehaviorSubject<any[]>(this.data);
}
loadData(d: any): void {
console.log("data loaded");
this.data = d;
this.data.push({isKot: true, newKot: true, info: "== New Kot =="})
loadData(bill: Bill): void {
this.bill = bill;
const view = this.bill.kots.map(k => {
return [{
isKot: true,
info: `Kot: ${k.code} / ${k.date} (${k.user.name}) `
}, ...k.inventories.map(i => {
return {
id: i.id,
isKot: false,
product: i.product,
productId: i.product.id,
isHappyHour: i.isHappyHour,
isPrinted: true,
info: `${i.product.name} (${i.product.units}) @ ${i.price}`,
quantity: i.quantity,
discount: i.discount,
taxRate: i.taxRate,
tax: i.tax,
modifiers: i.modifiers
};
})];
});
this.data = view.reduce((a, c) => a.concat(c) , []);
this.data.push({isKot: true, newKot: true, info: '== New Kot =='});
this.dataObs.next(this.data);
}
addProduct(product: Product): void {
let old = this.data.find(x=> !x.isKot && x.productId == product.id && x.isHappyHour == product.hasHappyHour);
const old = this.data.find(x => !x.isKot && x.productId === product.id && x.isHappyHour === product.hasHappyHour);
if (old !== undefined) {
old.quantity += 1;
} else {
let item = {
const item = {
isKot: false,
product: product,
productId: product.id,
isHappyHour: product.hasHappyHour,
info: `${product.name} (${product.units}) @ ${product.price}`,
price: product.price,
quantity: 1,
discount: 0,
taxRate: product.saleCategory.tax.rate,
tax: product.saleCategory.tax,
modifiers: []
};
this.data.push(item);
this.modifierCategoryService.listIsActiveOfProduct(product.id).subscribe(result => {
if (result.reduce((a: any, c: ModifierCategory) => {
return a + c.minimum
return a + c.minimum;
}, 0)) {
this.showModifier(item);
}
@ -76,8 +105,9 @@ export class BillService {
this.dataObs.next(this.data);
}
quantity(item: any): void {
quantity(item: any, quantity: number): void {
item.quantity = quantity;
this.dataObs.next(this.data);
}
subtractOne(item: any): void {
@ -98,4 +128,28 @@ export class BillService {
this.showModifier(item);
}
printKot(guest_book_id: string): void {
const nk = new Kot({
inventories: this.data.filter(x => !x.isKot && !x.isPrinted).map(y => new Inventory({
product: y.product,
quantity: y.quantity,
price: y.price,
isHappyHour: y.isHappyHour,
discount: y.discount,
modifiers: y.modifiers,
taxRate: y.taxRate,
tax: y.tax
}))
});
let item = JSON.parse(JSON.stringify(this.bill))
item.kots.push(nk);
this.ser.saveOrUpdate(item, PrintType.Kot, guest_book_id, true).subscribe(x =>
console.log(x)
);
}
printBill(): boolean {
return false;
}
}

View File

@ -1,15 +1,15 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { VoucherService } from "./voucher.service";
import { Bill } from "./bill";
import { VoucherService } from './voucher.service';
import { Bill } from './bill';
@Injectable({
providedIn: 'root'
})
export class BillResolver implements Resolve<Bill> {
constructor(private ser: VoucherService, private router: Router) {
constructor(private ser: VoucherService) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Bill> {

View File

@ -7,7 +7,6 @@ import {Modifier} from '../../core/modifier';
export class Inventory {
id: string;
sortOrder: number;
product: Product;
quantity: number;
price: number;
@ -16,14 +15,23 @@ export class Inventory {
tax: Tax;
discount: number;
modifiers: Modifier[];
sortOrder: number;
public constructor(init?: Partial<Inventory>) {
Object.assign(this, init);
}
}
export class Kot {
id: string;
serial: number;
code: number;
date: string;
user: User;
inventories: Inventory[];
public constructor(init?: Partial<Kot>) {
Object.assign(this, init);
}
}
export class Bill {
@ -43,4 +51,16 @@ export class Bill {
serial: number;
kots: Kot[];
reprints: any[];
public constructor(init?: Partial<Bill>) {
Object.assign(this, init);
}
}
export enum PrintType {
Kot = 'KOT',
Bill = 'BILL',
NoCharge = 'NO_CHARGE',
Staff = 'STAFF'
}

View File

@ -1,12 +1,10 @@
.kot {
background-color: hotpink;
background-color: lightblue;
}
.printed {
background-color: lightpink;
}
.new-kot {
background-color: lightblue;
}
.square-button {
min-width: 150px;

View File

@ -6,7 +6,7 @@
<mat-table #table [dataSource]="dataSource" aria-label="Elements">
<!-- Info Column -->
<ng-container matColumnDef="info">
<mat-cell *matCellDef="let row" fxLayout="column" fxLayoutAlign="start space-between end">
<mat-cell *matCellDef="let row" fxLayout="column" fxLayoutAlign="start">
<span>
{{row.info}}
</span>
@ -18,7 +18,7 @@
<!-- Quantity Column -->
<ng-container matColumnDef="quantity">
<mat-header-cell *matHeaderCellDef>Quantity</mat-header-cell>
<mat-cell *matCellDef="let row">
<mat-cell *matCellDef="let row" fxLayoutAlign="end">
<button mat-icon-button (click)="subtractOne(row)" [disabled]="row.isPrinted" *ngIf="!row.isKot">
<mat-icon class="del">indeterminate_check_box</mat-icon>
</button>
@ -38,8 +38,7 @@
</ng-container>
<mat-row *matRowDef="let row; columns: displayedColumns;" [class.kot]="row.isKot" [class.new-kot]="row.newKot"
[class.printed]="row.isPrinted"></mat-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" [class.kot]="row.isKot"[class.printed]="row.isPrinted"></mat-row>
</mat-table>
</mat-card-content>
</mat-card>

View File

@ -1,8 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {Bill, Inventory, Kot} from './bill';
import {BillsDataSource} from './bills-datasource';
import {BillService} from "../bill.service";
import { Bill } from './bill';
import { BillsDataSource } from './bills-datasource';
import { BillService } from '../bill.service';
import { QuantityComponent } from '../quantity/quantity.component';
import { MatDialog } from '@angular/material';
@Component({
selector: 'app-bills',
@ -17,40 +19,18 @@ export class BillsComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private dialog: MatDialog,
private bs: BillService
) {
}
ngOnInit() {
console.log("ds set")
this.dataSource = new BillsDataSource(this.bs.dataObs);
this.route.data
.subscribe((data: { item: Bill }) => {
this.updateView(data.item);
this.item = data.item;
this.bs.loadData(data.item);
});
}
updateView(item) {
this.item = item;
let view = item.kots.map(k => {
return [{
isKot: true,
}, ...k.inventories.map(i => {
return {
id: i.id,
isKot: false,
product: i.product,
productId: i.product.id,
isHappyHour: i.isHappyHour,
isPrinted: true,
info: `${i.product.name} (${i.product.units}) @ ${i.price}`,
quantity: 1,
modifiers: i.modifiers
}
})]
});
view = view.reduce((a, c) => {return a.concat(c)} , []);
this.bs.loadData(view);
this.dataSource = new BillsDataSource(this.bs.dataObs);
}
addOne(item: any): void {
@ -58,7 +38,17 @@ export class BillsComponent implements OnInit {
}
quantity(item: any): void {
this.bs.quantity(item);
const dialogRef = this.dialog.open(QuantityComponent, {
// width: '750px',
data: item.quantity
});
dialogRef.afterClosed().subscribe((result: boolean | number) => {
if (!result) {
return;
}
this.bs.quantity(item, result as number);
});
}
subtractOne(item: any): void {

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs/internal/Observable';
import { catchError } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { ErrorLoggerService } from '../../core/error-logger.service';
import {Bill} from "./bill";
import { Bill, PrintType } from './bill';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
@ -46,25 +46,33 @@ export class VoucherService {
);
}
save(voucher: Bill): Observable<Bill> {
return <Observable<Bill>>this.http.post<Bill>(`${url}/new`, voucher, httpOptions)
save(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
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)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(voucher: Bill): Observable<Bill> {
return <Observable<Bill>>this.http.put<Bill>(`${url}/${voucher.id}`, voucher, httpOptions)
update(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
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)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);
}
saveOrUpdate(voucher: Bill): Observable<Bill> {
saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable<Bill> {
if (!voucher.id) {
return this.save(voucher);
return this.save(voucher, printType, guest_book_id, updateTable);
} else {
return this.update(voucher);
return this.update(voucher, printType, guest_book_id, updateTable);
}
}
}

View File

@ -2,4 +2,19 @@
min-width: 150px;
max-width: 150px;
min-height: 150px;
max-height: 150px;
cursor: pointer;
margin: 20px;
}
.item-name {
text-align: center;
padding: 0.5rem;
}
.center {
text-align: center;
}
.warn {
background-color: red;
}

View File

@ -1,5 +1,18 @@
<button mat-raised-button class="square-button" [routerLink]="['menu-categories']">Add Product</button>
<button mat-raised-button class="square-button" [routerLink]="['menu-categories']">Print KOT</button>
<button mat-raised-button class="square-button" [routerLink]="['menu-categories']">Print Bill</button>
<button mat-raised-button class="square-button" [routerLink]="['menu-categories']">Back to Tables</button>
<button mat-raised-button class="square-button" [routerLink]="['menu-categories']">Receive Payment</button>
<div fxLayout="row wrap" fxLayoutGap="grid 20px">
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['menu-categories']"
queryParamsHandling="preserve">
<h3 class="item-name">Add Product</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple (click)="printKot()">
<h3 class="item-name">Print KOT</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple (click)="printBill()">
<h3 class="item-name">Print Bill</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button warn" matRipple [routerLink]="['../../tables']">
<h3 class="item-name">Back to Tables</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">
<h3 class="item-name">Receive Payment</h3>
</mat-card>
</div>

View File

@ -1,6 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {Subject} from 'rxjs';
import {AuthService} from '../../auth/auth.service';
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { AuthService } from '../../auth/auth.service';
import { BillService } from '../bill.service';
import { ActivatedRoute } from "@angular/router";
@Component({
selector: 'app-sales-home',
@ -10,7 +12,7 @@ import {AuthService} from '../../auth/auth.service';
export class SalesHomeComponent implements OnInit {
public nameObject = new Subject<string>();
constructor(private auth: AuthService) {
constructor(private route: ActivatedRoute, private auth: AuthService, private bs: BillService) {
}
ngOnInit() {
@ -22,4 +24,16 @@ export class SalesHomeComponent implements OnInit {
}
});
}
printKot() {
let guestBookId = null;
if (this.route.snapshot.queryParamMap.has("guest")) {
guestBookId = this.route.snapshot.queryParamMap.get("guest");
}
this.bs.printKot(guestBookId);
}
printBill() {
this.bs.printBill();
}
}

View File

@ -2,4 +2,19 @@
min-width: 150px;
max-width: 150px;
min-height: 150px;
max-height: 150px;
cursor: pointer;
margin: 20px;
}
.item-name {
text-align: center;
padding: 0.5rem;
}
.center {
text-align: center;
}
.warn {
background-color: red;
}

View File

@ -1,7 +1,12 @@
<mat-card>
<mat-card-content fxLayout="row wrap" fxLayoutGap="grid 20px">
<button mat-raised-button class="square-button"
*ngFor="let item of list" [routerLink]="['../products', item.id]">{{item.name}}</button>
<button mat-raised-button class="square-button" color="warn" (click)="goBack()">Back</button>
<mat-card fxLayout="column" class="square-button" matRipple *ngFor="let item of list"
[routerLink]="['../products', item.id]" queryParamsHandling="preserve">
<h3 class="item-name">{{item.name}}</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button warn" matRipple [routerLink]="['../']"
queryParamsHandling="preserve">
<h3 class="item-name">Back</h3>
</mat-card>
</mat-card-content>
</mat-card>

View File

@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { MenuCategory } from '../../core/menu-category';
@ -11,10 +10,7 @@ import { MenuCategory } from '../../core/menu-category';
export class MenuCategoriesComponent implements OnInit {
list: MenuCategory[];
constructor(
private route: ActivatedRoute,
private location: Location,
) {
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
@ -24,8 +20,4 @@ export class MenuCategoriesComponent implements OnInit {
});
}
goBack(): void {
this.location.back();
}
}

View File

@ -2,4 +2,6 @@
min-width: 150px;
max-width: 150px;
min-height: 150px;
cursor: pointer;
margin: 20px;
}

View File

@ -23,7 +23,7 @@ export class ModifiersComponent {
}
select(m: Modifier) {
let index = this.selectedIds.indexOf(m.id);
const index = this.selectedIds.indexOf(m.id);
if (index === -1) {
this.selected.push(m);
} else {

View File

@ -2,4 +2,19 @@
min-width: 150px;
max-width: 150px;
min-height: 150px;
max-height: 150px;
cursor: pointer;
margin: 20px;
}
.item-name {
text-align: center;
padding: 0.5rem;
}
.center {
text-align: center;
}
.warn {
background-color: red;
}

View File

@ -1,7 +1,11 @@
<mat-card>
<mat-card-content fxLayout="row wrap" fxLayoutGap="grid 20px">
<button mat-raised-button class="square-button"
*ngFor="let item of list" (click)="addProduct(item)">{{item.name}}</button>
<button mat-raised-button class="square-button" color="warn" (click)="goBack()">Back</button>
<mat-card fxLayout="column" class="square-button" matRipple *ngFor="let item of list" (click)="addProduct(item)">
<h3 class="item-name">{{item.name}}</h3>
</mat-card>
<mat-card fxLayout="column" class="square-button warn" matRipple [routerLink]="['../../menu-categories']"
queryParamsHandling="preserve">
<h3 class="item-name">Back</h3>
</mat-card>
</mat-card-content>
</mat-card>

View File

@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { Product } from '../../core/product';
import { BillService } from '../bill.service';
@ -12,11 +11,7 @@ import { BillService } from '../bill.service';
export class ProductsComponent implements OnInit {
list: Product[];
constructor(
private route: ActivatedRoute,
private location: Location,
private bs: BillService
) {
constructor(private route: ActivatedRoute, private bs: BillService) {
}
ngOnInit() {
@ -30,8 +25,4 @@ export class ProductsComponent implements OnInit {
this.bs.addProduct(product);
}
goBack(): void {
this.location.back();
}
}

View File

@ -0,0 +1,17 @@
<div mat-dialog-content>
<form [formGroup]="form">
<div fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-md="column" fxLayoutGap="20px"
fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<mat-label>Quantity</mat-label>
<input type="text" matInput #quantity placeholder="Quantity" formControlName="quantity" autocomplete="off"
(focus)="quantity.select()" cdkFocusInitial>
</mat-form-field>
</div>
</form>
</div>
<div mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">Cancel</button>
<button mat-button (click)="accept()" color="primary">Ok</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { QuantityComponent } from './quantity.component';
describe('QuantityComponent', () => {
let component: QuantityComponent;
let fixture: ComponentFixture<QuantityComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ QuantityComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(QuantityComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-quantity',
templateUrl: './quantity.component.html',
styleUrls: ['./quantity.component.css']
})
export class QuantityComponent implements OnInit {
form: FormGroup;
constructor(
public dialogRef: MatDialogRef<QuantityComponent>,
@Inject(MAT_DIALOG_DATA) public data: number,
private fb: FormBuilder,
) {
this.createForm();
}
ngOnInit() {
this.form.setValue({
quantity: this.data
});
}
createForm() {
this.form = this.fb.group({
quantity: ''
});
}
accept(): void {
const quantity = this.form.value.quantity;
this.dialogRef.close(quantity);
}
}

View File

@ -11,6 +11,7 @@
max-width: 150px;
min-height: 150px;
max-height: 150px;
cursor: pointer;
margin: 20px;
}
.item-name {

View File

@ -3,13 +3,15 @@
<mat-card-title>Running Tables</mat-card-title>
</mat-card-title-group>
<mat-card-content fxLayout="row wrap" fxLayoutGap="grid 20px">
<mat-card fxLayout="column" class="square-button"
*ngFor="let table of list" (click)="navigateToBill(table)"
[class.running]="table.status === 'running'" [class.printed]="table.status === 'printed'">
<h3 class="item-name">{{table.name}}</h3>
<mat-divider></mat-divider>
<mat-card fxLayout="column" class="square-button" matRipple
*ngFor="let table of list" (click)="navigateToBill(table)"
[class.running]="table.status === 'running'" [class.printed]="table.status === 'printed'">
<h3 class="item-name">{{table.name}}</h3>
<mat-card-subtitle class="center">{{table.guest}}</mat-card-subtitle>
<span class="center">{{table.pax || 0}} / {{table.seats}} Seats</span>
<span class="center" *ngIf="table.date">{{table.date}}</span>
<span class="center" *ngIf="table.amount">{{table.amount}}</span>
<span class="center" *ngIf="table.amount">{{table.amount | currency:'INR'}}</span>
</mat-card>
</mat-card-content>
</mat-card>

View File

@ -21,11 +21,11 @@ export class RunningTablesComponent implements OnInit {
}
navigateToBill(table: Table): void {
let qp = {table: table.id};
const qp = {table: table.id};
if (table.voucherId) {
qp["voucher"] = table.voucherId;
qp['voucher'] = table.voucherId;
}
let navigationExtras: NavigationExtras = {
const navigationExtras: NavigationExtras = {
queryParams: qp,
queryParamsHandling: 'merge',
preserveFragment: true

View File

@ -1,16 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
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 { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatRippleModule } from '@angular/material';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { FlexLayoutModule } from '@angular/flex-layout';
import { SharedModule } from "../shared/shared.module";
import { SalesRoutingModule } from './sales-routing.module';
import { RunningTablesComponent } from './running-tables/running-tables.component';
import { MenuCategoriesComponent } from './menu-categories/menu-categories.component';
@ -19,6 +24,7 @@ import { ModifiersComponent } from './modifiers/modifiers.component';
import { BillsComponent } from './bills/bills.component';
import { SalesHomeComponent } from './home/sales-home.component';
import { BillService } from './bill.service';
import { QuantityComponent } from './quantity/quantity.component';
@NgModule({
providers: [
@ -30,24 +36,31 @@ import { BillService } from './bill.service';
ProductsComponent,
ModifiersComponent,
BillsComponent,
SalesHomeComponent
SalesHomeComponent,
QuantityComponent
],
imports: [
CommonModule,
FlexLayoutModule,
ReactiveFormsModule,
MatBadgeModule,
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatChipsModule,
MatDialogModule,
MatDividerModule,
MatIconModule,
MatInputModule,
MatRippleModule,
MatTableModule,
MatTabsModule,
SharedModule,
SalesRoutingModule
],
entryComponents: [
ModifiersComponent
ModifiersComponent,
QuantityComponent
]
})
export class SalesModule { }