支付的页面和各种细节计算

This commit is contained in:
Xu Chang 2024-06-13 20:34:38 +08:00
parent 88fb060c67
commit 8a0580a5a6
14 changed files with 310 additions and 53 deletions

View File

@ -10,6 +10,7 @@ import wpProductCheckers from './wpProduct';
import abstractCheckers from './abstractChecker';
import withdrawAccounts from './withdrawAccount';
import refundCheckers from './refund';
import withdrawTransferCheckers from './withdrawTransfer';
const checkers = [
...refundCheckers,
@ -21,6 +22,7 @@ const checkers = [
...applicationCheckers,
...offlineAccountCheckers,
...wpProductCheckers,
...withdrawTransferCheckers,
] as Checker<EntityDict, keyof EntityDict, RuntimeCxt>[];
export default checkers;

View File

@ -10,9 +10,25 @@ const checkers: Checker<EntityDict, 'withdrawTransfer', RuntimeCxt>[] = [
action: 'succeed',
checker(operation, context) {
const { data } = operation as EntityDict['pay']['Update'];
const { externalId } = data;
if (!externalId) {
throw new OakAttrNotNullException('pay', ['externalId']);
if (data) {
const { externalId } = data;
if (!externalId) {
throw new OakAttrNotNullException('pay', ['externalId']);
}
}
}
},
{
entity: 'withdrawTransfer',
type: 'data',
action: 'fail',
checker(operation, context) {
const { data } = operation as EntityDict['pay']['Update'];
if (data) {
const { reason } = data;
if (!reason) {
throw new OakAttrNotNullException('pay', ['reason']);
}
}
}
}

View File

@ -130,7 +130,7 @@ export default OakComponent({
},
},
filter: {
iState: 'refunding',
iState: 'transferring',
withdraw: {
account: {
ofSystemId: systemId,

View File

@ -39,7 +39,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdrawAcc
{
isBank &&
<Form.Item
label={t('label.bank.org')}
label={t('withdraw::account.bank.org')}
>
<Input
value={withdrawAccount.org!}
@ -51,7 +51,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdrawAcc
</Form.Item>
}
<Form.Item
label={isBank ? t('label.bank.name') : t('label.others.name')}
label={isBank ? t('withdraw::account.bank.name') : t('withdraw::account.others.name')}
>
<Input
value={withdrawAccount.name!}

View File

@ -1,8 +1,11 @@
import assert from 'assert';
import { EntityDict } from '../../../oak-app-domain';
import { ToYuan, ThousandCont } from 'oak-domain/lib/utils/money';
export default OakComponent({
entity: 'withdrawTransfer',
isList: true,
actions: ['succeed', 'fail'],
projection: {
id: 1,
price: 1,
@ -32,6 +35,9 @@ export default OakComponent({
}
},
withdrawAccount: {
org: 1,
name: 1,
code: 1,
channel: {
entity: 1,
entityId: 1,
@ -47,25 +53,28 @@ export default OakComponent({
},
formData({ data }) {
return {
refunds: data?.map(
transfers: data?.map(
(ele) => {
const { creator, price, loss, operator, withdrawAccount, ...rest } = ele;
const { creator, operator, withdrawAccount, ...rest } = ele;
const { entity, offlineAccount } = withdrawAccount!.channel!;
const channel = entity === 'offlineAccount' ? this.t(`withdraw::channel.offlineAccount.${offlineAccount?.type}`) : this.t(`withdraw::channel.${entity}`);
return {
...rest,
price: ThousandCont(ToYuan(price!), 2),
loss: ThousandCont(ToYuan(loss!), 2),
creatorName: creator?.name || creator?.nickname || '-',
creatorMobile: creator?.mobile$user?.[0]?.mobile || '-',
operatorName: operator?.name || operator?.nickname || '-',
operatorMobile: operator?.mobile$user?.[0]?.mobile || '-',
withdrawAccount,
channel,
};
}
),
};
},
data: {
updateId: '',
sysAccountAmount: 0,
},
filters: [
{
filter() {
@ -81,4 +90,35 @@ export default OakComponent({
}
}
],
methods: {
async setUpdateId(id: string, action: string) {
this.clean();
this.setState({
updateId: id,
});
if (id) {
this.updateItem({}, id, action);
const row = (this.state.transfers as EntityDict['withdrawTransfer']['Schema'][]).find(
ele => ele.id === id
);
// 每次都刷新这个系统账户当前余额吧
const { entity, entityId } = row!.withdrawAccount!.channel;
const { data: [sysAccount] } = await this.features.cache.refresh(entity as 'offlineAccount', {
data: {
id: 1,
price: 1,
},
filter: {
id: entityId,
}
});
assert(sysAccount);
this.setState({
sysAccountAmount: sysAccount.price!,
});
}
}
}
})

View File

@ -1,7 +1,10 @@
{
"label": {
"cn": "创建人",
"on": "操作者",
"channel": "提现渠道"
}
"channel": "提现渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",
"on": "操作者"
},
"saNotEnough": "系统中此账户的余额不足,请及时同步数据和实际账户一致"
}

View File

@ -0,0 +1,34 @@
.spCon {
display: flex;
flex-direction: column;
align-items: center;
padding: 18px;
.btn {
margin-top: 13px;
align-self: flex-end;
}
}
.entityDetail {
font-size: small;
color: var(--oak-color-primary);
}
.title {
font-size: large;
font-weight: bold;
color: var(--oak-color-primary);
padding: 4px;
}
.warning {
font-weight: bold;
color: red;
}
.actualPrice {
font-weight: bold;
color: var(--oak-color-primary);
text-decoration: underline;
}

View File

@ -6,22 +6,29 @@ import Styles from './web.pc.module.less';
import classNames from 'classnames';
import ListPro from 'oak-frontend-base/es/components/listPro';
import { FilterPanel } from '../../../components/AbstractComponents';
import { ToYuan } from 'oak-domain/lib/utils/money';
import { ThousandCont, ToYuan } from 'oak-domain/lib/utils/money';
import dayJs from 'dayjs';
import assert from 'assert';
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
refunds?: (RowWithActions<EntityDict, 'refund'> & {
transfers?: (RowWithActions<EntityDict, 'withdrawTransfer'> & {
creatorName: string;
creatorMobile: string;
operatorName: string;
operatorMobile: string;
channel: string;
})[];
updateId: string;
sysAccountAmount: number;
}, {
setUpdateId: (id: string, action: string) => void;
}>) {
const { refunds, oakFullpath } = props.data;
const { t } = props.methods;
const { transfers, oakFullpath, oakExecutable, updateId, sysAccountAmount } = props.data;
const { t, updateItem, execute, clean, setMessage, setUpdateId } = props.methods;
if (refunds) {
const [updateAction, setUpdateAction] = useState('');
if (transfers) {
const updateRow = updateId && transfers.find(ele => ele.id === updateId);
return (
<div>
<FilterPanel
@ -38,23 +45,29 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
/>
<ListPro
entity="withdrawTransfer"
data={refunds}
data={transfers}
attributes={[
{
path: 'price',
label: t('order:attr.price'),
label: t('withdrawTransfer:attr.price'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row!.price), 2)}`
}
},
{
path: 'loss',
label: t('order:attr.loss'),
label: t('withdrawTransfer:attr.loss'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row!.loss), 2)}`
}
},
{
path: 'iState',
width: 80,
label: t('order:attr.iState'),
label: t('withdrawTransfer:attr.iState'),
},
{
path: '$$createAt$$',
@ -97,16 +110,123 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
},
{
path: 'externalId',
label: t('order:attr.externalId'),
label: t('withdrawTransfer:attr.externalId'),
width: 120,
},
{
path: 'reason',
label: t('order:attr.reason'),
label: t('withdrawTransfer:attr.reason'),
width: 200,
}
]}
onAction={(row, action) => {
setUpdateId(row.id, action);
setUpdateAction(action);
}}
/>
{updateRow && <Modal
open={!!updateId}
onCancel={() => {
setUpdateId('', '');
setUpdateAction('');
}}
onOk={async () => {
await execute();
setUpdateId('', '');
setUpdateAction('');
}}
destroyOnClose
closeIcon={null}
okText={t('common::confirm')}
cancelText={t('common::action.cancel')}
okButtonProps={{
disabled: oakExecutable !== true,
}}
>
<Form
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
layout="horizontal"
style={{ width: '100%', marginTop: 12 }}
>
<Form.Item
label={t('label.channel')}
>
{updateRow!.channel}
</Form.Item>
{updateRow.withdrawAccount?.org && <Form.Item
label={t('withdraw::account.bank.org')}
>
{updateRow!.withdrawAccount?.org}
</Form.Item>}
{updateRow.withdrawAccount?.name && <Form.Item
label={updateRow.withdrawAccount?.channel?.entity === 'bank' ?
t('withdraw::account.bank.name') :
t('withdraw::account.others.name')}
>
{updateRow!.withdrawAccount?.name}
</Form.Item>}
{updateRow.withdrawAccount?.code && <Form.Item
label={t('withdrawAccount:attr.code')}
>
{updateRow!.withdrawAccount?.code}
</Form.Item>}
<Form.Item
label={t('label.sysAccountAmount')}
help={
(updateAction === 'succeed' && sysAccountAmount < updateRow!.price! - updateRow.loss!) ?
<span className={Styles.warning}>{t('saNotEnough')}</span> :
undefined
}
>
<span className={(updateAction === 'succeed' && sysAccountAmount < updateRow!.price! - updateRow.loss!) ? Styles.warning : undefined}>
{`${t('common::pay.symbol')} ${ThousandCont(ToYuan(sysAccountAmount), 2)}`}
</span>
</Form.Item>
<Form.Item
label={t('label.transferActualPrice')}
>
<span className={Styles.actualPrice}>
{`${t('common::pay.symbol')} ${ThousandCont(ToYuan(updateRow!.price! - updateRow.loss!), 2)}`}
</span>
</Form.Item>
{
updateAction === 'succeed' && (
<Form.Item
label={t('withdrawTransfer:attr.externalId')}
>
<Input
value={updateRow!.externalId!}
onChange={({ currentTarget }) => {
const { value } = currentTarget;
updateItem({
externalId: value,
}, updateId);
}}
/>
</Form.Item>
)
}
{
updateAction === 'fail' && (
<Form.Item
label={t('withdrawTransfer:attr.reason')}
>
<Input
value={updateRow!.reason!}
onChange={({ currentTarget }) => {
const { value } = currentTarget;
updateItem({
reason: value,
}, updateId);
}}
/>
</Form.Item>
)
}
</Form>
</Modal>}
</div>
);
}

View File

@ -572,10 +572,13 @@ const i18ns: I18n[] = [
position: "src/components/withdrawTransfer/list",
data: {
"label": {
"cn": "创建人",
"on": "操作者",
"channel": "提现渠道"
}
"channel": "提现渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",
"on": "操作者"
},
"saNotEnough": "系统中此账户的余额不足,请及时同步数据和实际账户一致"
}
},
{
@ -813,6 +816,15 @@ const i18ns: I18n[] = [
"others": "其它渠道"
},
"wpAccount": "微信支付(到零钱)"
},
"account": {
"bank": {
"org": "银行及所属支行",
"name": "户主姓名"
},
"others": {
"name": "收款人真实姓名"
}
}
}
}

View File

@ -49,7 +49,7 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
loss: '损耗',
withdraw: '关联提现',
meta: 'metadata',
externalId: '外部退款ID',
externalId: '外部退款流水号',
iState: '状态',
creator: '创建者',
reason: '原因',

View File

@ -52,7 +52,7 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
operator: '操作者',
creator: '创建者',
iState: '状态',
externalId: '外部Id',
externalId: '外部转账流水号',
meta: 'metadata',
reason: '原因',
sysOpers: '账户操作'
@ -65,8 +65,8 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
},
},
action: {
succeed: '提现成功',
fail: '提现失败',
succeed: '转账成功',
fail: '转账失败',
},
},
},

View File

@ -22,5 +22,14 @@
"others": "其它渠道"
},
"wpAccount": "微信支付(到零钱)"
},
"account": {
"bank": {
"org": "银行及所属支行",
"name": "户主姓名"
},
"others": {
"name": "收款人真实姓名"
}
}
}

