Void Bill Working

This commit is contained in:
Amritanshu 2019-08-10 18:49:05 +05:30
parent 2fcff26e34
commit 40a958a935
17 changed files with 212 additions and 46 deletions

View File

@ -54,7 +54,6 @@ def receive_payment(request):
Overview.voucher_id == item.id Overview.voucher_id == item.id
).delete() ).delete()
transaction.commit() transaction.commit()
item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first() return True
return voucher_info(item)

View File

@ -3,46 +3,55 @@ import uuid
import transaction import transaction
from pyramid.view import view_config from pyramid.view import view_config
from barker.models import Voucher, SettleOption, Settlement from barker.models import Voucher, SettleOption, Settlement, Overview
from barker.views.voucher import get_settlements
from barker.views.voucher.show import voucher_info
@view_config( @view_config(
request_method="POST", request_method="POST",
route_name="voucher_void", route_name="v1_vouchers_id",
renderer="json", renderer="json",
request_param="v",
permission="Void Bill", permission="Void Bill",
trans=True, trans=True,
) )
def void_voucher(request): def void_voucher(request):
update_table = request.GET["u"]
json = request.json_body
id_ = uuid.UUID(request.matchdict["id"]) id_ = uuid.UUID(request.matchdict["id"])
reason = request.GET["v"]
update_table = request.GET["u"]
item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first()
item.void = True item.void = True
item.void_reason = json["Reason"] item.void_reason = reason
amount = round(-1 * sum(i.amount for k in item.kots for i in k.inventories), 5) * -1 settlements = []
s = Settlement(item.id, SettleOption.VOID(), amount) total_amount = item.amount
item.settlements.append(s) settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount})
request.dbsession.add(s) round_off = round(total_amount) - total_amount
allowed = [ if round_off != 0:
SettleOption.AMOUNT(), settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off})
SettleOption.ROUND_OFF(), settlements.append({"id": SettleOption.VOID(), "amount": round(total_amount)})
SettleOption.UNSETTLED(),
SettleOption.VOID(), for i in settlements:
] amount = i["amount"]
for i in (s for s in item.settlements if s.settled not in allowed): settlement_type_id = i["id"]
old = [s for s in item.settlements if s.settled == settlement_type_id]
if len(old) == 1:
old[0].amount = amount
else:
s = Settlement(item.id, settlement_type_id, amount)
item.settlements.append(s)
request.dbsession.add(s)
for i in (i for i in item.settlements if i.settled not in [x["id"] for x in settlements]):
item.settlements.remove(i) item.settlements.remove(i)
request.dbsession.delete(i) request.dbsession.delete(i)
get_settlements(item, request.dbsession)
if update_table: if update_table:
request.dbsession.delete(item.status) request.dbsession.query(Overview).filter(
Overview.voucher_id == item.id
).delete()
transaction.commit() transaction.commit()
item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first() return True
return voucher_info(item)

View File

