Feature: Changed the unposted report to entries report with paging, sorting, etc.
This commit is contained in:
@ -57,7 +57,7 @@ from .routers.reports import (
|
|||||||
reconcile,
|
reconcile,
|
||||||
stock_movement,
|
stock_movement,
|
||||||
trial_balance,
|
trial_balance,
|
||||||
unposted,
|
entries,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ app.include_router(raw_material_cost.router, prefix="/api/raw-material-cost", ta
|
|||||||
app.include_router(reconcile.router, prefix="/api/reconcile", tags=["reports"])
|
app.include_router(reconcile.router, prefix="/api/reconcile", tags=["reports"])
|
||||||
app.include_router(stock_movement.router, prefix="/api/stock-movement", tags=["reports"])
|
app.include_router(stock_movement.router, prefix="/api/stock-movement", tags=["reports"])
|
||||||
app.include_router(trial_balance.router, prefix="/api/trial-balance", tags=["reports"])
|
app.include_router(trial_balance.router, prefix="/api/trial-balance", tags=["reports"])
|
||||||
app.include_router(unposted.router, prefix="/api/unposted", tags=["reports"])
|
app.include_router(entries.router, prefix="/api/entries", tags=["reports"])
|
||||||
|
|
||||||
app.include_router(issue_grid.router, prefix="/api/issue-grid", tags=["vouchers"])
|
app.include_router(issue_grid.router, prefix="/api/issue-grid", tags=["vouchers"])
|
||||||
app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"])
|
app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"])
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
from datetime import date, datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def report_start_date(s: str = None) -> Optional[date]:
|
||||||
|
return None if s is None else datetime.strptime(s, "%d-%b-%Y").date()
|
||||||
|
|
||||||
|
|
||||||
|
def report_finish_date(f: str = None) -> Optional[date]:
|
||||||
|
return None if f is None else datetime.strptime(f, "%d-%b-%Y").date()
|
||||||
|
|||||||
117
brewman/brewman/routers/reports/entries.py
Normal file
117
brewman/brewman/routers/reports/entries.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
from datetime import date
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import brewman.schemas.entries as schemas
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, Security
|
||||||
|
from sqlalchemy import desc, or_, select
|
||||||
|
from sqlalchemy.orm import Session, contains_eager, joinedload
|
||||||
|
from sqlalchemy.sql.functions import count
|
||||||
|
|
||||||
|
from ...core.security import get_current_active_user as get_user
|
||||||
|
from ...db.session import SessionFuture
|
||||||
|
from ...models.journal import Journal
|
||||||
|
from ...models.voucher import Voucher
|
||||||
|
from ...models.voucher_type import VoucherType
|
||||||
|
from ...schemas.user import UserToken
|
||||||
|
from ...schemas.user_link import UserLink
|
||||||
|
from . import report_finish_date, report_start_date
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def build_report(
|
||||||
|
start_date: Optional[date],
|
||||||
|
finish_date: date,
|
||||||
|
posted: Optional[bool],
|
||||||
|
issue: bool,
|
||||||
|
page_size: int,
|
||||||
|
page_index: int,
|
||||||
|
active_sort: str,
|
||||||
|
sort_direction: str,
|
||||||
|
db: Session,
|
||||||
|
) -> (int, List[Voucher]):
|
||||||
|
query = (
|
||||||
|
select(Voucher)
|
||||||
|
.join(Voucher.user)
|
||||||
|
.options(
|
||||||
|
joinedload(Voucher.user, innerjoin=True),
|
||||||
|
joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True),
|
||||||
|
contains_eager(Voucher.user),
|
||||||
|
contains_eager(Voucher.journals, Journal.account),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sq = select(Voucher.id)
|
||||||
|
counts = select(count(Voucher.id))
|
||||||
|
if start_date is not None:
|
||||||
|
sq = sq.where(or_(Voucher.creation_date >= start_date, Voucher.last_edit_date >= start_date))
|
||||||
|
counts = counts.where(or_(Voucher.creation_date >= start_date, Voucher.last_edit_date >= start_date))
|
||||||
|
if finish_date is not None:
|
||||||
|
sq = sq.where(or_(Voucher.creation_date <= finish_date, Voucher.last_edit_date <= finish_date))
|
||||||
|
counts = counts.where(or_(Voucher.creation_date <= finish_date, Voucher.last_edit_date <= finish_date))
|
||||||
|
if posted is not None:
|
||||||
|
sq = sq.where(Voucher.posted == posted)
|
||||||
|
counts = counts.where(Voucher.posted == posted)
|
||||||
|
if issue is False:
|
||||||
|
sq = sq.where(Voucher.type != VoucherType.by_name("Issue").id)
|
||||||
|
counts = counts.where(Voucher.type != VoucherType.by_name("Issue").id)
|
||||||
|
if active_sort == "date":
|
||||||
|
if sort_direction == "desc":
|
||||||
|
sq = sq.order_by(desc(Voucher.date), desc(Voucher.last_edit_date))
|
||||||
|
query = query.order_by(desc(Voucher.date), desc(Voucher.last_edit_date))
|
||||||
|
else:
|
||||||
|
sq = sq.order_by(Voucher.date, Voucher.last_edit_date)
|
||||||
|
query = query.order_by(Voucher.date, Voucher.last_edit_date)
|
||||||
|
if active_sort == "user":
|
||||||
|
if sort_direction == "desc":
|
||||||
|
sq = sq.order_by(desc(Voucher.last_edit_date))
|
||||||
|
query = query.order_by(desc(Voucher.last_edit_date))
|
||||||
|
else:
|
||||||
|
sq = sq.order_by(Voucher.last_edit_date)
|
||||||
|
query = query.order_by(Voucher.last_edit_date)
|
||||||
|
if page_size:
|
||||||
|
sq = sq.limit(page_size)
|
||||||
|
if page_index:
|
||||||
|
sq = sq.offset(page_size * page_index)
|
||||||
|
return db.execute(counts).scalar_one(), db.execute(query.where(Voucher.id.in_(sq))).unique().scalars().all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=schemas.Report)
|
||||||
|
def report_data(
|
||||||
|
start_date: Optional[date] = Depends(report_start_date),
|
||||||
|
finish_date: Optional[date] = Depends(report_finish_date),
|
||||||
|
p: Optional[bool] = None, # Posted
|
||||||
|
ps: Optional[int] = 50, # Page Size
|
||||||
|
pi: Optional[int] = 0, # Page Index
|
||||||
|
a: Optional[str] = "date", # Active Sort
|
||||||
|
d: Optional[str] = "desc", # Sort Direction
|
||||||
|
i: Optional[bool] = False, # Show issue vouchers
|
||||||
|
user: UserToken = Security(get_user, scopes=["ledger"]),
|
||||||
|
) -> schemas.Report:
|
||||||
|
with SessionFuture() as db:
|
||||||
|
counts, report = build_report(start_date, finish_date, p, i, ps, pi, a, d, db)
|
||||||
|
return schemas.Report(
|
||||||
|
counts=counts,
|
||||||
|
report=[
|
||||||
|
schemas.Entries(
|
||||||
|
id=voucher.id,
|
||||||
|
date=voucher.date,
|
||||||
|
url=[
|
||||||
|
"/",
|
||||||
|
VoucherType.by_id(voucher.type).name.replace(" ", "-").lower(),
|
||||||
|
str(voucher.id),
|
||||||
|
],
|
||||||
|
type=VoucherType.by_id(voucher.type).name,
|
||||||
|
posted=voucher.posted,
|
||||||
|
narration=voucher.narration,
|
||||||
|
debitNames=[x.account.name for x in voucher.journals if x.debit == 1],
|
||||||
|
creditNames=[x.account.name for x in voucher.journals if x.debit != 1],
|
||||||
|
amount=sum(x.amount for x in voucher.journals if x.debit != 1),
|
||||||
|
creationDate=voucher.creation_date,
|
||||||
|
lastEditDate=voucher.last_edit_date,
|
||||||
|
user=UserLink(id=voucher.user.id, name=voucher.user.name),
|
||||||
|
)
|
||||||
|
for voucher in report
|
||||||
|
],
|
||||||
|
)
|
||||||
@ -1,77 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
import brewman.schemas.unposted as schemas
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Security
|
|
||||||
from sqlalchemy import select
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from ...core.security import get_current_active_user as get_user
|
|
||||||
from ...db.session import SessionFuture
|
|
||||||
from ...models.journal import Journal
|
|
||||||
from ...models.voucher import Voucher
|
|
||||||
from ...models.voucher_type import VoucherType
|
|
||||||
from ...schemas.user import UserToken
|
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=List[schemas.Unposted])
|
|
||||||
def report_data(
|
|
||||||
user: UserToken = Security(get_user, scopes=["post-vouchers"]),
|
|
||||||
) -> List[schemas.Unposted]:
|
|
||||||
with SessionFuture() as db:
|
|
||||||
return build_report(db)
|
|
||||||
|
|
||||||
|
|
||||||
def build_report(db: Session) -> List[schemas.Unposted]:
|
|
||||||
body = []
|
|
||||||
|
|
||||||
query = (
|
|
||||||
db.execute(
|
|
||||||
select(Voucher)
|
|
||||||
.join(Voucher.journals)
|
|
||||||
.join(Journal.account)
|
|
||||||
.where(Voucher.posted == False, Voucher.type != VoucherType.by_name("Issue").id) # noqa: E712
|
|
||||||
.order_by(Voucher.date, Voucher.last_edit_date)
|
|
||||||
)
|
|
||||||
.unique()
|
|
||||||
.scalars()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
for voucher in query:
|
|
||||||
debit = 0
|
|
||||||
credit = 0
|
|
||||||
name_debit = ""
|
|
||||||
name_credit = ""
|
|
||||||
for journal in voucher.journals:
|
|
||||||
if journal.debit == 1:
|
|
||||||
debit += journal.amount
|
|
||||||
name_debit += "{0} / ".format(journal.account.name)
|
|
||||||
else:
|
|
||||||
credit += journal.amount
|
|
||||||
name_credit += "{0} / ".format(journal.account.name)
|
|
||||||
name_debit = name_debit[:-3]
|
|
||||||
name_credit = name_credit[:-3]
|
|
||||||
|
|
||||||
body.append(
|
|
||||||
schemas.Unposted(
|
|
||||||
id=voucher.id,
|
|
||||||
date=voucher.date.strftime("%d-%b-%Y"),
|
|
||||||
url=[
|
|
||||||
"/",
|
|
||||||
VoucherType.by_id(voucher.type).name.replace(" ", "-").lower(),
|
|
||||||
str(voucher.id),
|
|
||||||
],
|
|
||||||
type=VoucherType.by_id(voucher.type).name,
|
|
||||||
narration=voucher.narration,
|
|
||||||
debitName=name_debit,
|
|
||||||
debitAmount=debit,
|
|
||||||
creditName=name_credit,
|
|
||||||
creditAmount=credit,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return body
|
|
||||||
64
brewman/brewman/schemas/entries.py
Normal file
64
brewman/brewman/schemas/entries.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
|
from . import to_camel
|
||||||
|
from .user_link import UserLink
|
||||||
|
|
||||||
|
|
||||||
|
class Entries(BaseModel):
|
||||||
|
id_: uuid.UUID
|
||||||
|
date_: date
|
||||||
|
url: List[str]
|
||||||
|
type_: str
|
||||||
|
posted: bool
|
||||||
|
narration: str
|
||||||
|
debit_names: List[str]
|
||||||
|
credit_names: List[str]
|
||||||
|
amount: Decimal
|
||||||
|
creation_date: datetime
|
||||||
|
last_edit_date: datetime
|
||||||
|
user: UserLink
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
anystr_strip_whitespace = True
|
||||||
|
alias_generator = to_camel
|
||||||
|
json_encoders = {
|
||||||
|
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||||
|
datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"),
|
||||||
|
}
|
||||||
|
|
||||||
|
@validator("date_", pre=True)
|
||||||
|
def parse_date(cls, value):
|
||||||
|
if isinstance(value, date):
|
||||||
|
return value
|
||||||
|
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||||
|
|
||||||
|
@validator("creation_date", pre=True)
|
||||||
|
def parse_creation_date(cls, value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value
|
||||||
|
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||||
|
|
||||||
|
@validator("last_edit_date", pre=True)
|
||||||
|
def parse_last_edit_date(cls, value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value
|
||||||
|
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||||
|
|
||||||
|
|
||||||
|
class Report(BaseModel):
|
||||||
|
counts: int
|
||||||
|
report: List[Entries]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
anystr_strip_whitespace = True
|
||||||
|
alias_generator = to_camel
|
||||||
|
json_encoders = {
|
||||||
|
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||||
|
datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"),
|
||||||
|
}
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import uuid
|
|
||||||
|
|
||||||
from datetime import date, datetime
|
|
||||||
from decimal import Decimal
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from pydantic import BaseModel, validator
|
|
||||||
|
|
||||||
from . import to_camel
|
|
||||||
|
|
||||||
|
|
||||||
class Unposted(BaseModel):
|
|
||||||
id_: uuid.UUID
|
|
||||||
date_: date
|
|
||||||
url: List[str]
|
|
||||||
type_: str
|
|
||||||
narration: str
|
|
||||||
debit_name: str
|
|
||||||
debit_amount: Decimal
|
|
||||||
credit_name: str
|
|
||||||
credit_amount: Decimal
|
|
||||||
|
|
||||||
@validator("date_", pre=True)
|
|
||||||
def parse_date(cls, value):
|
|
||||||
if isinstance(value, date):
|
|
||||||
return value
|
|
||||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
anystr_strip_whitespace = True
|
|
||||||
alias_generator = to_camel
|
|
||||||
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")}
|
|
||||||
@ -165,8 +165,8 @@ const appRoutes: Routes = [
|
|||||||
import('./trial-balance/trial-balance.module').then((mod) => mod.TrialBalanceModule),
|
import('./trial-balance/trial-balance.module').then((mod) => mod.TrialBalanceModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'unposted',
|
path: 'entries',
|
||||||
loadChildren: () => import('./unposted/unposted.module').then((mod) => mod.UnpostedModule),
|
loadChildren: () => import('./entries/entries.module').then((mod) => mod.EntriesModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
<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 color="primary" (click)="show()" fxFlex="20c">Show</button>
|
<button mat-raised-button color="primary" (click)="show()" fxFlex="20">Show</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
||||||
|
|||||||
@ -19,7 +19,9 @@
|
|||||||
<a mat-menu-item routerLink="/profit-loss">Profit and Loss</a>
|
<a mat-menu-item routerLink="/profit-loss">Profit and Loss</a>
|
||||||
<a mat-menu-item routerLink="/balance-sheet">Balance Sheet</a>
|
<a mat-menu-item routerLink="/balance-sheet">Balance Sheet</a>
|
||||||
<a mat-menu-item routerLink="/net-transactions">Net Transactions</a>
|
<a mat-menu-item routerLink="/net-transactions">Net Transactions</a>
|
||||||
<a mat-menu-item routerLink="/unposted">UnPosted Entries</a>
|
<a mat-menu-item routerLink="/entries" [queryParams]="{ p: 'false', a: 'date', d: 'desc' }"
|
||||||
|
>All Entries</a
|
||||||
|
>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<button mat-button [matMenuTriggerFor]="reportMenu">Reports</button>
|
<button mat-button [matMenuTriggerFor]="reportMenu">Reports</button>
|
||||||
|
|
||||||
|
|||||||
45
overlord/src/app/entries/entries-datasource.ts
Normal file
45
overlord/src/app/entries/entries-datasource.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { DataSource } from '@angular/cdk/collections';
|
||||||
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { SortDirection } from '@angular/material/sort/sort-direction';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
import { Entries } from './entries';
|
||||||
|
|
||||||
|
export class EntriesDatasource extends DataSource<Entries> {
|
||||||
|
constructor(
|
||||||
|
public data: Entries[],
|
||||||
|
public length: number,
|
||||||
|
private paginator?: MatPaginator,
|
||||||
|
private sort?: MatSort,
|
||||||
|
private pageSize?: number,
|
||||||
|
private pageIndex?: number,
|
||||||
|
private activeSort?: string,
|
||||||
|
private sortDirection?: SortDirection,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(): Observable<Entries[]> {
|
||||||
|
if (this.paginator) {
|
||||||
|
this.paginator.length = this.length;
|
||||||
|
if (this.pageSize !== undefined) {
|
||||||
|
this.paginator.pageSize = this.pageSize;
|
||||||
|
}
|
||||||
|
if (this.pageIndex !== undefined) {
|
||||||
|
this.paginator.pageIndex = this.pageIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.sort) {
|
||||||
|
if (this.activeSort !== undefined) {
|
||||||
|
this.sort.active = this.activeSort;
|
||||||
|
}
|
||||||
|
if (this.sortDirection !== undefined) {
|
||||||
|
this.sort.direction = this.sortDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return observableOf(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {}
|
||||||
|
}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { inject, TestBed } from '@angular/core/testing';
|
import { inject, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { UnpostedResolver } from './unposted-resolver.service';
|
import { EntriesResolver } from './entries-resolver.service';
|
||||||
|
|
||||||
describe('UnpostedResolver', () => {
|
describe('EntriesResolver', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientModule],
|
imports: [HttpClientModule],
|
||||||
providers: [UnpostedResolver],
|
providers: [EntriesResolver],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', inject([UnpostedResolver], (service: UnpostedResolver) => {
|
it('should be created', inject([EntriesResolver], (service: EntriesResolver) => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
38
overlord/src/app/entries/entries-resolver.service.ts
Normal file
38
overlord/src/app/entries/entries-resolver.service.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
|
import { EntriesService } from './entries.service';
|
||||||
|
import { Report } from './report';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class EntriesResolver implements Resolve<Report> {
|
||||||
|
constructor(private ser: EntriesService) {}
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot): Observable<Report> {
|
||||||
|
const start_date = route.queryParamMap.get('s');
|
||||||
|
const finish_date = route.queryParamMap.get('f');
|
||||||
|
const posted =
|
||||||
|
route.queryParamMap.get('p') === null ? null : route.queryParamMap.get('p') === 'true';
|
||||||
|
const page_size =
|
||||||
|
route.queryParamMap.get('ps') === null ? 50 : +(route.queryParamMap.get('ps') as string);
|
||||||
|
const page_index =
|
||||||
|
route.queryParamMap.get('pi') === null ? 0 : +(route.queryParamMap.get('pi') as string);
|
||||||
|
const sort = route.queryParamMap.get('a') ?? 'date';
|
||||||
|
const direction = route.queryParamMap.get('d') ?? 'desc';
|
||||||
|
const issue_vouchers =
|
||||||
|
route.queryParamMap.get('i') === null ? false : route.queryParamMap.get('i') === 'true';
|
||||||
|
return this.ser.list(
|
||||||
|
start_date,
|
||||||
|
finish_date,
|
||||||
|
posted,
|
||||||
|
page_size,
|
||||||
|
page_index,
|
||||||
|
sort,
|
||||||
|
direction,
|
||||||
|
issue_vouchers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
overlord/src/app/entries/entries-routing.module.spec.ts
Normal file
13
overlord/src/app/entries/entries-routing.module.spec.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { EntriesRoutingModule } from './entries-routing.module';
|
||||||
|
|
||||||
|
describe('EntriesRoutingModule', () => {
|
||||||
|
let entriesRoutingModule: EntriesRoutingModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
entriesRoutingModule = new EntriesRoutingModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(entriesRoutingModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
overlord/src/app/entries/entries-routing.module.ts
Normal file
30
overlord/src/app/entries/entries-routing.module.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { AuthGuard } from '../auth/auth-guard.service';
|
||||||
|
|
||||||
|
import { EntriesResolver } from './entries-resolver.service';
|
||||||
|
import { EntriesComponent } from './entries.component';
|
||||||
|
|
||||||
|
const entriesRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: EntriesComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
data: {
|
||||||
|
permission: 'Ledger',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
info: EntriesResolver,
|
||||||
|
},
|
||||||
|
runGuardsAndResolvers: 'always',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, RouterModule.forChild(entriesRoutes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
providers: [EntriesResolver],
|
||||||
|
})
|
||||||
|
export class EntriesRoutingModule {}
|
||||||
19
overlord/src/app/entries/entries.component.css
Normal file
19
overlord/src/app/entries/entries.component.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.my-margin {
|
||||||
|
margin: 0 12px;
|
||||||
|
}
|
||||||
|
.selected {
|
||||||
|
background: #fff3cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unposted {
|
||||||
|
background: #f8d7da;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-date,
|
||||||
|
.mat-column-voucherType {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
138
overlord/src/app/entries/entries.component.html
Normal file
138
overlord/src/app/entries/entries.component.html
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-title-group>
|
||||||
|
<mat-card-title>Entries</mat-card-title>
|
||||||
|
</mat-card-title-group>
|
||||||
|
<form [formGroup]="form" fxLayout="column">
|
||||||
|
<div
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayout.lt-md="column"
|
||||||
|
fxLayoutGap="20px"
|
||||||
|
fxLayoutGap.lt-md="0px"
|
||||||
|
fxLayoutAlign="space-around start"
|
||||||
|
>
|
||||||
|
<mat-form-field fxFlex="40%">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="startDate"
|
||||||
|
placeholder="Create / Last Edit Date From"
|
||||||
|
formControlName="startDate"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #startDate></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field fxFlex="40%">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="finishDate"
|
||||||
|
placeholder="Create / Last Edit Date Till"
|
||||||
|
formControlName="finishDate"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="finishDate"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #finishDate></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button (click)="refresh()" fxFlex="20%">
|
||||||
|
<mat-icon>refresh</mat-icon>Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayout.lt-md="column"
|
||||||
|
fxLayoutGap="20px"
|
||||||
|
fxLayoutGap.lt-md="0px"
|
||||||
|
fxLayoutAlign="space-around start"
|
||||||
|
>
|
||||||
|
<mat-radio-group fxFlex="40%" formControlName="posted" (change)="togglePosted($event)">
|
||||||
|
<mat-radio-button class="my-margin" value="true"> Posted </mat-radio-button>
|
||||||
|
<mat-radio-button class="my-margin" value="false"> Not Posted </mat-radio-button>
|
||||||
|
<mat-radio-button class="my-margin" value="null"> All </mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<mat-checkbox
|
||||||
|
fxFlex="40%"
|
||||||
|
formControlName="issue"
|
||||||
|
class="my-margin"
|
||||||
|
[checked]="issue"
|
||||||
|
(change)="toggleIssue()"
|
||||||
|
>
|
||||||
|
Include Issue Entries
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
||||||
|
<!-- Date Column -->
|
||||||
|
<ng-container matColumnDef="date">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Date</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row"
|
||||||
|
><a [routerLink]="row.url">{{ row.date }}</a></mat-cell
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Type Column -->
|
||||||
|
<ng-container matColumnDef="voucherType">
|
||||||
|
<mat-header-cell *matHeaderCellDef>Type</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{ row.type }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Narration Column -->
|
||||||
|
<ng-container matColumnDef="narration">
|
||||||
|
<mat-header-cell *matHeaderCellDef>Narration</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{ row.narration }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- DebitName Column -->
|
||||||
|
<ng-container matColumnDef="debitNames">
|
||||||
|
<mat-header-cell *matHeaderCellDef>Debit</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let item of row.debitNames">{{ item }}</li>
|
||||||
|
</ul>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- CreditName Column -->
|
||||||
|
<ng-container matColumnDef="creditNames">
|
||||||
|
<mat-header-cell *matHeaderCellDef>Credit</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let item of row.creditNames">{{ item }}</li>
|
||||||
|
</ul>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Amount Column -->
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="right">Amount</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="right">{{ row.amount | currency: 'INR' }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- User Column -->
|
||||||
|
<ng-container matColumnDef="user">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">User</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">
|
||||||
|
<ul>
|
||||||
|
<li>{{ row.creationDate | localTime }}</li>
|
||||||
|
<li>{{ row.lastEditDate | localTime }}</li>
|
||||||
|
<li>{{ row.user.name }}</li>
|
||||||
|
</ul>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
|
<mat-row
|
||||||
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
|
[class.unposted]="!row.posted && posted !== false && row.type !== 'Issue'"
|
||||||
|
></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
|
||||||
|
<mat-paginator
|
||||||
|
#paginator
|
||||||
|
[length]="dataSource.data.length"
|
||||||
|
[pageIndex]="0"
|
||||||
|
[pageSize]="50"
|
||||||
|
[pageSizeOptions]="[25, 50, 100, 250, 300]"
|
||||||
|
>
|
||||||
|
</mat-paginator>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
@ -2,23 +2,23 @@ import { HttpClientModule } from '@angular/common/http';
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
|
||||||
import { UnpostedComponent } from './unposted.component';
|
import { EntriesComponent } from './entries.component';
|
||||||
|
|
||||||
describe('UnpostedComponent', () => {
|
describe('EntriesComponent', () => {
|
||||||
let component: UnpostedComponent;
|
let component: EntriesComponent;
|
||||||
let fixture: ComponentFixture<UnpostedComponent>;
|
let fixture: ComponentFixture<EntriesComponent>;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(
|
||||||
waitForAsync(() => {
|
waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientModule, RouterTestingModule],
|
imports: [HttpClientModule, RouterTestingModule],
|
||||||
declarations: [UnpostedComponent],
|
declarations: [EntriesComponent],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(UnpostedComponent);
|
fixture = TestBed.createComponent(EntriesComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
143
overlord/src/app/entries/entries.component.ts
Normal file
143
overlord/src/app/entries/entries.component.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||||
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatRadioChange } from '@angular/material/radio';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { SortDirection } from '@angular/material/sort/sort-direction';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
import { EntriesDatasource } from './entries-datasource';
|
||||||
|
import { Report } from './report';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-entries',
|
||||||
|
templateUrl: './entries.component.html',
|
||||||
|
styleUrls: ['./entries.component.css'],
|
||||||
|
})
|
||||||
|
export class EntriesComponent implements OnInit {
|
||||||
|
@ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator;
|
||||||
|
@ViewChild(MatSort, { static: true }) sort?: MatSort;
|
||||||
|
info: Report = new Report();
|
||||||
|
dataSource: EntriesDatasource = new EntriesDatasource(this.info.report, 0);
|
||||||
|
form: FormGroup;
|
||||||
|
displayedColumns = [
|
||||||
|
'date',
|
||||||
|
'voucherType',
|
||||||
|
'narration',
|
||||||
|
'debitNames',
|
||||||
|
'creditNames',
|
||||||
|
'amount',
|
||||||
|
'user',
|
||||||
|
];
|
||||||
|
|
||||||
|
posted: boolean | null = null;
|
||||||
|
issue: boolean = false;
|
||||||
|
constructor(private route: ActivatedRoute, private router: Router, private fb: FormBuilder) {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
startDate: '',
|
||||||
|
finishDate: '',
|
||||||
|
posted: '',
|
||||||
|
issue: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.data.subscribe((value) => {
|
||||||
|
const data = value as { info: Report };
|
||||||
|
this.info = data.info;
|
||||||
|
const queryMap = this.route.snapshot.queryParamMap;
|
||||||
|
const startDateO = queryMap.get('s');
|
||||||
|
const finishDateO = queryMap.get('f');
|
||||||
|
const startDate = startDateO !== null ? moment(startDateO, 'DD-MMM-YYYY').toDate() : '';
|
||||||
|
const finishDate = finishDateO !== null ? moment(finishDateO, 'DD-MMM-YYYY').toDate() : '';
|
||||||
|
const postedO = queryMap.get('p');
|
||||||
|
this.posted = postedO !== null ? postedO === 'true' : null;
|
||||||
|
const pageSizeO = queryMap.get('ps');
|
||||||
|
const pageSize = pageSizeO !== null ? +pageSizeO : 50;
|
||||||
|
const pageIndexO = queryMap.get('pi');
|
||||||
|
const pageIndex = pageIndexO !== null ? +pageIndexO : 0;
|
||||||
|
const activeSortO = queryMap.get('a');
|
||||||
|
const activeSort = activeSortO !== null ? (activeSortO as string) : 'date';
|
||||||
|
const sortDirectionO = queryMap.get('d');
|
||||||
|
const sortDirection = sortDirectionO !== null ? (sortDirectionO as SortDirection) : 'desc';
|
||||||
|
const issueO = queryMap.get('i');
|
||||||
|
this.issue = issueO !== null ? issueO === 'true' : false;
|
||||||
|
this.form.setValue({
|
||||||
|
startDate: startDate,
|
||||||
|
finishDate: finishDate,
|
||||||
|
posted: postedO ?? 'null',
|
||||||
|
issue: this.issue,
|
||||||
|
});
|
||||||
|
if (this.posted === null || this.posted) {
|
||||||
|
(this.form.get('issue') as FormControl).enable();
|
||||||
|
} else {
|
||||||
|
(this.form.get('issue') as FormControl).disable();
|
||||||
|
}
|
||||||
|
this.dataSource = new EntriesDatasource(
|
||||||
|
this.info.report,
|
||||||
|
this.info.counts,
|
||||||
|
this.paginator,
|
||||||
|
this.sort,
|
||||||
|
pageSize,
|
||||||
|
pageIndex,
|
||||||
|
activeSort,
|
||||||
|
sortDirection,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
this.sort?.sortChange.subscribe((x) =>
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: { a: this.sort?.active ?? 'date', d: this.sort?.direction },
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.paginator?.page.subscribe((x) =>
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: { ps: this.paginator?.pageSize, pi: this.paginator?.pageIndex },
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
const formModel = this.form.value;
|
||||||
|
const startDate = formModel.startDate
|
||||||
|
? moment(formModel.startDate).format('DD-MMM-YYYY')
|
||||||
|
: null;
|
||||||
|
const finishDate = formModel.finishDate
|
||||||
|
? moment(formModel.finishDate).format('DD-MMM-YYYY')
|
||||||
|
: null;
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: { s: startDate, f: finishDate },
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePosted($event: MatRadioChange) {
|
||||||
|
if ($event.value === 'true') {
|
||||||
|
this.posted = true;
|
||||||
|
} else if ($event.value === 'false') {
|
||||||
|
this.posted = false;
|
||||||
|
this.issue = false;
|
||||||
|
} else {
|
||||||
|
this.posted = null;
|
||||||
|
}
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: { p: this.posted, i: this.issue || null },
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleIssue() {
|
||||||
|
this.issue = !this.issue;
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: { i: this.issue || null },
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
13
overlord/src/app/entries/entries.module.spec.ts
Normal file
13
overlord/src/app/entries/entries.module.spec.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { EntriesModule } from './entries.module';
|
||||||
|
|
||||||
|
describe('EntriesModule', () => {
|
||||||
|
let entriesModule: EntriesModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
entriesModule = new EntriesModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(entriesModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -2,22 +2,32 @@ import { A11yModule } from '@angular/cdk/a11y';
|
|||||||
import { CdkTableModule } from '@angular/cdk/table';
|
import { CdkTableModule } from '@angular/cdk/table';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MomentDateAdapter } from '@angular/material-moment-adapter';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatNativeDateModule } from '@angular/material/core';
|
import {
|
||||||
|
DateAdapter,
|
||||||
|
MAT_DATE_FORMATS,
|
||||||
|
MAT_DATE_LOCALE,
|
||||||
|
MatNativeDateModule,
|
||||||
|
} from '@angular/material/core';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
import { MatExpansionModule } from '@angular/material/expansion';
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
import { MatSortModule } from '@angular/material/sort';
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
import { UnpostedRoutingModule } from './unposted-routing.module';
|
import { EntriesRoutingModule } from './entries-routing.module';
|
||||||
import { UnpostedComponent } from './unposted.component';
|
import { EntriesComponent } from './entries.component';
|
||||||
|
|
||||||
export const MY_FORMATS = {
|
export const MY_FORMATS = {
|
||||||
parse: {
|
parse: {
|
||||||
@ -44,12 +54,20 @@ export const MY_FORMATS = {
|
|||||||
MatNativeDateModule,
|
MatNativeDateModule,
|
||||||
MatPaginatorModule,
|
MatPaginatorModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
|
MatRadioModule,
|
||||||
MatSortModule,
|
MatSortModule,
|
||||||
MatTableModule,
|
MatTableModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
UnpostedRoutingModule,
|
EntriesRoutingModule,
|
||||||
MatExpansionModule,
|
MatExpansionModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
],
|
||||||
|
declarations: [EntriesComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
|
||||||
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
||||||
],
|
],
|
||||||
declarations: [UnpostedComponent],
|
|
||||||
})
|
})
|
||||||
export class UnpostedModule {}
|
export class EntriesModule {}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { inject, TestBed } from '@angular/core/testing';
|
import { inject, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { UnpostedService } from './unposted.service';
|
import { EntriesService } from './entries.service';
|
||||||
|
|
||||||
describe('UnpostedService', () => {
|
describe('EntriesService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientModule],
|
imports: [HttpClientModule],
|
||||||
providers: [UnpostedService],
|
providers: [EntriesService],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', inject([UnpostedService], (service: UnpostedService) => {
|
it('should be created', inject([EntriesService], (service: EntriesService) => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
44
overlord/src/app/entries/entries.service.ts
Normal file
44
overlord/src/app/entries/entries.service.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { ErrorLoggerService } from '../core/error-logger.service';
|
||||||
|
|
||||||
|
import { Report } from './report';
|
||||||
|
|
||||||
|
const url = '/api/entries';
|
||||||
|
const serviceName = 'EntriesService';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class EntriesService {
|
||||||
|
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
|
||||||
|
|
||||||
|
list(
|
||||||
|
start_date: string | null,
|
||||||
|
finish_date: string | null,
|
||||||
|
posted: boolean | null,
|
||||||
|
page_size: number,
|
||||||
|
page_index: number,
|
||||||
|
sort: string,
|
||||||
|
direction: string,
|
||||||
|
issue: boolean,
|
||||||
|
): Observable<Report> {
|
||||||
|
let params = new HttpParams();
|
||||||
|
if (start_date !== null) params = params.set('s', start_date);
|
||||||
|
if (finish_date !== null) params = params.set('f', finish_date);
|
||||||
|
if (posted !== null) params = params.set('p', posted.toString());
|
||||||
|
params = params
|
||||||
|
.set('ps', page_size)
|
||||||
|
.set('pi', page_index)
|
||||||
|
.set('a', sort)
|
||||||
|
.set('d', direction)
|
||||||
|
.set('i', issue.toString());
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get<Report>(url, { params })
|
||||||
|
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Report>;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
overlord/src/app/entries/entries.ts
Normal file
32
overlord/src/app/entries/entries.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { User } from '../core/user';
|
||||||
|
|
||||||
|
export class Entries {
|
||||||
|
id: string;
|
||||||
|
date: string;
|
||||||
|
url: string[];
|
||||||
|
type: string;
|
||||||
|
posted: boolean;
|
||||||
|
narration: string;
|
||||||
|
debitNames: string[];
|
||||||
|
creditNames: string[];
|
||||||
|
amount: number;
|
||||||
|
creationDate: string;
|
||||||
|
lastEditDate: string;
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
public constructor(init?: Partial<Entries>) {
|
||||||
|
this.id = '';
|
||||||
|
this.date = '';
|
||||||
|
this.url = [];
|
||||||
|
this.type = '';
|
||||||
|
this.posted = false;
|
||||||
|
this.narration = '';
|
||||||
|
this.debitNames = [];
|
||||||
|
this.creditNames = [];
|
||||||
|
this.amount = 0;
|
||||||
|
this.creationDate = '';
|
||||||
|
this.lastEditDate = '';
|
||||||
|
this.user = new User();
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
overlord/src/app/entries/report.ts
Normal file
12
overlord/src/app/entries/report.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Entries } from './entries';
|
||||||
|
|
||||||
|
export class Report {
|
||||||
|
counts: number;
|
||||||
|
report: Entries[];
|
||||||
|
|
||||||
|
public constructor(init?: Partial<Report>) {
|
||||||
|
this.counts = 0;
|
||||||
|
this.report = [];
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -364,20 +364,21 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
productSelected(event: MatAutocompleteSelectedEvent): void {
|
productSelected(event: MatAutocompleteSelectedEvent): void {
|
||||||
const product: Product = event.option.value;
|
const product: Product = event.option.value;
|
||||||
|
const addRowForm: FormControl = this.form.get('addRow') as FormControl;
|
||||||
this.product = product;
|
this.product = product;
|
||||||
((this.form.get('addRow') as FormControl).get('price') as FormControl).setValue(product.price);
|
(addRowForm.get('price') as FormControl).setValue(product.price);
|
||||||
if (product.isRateContracted) {
|
if (product.isRateContracted) {
|
||||||
((this.form.get('addRow') as FormControl).get('price') as FormControl).disable();
|
(addRowForm.get('price') as FormControl).disable();
|
||||||
((this.form.get('addRow') as FormControl).get('tax') as FormControl).disable();
|
(addRowForm.get('tax') as FormControl).disable();
|
||||||
((this.form.get('addRow') as FormControl).get('discount') as FormControl).disable();
|
(addRowForm.get('discount') as FormControl).disable();
|
||||||
((this.form.get('addRow') as FormControl).get('tax') as FormControl).setValue('RC');
|
(addRowForm.get('tax') as FormControl).setValue('RC');
|
||||||
((this.form.get('addRow') as FormControl).get('discount') as FormControl).setValue('RC');
|
(addRowForm.get('discount') as FormControl).setValue('RC');
|
||||||
} else {
|
} else {
|
||||||
((this.form.get('addRow') as FormControl).get('price') as FormControl).enable();
|
(addRowForm.get('price') as FormControl).enable();
|
||||||
((this.form.get('addRow') as FormControl).get('tax') as FormControl).enable();
|
(addRowForm.get('tax') as FormControl).enable();
|
||||||
((this.form.get('addRow') as FormControl).get('discount') as FormControl).enable();
|
(addRowForm.get('discount') as FormControl).enable();
|
||||||
((this.form.get('addRow') as FormControl).get('tax') as FormControl).setValue('');
|
(addRowForm.get('tax') as FormControl).setValue('');
|
||||||
((this.form.get('addRow') as FormControl).get('discount') as FormControl).setValue('');
|
(addRowForm.get('discount') as FormControl).setValue('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
import { DataSource } from '@angular/cdk/collections';
|
|
||||||
import { EventEmitter } from '@angular/core';
|
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
|
||||||
import { MatSort, Sort } from '@angular/material/sort';
|
|
||||||
import { merge, Observable, of as observableOf } from 'rxjs';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { Unposted } from './unposted';
|
|
||||||
|
|
||||||
/** Simple sort comparator for example ID/Name columns (for client-side sorting). */
|
|
||||||
const compare = (a: string | number, b: string | number, isAsc: boolean) =>
|
|
||||||
(a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
|
||||||
|
|
||||||
export class UnpostedDataSource extends DataSource<Unposted> {
|
|
||||||
constructor(public data: Unposted[], private paginator?: MatPaginator, private sort?: MatSort) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(): Observable<Unposted[]> {
|
|
||||||
const dataMutations: (Observable<Unposted[]> | EventEmitter<PageEvent> | EventEmitter<Sort>)[] =
|
|
||||||
[observableOf(this.data)];
|
|
||||||
if (this.paginator) {
|
|
||||||
dataMutations.push((this.paginator as MatPaginator).page);
|
|
||||||
}
|
|
||||||
if (this.sort) {
|
|
||||||
dataMutations.push((this.sort as MatSort).sortChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the paginators length
|
|
||||||
if (this.paginator) {
|
|
||||||
this.paginator.length = this.data.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return merge(...dataMutations).pipe(
|
|
||||||
map(() => this.getPagedData(this.getSortedData([...this.data]))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {}
|
|
||||||
|
|
||||||
private getPagedData(data: Unposted[]) {
|
|
||||||
if (this.paginator === undefined) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
|
||||||
return data.splice(startIndex, this.paginator.pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSortedData(data: Unposted[]) {
|
|
||||||
if (this.sort === undefined) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
if (!this.sort.active || this.sort.direction === '') {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
const sort = this.sort as MatSort;
|
|
||||||
|
|
||||||
return data.sort((a: Unposted, b: Unposted) => {
|
|
||||||
const isAsc = sort.direction === 'asc';
|
|
||||||
switch (sort.active) {
|
|
||||||
case 'date':
|
|
||||||
return compare(a.date, b.date, isAsc);
|
|
||||||
case 'type':
|
|
||||||
return compare(a.type, b.type, isAsc);
|
|
||||||
case 'debitAmount':
|
|
||||||
return compare(+a.debitAmount, +b.debitAmount, isAsc);
|
|
||||||
case 'creditAmount':
|
|
||||||
return compare(+a.creditAmount, +b.creditAmount, isAsc);
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Resolve } from '@angular/router';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
|
|
||||||
import { Unposted } from './unposted';
|
|
||||||
import { UnpostedService } from './unposted.service';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class UnpostedResolver implements Resolve<Unposted[]> {
|
|
||||||
constructor(private ser: UnpostedService) {}
|
|
||||||
|
|
||||||
resolve(): Observable<Unposted[]> {
|
|
||||||
return this.ser.list();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { UnpostedRoutingModule } from './unposted-routing.module';
|
|
||||||
|
|
||||||
describe('UnpostedRoutingModule', () => {
|
|
||||||
let unpostedRoutingModule: UnpostedRoutingModule;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
unpostedRoutingModule = new UnpostedRoutingModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an instance', () => {
|
|
||||||
expect(unpostedRoutingModule).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
|
||||||
|
|
||||||
import { AuthGuard } from '../auth/auth-guard.service';
|
|
||||||
|
|
||||||
import { UnpostedResolver } from './unposted-resolver.service';
|
|
||||||
import { UnpostedComponent } from './unposted.component';
|
|
||||||
|
|
||||||
const unpostedRoutes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: UnpostedComponent,
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
data: {
|
|
||||||
permission: 'post-vouchers',
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
info: UnpostedResolver,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [CommonModule, RouterModule.forChild(unpostedRoutes)],
|
|
||||||
exports: [RouterModule],
|
|
||||||
providers: [UnpostedResolver],
|
|
||||||
})
|
|
||||||
export class UnpostedRoutingModule {}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
.right {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
.my-margin {
|
|
||||||
margin: 0 12px;
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
<mat-card>
|
|
||||||
<mat-card-title-group>
|
|
||||||
<mat-card-title>Unposted</mat-card-title>
|
|
||||||
<a mat-button (click)="refresh()">
|
|
||||||
<mat-icon>refresh</mat-icon>
|
|
||||||
Refresh
|
|
||||||
</a>
|
|
||||||
</mat-card-title-group>
|
|
||||||
<mat-expansion-panel (opened)="panelOpenState = true" (closed)="panelOpenState = false">
|
|
||||||
<mat-expansion-panel-header>
|
|
||||||
<mat-panel-title> Self aware panel </mat-panel-title>
|
|
||||||
<mat-panel-description>
|
|
||||||
Currently I am {{ panelOpenState ? 'open' : 'closed' }}
|
|
||||||
</mat-panel-description>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
<mat-checkbox
|
|
||||||
class="my-margin"
|
|
||||||
[checked]="!!unposted"
|
|
||||||
[indeterminate]="unposted === undefined"
|
|
||||||
(change)="toggleUnposted()"
|
|
||||||
>
|
|
||||||
Unposted
|
|
||||||
</mat-checkbox>
|
|
||||||
<p></p>
|
|
||||||
<mat-checkbox
|
|
||||||
class="my-margin"
|
|
||||||
[checked]="!!unposted"
|
|
||||||
[indeterminate]="unposted === undefined"
|
|
||||||
(change)="toggleUnposted()"
|
|
||||||
>
|
|
||||||
All userscv
|
|
||||||
</mat-checkbox>
|
|
||||||
<mat-checkbox
|
|
||||||
class="my-margin"
|
|
||||||
[checked]="!!unposted"
|
|
||||||
[indeterminate]="unposted === undefined"
|
|
||||||
(change)="toggleUnposted()"
|
|
||||||
>
|
|
||||||
Unposted
|
|
||||||
</mat-checkbox>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
<mat-card-content>
|
|
||||||
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
|
||||||
<!-- Date Column -->
|
|
||||||
<ng-container matColumnDef="date">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Date</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row"
|
|
||||||
><a [routerLink]="row.url">{{ row.date }}</a></mat-cell
|
|
||||||
>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- Type Column -->
|
|
||||||
<ng-container matColumnDef="voucherType">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Type</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row">{{ row.type }}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- Narration Column -->
|
|
||||||
<ng-container matColumnDef="narration">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Narration</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row">{{ row.narration }}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- DebitName Column -->
|
|
||||||
<ng-container matColumnDef="debitName">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Debit</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row">{{ row.debitName }}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- DebitAmount Column -->
|
|
||||||
<ng-container matColumnDef="debitAmount">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Amount</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row" class="right">{{
|
|
||||||
row.debitAmount | currency: 'INR' | accounting
|
|
||||||
}}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- CreditName Column -->
|
|
||||||
<ng-container matColumnDef="creditName">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Credit</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row">{{ row.creditName }}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- CreditAmount Column -->
|
|
||||||
<ng-container matColumnDef="creditAmount">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Amount</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row" class="right">{{
|
|
||||||
row.creditAmount | currency: 'INR' | accounting
|
|
||||||
}}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
|
||||||
<mat-row
|
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
|
||||||
[class.posted]="row.isPosted"
|
|
||||||
></mat-row>
|
|
||||||
</mat-table>
|
|
||||||
|
|
||||||
<mat-paginator
|
|
||||||
#paginator
|
|
||||||
[length]="dataSource.data.length"
|
|
||||||
[pageIndex]="0"
|
|
||||||
[pageSize]="50"
|
|
||||||
[pageSizeOptions]="[25, 50, 100, 250, 300]"
|
|
||||||
>
|
|
||||||
</mat-paginator>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
|
||||||
import { MatSort } from '@angular/material/sort';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { Unposted } from './unposted';
|
|
||||||
import { UnpostedDataSource } from './unposted-datasource';
|
|
||||||
import { UnpostedService } from './unposted.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-unposted',
|
|
||||||
templateUrl: './unposted.component.html',
|
|
||||||
styleUrls: ['./unposted.component.css'],
|
|
||||||
})
|
|
||||||
export class UnpostedComponent implements OnInit {
|
|
||||||
@ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator;
|
|
||||||
@ViewChild(MatSort, { static: true }) sort?: MatSort;
|
|
||||||
info: Unposted[] = [];
|
|
||||||
dataSource: UnpostedDataSource = new UnpostedDataSource(this.info);
|
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
|
||||||
displayedColumns = [
|
|
||||||
'date',
|
|
||||||
'voucherType',
|
|
||||||
'narration',
|
|
||||||
'debitName',
|
|
||||||
'debitAmount',
|
|
||||||
'creditName',
|
|
||||||
'creditAmount',
|
|
||||||
];
|
|
||||||
|
|
||||||
panelOpenState = false;
|
|
||||||
unposted: boolean | undefined = undefined;
|
|
||||||
constructor(
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router,
|
|
||||||
private ser: UnpostedService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.route.data.subscribe((value) => {
|
|
||||||
const data = value as { info: Unposted[] };
|
|
||||||
|
|
||||||
this.info = data.info;
|
|
||||||
});
|
|
||||||
this.dataSource = new UnpostedDataSource(this.info, this.paginator, this.sort);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.ser.list().subscribe((info: Unposted[]) => {
|
|
||||||
this.info = info;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleUnposted() {
|
|
||||||
if (this.unposted === undefined) {
|
|
||||||
this.unposted = false;
|
|
||||||
} else if (!this.unposted) {
|
|
||||||
this.unposted = true;
|
|
||||||
} else {
|
|
||||||
this.unposted = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { UnpostedModule } from './unposted.module';
|
|
||||||
|
|
||||||
describe('UnpostedModule', () => {
|
|
||||||
let unpostedModule: UnpostedModule;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
unpostedModule = new UnpostedModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an instance', () => {
|
|
||||||
expect(unpostedModule).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { ErrorLoggerService } from '../core/error-logger.service';
|
|
||||||
|
|
||||||
import { Unposted } from './unposted';
|
|
||||||
|
|
||||||
const url = '/api/unposted';
|
|
||||||
const serviceName = 'UnpostedService';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class UnpostedService {
|
|
||||||
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
|
|
||||||
|
|
||||||
list(): Observable<Unposted[]> {
|
|
||||||
return this.http
|
|
||||||
.get<Unposted[]>(url)
|
|
||||||
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Unposted[]>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
export class Unposted {
|
|
||||||
id: string;
|
|
||||||
date: string;
|
|
||||||
url: string[];
|
|
||||||
type: string;
|
|
||||||
narration: string;
|
|
||||||
debitName: string;
|
|
||||||
debitAmount: number;
|
|
||||||
creditName: string;
|
|
||||||
creditAmount: number;
|
|
||||||
|
|
||||||
public constructor(init?: Partial<Unposted>) {
|
|
||||||
this.id = '';
|
|
||||||
this.date = '';
|
|
||||||
this.url = [];
|
|
||||||
this.type = '';
|
|
||||||
this.narration = '';
|
|
||||||
this.debitName = '';
|
|
||||||
this.debitAmount = 0;
|
|
||||||
this.creditName = '';
|
|
||||||
this.creditAmount = 0;
|
|
||||||
Object.assign(this, init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user