Mostly working with Product + Version and Sku + Version.

There will still be many errors. But this is working somewhat
This commit is contained in:
2026-01-23 02:30:57 +00:00
parent aaf64ab75a
commit 1b8ab2857f
95 changed files with 4506 additions and 2624 deletions
+30 -30
View File
@@ -7,7 +7,7 @@ import { tap } from 'rxjs/operators';
import { BillViewItem } from '../core/bill-view-item';
import { ModifierCategory } from '../core/modifier-category';
import { Product } from '../core/product';
import { StockKeepingUnit } from '../core/stock-keeping-unit';
import { ReceivePaymentItem } from '../core/receive-payment-item';
import { SaleCategory } from '../core/sale-category';
import { Table } from '../core/table';
@@ -80,10 +80,10 @@ export class BillService {
id: i.id,
kotId: k.id,
isKot: false,
productId: i.product.id,
skuId: i.sku.id,
isHappyHour: i.isHappyHour,
isPrinted: !!k.id,
info: `${i.product.name} @ ${i.price} - ${this.math.halfRoundEven(i.discount * 100, 2)}%`,
info: `${i.sku.name} @ ${i.price} - ${this.math.halfRoundEven(i.discount * 100, 2)}%`,
quantity: i.quantity,
modifiers: i.modifiers,
}),
@@ -101,22 +101,22 @@ export class BillService {
this.displayBill();
}
minimum(productId: string, happyHour: boolean): number {
minimum(skuId: string, happyHour: boolean): number {
return this.bill.kots.reduce(
(t, k) =>
k.inventories.reduce(
(a, c) => (c.product.id === productId && c.isHappyHour === happyHour ? a + c.quantity : a),
(a, c) => (c.sku.id === skuId && c.isHappyHour === happyHour ? a + c.quantity : a),
0,
) + t,
0,
);
}
addProduct(product: Product, quantity: number, discount: number): void {
addSku(sku: StockKeepingUnit, quantity: number, discount: number): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find((x) => x.product.id === product.id && x.isHappyHour === product.hasHappyHour);
const old = newKot.inventories.find((x) => x.sku.id === sku.id && x.isHappyHour === sku.hasHappyHour);
if (quantity < 0) {
const minimum = this.minimum(product.id as string, product.hasHappyHour) + quantity;
const minimum = this.minimum(sku.id as string, sku.hasHappyHour) + quantity;
if (minimum + quantity < 0) {
this.snackBar.open('Total quantity cannot be negative!', 'Error');
return;
@@ -126,17 +126,17 @@ export class BillService {
old.quantity += quantity;
} else {
const item = new Inventory({
product,
sku,
quantity,
price: product.price,
isHappyHour: product.hasHappyHour,
taxRate: product.tax.rate,
tax: product.tax,
price: sku.price,
isHappyHour: sku.hasHappyHour,
taxRate: sku.tax.rate,
tax: sku.tax,
discount,
modifiers: [],
});
newKot.inventories.push(item);
this.modifierCategoryService.listForProduct(product.id as string).subscribe((result) => {
this.modifierCategoryService.listForSku(sku.id as string).subscribe((result) => {
if (result.reduce((a: number, c: ModifierCategory) => a + c.minimum, 0)) {
this.showModifier(item);
}
@@ -155,7 +155,7 @@ export class BillService {
maxWidth: 'none',
maxHeight: 'none',
data: {
list: this.modifierCategoryService.listForProduct(item.product.id as string),
list: this.modifierCategoryService.listForSku(item.sku.id as string),
selected: Object.assign([], item.modifiers),
},
});
@@ -171,7 +171,7 @@ export class BillService {
addOne(item: BillViewItem): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find(
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
(x) => x.sku.id === item.skuId && x.isHappyHour === item.isHappyHour,
) as Inventory;
old.quantity += 1;
this.displayBill();
@@ -180,7 +180,7 @@ export class BillService {
quantity(item: BillViewItem, quantity: number): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find(
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
(x) => x.sku.id === item.skuId && x.isHappyHour === item.isHappyHour,
) as Inventory;
old.quantity = quantity;
this.displayBill();
@@ -189,9 +189,9 @@ export class BillService {
subtractOne(item: BillViewItem, canEdit: boolean): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find(
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
(x) => x.sku.id === item.skuId && x.isHappyHour === item.isHappyHour,
) as Inventory;
if (item.quantity >= 1 || (canEdit && this.minimum(item.productId as string, item.isHappyHour) >= 1)) {
if (item.quantity >= 1 || (canEdit && this.minimum(item.skuId as string, item.isHappyHour) >= 1)) {
old.quantity -= 1;
} else if (item.quantity === 0) {
newKot.inventories.splice(newKot.inventories.indexOf(old), 1);
@@ -202,7 +202,7 @@ export class BillService {
removeItem(item: BillViewItem): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find(
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
(x) => x.sku.id === item.skuId && x.isHappyHour === item.isHappyHour,
) as Inventory;
newKot.inventories.splice(newKot.inventories.indexOf(old), 1);
this.displayBill();
@@ -211,17 +211,17 @@ export class BillService {
modifier(item: BillViewItem): void {
const newKot = this.bill.kots.find((k) => k.id === undefined) as Kot;
const old = newKot.inventories.find(
(x) => x.product.id === item.productId && x.isHappyHour === item.isHappyHour,
(x) => x.sku.id === item.skuId && x.isHappyHour === item.isHappyHour,
) as Inventory;
this.showModifier(old);
}
discount(discounts: { id: string; name: string; discount: number }[]): void {
for (const kot of this.bill.kots) {
const noDiscount = kot.inventories.filter((x) => x.isHappyHour).map((x) => x.product.id as string);
const noDiscount = kot.inventories.filter((x) => x.isHappyHour).map((x) => x.sku.id as string);
for (const inventory of kot.inventories) {
const e = discounts.find((d) => d.id === (inventory.product.saleCategory as SaleCategory).id);
if (e === undefined || noDiscount.indexOf(inventory.product.id as string) !== -1) {
const e = discounts.find((d) => d.id === (inventory.sku.saleCategory as SaleCategory).id);
if (e === undefined || noDiscount.indexOf(inventory.sku.id as string) !== -1) {
continue;
}
inventory.discount = e.discount;
@@ -264,11 +264,11 @@ export class BillService {
printBill(guestBookId: string | null, voucherType: VoucherType): Observable<boolean> {
const item = JSON.parse(JSON.stringify(this.bill));
const products = item.kots.reduce(
const skus = item.kots.reduce(
(p: number, k: Kot) => p + k.inventories.filter((i) => i.quantity !== 0).length,
0,
);
if (products === 0) {
if (skus === 0) {
return throwError(() => Error('Cannot print a blank Bill\nPlease add some products!'));
}
if (!this.happyHourItemsBalanced() || this.happyHourItemsMoreThanRegular()) {
@@ -364,7 +364,7 @@ export class BillService {
const data: { move: string[]; keep: string[] } = { move: [], keep: [] };
for (const kot of this.bill.kots) {
for (const inv of kot.inventories) {
if (saleCategories.indexOf(inv.product.saleCategory?.id as string) === -1) {
if (saleCategories.indexOf(inv.sku.saleCategory?.id as string) === -1) {
data.keep.push(inv.id as string);
} else {
data.move.push(inv.id as string);
@@ -378,10 +378,10 @@ export class BillService {
for (const kot of this.bill.kots) {
const happyHourItems = kot.inventories
.filter((x) => x.isHappyHour)
.map((x) => ({ id: x.product.id as string, quantity: x.quantity }));
.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.product.id === item.id && x.quantity === item.quantity,
(x) => !x.isHappyHour && x.sku.id === item.id && x.quantity === item.quantity,
);
if (q === undefined) {
return false;
@@ -397,7 +397,7 @@ export class BillService {
const invs: Record<string, { normal: number; happy: number }> = {};
for (const kot of this.bill.kots) {
for (const inventory of kot.inventories) {
const pid = inventory.product.id as string;
const pid = inventory.sku.id as string;
if (invs[pid] === undefined) {
invs[pid] = { normal: 0, happy: 0 };
}
@@ -1,13 +1,13 @@
export class BillSelectionItem {
kotId?: string;
inventoryId?: string;
productId: string;
skuId: string;
isHappyHour: boolean;
public constructor(init?: Partial<BillSelectionItem>) {
this.kotId = undefined;
this.inventoryId = undefined;
this.productId = '';
this.skuId = '';
this.isHappyHour = false;
Object.assign(this, init);
}
@@ -1,6 +1,6 @@
<h2>Bill</h2>
<table mat-table #table [dataSource]="dataSource" class="mat-elevation-z8" [trackBy]="trackByKotProductId">
<table mat-table #table [dataSource]="dataSource" class="mat-elevation-z8" [trackBy]="trackByKotskuId">
<ng-container matColumnDef="bill-no-title">
<mat-header-cell *matHeaderCellDef class="deep-purple-200 bold">Bill / KOT number</mat-header-cell>
</ng-container>
@@ -112,7 +112,7 @@ export class BillsComponent implements OnInit {
new BillSelectionItem({
kotId: kot.id,
inventoryId: c.id,
productId: c.product.id,
skuId: c.sku.id,
isHappyHour: c.isHappyHour,
}),
),
@@ -126,7 +126,7 @@ export class BillsComponent implements OnInit {
new BillSelectionItem({
kotId: invView.kotId,
inventoryId: invView.id,
productId: invView.productId,
skuId: invView.skuId,
isHappyHour: invView.isHappyHour,
}),
);
@@ -138,7 +138,7 @@ export class BillsComponent implements OnInit {
new BillSelectionItem({
kotId: invView.kotId,
inventoryId: invView.id,
productId: invView.productId,
skuId: invView.skuId,
isHappyHour: invView.isHappyHour,
}),
);
@@ -154,7 +154,7 @@ export class BillsComponent implements OnInit {
new BillSelectionItem({
kotId: kot.id,
inventoryId: item.id,
productId: item.product.id,
skuId: item.sku.id,
isHappyHour: item.isHappyHour,
}),
);
@@ -174,7 +174,7 @@ export class BillsComponent implements OnInit {
new BillSelectionItem({
kotId: kot.id,
inventoryId: item.id,
productId: item.product.id,
skuId: item.sku.id,
isHappyHour: item.isHappyHour,
}),
);
@@ -186,11 +186,11 @@ export class BillsComponent implements OnInit {
}
}
trackByKotProductId(index: number, row: BillViewItem): string {
trackByKotskuId(index: number, row: BillViewItem): string {
// Fallbacks in case fields are undefined/null
const kotId = row.kotId ?? '';
const productId = row.productId ?? '';
return `${kotId}_${productId}`;
const skuId = row.skuId ?? '';
return `${kotId}_${skuId}`;
}
addOne(item: BillViewItem): void {
+3 -3
View File
@@ -1,10 +1,10 @@
import { Modifier } from '../../core/modifier';
import { Product } from '../../core/product';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { Tax } from '../../core/tax';
export class Inventory {
id: string | undefined;
product: Product;
sku: Product;
quantity: number;
price: number;
isHappyHour: boolean;
@@ -16,7 +16,7 @@ export class Inventory {
public constructor(init?: Partial<Inventory>) {
this.id = undefined;
this.product = new Product();
this.sku = new Product();
this.quantity = 0;
this.price = 0;
this.isHappyHour = false;
+41
View File
@@ -0,0 +1,41 @@
import { MenuCategory } from './menu-category';
import { SaleCategory } from './sale-category';
import { Tax } from './tax';
export class Product {
id: string | undefined;
versionId?: string;
name: string;
units: string;
menuCategory?: MenuCategory;
saleCategory?: SaleCategory;
price: number;
hasHappyHour: boolean;
isNotAvailable: boolean;
quantity: number;
isActive: boolean;
sortOrder: number;
enabled: boolean;
tax: Tax;
validFrom: string | null;
validTill: string | null;
public constructor(init?: Partial<Product>) {
this.id = undefined;
this.name = '';
this.units = '';
this.price = 0;
this.hasHappyHour = false;
this.isNotAvailable = false;
this.quantity = 0;
this.isActive = true;
this.sortOrder = 0;
this.enabled = true;
this.validFrom = null;
this.validTill = null;
this.tax = new Tax();
Object.assign(this, init);
}
}
@@ -11,7 +11,7 @@
<mat-card
class="flex-col square-button"
matRipple
(click)="addProduct(item)"
(click)="addSku(item)"
[class.accent]="item.hasHappyHour"
[class.disabled]="item.isNotAvailable"
[class.primary]="!item.hasHappyHour && !item.isNotAvailable"
@@ -4,7 +4,7 @@ import { MatCardModule } from '@angular/material/card';
import { MatRippleModule } from '@angular/material/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { Product } from '../../core/product';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { BillService } from '../bill.service';
@Component({
@@ -26,10 +26,10 @@ export class ProductsComponent implements OnInit {
});
}
addProduct(product: Product): void {
addSku(product: Product): void {
if (product.isNotAvailable) {
return;
}
this.bs.addProduct(product, 1, 0);
this.bs.addSku(product, 1, 0);
}
}
@@ -1,7 +1,7 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { Product } from '../../core/product';
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
import { ProductService } from '../../product/product.service';
export const productsResolver: ResolveFn<Product[]> = (route) => {