Sale report now can be section wise

This commit is contained in:
Amritanshu Agrawal 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 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.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.product import Product
@ -34,6 +36,7 @@ router = APIRouter()
def get_sale_report(
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=["sale-report"]),
) -> SaleReport:
check_audit_permission(start_date, user.permissions)
@ -41,8 +44,9 @@ def get_sale_report(
return SaleReport(
start_date=start_date,
finish_date=finish_date,
section_id=section,
amounts=(
get_sale(start_date, finish_date, db)
get_sale(start_date, finish_date, section, db)
+ [SaleReportItem(name="--", amount=Decimal(0))]
+ get_settlements(start_date, finish_date, db)
+ [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(
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")
list_ = db.execute(
query = (
select(SaleCategory.name, func.sum(Inventory.net))
.join(Inventory.kot)
.join(Kot.voucher)
.join(Inventory.product)
.join(Product.versions)
.join(ProductVersion.sale_category)
.join(Voucher.food_table)
.where(
Voucher.date >= start_date,
Voucher.date <= finish_date,
@ -82,9 +87,11 @@ def get_sale(s: date, f: date, db: Session) -> list[SaleReportItem]:
ProductVersion.valid_till >= day,
),
)
.group_by(SaleCategory.name)
.order_by(SaleCategory.name)
).all()
)
if id_:
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)
info = []
for gt, am in list_:
@ -121,6 +128,7 @@ def get_settlements(s: date, f: date, db: Session) -> list[SaleReportItem]:
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=["sale-report"]),
) -> bool:
@ -130,7 +138,7 @@ def print_report(
start_date=start_date,
finish_date=finish_date,
amounts=(
get_sale(start_date, finish_date, db)
get_sale(start_date, finish_date, section, db)
+ [SaleReportItem(name="--", amount=Decimal(0))]
+ get_settlements(start_date, finish_date, db)
+ [SaleReportItem(name="--", amount=Decimal(0))]

View File

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

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

@ -2,26 +2,18 @@ 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 { MatCard, MatCardHeader, MatCardTitleGroup, MatCardTitle, MatCardContent } from '@angular/material/card';
import { MatDatepickerInput, MatDatepickerToggle, MatDatepicker } from '@angular/material/datepicker';
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 {
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
} from '@angular/material/table';
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 +26,30 @@ import { SaleReportService } from './sale-report.service';
templateUrl: './sale-report.component.html',
styleUrls: ['./sale-report.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatCardModule,
MatIconButton,
MatIcon,
MatCardContent,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepickerModule,
MatSuffix,
MatDatepicker,
MatButton,
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatTableModule,
CurrencyPipe,
MatOptionModule,
MatSelectModule,
],
})
export class SaleReportComponent implements OnInit {
sections: Section[] = [];
info: SaleReport = new SaleReport();
dataSource: SaleReportDatasource = new SaleReportDatasource(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 +66,19 @@ export class SaleReportComponent 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: SaleReport };
const data = value as { sections: Section[]; info: SaleReport };
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 SaleReportDatasource(this.info.amounts);
});
@ -102,25 +86,32 @@ export class SaleReportComponent implements OnInit {
show() {
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'], {
queryParams: {
startDate: info.startDate,
finishDate: info.finishDate,
},
queryParams: params,
});
}
getInfo(): SaleReport {
const formModel = this.form.value;
return new SaleReport({
const data = new SaleReport({
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 { SaleReportService } from './sale-report.service';
export const saleReportResolver: ResolveFn<SaleReport> = (route) => {
const startDate = route.queryParamMap.get('startDate') ?? 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 { authGuard } from '../auth/auth-guard.service';
import { sectionListResolver } from '../sections/section-list.resolver';
import { SaleReportComponent } from './sale-report.component';
import { saleReportResolver } from './sale-report.resolver';
@ -14,6 +15,7 @@ export const routes: Routes = [
permission: 'Sale Report',
},
resolve: {
sections: sectionListResolver,
info: saleReportResolver,
},
runGuardsAndResolvers: 'always',

View File

@ -19,7 +19,7 @@ export class SaleReportService {
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() };
if (startDate !== null) {
options.params = options.params.set('s', startDate);
@ -27,12 +27,15 @@ export class SaleReportService {
if (finishDate !== null) {
options.params = options.params.set('f', finishDate);
}
if (section !== null) {
options.params = options.params.set('section', section);
}
return this.http
.get<SaleReport>(url, options)
.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 options = { params: new HttpParams() };
if (startDate !== null) {
@ -41,6 +44,9 @@ export class SaleReportService {
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 { SaleReportItem } from './sale-report-item';
export class SaleReport {
startDate: string;
finishDate: string;
sectionId: string | null;
amounts: SaleReportItem[];
public constructor(init?: Partial<SaleReport>) {
this.startDate = '';
this.finishDate = '';
this.sectionId = null;
this.amounts = [];
Object.assign(this, init);
}

View File

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

View File

@ -1,7 +1,12 @@
<h2 mat-dialog-title>Receive Payment</h2>
<mat-dialog-content>
<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 -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef class="bold">Amount</mat-header-cell>
@ -24,7 +29,7 @@
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
<mat-footer-row *matFooterRowDef="displayedColumns"></mat-footer-row>
</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>
<input
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));
}

View File

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