Compare commits

..
9 Commits
Author SHA1 Message Date
tanshu 82747c4b89 Version Bump v12.1.2 2024-12-17 09:04:56 +05:30
tanshu e732354671 Fix: run was referencing files in a subdirectory.
Fix: Playbook was not importing the default vars.
Improvement: Dockerfile casheing should improve.
2024-12-17 09:04:49 +05:30
tanshu 6d3057bb55 Version Bump v12.1.1 2024-12-17 08:58:54 +05:30
tanshu 15a93c599e Chore: ruff check will fix the errors also. 2024-12-17 08:58:45 +05:30
tanshu 823e3a761e Fix: Docker entrypoint command. run.sh is in the /app subdirectory 2024-12-17 08:58:17 +05:30
tanshu ac728128b9 Chore: Cleanup Module imports 2024-12-17 08:57:52 +05:30
tanshu 5536bcae41 Version Bump v12.1.0 2024-12-17 08:47:13 +05:30
tanshu 55d5017254 Product sale report can now be section wise 2024-12-17 08:46:29 +05:30
tanshu bbb7be8070 Sale report now can be section wise 2024-12-17 08:23:18 +05:30
29 changed files with 206 additions and 171 deletions
+3 -3
View File
@@ -28,8 +28,6 @@ RUN \
FROM python:3.12 AS runner
LABEL maintainer="Amritanshu <docker@tanshu.com>"
COPY barker/pyproject.toml /app/pyproject.toml
RUN apt-get update && \
apt-get install -y locales && \
sed --in-place --expression='s/# en_IN UTF-8/en_IN UTF-8/' /etc/locale.gen && \
@@ -46,6 +44,8 @@ RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python
WORKDIR /app
COPY barker/pyproject.toml /app/pyproject.toml
# Allow installing dev dependencies to run tests
ARG INSTALL_DEV=false
RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --only main ; fi"
@@ -62,4 +62,4 @@ RUN chmod 777 /app/docker-entrypoint.sh \
&& ln -s /app/docker-entrypoint.sh /
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["poetry", "run", "run.sh"]
CMD ["poetry", "run", "/app/run.sh"]
+1 -1
View File
@@ -5,7 +5,7 @@
- hosts: barker
become: true
vars_files:
- default
- vars/default.yml
- "{{ var_file }}"
roles:
+1 -1
View File
@@ -1 +1 @@
__version__ = "12.0.1"
__version__ = "12.1.2"
@@ -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(
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()
)
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)
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)
+17 -8
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:
@@ -129,8 +137,9 @@ def print_report(
report = 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))]
+3
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)
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "barker"
version = "12.0.1"
version = "12.1.2"
description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"]
+1 -1
View File
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
gunicorn barker.main:app --worker-class uvicorn.workers.UvicornWorker --config ./barker/gunicorn.conf.py --log-config ./barker/logging.conf
gunicorn barker.main:app --worker-class uvicorn.workers.UvicornWorker --config ./gunicorn.conf.py --log-config ./logging.conf
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bookie",
"version": "12.0.1",
"version": "12.1.2",
"scripts": {
"ng": "ng",
"start": "ng serve",
+1 -1
View File
@@ -1,7 +1,7 @@
export const environment = {
production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '12.0.1',
version: '12.1.2',
};
export const dateFormat = {
+12 -18
View File
@@ -1,11 +1,11 @@
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardActions } from '@angular/material/card';
import { MatDivider } from '@angular/material/divider';
import { MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { ActivatedRoute, Router } from '@angular/router';
import { ToasterService } from '../../core/toaster.service';
@@ -17,19 +17,13 @@ import { AuthService } from '../auth.service';
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitle,
MatCardContent,
MatCardModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatIcon,
MatSuffix,
MatDivider,
MatCardActions,
MatButton,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatDividerModule,
MatButtonModule,
],
})
export class LoginComponent implements OnInit, AfterViewInit {
@@ -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">
@@ -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();
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: {
startDate: info.startDate,
finishDate: info.finishDate,
},
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');
},
@@ -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);
};
@@ -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',
@@ -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>;
@@ -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);
}
@@ -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">
@@ -1,27 +1,19 @@
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 { 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 { MatOptionModule } from '@angular/material/core';
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 +26,27 @@ import { SaleReportService } from './sale-report.service';
templateUrl: './sale-report.component.html',
styleUrls: ['./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,
MatIconModule,
MatInputModule,
MatDatepickerModule,
MatTableModule,
MatOptionModule,
MatSelectModule,
CurrencyPipe,
],
})
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 +63,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 +83,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');
},
@@ -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);
};
@@ -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',
@@ -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>;
@@ -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);
}
@@ -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) => {
@@ -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"
@@ -138,7 +138,6 @@ export class ReceivePaymentComponent {
}),
),
);
console.log('re', this.displayReason)
});
this.form.valueChanges.subscribe((x) => this.listenToAmountChange(x.amounts));
}
@@ -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 {
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "frank"
version = "12.0.1"
version = "12.1.2"
description = "Point of Sale for a restaurant"
authors = ["tanshu <git@tanshu.com>"]
+2 -2
View File
@@ -10,7 +10,7 @@ npx ng lint --fix
cd "$parent_path/barker" || exit
ruff format .
ruff check . # Lint all files in the current directory (and any subdirectories)
bandit --recursive brewman
ruff check --fix .
bandit --recursive barker
bandit --recursive .
safety check