Fix: Prevent creation of customer with blank name when adding a guest book item.

Fix: Also prevent creation of customer with blank phone number when adding a guest book.
Feature: Show the old bill of a customer in guest book
Fix: In reprint, allow changing of customer
Chore: Updated dependencies
This commit is contained in:
Amritanshu Agrawal 2023-04-09 15:42:32 +05:30
parent 302ed4a18f
commit 8bc7d66123
9 changed files with 81 additions and 31 deletions

View File

@ -11,5 +11,6 @@
"**/.mypy_cache": true, "**/.mypy_cache": true,
".idea": true, ".idea": true,
"**/node_modules": true, "**/node_modules": true,
"**/package-lock.json": true,
} }
} }

View File

@ -2,12 +2,12 @@ import uuid
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List
from barker.models.role_permission import RolePermission
from sqlalchemy import Unicode, text from sqlalchemy import Unicode, text
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..db.base_class import reg from ..db.base_class import reg
from .role_permission import RolePermission
if TYPE_CHECKING: if TYPE_CHECKING:

View File

@ -122,6 +122,6 @@ class ProductVersion:
return False, f"{self.name} is a fixture and cannot be edited or deleted." return False, f"{self.name} is a fixture and cannot be edited or deleted."
if self.is_active: if self.is_active:
return False, "Product is active" return False, "Product is active"
if len(self.inventories) > 0 and not advanced_delete: # if len(self.inventories) > 0 and not advanced_delete:
return False, "Product has entries" # return False, "Product has entries"
return True, "" return True, ""

View File

