Feature: In the Beer Sale Report, you can choose the type of consumption

Chore: Sorted the product sale report by name
Fix: Voided bills now also include the old bill number
This commit is contained in:
Amritanshu Agrawal 2021-08-17 07:26:54 +05:30
parent 1515c33e7b
commit f929a731cb
10 changed files with 96 additions and 27 deletions

View File

@ -1,5 +1,6 @@
from datetime import date, timedelta from datetime import date, timedelta
from operator import or_ from operator import or_
from typing import Optional
from fastapi import APIRouter, Depends, Security from fastapi import APIRouter, Depends, Security
from sqlalchemy.sql.expression import func, select from sqlalchemy.sql.expression import func, select
@ -23,6 +24,10 @@ router = APIRouter()
def beer_consumption( def beer_consumption(
start_date: date = Depends(report_start_date), start_date: date = Depends(report_start_date),
finish_date: date = Depends(report_finish_date), finish_date: date = Depends(report_finish_date),
r: Optional[bool] = True,
h: Optional[bool] = True,
st: Optional[bool] = True,
n: Optional[bool] = True,
user: UserToken = Security(get_user, scopes=["beer-sale-report"]), user: UserToken = Security(get_user, scopes=["beer-sale-report"]),
): ):
check_audit_permission(start_date, user.permissions) check_audit_permission(start_date, user.permissions)
@ -30,31 +35,39 @@ def beer_consumption(
"day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES) "day", Voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
).label("day") ).label("day")
sum_ = func.sum(Inventory.quantity * ProductVersion.quantity).label("sum") sum_ = func.sum(Inventory.quantity * ProductVersion.quantity).label("sum")
query = (
select(day, ProductVersion.name, sum_)
.join(Voucher.kots)
.join(Kot.inventories)
.join(Inventory.product)
.where(
day >= start_date,
day <= finish_date,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= day,
),
)
)
if h is False and r is not False:
query = query.where(Inventory.is_happy_hour == h)
if r is False and h is not False:
query = query.where(Inventory.is_happy_hour != r)
vt = []
if h is True or r is True:
vt.append(VoucherType.REGULAR_BILL)
if st is True:
vt.append(VoucherType.STAFF)
if n is True:
vt.append(VoucherType.NO_CHARGE)
with SessionFuture() as db: with SessionFuture() as db:
list_ = db.execute( list_ = db.execute(
select(day, ProductVersion.name, sum_) query.where(Voucher.voucher_type.in_(vt))
.join(Voucher.kots)
.join(Kot.inventories)
.join(Inventory.product)
.where(
day >= start_date,
day <= finish_date,
or_(
ProductVersion.valid_from == None, # noqa: E711
ProductVersion.valid_from <= day,
),
or_(
ProductVersion.valid_till == None, # noqa: E711
ProductVersion.valid_till >= day,
),
Voucher.voucher_type.in_(
[
VoucherType.REGULAR_BILL,
VoucherType.NO_CHARGE,
VoucherType.STAFF,
]
),
)
.group_by(day, ProductVersion.name) .group_by(day, ProductVersion.name)
.having(sum_ != 0) .having(sum_ != 0)
.order_by(day, ProductVersion.name) .order_by(day, ProductVersion.name)
@ -72,6 +85,10 @@ def beer_consumption(
return { return {
"startDate": start_date.strftime("%d-%b-%Y"), "startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"), "finishDate": finish_date.strftime("%d-%b-%Y"),
"regular": r,
"happy": h,
"staff": st,
"nc": n,
"headers": headers, "headers": headers,
"data": data, "data": data,
} }

View File

@ -82,7 +82,7 @@ def product_sale_report(s: date, f: date, db: Session):
Voucher.voucher_type, Voucher.voucher_type,
Inventory.is_happy_hour, Inventory.is_happy_hour,
) )
.order_by(SaleCategory.name, MenuCategory.name) .order_by(SaleCategory.name, MenuCategory.name, ProductVersion.full_name)
).all() ).all()
info = [] info = []
for id_, name, type_, hh, quantity in list_: for id_, name, type_, hh, quantity in list_:

View File

@ -84,9 +84,9 @@ def void_and_issue_new_bill(
guest_book = get_guest_book(g, db) guest_book = get_guest_book(g, db)
item: Voucher = do_save(data, old.voucher_type, guest_book, db, user) item: Voucher = do_save(data, old.voucher_type, guest_book, db, user)
db.flush() db.flush()
old.reason = f"Bill Discounted or Changed / Old Bill: {old.full_bill_id} / New Bill: {item.full_bill_id}."
old.bill_id = None old.bill_id = None
old.voucher_type = VoucherType.VOID old.voucher_type = VoucherType.VOID
old.reason = f"Bill Discounted / Changed. New Bill ID is {item.full_bill_id}"
do_update_settlements(old, [SettleSchema(id=SettleOption.VOID(), amount=round(old.amount))], db) do_update_settlements(old, [SettleSchema(id=SettleOption.VOID(), amount=round(old.amount))], db)
if update_table: if update_table:

View File

@ -14,6 +14,10 @@ export class BeerSaleReportResolver implements Resolve<BeerSaleReport> {
resolve(route: ActivatedRouteSnapshot): Observable<BeerSaleReport> { resolve(route: ActivatedRouteSnapshot): Observable<BeerSaleReport> {
const startDate = route.queryParamMap.get('startDate') || null; const startDate = route.queryParamMap.get('startDate') || null;
const finishDate = route.queryParamMap.get('finishDate') || null; const finishDate = route.queryParamMap.get('finishDate') || null;
return this.ser.get(startDate, finishDate); const regular = route.queryParamMap.get('regular') !== 'false';
const happy = route.queryParamMap.get('happy') !== 'false';
const staff = route.queryParamMap.get('staff') !== 'false';
const nc = route.queryParamMap.get('nc') !== 'false';
return this.ser.get(startDate, finishDate, regular, happy, staff, nc);
} }
} }

