diff --git a/barker/models/voucher.py b/barker/models/voucher.py index 7815b13..b8f886d 100644 --- a/barker/models/voucher.py +++ b/barker/models/voucher.py @@ -191,7 +191,7 @@ class Voucher(Base): @property def amount(self): - return sum(i.amount for k in self.kots for i in k.inventories) + return round(sum(i.amount for k in self.kots for i in k.inventories), 2) class Kot(Base): diff --git a/barker/routes.py b/barker/routes.py index ca16ee3..80b97fd 100644 --- a/barker/routes.py +++ b/barker/routes.py @@ -393,7 +393,6 @@ def includeme(config): config.add_route("sa_tax", "/SaleAnalysis/Tax.json") config.add_route("voucher_reprint", "/ReprintVoucher/{id}.json") - config.add_route("voucher_settle", "/Settle/{id}.json") config.add_route("voucher_split", "/Split/{id}.json") config.add_route("voucher_void", "/Void/{id}.json") diff --git a/barker/views/voucher/__init__.py b/barker/views/voucher/__init__.py index ac58aab..ae7c034 100644 --- a/barker/views/voucher/__init__.py +++ b/barker/views/voucher/__init__.py @@ -12,9 +12,7 @@ def get_tax(tax, voucher_type): def get_settlements(voucher, dbsession): - amount = round( - -1 * sum(sum(i.amount for i in k.inventories) for k in voucher.kots), 5 - ) + amount = voucher.amount so_amount = [s for s in voucher.settlements if s.settled == SettleOption.AMOUNT()] if len(so_amount) == 1: so_amount[0].amount = amount diff --git a/barker/views/voucher/receive_payment.py b/barker/views/voucher/receive_payment.py new file mode 100644 index 0000000..6da82f0 --- /dev/null +++ b/barker/views/voucher/receive_payment.py @@ -0,0 +1,60 @@ +import uuid +from decimal import Decimal + +import transaction +from pyramid.view import view_config + +from barker.models import SettleOption, Voucher, Settlement, Overview +from barker.models.validation_exception import ValidationError +from barker.views.voucher.show import voucher_info + + +@view_config( + request_method="POST", + route_name="v1_vouchers_id", + renderer="json", + request_param="r", + permission="Settle Bill", + trans=False, +) +def receive_payment(request): + update_table = request.GET["u"] + json = request.json_body + for item in json: + item["amount"] = round(Decimal(item["amount"]), 0) + id_ = uuid.UUID(request.matchdict["id"]) + item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() + + total_amount = item.amount + json.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) + round_off = round(total_amount) - total_amount + if round_off != 0: + json.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) + if sum([i["amount"] for i in json]) != 0: + raise ValidationError("Payment received is not equal to bill amount") + + for i in (j for j in json if j["amount"] != 0): + amount = i["amount"] + 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) + + allowed = [a["id"] for a in json if a["amount"] != 0] + for i in (s for s in item.settlements if s.settled not in allowed): + item.settlements.remove(i) + request.dbsession.delete(i) + + if update_table: + request.dbsession.query(Overview).filter( + Overview.voucher_id == item.id + ).delete() + transaction.commit() + item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first() + return voucher_info(item) + + diff --git a/barker/views/voucher/settle.py b/barker/views/voucher/settle.py deleted file mode 100644 index 385aa42..0000000 --- a/barker/views/voucher/settle.py +++ /dev/null @@ -1,53 +0,0 @@ -import uuid - -import transaction -from pyramid.view import view_config - -from barker.models import SettleOption, Voucher, Settlement, Overview -from barker.views.voucher import get_settlements -from barker.views.voucher.show import voucher_info - - -@view_config( - request_method="POST", - route_name="voucher_settle", - renderer="json", - permission="Settle Bill", - trans=False, -) -def settle_voucher(request): - update_table = request.GET["u"] - json = request.json_body - id_ = uuid.UUID(request.matchdict["id"]) - item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() - - allowed = [ - SettleOption.AMOUNT(), - SettleOption.ROUND_OFF(), - SettleOption.UNSETTLED(), - ] - for i in (j for j in json if j["Amount"] != 0): - amount = i["Amount"] - so = i["Settled"] - so_settled = [s for s in item.settlements if s.settled == so] - if len(so_settled) == 1: - so_settled[0].amount = amount - else: - s = Settlement(item.id, so, amount) - item.settlements.append(s) - request.dbsession.add(s) - allowed.append(so) - for i in (s for s in item.settlements if s.settled not in allowed): - item.settlements.remove(i) - request.dbsession.delete(i) - unsettled = get_settlements(item, request.dbsession) - - if unsettled == 0 and update_table: - request.dbsession.query(Overview).filter( - Overview.voucher_id == item.id - ).delete() - transaction.commit() - item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first() - return voucher_info(item) - - diff --git a/barker/views/voucher/show.py b/barker/views/voucher/show.py index f8bbd7f..488f865 100644 --- a/barker/views/voucher/show.py +++ b/barker/views/voucher/show.py @@ -97,7 +97,7 @@ def voucher_info(item): "narration": item.narration, "void": item.is_void, "voidReason": item.void_reason, - "voucherType": item.voucher_type.value, + "voucherType": item.voucher_type.name, "kotId": item.kot_id, "kots": [ { diff --git a/bookie/src/app/sales/bill.service.ts b/bookie/src/app/sales/bill.service.ts index b94bd9d..3fa8d43 100644 --- a/bookie/src/app/sales/bill.service.ts +++ b/bookie/src/app/sales/bill.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material'; import { BehaviorSubject } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; +import * as math from 'mathjs'; import { Product } from '../core/product'; import { ModifiersComponent } from './modifiers/modifiers.component'; import { ModifierCategoryService } from '../modifier-categories/modifier-category.service'; @@ -41,7 +42,7 @@ export class BillService { productId: i.product.id, isHappyHour: i.isHappyHour, isPrinted: true, - info: `${i.product.name} (${i.product.units}) @ ${i.price} - ${i.discount * 100}%`, + info: `${i.product.name} (${i.product.units}) @ ${i.price} - ${math.round(i.discount * 100, 2)}%`, price: i.price, quantity: i.quantity, discount: i.discount, @@ -139,7 +140,7 @@ export class BillService { this.data.forEach(x=> { if (!x.isKot) { x.discount = discounts.find(d => d.id === x.product.saleCategory.id).discount / 100; - x.info = `${x.product.name} (${x.product.units}) @ ${x.price} - ${x.discount * 100}%`; + x.info = `${x.product.name} (${x.product.units}) @ ${x.price} - ${math.round(x.discount * 100, 2)}%`; } }); this.dataObs.next(this.data); @@ -185,4 +186,21 @@ export class BillService { ); } + amount() { + return math.round(this.bill.kots.reduce( + (ka: number, k: Kot) => ka + k.inventories.reduce( + (ca: number, c: Inventory) => ca + ((c.isHappyHour ? 0 : c.price) * c.quantity * (1 - c.discount) * (1 + c.taxRate)) + , 0) + , 0)) + } + + type() { + return this.bill.voucherType; + } + + receivePayment(amounts: { id: string; name: string; amount: number }[]) { + return this.ser.receivePayment(this.bill.id, amounts, true).pipe( + tap(x => console.log(x)) + ); + } } diff --git a/bookie/src/app/sales/bills/voucher.service.ts b/bookie/src/app/sales/bills/voucher.service.ts index 385b59f..6a339be 100644 --- a/bookie/src/app/sales/bills/voucher.service.ts +++ b/bookie/src/app/sales/bills/voucher.service.ts @@ -75,4 +75,12 @@ export class VoucherService { return this.update(voucher, printType, guest_book_id, updateTable); } } + + receivePayment(id: string, amounts: { id: string; name: string; amount: number }[], updateTable: boolean): Observable { + const options = {params: new HttpParams().set('r', "").set('u', updateTable.toString())}; + return >this.http.post(`${url}/${id}`, amounts, options) + .pipe( + catchError(this.log.handleError(serviceName, 'receivePayment')) + ); + } } diff --git a/bookie/src/app/sales/home/sales-home.component.html b/bookie/src/app/sales/home/sales-home.component.html index f5efe98..5198d59 100644 --- a/bookie/src/app/sales/home/sales-home.component.html +++ b/bookie/src/app/sales/home/sales-home.component.html @@ -15,7 +15,19 @@

Back to Tables

- +

Receive Payment

+ +

Move Table

+
+ +

Void Bill

+
+ +

Move KOT

+
+ +

Split Bill

+
diff --git a/bookie/src/app/sales/home/sales-home.component.ts b/bookie/src/app/sales/home/sales-home.component.ts index 81fe6c0..0e357ac 100644 --- a/bookie/src/app/sales/home/sales-home.component.ts +++ b/bookie/src/app/sales/home/sales-home.component.ts @@ -10,6 +10,7 @@ import { SaleCategoryService } from "../../sale-category/sale-category.service"; import { BillTypeComponent } from "../bill-type/bill-type.component"; import { PrintType } from "../bills/bill"; import { AuthService } from "../../auth/auth.service"; +import { ReceivePaymentComponent } from "../receive-payment/receive-payment.component"; @Component({ selector: 'app-sales-home', @@ -106,4 +107,24 @@ export class SalesHomeComponent implements OnInit { this.toaster.show('Error', "No Bill Type Chosen") }); } + + receivePayment() { + let amount = this.bs.amount(); + let type = this.bs.type(); + const dialogRef = this.dialog.open(ReceivePaymentComponent, { + // width: '750px', + data: { + type: type, + amount: amount + } + }); + dialogRef.afterClosed().subscribe((result: boolean | { id: string, name: string, amount: number }[]) => { + if (!!result) { + this.bs.receivePayment(result as { id: string, name: string, amount: number }[]).subscribe(() => { + this.toaster.show('Success', ''); + this.router.navigate(['/sales']); + }); + } + }) + } } diff --git a/bookie/src/app/sales/receive-payment/receive-payment-datasource.ts b/bookie/src/app/sales/receive-payment/receive-payment-datasource.ts new file mode 100644 index 0000000..6b2d195 --- /dev/null +++ b/bookie/src/app/sales/receive-payment/receive-payment-datasource.ts @@ -0,0 +1,16 @@ +import { DataSource } from '@angular/cdk/collections'; +import { Observable, of as observableOf } from 'rxjs'; + +export class ReceivePaymentDatasource extends DataSource<{name: string, discount: number}> { + + constructor(private data: {name: string, discount: number}[]) { + super(); + } + + connect(): Observable<{name: string, discount: number}[]> { + return observableOf(this.data); + } + + disconnect() { + } +} diff --git a/bookie/src/app/sales/receive-payment/receive-payment.component.css b/bookie/src/app/sales/receive-payment/receive-payment.component.css new file mode 100644 index 0000000..6ebf914 --- /dev/null +++ b/bookie/src/app/sales/receive-payment/receive-payment.component.css @@ -0,0 +1,7 @@ +.right { + display: flex; + justify-content: flex-end; +} +.bold { + font-weight: bold; +} diff --git a/bookie/src/app/sales/receive-payment/receive-payment.component.html b/bookie/src/app/sales/receive-payment/receive-payment.component.html new file mode 100644 index 0000000..cce5089 --- /dev/null +++ b/bookie/src/app/sales/receive-payment/receive-payment.component.html @@ -0,0 +1,33 @@ +

Receive Payment

+ +
+ + + + + Amount + {{row.name}} + Balance + + + + + {{ amount | currency:'INR'}} + + + + + + {{ balance | currency:'INR' }} + + + + + + +
+
+ + + + diff --git a/bookie/src/app/sales/receive-payment/receive-payment.component.spec.ts b/bookie/src/app/sales/receive-payment/receive-payment.component.spec.ts new file mode 100644 index 0000000..213033d --- /dev/null +++ b/bookie/src/app/sales/receive-payment/receive-payment.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReceivePaymentComponent } from './receive-payment.component'; + +describe('ReceivePaymentComponent', () => { + let component: ReceivePaymentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReceivePaymentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReceivePaymentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/bookie/src/app/sales/receive-payment/receive-payment.component.ts b/bookie/src/app/sales/receive-payment/receive-payment.component.ts new file mode 100644 index 0000000..99df894 --- /dev/null +++ b/bookie/src/app/sales/receive-payment/receive-payment.component.ts @@ -0,0 +1,104 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { FormArray, FormBuilder, FormGroup } from "@angular/forms"; +import { distinctUntilChanged } from "rxjs/operators"; +import { ReceivePaymentDatasource } from "./receive-payment-datasource"; +import { PrintType } from "../bills/bill"; + +@Component({ + selector: 'app-receive-payment', + templateUrl: './receive-payment.component.html', + styleUrls: ['./receive-payment.component.css'] +}) +export class ReceivePaymentComponent { + choices = { + REGULAR_BILL: [ + { + id: 2, + name: "Cash", + amount: 0 + }, + { + id: 3, + name: "Credit Card", + amount: 0 + }, + { + id: 5, + name: "Bill To Company", + amount: 0 + }, + { + id: 6, + name: "Tip", + amount: 0 + } + ] + , + NO_CHARGE: [ + { + id: 4, + name: "No Charge", + amount: 0 + } + ], + STAFF: [ + { + id: 10, + name: "Staff Account", + amount: 0 + } + ] + }; + type: PrintType; + amount: number; + balance: number; + form: FormGroup; + dataSource: ReceivePaymentDatasource; + + displayedColumns = ['name', 'amount']; + + constructor( + public dialogRef: MatDialogRef, + private fb: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: {type: PrintType, amount: number} + ) { + this.createForm(); + this.type = data.type; + this.amount = data.amount; + this.balance = data.amount; + this.form.setControl('amounts', this.fb.array( + this.choices[this.type].map( + x => this.fb.group({ + name: [x.name], + amount: [""] + }) + ) + )); + this.dataSource = new ReceivePaymentDatasource(this.choices[this.type]); + this.listenToAmountChange(); + } + + createForm() { + this.form = this.fb.group({ + amounts: '' + }); + } + + listenToAmountChange() { + const array = this.form.get('amounts') as FormArray; + this.choices[this.type].forEach( + (z, i) => array.controls[i].valueChanges.pipe( + distinctUntilChanged() + ).subscribe(x => { + this.choices[this.type].find(s => s.name == x.name).amount = (x.amount === "" ? 0 : parseInt(x.amount, 10)); + this.balance = this.amount - this.choices[this.type].reduce((a, c) => a + c.amount, 0); + }) + ); + } + + + accept(): void { + this.dialogRef.close(this.choices[this.type]); + } +} diff --git a/bookie/src/app/sales/sales.module.ts b/bookie/src/app/sales/sales.module.ts index 4df267b..1f6f9b1 100644 --- a/bookie/src/app/sales/sales.module.ts +++ b/bookie/src/app/sales/sales.module.ts @@ -26,7 +26,8 @@ import { SalesHomeComponent } from './home/sales-home.component'; import { BillService } from './bill.service'; import { QuantityComponent } from './quantity/quantity.component'; 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"; @NgModule({ providers: [ @@ -40,6 +41,7 @@ import {BillTypeComponent} from "./bill-type/bill-type.component"; ModifiersComponent, ProductsComponent, QuantityComponent, + ReceivePaymentComponent, RunningTablesComponent, SalesHomeComponent ], @@ -66,7 +68,8 @@ import {BillTypeComponent} from "./bill-type/bill-type.component"; BillTypeComponent, DiscountComponent, ModifiersComponent, - QuantityComponent + QuantityComponent, + ReceivePaymentComponent ] }) export class SalesModule { }