@ -5,14 +5,16 @@ from datetime import date, datetime, timedelta
import barker.schemas.guest_book as schemas import barker.schemas.guest_book as schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import select from sqlalchemy import desc, select
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import joinedload
from ..core.config import settings from ..core.config import settings
from ..core.security import get_current_active_user as get_user from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture from ..db.session import SessionFuture
from ..models.customer import Customer from ..models.customer import Customer
from ..models.guest_book import GuestBook from ..models.guest_book import GuestBook
from ..models.voucher import Voucher
from ..schemas.user_token import UserToken from ..schemas.user_token import UserToken
@ -28,6 +30,13 @@ def save(
with SessionFuture() as db: with SessionFuture() as db:
customer = db.execute(select(Customer).where(Customer.phone == data.phone)).scalars().one_or_none() customer = db.execute(select(Customer).where(Customer.phone == data.phone)).scalars().one_or_none()
if customer is None: if customer is None:
if len(data.name) == 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="New customer name cannot be blank",
)
if len(data.phone) == 0:
data.phone = data.name
customer = Customer( customer = Customer(
name=data.name, name=data.name,
phone=data.phone, phone=data.phone,
@ -127,21 +136,34 @@ def show_list(
guest_book: list[schemas.GuestBookListItem] = [] guest_book: list[schemas.GuestBookListItem] = []
with SessionFuture() as db: with SessionFuture() as db:
for i, item in enumerate(db.execute(list_).scalars().all()): for i, item in enumerate(db.execute(list_).scalars().all()):
guest_book.insert( gbli = schemas.GuestBookListItem(
0, id=item.id,
schemas.GuestBookListItem( serial=i + 1,
id=item.id, name=item.customer.name,
serial=i + 1, phone=item.customer.phone,
name=item.customer.name, pax=item.pax,
phone=item.customer.phone, date=item.date,
pax=item.pax, status=None if item.status is None else item.status.status,
date=item.date, tableId=None if item.status is None else item.status.food_table.id,
status=None if item.status is None else item.status.status, voucherId=None if item.status is None else item.status.voucher_id,
tableId=None if item.status is None else item.status.food_table.id, tableName=None if item.status is None else item.status.food_table.name,
voucherId=None if item.status is None else item.status.voucher_id,
tableName=None if item.status is None else item.status.food_table.name,
),
) )
if item.status is None:
last = (
db.execute(
select(Voucher)
.where(Voucher.customer_id == item.customer_id)
.order_by(desc(Voucher.date))
.options(joinedload(Voucher.food_table, innerjoin=True))
)
.scalars()
.first()
)
if last is not None:
gbli.status = "old"
gbli.voucher_id = last.id
gbli.table_name = last.food_table.name
guest_book.insert(0, gbli)
return schemas.GuestBookList(date=d, list=guest_book) return schemas.GuestBookList(date=d, list=guest_book)

View File

@ -56,6 +56,10 @@ def change(
if bill_changed: if bill_changed:
id_ = void_and_issue_new_bill(data, u, g, old, db, user) id_ = void_and_issue_new_bill(data, u, g, old, db, user)
else: else:
if data.customer is not None:
old.customer_id = data.customer.id_
else:
old.customer_id = None
reprint_bill(id_, user.id_, db) reprint_bill(id_, user.id_, db)
db.commit() db.commit()
with SessionFuture() as db: with SessionFuture() as db:

View File

@ -6,31 +6,31 @@ authors = ["tanshu <git@tanshu.com>"]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = "^3.11"
uvicorn = {extras = ["standard"], version = "^0.20.0"} uvicorn = {extras = ["standard"], version = "^0.21.1"}
fastapi = "^0.92.0" fastapi = "^0.95.0"
python-jose = {extras = ["cryptography"], version = "^3.3.0"} python-jose = {extras = ["cryptography"], version = "^3.3.0"}
passlib = {extras = ["bcrypt"], version = "^1.7.4"} passlib = {extras = ["bcrypt"], version = "^1.7.4"}
psycopg2-binary = "^2.9.5" psycopg2-binary = "^2.9.5"
SQLAlchemy = "^2.0.4" SQLAlchemy = "^2.0.7"
python-multipart = "^0.0.5" python-multipart = "^0.0.6"
PyJWT = "^2.6.0" PyJWT = "^2.6.0"
alembic = "^1.9.4" alembic = "^1.10.2"
itsdangerous = "^2.1.2" itsdangerous = "^2.1.2"
python-dotenv = "^0.21.1" python-dotenv = "^1.0.0"
pydantic = {extras = ["dotenv"], version = "^1.10.5"} pydantic = {extras = ["dotenv"], version = "^1.10.7"}
starlette = "^0.25.0" starlette = "^0.26.1"
arq = "^0.25.0" arq = "^0.25.0"
aiohttp = "^3.8.4" aiohttp = "^3.8.4"
cryptography = "^39.0.1" cryptography = "^40.0.1"
gunicorn = "^20.1.0" gunicorn = "^20.1.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
flake8 = "^6.0.0" flake8 = "^6.0.0"
black = "^23.1.0" black = "^23.1.0"
isort = {extras = ["toml"], version = "^5.12.0"} isort = {extras = ["toml"], version = "^5.12.0"}
pre-commit = "^3.0.4" pre-commit = "^3.2.0"
mypy = "^1.0.1" mypy = "^1.1.1"
types-python-jose = "^3.3.4.4" types-python-jose = "^3.3.4.5"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
tomli = "^2.0.1" tomli = "^2.0.1"

View File

@ -54,7 +54,12 @@
<ng-container matColumnDef="action"> <ng-container matColumnDef="action">
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell> <mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
<mat-cell *matCellDef="let row" class="center"> <mat-cell *matCellDef="let row" class="center">
<button mat-icon-button [routerLink]="['/sales']" [queryParams]="{ guest: row.id }" *ngIf="!row.tableId"> <button
mat-icon-button
[routerLink]="['/sales']"
[queryParams]="{ guest: row.id }"
*ngIf="!row.tableId && !row.voucherId"
>
<mat-icon>chair</mat-icon> <mat-icon>chair</mat-icon>
</button> </button>
<button <button
@ -66,6 +71,15 @@
> >
{{ row.tableName }} {{ row.tableName }}
</button> </button>
<button
mat-stroked-button
color="primary"
[routerLink]="['/sales', 'bill']"
[queryParams]="{ voucher: row.voucherId }"
*ngIf="!row.tableId && row.voucherId"
>
{{ row.tableName }}
</button>
<button mat-icon-button [routerLink]="['/guest-book/', row.id]"> <button mat-icon-button [routerLink]="['/guest-book/', row.id]">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>

View File

@ -22,6 +22,9 @@ export class BillResolver implements Resolve<Bill> {
if (tableId !== null) { if (tableId !== null) {
return this.ser.getFromTable(tableId as string, voucherId, guestId); return this.ser.getFromTable(tableId as string, voucherId, guestId);
} }
if (voucherId !== null) {
return this.ser.getFromId(voucherId);
}
throw new Error('Unable to get bill'); throw new Error('Unable to get bill');
} }
} }

View File

@ -52,6 +52,12 @@ export class VoucherService {
.pipe(catchError(this.log.handleError(serviceName, `getFromBill billId=${billId}`))) as Observable<Bill>; .pipe(catchError(this.log.handleError(serviceName, `getFromBill billId=${billId}`))) as Observable<Bill>;
} }
getFromId(voucherId: string): Observable<Bill> {
return this.http
.get<Bill>(`${url}/from-id/${voucherId}`)
.pipe(catchError(this.log.handleError(serviceName, `getFromId voucherId=${voucherId}`))) as Observable<Bill>;
}
save(voucher: Bill, voucherType: VoucherType, guestBookId: string | null, updateTable: boolean): Observable<boolean> { save(voucher: Bill, voucherType: VoucherType, guestBookId: string | null, updateTable: boolean): Observable<boolean> {
const options = { const options = {
params: new HttpParams().set('p', voucherType.toString()).set('u', updateTable.toString()), params: new HttpParams().set('p', voucherType.toString()).set('u', updateTable.toString()),