Ported:
Guest Book Chore: Added New Day Offset and Timezone Offset in settings so that they can be shared consistently and also set in runtime.
This commit is contained in:
parent
cabdd505e0
commit
b19d8cc030
|
@ -8,8 +8,11 @@ from pydantic import BaseSettings, PostgresDsn, validator
|
|||
class Settings(BaseSettings):
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY: str = secrets.token_urlsafe(32)
|
||||
MIDDLEWARE_SECRET_KEY: str = secrets.token_urlsafe(5)
|
||||
ALGORITHM: str = "HS256"
|
||||
JWT_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
NEW_DAY_OFFSET_MINUTES: int = 7 * 60
|
||||
TIMEZONE_OFFSET_MINUTES: int = 330
|
||||
HOST: str = "0.0.0.0"
|
||||
PORT: int = 80
|
||||
DEBUG: bool = False
|
||||
|
|
|
@ -37,11 +37,16 @@ class GuestBook(Base):
|
|||
|
||||
customer = relationship("Customer")
|
||||
|
||||
def __init__(self, customer_id=None, pax=None, id_=None):
|
||||
def __init__(self, pax=None, id_=None, customer_id=None, customer=None):
|
||||
self.customer_id = customer_id
|
||||
self.pax = pax
|
||||
self.id = id_
|
||||
self.date = datetime.utcnow()
|
||||
if customer is None:
|
||||
self.customer_id = customer_id
|
||||
else:
|
||||
self.customer = customer
|
||||
|
||||
|
||||
|
||||
class Overview(Base):
|
||||
|
|
|
@ -1,69 +1,126 @@
|
|||
import uuid
|
||||
from datetime import datetime, date, timedelta
|
||||
from typing import Optional
|
||||
from datetime import date, timedelta, datetime
|
||||
from fastapi import APIRouter, HTTPException, status, Depends, Security
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
import transaction
|
||||
from pyramid.view import view_config
|
||||
from ..core.config import settings
|
||||
from ..schemas.auth import UserToken
|
||||
import barker.schemas.master as schemas
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionLocal
|
||||
from ..models.master import Customer
|
||||
from ..models.voucher import GuestBook
|
||||
|
||||
from barker.models import GuestBook, Customer
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@view_config(
|
||||
request_method="PUT", route_name="v1_guest_book_new", renderer="json", permission="Guest Book", trans=True,
|
||||
)
|
||||
def save(request):
|
||||
json = request.json_body
|
||||
company = json["company"].strip() if json["company"] is not None else ""
|
||||
name = json["name"].strip()
|
||||
phone = json["phone"]
|
||||
address = json["address"].strip()
|
||||
customer = request.dbsession.query(Customer).filter(Customer.phone == phone).first()
|
||||
if customer is None:
|
||||
customer = Customer(company, name, phone, address)
|
||||
request.dbsession.add(customer)
|
||||
else:
|
||||
if company != "":
|
||||
customer.company = company
|
||||
if name != "":
|
||||
customer.name = name
|
||||
if address != "":
|
||||
customer.address = address
|
||||
item = GuestBook(pax=json["pax"])
|
||||
item.customer = customer
|
||||
request.dbsession.add(item)
|
||||
transaction.commit()
|
||||
item = request.dbsession.query(GuestBook).filter(GuestBook.id == item.id).first()
|
||||
return guest_book_info(item)
|
||||
# Dependency
|
||||
def get_db():
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@view_config(
|
||||
request_method="GET", route_name="v1_guest_book_id", renderer="json", permission="Guest Book",
|
||||
)
|
||||
def show_id(request):
|
||||
id_ = request.matchdict["id"]
|
||||
item = request.dbsession.query(GuestBook).filter(GuestBook.id == uuid.UUID(id_)).one()
|
||||
return guest_book_info(item)
|
||||
@router.post("", response_model=schemas.GuestBook)
|
||||
def save(
|
||||
data: schemas.GuestBookIn,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserToken = Security(get_user, scopes=["guest-book"]),
|
||||
):
|
||||
try:
|
||||
customer: Customer = db.query(Customer).filter(Customer.phone == data.phone).first()
|
||||
if customer is None:
|
||||
customer = Customer(company=data.company, name=data.name, phone=data.phone, address=data.address,)
|
||||
db.add(customer)
|
||||
else:
|
||||
customer.name = data.name or customer.name
|
||||
customer.company = data.company or customer.company
|
||||
customer.address = data.address or customer.address
|
||||
item = GuestBook(pax=data.pax, customer=customer)
|
||||
db.add(item)
|
||||
db.commit()
|
||||
return guest_book_info(item)
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
|
||||
|
||||
@view_config(
|
||||
request_method="GET", route_name="v1_guest_book_new", renderer="json", permission="Guest Book",
|
||||
)
|
||||
def show_blank(request):
|
||||
@router.put("/{id_}", response_model=schemas.GuestBook)
|
||||
def update(
|
||||
id_: uuid.UUID,
|
||||
data: schemas.GuestBookIn,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserToken = Security(get_user, scopes=["guest-book"]),
|
||||
):
|
||||
try:
|
||||
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
|
||||
item.customer.company = data.company
|
||||
item.customer.name = data.name
|
||||
item.customer.phone = data.phone
|
||||
item.customer.address = data.address
|
||||
item.pax = data.pax
|
||||
db.commit()
|
||||
return guest_book_info(item)
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
|
||||
|
||||
@router.delete("/{id_}")
|
||||
def delete(
|
||||
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
|
||||
):
|
||||
try:
|
||||
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
|
||||
db.delete(item)
|
||||
db.commit()
|
||||
return guest_book_info(None)
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
|
||||
|
||||
@router.get("")
|
||||
def show_blank(
|
||||
db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
|
||||
):
|
||||
return guest_book_info(None)
|
||||
|
||||
|
||||
@view_config(
|
||||
request_method="GET", route_name="v1_guest_book_list", renderer="json", permission="Authenticated",
|
||||
)
|
||||
def show_list(request):
|
||||
date_ = request.GET.get("q", None)
|
||||
if date_ is None or date_ == "":
|
||||
date_ = date.today().strftime("%d-%b-%Y")
|
||||
@router.get("/list")
|
||||
def show_list(q: Optional[str] = None, db: Session = Depends(get_db), user: UserToken = Depends(get_user)):
|
||||
if q is None or q == "":
|
||||
q = date.today()
|
||||
else:
|
||||
q = datetime.strptime(q, "%d-%b-%Y")
|
||||
list_ = (
|
||||
request.dbsession.query(GuestBook)
|
||||
db.query(GuestBook)
|
||||
.filter(
|
||||
GuestBook.date >= datetime.strptime(date_, "%d-%b-%Y") - timedelta(minutes=30) # hack for timezone and 5 am
|
||||
GuestBook.date >= q + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
|
||||
)
|
||||
.filter(
|
||||
GuestBook.date
|
||||
< q + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES, days=1)
|
||||
)
|
||||
.filter(GuestBook.date < datetime.strptime(date_, "%d-%b-%Y") - timedelta(minutes=30) + timedelta(days=1))
|
||||
.order_by(GuestBook.date)
|
||||
.all()
|
||||
)
|
||||
|
@ -79,13 +136,22 @@ def show_list(request):
|
|||
"name": item.customer.name,
|
||||
"phone": item.customer.phone,
|
||||
"pax": item.pax,
|
||||
"date": item.date.strftime("%d-%b-%Y %H:%M"),
|
||||
"status": "" if item.status is None else item.status.status,
|
||||
},
|
||||
)
|
||||
return {"date": date_, "list": guest_book}
|
||||
return {"date": q.strftime("%d-%b-%Y"), "list": guest_book}
|
||||
|
||||
|
||||
def guest_book_info(item):
|
||||
@router.get("/{id_}")
|
||||
def show_id(
|
||||
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
|
||||
):
|
||||
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
|
||||
return guest_book_info(item)
|
||||
|
||||
|
||||
def guest_book_info(item: Optional[GuestBook]):
|
||||
if item is not None:
|
||||
return {
|
||||
"id": item.id,
|
||||
|
@ -94,6 +160,7 @@ def guest_book_info(item):
|
|||
"phone": item.customer.phone,
|
||||
"pax": item.pax,
|
||||
"address": item.customer.address,
|
||||
"date": item.date.strftime("%d-%b-%Y %H:%M"),
|
||||
}
|
||||
else:
|
||||
return {"company": "", "name": "", "phone": "", "pax": 0, "address": ""}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import uuid
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from barker.schemas import to_camel
|
||||
from barker.schemas.customer import CustomerIn
|
||||
|
||||
|
||||
class GuestBookIn(CustomerIn):
|
||||
pax: int = Field(ge=0)
|
||||
|
||||
class Config:
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
|
||||
|
||||
class GuestBook(GuestBookIn):
|
||||
id_: uuid.UUID
|
||||
date: str
|
||||
|
||||
class Config:
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
|
@ -9,6 +9,7 @@ from barker.schemas import to_camel
|
|||
from barker.schemas.tax import TaxLink, TaxIn, Tax # noqa: F401
|
||||
from barker.schemas.sale_category import SaleCategoryLink, SaleCategoryIn, SaleCategory # noqa: F401
|
||||
from barker.schemas.customer import CustomerIn, Customer # noqa: F401
|
||||
from barker.schemas.guest_book import GuestBookIn, GuestBook # noqa: F401
|
||||
from barker.schemas.menu_category import MenuCategoryLink, MenuCategoryIn, MenuCategory # noqa: F401
|
||||
from barker.schemas.product import ProductLink, ProductIn, Product # noqa: F401
|
||||
from barker.schemas.modifier_category import ModifierCategoryLink, ModifierCategoryIn, ModifierCategory # noqa: F401
|
||||
|
|
|
@ -25,7 +25,7 @@ import { CoreModule } from './core/core.module';
|
|||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatToolbarModule } from "@angular/material/toolbar";
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { NavBarComponent } from './nav-bar/nav-bar.component';
|
||||
|
||||
registerLocaleData(enIN);
|
||||
|
|
|
@ -43,6 +43,12 @@
|
|||
<mat-cell *matCellDef="let row">{{row.pax}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Time Column -->
|
||||
<ng-container matColumnDef="date">
|
||||
<mat-header-cell *matHeaderCellDef>Time</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.date | localTime}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Action Column -->
|
||||
<ng-container matColumnDef="action">
|
||||
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
|
||||
|
@ -53,7 +59,7 @@
|
|||
<button mat-icon-button [routerLink]="['/guest-book/', row.id]">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" (click)="deleteRow(row)">
|
||||
<button mat-icon-button color="warn" (click)="confirmDelete(row.id)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import * as moment from 'moment';
|
||||
import {GuestBook, GuestBookList} from '../guest-book';
|
||||
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
|
||||
import { ToasterService } from '../../core/toaster.service';
|
||||
import { GuestBook, GuestBookList } from '../guest-book';
|
||||
import { GuestBookService } from '../guest-book.service';
|
||||
import { GuestBookListDataSource } from './guest-book-list-datasource';
|
||||
|
||||
|
@ -18,9 +21,16 @@ export class GuestBookListComponent implements OnInit {
|
|||
form: FormGroup;
|
||||
data: BehaviorSubject<GuestBook[]>;
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
displayedColumns = ['sno', 'name', 'phone', 'pax', 'action'];
|
||||
displayedColumns = ['sno', 'name', 'phone', 'pax', 'date', 'action'];
|
||||
|
||||
constructor(private route: ActivatedRoute, private fb: FormBuilder, private ser: GuestBookService) {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private fb: FormBuilder,
|
||||
private dialog: MatDialog,
|
||||
private toaster: ToasterService,
|
||||
private ser: GuestBookService
|
||||
) {
|
||||
this.createForm();
|
||||
this.data = new BehaviorSubject([]);
|
||||
this.listenToDateChange();
|
||||
|
@ -51,4 +61,32 @@ export class GuestBookListComponent implements OnInit {
|
|||
});
|
||||
this.dataSource = new GuestBookListDataSource(this.data);
|
||||
}
|
||||
|
||||
delete(id: string) {
|
||||
this.ser.delete(id)
|
||||
.subscribe(
|
||||
(result) => {
|
||||
this.toaster.show('Success', '');
|
||||
this.router.navigateByUrl('/guest-book');
|
||||
},
|
||||
(error) => {
|
||||
this.toaster.show('Danger', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
confirmDelete(id: string): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '250px',
|
||||
data: {title: 'Delete Guest Book Entry?', content: 'Are you sure? This cannot be undone.'}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result: boolean) => {
|
||||
if (result) {
|
||||
this.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ const routes: Routes = [
|
|||
},
|
||||
resolve: {
|
||||
list: GuestBookListResolver
|
||||
}
|
||||
},
|
||||
runGuardsAndResolvers: 'always'
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
|
|
|
@ -15,6 +15,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
|||
import { GuestBookDetailComponent } from './guest-book-detail/guest-book-detail.component';
|
||||
import { GuestBookListComponent } from './guest-book-list/guest-book-list.component';
|
||||
import { GuestBookRoutingModule } from './guest-book-routing.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
export const MY_FORMATS = {
|
||||
parse: {
|
||||
|
@ -43,7 +44,8 @@ export const MY_FORMATS = {
|
|||
MatNativeDateModule,
|
||||
ReactiveFormsModule,
|
||||
FlexLayoutModule,
|
||||
GuestBookRoutingModule
|
||||
GuestBookRoutingModule,
|
||||
SharedModule
|
||||
],
|
||||
providers: [
|
||||
{provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]},
|
||||
|
|
|
@ -9,7 +9,7 @@ const httpOptions = {
|
|||
headers: new HttpHeaders({'Content-Type': 'application/json'})
|
||||
};
|
||||
|
||||
const url = '/v1/guest-book';
|
||||
const url = '/api/guest-book';
|
||||
const serviceName = 'GuestBookService';
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
|
@ -19,7 +19,7 @@ export class GuestBookService {
|
|||
}
|
||||
|
||||
get(id: string): Observable<GuestBook> {
|
||||
const getUrl: string = (id === null) ? `${url}/new` : `${url}/${id}`;
|
||||
const getUrl: string = (id === null) ? url : `${url}/${id}`;
|
||||
return <Observable<GuestBook>>this.http.get<GuestBook>(getUrl)
|
||||
.pipe(
|
||||
catchError(this.log.handleError(serviceName, `get id=${id}`))
|
||||
|
@ -28,22 +28,21 @@ export class GuestBookService {
|
|||
|
||||
list(date: string): Observable<GuestBookList> {
|
||||
const options = {params: new HttpParams().set('q', (date === null) ? '' : date)};
|
||||
const listUrl: string = url;
|
||||
return <Observable<GuestBookList>>this.http.get<GuestBookList>(listUrl, options)
|
||||
return <Observable<GuestBookList>>this.http.get<GuestBookList>(`${url}/list`, options)
|
||||
.pipe(
|
||||
catchError(this.log.handleError(serviceName, 'list'))
|
||||
);
|
||||
}
|
||||
|
||||
save(guestBook: GuestBook): Observable<GuestBook> {
|
||||
return <Observable<GuestBook>>this.http.put<GuestBook>(`${url}/new`, guestBook, httpOptions)
|
||||
return <Observable<GuestBook>>this.http.post<GuestBook>(url, guestBook, httpOptions)
|
||||
.pipe(
|
||||
catchError(this.log.handleError(serviceName, 'save'))
|
||||
);
|
||||
}
|
||||
|
||||
update(guestBook: GuestBook): Observable<GuestBook> {
|
||||
return <Observable<GuestBook>>this.http.post<GuestBook>(`${url}/${guestBook.id}`, guestBook, httpOptions)
|
||||
return <Observable<GuestBook>>this.http.put<GuestBook>(`${url}/${guestBook.id}`, guestBook, httpOptions)
|
||||
.pipe(
|
||||
catchError(this.log.handleError(serviceName, 'update'))
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ export class GuestBook {
|
|||
phone: string;
|
||||
pax: number;
|
||||
address: string;
|
||||
date: string;
|
||||
|
||||
public constructor(init?: Partial<GuestBook>) {
|
||||
Object.assign(this, init);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ConfirmDialogComponent} from './confirm-dialog/confirm-dialog.component';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import {LocalTimePipe} from './local-time.pipe';
|
||||
import {ClearPipe} from './clear.pipe';
|
||||
import {AccountingPipe} from './accounting.pipe';
|
||||
import {ImageDialogComponent} from './image-dialog/image-dialog.component';
|
||||
import { LocalTimePipe } from './local-time.pipe';
|
||||
import { ClearPipe } from './clear.pipe';
|
||||
import { AccountingPipe } from './accounting.pipe';
|
||||
import { ImageDialogComponent } from './image-dialog/image-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
|
Loading…
Reference in New Issue