From c14e64ce85539d35ae88f0b95f59fbad9bd20e1b Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Sun, 19 Mar 2023 09:56:48 +0530 Subject: [PATCH] Feature: The menu engineering report now recalculates the percentages based on filter --- .../reports/menu_engineering_report.py | 56 ++++++++++++------- .../barker/schemas/menu_engineering_report.py | 48 ++++++++++++++++ .../menu-engineering-report-datasource.ts | 29 ++++++++-- .../menu-engineering-report-item.ts | 8 ++- .../menu-engineering-report.component.html | 16 ++++-- .../menu-engineering-report.component.ts | 4 +- 6 files changed, 126 insertions(+), 35 deletions(-) create mode 100644 barker/barker/schemas/menu_engineering_report.py diff --git a/barker/barker/routers/reports/menu_engineering_report.py b/barker/barker/routers/reports/menu_engineering_report.py index 25a434e..2e05fa5 100644 --- a/barker/barker/routers/reports/menu_engineering_report.py +++ b/barker/barker/routers/reports/menu_engineering_report.py @@ -1,7 +1,8 @@ import uuid from datetime import date, datetime, time, timedelta -from typing import Any +from decimal import Decimal +from typing import Dict from fastapi import APIRouter, Cookie, Depends, Security from sqlalchemy import func, nulls_last, or_, select @@ -19,6 +20,7 @@ from ...models.sale_category import SaleCategory from ...models.voucher import Voucher from ...models.voucher_type import VoucherType from ...printing.product_sale_report import print_product_sale_report +from ...schemas.menu_engineering_report import MeItem, MeReport from ...schemas.user_token import UserToken from . import check_audit_permission, report_finish_date, report_start_date @@ -26,7 +28,7 @@ from . import check_audit_permission, report_finish_date, report_start_date router = APIRouter() -@router.get("") +@router.get("", response_model=MeReport) def menu_engineering_report_view( start_date: date = Depends(report_start_date), finish_date: date = Depends(report_finish_date), @@ -34,11 +36,11 @@ def menu_engineering_report_view( ): check_audit_permission(start_date, user.permissions) with SessionFuture() as db: - return { - "startDate": start_date.strftime("%d-%b-%Y"), - "finishDate": finish_date.strftime("%d-%b-%Y"), - "amounts": menu_engineering_report(start_date, finish_date, db), - } + return MeReport( + startDate=start_date, + finishDate=finish_date, + amounts=menu_engineering_report(start_date, finish_date, db), + ) def menu_engineering_report(s: date, f: date, db: Session): @@ -100,20 +102,36 @@ def menu_engineering_report(s: date, f: date, db: Session): ) print(q) list_ = db.execute(q).all() - info: list[Any] = [] - for sc, mc, id_, name, price, quantity, amount in list_: + info: list[MeItem] = [] + sc_sales: Dict[str, Decimal] = {} + sc_quantity: Dict[str, Decimal] = {} + for sc, mc, id_, name, price, quantity, sales in list_: + if sc not in sc_sales: + sc_sales[sc] = Decimal(0) + if sales is not None: + sc_sales[sc] += sales + if sc not in sc_quantity: + sc_quantity[sc] = Decimal(0) + if quantity is not None: + sc_quantity[sc] += quantity info.append( - { - "id": id_, - "name": name, - "price": price, - "average": round(amount / quantity) if amount and quantity else price, - "saleCategory": sc, - "menuCategory": mc, - "quantity": quantity, - "amount": amount, - } + MeItem( + id=id_, + name=name, + price=price, + average=round(sales / quantity) if sales and quantity else price, + saleCategory=sc, + menuCategory=mc, + quantity=quantity or Decimal(0), + sales=sales or Decimal(0), + quantityPercent=Decimal(0), + salesPercent=Decimal(0), + ) ) + for item in info: + if sc_quantity[item.sale_category] > 0: + item.quantity_percent = round(item.quantity / sc_quantity[item.sale_category], 4) + item.sales_percent = round(item.sales / sc_sales[item.sale_category], 4) return info diff --git a/barker/barker/schemas/menu_engineering_report.py b/barker/barker/schemas/menu_engineering_report.py new file mode 100644 index 0000000..bc9e6e2 --- /dev/null +++ b/barker/barker/schemas/menu_engineering_report.py @@ -0,0 +1,48 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal + +from pydantic import BaseModel, validator + +from . import to_camel + + +class MeItem(BaseModel): + id_: uuid.UUID + name: str + price: Decimal + average: Decimal + sale_category: str + menu_category: str + quantity: Decimal + sales: Decimal + quantity_percent: Decimal + sales_percent: Decimal + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class MeReport(BaseModel): + start_date: date + finish_date: date + amounts: list[MeItem] + + 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() diff --git a/bookie/src/app/menu-engineering-report/menu-engineering-report-datasource.ts b/bookie/src/app/menu-engineering-report/menu-engineering-report-datasource.ts index d4eea2f..f6788e1 100644 --- a/bookie/src/app/menu-engineering-report/menu-engineering-report-datasource.ts +++ b/bookie/src/app/menu-engineering-report/menu-engineering-report-datasource.ts @@ -37,6 +37,7 @@ export class MenuEngineeringReportDataSource extends DataSource this.getFilteredData([...this.data])), + map((x) => this.recalculate(x)), tap((x: MenuEngineeringReportItem[]) => { if (this.paginator) { this.paginator.length = x.length; @@ -67,10 +68,10 @@ export class MenuEngineeringReportDataSource extends DataSource=|<=|>=|<|>)(?\d*)$/); + if (c.startsWith('s:')) { + const result = c.match(/^s:(?=|<=|>=|<|>)(?\d*)$/); if (result && result.groups) { - return math_it_up(result.groups['sign'])(x.amount, +result.groups['amount'] ?? ''); + return math_it_up(result.groups['sign'])(x.sales, +result.groups['amount'] ?? ''); } } const itemString = `${x.name} ${x.saleCategory} ${x.menuCategory}`.toLowerCase(); @@ -80,6 +81,22 @@ export class MenuEngineeringReportDataSource extends DataSource, value: MenuEngineeringReportItem) { + if (!res[value['saleCategory']]) { + res[value['saleCategory']] = { q: 0, s: 0 }; + } + res[value['saleCategory']].q += value.quantity; + res[value['saleCategory']].s += value.sales; + return res; + }, {}); + data.forEach((v) => { + v.quantityPercent = v.quantity / gr[v.saleCategory].q; + v.salesPercent = v.sales / gr[v.saleCategory].s; + }); + return data; + } + private getPagedData(data: MenuEngineeringReportItem[]) { if (this.paginator === undefined) { return data; @@ -110,8 +127,8 @@ export class MenuEngineeringReportDataSource extends DataSource y; + return x === y; } function gt(x: number, y: number) { return x > y; diff --git a/bookie/src/app/menu-engineering-report/menu-engineering-report-item.ts b/bookie/src/app/menu-engineering-report/menu-engineering-report-item.ts index 998df09..f2fa687 100644 --- a/bookie/src/app/menu-engineering-report/menu-engineering-report-item.ts +++ b/bookie/src/app/menu-engineering-report/menu-engineering-report-item.ts @@ -6,7 +6,9 @@ export class MenuEngineeringReportItem { saleCategory: string; menuCategory: string; quantity: number; - amount: number; + quantityPercent: number; + sales: number; + salesPercent: number; public constructor(init?: Partial) { this.id = ''; @@ -16,7 +18,9 @@ export class MenuEngineeringReportItem { this.saleCategory = ''; this.menuCategory = ''; this.quantity = 0; - this.amount = 0; + this.quantityPercent = 0; + this.sales = 0; + this.salesPercent = 0; Object.assign(this, init); } } diff --git a/bookie/src/app/menu-engineering-report/menu-engineering-report.component.html b/bookie/src/app/menu-engineering-report/menu-engineering-report.component.html index 51ee0e0..6277499 100644 --- a/bookie/src/app/menu-engineering-report/menu-engineering-report.component.html +++ b/bookie/src/app/menu-engineering-report/menu-engineering-report.component.html @@ -46,7 +46,7 @@ n: Name, sc: Sale Category, mc: Menu Category, q:(=/<=/>=/</>)Quantity, - a:(=/<=/>=/</>)Amount @@ -81,13 +81,17 @@ Quantity - {{ row.quantity | number : '1.2-2' }} + {{ row.quantity | number : '1.2-2' }} ({{ row.quantityPercent | percent : '1.2-2' }}) - - - Amount - {{ row.amount | number : '1.2-2' }} + + + Sales + {{ row.sales | number : '1.2-2' }} ({{ row.salesPercent | percent : '1.2-2' }}) diff --git a/bookie/src/app/menu-engineering-report/menu-engineering-report.component.ts b/bookie/src/app/menu-engineering-report/menu-engineering-report.component.ts index df5e8f7..eb653d3 100644 --- a/bookie/src/app/menu-engineering-report/menu-engineering-report.component.ts +++ b/bookie/src/app/menu-engineering-report/menu-engineering-report.component.ts @@ -33,7 +33,7 @@ export class MenuEngineeringReportComponent implements OnInit { }>; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'price', 'saleCategory', 'menuCategory', 'quantity', 'amount']; + displayedColumns = ['name', 'price', 'saleCategory', 'menuCategory', 'quantity', 'sales']; constructor( private route: ActivatedRoute, @@ -104,7 +104,7 @@ export class MenuEngineeringReportComponent implements OnInit { 'Sale Category': 'saleCategory', 'Menu Category': 'menuCategory', Quantity: 'quantity', - Amount: 'amount', + Sales: 'sales', }; const csvData = new Blob([this.toCsv.toCsv(headers, this.dataSource.data)], { type: 'text/csv;charset=utf-8;',