Feature: Changed the unposted report to entries report with paging, sorting, etc.

This commit is contained in:
2021-09-14 11:36:40 +05:30
parent d34c8ea0a4
commit 176559466a
35 changed files with 776 additions and 518 deletions

View File

@ -57,7 +57,7 @@ from .routers.reports import (
reconcile,
stock_movement,
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(stock_movement.router, prefix="/api/stock-movement", 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(batch.router, prefix="/api/batch", tags=["vouchers"])

View File

@ -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()

View 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
],
)

View File

@ -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

View 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"),
}

View File

@ -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")}