Bump to version 3.0.0

Printing done on:
 Cashier Report
 Discount Report
 Sale Report
This commit is contained in:
2020-10-27 12:13:17 +05:30
parent 00fe2410b7
commit cbc2f29e29
32 changed files with 690 additions and 110 deletions

View File

@ -1 +1 @@
__version__ = "2.0.0"
__version__ = "3.0.0"

View File

@ -252,7 +252,7 @@ class Settlement(Base):
nullable=False,
index=True,
)
settled = Column("settled", ForeignKey("settle_options.id"), nullable=False)
settled = Column("settled", Integer, ForeignKey("settle_options.id"), nullable=False)
amount = Column("amount", Numeric, nullable=False)
settle_option = relationship("SettleOption")

View File

@ -0,0 +1,42 @@
import asyncio
from datetime import datetime
from arq import ArqRedis, create_pool
from barker.schemas.cashier_report import CashierReport
from sqlalchemy.orm import Session
from ..core.arq import settings as redis_settings
from ..models import Printer, SectionPrinter
def print_cashier_report(report: CashierReport, db: Session):
data = design_cashier_report(report)
printer = (
db.query(Printer)
.join(SectionPrinter.printer)
# .filter(SectionPrinter.section_id == voucher.food_table.section_id) TODO: Use device's section_id
.filter(SectionPrinter.menu_category_id == None) # noqa: E711
.first()
)
redis: ArqRedis = asyncio.run(create_pool(redis_settings))
asyncio.run(redis.enqueue_job("sent_to_printer", data, printer.address, printer.cut_code))
def design_cashier_report(report: CashierReport):
s = f"{report.cashier.name} Checkout By {report.user.name} @ {datetime.now():%d-%b-%Y %H:%M}".center(42)
s += "\n\r" + "-" * 42
s += "\n\r" f"{report.start_date:%d-%b-%Y} To {report.finish_date:%d-%b-%Y}".center(42)
s += "\n\r" + "-" * 42
for item in report.amounts:
s += f"\n\r{item.name} : {item.amount: >26.2f}"
s += "\n\r" + "=" * 42
s += f"\n\rActive Cashiers : {report.cashiers}"
for so, it in report.info.items():
s += f"\n\r" f"--- {so} ".ljust(42, "-")
for i in it:
s += f"\n\r{i.date} {i.bill_id} {i.customer}"
s += f"\n\rAmount: {i.amount:9.2f}"
s += "\n\r" + "-" * 42
return s

View File

@ -0,0 +1,32 @@
import asyncio
from arq import ArqRedis, create_pool
from sqlalchemy.orm import Session
from ..core.arq import settings as redis_settings
from ..models import Printer, SectionPrinter
from ..schemas.discount_report import DiscountReport
def print_discount_report(report: DiscountReport, db: Session):
data = design_discount_report(report)
printer = (
db.query(Printer)
.join(SectionPrinter.printer)
# .filter(SectionPrinter.section_id == voucher.food_table.section_id) TODO: Use device's section_id
.filter(SectionPrinter.menu_category_id == None) # noqa: E711
.first()
)
redis: ArqRedis = asyncio.run(create_pool(redis_settings))
asyncio.run(redis.enqueue_job("sent_to_printer", data, printer.address, printer.cut_code))
def design_discount_report(report: DiscountReport):
s = f"{report.start_date:%d-%b-%Y} To {report.finish_date:%d-%b-%Y}".center(42)
s += "\n\r" + "-" * 42
s += f"\n\r{'Name': <29.29} {'Amount': >12.12}"
for item in report.amounts:
s += f"\n\r{item.name: <29.29} {item.amount: >12.2f}"
s += "\n\r" + "=" * 42
return s

View File

