Product sale report can now be section wise

This commit is contained in:
Amritanshu Agrawal 2024-12-17 08:46:29 +05:30
parent bbb7be8070
commit 55d5017254
9 changed files with 98 additions and 83 deletions

View File

@ -1,15 +1,16 @@
import uuid
from datetime import date, datetime, time, timedelta
from typing import Any
from typing import Annotated, Any
from fastapi import APIRouter, Cookie, Depends, Security
from fastapi import APIRouter, Cookie, Depends, Query, Security
from sqlalchemy import func, or_, select
from sqlalchemy.orm import Session
from ...core.config import settings
from ...core.security import get_current_active_user as get_user
from ...db.session import SessionFuture
from ...models.food_table import FoodTable
from ...models.inventory import Inventory
from ...models.kot import Kot
from ...models.menu_category import MenuCategory
@ -31,6 +32,7 @@ router = APIRouter()
def product_sale_report_view(
start_date: date = Depends(report_start_date),
finish_date: date = Depends(report_finish_date),
section: Annotated[uuid.UUID | None, Query()] = None,
user: UserToken = Security(get_user, scopes=["product-sale-report"]),
):
check_audit_permission(start_date, user.permissions)
@ -38,11 +40,12 @@ def product_sale_report_view(
return {
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"amounts": product_sale_report(start_date, finish_date, db),
"sectionId": str(section) if section is not None else None,
"amounts": product_sale_report(start_date, finish_date, section, db),
}
def product_sale_report(s: date, f: date, db: Session):
def product_sale_report(s: date, f: date, id_: uuid.UUID, db: Session):
start_date = datetime.combine(s, time()) + timedelta(
minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES
)
@ -51,7 +54,7 @@ def product_sale_report(s: date, f: date, db: Session):
)
day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day")
list_ = db.execute(
query = (
select(
ProductVersion.id,
ProductVersion.full_name,
@ -65,6 +68,7 @@ def product_sale_report(s: date, f: date, db: Session):
.join(Product.versions)
.join(ProductVersion.sale_category)
.join(ProductVersion.menu_category)
.join(Voucher.food_table)
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
@ -77,16 +81,18 @@ def product_sale_report(s: date, f: date, db: Session):
ProductVersion.valid_till >= day,
),
)
.group_by(
)
if id_:
query = query.where(FoodTable.section_id == id_)
query = query.group_by(
SaleCategory.name,
MenuCategory.name,
ProductVersion.id,
ProductVersion.full_name,
Voucher.voucher_type,
Inventory.is_happy_hour,
)
.order_by(SaleCategory.name, MenuCategory.name, ProductVersion.full_name)
).all()
).order_by(SaleCategory.name, MenuCategory.name, ProductVersion.full_name)
list_ = db.execute(query).all()
info: list[Any] = []
for id_, name, v_type, hh, quantity in list_:
type_ = to_camel(VoucherType(v_type).name)
@ -111,6 +117,7 @@ def product_sale_report(s: date, f: date, db: Session):
def print_report(
start_date: date = Depends(report_start_date),
finish_date: date = Depends(report_finish_date),
section: Annotated[uuid.UUID | None, Query()] = None,
device_id: uuid.UUID = Cookie(None),
user: UserToken = Security(get_user, scopes=["product-sale-report"]),
) -> bool:
@ -120,6 +127,7 @@ def print_report(
"userName": user.name,
"startDate": start_date.strftime("%d-%b-%Y"),
"finishDate": finish_date.strftime("%d-%b-%Y"),
"sectionId": str(section) if section is not None else None,
"amounts": product_sale_report(start_date, finish_date, db),
}
print_product_sale_report(report, device_id, db)

View File

