Compare commits

...
5 Commits
Author SHA1 Message Date
tanshu dcf1ffad98 Version Bump v14.3.0 2026-02-17 09:45:32 +05:30
tanshu 338d9d63d5 Validation: Making Product name unique ignoring the fraction_units.
Making the sku_version.units unique for a product across stock_keeping_units.

Both respecting the validity
2026-02-17 04:13:11 +00:00
tanshu 9b0da9cb65 Fix: Product sale report was aggregating on the product_version_id 2026-02-14 12:20:09 +00:00
tanshu 4cc2ff2229 Version Bump v14.2.1 2026-02-14 11:30:40 +05:30
tanshu c9fa83e8ac Fix: Accidentally used menu category instead of sale category 2026-02-14 06:00:09 +00:00
19 changed files with 231 additions and 54 deletions
@@ -34,7 +34,7 @@ def upgrade():
) )
op.create_exclude_constraint( op.create_exclude_constraint(
"uq_product_versions_product_id", op.f("uq_product_versions_product_id"),
"product_versions", "product_versions",
(prod.c.product_id, "="), (prod.c.product_id, "="),
(func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"), (func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"),
@@ -0,0 +1,156 @@
"""index
Revision ID: 8260414066d6
Revises: 5cb65066be86
Create Date: 2026-02-13 06:22:39.926120
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "8260414066d6"
down_revision = "5cb65066be86"
branch_labels = None
depends_on = None
def upgrade():
# 1) Add the column (nullable for backfill)
op.add_column("sku_versions", sa.Column("product_id", sa.UUID(), nullable=True))
# 2) Define lightweight table objects (no autoload; explicit columns only)
sku_versions = sa.Table(
"sku_versions",
sa.MetaData(),
sa.Column("id", sa.UUID(), primary_key=True),
sa.Column("sku_id", sa.UUID(), nullable=False),
sa.Column("product_id", sa.UUID(), nullable=True),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
sa.Column("units", sa.Unicode(), nullable=False),
)
stock_keeping_units = sa.Table(
"stock_keeping_units",
sa.MetaData(),
sa.Column("id", sa.UUID(), primary_key=True),
sa.Column("product_id", sa.UUID(), nullable=False),
)
# 3) Backfill via SQLAlchemy Core UPDATE..SET..(SELECT ...)
product_id_subq = (
sa.select(stock_keeping_units.c.product_id)
.where(stock_keeping_units.c.id == sku_versions.c.sku_id)
.scalar_subquery()
)
backfill_stmt = (
sa.update(sku_versions).values(product_id=product_id_subq).where(sku_versions.c.product_id.is_(None))
)
op.execute(backfill_stmt)
# 5) Enforce NOT NULL at schema level
op.alter_column("sku_versions", "product_id", nullable=False)
# 6) Trigger keeps product_id in sync when sku_id changes
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION sku_versions_set_product_id()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
SELECT s.product_id INTO NEW.product_id
FROM stock_keeping_units s
WHERE s.id = NEW.sku_id;
IF NEW.product_id IS NULL THEN
RAISE EXCEPTION 'Invalid sku_id %, no product found', NEW.sku_id;
END IF;
RETURN NEW;
END;
$$;
"""
)
)
op.execute(sa.text("DROP TRIGGER IF EXISTS trg_sku_versions_set_product_id ON sku_versions;"))
op.execute(
sa.text(
"""
CREATE TRIGGER trg_sku_versions_set_product_id
BEFORE INSERT OR UPDATE OF sku_id
ON sku_versions
FOR EACH ROW
EXECUTE FUNCTION sku_versions_set_product_id();
"""
)
)
# 7) Exclusion constraint: product_id + units must not overlap in time
# daterange(valid_from, valid_till, '[]') overlap operator &&.
sv = sa.table(
"sku_versions",
sa.column("product_id", sa.UUID()),
sa.column("units", sa.UUID()),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
op.create_exclude_constraint(
op.f("uq_sku_versions_product_id_units"),
"sku_versions",
(sv.c.product_id, "="),
(sv.c.units, "="),
(sa.func.daterange(sv.c.valid_from, sv.c.valid_till, sa.text("'[]'")), "&&"),
)
prod = sa.table(
"product_versions",
sa.column("id", sa.UUID()),
sa.column("name", sa.Unicode(length=255)),
sa.column("fraction_units", sa.Unicode(length=255)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
# Update the exclude constraint on product_versions to only be on the name and drop fraction_units from it
op.drop_constraint("uq_product_versions_name", "product_versions", type_="unique")
op.create_exclude_constraint(
op.f("uq_product_versions_name"),
"product_versions",
(prod.c.name, "="),
(sa.func.daterange(prod.c.valid_from, prod.c.valid_till, sa.text("'[]'")), "&&"),
)
# ### end Alembic commands ###
def downgrade():
op.drop_constraint("uq_sku_versions_product_id_units", "sku_versions", type_="exclude")
# Drop trigger + function
op.execute(sa.text("DROP TRIGGER IF EXISTS trg_sku_versions_set_product_id ON sku_versions;"))
op.execute(sa.text("DROP FUNCTION IF EXISTS sku_versions_set_product_id();"))
# Drop column
op.drop_column("sku_versions", "product_id")
prod = sa.table(
"product_versions",
sa.column("id", sa.UUID()),
sa.column("name", sa.Unicode(length=255)),
sa.column("fraction_units", sa.Unicode(length=255)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
op.drop_constraint("uq_product_versions_name", "product_versions", type_="unique")
op.create_exclude_constraint(
"uq_product_versions_name",
"product_versions",
(prod.c.name, "="),
(prod.c.units, "="),
(sa.func.daterange(prod.c.valid_from, prod.c.valid_till, sa.text("'[]'")), "&&"),
)
+1 -1
View File
@@ -1 +1 @@
__version__ = "14.2.0" __version__ = "14.3.0"
+25 -1
View File
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
from sqlalchemy import Boolean, Date, ForeignKey, Numeric, Unicode, Uuid, func, text from sqlalchemy import Boolean, Date, ForeignKey, Numeric, Unicode, Uuid, func, text
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship, validates
from ..db.base_class import reg from ..db.base_class import reg
@@ -26,6 +26,14 @@ class SkuVersion:
Uuid, primary_key=True, insert_default=uuid.uuid4, server_default=text("gen_random_uuid()") Uuid, primary_key=True, insert_default=uuid.uuid4, server_default=text("gen_random_uuid()")
) )
sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False) sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False)
# DB column is "product_id", but ORM attribute is private: "_product_id"
_product_id: Mapped[uuid.UUID] = mapped_column(
"product_id",
Uuid,
nullable=False,
repr=False, # hides in dataclass repr
init=False, # not part of __init__ signature (dataclass)
)
units: Mapped[str] = mapped_column( units: Mapped[str] = mapped_column(
Unicode, nullable=False Unicode, nullable=False
) # Need to have logic in the application to handle unit uniqueness since we don't have product_id here ) # Need to have logic in the application to handle unit uniqueness since we don't have product_id here
@@ -56,8 +64,24 @@ class SkuVersion:
(sku_id, "="), (sku_id, "="),
(func.daterange(valid_from, valid_till, text("'[]'")), "&&"), (func.daterange(valid_from, valid_till, text("'[]'")), "&&"),
), ),
# product-level uniqueness per time range
postgresql.ExcludeConstraint(
(_product_id, "="),
(units, "="),
(func.daterange(valid_from, valid_till, text("'[]'")), "&&"),
name="uq_sku_versions_product_units_time",
using="gist",
),
) )
@validates("_product_id")
def _prevent_writes_to_product_id(self, key, value):
"""
This column is integrity-only. We never accept app-side writes.
DB trigger sets it.
"""
raise ValueError("product_id is managed by the database and must not be set in application code.")
def __init__( def __init__(
self, self,
units: str = "", units: str = "",
@@ -18,7 +18,7 @@ from ...models.stock_keeping_unit import StockKeepingUnit
from ...models.voucher import Voucher from ...models.voucher import Voucher
from ...models.voucher_type import VoucherType from ...models.voucher_type import VoucherType
from ...schemas.beer_consumption_report import BeerConsumptionReport, BeerConsumptionReportItem from ...schemas.beer_consumption_report import BeerConsumptionReport, BeerConsumptionReportItem
from ...schemas.menu_category import MenuCategoryLink from ...schemas.sale_category import SaleCategoryLink
from ...schemas.user_token import UserToken from ...schemas.user_token import UserToken
from .. import _pv_onclause, _sv_onclause from .. import _pv_onclause, _sv_onclause
from . import check_audit_permission, report_finish_date, report_start_date from . import check_audit_permission, report_finish_date, report_start_date
@@ -31,7 +31,7 @@ router = APIRouter()
def beer_consumption( def beer_consumption(
start_date: date = Depends(report_start_date), start_date: date = Depends(report_start_date),
finish_date: date = Depends(report_finish_date), finish_date: date = Depends(report_finish_date),
m: uuid.UUID | None = None, # Menu Category sc: uuid.UUID | None = None, # Sale Category
r: bool | None = True, r: bool | None = True,
h: bool | None = True, h: bool | None = True,
st: bool | None = True, st: bool | None = True,
@@ -51,7 +51,6 @@ def beer_consumption(
.join(Kot.inventories) .join(Kot.inventories)
.join(Inventory.sku) .join(Inventory.sku)
.join(SkuVersion, onclause=sku_version_onclause) .join(SkuVersion, onclause=sku_version_onclause)
.join(SkuVersion.menu_category)
.join(StockKeepingUnit.product) .join(StockKeepingUnit.product)
.join(ProductVersion, onclause=product_version_onclause) .join(ProductVersion, onclause=product_version_onclause)
.where( .where(
@@ -60,8 +59,8 @@ def beer_consumption(
day <= finish_date, day <= finish_date,
) )
) )
if m: if sc:
query = query.where(SkuVersion.menu_category_id == m) query = query.where(ProductVersion.sale_category_id == sc)
if h is False and r is not False: if h is False and r is not False:
query = query.where(Inventory.is_happy_hour == h) query = query.where(Inventory.is_happy_hour == h)
if r is False and h is not False: if r is False and h is not False:
@@ -96,7 +95,7 @@ def beer_consumption(
return BeerConsumptionReport( return BeerConsumptionReport(
start_date=start_date, start_date=start_date,
finish_date=finish_date, finish_date=finish_date,
menu_category=MenuCategoryLink(id_=m, name="", skus=[]) if m else None, sale_category=SaleCategoryLink(id_=sc, name="") if sc else None,
regular=r, regular=r,
happy=h, happy=h,
staff=st, staff=st,
@@ -53,13 +53,11 @@ def product_sale_report(
start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session
) -> list[ProductSaleReportItem]: ) -> list[ProductSaleReportItem]:
day = func.cast( day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date Kot.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day") ).label("day")
product_version_onclause = _pv_onclause(day)
sku_version_onclause = _sv_onclause(day)
query = ( query = (
select( select(
ProductVersion.id, StockKeepingUnit.id,
ProductVersion.name, ProductVersion.name,
SkuVersion.units, SkuVersion.units,
Voucher.voucher_type, Voucher.voucher_type,
@@ -69,10 +67,10 @@ def product_sale_report(
.join(Inventory.kot) .join(Inventory.kot)
.join(Kot.voucher) .join(Kot.voucher)
.join(Inventory.sku) .join(Inventory.sku)
.join(SkuVersion, onclause=sku_version_onclause) .join(SkuVersion, onclause=_sv_onclause(day))
.join(SkuVersion.menu_category) .join(SkuVersion.menu_category)
.join(StockKeepingUnit.product) .join(StockKeepingUnit.product)
.join(ProductVersion, onclause=product_version_onclause) .join(ProductVersion, onclause=_pv_onclause(day))
.join(ProductVersion.sale_category) .join(ProductVersion.sale_category)
.join(Voucher.food_table) .join(Voucher.food_table)
.where( .where(
@@ -85,7 +83,7 @@ def product_sale_report(
query = query.group_by( query = query.group_by(
SaleCategory.name, SaleCategory.name,
MenuCategory.name, MenuCategory.name,
ProductVersion.id, StockKeepingUnit.id,
ProductVersion.name, ProductVersion.name,
SkuVersion.units, SkuVersion.units,
Voucher.voucher_type, Voucher.voucher_type,
@@ -93,14 +91,14 @@ def product_sale_report(
).order_by(SaleCategory.name, MenuCategory.name, ProductVersion.name, SkuVersion.units) ).order_by(SaleCategory.name, MenuCategory.name, ProductVersion.name, SkuVersion.units)
list_ = db.execute(query).all() list_ = db.execute(query).all()
info: list[ProductSaleReportItem] = [] info: list[ProductSaleReportItem] = []
for product_version_id, name, units, v_type, hh, quantity in list_: for sku_id, name, units, v_type, hh, quantity in list_:
type_ = VoucherType(v_type).name type_ = VoucherType(v_type).name
old = next((i for i in info if i.product_version_id == product_version_id and i.is_happy_hour == hh), None) old = next((i for i in info if i.sku_id == sku_id and i.is_happy_hour == hh), None)
if old: if old:
old[type_] = old[type_] + quantity old[type_] = old[type_] + quantity
else: else:
item = ProductSaleReportItem( item = ProductSaleReportItem(
product_version_id=product_version_id, name=f"{'H H ' if hh else ''}{name} ({units})", is_happy_hour=hh sku_id=sku_id, name=f"{'H H ' if hh else ''}{name} ({units})", is_happy_hour=hh
) )
item[type_] = quantity item[type_] = quantity
info.append(item) info.append(item)
+1 -1
View File
@@ -60,7 +60,7 @@ def get_sale_report(
def get_sale(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]: def get_sale(start_date: date, finish_date: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]:
day = func.cast( day = func.cast(
Voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date Kot.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES), Date
).label("day") ).label("day")
product_version_onclause = _pv_onclause(day) product_version_onclause = _pv_onclause(day)
query = ( query = (
@@ -13,7 +13,7 @@ from pydantic import (
) )
from . import Daf, to_camel from . import Daf, to_camel
from .menu_category import MenuCategoryLink from .sale_category import SaleCategoryLink
class BeerConsumptionReportItem(BaseModel): class BeerConsumptionReportItem(BaseModel):
@@ -57,7 +57,7 @@ class BeerConsumptionReportItem(BaseModel):
class BeerConsumptionReport(BaseModel): class BeerConsumptionReport(BaseModel):
start_date: date start_date: date
finish_date: date finish_date: date
menu_category: MenuCategoryLink | None sale_category: SaleCategoryLink | None
regular: bool | None regular: bool | None
happy: bool | None happy: bool | None
staff: bool | None staff: bool | None
+2 -2
View File
@@ -19,7 +19,7 @@ from .user import UserLink
class ProductSaleReportItem(BaseModel): class ProductSaleReportItem(BaseModel):
product_version_id: uuid.UUID sku_id: uuid.UUID
name: str name: str
is_happy_hour: bool is_happy_hour: bool
@@ -40,7 +40,7 @@ class ProductSaleReportItem(BaseModel):
@model_serializer(mode="plain") @model_serializer(mode="plain")
def custom_dump(self) -> dict: # type: ignore def custom_dump(self) -> dict: # type: ignore
base = { base = {
"productVersionId": str(self.product_version_id), "skuId": str(self.sku_id),
"name": self.name, "name": self.name,
"isHappyHour": self.is_happy_hour, "isHappyHour": self.is_happy_hour,
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "barker" name = "barker"
version = "14.2.0" version = "14.3.0"
description = "Point of Sale for a restaurant" description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"] authors = ["tanshu <git@tanshu.com>"]
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "bookie", "name": "bookie",
"version": "14.2.0", "version": "14.3.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
+1 -1
View File
@@ -1,7 +1,7 @@
export const environment = { export const environment = {
production: true, production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '14.2.0', version: '14.3.0',
}; };
export const dateFormat = { export const dateFormat = {
@@ -32,11 +32,11 @@
<mat-datepicker #finishDate></mat-datepicker> <mat-datepicker #finishDate></mat-datepicker>
</mat-form-field> </mat-form-field>
<mat-form-field class="flex-auto"> <mat-form-field class="flex-auto">
<mat-label>Menu Category</mat-label> <mat-label>Sale Category</mat-label>
<mat-select formControlName="menuCategory" (selectionChange)="filterOn($event.value)"> <mat-select formControlName="saleCategory" (selectionChange)="filterOn($event.value)">
@for (mc of menuCategories; track mc) { @for (sc of saleCategories; track sc) {
<mat-option [value]="mc.id"> <mat-option [value]="sc.id">
{{ mc.name }} {{ sc.name }}
</mat-option> </mat-option>
} }
</mat-select> </mat-select>
@@ -12,7 +12,7 @@ import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment'; import moment from 'moment';
import { MenuCategory } from '../core/menu-category'; import { SaleCategory } from '../core/sale-category';
import { ToCsvService } from '../shared/to-csv.service'; import { ToCsvService } from '../shared/to-csv.service';
import { BeerSaleExportHeaderInterface } from './beer-sale-export-header-interface'; import { BeerSaleExportHeaderInterface } from './beer-sale-export-header-interface';
import { BeerSaleReport } from './beer-sale-report'; import { BeerSaleReport } from './beer-sale-report';
@@ -40,13 +40,13 @@ export class BeerSaleReportComponent implements OnInit {
private router = inject(Router); private router = inject(Router);
private toCsv = inject(ToCsvService); private toCsv = inject(ToCsvService);
menuCategories: MenuCategory[] = []; saleCategories: SaleCategory[] = [];
info: BeerSaleReport = new BeerSaleReport(); info: BeerSaleReport = new BeerSaleReport();
dataSource: BeerSaleReportDataSource = new BeerSaleReportDataSource(this.info.data); dataSource: BeerSaleReportDataSource = new BeerSaleReportDataSource(this.info.data);
form: FormGroup<{ form: FormGroup<{
startDate: FormControl<Date>; startDate: FormControl<Date>;
finishDate: FormControl<Date>; finishDate: FormControl<Date>;
menuCategory: FormControl<string>; saleCategory: FormControl<string>;
regular: FormControl<boolean>; regular: FormControl<boolean>;
happy: FormControl<boolean>; happy: FormControl<boolean>;
staff: FormControl<boolean>; staff: FormControl<boolean>;
@@ -61,7 +61,7 @@ export class BeerSaleReportComponent implements OnInit {
this.form = new FormGroup({ this.form = new FormGroup({
startDate: new FormControl(new Date(), { nonNullable: true }), startDate: new FormControl(new Date(), { nonNullable: true }),
finishDate: new FormControl(new Date(), { nonNullable: true }), finishDate: new FormControl(new Date(), { nonNullable: true }),
menuCategory: new FormControl('', { nonNullable: true }), saleCategory: new FormControl('', { nonNullable: true }),
regular: new FormControl<boolean>(true, { nonNullable: true }), regular: new FormControl<boolean>(true, { nonNullable: true }),
happy: new FormControl<boolean>(true, { nonNullable: true }), happy: new FormControl<boolean>(true, { nonNullable: true }),
staff: new FormControl<boolean>(true, { nonNullable: true }), staff: new FormControl<boolean>(true, { nonNullable: true }),
@@ -71,14 +71,14 @@ export class BeerSaleReportComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.route.data.subscribe((value) => { this.route.data.subscribe((value) => {
const data = value as { info: BeerSaleReport; menuCategories: MenuCategory[] }; const data = value as { info: BeerSaleReport; saleCategories: SaleCategory[] };
this.info = data.info; this.info = data.info;
this.menuCategories = data.menuCategories; this.saleCategories = data.saleCategories;
this.displayedColumns = ['date'].concat(this.info.headers); this.displayedColumns = ['date'].concat(this.info.headers);
this.form.setValue({ this.form.setValue({
startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(), startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(),
finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(), finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(),
menuCategory: this.info.menuCategory?.id ?? '', saleCategory: this.info.saleCategory?.id ?? '',
regular: this.info.regular, regular: this.info.regular,
happy: this.info.happy, happy: this.info.happy,
staff: this.info.staff, staff: this.info.staff,
@@ -94,7 +94,7 @@ export class BeerSaleReportComponent implements OnInit {
queryParams: { queryParams: {
startDate: info.startDate, startDate: info.startDate,
finishDate: info.finishDate, finishDate: info.finishDate,
menuCategory: info.menuCategory?.id ?? '', saleCategory: info.saleCategory?.id ?? '',
regular: info.regular, regular: info.regular,
happy: info.happy, happy: info.happy,
staff: info.staff, staff: info.staff,
@@ -104,8 +104,8 @@ export class BeerSaleReportComponent implements OnInit {
} }
filterOn(id: string) { filterOn(id: string) {
const mc = this.menuCategories.find((x) => x.id === id); const sc = this.saleCategories.find((x) => x.id === id);
this.info.menuCategory = mc ? mc : new MenuCategory({ id }); this.info.saleCategory = sc ? sc : new SaleCategory({ id });
} }
getInfo(): BeerSaleReport { getInfo(): BeerSaleReport {
@@ -114,7 +114,7 @@ export class BeerSaleReportComponent implements OnInit {
return new BeerSaleReport({ return new BeerSaleReport({
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
menuCategory: this.info.menuCategory, saleCategory: this.info.saleCategory,
regular: formModel.regular, regular: formModel.regular,
happy: formModel.happy, happy: formModel.happy,
staff: formModel.staff, staff: formModel.staff,
@@ -7,10 +7,10 @@ import { BeerSaleReportService } from './beer-sale-report.service';
export const beerSaleReportResolver: ResolveFn<BeerSaleReport> = (route) => { export const beerSaleReportResolver: ResolveFn<BeerSaleReport> = (route) => {
const startDate = route.queryParamMap.get('startDate') ?? null; const startDate = route.queryParamMap.get('startDate') ?? null;
const finishDate = route.queryParamMap.get('finishDate') ?? null; const finishDate = route.queryParamMap.get('finishDate') ?? null;
const menuCategory = route.queryParamMap.get('menuCategory') ?? null; const saleCategory = route.queryParamMap.get('saleCategory') ?? null;
const regular = route.queryParamMap.get('regular') !== 'false'; const regular = route.queryParamMap.get('regular') !== 'false';
const happy = route.queryParamMap.get('happy') !== 'false'; const happy = route.queryParamMap.get('happy') !== 'false';
const staff = route.queryParamMap.get('staff') !== 'false'; const staff = route.queryParamMap.get('staff') !== 'false';
const nc = route.queryParamMap.get('nc') !== 'false'; const nc = route.queryParamMap.get('nc') !== 'false';
return inject(BeerSaleReportService).get(startDate, finishDate, menuCategory, regular, happy, staff, nc); return inject(BeerSaleReportService).get(startDate, finishDate, saleCategory, regular, happy, staff, nc);
}; };
@@ -1,7 +1,7 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { authGuard } from '../auth/auth-guard.service'; import { authGuard } from '../auth/auth-guard.service';
import { menuCategoryListResolver } from '../menu-category/menu-category-list.resolver'; import { saleCategoryListResolver } from '../sale-category/sale-category-list.resolver';
import { BeerSaleReportComponent } from './beer-sale-report.component'; import { BeerSaleReportComponent } from './beer-sale-report.component';
import { beerSaleReportResolver } from './beer-sale-report.resolver'; import { beerSaleReportResolver } from './beer-sale-report.resolver';
@@ -15,7 +15,7 @@ export const routes: Routes = [
}, },
resolve: { resolve: {
info: beerSaleReportResolver, info: beerSaleReportResolver,
menuCategories: menuCategoryListResolver, saleCategories: saleCategoryListResolver,
}, },
runGuardsAndResolvers: 'always', runGuardsAndResolvers: 'always',
}, },
@@ -19,7 +19,7 @@ export class BeerSaleReportService {
get( get(
startDate: string | null, startDate: string | null,
finishDate: string | null, finishDate: string | null,
menuCategory: string | null, saleCategory: string | null,
regular: boolean, regular: boolean,
happy: boolean, happy: boolean,
staff: boolean, staff: boolean,
@@ -32,8 +32,8 @@ export class BeerSaleReportService {
if (finishDate !== null) { if (finishDate !== null) {
options.params = options.params.set('f', finishDate); options.params = options.params.set('f', finishDate);
} }
if (menuCategory != null) { if (saleCategory != null) {
options.params = options.params.set('m', menuCategory); options.params = options.params.set('sc', saleCategory);
} }
options.params = options.params.set('r', regular); options.params = options.params.set('r', regular);
options.params = options.params.set('h', happy); options.params = options.params.set('h', happy);
@@ -1,10 +1,10 @@
import { MenuCategory } from '../core/menu-category'; import { SaleCategory } from '../core/sale-category';
import { BeerSaleReportItem } from './beer-sale-report-item'; import { BeerSaleReportItem } from './beer-sale-report-item';
export class BeerSaleReport { export class BeerSaleReport {
startDate: string; startDate: string;
finishDate: string; finishDate: string;
menuCategory: MenuCategory; saleCategory: SaleCategory;
regular: boolean; regular: boolean;
happy: boolean; happy: boolean;
staff: boolean; staff: boolean;
@@ -15,7 +15,7 @@ export class BeerSaleReport {
public constructor(init?: Partial<BeerSaleReport>) { public constructor(init?: Partial<BeerSaleReport>) {
this.startDate = ''; this.startDate = '';
this.finishDate = ''; this.finishDate = '';
this.menuCategory = new MenuCategory(); this.saleCategory = new SaleCategory();
this.regular = true; this.regular = true;
this.happy = true; this.happy = true;
this.staff = true; this.staff = true;
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "frank" name = "frank"
version = "14.2.0" version = "14.3.0"
description = "Point of Sale for a restaurant" description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"] authors = ["tanshu <git@tanshu.com>"]