@ -0,0 +1,35 @@
import asyncio
from datetime import datetime
from arq import ArqRedis, create_pool
from sqlalchemy.orm import Session
from ..core.arq import settings as redis_settings
from ..models import Printer, SectionPrinter
from ..schemas.sale_report import SaleReport
def print_sale_report(report: SaleReport, db: Session):
data = design_sale_report(report)
printer = (
db.query(Printer)
.join(SectionPrinter.printer)
# .filter(SectionPrinter.section_id == voucher.food_table.section_id) TODO: Use device's section_id
.filter(SectionPrinter.menu_category_id == None) # noqa: E711
.first()
)
redis: ArqRedis = asyncio.run(create_pool(redis_settings))
asyncio.run(redis.enqueue_job("sent_to_printer", data, printer.address, printer.cut_code))
def design_sale_report(report: SaleReport):
s = f"{report.user.name} @ {datetime.now():%d-%b-%Y %H:%M}".center(42)
s += "\n\r" + "-" * 42
s += "\n\r" f"{report.start_date:%d-%b-%Y} To {report.finish_date:%d-%b-%Y}".center(42)
s += "\n\r" + "-" * 42
for item in report.amounts:
s += f"\n\r{item.name: <29.22} {item.amount: >12.2f}"
s += "\n\r" + "=" * 42
return s

View File

