|
|
|
@ -36,7 +36,7 @@ async def get_all(
|
|
|
|
*, db_session: DbSession, items_per_page: int, search: str = None, common
|
|
|
|
*, db_session: DbSession, items_per_page: int, search: str = None, common
|
|
|
|
) -> list[MasterData]:
|
|
|
|
) -> list[MasterData]:
|
|
|
|
"""Returns all documents."""
|
|
|
|
"""Returns all documents."""
|
|
|
|
query = Select(MasterData)
|
|
|
|
query = Select(MasterData).order_by(MasterData.description.asc())
|
|
|
|
|
|
|
|
|
|
|
|
if search:
|
|
|
|
if search:
|
|
|
|
query = query.filter(MasterData.name.ilike(f"%{search}%"))
|
|
|
|
query = query.filter(MasterData.name.ilike(f"%{search}%"))
|
|
|
|
@ -138,45 +138,81 @@ async def bulk_update(
|
|
|
|
result = await db_session.execute(query)
|
|
|
|
result = await db_session.execute(query)
|
|
|
|
records = result.scalars().all()
|
|
|
|
records = result.scalars().all()
|
|
|
|
|
|
|
|
|
|
|
|
# Create a mapping of id to record for easier access
|
|
|
|
# Create a mapping of id (string) to record and name to record for easier access
|
|
|
|
records_map = {record.id: record for record in records}
|
|
|
|
# IDs coming from the router are strings, so normalize keys to strings to match lookups
|
|
|
|
|
|
|
|
records_map = {str(record.id): record for record in records}
|
|
|
|
|
|
|
|
records_by_name = {record.name: record for record in records}
|
|
|
|
|
|
|
|
|
|
|
|
# Process updates in batches
|
|
|
|
# Process updates in batches
|
|
|
|
updated_records = []
|
|
|
|
updated_records = []
|
|
|
|
for masterdata_id, masterdata_in in zip(ids, updates):
|
|
|
|
for masterdata_id, masterdata_in in zip(ids, updates):
|
|
|
|
masterdata = records_map.get(masterdata_id)
|
|
|
|
masterdata = records_map.get(masterdata_id)
|
|
|
|
|
|
|
|
print("Processing update for ID:", masterdata)
|
|
|
|
if not masterdata:
|
|
|
|
if not masterdata:
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
data = masterdata_in.model_dump()
|
|
|
|
data = masterdata_in.model_dump()
|
|
|
|
update_data = masterdata_in.model_dump(exclude_defaults=True)
|
|
|
|
update_data = masterdata_in.model_dump(exclude_defaults=True)
|
|
|
|
|
|
|
|
|
|
|
|
def get_value(obj, name):
|
|
|
|
async def get_value(name):
|
|
|
|
return next((m.value_num for m in obj if m.name == name), 0)
|
|
|
|
# Prefer values from the current batch (records_by_name)
|
|
|
|
|
|
|
|
rd = records_by_name.get(name)
|
|
|
|
# Update direct values
|
|
|
|
if rd is not None and rd.value_num is not None:
|
|
|
|
for field in update_data:
|
|
|
|
return rd.value_num
|
|
|
|
setattr(masterdata, field, update_data[field])
|
|
|
|
|
|
|
|
|
|
|
|
# If not found in batch, try to fetch from DB
|
|
|
|
|
|
|
|
query_val = Select(MasterData).where(MasterData.name == name)
|
|
|
|
|
|
|
|
res_val = await db_session.execute(query_val)
|
|
|
|
|
|
|
|
row = res_val.scalars().one_or_none()
|
|
|
|
|
|
|
|
return row.value_num if row and row.value_num is not None else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Update direct values (attributes present on MasterData)
|
|
|
|
|
|
|
|
# Recognised attribute fields on MasterData
|
|
|
|
|
|
|
|
attr_fields = {
|
|
|
|
|
|
|
|
"name",
|
|
|
|
|
|
|
|
"description",
|
|
|
|
|
|
|
|
"unit_of_measurement",
|
|
|
|
|
|
|
|
"value_num",
|
|
|
|
|
|
|
|
"value_str",
|
|
|
|
|
|
|
|
"created_by",
|
|
|
|
|
|
|
|
"updated_by",
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for field, val in update_data.items():
|
|
|
|
|
|
|
|
if field in attr_fields:
|
|
|
|
|
|
|
|
setattr(masterdata, field, val)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# Field is not a direct attribute: treat it as a named masterdata
|
|
|
|
|
|
|
|
# e.g. payload included {"discount_rate": 5.0}
|
|
|
|
|
|
|
|
query_other = Select(MasterData).where(MasterData.name == field)
|
|
|
|
|
|
|
|
res_other = await db_session.execute(query_other)
|
|
|
|
|
|
|
|
other = res_other.scalars().one_or_none()
|
|
|
|
|
|
|
|
if other:
|
|
|
|
|
|
|
|
# Update numeric or string value depending on payload
|
|
|
|
|
|
|
|
if isinstance(val, (int, float)):
|
|
|
|
|
|
|
|
other.value_num = val
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
other.value_str = str(val)
|
|
|
|
|
|
|
|
# keep updated record available for batch calculations
|
|
|
|
|
|
|
|
records_by_name[other.name] = other
|
|
|
|
|
|
|
|
|
|
|
|
# Handle interdependent calculations
|
|
|
|
# Handle interdependent calculations
|
|
|
|
if "loan_portion" in update_data:
|
|
|
|
if "loan_portion" in update_data:
|
|
|
|
equity_portion = 100 - get_value(masterdata, "loan_portion")
|
|
|
|
equity_portion = 100 - await get_value("loan_portion")
|
|
|
|
setattr(masterdata, "equity_portion", equity_portion)
|
|
|
|
setattr(masterdata, "equity_portion", equity_portion)
|
|
|
|
|
|
|
|
|
|
|
|
total_project_cost = get_value(masterdata, "total_project_cost")
|
|
|
|
total_project_cost = await get_value("total_project_cost")
|
|
|
|
loan = total_project_cost * (get_value(masterdata, "loan_portion") / 100)
|
|
|
|
loan = total_project_cost * (await get_value("loan_portion") / 100)
|
|
|
|
setattr(masterdata, "loan", loan)
|
|
|
|
setattr(masterdata, "loan", loan)
|
|
|
|
|
|
|
|
|
|
|
|
equity = total_project_cost * (equity_portion / 100)
|
|
|
|
equity = total_project_cost * (equity_portion / 100)
|
|
|
|
setattr(masterdata, "equity", equity)
|
|
|
|
setattr(masterdata, "equity", equity)
|
|
|
|
|
|
|
|
|
|
|
|
if any(
|
|
|
|
if any(field in update_data for field in ["loan", "interest_rate", "loan_tenor"]):
|
|
|
|
field in update_data for field in ["loan", "interest_rate", "loan_tenor"]
|
|
|
|
|
|
|
|
):
|
|
|
|
|
|
|
|
pmt = calculate_pmt(
|
|
|
|
pmt = calculate_pmt(
|
|
|
|
rate=get_value(masterdata, "interest_rate"),
|
|
|
|
rate=await get_value("interest_rate"),
|
|
|
|
nper=get_value(masterdata, "loan_tenor"),
|
|
|
|
nper=await get_value("loan_tenor"),
|
|
|
|
pv=get_value(masterdata, "loan"),
|
|
|
|
pv=await get_value("loan"),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
setattr(masterdata, "principal_interest_payment", pmt)
|
|
|
|
setattr(masterdata, "principal_interest_payment", pmt)
|
|
|
|
|
|
|
|
|
|
|
|
@ -191,19 +227,18 @@ async def bulk_update(
|
|
|
|
]
|
|
|
|
]
|
|
|
|
):
|
|
|
|
):
|
|
|
|
wacc = (
|
|
|
|
wacc = (
|
|
|
|
get_value(masterdata, "loan_portion")
|
|
|
|
await get_value("loan_portion")
|
|
|
|
* (
|
|
|
|
* (
|
|
|
|
get_value(masterdata, "interest_rate")
|
|
|
|
await get_value("interest_rate")
|
|
|
|
* (1 - get_value(masterdata, "corporate_tax_rate"))
|
|
|
|
* (1 - await get_value("corporate_tax_rate"))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) + (
|
|
|
|
) + (
|
|
|
|
get_value(masterdata, "wacc_on_equity")
|
|
|
|
await get_value("wacc_on_equity") * await get_value("equity_portion")
|
|
|
|
* get_value(masterdata, "equity_portion")
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
setattr(masterdata, "wacc_on_project", wacc)
|
|
|
|
setattr(masterdata, "wacc_on_project", wacc)
|
|
|
|
|
|
|
|
|
|
|
|
updated_records.append(masterdata)
|
|
|
|
updated_records.append(masterdata)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("Updated masterdata:", updated_records)
|
|
|
|
# Commit all changes in a single transaction
|
|
|
|
# Commit all changes in a single transaction
|
|
|
|
await db_session.commit()
|
|
|
|
await db_session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|