fix: preserve mobile presign decimal precision.

This commit is contained in:
zlwu committed 2026-10-11 13:50:47 +08:00
1 parent f761c7021e
commit e0dc7f4cee
2 files changed
+206 -9

No files matched your search

+73 -8
View File
@@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime from datetime import datetime
from decimal import Decimal
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from fastapi import HTTPException from fastapi import HTTPException
@@ -29,6 +30,70 @@ from app.services.market_field_scope import (
from app.services.market_support import validate_attachment_refs from app.services.market_support import validate_attachment_refs
_MONEY_QUANTUM = Decimal('0.01')
def _money_text(value: Optional[Decimal]) -> Optional[str]:
if value is None:
return None
return format(Decimal(str(value)).quantize(_MONEY_QUANTUM), 'f')
def _mobile_transfer_to_dict(
db: Session, row: BizPresignTransfer, tenant_no: str
) -> dict:
data = presign_crud._transfer_to_dict(db, row, tenant_no)
data['transferAmount'] = _money_text(row.transfer_amount)
return data
def _mobile_order_to_dict(
db: Session,
row: BizPresignOrder,
tenant_no: str,
*,
with_children: bool,
deposit_amount: Optional[Decimal] = None,
user_name_map: Optional[dict] = None,
) -> dict:
deposit = (
deposit_amount
if deposit_amount is not None
else presign_crud.confirmed_deposit_amount(
db, presign_order_id=row.id, tenant_no=tenant_no
)
)
data = presign_crud.order_to_dict(
db,
row,
tenant_no,
with_children=False,
deposit_amount=deposit,
user_name_map=user_name_map,
)
data['contractAmount'] = _money_text(row.contract_amount)
data['confirmedDepositAmount'] = _money_text(deposit)
if with_children:
data['sampleTypeIds'] = presign_crud.list_sample_type_ids(
db, presign_order_id=row.id, tenant_no=tenant_no
)
transfer_rows = (
db.query(BizPresignTransfer)
.filter(
BizPresignTransfer.presign_order_id == row.id,
BizPresignTransfer.tenant_no == tenant_no,
BizPresignTransfer.del_flag == 0,
)
.order_by(BizPresignTransfer.create_time.asc())
.all()
)
data['transfers'] = [
_mobile_transfer_to_dict(db, transfer, tenant_no)
for transfer in transfer_rows
]
return data
def _dump_body(body: MarketPresignOrderBody) -> dict: def _dump_body(body: MarketPresignOrderBody) -> dict:
return body.model_dump(by_alias=False, exclude_unset=True) return body.model_dump(by_alias=False, exclude_unset=True)
@@ -236,7 +301,7 @@ def list_presign_orders(
db, person_ids, ctx.tenant_no db, person_ids, ctx.tenant_no
) )
return [ return [
presign_crud.order_to_dict( _mobile_order_to_dict(
db, db,
row, row,
ctx.tenant_no, ctx.tenant_no,
@@ -258,7 +323,7 @@ def get_presign_order(
row = assert_presign_access( row = assert_presign_access(
db, order_id, ctx, user_info=user_info db, order_id, ctx, user_info=user_info
) )
return presign_crud.order_to_dict( return _mobile_order_to_dict(
db, row, ctx.tenant_no, with_children=True db, row, ctx.tenant_no, with_children=True
) )
@@ -347,7 +412,7 @@ def create_presign_order(
except Exception: except Exception:
db.rollback() db.rollback()
raise raise
return presign_crud.order_to_dict( return _mobile_order_to_dict(
db, row, ctx.tenant_no, with_children=True db, row, ctx.tenant_no, with_children=True
) )
@@ -414,7 +479,7 @@ def update_presign_order(
row = assert_presign_access( row = assert_presign_access(
db, row.id, ctx, user_info=user_info db, row.id, ctx, user_info=user_info
) )
result = presign_crud.order_to_dict( result = _mobile_order_to_dict(
db, row, ctx.tenant_no, with_children=True db, row, ctx.tenant_no, with_children=True
) )
db.commit() db.commit()
@@ -538,7 +603,7 @@ def create_presign_transfer(
create_dept_id=_current_dept_id(user_info), create_dept_id=_current_dept_id(user_info),
commit=False, commit=False,
) )
result = presign_crud._transfer_to_dict(db, row, ctx.tenant_no) result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
db.commit() db.commit()
return result return result
except Exception: except Exception:
@@ -576,7 +641,7 @@ def update_presign_transfer(
fields=fields, fields=fields,
commit=False, commit=False,
) )
result = presign_crud._transfer_to_dict(db, row, ctx.tenant_no) result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
db.commit() db.commit()
return result return result
except Exception: except Exception:
@@ -626,7 +691,7 @@ def submit_presign_transfer(
user_id=ctx.user_id, user_id=ctx.user_id,
commit=False, commit=False,
) )
result = presign_crud._transfer_to_dict(db, row, ctx.tenant_no) result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
db.commit() db.commit()
return result return result
except Exception: except Exception:
@@ -649,7 +714,7 @@ def withdraw_presign_transfer(
user_id=ctx.user_id, user_id=ctx.user_id,
commit=False, commit=False,
) )
result = presign_crud._transfer_to_dict(db, row, ctx.tenant_no) result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
db.commit() db.commit()
return result return result
except Exception: except Exception:
+133 -1
View File
@@ -1437,7 +1437,7 @@ def test_dfj_presign_transfer_can_edit_submit_withdraw_and_soft_delete():
ctx=_ctx('specialist'), ctx=_ctx('specialist'),
user_info=_user(), user_info=_user(),
) )
assert updated['transferAmount'] == 200.25 assert updated['transferAmount'] == '200.25'
assert updated['remark'] == '更新' assert updated['remark'] == '更新'
assert ( assert (
db.query(BizPresignTransfer).filter_by(id=transfer_id).one().create_dept_id db.query(BizPresignTransfer).filter_by(id=transfer_id).one().create_dept_id
@@ -2172,3 +2172,135 @@ def test_market_presign_routes_are_fixed_and_expose_scoped_transfer_actions():
} }
) )
assert transfer_actual == transfer_expected assert transfer_actual == transfer_expected
def test_mobile_presign_money_dto_preserves_decimal_strings_for_presenter(
monkeypatch,
):
import json
import subprocess
from decimal import Decimal
from pathlib import Path
from app.crud import presign_order as presign_crud
from app.services import market_presign
db = _session()
normal = _order('money-normal', sales_user_id='u1')
normal.contract_amount = Decimal('1234.50')
empty = _order('money-empty', sales_user_id='u1')
empty.contract_amount = None
paid = _transfer(
'money-paid', order_id=normal.id, pay_status='yjf'
)
paid.transfer_amount = Decimal('200.25')
draft = _transfer(
'money-draft', order_id=normal.id, pay_status='dfj'
)
draft.transfer_amount = Decimal('12.30')
db.add_all([normal, empty, paid, draft])
db.commit()
items, total = market_presign.list_presign_orders(
db,
ctx=_ctx('specialist'),
user_info=_user(),
page=1,
page_size=10,
)
by_id = {item['id']: item for item in items}
assert total == 2
assert by_id['money-normal']['contractAmount'] == '1234.50'
assert by_id['money-normal']['confirmedDepositAmount'] == '200.25'
assert by_id['money-empty']['contractAmount'] is None
assert by_id['money-empty']['confirmedDepositAmount'] == '0.00'
normal_detail = market_presign.get_presign_order(
db,
order_id=normal.id,
ctx=_ctx('specialist'),
user_info=_user(),
)
transfer_amounts = {
transfer['id']: transfer['transferAmount']
for transfer in normal_detail['transfers']
}
assert transfer_amounts == {
'money-paid': '200.25',
'money-draft': '12.30',
}
maximum = Decimal('9999999999999999.99')
maximum_order = _order('money-maximum', sales_user_id='u1')
maximum_order.contract_amount = maximum
monkeypatch.setattr(
market_presign,
'assert_presign_access',
lambda *_args, **_kwargs: maximum_order,
)
monkeypatch.setattr(
presign_crud,
'confirmed_deposit_amount',
lambda *_args, **_kwargs: maximum,
)
maximum_detail = market_presign.get_presign_order(
db,
order_id=maximum_order.id,
ctx=_ctx('specialist'),
user_info=_user(),
)
assert maximum_detail['contractAmount'] == str(maximum)
assert maximum_detail['confirmedDepositAmount'] == str(maximum)
maximum_transfer = _transfer(
'money-maximum-transfer', order_id=maximum_order.id
)
maximum_transfer.transfer_amount = maximum
maximum_transfer_dto = market_presign._mobile_transfer_to_dict(
db, maximum_transfer, 'tenant-a'
)
assert maximum_transfer_dto['transferAmount'] == str(maximum)
serialized = json.dumps(
{
'maximum': maximum_detail,
'maximumTransfer': maximum_transfer_dto,
'normal': normal_detail,
'empty': by_id['money-empty'],
},
ensure_ascii=False,
)
presenter_url = (
Path(__file__).resolve().parents[2]
/ 'mobile-oa-h5/src/market/presign/presign-presenter.js'
).as_uri()
script = """
import fs from 'node:fs'
const { formatPresignMoney } = await import(process.argv[1])
const dto = JSON.parse(fs.readFileSync(0, 'utf8'))
const values = [
dto.maximum.contractAmount,
dto.maximum.confirmedDepositAmount,
dto.maximumTransfer.transferAmount,
dto.normal.contractAmount,
dto.normal.transfers.find((item) => item.id === 'money-paid').transferAmount,
dto.empty.contractAmount,
dto.empty.confirmedDepositAmount
]
process.stdout.write(JSON.stringify(values.map(formatPresignMoney)))
"""
formatted = subprocess.run(
['node', '--input-type=module', '-e', script, presenter_url],
input=serialized,
text=True,
capture_output=True,
check=True,
)
assert json.loads(formatted.stdout) == [
'¥9,999,999,999,999,999.99',
'¥9,999,999,999,999,999.99',
'¥9,999,999,999,999,999.99',
'¥1,234.50',
'¥200.25',
'-',
'¥0.00',
]