@ -1,4 +1,5 @@
from datetime import date, datetime, timedelta
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy.orm import Session
@ -8,6 +9,7 @@ from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models import Reprint, Settlement, SettleOption, Voucher, VoucherType
from ...schemas.auth import UserToken
from ...schemas.bill_settlement_report import BillSettlement, BillSettlementItem
router = APIRouter()
@ -22,13 +24,13 @@ def get_db() -> Session:
db.close()
@router.get("")
@router.get("", response_model=BillSettlement)
def bill_details(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["bill-settlement-report"]),
):
) -> BillSettlement:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -37,14 +39,14 @@ def bill_details(
detail="Accounts Audit",
)
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"amounts": settlements(start_date, finish_date, db) + reprints(start_date, finish_date, db),
}
return BillSettlement(
startDate=start_date,
finishDate=finish_date,
amounts=settlements(start_date, finish_date, db) + reprints(start_date, finish_date, db),
)
def settlements(s: date, f: date, db: Session):
def settlements(s: date, f: date, db: Session) -> List[BillSettlementItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
vouchers = (
@ -69,28 +71,30 @@ def settlements(s: date, f: date, db: Session):
else:
details = so.settle_option.name
report.append(
{
"date": item.date.strftime("%d-%b-%Y %H:%M:%S"),
"billId": item.full_bill_id,
"settlement": details,
"amount": round(so.amount, 2),
}
BillSettlementItem(
date=item.date,
billId=item.full_bill_id,
settlement=details,
amount=round(so.amount, 2),
)
)
for i in report:
print(i.json())
return report
def reprints(s: date, f: date, db: Session):
def reprints(s: date, f: date, db: Session) -> List[BillSettlementItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
return [
{
"date": item.date.strftime("%d-%b-%Y %H:%M:%S"),
"billId": item.voucher.full_bill_id,
"settlement": f"Reprinted by {item.user.name}",
"Amount": round(
BillSettlementItem(
date=item.date,
billId=item.voucher.full_bill_id,
settlement=f"Reprinted by {item.user.name}",
amount=round(
next(s.amount for s in item.voucher.settlements if s.settled == SettleOption.AMOUNT()) * -1,
2,
),
}
)
for item in db.query(Reprint).filter(Reprint.date >= start_date, Reprint.date <= finish_date).all()
]

View File

@ -1,6 +1,7 @@
import uuid
from datetime import date, datetime, timedelta
from typing import Dict, List
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import distinct
@ -10,7 +11,14 @@ from ...core.config import settings
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models import Settlement, User, Voucher
from ...schemas.auth import UserToken
from ...printing.cashier_report import print_cashier_report
from ...schemas.auth import UserLink, UserToken
from ...schemas.cashier_report import (
CashierReport,
InfoItem,
NameAmount,
SettleOptionSchema,
)
router = APIRouter()
@ -39,7 +47,10 @@ def active_cashiers(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
return get_active_cashiers(start_date, finish_date, db)
def get_active_cashiers(start_date: date, finish_date: date, db: Session) -> List[UserLink]:
users = (
db.query(User)
.filter(
@ -53,18 +64,17 @@ def active_cashiers(
.order_by(User.name)
.all()
)
return [{"id": u.id, "name": u.name} for u in users]
return [UserLink(id=u.id, name=u.name) for u in users]
@router.get("/{id_}")
@router.get("/{id_}", response_model=CashierReport)
def show_id(
id_: uuid.UUID,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cashier-report"]),
):
) -> CashierReport:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -72,7 +82,12 @@ def show_id(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
user = UserLink(id=user.id_, name=user.name)
return get_id(id_, start_date, finish_date, user, db)
def get_id(id_: uuid.UUID, start_date: date, finish_date: date, user: UserLink, db: Session) -> CashierReport:
cashier: str = db.query(User.name).filter(User.id == id_).scalar()
vouchers = (
db.query(Voucher)
.options(joinedload(Voucher.settlements, innerjoin=True).joinedload(Settlement.settle_option, innerjoin=True))
@ -86,38 +101,61 @@ def show_id(
.all()
)
info = {}
info: Dict[SettleOptionSchema, List[InfoItem]] = {}
amounts = {}
for item in vouchers:
for so in (so for so in item.settlements if so.settle_option.show_in_choices):
if so.settled not in info:
info[so.settled] = []
for so in (so for so in item.settlements if so.settle_option.show_in_choices or so.settle_option.is_print):
if so.settle_option.name not in info:
info[so.settle_option.name] = []
amounts[so.settle_option.name] = 0
info[so.settled].append(
{
"date": item.date.strftime("%d-%b-%Y %H:%M:%S"),
"billId": item.full_bill_id,
"customer": item.customer.name if item.customer is not None else "",
"amount": so.amount,
}
)
amounts[so.settle_option.name] += so.amount
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"user": {"id": id_},
"amounts": [{"name": key, "amount": value} for key, value in amounts.items()],
"info": info,
}
if so.settle_option.is_print:
info[so.settle_option.name].append(
InfoItem(
date=item.date,
billId=item.full_bill_id,
customer=item.customer.name if item.customer is not None else "",
amount=so.amount,
)
)
return CashierReport(
startDate=start_date,
finishDate=finish_date,
cashier=UserLink(id=id_, name=cashier),
cashiers=", ".join(x.name for x in get_active_cashiers(start_date, finish_date, db)),
amounts=[NameAmount(name=key, amount=value) for key, value in amounts.items()],
info=info,
user=user,
)
@router.get("")
@router.get("/print/{id_}", response_model=bool)
def print_report(
id_: uuid.UUID,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cashier-report"]),
) -> bool:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
report = get_id(id_, start_date, finish_date, user, db)
print_cashier_report(report, db)
return True
@router.get("", response_model=CashierReport)
def show_blank(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["cashier-report"]),
):
) -> CashierReport:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -126,10 +164,12 @@ def show_blank(
detail="Accounts Audit",
)
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"user": {"id": ""},
"amounts": [],
"info": [],
}
return CashierReport(
startDate=start_date,
finishDate=finish_date,
cashier=UserLink(id=None),
cashiers="",
amounts=[],
info=[],
user=UserLink(id=user.id_),
)

View File

@ -1,4 +1,5 @@
from datetime import date, datetime, timedelta
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import func
@ -8,7 +9,9 @@ from ...core.config import settings
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models import Inventory, Kot, Product, SaleCategory, Voucher, VoucherType
from ...printing.discount_report import print_discount_report
from ...schemas.auth import UserToken
from ...schemas.discount_report import DiscountReport, DiscountReportItem
router = APIRouter()
@ -23,13 +26,13 @@ def get_db() -> Session:
db.close()
@router.get("")
def discount_report_view(
@router.get("", response_model=DiscountReport)
def discount_report(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["discount-report"]),
):
) -> DiscountReport:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -37,15 +40,14 @@ def discount_report_view(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"amounts": discount_report(start_date, finish_date, db),
}
return DiscountReport(
startDate=start_date,
finishDate=finish_date,
amounts=get_discount_report(start_date, finish_date, db),
)
def discount_report(s: date, f: date, db: Session):
def get_discount_report(s: date, f: date, db: Session) -> List[DiscountReportItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
@ -67,4 +69,27 @@ def discount_report(s: date, f: date, db: Session):
.all()
)
return [{"name": pg, "amount": amt} for pg, amt in list_]
return [DiscountReportItem(name=pg, amount=amt) for pg, amt in list_]
@router.get("/print", response_model=bool)
def print_report(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["discount-report"]),
) -> bool:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
report = DiscountReport(
startDate=start_date,
finishDate=finish_date,
amounts=get_discount_report(start_date, finish_date, db),
)
print_discount_report(report, db)
return True

View File

@ -1,4 +1,5 @@
from datetime import date, datetime, timedelta
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import func
@ -17,7 +18,9 @@ from ...models import (
Voucher,
VoucherType,
)
from ...schemas.auth import UserToken
from ...printing.sale_report import print_sale_report
from ...schemas.auth import UserLink, UserToken
from ...schemas.sale_report import SaleReport, SaleReportItem
from .tax_report import get_tax
@ -33,13 +36,13 @@ def get_db() -> Session:
db.close()
@router.get("")
@router.get("", response_model=SaleReport)
def get_sale_analysis(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["sale-report"]),
):
) -> SaleReport:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -48,20 +51,21 @@ def get_sale_analysis(
detail="Accounts Audit",
)
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"amounts": (
return SaleReport(
startDate=start_date,
finishDate=finish_date,
amounts=(
get_sale(start_date, finish_date, db)
+ [{"name": "--", "amount": 0}]
+ [SaleReportItem(name="--", amount=0)]
+ get_settlements(start_date, finish_date, db)
+ [{"name": "--", "amount": 0}]
+ get_tax(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=0)]
+ [SaleReportItem(name=i.name, amount=i.amount) for i in get_tax(start_date, finish_date, db)]
),
}
user=UserLink(id=user.id_, name=user.name),
)
def get_sale(s: date, f: date, db: Session):
def get_sale(s: date, f: date, db: Session) -> List[SaleReportItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
@ -84,11 +88,11 @@ def get_sale(s: date, f: date, db: Session):
info = []
for gt, am in list_:
total += am
info.append({"name": gt, "amount": am})
return info + [{"name": "Total Settled", "amount": total}]
info.append(SaleReportItem(name=gt, amount=am))
return info + [SaleReportItem(name="Total Settled", amount=total)]
def get_settlements(s: date, f: date, db: Session):
def get_settlements(s: date, f: date, db: Session) -> List[SaleReportItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
@ -105,5 +109,35 @@ def get_settlements(s: date, f: date, db: Session):
info = []
for gt, am in list_:
total += am
info.append({"name": gt, "amount": am})
return info + [{"name": "Total", "amount": total}]
info.append(SaleReportItem(name=gt, amount=am))
return info + [SaleReportItem(name="Total", amount=total)]
@router.get("/print", response_model=bool)
def print_report(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["discount-report"]),
) -> bool:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Accounts Audit",
)
report = SaleReport(
startDate=start_date,
finishDate=finish_date,
amounts=(
get_sale(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=0)]
+ get_settlements(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=0)]
+ [SaleReportItem(name=i.name, amount=i.amount) for i in get_tax(start_date, finish_date, db)]
),
user=UserLink(id=user.id_, name=user.name),
)
print_sale_report(report, db)
return True