@ -12,6 +12,7 @@ import { VoucherService } from './bills/voucher.service';
import { ToasterService } from "../core/toaster.service"; import { ToasterService } from "../core/toaster.service";
import { tap } from "rxjs/operators"; import { tap } from "rxjs/operators";
import { Table } from "../core/table"; import { Table } from "../core/table";
import {observableToBeFn} from "rxjs/internal/testing/TestScheduler";
@Injectable() @Injectable()
export class BillService { export class BillService {
@ -192,7 +193,7 @@ export class BillService {
return this.bill.voucherType; return this.bill.voucherType;
} }
receivePayment(amounts: { id: string; name: string; amount: number }[]) { receivePayment(amounts: { id: string; name: string; amount: number }[]): Observable<boolean> {
return this.ser.receivePayment(this.bill.id, amounts, true).pipe( return this.ser.receivePayment(this.bill.id, amounts, true).pipe(
tap(x => console.log(x)) tap(x => console.log(x))
); );
@ -202,6 +203,10 @@ export class BillService {
return this.ser.moveTable(this.bill.id, table); return this.ser.moveTable(this.bill.id, table);
} }
voidBill(reason: string): Observable<boolean> {
return this.ser.voidBill(this.bill.id, reason, true);
}
netAmount() { netAmount() {
return math.round(this.bill.kots.reduce( return math.round(this.bill.kots.reduce(
(ka: number, k: Kot) => ka + k.inventories.reduce( (ka: number, k: Kot) => ka + k.inventories.reduce(

View File

@ -5,6 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { ErrorLoggerService } from '../../core/error-logger.service'; import { ErrorLoggerService } from '../../core/error-logger.service';
import { Bill, PrintType } from './bill'; import { Bill, PrintType } from './bill';
import { Table } from "../../core/table"; import { Table } from "../../core/table";
import {ObjectUnsubscribedError} from "rxjs";
const httpOptions = { const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'}) headers: new HttpHeaders({'Content-Type': 'application/json'})
@ -77,9 +78,9 @@ export class VoucherService {
} }
} }
receivePayment(id: string, amounts: { id: string; name: string; amount: number }[], updateTable: boolean): Observable<Bill> { receivePayment(id: string, amounts: { id: string; name: string; amount: number }[], updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('r', "").set('u', updateTable.toString())}; const options = {params: new HttpParams().set('r', "").set('u', updateTable.toString())};
return <Observable<Bill>>this.http.post<Bill>(`${url}/${id}`, amounts, options) return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, amounts, options)
.pipe( .pipe(
catchError(this.log.handleError(serviceName, 'receivePayment')) catchError(this.log.handleError(serviceName, 'receivePayment'))
); );
@ -92,4 +93,12 @@ export class VoucherService {
catchError(this.log.handleError(serviceName, 'moveTable')) catchError(this.log.handleError(serviceName, 'moveTable'))
); );
} }
voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
const options = {params: new HttpParams().set('v', reason).set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>(`${url}/${id}`, {}, options)
.pipe(
catchError(this.log.handleError(serviceName, 'voidBill'))
);
}
} }

View File

@ -21,7 +21,7 @@
<mat-card fxLayout="column" class="square-button" matRipple (click)="moveTable()"> <mat-card fxLayout="column" class="square-button" matRipple (click)="moveTable()">
<h3 class="item-name">Move Table</h3> <h3 class="item-name">Move Table</h3>
</mat-card> </mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']"> <mat-card fxLayout="column" class="square-button" matRipple (click)="voidBill()">
<h3 class="item-name">Void Bill</h3> <h3 class="item-name">Void Bill</h3>
</mat-card> </mat-card>
<mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']"> <mat-card fxLayout="column" class="square-button" matRipple [routerLink]="['../../tables']">

View File

@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material";
import { concatMap, map, switchMap, tap } from "rxjs/operators"; import { concatMap, map, switchMap, tap } from "rxjs/operators";
import { iif, Observable, of as observableOf, throwError} from "rxjs"; import { iif, Observable, of as observableOf, throwError } from "rxjs";
import { BillService } from '../bill.service'; import { BillService } from '../bill.service';
import { ToasterService } from "../../core/toaster.service"; import { ToasterService } from "../../core/toaster.service";
import { DiscountComponent } from "../discount/discount.component"; import { DiscountComponent } from "../discount/discount.component";
@ -15,6 +15,7 @@ import { TableService } from "../../tables/table.service";
import { Table } from "../../core/table"; import { Table } from "../../core/table";
import { TablesDialogComponent } from "../tables-dialog/tables-dialog.component"; import { TablesDialogComponent } from "../tables-dialog/tables-dialog.component";
import { ConfirmDialogComponent } from "../../shared/confirm-dialog/confirm-dialog.component"; import { ConfirmDialogComponent } from "../../shared/confirm-dialog/confirm-dialog.component";
import { VoidReasonComponent } from "../void-reason/void-reason.component";
@Component({ @Component({
selector: 'app-sales-home', selector: 'app-sales-home',
@ -97,6 +98,15 @@ export class SalesHomeComponent implements OnInit {
); );
} }
confirmVoidDialog(reason: string): Observable<boolean | string> {
return this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Void Bill?', content: 'Are you sure?'}
}).afterClosed().pipe(
map((x: boolean) => x ? reason : x)
);
}
printBill() { printBill() {
const canGiveDiscount = this.auth.hasPermission("Discount"); const canGiveDiscount = this.auth.hasPermission("Discount");
let guestBookId = null; let guestBookId = null;
@ -143,19 +153,18 @@ export class SalesHomeComponent implements OnInit {
} }
moveTable() { moveTable() {
const dialogRef = this.dialog.open(TablesDialogComponent, { this.dialog.open(TablesDialogComponent, {
// width: '750px', // width: '750px',
data: { data: {
list: this.tSer.running(), list: this.tSer.running(),
canChooseRunning: false canChooseRunning: false
} }
}); }).afterClosed().pipe(
dialogRef.afterClosed().pipe(
switchMap((x: boolean | Table) => { switchMap((x: boolean | Table) => {
if (!!x) { if (!!x) {
return this.confirmMoveTableDialog(x as Table) return this.confirmMoveTableDialog(x as Table);
} else { } else {
return throwError("Please choose a table") return throwError("Please choose a table");
} }
}), }),
switchMap((value: { table: Table, confirmed: boolean }, index: number) => { switchMap((value: { table: Table, confirmed: boolean }, index: number) => {
@ -176,6 +185,29 @@ export class SalesHomeComponent implements OnInit {
} }
voidBill() { voidBill() {
this.dialog.open(VoidReasonComponent, {
// width: '750px'
}).afterClosed().pipe(
switchMap((x: boolean | string) => {
if (!!x) {
return this.confirmVoidDialog(x as string);
} else {
return throwError("Please choose a reason to void the bill");
}
}),
switchMap((x: boolean | string) => {
if (!!x) {
return this.bs.voidBill(x as string)
} else {
return throwError("You chose not to void the bill")
}
})
).subscribe((x) => {
this.toaster.show('Success', '');
this.router.navigate(['/sales']);
},
x => {
this.toaster.show('Error', x)
})
} }
} }

View File

@ -5,3 +5,7 @@
cursor: pointer; cursor: pointer;
margin: 20px; margin: 20px;
} }
.selected {
background-color: #dddddd;
}

View File

@ -7,11 +7,12 @@
<mat-chip *ngIf="!!item.maximum">{{item.minimum}} - {{item.maximum}}</mat-chip> <mat-chip *ngIf="!!item.maximum">{{item.minimum}} - {{item.maximum}}</mat-chip>
</mat-chip-list> </mat-chip-list>
</ng-template> </ng-template>
<mat-button-toggle-group multiple> <div fxLayout="row wrap" fxLayoutGap="grid 20px">
<mat-button-toggle mat-raised-button class="square-button" *ngFor="let m of item.modifiers" (click)="select(m)" [checked]="selectedIds.indexOf(m.id) !== -1"> <mat-card fxLayout="column" class="square-button" matRipple *ngFor="let m of item.modifiers" (click)="select(m)"
{{m.name}} [class.selected]="selectedIds.indexOf(m.id) !== -1">
</mat-button-toggle> <h3 class="item-name">{{m.name}}</h3>
</mat-button-toggle-group> </mat-card>
</div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
<mat-dialog-actions align="end"> <mat-dialog-actions align="end">

View File

@ -15,6 +15,11 @@
text-align: center; text-align: center;
} }
.warn { .yellow300 {
background-color: red; background-color: #fff176;
}
.red700 {
background-color: #d32f2f;
color: #ffffff;
} }

View File

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

View File

@ -29,6 +29,7 @@ import { DiscountComponent } from "./discount/discount.component";
import { BillTypeComponent } from "./bill-type/bill-type.component"; import { BillTypeComponent } from "./bill-type/bill-type.component";
import { ReceivePaymentComponent } from "./receive-payment/receive-payment.component"; import { ReceivePaymentComponent } from "./receive-payment/receive-payment.component";
import { TablesDialogComponent } from "./tables-dialog/tables-dialog.component"; import { TablesDialogComponent } from "./tables-dialog/tables-dialog.component";
import { VoidReasonComponent } from "./void-reason/void-reason.component";
@NgModule({ @NgModule({
providers: [ providers: [
@ -45,7 +46,8 @@ import { TablesDialogComponent } from "./tables-dialog/tables-dialog.component";
ReceivePaymentComponent, ReceivePaymentComponent,
RunningTablesComponent, RunningTablesComponent,
SalesHomeComponent, SalesHomeComponent,
TablesDialogComponent TablesDialogComponent,
VoidReasonComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -72,7 +74,8 @@ import { TablesDialogComponent } from "./tables-dialog/tables-dialog.component";
ModifiersComponent, ModifiersComponent,
QuantityComponent, QuantityComponent,
ReceivePaymentComponent, ReceivePaymentComponent,
TablesDialogComponent TablesDialogComponent,
VoidReasonComponent
] ]
}) })
export class SalesModule { } export class SalesModule { }

View File

@ -22,6 +22,7 @@
.center { .center {
text-align: center; text-align: center;
} }
.selected { .selected {
background-color: #dddddd; background-color: #dddddd;
} }

View File

@ -0,0 +1,16 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable, of as observableOf } from 'rxjs';
export class VoidReasonDatasource extends DataSource<string> {
constructor(private data: string[]) {
super();
}
connect(): Observable<string[]> {
return observableOf(this.data);
}
disconnect() {
}
}

View File

@ -0,0 +1,3 @@
.selected {
background-color: #dddddd;
}

View File

@ -0,0 +1,16 @@
<h2 mat-dialog-title>Reason</h2>
<mat-dialog-content>
<mat-table [dataSource]="dataSource">
<!-- Name Column -->
<ng-container matColumnDef="reason">
<mat-cell *matCellDef="let row" (click)="select(row)">{{row}}</mat-cell>
</ng-container>
<mat-row *matRowDef="let row; columns: displayedColumns;" [class.selected]="row === selected"></mat-row>
</mat-table>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button [mat-dialog-close]="false">Cancel</button>
<button mat-button (click)="accept()" color="primary" [disabled]="!selected">Ok</button>
</mat-dialog-actions>

View File

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

View File

@ -0,0 +1,38 @@
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material';
import { VoidReasonDatasource } from "./void-reason-datasource";
@Component({
selector: 'app-void-reason',
templateUrl: './void-reason.component.html',
styleUrls: ['./void-reason.component.css']
})
export class VoidReasonComponent {
dataSource: VoidReasonDatasource;
selected: string;
reasons = [
"Discount",
"Printing fault",
"Item changed",
"Quantity reduced",
"Costing bill for party",
"Cashier mistake",
"Management free sale",
"Other"
];
displayedColumns = ['reason'];
constructor(
public dialogRef: MatDialogRef<VoidReasonComponent>
) {
this.dataSource = new VoidReasonDatasource(this.reasons);
}
select(reason: string) {
this.selected = reason;
}
accept(): void {
this.dialogRef.close(this.selected);
}
}