Feature: Sale Category also shows the products in it to make it easier to check for errors.

This commit is contained in:
tanshu committed 2021-08-06 08:23:36 +05:30
1 parent 0b3ec5da16
commit 2f440a4127
8 files changed
+260 -96

No files matched your search

+93 -32
View File
@@ -1,7 +1,7 @@
import uuid import uuid
from datetime import date, timedelta from datetime import date, timedelta
from typing import List from typing import List, Optional
import barker.schemas.product as schemas import barker.schemas.product as schemas
@@ -299,45 +299,106 @@ def product_list(date_: date, db: Session) -> List[schemas.Product]:
@router.get("/query") @router.get("/query")
async def show_term( async def show_term(
mc: uuid.UUID = None, mc: Optional[uuid.UUID] = None,
sc: Optional[uuid.UUID] = None,
date_: date = Depends(effective_date), date_: date = Depends(effective_date),
current_user: UserToken = Depends(get_user), current_user: UserToken = Depends(get_user),
): ):
list_ = [] list_ = []
query = select(ProductVersion)
if sc is not None:
query = query.join(ProductVersion.menu_category)
if mc is not None:
query = query.join(ProductVersion.sale_category).join(SaleCategory.tax)
query = query.where(
and_(
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= date_,
),
)
)
if mc is not None:
query = query.where(ProductVersion.menu_category_id == mc).order_by(
ProductVersion.sort_order, ProductVersion.name
)
if sc is not None:
query = query.where(ProductVersion.sale_category_id == sc).order_by(
MenuCategory.sort_order, ProductVersion.sort_order, ProductVersion.name
)
if mc is not None:
query = query.options(
joinedload(ProductVersion.sale_category, innerjoin=True),
joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True),
contains_eager(ProductVersion.sale_category),
contains_eager(ProductVersion.sale_category, SaleCategory.tax),
)
if sc is not None:
query = query.options(
joinedload(ProductVersion.menu_category, innerjoin=True),
contains_eager(ProductVersion.menu_category),
)
with SessionFuture() as db: with SessionFuture() as db:
for item in ( for item in db.execute(query).scalars().all():
db.execute( if sc is not None:
select(ProductVersion) list_.append(
.join(ProductVersion.sale_category) {
.join(SaleCategory.tax) "id": item.product_id,
.where( "name": item.full_name,
and_( "menuCategory": {
ProductVersion.menu_category_id == mc, "id": item.menu_category_id,
or_( "name": item.menu_category.name,
ProductVersion.valid_from == None, # noqa: E711 },
ProductVersion.valid_from <= date_, "price": item.price,
), }
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= date_,
),
)
) )
.order_by(ProductVersion.sort_order, ProductVersion.name) if mc is not None:
.options( list_.append(query_product_info(item, False))
joinedload(ProductVersion.sale_category, innerjoin=True), if item.has_happy_hour:
joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), list_.append(query_product_info(item, True))
contains_eager(ProductVersion.sale_category), return list_
contains_eager(ProductVersion.sale_category, SaleCategory.tax),
def product_list_of_sale_category(date_: date, db: Session) -> List[schemas.Product]:
return [
product_info(item)
for item in db.execute(
select(ProductVersion)
.join(ProductVersion.menu_category)
.join(ProductVersion.sale_category)
.where(
and_(
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= date_,
),
) )
) )
.scalars() .order_by(MenuCategory.sort_order)
.all() .order_by(MenuCategory.name)
): .order_by(ProductVersion.sort_order)
list_.append(query_product_info(item, False)) .order_by(ProductVersion.name)
if item.has_happy_hour: .options(
list_.append(query_product_info(item, True)) joinedload(ProductVersion.menu_category, innerjoin=True),
return list_ joinedload(ProductVersion.sale_category, innerjoin=True),
contains_eager(ProductVersion.menu_category),
contains_eager(ProductVersion.sale_category),
)
)
.scalars()
.all()
]
@router.get("/{id_}", response_model=schemas.Product) @router.get("/{id_}", response_model=schemas.Product)
@@ -30,6 +30,15 @@ export class ProductService {
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Product[]>; .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[]> { listIsActiveOfCategory(id: string): Observable<Product[]> {
const options = { params: new HttpParams().set('mc', id) }; const options = { params: new HttpParams().set('mc', id) };
return this.http return this.http
@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import { Observable, of as observableOf } from 'rxjs';
import { Product } from '../core/product';
import { ProductService } from '../product/product.service';
@Injectable({
providedIn: 'root',
})
export class ProductListResolver implements Resolve<Product[]> {
constructor(private ser: ProductService) {}
resolve(route: ActivatedRouteSnapshot): Observable<Product[]> {
const id = route.paramMap.get('id');
if (id === null) {
return observableOf([]);
}
return this.ser.listOfSaleCategory(id);
}
}
@@ -5,6 +5,7 @@ import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '../auth/auth-guard.service'; import { AuthGuard } from '../auth/auth-guard.service';
import { TaxListResolver } from '../taxes/tax-list-resolver.service'; import { TaxListResolver } from '../taxes/tax-list-resolver.service';
import { ProductListResolver } from './product-list-resolver.service';
import { SaleCategoryDetailComponent } from './sale-category-detail/sale-category-detail.component'; import { SaleCategoryDetailComponent } from './sale-category-detail/sale-category-detail.component';
import { SaleCategoryListResolver } from './sale-category-list-resolver.service'; import { SaleCategoryListResolver } from './sale-category-list-resolver.service';
import { SaleCategoryListComponent } from './sale-category-list/sale-category-list.component'; import { SaleCategoryListComponent } from './sale-category-list/sale-category-list.component';
@@ -32,6 +33,7 @@ const saleCategoriesRoutes: Routes = [
resolve: { resolve: {
item: SaleCategoryResolver, item: SaleCategoryResolver,
taxes: TaxListResolver, taxes: TaxListResolver,
products: ProductListResolver,
}, },
}, },
{ {
@@ -44,6 +46,7 @@ const saleCategoriesRoutes: Routes = [
resolve: { resolve: {
item: SaleCategoryResolver, item: SaleCategoryResolver,
taxes: TaxListResolver, taxes: TaxListResolver,
products: ProductListResolver,
}, },
}, },
]; ];
@@ -0,0 +1,24 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Product } from '../../core/product';
export class SaleCategoryDetailDatasource extends DataSource<Product> {
private data: Product[] = [];
constructor(private readonly dataObs: Observable<Product[]>) {
super();
this.dataObs = dataObs.pipe(
tap((x) => {
this.data = x;
}),
);
}
connect(): Observable<Product[]> {
return this.dataObs;
}
disconnect() {}
}
@@ -1,64 +1,101 @@
<div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card"> <div fxLayout="column">
<mat-card fxFlex> <div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card">
<mat-card-title-group> <mat-card fxFlex>
<mat-card-title>Sale Category</mat-card-title> <mat-card-title-group>
</mat-card-title-group> <mat-card-title>Sale Category</mat-card-title>
<mat-card-content> </mat-card-title-group>
<form [formGroup]="form" fxLayout="column"> <mat-card-content>
<div <form [formGroup]="form" fxLayout="column">
fxLayout="row" <div
fxLayoutAlign="space-around start" fxLayout="row"
fxLayout.lt-md="column" fxLayoutAlign="space-around start"
fxLayoutGap="20px" fxLayout.lt-md="column"
fxLayoutGap.lt-md="0px" fxLayoutGap="20px"
> fxLayoutGap.lt-md="0px"
<mat-form-field fxFlex> >
<mat-label>Name</mat-label> <mat-form-field fxFlex>
<input matInput #nameElement placeholder="Name" formControlName="name" /> <mat-label>Name</mat-label>
</mat-form-field> <input matInput #nameElement placeholder="Name" formControlName="name" />
</div> </mat-form-field>
<div </div>
fxLayout="row" <div
fxLayoutAlign="space-around start" fxLayout="row"
fxLayout.lt-md="column" fxLayoutAlign="space-around start"
fxLayoutGap="20px" fxLayout.lt-md="column"
fxLayoutGap.lt-md="0px" fxLayoutGap="20px"
> fxLayoutGap.lt-md="0px"
<mat-form-field fxFlex> >
<mat-label>Discount Limit</mat-label> <mat-form-field fxFlex>
<input <mat-label>Discount Limit</mat-label>
matInput <input
type="number" matInput
placeholder="Discount Limit" type="number"
formControlName="discountLimit" placeholder="Discount Limit"
class="right-align" formControlName="discountLimit"
/> class="right-align"
<span matSuffix>%</span> />
</mat-form-field> <span matSuffix>%</span>
</div> </mat-form-field>
<div </div>
fxLayout="row" <div
fxLayoutAlign="space-around start" fxLayout="row"
fxLayout.lt-md="column" fxLayoutAlign="space-around start"
fxLayoutGap="20px" fxLayout.lt-md="column"
fxLayoutGap.lt-md="0px" fxLayoutGap="20px"
> fxLayoutGap.lt-md="0px"
<mat-form-field fxFlex> >
<mat-label>Tax</mat-label> <mat-form-field fxFlex>
<mat-select placeholder="Tax" formControlName="tax"> <mat-label>Tax</mat-label>
<mat-option *ngFor="let t of taxes" [value]="t.id"> <mat-select placeholder="Tax" formControlName="tax">
{{ t.name }} <mat-option *ngFor="let t of taxes" [value]="t.id">
</mat-option> {{ t.name }}
</mat-select> </mat-option>
</mat-form-field> </mat-select>
</div> </mat-form-field>
</form> </div>
</mat-card-content> </form>
<mat-card-actions> </mat-card-content>
<button mat-raised-button color="primary" (click)="save()">Save</button> <mat-card-actions>
<button mat-raised-button color="warn" (click)="confirmDelete()" *ngIf="!!item.id"> <button mat-raised-button color="primary" (click)="save()">Save</button>
Delete <button mat-raised-button color="warn" (click)="confirmDelete()" *ngIf="!!item.id">
</button> Delete
</mat-card-actions> </button>
</mat-card> </mat-card-actions>
</mat-card>
</div>
<div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center">
<mat-card fxFlex>
<mat-card-title-group>
<mat-card-title>Products</mat-card-title>
</mat-card-title-group>
<mat-card-content>
<mat-table [dataSource]="dataSource" aria-label="Elements">
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
<mat-cell *matCellDef="let row"
><a [routerLink]="['/products', row.id]">{{ row.name }}</a></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>
</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>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</mat-card-content>
</mat-card>
</div>
</div> </div>
@@ -3,13 +3,17 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs'; import { round } from 'mathjs';
import { BehaviorSubject } from 'rxjs';
import { Product } from '../../core/product';
import { SaleCategory } from '../../core/sale-category'; import { SaleCategory } from '../../core/sale-category';
import { Tax } from '../../core/tax'; import { Tax } from '../../core/tax';
import { ToasterService } from '../../core/toaster.service'; import { ToasterService } from '../../core/toaster.service';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { SaleCategoryService } from '../sale-category.service'; import { SaleCategoryService } from '../sale-category.service';
import { SaleCategoryDetailDatasource } from './sale-category-detail-datasource';
@Component({ @Component({
selector: 'app-sale-category-detail', selector: 'app-sale-category-detail',
templateUrl: './sale-category-detail.component.html', templateUrl: './sale-category-detail.component.html',
@@ -20,6 +24,9 @@ export class SaleCategoryDetailComponent implements OnInit, AfterViewInit {
form: FormGroup; form: FormGroup;
taxes: Tax[] = []; taxes: Tax[] = [];
item: SaleCategory = new SaleCategory(); item: SaleCategory = new SaleCategory();
products: BehaviorSubject<Product[]> = new BehaviorSubject<Product[]>([]);
dataSource: SaleCategoryDetailDatasource = new SaleCategoryDetailDatasource(this.products);
displayedColumns = ['name', 'price', 'menuCategory'];
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@@ -39,9 +46,10 @@ export class SaleCategoryDetailComponent implements OnInit, AfterViewInit {
ngOnInit() { ngOnInit() {
this.route.data.subscribe((value) => { this.route.data.subscribe((value) => {
const data = value as { item: SaleCategory; taxes: Tax[] }; const data = value as { item: SaleCategory; taxes: Tax[]; products: Product[] };
this.showItem(data.item); this.showItem(data.item);
this.taxes = data.taxes; this.taxes = data.taxes;
this.products.next(data.products);
}); });
} }
@@ -3,6 +3,7 @@ import { MatTable } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { Product } from '../../core/product';
import { SaleCategory } from '../../core/sale-category'; import { SaleCategory } from '../../core/sale-category';
import { ToasterService } from '../../core/toaster.service'; import { ToasterService } from '../../core/toaster.service';
import { SaleCategoryService } from '../sale-category.service'; import { SaleCategoryService } from '../sale-category.service';