View File

@ -1,4 +1,5 @@
from datetime import date, datetime, timedelta
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Security, status
from sqlalchemy import func
@ -9,6 +10,7 @@ from ...core.security import get_current_active_user as get_user
from ...db.session import SessionLocal
from ...models import Inventory, Kot, Tax, Voucher, VoucherType
from ...schemas.auth import UserToken
from ...schemas.tax_report import TaxReport, TaxReportItem
router = APIRouter()
@ -23,13 +25,13 @@ def get_db() -> Session:
db.close()
@router.get("")
@router.get("", response_model=TaxReport)
def get_tax_report(
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["tax-report"]),
):
) -> TaxReport:
start_date = date.today() if s is None else datetime.strptime(s, "%d-%b-%Y").date()
finish_date = date.today() if f is None else datetime.strptime(f, "%d-%b-%Y").date()
if (date.today() - start_date).days > 5 and "accounts-audit" not in user.permissions:
@ -38,14 +40,14 @@ def get_tax_report(
detail="Accounts Audit",
)
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"amounts": get_tax(start_date, finish_date, db),
}
return TaxReport(
startDate=start_date.strftime("%d-%b-%Y"),
finishDate=finish_date.strftime("%d-%b-%Y"),
amounts=get_tax(start_date, finish_date, db),
)
def get_tax(s: date, f: date, db: Session):
def get_tax(s: date, f: date, db: Session) -> List[TaxReportItem]:
start_date = s + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)
finish_date = f + timedelta(days=1, minutes=settings.NEW_DAY_OFFSET_MINUTES)
@ -68,12 +70,4 @@ def get_tax(s: date, f: date, db: Session):
.order_by(Tax.name, Inventory.tax_rate)
.all()
)
return [
{
"name": "{0} - {1:.2%}".format(i[0], i[1]),
"taxRate": i[1],
"saleAmount": i[2],
"amount": i[3],
}
for i in amounts
]
return [TaxReportItem(name=f"{i[0]} - {i[1]:.2%}", taxRate=i[1], saleAmount=i[2], amount=i[3]) for i in amounts]

