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 datetime import datetime
from decimal import Decimal
from typing import List, Optional, Tuple
from fastapi import HTTPException
@@ -29,6 +30,70 @@ from app.services.market_field_scope import (
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:
return body.model_dump(by_alias=False, exclude_unset=True)
@@ -236,7 +301,7 @@ def list_presign_orders(
db, person_ids, ctx.tenant_no
)
return [
presign_crud.order_to_dict(
_mobile_order_to_dict(
db,
row,
ctx.tenant_no,
@@ -258,7 +323,7 @@ def get_presign_order(
row = assert_presign_access(
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
)
@@ -347,7 +412,7 @@ def create_presign_order(
except Exception:
db.rollback()
raise
return presign_crud.order_to_dict(
return _mobile_order_to_dict(
db, row, ctx.tenant_no, with_children=True
)
@@ -414,7 +479,7 @@ def update_presign_order(
row = assert_presign_access(
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.commit()
@@ -538,7 +603,7 @@ def create_presign_transfer(
create_dept_id=_current_dept_id(user_info),
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()
return result
except Exception:
@@ -576,7 +641,7 @@ def update_presign_transfer(
fields=fields,
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()
return result
except Exception:
@@ -626,7 +691,7 @@ def submit_presign_transfer(
user_id=ctx.user_id,
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()
return result
except Exception:
@@ -649,7 +714,7 @@ def withdraw_presign_transfer(
user_id=ctx.user_id,
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()
return result
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'),
user_info=_user(),
)
assert updated['transferAmount'] == 200.25
assert updated['transferAmount'] == '200.25'
assert updated['remark'] == '更新'
assert (
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
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',
]