@ -137,6 +137,7 @@ def print_report(
report = SaleReport(
start_date=start_date,
finish_date=finish_date,
section_id=section,
amounts=(
get_sale(start_date, finish_date, section, db)
+ [SaleReportItem(name="--", amount=Decimal(0))]

View File

@ -14,7 +14,7 @@
<mat-card-content>
<form [formGroup]="form" class="flex flex-col">
<div class="flex flex-row justify-around content-start items-start sm:max-lg:flex-col">
<mat-form-field class="flex-auto basis-2/5 mr-5">
<mat-form-field class="flex-auto basis-1/3 mr-5">
<mat-label>Start Date</mat-label>
<input
matInput
@ -26,7 +26,7 @@
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
<mat-datepicker #startDate></mat-datepicker>
</mat-form-field>
<mat-form-field class="flex-auto basis-2/5 mr-5">
<mat-form-field class="flex-auto basis-1/3 mr-5">
<mat-label>Finish Date</mat-label>
<input
matInput
@ -38,7 +38,18 @@
<mat-datepicker-toggle matSuffix [for]="finishDate"></mat-datepicker-toggle>
<mat-datepicker #finishDate></mat-datepicker>
</mat-form-field>
<button mat-raised-button class="flex-auto basis-1/5" color="primary" (click)="show()">Show</button>
<mat-form-field class="flex-auto basis-1/6 mr-5">
<mat-label>Section</mat-label>
<mat-select formControlName="section" name="section">
<mat-option>-- All --</mat-option>
@for (p of sections; track p.id) {
<mat-option [value]="p.id">
{{ p.name }}
</mat-option>
}
</mat-select>
</mat-form-field>
<button mat-raised-button class="flex-auto basis-1/6" color="primary" (click)="show()">Show</button>
</div>
</form>
<mat-table #table [dataSource]="dataSource" aria-label="Elements">

View File

@ -1,27 +1,18 @@
import { DecimalPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatIconButton, MatButton } from '@angular/material/button';
import { MatCard, MatCardHeader, MatCardTitleGroup, MatCardTitle, MatCardContent } from '@angular/material/card';
import { MatDatepickerInput, MatDatepickerToggle, MatDatepicker } from '@angular/material/datepicker';
import { MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import {
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
} from '@angular/material/table';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment';
import { Section } from '../core/section';
import { ToasterService } from '../core/toaster.service';
import { ToCsvService } from '../shared/to-csv.service';
@ -34,41 +25,26 @@ import { ProductSaleReportService } from './product-sale-report.service';
templateUrl: './product-sale-report.component.html',
styleUrls: ['./product-sale-report.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIconButton,
MatIcon,
MatCardContent,
MatCardModule,
MatButtonModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatSuffix,
MatDatepicker,
MatButton,
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatDatepickerModule,
MatTableModule,
MatSelectModule,
DecimalPipe,
],
})
export class ProductSaleReportComponent implements OnInit {
sections: Section[] = [];
info: ProductSaleReport = new ProductSaleReport();
dataSource: ProductSaleReportDataSource = new ProductSaleReportDataSource(this.info.amounts);
form: FormGroup<{
startDate: FormControl<Date>;
finishDate: FormControl<Date>;
section: FormControl<string | null>;
}>;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
@ -85,16 +61,19 @@ export class ProductSaleReportComponent implements OnInit {
this.form = new FormGroup({
startDate: new FormControl(new Date(), { nonNullable: true }),
finishDate: new FormControl(new Date(), { nonNullable: true }),
section: new FormControl<string | null>(null),
});
}
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { info: ProductSaleReport };
const data = value as { sections: Section[]; info: ProductSaleReport };
this.sections = data.sections;
this.info = data.info;
this.form.setValue({
startDate: moment(this.info.startDate, 'DD-MMM-YYYY').toDate(),
finishDate: moment(this.info.finishDate, 'DD-MMM-YYYY').toDate(),
section: this.info.sectionId || null,
});
this.dataSource = new ProductSaleReportDataSource(this.info.amounts);
});
@ -102,25 +81,33 @@ export class ProductSaleReportComponent implements OnInit {
show() {
const info = this.getInfo();
this.router.navigate(['product-sale-report'], {
queryParams: {
const params = {
startDate: info.startDate,
finishDate: info.finishDate,
},
} as { startDate: string; finishDate: string; section?: string };
if (info.sectionId) {
params['section'] = info.sectionId;
}
this.router.navigate(['product-sale-report'], {
queryParams: params,
});
}
getInfo(): ProductSaleReport {
const formModel = this.form.value;
return new ProductSaleReport({
const data = new ProductSaleReport({
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
});
if (formModel.section) {
data.sectionId = formModel.section;
}
return data;
}
print() {
this.ser.print(this.info.startDate, this.info.finishDate).subscribe({
this.ser.print(this.info.startDate, this.info.finishDate, this.info.sectionId).subscribe({
next: () => {
this.toaster.show('', 'Successfully Printed');
},

View File

@ -7,5 +7,6 @@ import { ProductSaleReportService } from './product-sale-report.service';
export const productSaleReportResolver: ResolveFn<ProductSaleReport> = (route) => {
const startDate = route.queryParamMap.get('startDate') ?? null;
const finishDate = route.queryParamMap.get('finishDate') ?? null;
return inject(ProductSaleReportService).get(startDate, finishDate);
const section = route.queryParamMap.get('section') ?? null;
return inject(ProductSaleReportService).get(startDate, finishDate, section);
};

View File

@ -1,6 +1,7 @@
import { Routes } from '@angular/router';
import { authGuard } from '../auth/auth-guard.service';
import { sectionListResolver } from '../sections/section-list.resolver';
import { ProductSaleReportComponent } from './product-sale-report.component';
import { productSaleReportResolver } from './product-sale-report.resolver';
@ -14,6 +15,7 @@ export const routes: Routes = [
permission: 'Product Sale Report',
},
resolve: {
sections: sectionListResolver,
info: productSaleReportResolver,
},
runGuardsAndResolvers: 'always',

View File

@ -19,7 +19,7 @@ export class ProductSaleReportService {
private log: ErrorLoggerService,
) {}
get(startDate: string | null, finishDate: string | null): Observable<ProductSaleReport> {
get(startDate: string | null, finishDate: string | null, section: string | null): Observable<ProductSaleReport> {
const options = { params: new HttpParams() };
if (startDate !== null) {
options.params = options.params.set('s', startDate);
@ -27,12 +27,15 @@ export class ProductSaleReportService {
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
if (section !== null) {
options.params = options.params.set('section', section);
}
return this.http
.get<ProductSaleReport>(url, options)
.pipe(catchError(this.log.handleError(serviceName, 'get'))) as Observable<ProductSaleReport>;
}
print(startDate: string | null, finishDate: string | null): Observable<boolean> {
print(startDate: string | null, finishDate: string | null, section: string | null): Observable<boolean> {
const printUrl = `${url}/print`;
const options = { params: new HttpParams() };
if (startDate !== null) {
@ -41,6 +44,9 @@ export class ProductSaleReportService {
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
if (section !== null) {
options.params = options.params.set('section', section);
}
return this.http
.get<boolean>(printUrl, options)
.pipe(catchError(this.log.handleError(serviceName, 'print'))) as Observable<boolean>;

View File

@ -3,11 +3,13 @@ import { ProductSaleReportItem } from './product-sale-report-item';
export class ProductSaleReport {
startDate: string;
finishDate: string;
sectionId: string | null;
amounts: ProductSaleReportItem[];
public constructor(init?: Partial<ProductSaleReport>) {
this.startDate = '';
this.finishDate = '';
this.sectionId = null;
this.amounts = [];
Object.assign(this, init);
}

View File

@ -1,13 +1,13 @@
import { CurrencyPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatIconButton, MatButton } from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
@ -27,19 +27,16 @@ import { SaleReportService } from './sale-report.service';
styleUrls: ['./sale-report.component.css'],
imports: [
MatCardModule,
MatIconButton,
MatIcon,
MatButtonModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatDatepickerModule,
MatSuffix,
MatButton,
MatTableModule,
CurrencyPipe,
MatOptionModule,
MatSelectModule,
CurrencyPipe,
],
})
export class SaleReportComponent implements OnInit {