View File

@ -89,6 +89,14 @@ class User(UserIn):
id_: uuid.UUID
class UserLink(BaseModel):
id_: Optional[uuid.UUID]
name: Optional[str]
class Config:
alias_generator = to_camel
class UserList(BaseModel):
id_: uuid.UUID
name: str

View File

@ -0,0 +1,50 @@
from datetime import date, datetime
from decimal import Decimal
from typing import List
from pydantic import BaseModel, validator
from . import to_camel
class BillSettlementItem(BaseModel):
date: datetime
bill_id: str
settlement: str
amount: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {
datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"),
}
@validator("date", pre=True)
def parse_date(cls, value):
if isinstance(value, datetime):
return value
return datetime.strptime(value, "%d-%b-%Y %H:%M")
class BillSettlement(BaseModel):
start_date: date
finish_date: date
amounts: List[BillSettlementItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")}
@validator("start_date", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("finish_date", pre=True)
def parse_finish_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

View File

@ -0,0 +1,76 @@
from datetime import date, datetime
from decimal import Decimal
from typing import Dict, List
from pydantic import BaseModel, validator
from . import to_camel
from .auth import UserLink
class NameAmount(BaseModel):
name: str
amount: Decimal
class Config:
anystr_strip_whitespace = True
class SettleOptionSchema(BaseModel):
def __hash__(self):
return hash(self.id)
def __eq__(self, other):
return self.id == other.id
id: int
name: str
class Config:
fields = {"id": "id"}
anystr_strip_whitespace = True
class InfoItem(BaseModel):
date: datetime
bill_id: str
customer: str
amount: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
@validator("date", pre=True)
def parse_date(cls, value):
if isinstance(value, datetime):
return value
return datetime.strptime(value, "%d-%b-%Y %H:%M")
class CashierReport(BaseModel):
start_date: date
finish_date: date
cashier: UserLink
cashiers: str
user: UserLink
amounts: List[NameAmount]
info: Dict[str, List[InfoItem]]
class Config:
fields = {"id_": "id"}
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")}
@validator("start_date", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("finish_date", pre=True)
def parse_finish_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

View File

@ -0,0 +1,39 @@
from datetime import date, datetime
from decimal import Decimal
from typing import List
from pydantic import BaseModel, validator
from . import to_camel
class DiscountReportItem(BaseModel):
name: str
amount: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
class DiscountReport(BaseModel):
start_date: date
finish_date: date
amounts: List[DiscountReportItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")}
@validator("start_date", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("finish_date", pre=True)
def parse_finish_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

View File

@ -0,0 +1,41 @@
from datetime import date, datetime
from decimal import Decimal
from typing import List
from pydantic import BaseModel, validator
from . import to_camel
from .auth import UserLink
class SaleReportItem(BaseModel):
name: str
amount: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
class SaleReport(BaseModel):
start_date: date
finish_date: date
amounts: List[SaleReportItem]
user: UserLink
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")}
@validator("start_date", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("finish_date", pre=True)
def parse_finish_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

View File

@ -0,0 +1,41 @@
from datetime import date, datetime
from decimal import Decimal
from typing import List
from pydantic import BaseModel, validator
from . import to_camel
class TaxReportItem(BaseModel):
name: str
tax_rate: Decimal
sale_amount: Decimal
amount: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
class TaxReport(BaseModel):
start_date: date
finish_date: date
amounts: List[TaxReportItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")}
@validator("start_date", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("finish_date", pre=True)
def parse_finish_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "barker"
version = "2.0.0"
version = "3.0.0"
description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"]

View File

@ -2,3 +2,7 @@
display: flex;
justify-content: flex-end;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -1,9 +1,13 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>Cashier Report</mat-card-title>
<button mat-button mat-icon-button (click)="exportCsv()">
<span class="spacer"></span>
<button mat-button mat-icon-button [disabled]="!info.cashier.id" (click)="exportCsv()">
<mat-icon>save_alt</mat-icon>
</button>
<button mat-button mat-icon-button [disabled]="!info.cashier.id" (click)="print()">
<mat-icon>print</mat-icon>
</button>
</mat-card-title-group>
<mat-card-content>
<form [formGroup]="form" fxLayout="column">

View File

@ -3,11 +3,13 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { ToasterService } from '../core/toaster.service';
import { User } from '../core/user';
import { ToCsvService } from '../shared/to-csv.service';
import { CashierReport } from './cashier-report';
import { CashierReportDataSource } from './cashier-report-datasource';
import { CashierReportService } from './cashier-report.service';
@Component({
selector: 'app-cashier-report',
@ -28,6 +30,8 @@ export class CashierReportComponent implements OnInit {
private router: Router,
private fb: FormBuilder,
private toCsv: ToCsvService,
private toaster: ToasterService,
private ser: CashierReportService,
) {
this.createForm();
}
@ -37,7 +41,7 @@ export class CashierReportComponent implements OnInit {
this.activeCashiers = data.cashiers;
this.info = data.info;
this.form.setValue({
cashier: this.info.user.id,
cashier: this.info.cashier.id,
startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(),
finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(),
});
@ -48,8 +52,8 @@ export class CashierReportComponent implements OnInit {
show() {
const info = this.getInfo();
const url = ['cashier-report'];
if (info.user.id) {
url.push(info.user.id);
if (info.cashier.id) {
url.push(info.cashier.id);
}
this.router.navigate(url, {
queryParams: {
@ -59,6 +63,17 @@ export class CashierReportComponent implements OnInit {
});
}
print() {
this.ser.print(this.info.cashier.id, this.info.startDate, this.info.finishDate).subscribe(
() => {
this.toaster.show('', 'Successfully Printed');
},
(error) => {
this.toaster.show('Error', error.error);
},
);
}
createForm() {
this.form = this.fb.group({
startDate: '',
@ -71,7 +86,8 @@ export class CashierReportComponent implements OnInit {
const formModel = this.form.value;
return {
user: new User({ id: formModel.cashier }),
cashier: new User({ id: formModel.cashier }),
cashiers: this.info.cashiers,
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
};

View File

@ -46,4 +46,20 @@ export class CashierReportService {
.pipe(catchError(this.log.handleError(serviceName, 'activeCashiers')));
}
print(id: string, startDate: string, finishDate): Observable<boolean> {
const printUrl = `${url}/print/${id}`;
const options = { params: new HttpParams() };
if (startDate !== null) {
options.params = options.params.set('s', startDate);
}
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
return <Observable<boolean>>(
this.http
.get<boolean>(printUrl, options)
.pipe(catchError(this.log.handleError(serviceName, 'print')))
);
}
}

View File

@ -6,7 +6,8 @@ import { CashierReportPrintItem } from './cashier-report-print-item';
export class CashierReport {
startDate: string;
finishDate: string;
user: User;
cashier: User;
cashiers: string;
amounts?: CashierReportDisplayItem[];
info?: CashierReportPrintItem[];
}

View File

@ -2,3 +2,7 @@
display: flex;
justify-content: flex-end;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -1,9 +1,13 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>Discount Report</mat-card-title>
<span class="spacer"></span>
<button mat-button mat-icon-button (click)="exportCsv()">
<mat-icon>save_alt</mat-icon>
</button>
<button mat-button mat-icon-button (click)="print()">
<mat-icon>print</mat-icon>
</button>
</mat-card-title-group>
<mat-card-content>
<form [formGroup]="form" fxLayout="column">

View File

@ -3,10 +3,12 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { ToasterService } from '../core/toaster.service';
import { ToCsvService } from '../shared/to-csv.service';
import { DiscountReport } from './discount-report';
import { DiscountReportDataSource } from './discount-report-datasource';
import { DiscountReportService } from './discount-report.service';
@Component({
selector: 'app-discount-report',
@ -26,6 +28,8 @@ export class DiscountReportComponent implements OnInit {
private router: Router,
private fb: FormBuilder,
private toCsv: ToCsvService,
private toaster: ToasterService,
private ser: DiscountReportService,
) {
this.createForm();
}
@ -67,6 +71,17 @@ export class DiscountReportComponent implements OnInit {
};
}
print() {
this.ser.print(this.info.startDate, this.info.finishDate).subscribe(
() => {
this.toaster.show('', 'Successfully Printed');
},
(error) => {
this.toaster.show('Error', error.error);
},
);
}
exportCsv() {
const headers = {
Name: 'name',

View File

@ -30,4 +30,20 @@ export class DiscountReportService {
.pipe(catchError(this.log.handleError(serviceName, 'get')))
);
}
print(startDate: string, finishDate): Observable<boolean> {
const printUrl = `${url}/print`;
const options = { params: new HttpParams() };
if (startDate !== null) {
options.params = options.params.set('s', startDate);
}
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
return <Observable<boolean>>(
this.http
.get<boolean>(printUrl, options)
.pipe(catchError(this.log.handleError(serviceName, 'print')))
);
}
}

View File

@ -2,3 +2,7 @@
display: flex;
justify-content: flex-end;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -1,9 +1,13 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>Sale Report</mat-card-title>
<span class="spacer"></span>
<button mat-button mat-icon-button (click)="exportCsv()">
<mat-icon>save_alt</mat-icon>
</button>
<button mat-button mat-icon-button (click)="print()">
<mat-icon>print</mat-icon>
</button>
</mat-card-title-group>
<mat-card-content>
<form [formGroup]="form" fxLayout="column">

View File

@ -3,10 +3,12 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { ToasterService } from '../core/toaster.service';
import { ToCsvService } from '../shared/to-csv.service';
import { SaleReport } from './sale-report';
import { SaleReportDatasource } from './sale-report-datasource';
import { SaleReportService } from './sale-report.service';
@Component({
selector: 'app-sale-report',
@ -26,6 +28,8 @@ export class SaleReportComponent implements OnInit {
private router: Router,
private fb: FormBuilder,
private toCsv: ToCsvService,
private toaster: ToasterService,
private ser: SaleReportService,
) {
this.createForm();
}
@ -67,6 +71,17 @@ export class SaleReportComponent implements OnInit {
};
}
print() {
this.ser.print(this.info.startDate, this.info.finishDate).subscribe(
() => {
this.toaster.show('', 'Successfully Printed');
},
(error) => {
this.toaster.show('Error', error.error);
},
);
}
exportCsv() {
const headers = {
Name: 'name',

View File

@ -30,4 +30,20 @@ export class SaleReportService {
.pipe(catchError(this.log.handleError(serviceName, 'get')))
);
}
print(startDate: string, finishDate): Observable<boolean> {
const printUrl = `${url}/print`;
const options = { params: new HttpParams() };
if (startDate !== null) {
options.params = options.params.set('s', startDate);
}
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
return <Observable<boolean>>(
this.http
.get<boolean>(printUrl, options)
.pipe(catchError(this.log.handleError(serviceName, 'print')))
);
}
}

View File

@ -1,5 +1,5 @@
export const environment = {
production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: "2.0.0",
version: '3.0.0',
};

View File

@ -5,7 +5,7 @@
export const environment = {
production: false,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: "2.0.0",
version: '3.0.0',
};
/*