DB Normalization: Moved fractionUnits back to Product from SKU as it is better suited there.

Feature: Created the ProductSku schema for the product/sku autocomplete
This commit is contained in:
2021-11-02 13:50:35 +05:30
parent b3075577e6
commit 30e3288b1e
26 changed files with 295 additions and 244 deletions

View File

@ -16,10 +16,6 @@
<mat-label>Fraction</mat-label>
<input matInput type="number" placeholder="Fraction" formControlName="fraction" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>Fraction Units</mat-label>
<input matInput placeholder="Fraction Units" formControlName="fractionUnits" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>Yield</mat-label>
<input matInput type="number" placeholder="Yield" formControlName="productYield" />
@ -30,7 +26,7 @@
matInput
type="number"
placeholder="{{ data.isPurchased ? 'Purchase Price' : 'Cost Price' }}"
formControlName="price"
formControlName="costPrice"
/>
</mat-form-field>
<mat-form-field fxFlex [hidden]="!data.isSold">

View File

@ -21,9 +21,8 @@ export class ProductDetailDialogComponent implements OnInit {
this.form = this.fb.group({
units: '',
fraction: '',
fractionUnits: '',
productYield: '',
price: '',
costPrice: '',
salePrice: '',
});
}
@ -32,9 +31,8 @@ export class ProductDetailDialogComponent implements OnInit {
this.form.setValue({
units: this.data.item.units,
fraction: '' + this.data.item.fraction,
fractionUnits: this.data.item.fractionUnits,
productYield: '' + this.data.item.productYield,
price: '' + this.data.item.price,
costPrice: '' + this.data.item.costPrice,
salePrice: '' + this.data.item.salePrice,
});
}
@ -49,8 +47,8 @@ export class ProductDetailDialogComponent implements OnInit {
if (productYield < 0 || productYield > 1) {
return;
}
const price = +formValue.price;
if (price < 0) {
const costPrice = +formValue.costPrice;
if (costPrice < 0) {
return;
}
const salePrice = +formValue.salePrice;
@ -59,9 +57,8 @@ export class ProductDetailDialogComponent implements OnInit {
}
this.data.item.units = formValue.units;
this.data.item.fraction = fraction;
this.data.item.fractionUnits = formValue.fractionUnits;
this.data.item.productYield = productYield;
this.data.item.price = price;
this.data.item.costPrice = costPrice;
this.data.item.salePrice = salePrice;
this.dialogRef.close(this.data.item);
}

View File

@ -24,10 +24,14 @@
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<mat-form-field fxFlex>
<mat-form-field fxFlex="80">
<mat-label>Name</mat-label>
<input matInput #nameElement placeholder="Name" formControlName="name" />
</mat-form-field>
<mat-form-field fxFlex="20">
<mat-label>Fraction Units</mat-label>
<input matInput placeholder="Fraction Units" formControlName="fractionUnits" />
</mat-form-field>
</div>
<div
fxLayout="row"
@ -73,10 +77,6 @@
<mat-label>Fraction</mat-label>
<input matInput type="number" placeholder="Fraction" formControlName="fraction" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>Fraction Units</mat-label>
<input matInput placeholder="Fraction Units" formControlName="fractionUnits" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>Yield</mat-label>
<input matInput type="number" placeholder="Yield" formControlName="productYield" />
@ -87,7 +87,7 @@
matInput
type="number"
placeholder="{{ item.isPurchased ? 'Purchase Price' : 'Cost Price' }}"
formControlName="price"
formControlName="costPrice"
/>
</mat-form-field>
<mat-form-field fxFlex>
@ -98,18 +98,6 @@
</div>
</form>
<mat-table [dataSource]="dataSource" aria-label="Elements">
<!-- Checkbox Column -->
<ng-container matColumnDef="isDefault">
<mat-header-cell *matHeaderCellDef>Default</mat-header-cell>
<mat-cell *matCellDef="let row">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="changeDefault($event, row)"
[checked]="row.isDefault"
>
</mat-checkbox>
</mat-cell>
</ng-container>
<!-- Units Column -->
<ng-container matColumnDef="units">
<mat-header-cell *matHeaderCellDef>Units</mat-header-cell>
@ -122,22 +110,18 @@
<mat-cell *matCellDef="let row">{{ row.fraction }}</mat-cell>
</ng-container>
<!-- Fraction Units Column -->
<ng-container matColumnDef="fractionUnits">
<mat-header-cell *matHeaderCellDef>Fraction Units</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row.fractionUnits }}</mat-cell>
</ng-container>
<!-- Yield Column -->
<ng-container matColumnDef="yield">
<mat-header-cell *matHeaderCellDef class="right">Yield</mat-header-cell>
<mat-cell *matCellDef="let row" class="right">{{ row.productYield }}</mat-cell>
</ng-container>
<!-- 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>
<!-- Cost Price Column -->
<ng-container matColumnDef="costPrice">
<mat-header-cell *matHeaderCellDef class="right">Cost Price</mat-header-cell>
<mat-cell *matCellDef="let row" class="right">{{
row.costPrice | currency: 'INR'
}}</mat-cell>
</ng-container>
<!-- Sale Price Column -->