View File

@ -155,8 +155,7 @@ const triggers: Trigger<EntityDict, 'withdraw', BRC>[] = [
]
return 1;
},
} as CreateTriggerInTxn<EntityDict, 'withdraw', BRC>,
} as CreateTriggerInTxn<EntityDict, 'withdraw', BRC>,
{
name: '当withdraw失败时将total和avail还回到帐户',
entity: 'withdraw',

View File

@ -60,9 +60,7 @@ const triggers: Trigger<EntityDict, 'withdrawTransfer', BRC>[] = [
}, {});
cnt ++;
if (tax) {
// 如果转账有手续费由system的account来承受
assert(tax > 0);
if (tax || loss) {
const systemId = withdrawAccount!.ofSystemId!;
const [account] = await context.select('account', {
data: {
@ -73,22 +71,46 @@ const triggers: Trigger<EntityDict, 'withdrawTransfer', BRC>[] = [
entityId: systemId,
}
}, { dontCollect: true });
await context.operate('accountOper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
if (loss) {
// loss也进system account的账户
await context.operate('accountOper', {
id: await generateNewIdAsync(),
accountId: account!.id,
type: 'tax',
totalPlus: -tax,
availPlus: -tax,
entity: 'withdrawTransfer',
entityId: transfer.id!,
},
}, {});
cnt++;
action: 'create',
data: {
id: await generateNewIdAsync(),
accountId: account!.id,
type: 'earn',
totalPlus: loss,
availPlus: loss,
entity: 'withdrawTransfer',
entityId: transfer.id!,
},
}, {});
cnt++;
}
if (tax) {
// 如果转账有手续费由system的account来承受
assert(tax > 0);
await context.operate('accountOper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
accountId: account!.id,
type: 'tax',
totalPlus: -tax,
availPlus: -tax,
entity: 'withdrawTransfer',
entityId: transfer.id!,
},
}, {});
cnt++;
}
}
}