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

This commit is contained in:
2021-08-06 08:23:36 +05:30
parent 0b3ec5da16
commit 2f440a4127
8 changed files with 260 additions and 96 deletions

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,20 +299,19 @@ 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_ = []
with SessionFuture() as db: query = select(ProductVersion)
for item in ( if sc is not None:
db.execute( query = query.join(ProductVersion.menu_category)
select(ProductVersion) if mc is not None:
.join(ProductVersion.sale_category) query = query.join(ProductVersion.sale_category).join(SaleCategory.tax)
.join(SaleCategory.tax) query = query.where(
.where(
and_( and_(
ProductVersion.menu_category_id == mc,
or_( or_(
ProductVersion.valid_from == None, # noqa: E711 ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= date_, ProductVersion.valid_from <= date_,
@ -323,23 +322,85 @@ async def show_term(
), ),
) )
) )
.order_by(ProductVersion.sort_order, ProductVersion.name) if mc is not None:
.options( 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, innerjoin=True),
joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True), joinedload(ProductVersion.sale_category, SaleCategory.tax, innerjoin=True),
contains_eager(ProductVersion.sale_category), contains_eager(ProductVersion.sale_category),
contains_eager(ProductVersion.sale_category, SaleCategory.tax), 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),
) )
.scalars()
.all() with SessionFuture() as db:
): for item in db.execute(query).scalars().all():
if sc is not None:
list_.append(
{
"id": item.product_id,
"name": item.full_name,
"menuCategory": {
"id": item.menu_category_id,
"name": item.menu_category.name,
},
"price": item.price,
}
)
if mc is not None:
list_.append(query_product_info(item, False)) list_.append(query_product_info(item, False))
if item.has_happy_hour: if item.has_happy_hour:
list_.append(query_product_info(item, True)) list_.append(query_product_info(item, True))
return list_ return list_
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_,
),
)
)
.order_by(MenuCategory.sort_order)
.order_by(MenuCategory.name)
.order_by(ProductVersion.sort_order)
.order_by(ProductVersion.name)
.options(
joinedload(ProductVersion.menu_category, innerjoin=True),
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)
def show_id( def show_id(
id_: uuid.UUID, id_: uuid.UUID,

View File

@ -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

View File

@ -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);
}
}

View File

@ -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,
}, },
}, },
]; ];

View File

@ -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() {}
}

View File

@ -1,3 +1,4 @@
<div fxLayout="column">
<div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card"> <div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card">
<mat-card fxFlex> <mat-card fxFlex>
<mat-card-title-group> <mat-card-title-group>
@ -62,3 +63,39 @@
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </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>

View File

@ -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);
}); });
} }

View File

@ -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';