Sale report now can be section wise

This commit is contained in:
2024-12-17 08:23:18 +05:30
parent b1407f339d
commit bbb7be8070
12 changed files with 85 additions and 59 deletions

View File

@ -2,14 +2,16 @@ import uuid
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
from decimal import Decimal from decimal import Decimal
from typing import Annotated
from fastapi import APIRouter, Cookie, Depends, Security from fastapi import APIRouter, Cookie, Depends, Query, Security
from sqlalchemy import func, or_, select from sqlalchemy import func, or_, select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from ...core.config import settings from ...core.config import settings
from ...core.security import get_current_active_user as get_user from ...core.security import get_current_active_user as get_user
from ...db.session import SessionFuture from ...db.session import SessionFuture
from ...models.food_table import FoodTable
from ...models.inventory import Inventory from ...models.inventory import Inventory
from ...models.kot import Kot from ...models.kot import Kot
from ...models.product import Product from ...models.product import Product
@ -34,6 +36,7 @@ router = APIRouter()
def get_sale_report( def get_sale_report(
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),
section: Annotated[uuid.UUID | None, Query()] = None,
user: UserToken = Security(get_user, scopes=["sale-report"]), user: UserToken = Security(get_user, scopes=["sale-report"]),
) -> SaleReport: ) -> SaleReport:
check_audit_permission(start_date, user.permissions) check_audit_permission(start_date, user.permissions)
@ -41,8 +44,9 @@ def get_sale_report(
return SaleReport( return SaleReport(
start_date=start_date, start_date=start_date,
finish_date=finish_date, finish_date=finish_date,
section_id=section,
amounts=( amounts=(
get_sale(start_date, finish_date, db) get_sale(start_date, finish_date, section, db)
+ [SaleReportItem(name="--", amount=Decimal(0))] + [SaleReportItem(name="--", amount=Decimal(0))]
+ get_settlements(start_date, finish_date, db) + get_settlements(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=Decimal(0))] + [SaleReportItem(name="--", amount=Decimal(0))]
@ -52,7 +56,7 @@ def get_sale_report(
) )
def get_sale(s: date, f: date, db: Session) -> list[SaleReportItem]: def get_sale(s: date, f: date, id_: uuid.UUID | None, db: Session) -> list[SaleReportItem]:
start_date = datetime.combine(s, time()) + timedelta( start_date = datetime.combine(s, time()) + timedelta(
minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES
) )
@ -61,13 +65,14 @@ def get_sale(s: date, f: date, db: Session) -> list[SaleReportItem]:
) )
day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day") day = func.date_trunc("day", Voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).label("day")
list_ = db.execute( query = (
select(SaleCategory.name, func.sum(Inventory.net)) select(SaleCategory.name, func.sum(Inventory.net))
.join(Inventory.kot) .join(Inventory.kot)
.join(Kot.voucher) .join(Kot.voucher)
.join(Inventory.product) .join(Inventory.product)
.join(Product.versions) .join(Product.versions)
.join(ProductVersion.sale_category) .join(ProductVersion.sale_category)
.join(Voucher.food_table)
.where( .where(
Voucher.date >= start_date, Voucher.date >= start_date,
Voucher.date <= finish_date, Voucher.date <= finish_date,
@ -82,9 +87,11 @@ def get_sale(s: date, f: date, db: Session) -> list[SaleReportItem]:
ProductVersion.valid_till >= day, ProductVersion.valid_till >= day,
), ),
) )
.group_by(SaleCategory.name) )
.order_by(SaleCategory.name) if id_:
).all() query = query.where(FoodTable.section_id == id_)
query = query.group_by(SaleCategory.name).order_by(SaleCategory.name)
list_ = db.execute(query).all()
total = Decimal(0) total = Decimal(0)
info = [] info = []
for gt, am in list_: for gt, am in list_:
@ -121,6 +128,7 @@ def get_settlements(s: date, f: date, db: Session) -> list[SaleReportItem]:
def print_report( def print_report(
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),
section: Annotated[uuid.UUID | None, Query()] = None,
device_id: uuid.UUID = Cookie(None), device_id: uuid.UUID = Cookie(None),
user: UserToken = Security(get_user, scopes=["sale-report"]), user: UserToken = Security(get_user, scopes=["sale-report"]),
) -> bool: ) -> bool:
@ -130,7 +138,7 @@ def print_report(
start_date=start_date, start_date=start_date,
finish_date=finish_date, finish_date=finish_date,
amounts=( amounts=(
get_sale(start_date, finish_date, db) get_sale(start_date, finish_date, section, db)
+ [SaleReportItem(name="--", amount=Decimal(0))] + [SaleReportItem(name="--", amount=Decimal(0))]
+ get_settlements(start_date, finish_date, db) + get_settlements(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=Decimal(0))] + [SaleReportItem(name="--", amount=Decimal(0))]

View File

@ -1,3 +1,5 @@
import uuid
from datetime import date, datetime from datetime import date, datetime
from pydantic import BaseModel, ConfigDict, field_serializer, field_validator from pydantic import BaseModel, ConfigDict, field_serializer, field_validator
@ -15,6 +17,7 @@ class SaleReportItem(BaseModel):
class SaleReport(BaseModel): class SaleReport(BaseModel):
start_date: date start_date: date
finish_date: date finish_date: date
section_id: uuid.UUID | None
amounts: list[SaleReportItem] amounts: list[SaleReportItem]
user: UserLink user: UserLink
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)

View File

@ -14,7 +14,7 @@
<mat-card-content> <mat-card-content>
<form [formGroup]="form" class="flex flex-col"> <form [formGroup]="form" class="flex flex-col">
<div class="flex flex-row justify-around content-start items-start sm:max-lg: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> <mat-label>Start Date</mat-label>
<input <input
matInput matInput
@ -26,7 +26,7 @@
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
<mat-datepicker #startDate></mat-datepicker> <mat-datepicker #startDate></mat-datepicker>
</mat-form-field> </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> <mat-label>Finish Date</mat-label>
<input <input
matInput matInput
@ -38,7 +38,18 @@
<mat-datepicker-toggle matSuffix [for]="finishDate"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="finishDate"></mat-datepicker-toggle>
<mat-datepicker #finishDate></mat-datepicker> <mat-datepicker #finishDate></mat-datepicker>
</mat-form-field> </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> </div>
</form> </form>
<mat-table #table [dataSource]="dataSource" aria-label="Elements"> <mat-table #table [dataSource]="dataSource" aria-label="Elements">

View File

@ -2,26 +2,18 @@ import { CurrencyPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatIconButton, MatButton } from '@angular/material/button'; import { MatIconButton, MatButton } from '@angular/material/button';
import { MatCard, MatCardHeader, MatCardTitleGroup, MatCardTitle, MatCardContent } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDatepickerInput, MatDatepickerToggle, MatDatepicker } from '@angular/material/datepicker'; import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field'; import { MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input'; import { MatInput } from '@angular/material/input';
import { import { MatSelectModule } from '@angular/material/select';
MatTable, import { MatTableModule } from '@angular/material/table';
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
} from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment'; import moment from 'moment';
import { Section } from '../core/section';
import { ToasterService } from '../core/toaster.service'; import { ToasterService } from '../core/toaster.service';
import { ToCsvService } from '../shared/to-csv.service'; import { ToCsvService } from '../shared/to-csv.service';
@ -34,41 +26,30 @@ import { SaleReportService } from './sale-report.service';
templateUrl: './sale-report.component.html', templateUrl: './sale-report.component.html',
styleUrls: ['./sale-report.component.css'], styleUrls: ['./sale-report.component.css'],
imports: [ imports: [
MatCard, MatCardModule,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIconButton, MatIconButton,
MatIcon, MatIcon,
MatCardContent,
ReactiveFormsModule, ReactiveFormsModule,
MatFormField, MatFormField,
MatLabel, MatLabel,
MatInput, MatInput,
MatDatepickerInput, MatDatepickerModule,
MatDatepickerToggle,
MatSuffix, MatSuffix,
MatDatepicker,
MatButton, MatButton,
MatTable, MatTableModule,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
CurrencyPipe, CurrencyPipe,
MatOptionModule,
MatSelectModule,
], ],
}) })
export class SaleReportComponent implements OnInit { export class SaleReportComponent implements OnInit {
sections: Section[] = [];
info: SaleReport = new SaleReport(); info: SaleReport = new SaleReport();
dataSource: SaleReportDatasource = new SaleReportDatasource(this.info.amounts); dataSource: SaleReportDatasource = new SaleReportDatasource(this.info.amounts);
form: FormGroup<{ form: FormGroup<{
startDate: FormControl<Date>; startDate: FormControl<Date>;
finishDate: FormControl<Date>; finishDate: FormControl<Date>;
section: FormControl<string | null>;
}>; }>;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
@ -85,16 +66,19 @@ export class SaleReportComponent implements OnInit {
this.form = new FormGroup({ this.form = new FormGroup({
startDate: new FormControl(new Date(), { nonNullable: true }), startDate: new FormControl(new Date(), { nonNullable: true }),
finishDate: new FormControl(new Date(), { nonNullable: true }), finishDate: new FormControl(new Date(), { nonNullable: true }),
section: new FormControl<string | null>(null),
}); });
} }
ngOnInit() { ngOnInit() {
this.route.data.subscribe((value) => { this.route.data.subscribe((value) => {
const data = value as { info: SaleReport }; const data = value as { sections: Section[]; info: SaleReport };
this.sections = data.sections;
this.info = data.info; this.info = data.info;
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(),
section: this.info.sectionId || null,
}); });
this.dataSource = new SaleReportDatasource(this.info.amounts); this.dataSource = new SaleReportDatasource(this.info.amounts);
}); });
@ -102,25 +86,32 @@ export class SaleReportComponent implements OnInit {
show() { show() {
const info = this.getInfo(); const info = this.getInfo();
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(['sale-report'], { this.router.navigate(['sale-report'], {
queryParams: { queryParams: params,
startDate: info.startDate,
finishDate: info.finishDate,
},
}); });
} }
getInfo(): SaleReport { getInfo(): SaleReport {
const formModel = this.form.value; const formModel = this.form.value;
const data = new SaleReport({
return new SaleReport({
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'),
}); });
if (formModel.section) {
data.sectionId = formModel.section;
}
return data;
} }
print() { 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: () => { next: () => {
this.toaster.show('', 'Successfully Printed'); this.toaster.show('', 'Successfully Printed');
}, },

View File

@ -7,5 +7,6 @@ import { SaleReportService } from './sale-report.service';
export const saleReportResolver: ResolveFn<SaleReport> = (route) => { export const saleReportResolver: ResolveFn<SaleReport> = (route) => {
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 inject(SaleReportService).get(startDate, finishDate); const section = route.queryParamMap.get('section') ?? null;
return inject(SaleReportService).get(startDate, finishDate, section);
}; };

View File

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

View File

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

View File

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

View File

@ -92,7 +92,6 @@ export class DiscountComponent {
this.data.subscribe((list: DiscountItem[]) => { this.data.subscribe((list: DiscountItem[]) => {
this.list = list; this.list = list;
console.log(list);
this.form.controls.discounts.clear(); this.form.controls.discounts.clear();
this.list.forEach((x) => { this.list.forEach((x) => {

View File

@ -1,7 +1,12 @@
<h2 mat-dialog-title>Receive Payment</h2> <h2 mat-dialog-title>Receive Payment</h2>
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form" class="flex flex-col"> <form [formGroup]="form" class="flex flex-col">
<mat-table #table [dataSource]="dataSource" formArrayName="amounts" [style.visibility]="this.displayTable ? 'visible': 'hidden'"> <mat-table
#table
[dataSource]="dataSource"
formArrayName="amounts"
[style.visibility]="this.displayTable ? 'visible' : 'hidden'"
>
<!-- Name Column --> <!-- Name Column -->
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef class="bold">Amount</mat-header-cell> <mat-header-cell *matHeaderCellDef class="bold">Amount</mat-header-cell>
@ -24,7 +29,7 @@
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
<mat-footer-row *matFooterRowDef="displayedColumns"></mat-footer-row> <mat-footer-row *matFooterRowDef="displayedColumns"></mat-footer-row>
</mat-table> </mat-table>
<mat-form-field class="flex-auto" [style.visibility]="this.displayReason ? 'visible': 'hidden'"> <mat-form-field class="flex-auto" [style.visibility]="this.displayReason ? 'visible' : 'hidden'">
<mat-label>Reason</mat-label> <mat-label>Reason</mat-label>
<input <input
type="text" type="text"

View File

@ -138,7 +138,6 @@ export class ReceivePaymentComponent {
}), }),
), ),
); );
console.log('re', this.displayReason)
}); });
this.form.valueChanges.subscribe((x) => this.listenToAmountChange(x.amounts)); this.form.valueChanges.subscribe((x) => this.listenToAmountChange(x.amounts));
} }

View File

@ -103,7 +103,6 @@ export class UserDetailComponent implements OnInit, AfterViewInit {
this.ser.saveOrUpdate(this.getItem()).subscribe({ this.ser.saveOrUpdate(this.getItem()).subscribe({
next: () => { next: () => {
this.toaster.show('Success', ''); this.toaster.show('Success', '');
console.log(this.item.id);
if ((this.item.id as string) === 'me') { if ((this.item.id as string) === 'me') {
this.router.navigateByUrl('/'); this.router.navigateByUrl('/');
} else { } else {