Modifier Category Working

This commit is contained in:
2026-01-24 03:26:03 +00:00
parent e4072d0872
commit 4f0346551a
24 changed files with 231 additions and 215 deletions
+107 -24
View File
@@ -14,6 +14,8 @@ from ..models.menu_category import MenuCategory
from ..models.modifier_category import ModifierCategory
from ..models.product import Product
from ..models.product_version import ProductVersion
from ..models.sku_version import SkuVersion
from ..models.stock_keeping_unit import StockKeepingUnit
from ..schemas import modifier_category as schemas
from ..schemas.menu_category import MenuCategoryLink
from ..schemas.modifier_category_for_product import ModifierCategoryForProduct, ModifierLink
@@ -41,7 +43,7 @@ def save(
is_active=data.is_active,
)
db.add(item)
add_products(item, data.menu_categories, db)
add_skus(item, data.menu_categories, db)
db.commit()
return modifier_category_info(item, date_, db=db)
except SQLAlchemyError as e:
@@ -65,7 +67,7 @@ def update_route(
item.minimum = data.minimum
item.maximum = None if data.maximum == 0 else data.maximum
item.is_active = data.is_active
add_products(item, data.menu_categories, db)
add_skus(item, data.menu_categories, db)
db.commit()
return modifier_category_info(item, date_, db=db)
except SQLAlchemyError as e:
@@ -109,7 +111,7 @@ def show_list(
) -> list[schemas.ModifierCategory]:
with SessionFuture() as db:
product_version_onclause = and_(
ProductVersion.menu_category_id == MenuCategory.id,
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
@@ -119,6 +121,22 @@ def show_list(
ProductVersion.valid_till >= date_,
),
)
sku_version_onclause = and_(
SkuVersion.sku_id == StockKeepingUnit.id,
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= date_), # noqa: E711
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= date_), # noqa: E711
)
sku_version_menu_category_onclause = and_(
SkuVersion.menu_category_id == MenuCategory.id,
or_(
SkuVersion.valid_from == None, # noqa: E711
SkuVersion.valid_from <= date_,
),
or_(
SkuVersion.valid_till == None, # noqa: E711
SkuVersion.valid_till >= date_,
),
)
list_ = (
db.execute(select(ModifierCategory).order_by(ModifierCategory.sort_order).order_by(ModifierCategory.name))
.scalars()
@@ -127,9 +145,17 @@ def show_list(
menu_categories = (
db.execute(
select(MenuCategory)
.join(SkuVersion, onclause=sku_version_menu_category_onclause)
.join(StockKeepingUnit, onclause=sku_version_onclause)
.join(StockKeepingUnit.product)
.join(ProductVersion, onclause=product_version_onclause)
.order_by(MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name)
.options(contains_eager(MenuCategory.products))
.order_by(MenuCategory.sort_order, StockKeepingUnit.sort_order, ProductVersion.name)
.options(
contains_eager(MenuCategory.skus)
.contains_eager(SkuVersion.sku)
.contains_eager(StockKeepingUnit.product)
.contains_eager(Product.versions)
)
)
.unique()
.scalars()
@@ -149,13 +175,17 @@ def show_list(
schemas.MenuCategoryLink(
id_=mc.id,
name=mc.name,
enabled=reduce(lambda x, y: x and (y.product_id in products), mc.products, True),
products=[ProductLink(id_=p.id, name=p.name) for p in mc.products if p.product_id in products],
enabled=reduce(lambda x, y: x and (y.sku.product_id in products), mc.skus, True),
skus=[
ProductLink(id_=p.sku.product_id, name=f"{p.sku.product.versions[0].name} ({p.units})")
for p in mc.skus
if p.sku.product_id in products
],
)
for mc in menu_categories
],
)
modifier_category.menu_categories = [i for i in modifier_category.menu_categories if len(i.products) > 0]
modifier_category.menu_categories = [i for i in modifier_category.menu_categories if len(i.skus) > 0]
modifier_categories.append(modifier_category)
return modifier_categories
@@ -198,7 +228,7 @@ def show_id(
def modifier_category_info(item: ModifierCategory, date_: date, db: Session) -> schemas.ModifierCategory:
product_version_onclause = and_(
ProductVersion.menu_category_id == MenuCategory.id,
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
@@ -208,18 +238,42 @@ def modifier_category_info(item: ModifierCategory, date_: date, db: Session) ->
ProductVersion.valid_till >= date_,
),
)
sku_version_onclause = and_(
SkuVersion.sku_id == StockKeepingUnit.id,
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= date_), # noqa: E711
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= date_), # noqa: E711
)
sku_version_menu_category_onclause = and_(
SkuVersion.menu_category_id == MenuCategory.id,
or_(
SkuVersion.valid_from == None, # noqa: E711
SkuVersion.valid_from <= date_,
),
or_(
SkuVersion.valid_till == None, # noqa: E711
SkuVersion.valid_till >= date_,
),
)
menu_categories = (
db.execute(
select(MenuCategory)
.join(SkuVersion, onclause=sku_version_menu_category_onclause)
.join(StockKeepingUnit, onclause=sku_version_onclause)
.join(StockKeepingUnit.product)
.join(ProductVersion, onclause=product_version_onclause)
.order_by(MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name)
.options(contains_eager(MenuCategory.products))
.order_by(MenuCategory.sort_order, StockKeepingUnit.sort_order, ProductVersion.name)
.options(
contains_eager(MenuCategory.skus)
.contains_eager(SkuVersion.sku)
.contains_eager(StockKeepingUnit.product)
.contains_eager(Product.versions)
)
)
.unique()
.scalars()
.all()
)
products = [p.id for p in item.products]
products = [x.id for x in item.products]
return schemas.ModifierCategory(
id_=item.id,
name=item.name,
@@ -230,13 +284,13 @@ def modifier_category_info(item: ModifierCategory, date_: date, db: Session) ->
schemas.MenuCategoryLink(
id_=mc.id,
name=mc.name,
products=[
ProductLinkSchema(
id_=p.product_id,
name=p.name,
enabled=p.product_id in products,
skus=[
ProductLink(
id_=p.sku.product_id,
name=f"{p.sku.product.versions[0].name} ({p.units})",
enabled=p.sku.product_id in products,
)
for p in mc.products
for p in mc.skus
],
)
for mc in menu_categories
@@ -247,7 +301,7 @@ def modifier_category_info(item: ModifierCategory, date_: date, db: Session) ->
def modifier_category_blank(date_: date, db: Session) -> schemas.ModifierCategoryBlank:
product_version_onclause = and_(
ProductVersion.menu_category_id == MenuCategory.id,
ProductVersion.product_id == Product.id,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
@@ -257,12 +311,36 @@ def modifier_category_blank(date_: date, db: Session) -> schemas.ModifierCategor
ProductVersion.valid_till >= date_,
),
)
sku_version_onclause = and_(
SkuVersion.sku_id == StockKeepingUnit.id,
or_(SkuVersion.valid_from == None, SkuVersion.valid_from <= date_), # noqa: E711
or_(SkuVersion.valid_till == None, SkuVersion.valid_till >= date_), # noqa: E711
)
sku_version_menu_category_onclause = and_(
SkuVersion.menu_category_id == MenuCategory.id,
or_(
SkuVersion.valid_from == None, # noqa: E711
SkuVersion.valid_from <= date_,
),
or_(
SkuVersion.valid_till == None, # noqa: E711
SkuVersion.valid_till >= date_,
),
)
menu_categories = (
db.execute(
select(MenuCategory)
.join(SkuVersion, onclause=sku_version_menu_category_onclause)
.join(StockKeepingUnit, onclause=sku_version_onclause)
.join(StockKeepingUnit.product)
.join(ProductVersion, onclause=product_version_onclause)
.order_by(MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name)
.options(contains_eager(MenuCategory.products))
.order_by(MenuCategory.sort_order, StockKeepingUnit.sort_order, ProductVersion.name)
.options(
contains_eager(MenuCategory.skus)
.contains_eager(SkuVersion.sku)
.contains_eager(StockKeepingUnit.product)
.contains_eager(Product.versions)
)
)
.unique()
.scalars()
@@ -277,16 +355,21 @@ def modifier_category_blank(date_: date, db: Session) -> schemas.ModifierCategor
schemas.MenuCategoryLink(
id_=mc.id,
name=mc.name,
products=[ProductLinkSchema(id_=p.product_id, name=p.name, enabled=False) for p in mc.products],
skus=[
ProductLinkSchema(
id_=p.sku.product_id, name=f"{p.sku.product.versions[0].name} ({p.units})", enabled=False
)
for p in mc.skus
],
)
for mc in menu_categories
],
)
def add_products(modifier_category: ModifierCategory, menu_categories: list[MenuCategoryLink], db: Session) -> None:
def add_skus(modifier_category: ModifierCategory, menu_categories: list[MenuCategoryLink], db: Session) -> None:
for mc in menu_categories:
for p in mc.products:
for p in mc.skus:
old = next((x for x in modifier_category.products if x.id == p.id_), None)
if p.enabled and old is None:
product_object = db.execute(select(Product).where(Product.id == p.id_)).scalar_one()
+1 -1
View File
@@ -28,5 +28,5 @@ class MenuCategoryLink(BaseModel):
id_: uuid.UUID = Field(...)
name: str | None = None
enabled: bool | None = None
products: list[ProductLink]
skus: list[ProductLink]
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
+3 -3
View File
@@ -1,4 +1,4 @@
import { StockKeepingUnit as Product } from './stock-keeping-unit';
import { StockKeepingUnit } from './product';
export class MenuCategory {
id: string | undefined;
@@ -6,7 +6,7 @@ export class MenuCategory {
isActive: boolean;
isFixture: boolean;
sortOrder: number;
products: Product[];
skus: StockKeepingUnit[];
enabled?: boolean;
public constructor(init?: Partial<MenuCategory>) {
@@ -15,7 +15,7 @@ export class MenuCategory {
this.isActive = true;
this.isFixture = false;
this.sortOrder = 0;
this.products = [];
this.skus = [];
Object.assign(this, init);
}
}
+27 -1
View File
@@ -1,6 +1,32 @@
import { MenuCategory } from './menu-category';
import { Modifier } from './modifier';
export class StockKeepingUnitLink {
id: string | undefined;
name: string;
enabled: boolean;
public constructor(init?: Partial<StockKeepingUnitLink>) {
this.name = '';
this.enabled = true;
Object.assign(this, init);
}
}
export class MenuCategory {
id: string | undefined;
name: string;
skus: StockKeepingUnitLink[];
enabled?: boolean;
public constructor(init?: Partial<MenuCategory>) {
this.id = undefined;
this.name = '';
this.enabled = true;
this.skus = [];
Object.assign(this, init);
}
}
export class ModifierCategory {
id: string | undefined;
name: string;
@@ -12,9 +12,7 @@ import { MatTreeModule, MatTreeNestedDataSource } from '@angular/material/tree';
import { ActivatedRoute, Router } from '@angular/router';
import { map } from 'rxjs/operators';
import { MenuCategory } from '../../core/menu-category';
import { ModifierCategory } from '../../core/modifier-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { ModifierCategory, MenuCategory, StockKeepingUnitLink } from '../../core/modifier-category';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { ModifierCategoryService } from '../modifier-category.service';
import { NodeItem } from '../node-item';
@@ -35,7 +33,6 @@ interface ModifierCategoryNode {
styleUrls: ['./modifier-category-detail.component.css'],
imports: [
MatButtonModule,
MatCheckboxModule,
MatDividerModule,
MatFormFieldModule,
@@ -65,8 +62,8 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
item: ModifierCategory = new ModifierCategory();
dataSource = new MatTreeNestedDataSource<NodeItem>();
products: Map<string, Product> = new Map<string, Product>();
productsOfMenuCategory: Map<string, Product[]> = new Map<string, Product[]>();
products: Map<string, StockKeepingUnitLink> = new Map<string, StockKeepingUnitLink>();
productsOfMenuCategory: Map<string, StockKeepingUnitLink[]> = new Map<string, StockKeepingUnitLink[]>();
constructor() {
// Create form
@@ -87,7 +84,7 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
const tree: NodeItem[] = data.item.menuCategories.map((x: MenuCategory) => ({
id: x.id as string,
name: x.name,
children: x.products.map((y: Product) => ({ id: y.id as string, name: y.name })),
children: x.skus.map((y: StockKeepingUnitLink) => ({ id: y.id as string, name: y.name })),
}));
return { item: data.item, tree };
}),
@@ -109,12 +106,12 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
this.products = new Map();
this.productsOfMenuCategory = new Map();
this.item.menuCategories.forEach((mc: MenuCategory) => {
mc.products.forEach((p: Product) => {
mc.skus.forEach((p: StockKeepingUnitLink) => {
this.products.set(p.id as string, p);
if (!this.productsOfMenuCategory.has(mc.id as string)) {
this.productsOfMenuCategory.set(mc.id as string, []);
}
(this.productsOfMenuCategory.get(mc.id as string) as Product[]).push(p);
(this.productsOfMenuCategory.get(mc.id as string) as StockKeepingUnitLink[]).push(p);
});
});
// this.setMenuCategories();
@@ -180,19 +177,19 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
}
isProductSelected(node: NodeItem) {
return (this.products.get(node.id) as Product).enabled;
return (this.products.get(node.id) as StockKeepingUnitLink).enabled;
}
isMenuCategorySelected(node: NodeItem) {
return (this.productsOfMenuCategory.get(node.id) as Product[]).reduce(
(acc: boolean, current: Product) => acc && current.enabled,
return (this.productsOfMenuCategory.get(node.id) as StockKeepingUnitLink[]).reduce(
(acc: boolean, current: StockKeepingUnitLink) => acc && current.enabled,
true,
);
}
isMenuCategoryPartiallySelected(node: NodeItem) {
const total = (this.productsOfMenuCategory.get(node.id) as Product[]).length;
const ticked = (this.productsOfMenuCategory.get(node.id) as Product[]).reduce((acc, current) => {
const total = (this.productsOfMenuCategory.get(node.id) as StockKeepingUnitLink[]).length;
const ticked = (this.productsOfMenuCategory.get(node.id) as StockKeepingUnitLink[]).reduce((acc, current) => {
if (current.enabled) {
acc += 1;
}
@@ -202,12 +199,13 @@ export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
}
toggleProductSelection(node: NodeItem) {
(this.products.get(node.id) as Product).enabled = !(this.products.get(node.id) as Product).enabled;
(this.products.get(node.id) as StockKeepingUnitLink).enabled = !(this.products.get(node.id) as StockKeepingUnitLink)
.enabled;
}
toggleMenuCategorySelection(node: NodeItem) {
const sel = !this.isMenuCategorySelected(node);
(this.productsOfMenuCategory.get(node.id as string) as Product[]).forEach((p) => {
(this.productsOfMenuCategory.get(node.id as string) as StockKeepingUnitLink[]).forEach((p) => {
p.enabled = sel;
});
return sel;
@@ -47,7 +47,7 @@
{{ mc.name }}
@if (!mc.enabled) {
<ul>
@for (p of mc.products; track p) {
@for (p of mc.skus; track p) {
<li>{{ p.name }}</li>
}
</ul>
@@ -63,13 +63,13 @@
</mat-select>
</mat-form-field>
<mat-checkbox formControlName="hasHappyHour" class="flex-auto">Has Happy Hour?</mat-checkbox>
<mat-checkbox formControlName="isNotAvailable" class="flex-auto">Not Available?</mat-checkbox>
<mat-checkbox formControlName="hasHappyHour" class="flex-auto">Has Happy Hour?</mat-checkbox>
<mat-checkbox formControlName="isNotAvailable" class="flex-auto">Not Available?</mat-checkbox>
<button mat-raised-button color="primary" (click)="addRow()" class="flex-auto">Add</button>
</div></form>
<!-- SKU table -->
<button mat-raised-button color="primary" (click)="addRow()" class="flex-auto">Add</button>
</div>
</form>
<!-- SKU table -->
<div class="row-container wrapped">
<mat-table [dataSource]="dataSource" aria-label="Stock Keeping Units" class="flex-auto">
<!-- Units Column -->
@@ -1,3 +1,4 @@
import { CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
@@ -5,21 +6,20 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core';
import { MatDialog } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { MenuCategory } from '../../core/menu-category';
import { Product, StockKeepingUnit } from '../../core/product';
import { SaleCategory } from '../../core/sale-category';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { ProductService } from '../product.service';
import { BehaviorSubject } from 'rxjs';
import { ProductDetailDatasource } from './product-detail-datasource';
import { MatTableModule } from '@angular/material/table';
import { CurrencyPipe } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'app-product-detail',
@@ -46,7 +46,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
private ser = inject(ProductService);
@ViewChild('name', { static: true }) nameElement?: ElementRef;
form: FormGroup<{
form: FormGroup<{
// Product fields
name: FormControl<string>;
fractionUnits: FormControl<string>;
@@ -66,18 +66,27 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
}>;
}>;
menuCategories: MenuCategory[] = [];
saleCategories: SaleCategory[] = [];
public skus = new BehaviorSubject<StockKeepingUnit[]>([]);
dataSource: ProductDetailDatasource = new ProductDetailDatasource(this.skus);
item: Product = new Product();
displayedColumns = ['units', 'fraction', 'productYield', 'costPrice', 'salePrice', 'menuCategory', 'hasHappyHour', 'isNotAvailable', 'action'];
displayedColumns = [
'units',
'fraction',
'productYield',
'costPrice',
'salePrice',
'menuCategory',
'hasHappyHour',
'isNotAvailable',
'action',
];
constructor() {
// Create form
this.form = new FormGroup({
this.form = new FormGroup({
name: new FormControl<string>('', { nonNullable: true }),
fractionUnits: new FormControl<string>('', { nonNullable: true }),
saleCategory: new FormControl<string>('', { nonNullable: true }),
@@ -174,13 +183,16 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.snackBar.open('Menu Category is required', 'Error');
return;
}
const sku = new StockKeepingUnit({
const sku = new StockKeepingUnit({
units,
fraction,
productYield,
costPrice,
salePrice,
menuCategory: new MenuCategory({ id: menuCategoryId, name: this.menuCategories.find((x) => x.id === menuCategoryId)?.name ?? '' }),
menuCategory: new MenuCategory({
id: menuCategoryId,
name: this.menuCategories.find((x) => x.id === menuCategoryId)?.name ?? '',
}),
hasHappyHour: formValue.hasHappyHour ?? false,
isNotAvailable: formValue.isNotAvailable ?? false,
});
@@ -203,7 +215,6 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
// isPurchased: this.item.isPurchased,
// },
// });
// dialogRef.afterClosed().subscribe((result: boolean | StockKeepingUnit) => {
// if (!result) {
// return;
@@ -280,14 +291,12 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
}
}
// if (this.item.menuCategory === null || this.item.menuCategory === undefined) {
// this.item.menuCategory = new MenuCategory();
// }
// this.item.menuCategory.id = formModel.menuCategory;
// if (this.item.menuCategory === null || this.item.menuCategory === undefined) {
// this.item.menuCategory = new MenuCategory();
// }
// this.item.menuCategory.id = formModel.menuCategory;
// this.item.price = formModel.price ?? 0;
// this.item.hasHappyHour = formModel.hasHappyHour ?? false;
// this.item.isNotAvailable = formModel.isNotAvailable ?? false;
// this.item.quantity = formModel.quantity ?? 0;
// this.item.price = formModel.price ?? 0;
// this.item.hasHappyHour = formModel.hasHappyHour ?? false;
// this.item.isNotAvailable = formModel.isNotAvailable ?? false;
// this.item.quantity = formModel.quantity ?? 0;
@@ -1,7 +1,7 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
import { Product } from '../core/product';
import { ProductService } from './product.service';
export const productListResolver: ResolveFn<Product[]> = () => {
@@ -2,7 +2,6 @@ import { DataSource } from '@angular/cdk/collections';
import { merge, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { MenuCategory } from '../../core/menu-category';
import { Product } from '../../core/product';
export class ProductListDataSource extends DataSource<Product> {
@@ -50,8 +50,7 @@
<ul>
@for (sku of row.skus; track sku) {
<li>
<a [routerLink]="['/products', row.id]"
>{{ row.name }} ({{ sku.units }})</a>
<a [routerLink]="['/products', row.id]">{{ row.name }} ({{ sku.units }})</a>
</li>
}
</ul>
@@ -99,19 +98,19 @@
<ul>
@for (sku of row.skus; track sku) {
<li layout="row">
<div>
<mat-icon>
{{ sku.hasHappyHour ? 'sentiment_satisfied_alt' : 'sentiment_dissatisfied' }}
</mat-icon>
<b> {{ sku.hasHappyHour ? 'Happy Hour' : 'Regular' }}</b>
</div>
<div>
<mat-icon>
{{ row.isNotAvailable ? 'pause' : 'play_arrow' }}
</mat-icon>
<b> {{ row.isNotAvailable ? 'Not Available' : 'Available' }}</b>
</div>
</li>
<div>
<mat-icon>
{{ sku.hasHappyHour ? 'sentiment_satisfied_alt' : 'sentiment_dissatisfied' }}
</mat-icon>
<b> {{ sku.hasHappyHour ? 'Happy Hour' : 'Regular' }}</b>
</div>
<div>
<mat-icon>
{{ row.isNotAvailable ? 'pause' : 'play_arrow' }}
</mat-icon>
<b> {{ row.isNotAvailable ? 'Not Available' : 'Available' }}</b>
</div>
</li>
}
</ul>
</mat-cell>
@@ -121,15 +120,11 @@
<ng-container matColumnDef="quantity">
<mat-header-cell *matHeaderCellDef class="right">Quantity</mat-header-cell>
<mat-cell *matCellDef="let row" class="right">
<ul>
<ul>
@for (sku of row.skus; track sku) {
<li>
{{ sku.fraction | number: '1.2-2' }}{{ row.fractionUnits }}
</li>
<li>{{ sku.fraction | number: '1.2-2' }}{{ row.fractionUnits }}</li>
}
</ul>
</mat-cell>
</ng-container>
@@ -1,4 +1,4 @@
import { CdkDragDrop, moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop';
import { AsyncPipe, DecimalPipe, CurrencyPipe } from '@angular/common';
import { Component, OnInit, inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
@@ -1,85 +0,0 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
const url = '/api/products';
const serviceName = 'ProductService';
@Injectable({ providedIn: 'root' })
export class ProductService {
private http = inject(HttpClient);
private log = inject(ErrorLoggerService);
get(id: string | null): Observable<Product> {
const getUrl: string = id === null ? `${url}` : `${url}/${id}`;
return this.http
.get<Product>(getUrl)
.pipe(catchError(this.log.handleError(serviceName, `get id=${id}`))) as Observable<Product>;
}
list(): Observable<Product[]> {
return this.http
.get<Product[]>(`${url}/list`)
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Product[]>;
}
listOfSaleCategory(id: string): Observable<Product[]> {
const options = { params: new HttpParams().set('sc', id) };
return this.http
.get<Product[]>(`${url}/query`, options)
.pipe(catchError(this.log.handleError(serviceName, 'listOfSaleCategory'))) as Observable<Product[]>;
}
listIsActiveOfCategory(id: string): Observable<Product[]> {
const options = { params: new HttpParams().set('mc', id) };
return this.http
.get<Product[]>(`${url}/query`, options)
.pipe(catchError(this.log.handleError(serviceName, 'listIsActiveOfCategory'))) as Observable<Product[]>;
}
save(product: Product): Observable<void> {
return this.http
.post<Product>(`${url}`, product, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'save'))) as Observable<void>;
}
update(product: Product): Observable<void> {
return this.http
.put<Product>(`${url}/${product.id}`, product, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<void>;
}
updateSortOrder(list: Product[]): Observable<Product[]> {
return this.http
.post<Product[]>(`${url}/list`, list, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'updateSortOrder'))) as Observable<Product[]>;
}
saveOrUpdate(product: Product): Observable<void> {
if (!product.versionId) {
return this.save(product);
}
return this.update(product);
}
delete(id: string): Observable<void> {
return this.http
.delete<Product>(`${url}/${id}`, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable<void>;
}
balance(id: string, date: string): Observable<number> {
const options = { params: new HttpParams().set('d', date) };
return this.http
.get<number>(`${url}/balance`, options)
.pipe(catchError(this.log.handleError(serviceName, 'balance'))) as Observable<number>;
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
import { Product } from '../core/product';
import { ProductService } from './product.service';
export const productResolver: ResolveFn<Product> = (route) => {
+9 -10
View File
@@ -4,8 +4,7 @@ import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
import { Product as ProductNew } from '../core/product';
import { Product } from '../core/product';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
@@ -46,25 +45,25 @@ export class ProductService {
.pipe(catchError(this.log.handleError(serviceName, 'listIsActiveOfCategory'))) as Observable<Product[]>;
}
save(product: ProductNew): Observable<void> {
save(product: Product): Observable<void> {
return this.http
.post<ProductNew>(`${url}`, product, httpOptions)
.post<Product>(`${url}`, product, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'save'))) as Observable<void>;
}
update(product: ProductNew): Observable<void> {
update(product: Product): Observable<void> {
return this.http
.put<ProductNew>(`${url}/${product.id}`, product, httpOptions)
.put<Product>(`${url}/${product.id}`, product, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<void>;
}
updateSortOrder(list: ProductNew[]): Observable<ProductNew[]> {
updateSortOrder(list: Product[]): Observable<Product[]> {
return this.http
.post<ProductNew[]>(`${url}/list`, list, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'updateSortOrder'))) as Observable<ProductNew[]>;
.post<Product[]>(`${url}/list`, list, httpOptions)
.pipe(catchError(this.log.handleError(serviceName, 'updateSortOrder'))) as Observable<Product[]>;
}
saveOrUpdate(product: ProductNew): Observable<void> {
saveOrUpdate(product: Product): Observable<void> {
if (!product.versionId) {
return this.save(product);
}
@@ -2,7 +2,7 @@ import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { of as observableOf } from 'rxjs';
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
import { Product } from '../core/product';
import { ProductService } from '../product/product.service';
export const productListResolver: ResolveFn<Product[]> = (route) => {
@@ -14,8 +14,8 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { round } from 'mathjs';
import { BehaviorSubject } from 'rxjs';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { SaleCategory } from '../../core/sale-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { Tax } from '../../core/tax';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { SaleCategoryService } from '../sale-category.service';
+4 -12
View File
@@ -7,9 +7,9 @@ import { tap } from 'rxjs/operators';
import { BillViewItem } from '../core/bill-view-item';
import { ModifierCategory } from '../core/modifier-category';
import { StockKeepingUnit } from '../core/stock-keeping-unit';
import { ReceivePaymentItem } from '../core/receive-payment-item';
import { SaleCategory } from '../core/sale-category';
import { StockKeepingUnit } from '../core/stock-keeping-unit';
import { Table } from '../core/table';
import { ModifierCategoryService } from '../modifier-categories/modifier-category.service';
import { MathService } from '../shared/math.service';
@@ -104,10 +104,7 @@ export class BillService {
minimum(skuId: string, happyHour: boolean): number {
return this.bill.kots.reduce(
(t, k) =>
k.inventories.reduce(
(a, c) => (c.sku.id === skuId && c.isHappyHour === happyHour ? a + c.quantity : a),
0,
) + t,
k.inventories.reduce((a, c) => (c.sku.id === skuId && c.isHappyHour === happyHour ? a + c.quantity : a), 0) + t,
0,
);
}
@@ -264,10 +261,7 @@ export class BillService {
printBill(guestBookId: string | null, voucherType: VoucherType): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
const skus = item.kots.reduce(
(p: number, k: Kot) => p + k.inventories.filter((i) => i.quantity !== 0).length,
0,
);
const skus = item.kots.reduce((p: number, k: Kot) => p + k.inventories.filter((i) => i.quantity !== 0).length, 0);
if (skus === 0) {
return throwError(() => Error('Cannot print a blank Bill\nPlease add some products!'));
}
@@ -380,9 +374,7 @@ export class BillService {
.filter((x) => x.isHappyHour)
.map((x) => ({ id: x.sku.id as string, quantity: x.quantity }));
for (const item of happyHourItems) {
const q = kot.inventories.find(
(x) => !x.isHappyHour && x.sku.id === item.id && x.quantity === item.quantity,
);
const q = kot.inventories.find((x) => !x.isHappyHour && x.sku.id === item.id && x.quantity === item.quantity);
if (q === undefined) {
return false;
}
+3 -3
View File
@@ -1,6 +1,6 @@
import { MenuCategory } from './menu-category';
import { SaleCategory } from './sale-category';
import { Tax } from './tax';
import { MenuCategory } from '../core/menu-category';
import { SaleCategory } from '../core/sale-category';
import { Tax } from '../core/tax';
export class Product {
id: string | undefined;
@@ -4,8 +4,8 @@ import { MatCardModule } from '@angular/material/card';
import { MatRippleModule } from '@angular/material/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { BillService } from '../bill.service';
import { Product } from '../product';
@Component({
selector: 'app-products',
@@ -1,7 +1,7 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { Product } from '../../core/product';
import { ProductService } from '../../product/product.service';
export const productsResolver: ResolveFn<Product[]> = (route) => {
@@ -13,8 +13,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment';
import { MenuCategory } from '../../core/menu-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { SaleCategory } from '../../core/sale-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { TemporalProductService } from '../temporal-product.service';
@@ -3,8 +3,8 @@ import { merge, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { MenuCategory } from '../../core/menu-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { SaleCategory } from '../../core/sale-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
export class TemporalProductListDatasource extends DataSource<Product> {
public data: Product[][];
@@ -12,8 +12,8 @@ import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { MenuCategory } from '../../core/menu-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { SaleCategory } from '../../core/sale-category';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { TemporalProductListDatasource } from './temporal-product-list-datasource';
@Component({