fix: preserve mobile presign decimal precision.
This commit is contained in:
1 parent
f761c7021e
commit
e0dc7f4cee
2 files changed
+206
-9
No files matched your search
@@ -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:
|
||||||
|
|||||||
@@ -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',
|
||||||
|
]
|
||||||
Reference in new issue
Block a user