Temporal Product Fixed.
This commit is contained in:
@ -0,0 +1,30 @@
|
||||
.two-col {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.backend-actions {
|
||||
margin-top: 16px;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@ -1,73 +1,190 @@
|
||||
<h2>Product</h2>
|
||||
<form [formGroup]="form" class="flex-col">
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Product ID</mat-label>
|
||||
<input matInput formControlName="id" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-3-4">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput #name formControlName="name" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1-4">
|
||||
<mat-label>Units</mat-label>
|
||||
<input matInput formControlName="units" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Price</mat-label>
|
||||
<input matInput type="number" formControlName="price" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Quantity</mat-label>
|
||||
<input matInput type="number" formControlName="quantity" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-checkbox formControlName="hasHappyHour">Has Happy Hour?</mat-checkbox>
|
||||
<mat-checkbox formControlName="isNotAvailable">Is Not Available?</mat-checkbox>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Menu Category</mat-label>
|
||||
<mat-select formControlName="menuCategory">
|
||||
@for (mc of menuCategories; track mc) {
|
||||
<mat-option [value]="mc.id">
|
||||
{{ mc.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Sale Category</mat-label>
|
||||
<mat-select formControlName="saleCategory">
|
||||
@for (sc of saleCategories; track sc) {
|
||||
<mat-option [value]="sc.id">
|
||||
{{ sc.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid From</mat-label>
|
||||
<input matInput [matDatepicker]="validFrom" formControlName="validFrom" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="validFrom"></mat-datepicker-toggle>
|
||||
<mat-datepicker #validFrom></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid Till</mat-label>
|
||||
<input matInput [matDatepicker]="validTill" formControlName="validTill" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="validTill"></mat-datepicker-toggle>
|
||||
<mat-datepicker #validTill></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h2>Product Versions</h2>
|
||||
<form [formGroup]="productForm" class="flex-col">
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-3-4">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput #name formControlName="name" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1-4">
|
||||
<mat-label>Fraction Units</mat-label>
|
||||
<input matInput formControlName="fractionUnits" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Sale Category</mat-label>
|
||||
<mat-select formControlName="saleCategory">
|
||||
@for (sc of saleCategories; track sc) {
|
||||
<mat-option [value]="sc.id">
|
||||
{{ sc.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid From</mat-label>
|
||||
<input matInput [matDatepicker]="productValidFromPicker" formControlName="validFrom" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="productValidFromPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #productValidFromPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid Till</mat-label>
|
||||
<input matInput [matDatepicker]="productValidTillPicker" formControlName="validTill" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="productValidTillPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #productValidTillPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container buttons">
|
||||
<button mat-raised-button color="primary" type="button" (click)="updateProduct()">Update</button>
|
||||
<button mat-raised-button color="warn" type="button" (click)="deleteProduct()">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
<table mat-table [dataSource]="item.products" class="mat-elevation-z1 full-width">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>Name</th>
|
||||
<td mat-cell *matCellDef="let p">{{ p.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="fractionUnits">
|
||||
<th mat-header-cell *matHeaderCellDef>Fraction Units</th>
|
||||
<td mat-cell *matCellDef="let p">{{ p.fraction_units }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="saleCategory">
|
||||
<th mat-header-cell *matHeaderCellDef>Sale Category</th>
|
||||
<td mat-cell *matCellDef="let p">{{ p.saleCategory?.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validFrom">
|
||||
<th mat-header-cell *matHeaderCellDef>Valid From</th>
|
||||
<td mat-cell *matCellDef="let p">{{ p.validFrom }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validTill">
|
||||
<th mat-header-cell *matHeaderCellDef>Valid Till</th>
|
||||
<td mat-cell *matCellDef="let p">{{ p.validTill }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
||||
<td mat-cell *matCellDef="let p">
|
||||
<button mat-stroked-button type="button" (click)="editProduct(p)">Edit</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="productDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: productDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2>SKU Versions</h2>
|
||||
<form [formGroup]="skuForm" class="flex-col">
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Units</mat-label>
|
||||
<input matInput formControlName="units" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Fraction</mat-label>
|
||||
<input matInput type="number" formControlName="fraction" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Yield</mat-label>
|
||||
<input matInput type="number" formControlName="productYield" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Cost Price</mat-label>
|
||||
<input matInput type="number" formControlName="costPrice" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Sale Price</mat-label>
|
||||
<input matInput type="number" formControlName="salePrice" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Menu Category</mat-label>
|
||||
<mat-select formControlName="menuCategory">
|
||||
@for (mc of menuCategories; track mc) {
|
||||
<mat-option [value]="mc.id">
|
||||
{{ mc.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-checkbox formControlName="hasHappyHour">Has Happy Hour?</mat-checkbox>
|
||||
<mat-checkbox formControlName="isNotAvailable">Is Not Available?</mat-checkbox>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid From</mat-label>
|
||||
<input matInput [matDatepicker]="skuValidFromPicker" formControlName="validFrom" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="skuValidFromPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #skuValidFromPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-1-2">
|
||||
<mat-label>Valid Till</mat-label>
|
||||
<input matInput [matDatepicker]="skuValidTillPicker" formControlName="validTill" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="skuValidTillPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #skuValidTillPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="row-container buttons">
|
||||
<button mat-raised-button color="primary" type="button" (click)="updateSku()">Update</button>
|
||||
<button mat-raised-button color="warn" type="button" (click)="deleteSku()">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
<table mat-table [dataSource]="item.skus" class="mat-elevation-z1 full-width">
|
||||
<ng-container matColumnDef="units">
|
||||
<th mat-header-cell *matHeaderCellDef>Units</th>
|
||||
<td mat-cell *matCellDef="let s">{{ s.units }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="salePrice">
|
||||
<th mat-header-cell *matHeaderCellDef>Sale Price</th>
|
||||
<td mat-cell *matCellDef="let s">{{ s.salePrice }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="menuCategory">
|
||||
<th mat-header-cell *matHeaderCellDef>Menu Category</th>
|
||||
<td mat-cell *matCellDef="let s">{{ s.menuCategory?.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validFrom">
|
||||
<th mat-header-cell *matHeaderCellDef>Valid From</th>
|
||||
<td mat-cell *matCellDef="let s">{{ s.validFrom }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validTill">
|
||||
<th mat-header-cell *matHeaderCellDef>Valid Till</th>
|
||||
<td mat-cell *matCellDef="let s">{{ s.validTill }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
||||
<td mat-cell *matCellDef="let s">
|
||||
<button mat-stroked-button type="button" (click)="editSku(s)">Edit</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="skuDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: skuDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-container">
|
||||
<button mat-raised-button color="primary" class="" (click)="update()">Update</button>
|
||||
<button mat-raised-button color="warn" (click)="confirmDelete()">Delete</button>
|
||||
|
||||
@ -9,13 +9,14 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
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 moment from 'moment';
|
||||
|
||||
import { MenuCategory } from '../../core/menu-category';
|
||||
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 { Product, StockKeepingUnit, TemporalProduct } from '../temporal-product';
|
||||
import { TemporalProductService } from '../temporal-product.service';
|
||||
|
||||
@Component({
|
||||
@ -24,14 +25,13 @@ import { TemporalProductService } from '../temporal-product.service';
|
||||
styleUrls: ['./temporal-product-detail.component.css'],
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatOptionModule,
|
||||
|
||||
MatSelectModule,
|
||||
MatTableModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
@ -42,72 +42,117 @@ export class TemporalProductDetailComponent implements OnInit, AfterViewInit {
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private ser = inject(TemporalProductService);
|
||||
|
||||
productDisplayedColumns = ['name', 'fractionUnits', 'saleCategory', 'validFrom', 'validTill', 'actions'];
|
||||
skuDisplayedColumns = ['units', 'salePrice', 'menuCategory', 'validFrom', 'validTill', 'actions'];
|
||||
|
||||
@ViewChild('name', { static: true }) nameElement?: ElementRef;
|
||||
form: FormGroup<{
|
||||
id: FormControl<string>;
|
||||
|
||||
private selectedProduct: Product | null;
|
||||
private selectedSku: StockKeepingUnit | null;
|
||||
|
||||
productForm: FormGroup<{
|
||||
name: FormControl<string>;
|
||||
units: FormControl<string>;
|
||||
menuCategory: FormControl<string>;
|
||||
fractionUnits: FormControl<string>;
|
||||
saleCategory: FormControl<string>;
|
||||
price: FormControl<number>;
|
||||
validFrom: FormControl<Date | null>;
|
||||
validTill: FormControl<Date | null>;
|
||||
}>;
|
||||
|
||||
skuForm: FormGroup<{
|
||||
units: FormControl<string>;
|
||||
fraction: FormControl<number>;
|
||||
productYield: FormControl<number>;
|
||||
costPrice: FormControl<number>;
|
||||
salePrice: FormControl<number>;
|
||||
menuCategory: FormControl<string>;
|
||||
hasHappyHour: FormControl<boolean>;
|
||||
isNotAvailable: FormControl<boolean>;
|
||||
quantity: FormControl<number>;
|
||||
validFrom: FormControl<Date | null>;
|
||||
validTill: FormControl<Date | null>;
|
||||
}>;
|
||||
|
||||
menuCategories: MenuCategory[] = [];
|
||||
saleCategories: SaleCategory[] = [];
|
||||
item: Product = new Product();
|
||||
item: TemporalProduct = new TemporalProduct();
|
||||
|
||||
constructor() {
|
||||
// Create form
|
||||
this.form = new FormGroup({
|
||||
id: new FormControl<string>('', { nonNullable: true }),
|
||||
this.productForm = new FormGroup({
|
||||
name: new FormControl<string>('', { nonNullable: true }),
|
||||
units: new FormControl<string>('', { nonNullable: true }),
|
||||
menuCategory: new FormControl<string>('', { nonNullable: true }),
|
||||
fractionUnits: new FormControl<string>('', { nonNullable: true }),
|
||||
saleCategory: new FormControl<string>('', { nonNullable: true }),
|
||||
price: new FormControl<number>(0, { nonNullable: true }),
|
||||
validFrom: new FormControl<Date | null>(null),
|
||||
validTill: new FormControl<Date | null>(null),
|
||||
});
|
||||
this.skuForm = new FormGroup({
|
||||
units: new FormControl<string>('', { nonNullable: true }),
|
||||
fraction: new FormControl<number>(1, { nonNullable: true }),
|
||||
productYield: new FormControl<number>(1, { nonNullable: true }),
|
||||
costPrice: new FormControl<number>(0, { nonNullable: true }),
|
||||
salePrice: new FormControl<number>(0, { nonNullable: true }),
|
||||
|
||||
menuCategory: new FormControl<string>('', { nonNullable: true }),
|
||||
hasHappyHour: new FormControl<boolean>(false, { nonNullable: true }),
|
||||
isNotAvailable: new FormControl<boolean>(false, { nonNullable: true }),
|
||||
quantity: new FormControl<number>(0, { nonNullable: true }),
|
||||
validFrom: new FormControl(new Date()),
|
||||
validTill: new FormControl(new Date()),
|
||||
validFrom: new FormControl<Date | null>(null),
|
||||
validTill: new FormControl<Date | null>(null),
|
||||
});
|
||||
this.selectedProduct = null;
|
||||
this.selectedSku = null;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.data.subscribe((value) => {
|
||||
const data = value as {
|
||||
item: Product;
|
||||
item: TemporalProduct;
|
||||
menuCategories: MenuCategory[];
|
||||
saleCategories: SaleCategory[];
|
||||
};
|
||||
this.menuCategories = data.menuCategories;
|
||||
this.saleCategories = data.saleCategories;
|
||||
this.showItem(data.item);
|
||||
this.item = data.item;
|
||||
// this.showItem(data.item);
|
||||
});
|
||||
}
|
||||
|
||||
showItem(item: Product) {
|
||||
this.item = item;
|
||||
this.form.setValue({
|
||||
id: this.item.id ?? '',
|
||||
name: this.item.name,
|
||||
units: this.item.units,
|
||||
menuCategory: this.item.menuCategory?.id ?? '',
|
||||
saleCategory: this.item.saleCategory?.id ?? '',
|
||||
price: this.item.price,
|
||||
hasHappyHour: this.item.hasHappyHour,
|
||||
isNotAvailable: this.item.isNotAvailable,
|
||||
quantity: this.item.quantity,
|
||||
validFrom: this.item.validFrom === null ? null : moment(this.item.validFrom, 'DD-MMM-YYYY').toDate(),
|
||||
validTill: this.item.validTill === null ? null : moment(this.item.validTill, 'DD-MMM-YYYY').toDate(),
|
||||
});
|
||||
private parseDateOrNull(d: string | null | undefined): Date | null {
|
||||
return !d ? null : moment(d, 'DD-MMM-YYYY').toDate();
|
||||
}
|
||||
|
||||
private formatDateOrNull(d: Date | null | undefined): string | null {
|
||||
return !d ? null : moment(d).format('DD-MMM-YYYY');
|
||||
}
|
||||
|
||||
// showItem(item: TemporalProduct) {
|
||||
// this.item = item;
|
||||
// this.selectedProduct = this.item.products[0];
|
||||
// this.selectedSku = this.item.skus[0];
|
||||
|
||||
// this.productForm.setValue({
|
||||
// // product
|
||||
// name: this.selectedProduct.name ?? '',
|
||||
// fractionUnits: this.selectedProduct.fraction_units ?? '',
|
||||
// saleCategory: this.selectedProduct.saleCategory?.id ?? '',
|
||||
// validFrom: this.parseDateOrNull(this.selectedProduct.validFrom),
|
||||
// validTill: this.parseDateOrNull(this.selectedProduct.validTill),
|
||||
// });
|
||||
|
||||
// this.skuForm.setValue({
|
||||
// // sku
|
||||
// units: this.selectedSku.units ?? '',
|
||||
// fraction: this.selectedSku.fraction ?? 1,
|
||||
// productYield: this.selectedSku.productYield ?? 1,
|
||||
// costPrice: this.selectedSku.costPrice ?? 0,
|
||||
// salePrice: this.selectedSku.salePrice ?? 0,
|
||||
|
||||
// menuCategory: this.selectedSku.menuCategory?.id ?? '',
|
||||
// hasHappyHour: this.selectedSku.hasHappyHour ?? false,
|
||||
// isNotAvailable: this.selectedSku.isNotAvailable ?? false,
|
||||
// validFrom: this.parseDateOrNull(this.selectedSku.validFrom),
|
||||
// validTill: this.parseDateOrNull(this.selectedSku.validTill),
|
||||
// });
|
||||
// }
|
||||
|
||||
ngAfterViewInit() {
|
||||
setTimeout(() => {
|
||||
if (this.nameElement !== undefined) {
|
||||
@ -116,8 +161,131 @@ export class TemporalProductDetailComponent implements OnInit, AfterViewInit {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
editProduct(p: Product) {
|
||||
this.selectedProduct = p;
|
||||
|
||||
console.log(p);
|
||||
|
||||
this.productForm.setValue({
|
||||
name: p.name ?? '',
|
||||
fractionUnits: p.fraction_units ?? '',
|
||||
saleCategory: p.saleCategory?.id ?? '',
|
||||
validFrom: this.parseDateOrNull(p.validFrom),
|
||||
validTill: this.parseDateOrNull(p.validTill),
|
||||
});
|
||||
setTimeout(() => this.nameElement?.nativeElement?.focus?.(), 0);
|
||||
}
|
||||
|
||||
updateProduct() {
|
||||
if (!this.selectedProduct) return;
|
||||
const formModel = this.productForm.value;
|
||||
|
||||
const p = this.selectedProduct;
|
||||
|
||||
p.name = formModel.name ?? '';
|
||||
p.fraction_units = formModel.fractionUnits ?? '';
|
||||
|
||||
if (!p.saleCategory) p.saleCategory = new SaleCategory();
|
||||
if (p.saleCategory === null || p.saleCategory === undefined) {
|
||||
p.saleCategory = new SaleCategory();
|
||||
}
|
||||
|
||||
p.saleCategory.id = formModel.saleCategory;
|
||||
|
||||
p.validFrom = this.formatDateOrNull(formModel.validFrom);
|
||||
p.validTill = this.formatDateOrNull(formModel.validTill);
|
||||
}
|
||||
|
||||
deleteProduct() {
|
||||
if (!this.selectedProduct) return;
|
||||
|
||||
const idx = (this.item.products ?? []).indexOf(this.selectedProduct);
|
||||
if (idx >= 0) {
|
||||
this.item.products.splice(idx, 1);
|
||||
}
|
||||
|
||||
this.selectedProduct = null;
|
||||
|
||||
// Reset form
|
||||
this.productForm.reset({
|
||||
name: '',
|
||||
fractionUnits: '',
|
||||
saleCategory: '',
|
||||
validFrom: null,
|
||||
validTill: null,
|
||||
});
|
||||
}
|
||||
|
||||
editSku(s: StockKeepingUnit) {
|
||||
this.selectedSku = s;
|
||||
|
||||
this.skuForm.setValue({
|
||||
units: s.units ?? '',
|
||||
fraction: s.fraction ?? 1,
|
||||
productYield: s.productYield ?? 1,
|
||||
costPrice: s.costPrice ?? 0,
|
||||
salePrice: s.salePrice ?? 0,
|
||||
menuCategory: s.menuCategory?.id ?? '',
|
||||
|
||||
hasHappyHour: s.hasHappyHour ?? false,
|
||||
isNotAvailable: s.isNotAvailable ?? false,
|
||||
validFrom: this.parseDateOrNull(s.validFrom),
|
||||
validTill: this.parseDateOrNull(s.validTill),
|
||||
});
|
||||
}
|
||||
|
||||
updateSku() {
|
||||
if (!this.selectedSku) return;
|
||||
const formModel = this.skuForm.value;
|
||||
|
||||
const s = this.selectedSku;
|
||||
s.units = formModel.units ?? '';
|
||||
s.fraction = formModel.fraction ?? 1;
|
||||
s.productYield = formModel.productYield ?? 1;
|
||||
s.costPrice = formModel.costPrice ?? 0;
|
||||
s.salePrice = formModel.salePrice ?? 0;
|
||||
|
||||
if (!s.menuCategory) s.menuCategory = new MenuCategory();
|
||||
if (s.menuCategory === null || s.menuCategory === undefined) {
|
||||
s.menuCategory = new MenuCategory();
|
||||
}
|
||||
|
||||
s.menuCategory.id = formModel.menuCategory;
|
||||
|
||||
s.hasHappyHour = formModel.hasHappyHour ?? false;
|
||||
s.isNotAvailable = formModel.isNotAvailable ?? false;
|
||||
|
||||
s.validFrom = this.formatDateOrNull(formModel.validFrom);
|
||||
s.validTill = this.formatDateOrNull(formModel.validTill);
|
||||
}
|
||||
|
||||
deleteSku() {
|
||||
if (!this.selectedSku) return;
|
||||
|
||||
const idx = (this.item.skus ?? []).indexOf(this.selectedSku);
|
||||
if (idx >= 0) {
|
||||
this.item.skus.splice(idx, 1);
|
||||
}
|
||||
|
||||
this.selectedSku = null;
|
||||
|
||||
// Reset form
|
||||
this.skuForm.reset({
|
||||
units: '',
|
||||
fraction: 1,
|
||||
productYield: 1,
|
||||
costPrice: 0,
|
||||
salePrice: 0,
|
||||
menuCategory: '',
|
||||
hasHappyHour: false,
|
||||
isNotAvailable: false,
|
||||
validFrom: null,
|
||||
validTill: null,
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.ser.update(this.getItem()).subscribe({
|
||||
this.ser.update(this.item).subscribe({
|
||||
next: () => {
|
||||
this.snackBar.open('', 'Success');
|
||||
this.router.navigateByUrl('/temporal-products');
|
||||
@ -129,7 +297,7 @@ export class TemporalProductDetailComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.ser.delete(this.item.versionId as string).subscribe({
|
||||
this.ser.delete(this.item.products[0].id as string).subscribe({
|
||||
next: () => {
|
||||
this.snackBar.open('', 'Success');
|
||||
this.router.navigateByUrl('/temporal-products');
|
||||
@ -152,28 +320,4 @@ export class TemporalProductDetailComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getItem(): Product {
|
||||
const formModel = this.form.value;
|
||||
this.item.id = formModel.id;
|
||||
this.item.name = formModel.name ?? '';
|
||||
this.item.units = formModel.units ?? '';
|
||||
if (this.item.menuCategory === null || this.item.menuCategory === undefined) {
|
||||
this.item.menuCategory = new MenuCategory();
|
||||
}
|
||||
this.item.menuCategory.id = formModel.menuCategory;
|
||||
if (this.item.saleCategory === null || this.item.saleCategory === undefined) {
|
||||
this.item.saleCategory = new SaleCategory();
|
||||
}
|
||||
this.item.saleCategory.id = formModel.saleCategory;
|
||||
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.validFrom = !formModel.validFrom ? null : moment(formModel.validFrom).format('DD-MMM-YYYY');
|
||||
console.log(formModel.validTill);
|
||||
this.item.validTill = !formModel.validTill ? null : moment(formModel.validTill).format('DD-MMM-YYYY');
|
||||
|
||||
return this.item;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
|
||||
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
|
||||
import { TemporalProduct } from './temporal-product';
|
||||
import { TemporalProductService } from './temporal-product.service';
|
||||
|
||||
export const temporalProductListResolver: ResolveFn<Product[][]> = () => {
|
||||
export const temporalProductListResolver: ResolveFn<TemporalProduct[]> = () => {
|
||||
return inject(TemporalProductService).list();
|
||||
};
|
||||
|
||||
@ -2,13 +2,11 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { merge, Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
import { MenuCategory } from '../../core/menu-category';
|
||||
import { SaleCategory } from '../../core/sale-category';
|
||||
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
|
||||
import { TemporalProduct } from '../temporal-product';
|
||||
|
||||
export class TemporalProductListDatasource extends DataSource<Product> {
|
||||
public data: Product[][];
|
||||
public filteredData: Product[][];
|
||||
export class TemporalProductListDatasource extends DataSource<TemporalProduct> {
|
||||
public data: TemporalProduct[];
|
||||
public filteredData: TemporalProduct[];
|
||||
public search: string;
|
||||
public menuCategory: string;
|
||||
public saleCategory: string;
|
||||
@ -17,7 +15,7 @@ export class TemporalProductListDatasource extends DataSource<Product> {
|
||||
private readonly searchFilter: Observable<string>,
|
||||
private readonly menuCategoryFilter: Observable<string>,
|
||||
private readonly saleCategoryFilter: Observable<string>,
|
||||
private readonly dataObs: Observable<Product[][]>,
|
||||
private readonly dataObs: Observable<TemporalProduct[]>,
|
||||
) {
|
||||
super();
|
||||
this.data = [];
|
||||
@ -27,7 +25,7 @@ export class TemporalProductListDatasource extends DataSource<Product> {
|
||||
this.saleCategory = '';
|
||||
}
|
||||
|
||||
connect(): Observable<Product[]> {
|
||||
connect(): Observable<TemporalProduct[]> {
|
||||
const dataMutations = [
|
||||
this.dataObs.pipe(
|
||||
tap((x) => {
|
||||
@ -52,42 +50,49 @@ export class TemporalProductListDatasource extends DataSource<Product> {
|
||||
];
|
||||
return merge(...dataMutations).pipe(
|
||||
map(() => this.getFilteredData(this.data, this.search, this.menuCategory, this.saleCategory)),
|
||||
tap((x: Product[][]) => {
|
||||
tap((x: TemporalProduct[]) => {
|
||||
this.filteredData = x;
|
||||
}),
|
||||
map((x: Product[][]) => x.reduce((p, c) => p.concat(c), [])),
|
||||
);
|
||||
}
|
||||
|
||||
disconnect() {}
|
||||
|
||||
private getFilteredData(data: Product[][], search: string, menuCategory: string, saleCategory: string): Product[][] {
|
||||
return data.filter(
|
||||
(o: Product[]) =>
|
||||
o
|
||||
.filter(
|
||||
(x: Product) =>
|
||||
search === null ||
|
||||
search === undefined ||
|
||||
search === '' ||
|
||||
`${x.name} ${x.units} ${x.saleCategory?.name} ${x.menuCategory?.name}`
|
||||
.toLowerCase()
|
||||
.indexOf(search.toLowerCase()) !== -1,
|
||||
)
|
||||
.filter(
|
||||
(x) =>
|
||||
menuCategory === null ||
|
||||
menuCategory === undefined ||
|
||||
menuCategory === '' ||
|
||||
(x.menuCategory as MenuCategory).id === menuCategory,
|
||||
)
|
||||
.filter(
|
||||
(x) =>
|
||||
saleCategory === null ||
|
||||
saleCategory === undefined ||
|
||||
saleCategory === '' ||
|
||||
(x.saleCategory as SaleCategory).id === saleCategory,
|
||||
).length > 0,
|
||||
);
|
||||
private getFilteredData(
|
||||
data: TemporalProduct[],
|
||||
search: string,
|
||||
menuCategory: string,
|
||||
saleCategory: string,
|
||||
): TemporalProduct[] {
|
||||
const tokens = (search ?? '').toLowerCase().split(/\s+/).filter(Boolean);
|
||||
|
||||
return data.filter((tp: TemporalProduct) => {
|
||||
search = search.toLowerCase();
|
||||
|
||||
const products = tp.products ?? [];
|
||||
const skus = tp.skus ?? [];
|
||||
|
||||
// 1) Search: match ANY product/sku fields
|
||||
const matchesSearch =
|
||||
tokens.length === 0 ||
|
||||
tokens.every(
|
||||
(token) =>
|
||||
products.some((p) => {
|
||||
const hay = `${p.name ?? ''} ${p.fraction_units ?? ''} ${p.saleCategory?.name ?? ''}`.toLowerCase();
|
||||
return hay.includes(token);
|
||||
}) ||
|
||||
skus.some((k) => {
|
||||
const hay = `${k.units ?? ''} ${k.menuCategory?.name ?? ''}`.toLowerCase();
|
||||
return hay.includes(token);
|
||||
}),
|
||||
);
|
||||
|
||||
const matchesMenuCategory = menuCategory === '' || skus.some((k) => (k.menuCategory?.id ?? '') === menuCategory);
|
||||
|
||||
const matchesSaleCategory =
|
||||
saleCategory === '' || products.some((p) => (p.saleCategory?.id ?? '') === saleCategory);
|
||||
|
||||
return matchesSearch && matchesMenuCategory && matchesSaleCategory;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,16 +31,36 @@
|
||||
</form>
|
||||
<mat-table [dataSource]="dataSource">
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
|
||||
<ng-container matColumnDef="product">
|
||||
<mat-header-cell *matHeaderCellDef>Products</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<ul>
|
||||
<li>
|
||||
<a [routerLink]="['/temporal-products', row.versionId]">{{ row.name }} ({{ row.units }})</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ row.id }}
|
||||
</li>
|
||||
@for (p of row.products; track p) {
|
||||
<li>
|
||||
<a [routerLink]="['/temporal-products', p.id]">
|
||||
<b>{{ p.name }}</b>
|
||||
</a>
|
||||
<div class="text-sm">
|
||||
<b>Valid:</b>
|
||||
{{ p.validFrom ?? '∞' }}
|
||||
<mat-icon>linear_scale</mat-icon>
|
||||
{{ p.validTill ?? '∞' }}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="sku">
|
||||
<mat-header-cell *matHeaderCellDef>Skus</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<ul>
|
||||
@for (s of row.skus; track s) {
|
||||
<li>
|
||||
{{ s.units }}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
@ -48,26 +68,26 @@
|
||||
<!-- Price Column -->
|
||||
<ng-container matColumnDef="price">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Price</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.price | currency: 'INR' }}</mat-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.skus.at(-1).price | currency: 'INR' }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Menu Category Column -->
|
||||
<ng-container matColumnDef="menuCategory">
|
||||
<mat-header-cell *matHeaderCellDef>Menu Category</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.menuCategory.name }}</mat-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.skus.at(-1).menuCategory.name }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Sale Category Column -->
|
||||
<ng-container matColumnDef="saleCategory">
|
||||
<mat-header-cell *matHeaderCellDef>Sale Category</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.saleCategory.name }}</mat-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.products.at(-1).saleCategory.name }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Info Column -->
|
||||
<ng-container matColumnDef="info">
|
||||
<mat-header-cell *matHeaderCellDef class="center">Details</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<ul>
|
||||
<!-- <ul>
|
||||
<li>
|
||||
<b>Valid From: {{ row.validFrom ?? '∞' }} </b> <mat-icon>linear_scale</mat-icon>
|
||||
<b>Till: {{ row.validTill ?? '∞' }} </b>
|
||||
@ -84,15 +104,15 @@
|
||||
</mat-icon>
|
||||
<b> {{ row.isNotAvailable ? 'Is not Available' : 'Is Available' }}</b>
|
||||
</li>
|
||||
</ul>
|
||||
</ul> -->
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container matColumnDef="quantity">
|
||||
<!-- <ng-container matColumnDef="quantity">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Quantity</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.quantity | number: '1.2-2' }}</mat-cell>
|
||||
</ng-container>
|
||||
</ng-container> -->
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { DecimalPipe, CurrencyPipe } from '@angular/common';
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
@ -13,7 +13,7 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
import { MenuCategory } from '../../core/menu-category';
|
||||
import { SaleCategory } from '../../core/sale-category';
|
||||
import { StockKeepingUnit as Product } from '../../core/stock-keeping-unit';
|
||||
import { TemporalProduct } from '../temporal-product';
|
||||
import { TemporalProductListDatasource } from './temporal-product-list-datasource';
|
||||
|
||||
@Component({
|
||||
@ -22,7 +22,7 @@ import { TemporalProductListDatasource } from './temporal-product-list-datasourc
|
||||
styleUrls: ['./temporal-product-list.component.css'],
|
||||
imports: [
|
||||
CurrencyPipe,
|
||||
DecimalPipe,
|
||||
// DecimalPipe,
|
||||
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
@ -40,7 +40,7 @@ export class TemporalProductListComponent implements OnInit {
|
||||
searchFilter = new Observable<string>();
|
||||
menuCategoryFilter = new BehaviorSubject<string>('');
|
||||
saleCategoryFilter = new BehaviorSubject<string>('');
|
||||
data: BehaviorSubject<Product[][]> = new BehaviorSubject<Product[][]>([]);
|
||||
data: BehaviorSubject<TemporalProduct[]> = new BehaviorSubject<TemporalProduct[]>([]);
|
||||
dataSource: TemporalProductListDatasource = new TemporalProductListDatasource(
|
||||
this.searchFilter,
|
||||
this.menuCategoryFilter,
|
||||
@ -54,11 +54,11 @@ export class TemporalProductListComponent implements OnInit {
|
||||
saleCategory: FormControl<string | null>;
|
||||
}>;
|
||||
|
||||
list: Product[][] = [];
|
||||
list: TemporalProduct[] = [];
|
||||
menuCategories: MenuCategory[] = [];
|
||||
saleCategories: SaleCategory[] = [];
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
displayedColumns: string[] = ['name', 'price', 'menuCategory', 'saleCategory', 'info', 'quantity'];
|
||||
displayedColumns: string[] = ['product', 'sku', 'price', 'menuCategory', 'saleCategory', 'info'];
|
||||
|
||||
constructor() {
|
||||
this.form = new FormGroup({
|
||||
@ -66,7 +66,7 @@ export class TemporalProductListComponent implements OnInit {
|
||||
menuCategory: new FormControl<string | null>(''),
|
||||
saleCategory: new FormControl<string | null>(''),
|
||||
});
|
||||
this.data.subscribe((data: Product[][]) => {
|
||||
this.data.subscribe((data: TemporalProduct[]) => {
|
||||
this.list = data;
|
||||
});
|
||||
this.searchFilter = this.form.controls.filter.valueChanges.pipe(debounceTime(150), distinctUntilChanged());
|
||||
@ -89,7 +89,7 @@ export class TemporalProductListComponent implements OnInit {
|
||||
);
|
||||
this.route.data.subscribe((value) => {
|
||||
const data = value as {
|
||||
list: Product[][];
|
||||
list: TemporalProduct[];
|
||||
menuCategories: MenuCategory[];
|
||||
saleCategories: SaleCategory[];
|
||||
};
|
||||
@ -97,7 +97,7 @@ export class TemporalProductListComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
loadData(list: Product[][], menuCategories: MenuCategory[], saleCategories: SaleCategory[]) {
|
||||
loadData(list: TemporalProduct[], menuCategories: MenuCategory[], saleCategories: SaleCategory[]) {
|
||||
this.menuCategories = menuCategories;
|
||||
this.saleCategories = saleCategories;
|
||||
this.data.next(list);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
|
||||
import { StockKeepingUnit as Product } from '../core/stock-keeping-unit';
|
||||
import { TemporalProduct } from './temporal-product';
|
||||
import { TemporalProductService } from './temporal-product.service';
|
||||
|
||||
export const temporalProductResolver: ResolveFn<Product> = (route) => {
|
||||
export const temporalProductResolver: ResolveFn<TemporalProduct> = (route) => {
|
||||
const id = route.paramMap.get('id');
|
||||
return inject(TemporalProductService).get(id as string);
|
||||
};
|
||||
|
||||
@ -4,7 +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 { TemporalProduct } from './temporal-product';
|
||||
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
|
||||
@ -18,27 +18,27 @@ export class TemporalProductService {
|
||||
private http = inject(HttpClient);
|
||||
private log = inject(ErrorLoggerService);
|
||||
|
||||
get(id: string): Observable<Product> {
|
||||
get(id: string): Observable<TemporalProduct> {
|
||||
return this.http
|
||||
.get<Product>(`${url}/${id}`)
|
||||
.pipe(catchError(this.log.handleError(serviceName, `get id=${id}`))) as Observable<Product>;
|
||||
.get<TemporalProduct>(`${url}/${id}`)
|
||||
.pipe(catchError(this.log.handleError(serviceName, `get id=${id}`))) as Observable<TemporalProduct>;
|
||||
}
|
||||
|
||||
list(): Observable<Product[][]> {
|
||||
list(): Observable<TemporalProduct[]> {
|
||||
return this.http
|
||||
.get<Product[][]>(`${url}/list`)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Product[][]>;
|
||||
.get<TemporalProduct[]>(`${url}/list`)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<TemporalProduct[]>;
|
||||
}
|
||||
|
||||
update(product: Product): Observable<void> {
|
||||
update(product: TemporalProduct): Observable<void> {
|
||||
return this.http
|
||||
.put<Product>(`${url}/${product.versionId}`, product, httpOptions)
|
||||
.put<TemporalProduct>(`${url}/${product.products[0].id}`, product, httpOptions)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<void>;
|
||||
}
|
||||
|
||||
delete(id: string): Observable<void> {
|
||||
return this.http
|
||||
.delete<Product>(`${url}/${id}`, httpOptions)
|
||||
.delete<TemporalProduct>(`${url}/${id}`, httpOptions)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable<void>;
|
||||
}
|
||||
}
|
||||
|
||||
68
bookie/src/app/temporal-product/temporal-product.ts
Normal file
68
bookie/src/app/temporal-product/temporal-product.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { MenuCategory } from '../core/menu-category';
|
||||
import { SaleCategory } from '../core/sale-category';
|
||||
|
||||
export class Product {
|
||||
id: string | undefined;
|
||||
versionId?: string;
|
||||
name: string;
|
||||
fraction_units: string;
|
||||
saleCategory?: SaleCategory;
|
||||
sortOrder: number;
|
||||
|
||||
validFrom: string | null;
|
||||
validTill: string | null;
|
||||
|
||||
public constructor(init?: Partial<Product>) {
|
||||
this.id = undefined;
|
||||
this.name = '';
|
||||
this.fraction_units = '';
|
||||
this.sortOrder = 0;
|
||||
this.validFrom = null;
|
||||
this.validTill = null;
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class StockKeepingUnit {
|
||||
id: string | undefined;
|
||||
versionId?: string;
|
||||
units: string;
|
||||
fraction: number;
|
||||
productYield: number;
|
||||
costPrice: number;
|
||||
salePrice: number;
|
||||
menuCategory?: MenuCategory;
|
||||
|
||||
sortOrder: number;
|
||||
|
||||
hasHappyHour: boolean;
|
||||
isNotAvailable: boolean;
|
||||
|
||||
validFrom: string | null;
|
||||
validTill: string | null;
|
||||
|
||||
public constructor(init?: Partial<StockKeepingUnit>) {
|
||||
this.units = '';
|
||||
this.fraction = 1;
|
||||
this.productYield = 1;
|
||||
this.costPrice = 0;
|
||||
this.salePrice = 0;
|
||||
this.sortOrder = 0;
|
||||
this.hasHappyHour = false;
|
||||
this.isNotAvailable = false;
|
||||
this.validFrom = null;
|
||||
this.validTill = null;
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class TemporalProduct {
|
||||
products: Product[];
|
||||
skus: StockKeepingUnit[];
|
||||
|
||||
public constructor(init?: Partial<TemporalProduct>) {
|
||||
this.products = [];
|
||||
this.skus = [];
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user