|
|
|
|
@ -1,17 +1,19 @@
|
|
|
|
|
import traceback
|
|
|
|
|
import uuid
|
|
|
|
|
from typing import List
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, Security
|
|
|
|
|
from pydantic import ValidationError
|
|
|
|
|
from fastapi import APIRouter, HTTPException, status, Depends, Security
|
|
|
|
|
from sqlalchemy import func
|
|
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
|
from sqlalchemy.orm import joinedload_all, Session
|
|
|
|
|
|
|
|
|
|
from ..core.security import User, get_current_active_user as get_user
|
|
|
|
|
from ..db.session import SessionLocal
|
|
|
|
|
from ..models.master import CostCentre, Account, AccountType, AccountBase
|
|
|
|
|
from ..models.validation_exception import ValidationError
|
|
|
|
|
from ..models.voucher import Voucher, Journal, VoucherType
|
|
|
|
|
from ..schemas.master import Account as AccountSchema
|
|
|
|
|
import brewman.schemas.master as schemas
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -24,11 +26,14 @@ def get_db():
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/")
|
|
|
|
|
def save(data: AccountSchema, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
|
|
|
|
|
@router.post("/", response_model=schemas.Account)
|
|
|
|
|
def save(
|
|
|
|
|
data: schemas.AccountSaveUpdate,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
user: User = Security(get_user, scopes=["accounts"]),
|
|
|
|
|
):
|
|
|
|
|
try:
|
|
|
|
|
item = Account(
|
|
|
|
|
code=0,
|
|
|
|
|
name=data.name,
|
|
|
|
|
type=data.type,
|
|
|
|
|
is_starred=data.is_starred,
|
|
|
|
|
@ -38,56 +43,85 @@ def save(data: AccountSchema, db: Session = Depends(get_db), user: User = Securi
|
|
|
|
|
).create(db)
|
|
|
|
|
db.commit()
|
|
|
|
|
return account_info(item.id, db)
|
|
|
|
|
except:
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
db.rollback()
|
|
|
|
|
finally:
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.put("/{id_}")
|
|
|
|
|
def update(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
|
|
|
|
|
item = (
|
|
|
|
|
db.query(Account)
|
|
|
|
|
.filter(Account.id == id_)
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
if item.is_fixture:
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail=str(e),
|
|
|
|
|
)
|
|
|
|
|
except Exception:
|
|
|
|
|
db.rollback()
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail=traceback.format_exc(),
|
|
|
|
|
)
|
|
|
|
|
new_type = int(request.json_body["type"])
|
|
|
|
|
if not item.type == new_type:
|
|
|
|
|
item.code = Account.get_code(new_type, request.dbsession)
|
|
|
|
|
item.type = new_type
|
|
|
|
|
item.name = request.json_body["name"]
|
|
|
|
|
item.is_active = request.json_body["isActive"]
|
|
|
|
|
item.is_reconcilable = request.json_body["isReconcilable"]
|
|
|
|
|
item.is_starred = request.json_body["isStarred"]
|
|
|
|
|
item.cost_centre_id = uuid.UUID(request.json_body["costCentre"]["id"])
|
|
|
|
|
transaction.commit()
|
|
|
|
|
return account_info(item.id, request.dbsession)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/{id}") # "Accounts"
|
|
|
|
|
def delete(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
|
|
|
|
|
account = (
|
|
|
|
|
db.query(Account)
|
|
|
|
|
.filter(Account.id == id_)
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
can_delete, reason = account.can_delete(request.has_permission("Advanced Delete"))
|
|
|
|
|
@router.put("/{id_}", response_model=schemas.Account)
|
|
|
|
|
def update(
|
|
|
|
|
id_: uuid.UUID,
|
|
|
|
|
data: schemas.AccountSaveUpdate,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
user: User = Security(get_user, scopes=["accounts"]),
|
|
|
|
|
):
|
|
|
|
|
try:
|
|
|
|
|
item: Account = db.query(Account).filter(Account.id == id_).first()
|
|
|
|
|
if item.is_fixture:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
|
|
|
|
|
)
|
|
|
|
|
if not item.type == data.type:
|
|
|
|
|
item.code = Account.get_code(data.type, db)
|
|
|
|
|
item.type = data.type
|
|
|
|
|
item.name = data.name
|
|
|
|
|
item.is_active = data.is_active
|
|
|
|
|
item.is_reconcilable = data.is_reconcilable
|
|
|
|
|
item.is_starred = data.is_starred
|
|
|
|
|
item.cost_centre_id = data.cost_centre.id_
|
|
|
|
|
db.commit()
|
|
|
|
|
return account_info(item.id, db)
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
db.rollback()
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
|
|
|
|
)
|
|
|
|
|
except Exception:
|
|
|
|
|
db.rollback()
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail=traceback.format_exc(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/{id}")
|
|
|
|
|
def delete(
|
|
|
|
|
id_: uuid.UUID,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
user: User = Security(get_user, scopes=["accounts"]),
|
|
|
|
|
):
|
|
|
|
|
account: Account = db.query(Account).filter(Account.id == id_).first()
|
|
|
|
|
can_delete, reason = account.can_delete("Advanced Delete" in user.permissions)
|
|
|
|
|
if can_delete:
|
|
|
|
|
delete_with_data(account, db)
|
|
|
|
|
transaction.commit()
|
|
|
|
|
db.commit()
|
|
|
|
|
return account_info(None, db)
|
|
|
|
|
else:
|
|
|
|
|
transaction.abort()
|
|
|
|
|
response = Response("Cannot delete account because {0}".format(reason))
|
|
|
|
|
response.status_int = 500
|
|
|
|
|
return response
|
|
|
|
|
db.abort()
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail=f"Cannot delete account because {reason}",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/list") # "Authenticated"
|
|
|
|
|
@router.get("/")
|
|
|
|
|
def show_blank(
|
|
|
|
|
db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])
|
|
|
|
|
):
|
|
|
|
|
return account_info(None, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/list")
|
|
|
|
|
async def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
|
|
|
|
|
list_ = (
|
|
|
|
|
db.query(Account)
|
|
|
|
|
@ -113,9 +147,16 @@ async def show_list(db: Session = Depends(get_db), user: User = Depends(get_user
|
|
|
|
|
return accounts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/query") # "Authenticated"
|
|
|
|
|
async def show_term(q: str, t: int = None, r: bool = None, a: bool = None, c: int = None,
|
|
|
|
|
db: Session = Depends(get_db), current_user: User = Depends(get_user)):
|
|
|
|
|
@router.get("/query")
|
|
|
|
|
async def show_term(
|
|
|
|
|
q: str,
|
|
|
|
|
t: int = None,
|
|
|
|
|
r: bool = None,
|
|
|
|
|
a: bool = None,
|
|
|
|
|
c: int = None,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
current_user: User = Depends(get_user),
|
|
|
|
|
):
|
|
|
|
|
count = c
|
|
|
|
|
|
|
|
|
|
list_ = []
|
|
|
|
|
@ -126,29 +167,32 @@ async def show_term(q: str, t: int = None, r: bool = None, a: bool = None, c: in
|
|
|
|
|
return {"user": current_user.name, "list": list_}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/{id_}/balance") # "Authenticated"
|
|
|
|
|
async def show_balance(id_: uuid.UUID, d: str = None, db: Session = Depends(get_db), user: User = Depends(get_user)):
|
|
|
|
|
date = (
|
|
|
|
|
None
|
|
|
|
|
if d is None or d == ""
|
|
|
|
|
else datetime.datetime.strptime(d, "%d-%b-%Y")
|
|
|
|
|
)
|
|
|
|
|
@router.get("/{id_}/balance")
|
|
|
|
|
async def show_balance(
|
|
|
|
|
id_: uuid.UUID,
|
|
|
|
|
d: str = None,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
user: User = Depends(get_user),
|
|
|
|
|
):
|
|
|
|
|
date = None if d is None or d == "" else datetime.datetime.strptime(d, "%d-%b-%Y")
|
|
|
|
|
return {"date": balance(id_, date, db), "total": balance(id_, None, db)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/{id_}") # "Accounts"
|
|
|
|
|
def show_id(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
|
|
|
|
|
@router.get("/{id_}")
|
|
|
|
|
def show_id(
|
|
|
|
|
id_: uuid.UUID,
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
user: User = Security(get_user, scopes=["accounts"]),
|
|
|
|
|
):
|
|
|
|
|
return account_info(id_, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def balance(id_: uuid.UUID, date, dbsession: Session):
|
|
|
|
|
account = dbsession.query(AccountBase).filter(AccountBase.id == id_).first()
|
|
|
|
|
def balance(id_: uuid.UUID, date, db: Session):
|
|
|
|
|
account = db.query(AccountBase).filter(AccountBase.id == id_).first()
|
|
|
|
|
if not account.type_object.balance_sheet:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
bal = dbsession.query(func.sum(Journal.amount * Journal.debit)).join(
|
|
|
|
|
Journal.voucher
|
|
|
|
|
)
|
|
|
|
|
bal = db.query(func.sum(Journal.amount * Journal.debit)).join(Journal.voucher)
|
|
|
|
|
if date is not None:
|
|
|
|
|
bal = bal.filter(Voucher.date <= date)
|
|
|
|
|
|
|
|
|
|
@ -160,11 +204,6 @@ def balance(id_: uuid.UUID, date, dbsession: Session):
|
|
|
|
|
return 0 if bal is None else bal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/") # "Accounts"
|
|
|
|
|
def show_blank(db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
|
|
|
|
|
return account_info(None, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def account_info(id_, db):
|
|
|
|
|
if id_ is None:
|
|
|
|
|
account = {
|
|
|
|
|
@ -198,7 +237,7 @@ def delete_with_data(account, db):
|
|
|
|
|
suspense_account = (
|
|
|
|
|
db.query(Account).filter(Account.id == Account.suspense()).first()
|
|
|
|
|
)
|
|
|
|
|
query = (
|
|
|
|
|
query: List[Voucher] = (
|
|
|
|
|
db.query(Voucher)
|
|
|
|
|
.options(joinedload_all(Voucher.journals, Journal.account, innerjoin=True))
|
|
|
|
|
.filter(Voucher.journals.any(Journal.account_id == account.id))
|
|
|
|
|
@ -219,8 +258,8 @@ def delete_with_data(account, db):
|
|
|
|
|
else:
|
|
|
|
|
if sus_jnl is None:
|
|
|
|
|
acc_jnl.account = suspense_account
|
|
|
|
|
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
|
|
|
|
|
acc_jnl.amount, account.name
|
|
|
|
|
voucher.narration += (
|
|
|
|
|
f"\nSuspense \u20B9{acc_jnl.amount:,.2f} is {account.name}"
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
amount = (sus_jnl.debit * sus_jnl.amount) + (
|
|
|
|
|
@ -232,9 +271,7 @@ def delete_with_data(account, db):
|
|
|
|
|
else:
|
|
|
|
|
sus_jnl.amount = abs(amount)
|
|
|
|
|
sus_jnl.debit = -1 if amount < 0 else 1
|
|
|
|
|
voucher.narration += "\nDeleted \u20B9{0:,.2f} of {1}".format(
|
|
|
|
|
acc_jnl.amount * acc_jnl.debit, account.name
|
|
|
|
|
)
|
|
|
|
|
voucher.narration += f"\nDeleted \u20B9{acc_jnl.amount * acc_jnl.debit:,.2f} of {account.name}"
|
|
|
|
|
if voucher.type in (
|
|
|
|
|
VoucherType.by_name("Payment").id,
|
|
|
|
|
VoucherType.by_name("Receipt").id,
|
|
|
|
|
|