diff --git a/barker/models/master.py b/barker/models/master.py index 15212a0..f0f6268 100644 --- a/barker/models/master.py +++ b/barker/models/master.py @@ -89,7 +89,6 @@ class MenuCategory(Base): id = Column("id", GUID(), primary_key=True, default=uuid.uuid4) name = Column("name", Unicode(255), nullable=False, unique=True) discount_limit = Column("discount_limit", Numeric, nullable=False) - is_modifier_compulsory = Column("is_modifier_compulsory", Boolean, nullable=False) is_active = Column("is_active", Boolean, nullable=False) is_fixture = Column("is_fixture", Boolean, nullable=False) @@ -99,7 +98,6 @@ class MenuCategory(Base): self, name, discount_limit, - is_modifier_compulsory, is_active, sort_order, is_fixture=False, @@ -107,7 +105,6 @@ class MenuCategory(Base): ): self.name = name self.discount_limit = discount_limit - self.is_modifier_compulsory = is_modifier_compulsory self.is_active = is_active self.sort_order = sort_order self.is_fixture = is_fixture @@ -152,6 +149,7 @@ class Product(Base): menu_category = relationship("MenuCategory", backref="products") sale_category = relationship("SaleCategory", backref="products") + modifier_categories = relationship("ModifierCategory", secondary="modifier_categories_products", order_by="ModifierCategory.sort_order", backref="products") def __init__( self, @@ -218,8 +216,6 @@ class ModifierCategory(Base): maximum = Column("maximum", Integer, nullable=True) sort_order = Column("sort_order", Numeric, nullable=False) - products = relationship("Product", secondary=modifier_categories_products, backref="modifier_categories") - def __init__(self, name=None, minimum=None, maximum=None, sort_order=0, id=None): self.id = id self.name = name diff --git a/barker/routes.py b/barker/routes.py index c57d0cf..5bbaf9e 100644 --- a/barker/routes.py +++ b/barker/routes.py @@ -27,10 +27,6 @@ def includeme(config): config.add_route('merge_kot', '/MergeKot.json') config.add_route('merge_table', '/MergeTable.json') - config.add_route('modifier', '/Modifier.json') - config.add_route('modifier_list', '/Modifiers.json') - config.add_route('modifier_id', '/Modifier/{id}.json') - config.add_route('move_kot', '/MoveKot.json') config.add_route('move_table', '/MoveTable.json') @@ -69,7 +65,13 @@ def includeme(config): config.add_route("v1_modifier_categories_new", "/v1/modifier-categories/new") config.add_route("v1_modifier_categories_id", "/v1/modifier-categories/{id}") config.add_route("v1_modifier_categories_list", "/v1/modifier-categories") - config.add_route("v1_modifier_category_types_list", "/v1/modifier-category-types") + + config.add_route("modifiers_new", "/modifiers/new") + config.add_route("modifiers_id", "/modifiers/{id}") + config.add_route("modifiers_list", "/modifiers") + config.add_route("v1_modifiers_new", "/v1/modifiers/new") + config.add_route("v1_modifiers_id", "/v1/modifiers/{id}") + config.add_route("v1_modifiers_list", "/v1/modifiers") config.add_route("v1_bills_new", "/v1/bills/new") config.add_route("v1_bills_id", "/v1/bills/{id}") diff --git a/barker/views/menu_category.py b/barker/views/menu_category.py index cf3e6a7..b456575 100644 --- a/barker/views/menu_category.py +++ b/barker/views/menu_category.py @@ -28,9 +28,8 @@ def save(request): raise ValidationError("Tax Rate must be a decimal >= 0 and <= 100") except (ValueError, InvalidOperation): raise ValidationError("Tax Rate must be a decimal >= 0 and <= 100") - is_modifier_compulsory = json.get("isModifierCompulsory", True) is_active = json.get("isActive", True) - item = MenuCategory(name, discount_limit, is_modifier_compulsory, is_active, 0) + item = MenuCategory(name, discount_limit, is_active, 0) request.dbsession.add(item) transaction.commit() return menu_category_info(item.id, request.dbsession) @@ -61,7 +60,6 @@ def update(request): raise ValidationError("Tax Rate must be a decimal >= 0 and <= 100") except (ValueError, InvalidOperation): raise ValidationError("Tax Rate must be a decimal >= 0 and <= 100") - item.is_modifier_compulsory = json.get("isModifierCompulsory", True) item.is_active = json.get("isActive", True) transaction.commit() return menu_category_info(item.id, request.dbsession) @@ -202,7 +200,6 @@ def menu_category_info(item, dbsession): return { "name": "", "discountLimit": 0, - "isModifierCompulsory": False, "isActive": True, "isFixture": False, "sortOrder": 0, @@ -212,8 +209,7 @@ def menu_category_info(item, dbsession): return { "id": item.id, "name": item.name, - "discountLimit": item.discount_limit, - "isModifierCompulsory": item.is_modifier_compulsory, + "discountLimit": item.discount_limit * 100, "isActive": item.is_active, "isFixture": item.is_fixture, "sortOrder": item.sort_order, diff --git a/barker/views/modifier.py b/barker/views/modifier.py index d1ef96d..734d0d8 100644 --- a/barker/views/modifier.py +++ b/barker/views/modifier.py @@ -1,117 +1,137 @@ import uuid +from decimal import Decimal, InvalidOperation import transaction from pyramid.view import view_config -from sqlalchemy import or_ -from barker.models import Modifier, ModifierCategory +from barker.models import Modifier +from barker.models.validation_exception import ValidationError -@view_config(request_method='POST', route_name='modifier', renderer='json', permission='Modifiers', trans=True) +@view_config( + request_method="POST", + route_name="v1_modifiers_new", + renderer="json", + permission="Modifiers", + trans=True, +) def save(request): json = request.json_body - item = Modifier(json['Name'], json['ShowInBill'], json['Price']) + + name = json.get("name", "").strip() + if name == "": + raise ValidationError("Name cannot be blank") + show_in_bill = json.get("showInBill", False) + try: + price = Decimal(json.get("price", 0)) + if price < 0: + raise ValidationError("Price must be a decimal >= 0") + except (ValueError, InvalidOperation): + raise ValidationError("Price must be a decimal >= 0") + modifier_category = json.get("modifierCategory", None) + if modifier_category is None: + raise ValidationError("please choose a modifier category") + modifier_category_id = uuid.UUID(modifier_category["id"]) + item = Modifier(name, show_in_bill, price, modifier_category_id) request.dbsession.add(item) - add_groups(item, json['MenuCategoryModifiers'], request.dbsession) transaction.commit() - item = request.dbsession.query(Modifier).filter(Modifier.id == item.id).first() - return modifier_info(item) + return modifier_info(item.id, request.dbsession) -@view_config(request_method='PUT', route_name='modifier_id', renderer='json', permission='Modifiers', trans=True) +@view_config( + request_method="PUT", + route_name="v1_modifiers_id", + renderer="json", + permission="Modifiers", + trans=True, +) def update(request): + item = ( + request.dbsession.query(Modifier) + .filter(Modifier.id == uuid.UUID(request.matchdict["id"])) + .first() + ) json = request.json_body - item = request.dbsession.query(Modifier).filter(Modifier.id == uuid.UUID(request.matchdict['id'])).first() - item.name = json['Name'] - item.show_in_bill = json['ShowInBill'] - item.price = json['Price'] - add_groups(item, json['MenuCategoryModifiers'], request.dbsession) + item.name = json["name"].strip() + if item.name == "": + raise ValidationError("Name cannot be blank") + item.show_in_bill = json["showInBill"] + try: + item.price = Decimal(json["price"]) + if item.price < 0: + raise ValidationError("Price must be a decimal >= 0") + except (ValueError, InvalidOperation): + raise ValidationError("Price must be a decimal >= 0") + item.modifier_category_id = uuid.UUID(json["modifierCategory"]["id"]) transaction.commit() - item = request.dbsession.query(Modifier).filter(Modifier.id == item.id).first() - return modifier_info(item) + return modifier_info(item.id, request.dbsession) -@view_config(request_method='DELETE', route_name='modifier_id', renderer='json', permission='Modifiers', trans=True) +@view_config( + request_method="DELETE", + route_name="v1_modifiers_id", + renderer="json", + permission="Modifiers", + trans=True, +) def delete(request): - item = request.dbsession.query(Modifier).filter(Modifier.id == uuid.UUID(request.matchdict['id'])).first() + item = ( + request.dbsession.query(Modifier) + .filter(Modifier.id == uuid.UUID(request.matchdict["id"])) + .first() + ) request.dbsession.delete(item) transaction.commit() return {} -@view_config(request_method='GET', route_name='modifier_id', renderer='json', permission='Authenticated') +@view_config( + request_method="GET", + route_name="v1_modifiers_new", + renderer="json", + permission="Authenticated", +) +def show_blank(request): + return modifier_info(None, request.dbsession) + + +@view_config( + request_method="GET", + route_name="v1_modifiers_id", + renderer="json", + permission="Authenticated", +) def show_id(request): - id = uuid.UUID(request.matchdict['id']) - modifier = request.dbsession.query(Modifier).filter(Modifier.id == id).first() - return modifier_info(modifier) + id_ = uuid.UUID(request.matchdict["id"]) + return modifier_info(id_, request.dbsession) -@view_config(request_method='GET', route_name='modifier_list', renderer='json', permission='Authenticated') +@view_config( + request_method="GET", + route_name="v1_modifiers_list", + renderer="json", + permission="Authenticated", +) def show_list(request): - list = request.dbsession.query(Modifier).order_by(Modifier.name).all() - modifiers = [] - for item in list: - modifiers.append({ - 'ModifierID': item.id, - 'Name': item.name, - 'ShowInBill': item.show_in_bill, - 'Price': item.price - }) - return modifiers + return [ + modifier_info(item, request.dbsession) + for item in request.dbsession.query(Modifier).order_by(Modifier.name).all() + ] -@view_config(request_method='GET', route_name='modifier_list', renderer='json', request_param='pg', - permission='Authenticated') -def show_for_pg(request): - menu_category_id = uuid.UUID(request.GET['pg']) - pgm_list = request.dbsession.query( - MenuCategoryModifier - ).filter( - or_(MenuCategoryModifier.menu_category == None, MenuCategoryModifier.menu_category_id == menu_category_id) - ).all() - - pg_modifiers = [] - for item in pgm_list: - pg_modifiers.append({ - 'ModifierID': item.modifier_id, - 'Name': item.modifier.name, - 'ShowInBill': item.modifier.show_in_bill, - 'Price': item.modifier.price - }) - return pg_modifiers - - -def modifier_info(modifier): +def modifier_info(item, dbsession): + if item is None: + return {"name": "", "showInBill": False, "price": 0, "modifierCategory": {}} + if type(item) is uuid.UUID: + item = dbsession.query(Modifier).filter(Modifier.id == item).first() return { - 'id': modifier.id, - 'name': modifier.name, - 'showInBill': modifier.show_in_bill, - 'price': modifier.price, - 'menuCategoryModifiers': [ - { - 'id': i.id, - 'menuCategory': {'id': i.menu_category_id} if i.menu_category_id is not None else None, - 'showAutomatically': i.show_automatically - } for i in modifier.menu_categories] + "id": item.id, + "name": item.name, + "showInBill": item.show_in_bill, + "price": item.price, + "modifierCategory": { + "id": item.modifier_category.id, + "name": item.modifier_category.name, + }, } - - -def add_groups(modifier, groups, dbsession): - all = [p for p in groups if p['MenuCategory'] is None] - if len(all) > 0: - old = [p for p in modifier.menu_categories if p.menu_category_id is None] - if len(old) != 1: - for item in modifier.menu_categories: - dbsession.delete(item) - modifier.menu_categories.clear() - modifier.menu_categories.append(MenuCategoryModifier(None, None, False)) - else: - new = {uuid.UUID(p['menuCategory']['id']) for p in groups} - old = {p.menu_category_id for p in modifier.menu_categories} - for pg_id in new - old: - modifier.menu_categories.append(MenuCategoryModifier(pg_id, None, False)) - - gp = [p for p in modifier.menu_categories if p.menu_category_id in (old - new)] - for item in gp: - dbsession.delete(item) diff --git a/barker/views/modifier_category.py b/barker/views/modifier_category.py index 97ee1d7..26f20a2 100644 --- a/barker/views/modifier_category.py +++ b/barker/views/modifier_category.py @@ -4,7 +4,6 @@ import transaction from pyramid.view import view_config from sqlalchemy import or_ -from sqlalchemy.orm import joinedload from barker.models import ModifierCategory, Product from barker.models.validation_exception import ValidationError @@ -157,33 +156,17 @@ def show_list(request): request_method="GET", route_name="v1_modifier_categories_list", renderer="json", - request_param="pg", + request_param="p", permission="Authenticated", ) def show_for_pg(request): - menu_category_id = uuid.UUID(request.GET["pg"]) - pgm_list = ( - request.dbsession.query(MenuCategoryModifier) - .filter( - or_( - MenuCategoryModifier.menu_category == None, - MenuCategoryModifier.menu_category_id == menu_category_id, - ) - ) - .all() - ) + product_id = uuid.UUID(request.GET["p"]) + product = request.dbsession.query(Product).filter(Product.id == product_id).first() - pg_modifiers = [] - for item in pgm_list: - pg_modifiers.append( - { - "ModifierID": item.modifier_id, - "Name": item.modifier.name, - "ShowInBill": item.modifier.show_in_bill, - "Price": item.modifier.price, - } - ) - return pg_modifiers + return [ + modifier_category_info(item, request.dbsession) + for item in product.modifier_categories + ] def modifier_category_info(item, dbsession): diff --git a/barker/views/product.py b/barker/views/product.py index 11e04e8..4dff0e0 100644 --- a/barker/views/product.py +++ b/barker/views/product.py @@ -31,7 +31,7 @@ def save(request): menu_category_id = uuid.UUID(menu_category["id"]) sale_category = json.get("saleCategory", None) - if menu_category is None: + if sale_category is None: raise ValidationError("please choose a sale category") sale_category_id = uuid.UUID(sale_category["id"]) diff --git a/bookie/src/app/app-routing.module.ts b/bookie/src/app/app-routing.module.ts index e74afdc..4ca38c6 100644 --- a/bookie/src/app/app-routing.module.ts +++ b/bookie/src/app/app-routing.module.ts @@ -13,6 +13,10 @@ const routes: Routes = [ path: 'guest-book', loadChildren: () => import('./guest-book/guest-book.module').then(mod => mod.GuestBookModule) }, + { + path: 'modifiers', + loadChildren: () => import('./modifiers/modifiers.module').then(mod => mod.ModifiersModule) + }, { path: 'modifier-categories', loadChildren: () => import('./modifier-categories/modifier-categories.module').then(mod => mod.ModifierCategoriesModule) diff --git a/bookie/src/app/core/menu-category.ts b/bookie/src/app/core/menu-category.ts index 27735f1..349aff7 100644 --- a/bookie/src/app/core/menu-category.ts +++ b/bookie/src/app/core/menu-category.ts @@ -4,7 +4,6 @@ export class MenuCategory { id: string; name: string; discountLimit: number; - isModifierCompulsory: boolean; isActive: boolean; isFixture: boolean; sortOrder: number; diff --git a/bookie/src/app/modifier-categories/modifier-category.ts b/bookie/src/app/core/modifier-category.ts similarity index 72% rename from bookie/src/app/modifier-categories/modifier-category.ts rename to bookie/src/app/core/modifier-category.ts index 2bb960f..38ae814 100644 --- a/bookie/src/app/modifier-categories/modifier-category.ts +++ b/bookie/src/app/core/modifier-category.ts @@ -1,4 +1,4 @@ -import {MenuCategory} from "../core/menu-category"; +import {MenuCategory} from "./menu-category"; export class ModifierCategory { id: string; diff --git a/bookie/src/app/core/modifier.ts b/bookie/src/app/core/modifier.ts new file mode 100644 index 0000000..c5fcd74 --- /dev/null +++ b/bookie/src/app/core/modifier.ts @@ -0,0 +1,9 @@ +import {ModifierCategory} from "./modifier-category"; + +export class Modifier { + id: string; + name: string; + showInBill: boolean; + price: number; + modifierCategory: ModifierCategory; +} diff --git a/bookie/src/app/home/home.component.html b/bookie/src/app/home/home.component.html index 43ad38b..30ea689 100644 --- a/bookie/src/app/home/home.component.html +++ b/bookie/src/app/home/home.component.html @@ -22,6 +22,9 @@ Modifier Categories + + Modifiers + Taxes diff --git a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html index ea29386..82efde6 100644 --- a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html +++ b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.html @@ -22,7 +22,6 @@
- Is Modifier Compulsory? Is Active?
diff --git a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.ts b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.ts index 7c9c309..d7434b8 100644 --- a/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.ts +++ b/bookie/src/app/menu-category/menu-category-detail/menu-category-detail.component.ts @@ -33,7 +33,6 @@ export class MenuCategoryDetailComponent implements OnInit, AfterViewInit { this.form = this.fb.group({ name: '', discountLimit: '', - isModifierCompulsory: '', isActive: '' }); } @@ -50,7 +49,6 @@ export class MenuCategoryDetailComponent implements OnInit, AfterViewInit { this.form.setValue({ name: this.item.name, discountLimit: this.item.discountLimit, - isModifierCompulsory: this.item.isModifierCompulsory, isActive: this.item.isActive }); } @@ -104,7 +102,6 @@ export class MenuCategoryDetailComponent implements OnInit, AfterViewInit { const formModel = this.form.value; this.item.name = formModel.name; this.item.discountLimit = +formModel.discountLimit; - this.item.isModifierCompulsory = formModel.isModifierCompulsory; this.item.isActive = formModel.isActive; return this.item; } diff --git a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.html b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.html index 7f8a2f3..0b5f832 100644 --- a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.html +++ b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.html @@ -24,12 +24,6 @@ {{row.discountLimit | percent:'1.2-2'}} - - - Modifier Compulsory? - {{row.isModifierCompulsory}} - - Active? diff --git a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts index a2fe925..4ce0b08 100644 --- a/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts +++ b/bookie/src/app/menu-category/menu-category-list/menu-category-list.component.ts @@ -19,7 +19,7 @@ export class MenuCategoryListComponent implements OnInit { list: MenuCategory[]; data: BehaviorSubject; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'discountLimit', 'isModifierCompulsory', 'isActive', 'isFixture']; + displayedColumns = ['name', 'discountLimit', 'isActive', 'isFixture']; constructor( private route: ActivatedRoute, @@ -36,6 +36,7 @@ export class MenuCategoryListComponent implements OnInit { ngOnInit() { this.route.data .subscribe((data: { list: MenuCategory[] }) => { + data.list.forEach(x=> x.discountLimit = x.discountLimit / 100); this.data.next(data.list); }); this.dataSource = new MenuCategoryListDatasource(this.data); diff --git a/bookie/src/app/modifier-categories/modifier-category-detail/modifier-category-detail.component.ts b/bookie/src/app/modifier-categories/modifier-category-detail/modifier-category-detail.component.ts index 0f1d927..7bc10f7 100644 --- a/bookie/src/app/modifier-categories/modifier-category-detail/modifier-category-detail.component.ts +++ b/bookie/src/app/modifier-categories/modifier-category-detail/modifier-category-detail.component.ts @@ -1,7 +1,7 @@ import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {ModifierCategoryService} from '../modifier-category.service'; -import {ModifierCategory} from '../modifier-category'; +import {ModifierCategory} from '../../core/modifier-category'; import {ToasterService} from '../../core/toaster.service'; import {ConfirmDialogComponent} from '../../shared/confirm-dialog/confirm-dialog.component'; import {MatDialog} from '@angular/material/dialog'; diff --git a/bookie/src/app/modifier-categories/modifier-category-list-resolver.service.ts b/bookie/src/app/modifier-categories/modifier-category-list-resolver.service.ts index a7f43bd..393aaa5 100644 --- a/bookie/src/app/modifier-categories/modifier-category-list-resolver.service.ts +++ b/bookie/src/app/modifier-categories/modifier-category-list-resolver.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router'; -import {ModifierCategory} from './modifier-category'; +import {ModifierCategory} from '../core/modifier-category'; import {Observable} from 'rxjs/internal/Observable'; import {ModifierCategoryService} from './modifier-category.service'; diff --git a/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list-datasource.ts b/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list-datasource.ts index 63e7be8..7d225f3 100644 --- a/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list-datasource.ts +++ b/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list-datasource.ts @@ -1,6 +1,6 @@ import { DataSource } from '@angular/cdk/collections'; import { Observable, of as observableOf } from 'rxjs'; -import { ModifierCategory } from '../modifier-category'; +import { ModifierCategory } from '../../core/modifier-category'; export class ModifierCategoryListDatasource extends DataSource { diff --git a/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list.component.ts b/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list.component.ts index c30ab6c..3a2d926 100644 --- a/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list.component.ts +++ b/bookie/src/app/modifier-categories/modifier-category-list/modifier-category-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ModifierCategoryListDatasource } from './modifier-category-list-datasource'; -import { ModifierCategory } from '../modifier-category'; +import { ModifierCategory } from '../../core/modifier-category'; import { ActivatedRoute } from '@angular/router'; @Component({ diff --git a/bookie/src/app/modifier-categories/modifier-category-resolver.service.ts b/bookie/src/app/modifier-categories/modifier-category-resolver.service.ts index 203b791..b2d95c1 100644 --- a/bookie/src/app/modifier-categories/modifier-category-resolver.service.ts +++ b/bookie/src/app/modifier-categories/modifier-category-resolver.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router'; -import {ModifierCategory} from './modifier-category'; +import {ModifierCategory} from '../core/modifier-category'; import {Observable} from 'rxjs/internal/Observable'; import {ModifierCategoryService} from './modifier-category.service'; diff --git a/bookie/src/app/modifier-categories/modifier-category.service.ts b/bookie/src/app/modifier-categories/modifier-category.service.ts index a780244..04902b6 100644 --- a/bookie/src/app/modifier-categories/modifier-category.service.ts +++ b/bookie/src/app/modifier-categories/modifier-category.service.ts @@ -3,7 +3,7 @@ import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import {ErrorLoggerService} from '../core/error-logger.service'; import {catchError} from 'rxjs/operators'; import {Observable} from 'rxjs/internal/Observable'; -import {ModifierCategory} from './modifier-category'; +import {ModifierCategory} from '../core/modifier-category'; const httpOptions = { headers: new HttpHeaders({'Content-Type': 'application/json'}) diff --git a/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.css b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.css new file mode 100644 index 0000000..e69de29 diff --git a/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.html b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.html new file mode 100644 index 0000000..3976803 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.html @@ -0,0 +1,44 @@ +
+ + + Modifier + + +
+
+ + Name + + +
+
+ Show In Bill? +
+
+ + Price + + +
+
+ + Category + + + {{ mc.name }} + + + +
+
+
+ + + + +
+
diff --git a/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.spec.ts b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.spec.ts new file mode 100644 index 0000000..d6417cc --- /dev/null +++ b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {ModifierDetailComponent} from './modifier-detail.component'; + +describe('ModifierDetailComponent', () => { + let component: ModifierDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ModifierDetailComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ModifierDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.ts b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.ts new file mode 100644 index 0000000..a9148a5 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-detail/modifier-detail.component.ts @@ -0,0 +1,113 @@ +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { ToasterService } from '../../core/toaster.service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ModifierService } from '../modifier.service'; +import { Modifier } from '../../core/modifier'; +import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { ModifierCategory } from "../../core/modifier-category"; + +@Component({ + selector: 'app-modifier-detail', + templateUrl: './modifier-detail.component.html', + styleUrls: ['./modifier-detail.component.css'] +}) +export class ModifierDetailComponent implements OnInit, AfterViewInit { + @ViewChild('name', { static: true }) nameElement: ElementRef; + form: FormGroup; + modifierCategories: ModifierCategory[]; + item: Modifier; + + constructor( + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private fb: FormBuilder, + private toaster: ToasterService, + private ser: ModifierService + ) { + this.createForm(); + } + + createForm() { + this.form = this.fb.group({ + name: '', + showInBill: '', + price: '', + modifierCategory: '' + }); + } + + ngOnInit() { + this.route.data + .subscribe((data: { item: Modifier, modifierCategories: ModifierCategory[] }) => { + this.modifierCategories = data.modifierCategories; + this.showItem(data.item); + }); + } + + showItem(item: Modifier) { + this.item = item; + this.form.setValue({ + name: this.item.name || '', + showInBill: this.item.showInBill, + price: this.item.price || '', + modifierCategory: this.item.modifierCategory.id ? this.item.modifierCategory.id : '' + }); + } + + ngAfterViewInit() { + setTimeout(() => { + this.nameElement.nativeElement.focus(); + }, 0); + } + + save() { + this.ser.saveOrUpdate(this.getItem()) + .subscribe( + (result) => { + this.toaster.show('Success', ''); + this.router.navigateByUrl('/modifiers'); + }, + (error) => { + this.toaster.show('Danger', error.error); + } + ); + } + + delete() { + this.ser.delete(this.item.id) + .subscribe( + (result) => { + this.toaster.show('Success', ''); + this.router.navigateByUrl('/modifiers'); + }, + (error) => { + this.toaster.show('Danger', error.error); + } + ); + } + + confirmDelete(): void { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + width: '250px', + data: {title: 'Delete Modifier?', content: 'Are you sure? This cannot be undone.'} + }); + + dialogRef.afterClosed().subscribe((result: boolean) => { + if (result) { + this.delete(); + } + }); + } + + getItem(): Modifier { + const formModel = this.form.value; + this.item.name = formModel.name; + this.item.showInBill = formModel.showInBill; + this.item.price = +formModel.price; + this.item.modifierCategory.id = formModel.modifierCategory; + return this.item; + } +} diff --git a/bookie/src/app/modifiers/modifier-list-resolver.service.spec.ts b/bookie/src/app/modifiers/modifier-list-resolver.service.spec.ts new file mode 100644 index 0000000..f0c6ea8 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list-resolver.service.spec.ts @@ -0,0 +1,15 @@ +import {inject, TestBed} from '@angular/core/testing'; + +import {ModifierListResolver} from './modifier-list-resolver.service'; + +describe('ModifierListResolver', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ModifierListResolver] + }); + }); + + it('should be created', inject([ModifierListResolver], (service: ModifierListResolver) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/bookie/src/app/modifiers/modifier-list-resolver.service.ts b/bookie/src/app/modifiers/modifier-list-resolver.service.ts new file mode 100644 index 0000000..5e5485e --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list-resolver.service.ts @@ -0,0 +1,18 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router'; +import {Modifier} from '../core/modifier'; +import {Observable} from 'rxjs/internal/Observable'; +import {ModifierService} from './modifier.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ModifierListResolver implements Resolve { + + constructor(private ser: ModifierService) { + } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.ser.list(); + } +} diff --git a/bookie/src/app/modifiers/modifier-list/modifier-list-datasource.ts b/bookie/src/app/modifiers/modifier-list/modifier-list-datasource.ts new file mode 100644 index 0000000..91c59ec --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list/modifier-list-datasource.ts @@ -0,0 +1,48 @@ +import { DataSource } from '@angular/cdk/collections'; +import { map, tap } from 'rxjs/operators'; +import { merge, Observable } from 'rxjs'; +import { Modifier } from '../../core/modifier'; + + +export class ModifierListDataSource extends DataSource { + public data: Modifier[]; + public viewData: Modifier[]; + private filterValue: string; + + constructor(private readonly filter: Observable, private readonly dataObs: Observable) { + super(); + this.data = []; + this.viewData = []; + this.filter = filter.pipe( + tap(x => this.filterValue = x) + ); + this.dataObs = dataObs.pipe( + tap(x => this.data = x) + ); + } + + connect(): Observable { + const dataMutations = [ + this.dataObs, + this.filter + ]; + + return merge(...dataMutations).pipe( + map((x: any) => { + this.viewData = this.getFilteredData([...this.data]); + return this.viewData; + }) + ); + } + + disconnect() { + } + + private getFilteredData(data: Modifier[]): Modifier[] { + const filter = (this.filterValue === undefined) ? "" : this.filterValue; + return data.filter(x => { + return x.modifierCategory.id === filter || filter === ""; + } + ); + } +} diff --git a/bookie/src/app/modifiers/modifier-list/modifier-list.component.css b/bookie/src/app/modifiers/modifier-list/modifier-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/bookie/src/app/modifiers/modifier-list/modifier-list.component.html b/bookie/src/app/modifiers/modifier-list/modifier-list.component.html new file mode 100644 index 0000000..373877a --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list/modifier-list.component.html @@ -0,0 +1,64 @@ + + + Modifiers + + add_box + Add + + + +
+
+ + Modifier Category + + -- All Categories -- + + {{ mc.name }} + + + +
+
+ + + + + Name + {{row.name}} ({{row.units}}) + + + + + Show + +
+
+ + {{ row.showInBill ? "visibility" : "visibility_off" }} + + {{ row.showInBill ? "Show" : "Hide" }} +
+
+
+
+ + + + Price + {{row.price | currency:'INR'}} + + + + + Category + {{row.modifierCategory.name}} + + + + + +
+
+
diff --git a/bookie/src/app/modifiers/modifier-list/modifier-list.component.spec.ts b/bookie/src/app/modifiers/modifier-list/modifier-list.component.spec.ts new file mode 100644 index 0000000..bd1585e --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list/modifier-list.component.spec.ts @@ -0,0 +1,23 @@ +import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; + +import {ModifierListComponent} from './modifier-list.component'; + +describe('ModifierListComponent', () => { + let component: ModifierListComponent; + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + declarations: [ModifierListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModifierListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/bookie/src/app/modifiers/modifier-list/modifier-list.component.ts b/bookie/src/app/modifiers/modifier-list/modifier-list.component.ts new file mode 100644 index 0000000..2ecfbf5 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-list/modifier-list.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { ModifierListDataSource } from './modifier-list-datasource'; +import { MatTable } from "@angular/material"; +import { Modifier } from '../../core/modifier'; +import { BehaviorSubject } from "rxjs"; +import {ModifierCategory} from "../../core/modifier-category"; + +@Component({ + selector: 'app-modifier-list', + templateUrl: './modifier-list.component.html', + styleUrls: ['./modifier-list.component.css'] +}) +export class ModifierListComponent implements OnInit { + @ViewChild('table', { static: true }) table: MatTable; + dataSource: ModifierListDataSource; + filter: BehaviorSubject; + form: FormGroup; + list: Modifier[]; + data: BehaviorSubject; + modifierCategories: ModifierCategory[]; + /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ + displayedColumns: string[] = ['name', 'showInBill', 'price', 'modifierCategory']; + + constructor( + private route: ActivatedRoute, + private fb: FormBuilder, + private router: Router + ) { + this.form = this.fb.group({ + modifierCategory: '' + }); + this.filter = new BehaviorSubject(""); + this.data = new BehaviorSubject([]); + this.data.subscribe((data: Modifier[]) => { + this.list = data; + }) + } + + filterOn(val: any) { + this.filter.next(val.value); + } + + ngOnInit() { + this.route.data + .subscribe((data: { list: Modifier[], modifierCategories: ModifierCategory[] }) => { + this.data.next(data.list); + this.modifierCategories = data.modifierCategories; + }); + this.dataSource = new ModifierListDataSource(this.filter, this.data); + } +} diff --git a/bookie/src/app/modifiers/modifier-resolver.service.spec.ts b/bookie/src/app/modifiers/modifier-resolver.service.spec.ts new file mode 100644 index 0000000..b65e093 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-resolver.service.spec.ts @@ -0,0 +1,15 @@ +import {inject, TestBed} from '@angular/core/testing'; + +import {ModifierResolver} from './modifier-resolver.service'; + +describe('ModifierResolver', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ModifierResolver] + }); + }); + + it('should be created', inject([ModifierResolver], (service: ModifierResolver) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/bookie/src/app/modifiers/modifier-resolver.service.ts b/bookie/src/app/modifiers/modifier-resolver.service.ts new file mode 100644 index 0000000..3f79f99 --- /dev/null +++ b/bookie/src/app/modifiers/modifier-resolver.service.ts @@ -0,0 +1,19 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router'; +import {ModifierService} from './modifier.service'; +import {Modifier} from '../core/modifier'; +import {Observable} from 'rxjs/internal/Observable'; + +@Injectable({ + providedIn: 'root' +}) +export class ModifierResolver implements Resolve { + + constructor(private ser: ModifierService, private router: Router) { + } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + const id = route.paramMap.get('id'); + return this.ser.get(id); + } +} diff --git a/bookie/src/app/modifiers/modifier.service.spec.ts b/bookie/src/app/modifiers/modifier.service.spec.ts new file mode 100644 index 0000000..5b81462 --- /dev/null +++ b/bookie/src/app/modifiers/modifier.service.spec.ts @@ -0,0 +1,15 @@ +import {inject, TestBed} from '@angular/core/testing'; + +import {ModifierService} from './modifier.service'; + +describe('ModifierService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ModifierService] + }); + }); + + it('should be created', inject([ModifierService], (service: ModifierService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/bookie/src/app/modifiers/modifier.service.ts b/bookie/src/app/modifiers/modifier.service.ts new file mode 100644 index 0000000..ee422bd --- /dev/null +++ b/bookie/src/app/modifiers/modifier.service.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { catchError } from 'rxjs/operators'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Modifier } from '../core/modifier'; +import { ErrorLoggerService } from '../core/error-logger.service'; + +const httpOptions = { + headers: new HttpHeaders({'Content-Type': 'application/json'}) +}; + +const url = '/v1/modifiers'; +const serviceName = 'ModifierService'; + +@Injectable({providedIn: 'root'}) +export class ModifierService { + + constructor(private http: HttpClient, private log: ErrorLoggerService) { + } + + get(id: string): Observable { + const getUrl: string = (id === null) ? `${url}/new` : `${url}/${id}`; + return >this.http.get(getUrl) + .pipe( + catchError(this.log.handleError(serviceName, `get id=${id}`)) + ); + } + + list(): Observable { + const options = {params: new HttpParams().set('l', '')}; + return >this.http.get(url, options) + .pipe( + catchError(this.log.handleError(serviceName, 'getList')) + ); + } + + save(modifier: Modifier): Observable { + return >this.http.post(`${url}/new`, modifier, httpOptions) + .pipe( + catchError(this.log.handleError(serviceName, 'save')) + ); + } + + update(modifier: Modifier): Observable { + return >this.http.put(`${url}/${modifier.id}`, modifier, httpOptions) + .pipe( + catchError(this.log.handleError(serviceName, 'update')) + ); + } + + updateSortOrder(list: Modifier[]): Observable { + return >this.http.post(url, list, httpOptions) + .pipe( + catchError(this.log.handleError(serviceName, 'updateSortOrder')) + ); + } + + saveOrUpdate(modifier: Modifier): Observable { + if (!modifier.id) { + return this.save(modifier); + } else { + return this.update(modifier); + } + } + + delete(id: string): Observable { + return >this.http.delete(`${url}/${id}`, httpOptions) + .pipe( + catchError(this.log.handleError(serviceName, 'delete')) + ); + } + + autocomplete(term: string): Observable { + const options = {params: new HttpParams().set('t', term)}; + return >this.http.get(url, options) + .pipe( + catchError(this.log.handleError(serviceName, 'autocomplete')) + ); + } + + balance(id: string, date: string): Observable { + const options = {params: new HttpParams().set('b', 'true').set('d', date)}; + return >this.http.get(`${url}/${id}`, options) + .pipe( + catchError(this.log.handleError(serviceName, 'balance')) + ); + } +} diff --git a/bookie/src/app/modifiers/modifiers-routing.module.spec.ts b/bookie/src/app/modifiers/modifiers-routing.module.spec.ts new file mode 100644 index 0000000..c5c53a4 --- /dev/null +++ b/bookie/src/app/modifiers/modifiers-routing.module.spec.ts @@ -0,0 +1,13 @@ +import {ModifiersRoutingModule} from './modifiers-routing.module'; + +describe('ModifiersRoutingModule', () => { + let modifiersRoutingModule: ModifiersRoutingModule; + + beforeEach(() => { + modifiersRoutingModule = new ModifiersRoutingModule(); + }); + + it('should create an instance', () => { + expect(modifiersRoutingModule).toBeTruthy(); + }); +}); diff --git a/bookie/src/app/modifiers/modifiers-routing.module.ts b/bookie/src/app/modifiers/modifiers-routing.module.ts new file mode 100644 index 0000000..8bbad73 --- /dev/null +++ b/bookie/src/app/modifiers/modifiers-routing.module.ts @@ -0,0 +1,67 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, Routes } from '@angular/router'; + +import { ModifierListResolver } from './modifier-list-resolver.service'; +import { ModifierResolver } from './modifier-resolver.service'; +import { ModifierDetailComponent } from './modifier-detail/modifier-detail.component'; +import { ModifierListComponent } from './modifier-list/modifier-list.component'; + +import { AuthGuard } from '../auth/auth-guard.service'; +import { ModifierCategoryListResolver } from "../modifier-categories/modifier-category-list-resolver.service"; + +const modifierRoutes: Routes = [ + { + path: '', + component: ModifierListComponent, + canActivate: [AuthGuard], + data: { + permission: 'Modifiers' + }, + resolve: { + list: ModifierListResolver, + modifierCategories: ModifierCategoryListResolver + } + }, + { + path: 'new', + component: ModifierDetailComponent, + canActivate: [AuthGuard], + data: { + permission: 'Modifiers' + }, + resolve: { + item: ModifierResolver, + modifierCategories: ModifierCategoryListResolver + } + }, + { + path: ':id', + component: ModifierDetailComponent, + canActivate: [AuthGuard], + data: { + permission: 'Modifiers' + }, + resolve: { + item: ModifierResolver, + modifierCategories: ModifierCategoryListResolver + } + } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forChild(modifierRoutes) + + ], + exports: [ + RouterModule + ], + providers: [ + ModifierListResolver, + ModifierResolver + ] +}) +export class ModifiersRoutingModule { +} diff --git a/bookie/src/app/modifiers/modifiers.module.spec.ts b/bookie/src/app/modifiers/modifiers.module.spec.ts new file mode 100644 index 0000000..b37d873 --- /dev/null +++ b/bookie/src/app/modifiers/modifiers.module.spec.ts @@ -0,0 +1,13 @@ +import {ModifiersModule} from './modifiers.module'; + +describe('ModifiersModule', () => { + let modifiersModule: ModifiersModule; + + beforeEach(() => { + modifiersModule = new ModifiersModule(); + }); + + it('should create an instance', () => { + expect(modifiersModule).toBeTruthy(); + }); +}); diff --git a/bookie/src/app/modifiers/modifiers.module.ts b/bookie/src/app/modifiers/modifiers.module.ts new file mode 100644 index 0000000..ac7855b --- /dev/null +++ b/bookie/src/app/modifiers/modifiers.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ModifierListComponent } from './modifier-list/modifier-list.component'; +import { ModifierDetailComponent } from './modifier-detail/modifier-detail.component'; +import { ModifiersRoutingModule } from './modifiers-routing.module'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatOptionModule } from '@angular/material/core'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTableModule } from '@angular/material/table'; +import { ReactiveFormsModule } from '@angular/forms'; +import { FlexLayoutModule } from '@angular/flex-layout'; + +@NgModule({ + imports: [ + CommonModule, + FlexLayoutModule, + MatTableModule, + MatCardModule, + MatProgressSpinnerModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatOptionModule, + MatSelectModule, + MatCheckboxModule, + ReactiveFormsModule, + ModifiersRoutingModule + ], + declarations: [ + ModifierListComponent, + ModifierDetailComponent + ] +}) +export class ModifiersModule { +}