Support date query param for journal / payment / reciept voucher

This commit is contained in:
2025-07-15 17:26:42 +00:00
parent e35db8144b
commit efd9d1da7a
17 changed files with 264 additions and 448 deletions

View File

@ -1,9 +1,12 @@
from collections.abc import Sequence
from datetime import UTC, date, datetime, timedelta
from fastapi import Request
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from brewman.core.session import get_date
from ..models.db_setting import DbSetting
from ..models.setting_type import SettingType
from ..models.voucher_type import VoucherType
@ -83,3 +86,10 @@ def effective_date(d: str | None = None) -> date:
if d is None
else datetime.strptime(d, "%d-%b-%Y").date()
)
def session_or_query_date(request: Request, d: str | None = None) -> date:
if d:
return datetime.strptime(d, "%d-%b-%Y").date()
session_date_str = get_date(request.session)
return datetime.strptime(session_date_str, "%d-%b-%Y").date()

View File

@ -1,6 +1,6 @@
import uuid
from datetime import UTC, datetime
from datetime import UTC, date, datetime
from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status
from sqlalchemy import distinct, select
@ -8,7 +8,7 @@ from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, set_date
from ..core.session import set_date
from ..db.session import SessionFuture
from ..models.account_base import AccountBase
from ..models.journal import Journal
@ -20,7 +20,7 @@ from ..schemas import voucher as output
from ..schemas.account import AccountLink
from ..schemas.blank_voucher_info import BlankVoucherInfo
from ..schemas.user import UserToken
from . import get_lock_info
from . import get_lock_info, session_or_query_date
from .db_image import save_files, update_files
from .tag import save_tags, update_tags
from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info
@ -99,8 +99,8 @@ def update_route(
t: list[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["journal"]),
) -> output.Voucher:
try:
with SessionFuture() as db:
with SessionFuture() as db:
try:
item: Voucher = update_voucher(id_, data, user, db)
check_journals_are_valid(item)
update_files(item.id, data.files, i, t, db)
@ -108,15 +108,15 @@ def update_route(
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def update_voucher(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
@ -192,6 +192,7 @@ def get_id(
def show_blank(
request: Request,
a: uuid.UUID | None = None,
date_: date = Depends(session_or_query_date),
user: UserToken = Security(get_user, scopes=["journal"]),
) -> output.Voucher:
if request.scope.get("path") == "/api/payment":
@ -201,7 +202,7 @@ def show_blank(
else:
type_ = VoucherType.JOURNAL
additional_info = BlankVoucherInfo(date_=get_date(request.session), type_=type_) # type: ignore[arg-type]
additional_info = BlankVoucherInfo(date_=date_, type_=type_) # type: ignore[arg-type]
if a:
additional_info.account = AccountLink(id_=a)
with SessionFuture() as db:

View File

@ -38,11 +38,16 @@ export class VoucherService {
.pipe(catchError(this.log.handleError(serviceName, 'Get Voucher'))) as Observable<Voucher>;
}
getOfType(type: string, account?: string): Observable<Voucher> {
getOfType(type: string, account: string | null, date: string | null): Observable<Voucher> {
const endpoint = type.replace(/ /g, '-').toLowerCase();
let options = {};
if (account !== undefined && account !== null) {
options = { params: new HttpParams().set('a', account) };
const options = {
params: new HttpParams(),
};
if (account !== null) {
options.params = options.params.set('a', account);
}
if (date !== null) {
options.params = options.params.set('d', date);
}
return this.http
.get<Voucher>(`${url}/${endpoint}`, options)

View File

@ -6,8 +6,9 @@ import { VoucherService } from '../core/voucher.service';
export const employeeBenefitsResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const date = route.queryParamMap.get('d') || null;
if (id === null) {
return inject(VoucherService).getOfType('Employee Benefit');
return inject(VoucherService).getOfType('Employee Benefit', null, date);
}
return inject(VoucherService).get(id, 'Employee Benefit');
};

View File

@ -6,8 +6,9 @@ import { VoucherService } from '../core/voucher.service';
export const incentiveResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const date = route.queryParamMap.get('d') || null;
if (id === null) {
return inject(VoucherService).getOfType('Incentive');
return inject(VoucherService).getOfType('Incentive', null, date);
}
return inject(VoucherService).get(id, 'Incentive');
};

View File

@ -1,37 +1,19 @@
import { AsyncPipe, CurrencyPipe, DecimalPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardFooter,
MatCardHeader,
MatCardTitle,
} from '@angular/material/card';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
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 { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatSelect } from '@angular/material/select';
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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -60,39 +42,18 @@ import { IssueGridService } from './issue-grid.service';
templateUrl: './issue.component.html',
styleUrls: ['./issue.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitle,
MatCardContent,
MatCardModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatSuffix,
MatDatepicker,
MatSelect,
MatOption,
MatPrefix,
MatAutocompleteTrigger,
MatAutocomplete,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatIcon,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatCardActions,
MatCardFooter,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatSelectModule,
MatOptionModule,
MatAutocompleteModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatSortModule,
AsyncPipe,
DecimalPipe,
CurrencyPipe,
@ -133,7 +94,7 @@ export class IssueComponent implements OnInit, AfterViewInit {
dataSource: IssueDataSource = new IssueDataSource(this.inventoryObservable);
gridDataSource: IssueGridDataSource = new IssueGridDataSource(this.gridObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
source: FormControl<string | null>;
destination: FormControl<string | null>;
amount: FormControl<number>;
@ -155,7 +116,7 @@ export class IssueComponent implements OnInit, AfterViewInit {
constructor() {
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
source: new FormControl<string | null>(null),
destination: new FormControl<string | null>(null),
amount: new FormControl({ value: 0, disabled: true }, { nonNullable: true }),
@ -198,7 +159,7 @@ export class IssueComponent implements OnInit, AfterViewInit {
loadVoucher(voucher: Voucher) {
this.voucher = voucher;
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
source: (this.voucher.source as CostCentre).id ?? '',
destination: (this.voucher.destination as CostCentre).id ?? '',
amount: Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)),

View File

@ -6,8 +6,9 @@ import { VoucherService } from '../core/voucher.service';
export const issueResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const date = route.queryParamMap.get('d') || null;
if (id === null) {
return inject(VoucherService).getOfType('Issue');
return inject(VoucherService).getOfType('Issue', null, date);
}
return inject(VoucherService).get(id, 'Issue');
};

View File

@ -2,38 +2,20 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { AsyncPipe, CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardTitleGroup,
} from '@angular/material/card';
import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatHint, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatSelect } from '@angular/material/select';
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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -65,44 +47,19 @@ import { JournalDialogComponent } from './journal-dialog.component';
templateUrl: './journal.component.html',
styleUrls: ['./journal.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIcon,
MatSuffix,
MatCardContent,
MatCardModule,
MatIconModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepicker,
MatSelect,
MatOption,
MatAutocompleteTrigger,
MatHint,
MatAutocomplete,
MatPrefix,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatChipGrid,
MatChipRow,
MatChipRemove,
MatChipInput,
MatCardActions,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatSelectModule,
MatOptionModule,
MatAutocompleteModule,
MatButtonModule,
MatTableModule,
MatSortModule,
MatChipsModule,
AsyncPipe,
CurrencyPipe,
AccountingPipe,
@ -135,13 +92,23 @@ export class JournalComponent implements OnInit, AfterViewInit {
@HostListener('window:keydown.f5', ['$event'])
redirectToPayment(event: KeyboardEvent) {
event.preventDefault();
this.router.navigate(['payment']);
this.router.navigate(['payment'], {
queryParams: {
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@HostListener('window:keydown.f6', ['$event'])
redirectToReciept(event: KeyboardEvent) {
event.preventDefault();
this.router.navigate(['receipt']);
this.router.navigate(['receipt'], {
queryParams: {
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@HostListener('window:keydown.f7', ['$event'])
@ -169,7 +136,7 @@ export class JournalComponent implements OnInit, AfterViewInit {
public journalObservable = new BehaviorSubject<Journal[]>([]);
dataSource: JournalDataSource = new JournalDataSource(this.journalObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
addRow: FormGroup<{
debit: FormControl<number>;
account: FormControl<string | null>;
@ -191,7 +158,7 @@ export class JournalComponent implements OnInit, AfterViewInit {
constructor() {
this.account = null;
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
addRow: new FormGroup({
debit: new FormControl(1, { nonNullable: true }),
account: new FormControl(''),
@ -213,6 +180,15 @@ export class JournalComponent implements OnInit, AfterViewInit {
map((tag: string | Tag | null) => (tag === null ? '' : typeof tag !== 'string' ? tag.name.toLowerCase() : tag)),
switchMap((tag: string) => this.tagSer.autocomplete(tag)),
);
// Listen to Date Change
this.form.controls.date.valueChanges.subscribe((x) =>
this.router.navigate([], {
relativeTo: this.route,
queryParams: { d: moment(x).format('DD-MMM-YYYY') },
replaceUrl: true,
queryParamsHandling: 'merge',
}),
);
}
ngOnInit() {
@ -230,7 +206,7 @@ export class JournalComponent implements OnInit, AfterViewInit {
loadVoucher(voucher: Voucher) {
this.voucher = voucher;
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
addRow: {
debit: 1,
account: null,

View File

@ -6,8 +6,9 @@ import { VoucherService } from '../core/voucher.service';
export const journalResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const date = route.queryParamMap.get('d') || null;
if (id === null) {
return inject(VoucherService).getOfType('Journal');
return inject(VoucherService).getOfType('Journal', null, date);
}
return inject(VoucherService).get(id, 'Journal');
};

View File

@ -2,38 +2,20 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { AsyncPipe, CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardTitleGroup,
} from '@angular/material/card';
import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatHint, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatSelect } from '@angular/material/select';
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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -65,44 +47,19 @@ import { PaymentDialogComponent } from './payment-dialog.component';
templateUrl: './payment.component.html',
styleUrls: ['./payment.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIcon,
MatSuffix,
MatCardContent,
MatCardModule,
MatIconModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepicker,
MatSelect,
MatOption,
MatPrefix,
MatAutocompleteTrigger,
MatHint,
MatAutocomplete,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatChipGrid,
MatChipRow,
MatChipRemove,
MatChipInput,
MatCardActions,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatSelectModule,
MatOptionModule,
MatAutocompleteModule,
MatButtonModule,
MatTableModule,
MatSortModule,
MatChipsModule,
AsyncPipe,
CurrencyPipe,
AccountingPipe,
@ -143,14 +100,22 @@ export class PaymentComponent implements OnInit, AfterViewInit {
this.router.navigate(['receipt'], {
queryParams: {
a: this.form.controls.paymentAccount.value,
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@HostListener('window:keydown.f7', ['$event'])
redirectToJournal(event: KeyboardEvent) {
event.preventDefault();
this.router.navigate(['journal']);
this.router.navigate(['journal'], {
queryParams: {
a: this.form.controls.paymentAccount.value,
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@HostListener('window:keydown.control.s', ['$event'])
@ -173,7 +138,7 @@ export class PaymentComponent implements OnInit, AfterViewInit {
public journalObservable = new BehaviorSubject<Journal[]>([]);
dataSource: PaymentDataSource = new PaymentDataSource(this.journalObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
paymentAccount: FormControl<string>;
paymentAmount: FormControl<number>;
addRow: FormGroup<{
@ -198,7 +163,7 @@ export class PaymentComponent implements OnInit, AfterViewInit {
constructor() {
this.account = null;
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
paymentAccount: new FormControl('', { nonNullable: true }),
paymentAmount: new FormControl({ value: 0, disabled: true }, { nonNullable: true }),
addRow: new FormGroup({
@ -221,12 +186,22 @@ export class PaymentComponent implements OnInit, AfterViewInit {
map((tag: string | Tag | null) => (tag === null ? '' : typeof tag !== 'string' ? tag.name.toLowerCase() : tag)),
switchMap((tag: string) => this.tagSer.autocomplete(tag)),
);
// Listen to Date Change
this.form.controls.date.valueChanges.subscribe((x) =>
this.router.navigate([], {
relativeTo: this.route,
queryParams: { d: x.format('DD-MMM-YYYY') },
replaceUrl: true,
queryParamsHandling: 'merge',
}),
);
// Listen to Payment Account Change
this.form.controls.paymentAccount.valueChanges.subscribe((x) =>
this.router.navigate([], {
relativeTo: this.route,
queryParams: { a: x },
replaceUrl: true,
queryParamsHandling: 'merge',
}),
);
}
@ -248,7 +223,7 @@ export class PaymentComponent implements OnInit, AfterViewInit {
this.voucher = voucher;
[this.paymentJournal] = this.voucher.journals.filter((x) => x.debit === -1);
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
paymentAccount: this.paymentJournal.account.id ?? '',
paymentAmount: this.paymentJournal.amount,
addRow: {

View File

@ -7,11 +7,9 @@ import { VoucherService } from '../core/voucher.service';
export const paymentResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const account = route.queryParamMap.get('a') || null;
const date = route.queryParamMap.get('d') || null;
if (id !== null) {
return inject(VoucherService).get(id, 'Payment');
}
if (account !== null) {
return inject(VoucherService).getOfType('Payment', account);
}
return inject(VoucherService).getOfType('Payment');
return inject(VoucherService).getOfType('Payment', account, date);
};

View File

@ -2,37 +2,19 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { AsyncPipe, CurrencyPipe, DecimalPipe, PercentPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardTitleGroup,
} from '@angular/material/card';
import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatHint, MatLabel, MatPrefix, 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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -66,43 +48,18 @@ import { PurchaseReturnDialogComponent } from './purchase-return-dialog.componen
templateUrl: './purchase-return.component.html',
styleUrls: ['./purchase-return.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIcon,
MatSuffix,
MatCardContent,
MatCardModule,
MatIconModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepicker,
MatAutocompleteTrigger,
MatHint,
MatAutocomplete,
MatOption,
MatPrefix,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatChipGrid,
MatChipRow,
MatChipRemove,
MatChipInput,
MatCardActions,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatAutocompleteModule,
MatOptionModule,
MatButtonModule,
MatTableModule,
MatSortModule,
MatChipsModule,
AsyncPipe,
DecimalPipe,
PercentPipe,
@ -156,7 +113,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit {
public inventoryObservable = new BehaviorSubject<Inventory[]>([]);
dataSource: PurchaseReturnDataSource = new PurchaseReturnDataSource(this.inventoryObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
account: FormControl<string | null>;
amount: FormControl<number>;
addRow: FormGroup<{
@ -179,7 +136,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit {
constructor() {
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
account: new FormControl<string | null>(null),
amount: new FormControl({ value: 0, disabled: true }, { nonNullable: true }),
addRow: new FormGroup({
@ -228,7 +185,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit {
loadVoucher(voucher: Voucher) {
this.voucher = voucher;
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
account: this.voucher.vendor?.name ?? null,
amount: Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)),
addRow: {

View File

@ -7,7 +7,7 @@ import { VoucherService } from '../core/voucher.service';
export const purchaseReturnResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
if (id === null) {
return inject(VoucherService).getOfType('Purchase Return');
return inject(VoucherService).getOfType('Purchase Return', null, null);
}
return inject(VoucherService).get(id, 'Purchase Return');
};

View File

@ -2,37 +2,19 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { AsyncPipe, CurrencyPipe, DecimalPipe, PercentPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardTitleGroup,
} from '@angular/material/card';
import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatHint, MatLabel, MatPrefix, 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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -68,43 +50,18 @@ import { PurchaseDialogComponent } from './purchase-dialog.component';
templateUrl: './purchase.component.html',
styleUrls: ['./purchase.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIcon,
MatSuffix,
MatCardContent,
MatCardModule,
MatIconModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepicker,
MatAutocompleteTrigger,
MatHint,
MatAutocomplete,
MatOption,
MatPrefix,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatChipGrid,
MatChipRow,
MatChipRemove,
MatChipInput,
MatCardActions,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatAutocompleteModule,
MatOptionModule,
MatButtonModule,
MatTableModule,
MatSortModule,
MatChipsModule,
AsyncPipe,
DecimalPipe,
PercentPipe,
@ -158,7 +115,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit {
public inventoryObservable = new BehaviorSubject<Inventory[]>([]);
dataSource: PurchaseDataSource = new PurchaseDataSource(this.inventoryObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
account: FormControl<string | null>;
amount: FormControl<number>;
addRow: FormGroup<{
@ -184,7 +141,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit {
constructor() {
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
account: new FormControl<string | null>(null),
amount: new FormControl({ value: 0, disabled: true }, { nonNullable: true }),
addRow: new FormGroup({
@ -243,7 +200,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit {
loadVoucher(voucher: Voucher) {
this.voucher = voucher;
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
account: this.voucher.vendor?.name ?? null,
amount: Math.abs(this.voucher.inventories.map((x) => x.amount).reduce((p, c) => p + c, 0)),
addRow: {

View File

@ -7,7 +7,7 @@ import { VoucherService } from '../core/voucher.service';
export const purchaseResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
if (id === null) {
return inject(VoucherService).getOfType('Purchase');
return inject(VoucherService).getOfType('Purchase', null, null);
}
return inject(VoucherService).get(id, 'Purchase');
};

View File

@ -2,38 +2,20 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { AsyncPipe, CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { MatButton, MatIconButton } from '@angular/material/button';
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardTitle,
MatCardTitleGroup,
} from '@angular/material/card';
import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips';
import { MatOption } from '@angular/material/core';
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { MatOptionModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormField, MatHint, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { MatSelect } from '@angular/material/select';
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 { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort } from '@angular/material/sort';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import { round } from 'mathjs';
import moment from 'moment';
@ -65,44 +47,19 @@ import { ReceiptDialogComponent } from './receipt-dialog.component';
templateUrl: './receipt.component.html',
styleUrls: ['./receipt.component.css'],
imports: [
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardTitle,
MatIcon,
MatSuffix,
MatCardContent,
MatCardModule,
MatIconModule,
ReactiveFormsModule,
MatFormField,
MatLabel,
MatInput,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepicker,
MatSelect,
MatOption,
MatPrefix,
MatAutocompleteTrigger,
MatHint,
MatAutocomplete,
MatButton,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatIconButton,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatChipGrid,
MatChipRow,
MatChipRemove,
MatChipInput,
MatCardActions,
MatFormFieldModule,
MatDatepickerModule,
MatInputModule,
MatSelectModule,
MatOptionModule,
MatAutocompleteModule,
MatButtonModule,
MatTableModule,
MatSortModule,
MatChipsModule,
AsyncPipe,
CurrencyPipe,
AccountingPipe,
@ -138,7 +95,9 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
this.router.navigate(['payment'], {
queryParams: {
a: this.form.controls.receiptAccount.value,
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@ -150,7 +109,13 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
@HostListener('window:keydown.f7', ['$event'])
redirectToJournal(event: KeyboardEvent) {
event.preventDefault();
this.router.navigate(['journal']);
this.router.navigate(['journal'], {
queryParams: {
a: this.form.controls.receiptAccount.value,
d: this.form.controls.date.value.format('DD-MMM-YYYY'),
},
queryParamsHandling: 'merge',
});
}
@HostListener('window:keydown.control.s', ['$event'])
@ -173,7 +138,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
public journalObservable = new BehaviorSubject<Journal[]>([]);
dataSource: ReceiptDataSource = new ReceiptDataSource(this.journalObservable);
form: FormGroup<{
date: FormControl<Date>;
date: FormControl<moment.Moment>;
receiptAccount: FormControl<string>;
receiptAmount: FormControl<number>;
addRow: FormGroup<{
@ -197,7 +162,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
constructor() {
this.form = new FormGroup({
date: new FormControl(new Date(), { nonNullable: true }),
date: new FormControl(moment(new Date()), { nonNullable: true }),
receiptAccount: new FormControl('', { nonNullable: true }),
receiptAmount: new FormControl({ value: 0, disabled: true }, { nonNullable: true }),
addRow: new FormGroup({
@ -220,12 +185,21 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
map((tag: string | Tag | null) => (tag === null ? '' : typeof tag !== 'string' ? tag.name.toLowerCase() : tag)),
switchMap((tag: string) => this.tagSer.autocomplete(tag)),
);
// Listen to Date Change
this.form.controls.date.valueChanges.subscribe((x) =>
this.router.navigate([], {
relativeTo: this.route,
queryParams: { d: x.format('DD-MMM-YYYY') },
replaceUrl: true,
queryParamsHandling: 'merge',
}),
);
// Listen to Receipt Account Change
this.form.controls.receiptAccount.valueChanges.subscribe((x) =>
this.router.navigate([], {
relativeTo: this.route,
queryParams: { a: x },
replaceUrl: true,
queryParamsHandling: 'merge',
}),
);
}
@ -247,7 +221,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit {
this.voucher = voucher;
[this.receiptJournal] = this.voucher.journals.filter((x) => x.debit === 1);
this.form.setValue({
date: moment(this.voucher.date, 'DD-MMM-YYYY').toDate(),
date: moment(this.voucher.date, 'DD-MMM-YYYY'),
receiptAccount: this.receiptJournal.account.id ?? '',
receiptAmount: this.receiptJournal.amount,
addRow: {

View File

@ -7,11 +7,9 @@ import { VoucherService } from '../core/voucher.service';
export const receiptResolver: ResolveFn<Voucher> = (route) => {
const id = route.paramMap.get('id');
const account = route.queryParamMap.get('a') || null;
const date = route.queryParamMap.get('d') || null;
if (id !== null) {
return inject(VoucherService).get(id, 'Receipt');
}
if (account !== null) {
return inject(VoucherService).getOfType('Receipt', account);
}
return inject(VoucherService).getOfType('Receipt');
return inject(VoucherService).getOfType('Receipt', account, date);
};