View File

@ -1,6 +1,5 @@
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
@ -27,16 +26,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
dataSource: ProductDetailDatasource = new ProductDetailDatasource(this.skus);
item: Product = new Product();
displayedColumns = [
'isDefault',
'units',
'fraction',
'fractionUnits',
'yield',
'price',
'salePrice',
'action',
];
displayedColumns = ['units', 'fraction', 'yield', 'costPrice', 'salePrice', 'action'];
constructor(
private route: ActivatedRoute,
@ -49,12 +39,12 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.form = this.fb.group({
code: { value: '', disabled: true },
name: '',
fractionUnits: '',
addRow: this.fb.group({
units: '',
fraction: '',
fractionUnits: '',
productYield: '',
price: '',
costPrice: '',
salePrice: '',
}),
isPurchased: '',
@ -79,13 +69,13 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.item = item;
this.form.setValue({
code: this.item.code || '(Auto)',
name: this.item.name || '',
name: this.item.name,
fractionUnits: this.item.fractionUnits,
addRow: {
units: '',
fraction: '',
fractionUnits: '',
productYield: '',
price: '',
costPrice: '',
salePrice: '',
},
isPurchased: this.item.isPurchased,
@ -115,8 +105,8 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.toaster.show('Danger', 'Product Yield has to be > 0 and <= 1');
return;
}
const price = +formValue.price;
if (price < 0) {
const costPrice = +formValue.costPrice;
if (costPrice < 0) {
this.toaster.show('Danger', 'Price has to be >= 0');
return;
}
@ -129,9 +119,8 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
new StockKeepingUnit({
units: formValue.units,
fraction,
fractionUnits: formValue.fractionUnits,
productYield,
price,
costPrice,
salePrice,
}),
);
@ -143,9 +132,8 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
(this.form.get('addRow') as FormControl).reset({
units: '',
fraction: '',
fractionUnits: '',
productYield: '',
price: '',
costPrice: '',
salePrice: '',
});
}
@ -216,6 +204,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
getItem(): Product {
const formModel = this.form.value;
this.item.name = formModel.name;
this.item.fractionUnits = formModel.fractionUnits;
this.item.isPurchased = formModel.isPurchased;
this.item.isSold = formModel.isSold;
this.item.isActive = formModel.isActive;
@ -225,9 +214,4 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.item.productGroup.id = formModel.productGroup;
return this.item;
}
changeDefault($event: MatCheckboxChange, row: StockKeepingUnit) {
this.item.skus.forEach((x) => (x.isDefault = false));
row.isDefault = true;
}
}

View File

@ -39,7 +39,7 @@
<li *ngFor="let sku of row.skus">
<a [routerLink]="['/products', row.id]"
>{{ row.name }} ({{
showExtended ? sku.fraction + ' ' + sku.fractionUnits + ' = 1 ' : ''
showExtended ? sku.fraction + ' ' + row.fractionUnits + ' = 1 ' : ''
}}{{ sku.units }})</a
>
</li>
@ -53,7 +53,7 @@
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let sku of row.skus">
{{ sku.price | currency: 'INR' }}
{{ sku.costPrice | currency: 'INR' }}
</li>
</ul>
</mat-cell>

View File

@ -5,6 +5,7 @@ import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { Product } from '../core/product';
import { ProductSku } from '../core/product-sku';
const url = '/api/products';
const serviceName = 'ProductService';
@ -51,16 +52,28 @@ export class ProductService {
.pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable<Product>;
}
autocomplete(
autocompleteProduct(query: string, isPurchased: boolean | null): Observable<ProductSku[]> {
const options = {
params: new HttpParams().set('q', query),
};
if (isPurchased !== null) {
options.params = options.params.set('p', isPurchased.toString());
}
return this.http
.get<ProductSku[]>(`${url}/q-product`, options)
.pipe(catchError(this.log.handleError(serviceName, 'autocomplete'))) as Observable<
ProductSku[]
>;
}
autocompleteSku(
query: string,
isPurchased: boolean | null,
extended: boolean = false,
skus: boolean = true,
date?: string,
vendorId?: string,
): Observable<Product[]> {
): Observable<ProductSku[]> {
const options = {
params: new HttpParams().set('q', query).set('e', extended.toString()).set('s', skus),
params: new HttpParams().set('q', query),
};
if (isPurchased !== null) {
options.params = options.params.set('p', isPurchased.toString());
@ -69,7 +82,9 @@ export class ProductService {
options.params = options.params.set('v', vendorId as string).set('d', date as string);
}
return this.http
.get<Product[]>(`${url}/query`, options)
.pipe(catchError(this.log.handleError(serviceName, 'autocomplete'))) as Observable<Product[]>;
.get<ProductSku[]>(`${url}/q-sku`, options)
.pipe(catchError(this.log.handleError(serviceName, 'autocomplete'))) as Observable<
ProductSku[]
>;
}
}