diff --git a/src/masterdata/__pycache__/schema.cpython-311.pyc b/src/masterdata/__pycache__/schema.cpython-311.pyc index 4bb5e38..e8d9714 100644 Binary files a/src/masterdata/__pycache__/schema.cpython-311.pyc and b/src/masterdata/__pycache__/schema.cpython-311.pyc differ diff --git a/src/masterdata/__pycache__/service.cpython-311.pyc b/src/masterdata/__pycache__/service.cpython-311.pyc index 11a6415..5860f01 100644 Binary files a/src/masterdata/__pycache__/service.cpython-311.pyc and b/src/masterdata/__pycache__/service.cpython-311.pyc differ diff --git a/src/masterdata/service.py b/src/masterdata/service.py index 3e6cd39..87da8c7 100644 --- a/src/masterdata/service.py +++ b/src/masterdata/service.py @@ -36,7 +36,7 @@ async def get_all( *, db_session: DbSession, items_per_page: int, search: str = None, common ) -> list[MasterData]: """Returns all documents.""" - query = Select(MasterData) + query = Select(MasterData).order_by(MasterData.description.asc()) if search: query = query.filter(MasterData.name.ilike(f"%{search}%")) @@ -138,45 +138,81 @@ async def bulk_update( result = await db_session.execute(query) records = result.scalars().all() - # Create a mapping of id to record for easier access - records_map = {record.id: record for record in records} + # Create a mapping of id (string) to record and name to record for easier access + # 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 updated_records = [] for masterdata_id, masterdata_in in zip(ids, updates): masterdata = records_map.get(masterdata_id) + print("Processing update for ID:", masterdata) if not masterdata: continue data = masterdata_in.model_dump() update_data = masterdata_in.model_dump(exclude_defaults=True) - def get_value(obj, name): - return next((m.value_num for m in obj if m.name == name), 0) - - # Update direct values - for field in update_data: - setattr(masterdata, field, update_data[field]) + async def get_value(name): + # Prefer values from the current batch (records_by_name) + rd = records_by_name.get(name) + if rd is not None and rd.value_num is not None: + return rd.value_num + + # 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 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) - total_project_cost = get_value(masterdata, "total_project_cost") - loan = total_project_cost * (get_value(masterdata, "loan_portion") / 100) + total_project_cost = await get_value("total_project_cost") + loan = total_project_cost * (await get_value("loan_portion") / 100) setattr(masterdata, "loan", loan) equity = total_project_cost * (equity_portion / 100) setattr(masterdata, "equity", equity) - if any( - field in update_data for field in ["loan", "interest_rate", "loan_tenor"] - ): + if any(field in update_data for field in ["loan", "interest_rate", "loan_tenor"]): pmt = calculate_pmt( - rate=get_value(masterdata, "interest_rate"), - nper=get_value(masterdata, "loan_tenor"), - pv=get_value(masterdata, "loan"), + rate=await get_value("interest_rate"), + nper=await get_value("loan_tenor"), + pv=await get_value("loan"), ) setattr(masterdata, "principal_interest_payment", pmt) @@ -191,19 +227,18 @@ async def bulk_update( ] ): wacc = ( - get_value(masterdata, "loan_portion") + await get_value("loan_portion") * ( - get_value(masterdata, "interest_rate") - * (1 - get_value(masterdata, "corporate_tax_rate")) + await get_value("interest_rate") + * (1 - await get_value("corporate_tax_rate")) ) ) + ( - get_value(masterdata, "wacc_on_equity") - * get_value(masterdata, "equity_portion") + await get_value("wacc_on_equity") * await get_value("equity_portion") ) setattr(masterdata, "wacc_on_project", wacc) - updated_records.append(masterdata) + print("Updated masterdata:", updated_records) # Commit all changes in a single transaction await db_session.commit()