From a4b9fb7408a9ad5dc15a817f144a1be0e5d32260 Mon Sep 17 00:00:00 2001 From: tanshu Date: Thu, 14 May 2020 11:26:28 +0530 Subject: [PATCH] Works: Balance sheet Ledger Cash Flow along with urls Balance sheet schema does not enforce multiple_of for amounts as multiple_of borks on random figures --- brewman/models/master.py | 2 +- brewman/models/voucher.py | 10 +- brewman/routers/reports/balance_sheet.py | 51 +++---- brewman/routers/reports/cash_flow.py | 11 +- brewman/routers/reports/ledger.py | 2 + brewman/routers/reports/unposted.py | 1 - brewman/schemas/reports.py | 137 ++++++++---------- .../balance-sheet.component.html | 8 +- .../balance-sheet/balance-sheet.component.ts | 11 +- .../src/app/balance-sheet/balance-sheet.ts | 11 +- .../app/cash-flow/cash-flow.component.html | 2 +- overlord/src/app/cash-flow/cash-flow.ts | 2 +- overlord/src/app/ledger/ledger.component.html | 2 +- overlord/src/app/ledger/ledger.component.ts | 7 +- overlord/src/app/unposted/unposted.ts | 1 - 15 files changed, 120 insertions(+), 138 deletions(-) diff --git a/brewman/models/master.py b/brewman/models/master.py index b618f817..399f6716 100644 --- a/brewman/models/master.py +++ b/brewman/models/master.py @@ -268,7 +268,7 @@ class AccountBase(Base): __mapper_args__ = {"polymorphic_on": account_type} - journals = relationship("Journal", backref="account") + journals = relationship("Journal", back_populates="account") @property def __name__(self): diff --git a/brewman/models/voucher.py b/brewman/models/voucher.py index 28fd3095..eb320491 100644 --- a/brewman/models/voucher.py +++ b/brewman/models/voucher.py @@ -80,12 +80,7 @@ class Voucher(Base): "User", primaryjoin="User.id==Voucher.poster_id", cascade=None ) - journals = relationship( - "Journal", - backref="voucher", - cascade="delete, delete-orphan", - cascade_backrefs=False, - ) + journals = relationship("Journal", back_populates="voucher", cascade="delete, delete-orphan", cascade_backrefs=False,) inventories = relationship( "Inventory", backref="voucher", @@ -167,6 +162,9 @@ class Journal(Base): account_id = Column("account_id", GUID(), ForeignKey("accounts.id"), nullable=False) cost_centre_id = Column("cost_centre_id", GUID(), ForeignKey("cost_centres.id"), nullable=False) + voucher = relationship("Voucher", back_populates="journals") + account = relationship("AccountBase", back_populates="journals") + @hybrid_property def signed_amount(self): return self.debit * self.amount diff --git a/brewman/routers/reports/balance_sheet.py b/brewman/routers/reports/balance_sheet.py index db5e34bf..f97534af 100644 --- a/brewman/routers/reports/balance_sheet.py +++ b/brewman/routers/reports/balance_sheet.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime, date from fastapi import APIRouter, Depends, Security, Request from sqlalchemy.orm import Session @@ -11,6 +11,7 @@ from brewman.models.master import AccountType, AccountBase from brewman.models.voucher import Voucher, Journal, VoucherType from brewman.routers.reports.closing_stock import get_closing_stock from brewman.routers.reports.profit_loss import get_accumulated_profit +import brewman.schemas.reports as schemas from ...core.session import ( set_period, get_start_date, @@ -29,60 +30,58 @@ def get_db() -> Session: db.close() -@router.get("") +@router.get("", response_model=schemas.BalanceSheet) def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["balance-sheet"]), ): - return {"date": get_finish_date(request.session), "body": [], "footer": []} + return {"date": get_finish_date(request.session), "body": [], "footer": None} -@router.get("/{date}") +@router.get("/{date_}", response_model=schemas.BalanceSheet) def report_data( - date: str, + date_: str, request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["balance-sheet"]), ): - body, footer = build_balance_sheet(date, db) - set_period(get_start_date(request.session), date, request.session) - return {"date": date, "body": body, "footer": footer} + body, footer = build_balance_sheet(datetime.strptime(date_, "%d-%b-%Y"), db) + set_period(get_start_date(request.session), date_, request.session) + return {"date": date_, "body": body, "footer": footer} -def build_balance_sheet(finish_date, db): - if not isinstance(finish_date, datetime.datetime): - finish_date = datetime.datetime.strptime(finish_date, "%d-%b-%Y") - type_list = [i.id for i in AccountType.list() if i.balance_sheet == True] +def build_balance_sheet(date_: date, db: Session): + type_list = [i.id for i in AccountType.list() if i.balance_sheet] report = [] groups = dict() # Add Net Profit / Loss - closing_stock = get_closing_stock(finish_date, db) - net_profit = get_accumulated_profit(finish_date, db) - closing_stock + closing_stock = round(get_closing_stock(date_, db), 2) + net_profit = round(get_accumulated_profit(date_, db), 2) - closing_stock total_amount = net_profit report.append( { "name": "Net Loss" if net_profit >= 0 else "Net Profit", - "subAmount": net_profit, - "order": 79, + "subAmount": round(net_profit, 2), + "order": 79000, } ) capital_group = AccountType.by_id(5) groups[capital_group.id] = { "group": capital_group.name, - "amount": total_amount, + "amount": round(total_amount, 2), "order": capital_group.order, } total_amount += closing_stock report.append( - {"name": "Closing Stock", "subAmount": closing_stock, "order": 20.001} + {"name": "Closing Stock", "subAmount": round(closing_stock, 2), "order": 20001} ) asset_group = AccountType.by_id(4) groups[asset_group.id] = { "group": asset_group.name, - "amount": closing_stock, + "amount": round(closing_stock, 2), "order": asset_group.order, } @@ -91,7 +90,7 @@ def build_balance_sheet(finish_date, db): db.query(AccountBase, amount_sum) .join(Journal.voucher) .join(Journal.account) - .filter(Voucher.date <= finish_date) + .filter(Voucher.date <= date_) .filter(Voucher.type != VoucherType.by_name("Issue").id) .filter(AccountBase.type.in_(type_list)) .group_by(AccountBase) @@ -101,27 +100,25 @@ def build_balance_sheet(finish_date, db): ) counter = 0 - sss = 0 for account, amount in query: # Add Items account_type = AccountType.by_id(account.type) - sss += amount total_amount += amount if amount != 0: - counter += 0.001 + counter += 1 report.append( { "name": account.name, - "subAmount": amount, + "subAmount": round(amount, 2), "order": account_type.order + counter, } ) if account_type.id in groups: - groups[account_type.id]["amount"] += amount + groups[account_type.id]["amount"] = round(groups[account_type.id]["amount"] + amount, 2) else: groups[account_type.id] = { "group": account_type.name, - "amount": amount, + "amount": round(amount, 2), "order": account_type.order, } @@ -129,5 +126,5 @@ def build_balance_sheet(finish_date, db): for item in groups.values(): report.append(item) - footer = {"name": "Total", "amount": total_amount, "order": 100} + footer = {"name": "Total", "amount": round(total_amount, 2), "order": 100000} return sorted(report, key=lambda d: d["order"]), footer diff --git a/brewman/routers/reports/cash_flow.py b/brewman/routers/reports/cash_flow.py index 806d10b6..8fbbfa1f 100644 --- a/brewman/routers/reports/cash_flow.py +++ b/brewman/routers/reports/cash_flow.py @@ -10,6 +10,7 @@ from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from brewman.models.master import AccountBase, AccountType from brewman.models.voucher import Voucher, Journal, VoucherType +import brewman.schemas.reports as schemas from ...core.session import ( set_period, get_start_date, @@ -28,7 +29,7 @@ def get_db() -> Session: db.close() -@router.get("") +@router.get("", response_model=schemas.CashFlow) def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["cash-flow"]), @@ -36,8 +37,8 @@ def report_blank( return { "startDate": get_start_date(request.session), "finishDate": get_finish_date(request.session), - "body": [], - "footer": {}, + "body": {"operating": [], "investing": [], "financing": [], "details": []}, + "footer": None, } @@ -112,7 +113,7 @@ def build_report(start_date, finish_date, db): cf[lt.cash_flow_classification.lower()].append( { "name": lt.name, - "url": "", # request.route_url("cash_flow_id", id=str(lt.id), _query={"startDate": start_date, "finishDate": finish_date},), + "url": ['/', 'cash-flow', str(lt.id)], "amount": amount * -1, } ) @@ -186,7 +187,7 @@ def build_report_id(account_type, start_date, finish_date, db): details.append( { "name": account.name, - "url": "", # request.route_url("ledger_id", id=account.id, _query={"startDate": start_date, "finishDate": finish_date},), + "url": ['/', 'ledger', str(account.id)], "amount": amount * -1, } ) diff --git a/brewman/routers/reports/ledger.py b/brewman/routers/reports/ledger.py index 260be158..7e3aadb1 100644 --- a/brewman/routers/reports/ledger.py +++ b/brewman/routers/reports/ledger.py @@ -104,6 +104,7 @@ def build_report(account_id, start_date, finish_date, db): "id": voucher.id, "date": voucher.date.strftime("%d-%b-%Y"), "name": name, + "url": ['/', VoucherType.by_id(voucher.type).name.replace(" ", "-").lower(), str(voucher.id)], "type": VoucherType.by_id(voucher.type).name, "narration": voucher.narration, "debit": debit, @@ -135,6 +136,7 @@ def opening_balance(account_id, start_date, db): "id": None, "name": "Opening Balance", "type": "Opening Balance", + "url": [], "narration": "", "debit": debit, "credit": credit, diff --git a/brewman/routers/reports/unposted.py b/brewman/routers/reports/unposted.py index 25d39a4e..fb058855 100644 --- a/brewman/routers/reports/unposted.py +++ b/brewman/routers/reports/unposted.py @@ -61,7 +61,6 @@ def build_report(db: Session): "date": voucher.date.strftime("%d-%b-%Y"), "voucherType": VoucherType.by_id(voucher.type).name, "narration": voucher.narration, - "isPosted": voucher.posted, "debitName": name_debit, "debitAmount": debit, "creditName": name_credit, diff --git a/brewman/schemas/reports.py b/brewman/schemas/reports.py index a925213b..37bf7d39 100644 --- a/brewman/schemas/reports.py +++ b/brewman/schemas/reports.py @@ -16,7 +16,7 @@ class LedgerItem(BaseModel): id_: Optional[uuid.UUID] date_: date name: str - url: str + url: List[str] type_: str narration: str debit: Decimal = Field(multiple_of=0.01) @@ -33,6 +33,9 @@ class LedgerItem(BaseModel): class Config: anystr_strip_whitespace = True alias_generator = to_camel + json_encoders = { + date: lambda v: v.strftime("%d-%b-%Y") + } class Ledger(BaseModel): @@ -63,100 +66,84 @@ class Ledger(BaseModel): ).date() -class ClientIn(BaseModel): - name: str - enabled: bool - otp: Optional[int] - - -class Client(ClientIn): - id_: uuid.UUID - code: int - creation_date: datetime - - -class LoginHistory(BaseModel): - id_: uuid.UUID - user_id: uuid.UUID - client_id: uuid.UUID - date: datetime +class BalanceSheetItem(BaseModel): + name: Optional[str] + group: Optional[str] + amount: Optional[Decimal] + sub_amount: Optional[Decimal] + order: int class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel -class PermissionItem(BaseModel): - id_: uuid.UUID - name: str - enabled: bool +class BalanceSheet(BaseModel): + date_: date + body: List[BalanceSheetItem] + footer: Optional[BalanceSheetItem] class Config: - fields = {"id_": "id"} - - -class RoleIn(BaseModel): - name: str - permissions: List[PermissionItem] - - class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = { + date: lambda v: v.strftime("%d-%b-%Y") + } + + @validator("date_", pre=True) + def parse_date(cls, value): + return datetime.strptime( + value, + "%d-%b-%Y" + ).date() -class Role(RoleIn): - id_: uuid.UUID - - -class RoleList(BaseModel): - id_: uuid.UUID +class CashFlowItem(BaseModel): name: str - permissions: List[str] + url: Optional[List[str]] + amount: Decimal = Field(multiple_of=0.01) class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - - -class RoleItem(BaseModel): - id_: uuid.UUID - name: str - enabled: bool - - class Config: - fields = {"id_": "id"} - - -class UserIn(BaseModel): - name: str - password: str - locked_out: bool - roles: List[RoleItem] - - class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel -class User(UserIn): - id_: uuid.UUID - - -class UserList(BaseModel): - id_: uuid.UUID - name: str - roles: List[str] +class CashFlowBody(BaseModel): + operating: List[CashFlowItem] + investing: List[CashFlowItem] + financing: List[CashFlowItem] + details: List[CashFlowItem] class Config: - fields = {"id_": "id"} anystr_strip_whitespace = True + alias_generator = to_camel + + +class CashFlow(BaseModel): + start_date: date + finish_date: date + body: CashFlowBody + footer: Optional[List[CashFlowItem]] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = { + date: lambda v: v.strftime("%d-%b-%Y") + } + + @validator("start_date", pre=True) + def parse_start_date(cls, value): + return datetime.strptime( + value, + "%d-%b-%Y" + ).date() + + @validator("finish_date", pre=True) + def parse_finish_date(cls, value): + return datetime.strptime( + value, + "%d-%b-%Y" + ).date() -class UserToken(BaseModel): - id_: uuid.UUID - name: str - locked_out: bool = None - password: str - permissions: List[str] diff --git a/overlord/src/app/balance-sheet/balance-sheet.component.html b/overlord/src/app/balance-sheet/balance-sheet.component.html index 28137f3a..8146547e 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.component.html +++ b/overlord/src/app/balance-sheet/balance-sheet.component.html @@ -21,21 +21,21 @@ Group {{row.group}} - {{info.footer.group}} + {{info.footer?.group}} Name {{row.name}} - {{info.footer.name}} + {{info.footer?.name}} Amount {{row.subAmount | currency:'INR' | clear}} - {{info.footer.subAmount | currency:'INR' | clear}} + {{info.footer?.subAmount | currency:'INR' | clear}} @@ -43,7 +43,7 @@ Total {{row.amount | currency:'INR' | clear}} - {{info.footer.amount | currency:'INR' | clear}} + {{info.footer?.amount | currency:'INR' | clear}} diff --git a/overlord/src/app/balance-sheet/balance-sheet.component.ts b/overlord/src/app/balance-sheet/balance-sheet.component.ts index 63cda1a6..4d94d40c 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.component.ts +++ b/overlord/src/app/balance-sheet/balance-sheet.component.ts @@ -43,15 +43,12 @@ export class BalanceSheetComponent implements OnInit { } show() { - const info = this.getInfo(); - this.router.navigate(['balance-sheet', info.date]); + const date = this.getDate(); + this.router.navigate(['balance-sheet', date]); } - getInfo(): BalanceSheet { + getDate(): string { const formModel = this.form.value; - - return { - date: moment(formModel.date).format('DD-MMM-YYYY') - }; + return moment(formModel.date).format('DD-MMM-YYYY'); } } diff --git a/overlord/src/app/balance-sheet/balance-sheet.ts b/overlord/src/app/balance-sheet/balance-sheet.ts index 8235c971..043c6ee0 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.ts +++ b/overlord/src/app/balance-sheet/balance-sheet.ts @@ -1,5 +1,12 @@ +export class BalanceSheetItem { + name?: string; + group?: string; + amount?: number; + subAmount?: number; + order: number; +} export class BalanceSheet { date: string; - body?: any[]; - footer?: any[]; + body: BalanceSheetItem[]; + footer?: BalanceSheetItem; } diff --git a/overlord/src/app/cash-flow/cash-flow.component.html b/overlord/src/app/cash-flow/cash-flow.component.html index 6858fb83..ad7daf68 100644 --- a/overlord/src/app/cash-flow/cash-flow.component.html +++ b/overlord/src/app/cash-flow/cash-flow.component.html @@ -26,7 +26,7 @@ Name - {{row.name}} + {{row.name}} diff --git a/overlord/src/app/cash-flow/cash-flow.ts b/overlord/src/app/cash-flow/cash-flow.ts index dc5bc422..5c422a9d 100644 --- a/overlord/src/app/cash-flow/cash-flow.ts +++ b/overlord/src/app/cash-flow/cash-flow.ts @@ -1,6 +1,6 @@ export class CashFlowItem { name: string; - url: string; + url: []; amount: number; constructor(name: string) { diff --git a/overlord/src/app/ledger/ledger.component.html b/overlord/src/app/ledger/ledger.component.html index b6c6ee70..be281cbb 100644 --- a/overlord/src/app/ledger/ledger.component.html +++ b/overlord/src/app/ledger/ledger.component.html @@ -47,7 +47,7 @@ Particulars - {{row.name}} + {{row.name}} Closing Balance diff --git a/overlord/src/app/ledger/ledger.component.ts b/overlord/src/app/ledger/ledger.component.ts index 149907be..7a81d3d9 100644 --- a/overlord/src/app/ledger/ledger.component.ts +++ b/overlord/src/app/ledger/ledger.component.ts @@ -56,12 +56,7 @@ export class LedgerComponent implements OnInit, AfterViewInit { } ngOnInit() { - this.route.data.pipe(map( - (data: { info: Ledger }) => { - data.info.body = data.info.body.map(x => ({...x, url: x['type'].replace(/ /g, '-').toLowerCase()})); - return data; - } - )).subscribe((data: { info: Ledger }) => { + this.route.data.subscribe((data: { info: Ledger }) => { this.info = data.info; this.calculateTotals(); this.form.setValue({ diff --git a/overlord/src/app/unposted/unposted.ts b/overlord/src/app/unposted/unposted.ts index 2f5292d0..0a992013 100644 --- a/overlord/src/app/unposted/unposted.ts +++ b/overlord/src/app/unposted/unposted.ts @@ -3,7 +3,6 @@ export class Unposted { date: string; voucherType: string; narration: string; - isPosted: boolean; debitName: string; debitAmount: number; creditName: string;