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,
|
||||
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"])
|
||||
|
||||
@ -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")}
|
||||
Reference in New Issue
Block a user