View File

@ -2,3 +2,6 @@
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
.example-margin {
margin: 0 10px;
}

View File

@ -40,6 +40,14 @@
</mat-form-field> </mat-form-field>
<button fxFlex="20" mat-raised-button color="primary" (click)="show()">Show</button> <button fxFlex="20" mat-raised-button color="primary" (click)="show()">Show</button>
</div> </div>
<div>
<mat-checkbox class="example-margin" formControlName="regular">Regular</mat-checkbox>
<mat-checkbox class="example-margin" formControlName="happy">Happy Hour</mat-checkbox>
<mat-checkbox class="example-margin" formControlName="staff"
>Staff Consumption</mat-checkbox
>
<mat-checkbox class="example-margin" formControlName="nc">No Charge</mat-checkbox>
</div>
</form> </form>
<mat-table #table [dataSource]="dataSource" aria-label="Elements"> <mat-table #table [dataSource]="dataSource" aria-label="Elements">
<!-- Date Column --> <!-- Date Column -->

View File

@ -32,6 +32,10 @@ export class BeerSaleReportComponent implements OnInit {
this.form = this.fb.group({ this.form = this.fb.group({
startDate: '', startDate: '',
finishDate: '', finishDate: '',
regular: true,
happy: true,
staff: true,
nc: true,
}); });
} }
@ -43,6 +47,10 @@ export class BeerSaleReportComponent implements OnInit {
this.form.setValue({ this.form.setValue({
startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(), startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(),
finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(), finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(),
regular: this.info.regular,
happy: this.info.happy,
staff: this.info.staff,
nc: this.info.nc,
}); });
this.dataSource = new BeerSaleReportDataSource(this.info.data); this.dataSource = new BeerSaleReportDataSource(this.info.data);
}); });
@ -54,6 +62,10 @@ export class BeerSaleReportComponent implements OnInit {
queryParams: { queryParams: {
startDate: info.startDate, startDate: info.startDate,
finishDate: info.finishDate, finishDate: info.finishDate,
regular: info.regular,
happy: info.happy,
staff: info.staff,
nc: info.nc,
}, },
}); });
} }
@ -64,6 +76,10 @@ export class BeerSaleReportComponent implements OnInit {
return new BeerSaleReport({ return new BeerSaleReport({
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
regular: formModel.regular,
happy: formModel.happy,
staff: formModel.staff,
nc: formModel.nc,
}); });
} }

View File

@ -8,6 +8,7 @@ import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { import {
DateAdapter, DateAdapter,
MAT_DATE_FORMATS, MAT_DATE_FORMATS,
@ -55,6 +56,7 @@ export const MY_FORMATS = {
ReactiveFormsModule, ReactiveFormsModule,
SharedModule, SharedModule,
BeerSaleReportRoutingModule, BeerSaleReportRoutingModule,
MatCheckboxModule,
], ],
declarations: [BeerSaleReportComponent], declarations: [BeerSaleReportComponent],
providers: [ providers: [

View File

@ -16,7 +16,14 @@ const serviceName = 'BeerSaleReportService';
export class BeerSaleReportService { export class BeerSaleReportService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {} constructor(private http: HttpClient, private log: ErrorLoggerService) {}
get(startDate: string | null, finishDate: string | null): Observable<BeerSaleReport> { get(
startDate: string | null,
finishDate: string | null,
regular: boolean,
happy: boolean,
staff: boolean,
nc: boolean,
): Observable<BeerSaleReport> {
const options = { params: new HttpParams() }; const options = { params: new HttpParams() };
if (startDate !== null) { if (startDate !== null) {
options.params = options.params.set('s', startDate); options.params = options.params.set('s', startDate);
@ -24,6 +31,10 @@ export class BeerSaleReportService {
if (finishDate !== null) { if (finishDate !== null) {
options.params = options.params.set('f', finishDate); options.params = options.params.set('f', finishDate);
} }
options.params = options.params.set('r', regular);
options.params = options.params.set('h', happy);
options.params = options.params.set('st', staff);
options.params = options.params.set('n', nc);
return this.http return this.http
.get<BeerSaleReport>(url, options) .get<BeerSaleReport>(url, options)
.pipe(catchError(this.log.handleError(serviceName, 'get'))) as Observable<BeerSaleReport>; .pipe(catchError(this.log.handleError(serviceName, 'get'))) as Observable<BeerSaleReport>;

View File

@ -3,12 +3,20 @@ import { BeerSaleReportItem } from './beer-sale-report-item';
export class BeerSaleReport { export class BeerSaleReport {
startDate: string; startDate: string;
finishDate: string; finishDate: string;
regular: boolean;
happy: boolean;
staff: boolean;
nc: boolean;
headers: string[]; headers: string[];
data: BeerSaleReportItem[]; data: BeerSaleReportItem[];
public constructor(init?: Partial<BeerSaleReport>) { public constructor(init?: Partial<BeerSaleReport>) {
this.startDate = ''; this.startDate = '';
this.finishDate = ''; this.finishDate = '';
this.regular = true;
this.happy = true;
this.staff = true;
this.nc = true;
this.data = []; this.data = [];
this.headers = []; this.headers = [];
Object.assign(this, init); Object.assign(this, init);