This commit is contained in:
lxy 2025-07-18 17:21:18 +08:00
parent 5c0508cb33
commit c4021ff42c
168 changed files with 2352 additions and 626 deletions

View File

@ -99,22 +99,22 @@ const checkers = [
}
break;
}
case 'preSettle': {
//预分账时账户total增加avail不变refundable不变
if (totalPlus < 0 || availPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
throw new OakInputIllegalException('accountOper', ['totalPlus', 'availPlus', 'refundablePlus'], 'accountOper为preSettle时其totalPlus必须为非负数、availPlus必须为0refundable必须为空或者0');
}
break;
}
// case 'preSettle': {
// //预分账时账户total增加avail不变refundable不变
// if (totalPlus < 0 || availPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
// throw new OakInputIllegalException('accountOper', ['totalPlus', 'availPlus', 'refundablePlus'], 'accountOper为preSettle时其totalPlus必须为非负数、availPlus必须为0refundable必须为空或者0');
// }
// break;
// }
case 'settle': {
//分账时账户total不变refundable不变
if (totalPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
throw new OakInputIllegalException('accountOper', ['totalPlus', 'refundablePlus'], 'accountOper为settle时其totalPlus必须为0refundable必须为空或者0');
//分账时账户total增加avail增加refundable不变
if (totalPlus < 0 || availPlus < 0 || (refundablePlus && refundablePlus !== 0)) {
throw new OakInputIllegalException('accountOper', ['totalPlus', 'refundablePlus'], 'accountOper为settle时其totalPlus、availPlus必须为非负数refundable必须为空或者0');
}
break;
}
default: {
assert(false);
// assert(false);
break;
}
}

View File

@ -9,6 +9,7 @@ import withdrawAccounts from './withdrawAccount';
import refundCheckers from './refund';
import withdrawTransferCheckers from './withdrawTransfer';
import shipCheckers from './ship';
import settlePlanCheckers from './settlePlan';
const checkers = [
...refundCheckers,
...withdrawAccounts,
@ -21,5 +22,6 @@ const checkers = [
...wpProductCheckers,
...withdrawTransferCheckers,
...shipCheckers,
...settlePlanCheckers,
];
export default checkers;

View File

@ -13,7 +13,8 @@ const checkers = [
}
data.paid = 0;
data.refunded = 0;
data.settled = false;
data.settled = 0;
data.settlePlanned = 0;
if (!data.systemId) {
const contextSystemId = context.getApplication()?.systemId;
assert(contextSystemId);
@ -105,51 +106,59 @@ const checkers = [
});
}
},
/**todo settlePlan settle
{
// 订单结算检查存在关联的未结算的settlement其price之和等于订单的已支付与已退款的差值
type: 'logicalData',
entity: 'order',
action: 'settle',
checker: (operation, context) => {
const { data, filter } = operation;
assert(typeof filter.id === 'string');
const { data, filter } = operation as EntityDict['order']['Update'];
assert(typeof filter!.id === 'string');
data.settled = true;
return pipeline(() => context.select('order', {
data: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
iState: 1,
settlement$order: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled'
return pipeline(
() => context.select('order', {
data: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
iState: 1,
settlement$order: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled'
}
}
}
},
filter: {
id: filter.id,
},
}, {}), (orders) => {
const [order] = orders;
const { price, paid, refunded, iState, settlement$order: settlements } = order;
assert(['paid'].includes(iState) && refunded === 0 && paid === price);
assert(settlements && settlements.length > 0, '该结算订单需添加结算明细');
let amount = 0;
settlements.forEach((settlement) => {
amount += settlement.price;
assert(settlement.iState === 'unsettled', '订单结算前settlement必须处于未分账状态');
});
assert(amount === paid);
});
},
filter: {
id: filter!.id!,
},
}, {}),
(orders: EntityDict['order']['Schema'][]) => {
const [order] = orders;
const { price, paid, refunded, iState, settlement$order: settlements } = order;
assert(['paid'].includes(iState!) && refunded === 0 && paid === price);
assert(settlements && settlements.length > 0, '该结算订单需添加结算明细');
let amount = 0;
settlements.forEach((settlement) => {
amount += settlement.price;
assert(settlement.iState === 'unsettled', '订单结算前settlement必须处于未分账状态');
})
assert(amount === paid);
}
);
}
},
*/
];
export default checkers;

5
es/checkers/settlePlan.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Checker } from 'oak-domain/lib/types/Auth';
import { EntityDict } from '../oak-app-domain';
import { RuntimeCxt } from '../types/RuntimeCxt';
declare const checkers: Checker<EntityDict, 'settlePlan', RuntimeCxt>[];
export default checkers;

105
es/checkers/settlePlan.js Normal file
View File

@ -0,0 +1,105 @@
import { pipeline } from 'oak-domain/lib/utils/executor';
import { assert } from 'oak-domain/lib/utils/assert';
const checkers = [
{
/**
* 1settlement.price总和等于settlePlan.price
* 2settlePlan.price <= order.paid - order.refunded - order.settlePlanned
*/
entity: 'settlePlan',
action: 'create',
type: 'logicalData',
priority: 1,
checker: (operation, context) => {
const { data } = operation;
const checkfn = (settlePlan) => {
return pipeline(() => context.select('order', {
data: {
id: 1,
paid: 1,
refunded: 1,
settlePlanned: 1,
},
filter: {
id: settlePlan.orderId,
},
}, { dontCollect: true }), (orders) => {
const [order] = orders;
const { paid, refunded, settlePlanned } = order;
const orderPrice = paid - refunded - settlePlanned;
assert(settlePlan.price <= orderPrice, '结算计划金额不大于订单可结算金额');
return context.select('settlement', {
data: {
id: 1,
price: 1,
iState: 1,
planId: 1,
},
filter: {
planId: settlePlan.id,
iState: 'unsettled',
},
}, { dontCollect: true });
}, (settlements) => {
let amount = 0;
settlements.forEach((settlement) => amount += settlement.price);
if (settlePlan.settlement$plan && settlePlan.settlement$plan.length > 0) {
for (const settlementOper of settlePlan.settlement$plan) {
const settlementData = settlementOper.data;
if (settlementData instanceof Array) {
settlementData.forEach((s) => amount += s.price ?? 0);
}
else {
amount += settlementData?.price ?? 0;
}
}
}
assert(amount === settlePlan.price, '结算计划金额需等于关联的结算明细金额总和');
});
};
if (data instanceof Array) {
for (const d of data) {
checkfn(d);
}
}
else {
checkfn(data);
}
}
},
{
entity: 'settlePlan',
action: 'close',
type: 'logicalData',
checker: (operation, context) => {
const { filter } = operation;
return pipeline(() => context.select('settlePlan', {
data: {
id: 1,
price: 1,
iState: 1,
settlement$plan: {
$entity: 'settlement',
data: {
id: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled',
}
}
},
filter,
}, { dontCollect: true }), (settlePlans) => {
for (const settlePlan of settlePlans) {
const { price: planPrice, settlement$plan: settlements } = settlePlan;
let amount = 0;
settlements.forEach((settlement) => amount += settlement.price);
assert(amount === planPrice, '结算计划金额需等于未结算的结算明细金额总和');
}
});
}
}
];
export default checkers;

View File

@ -4,15 +4,71 @@
*/
import React from 'react';
import { EntityDict } from '../oak-app-domain';
import { ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef } from 'oak-frontend-base';
import AbsFilterPanel from 'oak-frontend-base/es/components/filterPanel';
import AbsList from 'oak-frontend-base/es/components/list';
import AbsListPro from 'oak-frontend-base/es/components/listPro';
import AbsDetail from 'oak-frontend-base/es/components/detail';
import AbsUpsert from 'oak-frontend-base/es/components/upsert';
declare const FilterPanel: <T extends keyof EntityDict>(...props: Parameters<typeof AbsFilterPanel<EntityDict, T>>) => React.ReactElement;
declare const List: <T extends keyof EntityDict>(...props: Parameters<typeof AbsList<EntityDict, T>>) => React.ReactElement;
declare const ListPro: <T extends keyof EntityDict>(...props: Parameters<typeof AbsListPro<EntityDict, T>>) => React.ReactElement;
declare const Detail: <T extends keyof EntityDict>(...props: Parameters<typeof AbsDetail<EntityDict, T>>) => React.ReactElement;
declare const Upsert: <T extends keyof EntityDict>(...props: Parameters<typeof AbsUpsert<EntityDict, T>>) => React.ReactElement;
import { ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef, ListButtonProps, OakAbsAttrUpsertDef } from 'oak-frontend-base';
declare const FilterPanel: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
entity: T;
columns: ColumnProps<EntityDict, T>[];
}>) => React.ReactElement;
declare const List: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
entity: T;
extraActions: OakExtraActionProps[] | ((row: RowWithActions<EntityDict, T>) => OakExtraActionProps[]);
onAction: onActionFnDef;
disabledOp: boolean;
attributes: OakAbsAttrDef[];
data: RowWithActions<EntityDict, T>[];
loading: boolean;
tablePagination?: React.ReactNode;
rowSelection?: any;
hideHeader?: boolean | undefined;
disableSerialNumber?: boolean | undefined;
size?: "small" | "middle" | "large" | undefined;
scroll?: ({
x?: string | number | true | undefined;
y?: string | number | undefined;
} & {
scrollToFirstRowOnChange?: boolean | undefined;
}) | undefined;
empty?: React.ReactNode;
opWidth?: number | undefined;
ellipsis?: boolean | undefined;
}>) => React.ReactElement;
declare const ListPro: <T extends keyof EntityDict>(props: {
title?: any;
extraContent?: any;
hideDefaultButtons?: boolean | undefined;
buttonGroup?: ListButtonProps[] | undefined;
onReload?: (() => void) | undefined;
entity: T;
extraActions?: OakExtraActionProps[] | ((row: RowWithActions<EntityDict, T>) => OakExtraActionProps[]) | undefined;
onAction?: onActionFnDef | undefined;
disabledOp?: boolean | undefined;
attributes: OakAbsAttrDef[];
data: RowWithActions<EntityDict, T>[];
loading?: boolean | undefined;
tablePagination?: any;
rowSelection?: any;
disableSerialNumber?: boolean | undefined;
size?: "small" | "middle" | "large" | undefined;
scroll?: any;
empty?: any;
opWidth?: number | undefined;
oakPath?: string | undefined;
}) => React.ReactElement;
declare const Detail: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
column?: number | Record<Breakpoint, number> | undefined;
entity: T;
attributes: OakAbsAttrDef[];
data: Partial<EntityDict[T]["Schema"]>;
title?: string | undefined;
bordered?: boolean | undefined;
layout?: "horizontal" | "vertical" | undefined;
}>) => React.ReactElement;
declare const Upsert: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
helps: Record<string, string>;
entity: T;
attributes: OakAbsAttrUpsertDef<EntityDict, T, string | number>[];
data: EntityDict[T]["Schema"];
layout: "horizontal" | "vertical";
mode: "default" | "card";
}>) => React.ReactElement;
export { FilterPanel, List, ListPro, Detail, Upsert, ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef, };

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
import { EntityDict } from "../../../oak-app-domain";
export default function Render(props: {
accountOpers: EntityDict['accountOper']['OpSchema'][];

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "offlineAccount", false, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "order", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -2,7 +2,7 @@ import { EntityDict } from "../../../oak-app-domain";
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "order", false, {
accountId: string;
accountAvailMax: number;
onSetPays: (pays: Partial<EntityDict["pay"]["CreateOperationData"]>[]) => void;
onSetPays: (pays: Partial<EntityDict['pay']['CreateOperationData']>[]) => void;
accountTips: string;
autoStartPay: boolean;
}>) => React.ReactElement;

View File

@ -24,10 +24,10 @@ export default OakComponent({
},
isList: false,
properties: {
accountId: '', // 是否可以使用帐户中的余额抵扣
accountAvailMax: 0, // 本次交易可以使用的帐户中的Avail max值调用者自己保证此数值的一致性不要扣成负数
accountId: '',
accountAvailMax: 0,
onSetPays: (pays) => undefined,
accountTips: '', // 使用余额支付的提示说明
accountTips: '',
autoStartPay: false,
},
formData({ data }) {

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
export default function Info(props: {
price: number;
t: (k: string) => string;

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
import { PayChannelOption } from "../../../types/Pay";
export default function Render(props: {
data: {

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
import { PayChannelOption } from "../../../types/Pay";
export default function Render(props: {
data: {

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "pay", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "system", false, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "refund", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "system", false, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,3 +1,4 @@
/// <reference types="wechat-miniprogram" />
import { EntityDict } from "../../../../oak-app-domain";
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "wechatMpShip", false, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -156,7 +156,7 @@ export default OakComponent({
},
},
filter: {
settled: false,
settled: 0,
price: {
$gt: 0,
},

View File

@ -1,2 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "withdrawTransfer", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,3 +1,4 @@
/// <reference types="wechat-miniprogram" />
import { EntityDict } from "../../../oak-app-domain";
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "withdraw", false, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
import { EntityDict } from '../../../oak-app-domain';
export default function render(props: {
data: {

View File

@ -1,3 +1,4 @@
/// <reference types="react" />
import { EntityDict } from '../../../oak-app-domain';
export default function render(props: {
data: {

View File

@ -1,3 +1,4 @@
/// <reference types="wechat-miniprogram" />
import { EntityDict } from '../../../oak-app-domain';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "withdrawTransfer", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -924,7 +924,9 @@ const i18ns = [
"create": {
"hasAnotherRefunding": "您有一笔退款正在进行中",
"exceedMax": "退款额度大于可用值",
"payUnrefundable": "支付已经不可退款"
"payUnrefundable": "支付已经不可退款",
"opersNotEnough": "指定退款帐户的操作金额总和不足",
"opersExceed": "指定退款帐户的操作金额总和超过订单已结算金额"
}
},
"pay": {
@ -932,7 +934,11 @@ const i18ns = [
"priceOverflow": "支付金额总和大于订单金额"
},
"ship": {
"noClazz": "当前物流未关联外部接口"
"noClazz": "当前物流未关联外部接口",
"uploadShipException": "小程序上传发货信息异常"
},
"settlePlan": {
"exceed": "结算计划总金额超过订单可结算金额上限"
}
}
},

View File

@ -2,7 +2,7 @@ import { String, Price } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as Account } from './Account';
type Type = 'deposit' | 'withdraw' | 'consume' | 'consumeBack' | 'loan' | 'repay' | 'withdrawBack' | 'earn' | 'cutoffRefundable' | 'tax' | 'taxRefund' | 'refund' | 'refundFailure' | 'preSettle' | 'settle';
type Type = 'deposit' | 'withdraw' | 'consume' | 'consumeBack' | 'loan' | 'repay' | 'withdrawBack' | 'earn' | 'cutoffRefundable' | 'tax' | 'taxRefund' | 'refund' | 'refundFailure' | 'settle';
export interface Schema extends EntityShape {
account: Account;
type: Type;

View File

@ -30,7 +30,6 @@ export const entityDesc = {
cutoffRefundable: '削减可自由退额度',
refund: '退款',
refundFailure: '退款失败',
preSettle: '预分账',
settle: '分账',
},
},
@ -52,7 +51,6 @@ export const entityDesc = {
cutoffRefundable: '#2E4053',
refund: '#CC3333',
refundFailure: '#009933',
preSettle: '#8A631D',
settle: '#157d5a',
}
}

View File

@ -30,7 +30,7 @@ export const entityDesc = {
keyType: '指定应用私钥类型',
gateway: "支付宝网关地址",
wsServiceUrl: 'websocket服务地址',
timeout: "网关超时时间", //(单位毫秒),默认值是 5000
timeout: "网关超时时间",
needEncrypt: "是否需要AES加解密",
encryptKey: 'AES密钥', //调用AES加解密相关接口时需要
},

View File

@ -9,13 +9,14 @@ export interface Schema extends EntityShape {
price: Price;
paid: Price;
refunded: Price;
settled: Price;
settlePlanned: Price;
title: String<32>;
desc: Text;
timeoutAt?: Datetime;
creator: User;
entity: String<32>;
entityId: String<64>;
settled: Boolean;
allowPartialPay?: Boolean;
system: System;
address?: Address;
@ -25,7 +26,7 @@ export interface Schema extends EntityShape {
export type IAction = 'startPaying' | 'payAll' | 'payPartially' | 'payNone' | 'timeout' | 'cancel' | 'startRefunding' | 'refundAll' | 'refundPartially' | 'refundNone';
export type IState = 'unpaid' | 'timeout' | 'cancelled' | 'paying' | 'partiallyPaid' | 'paid' | 'refunding' | 'partiallyRefunded' | 'refunded';
export declare const IActionDef: ActionDef<IAction, IState>;
export type Action = IAction | 'settle';
export type Action = IAction;
export declare const entityDesc: EntityDesc<Schema, Action, '', {
iState: IState;
}>;

View File

@ -51,6 +51,8 @@ export const entityDesc = {
price: '订单金额',
paid: '已支付金额',
refunded: '已退款金额',
settled: '已结算金额',
settlePlanned: '已计划结算金额',
iState: '订单状态',
title: '订单标题',
desc: "订单描述",
@ -59,7 +61,6 @@ export const entityDesc = {
creator: '创建者',
entity: '关联对象',
entityId: '关联对象Id',
settled: '是否结算',
opers: '相关帐户操作',
system: '所属系统',
address: '收货地址',
@ -76,7 +77,6 @@ export const entityDesc = {
refundAll: '完全退款',
refundNone: '退款失败',
refundPartially: '部分退款',
settle: '结算',
},
v: {
iState: {
@ -105,7 +105,6 @@ export const entityDesc = {
refundAll: '',
refundNone: '',
refundPartially: '',
settle: '',
},
color: {
iState: {

17
es/entities/SettlePlan.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
import { Price, Datetime } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as Order } from './Order';
export interface Schema extends EntityShape {
when?: Datetime;
order: Order;
price: Price;
}
type IState = 'unsettled' | 'settled' | 'closed';
type IAction = 'settle' | 'close';
export declare const IActionDef: ActionDef<IAction, IState>;
type Action = IAction;
export declare const entityDesc: EntityDesc<Schema, Action, '', {
iState: IState;
}>;
export {};

45
es/entities/SettlePlan.js Normal file
View File

@ -0,0 +1,45 @@
;
export const IActionDef = {
stm: {
settle: ['unsettled', 'settled'],
close: ['unsettled', 'closed'],
},
is: 'unsettled',
};
export const entityDesc = {
locales: {
zh_CN: {
name: '结算计划',
attr: {
when: '结算时间',
order: '订单',
price: '结算金额',
iState: '结算计划状态',
},
action: {
settle: '结算',
close: '关闭',
},
v: {
iState: {
unsettled: '未结算',
settled: '已结算',
closed: '已关闭',
},
},
},
},
style: {
icon: {
settle: '',
close: '',
},
color: {
iState: {
unsettled: '#52BE80',
settled: '#2E86C1',
closed: '#8C949C'
},
}
},
};

View File

@ -2,18 +2,18 @@ import { Price } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as Account } from './Account';
import { Schema as Order } from './Order';
import { Schema as SettlePlan } from './SettlePlan';
import { Schema as AccountOper } from './AccountOper';
export interface Schema extends EntityShape {
account: Account;
plan: SettlePlan;
price: Price;
order: Order;
opers?: AccountOper[];
}
type IState = 'unsettled' | 'settled';
type IAction = 'settle';
type IState = 'unsettled' | 'settled' | 'closed';
type IAction = 'settle' | 'close';
export declare const IActionDef: ActionDef<IAction, IState>;
type Action = IAction | 'preSettle' | 'refund' | 'refundFailure';
type Action = IAction | 'refund' | 'refundFailure';
export declare const entityDesc: EntityDesc<Schema, Action, '', {
iState: IState;
}>;

View File

@ -2,6 +2,7 @@
export const IActionDef = {
stm: {
settle: ['unsettled', 'settled'],
close: ['unsettled', 'closed'],
},
is: 'unsettled',
};
@ -11,14 +12,14 @@ export const entityDesc = {
name: '结算明细',
attr: {
account: '帐号',
plan: '结算计划',
price: '变化金额',
order: '订单',
iState: '结算状态',
opers: '相关帐户操作',
},
action: {
preSettle: '预结算',
settle: '结算',
close: '关闭',
refund: '退款',
refundFailure: '退款失败',
},
@ -26,6 +27,7 @@ export const entityDesc = {
iState: {
unsettled: '未结算',
settled: '已结算',
closed: '已关闭',
},
},
},
@ -40,6 +42,7 @@ export const entityDesc = {
iState: {
unsettled: '#52BE80',
settled: '#2E86C1',
closed: '#8C949C'
},
}
},

View File

@ -21,7 +21,9 @@
"create": {
"hasAnotherRefunding": "您有一笔退款正在进行中",
"exceedMax": "退款额度大于可用值",
"payUnrefundable": "支付已经不可退款"
"payUnrefundable": "支付已经不可退款",
"opersNotEnough": "指定退款帐户的操作金额总和不足",
"opersExceed": "指定退款帐户的操作金额总和超过订单已结算金额"
}
},
"pay": {
@ -29,6 +31,10 @@
"priceOverflow": "支付金额总和大于订单金额"
},
"ship": {
"noClazz": "当前物流未关联外部接口"
"noClazz": "当前物流未关联外部接口",
"uploadShipException": "小程序上传发货信息异常"
},
"settlePlan": {
"exceed": "结算计划总金额超过订单可结算金额上限"
}
}

View File

@ -9,7 +9,7 @@ export const desc = {
type: {
notNull: true,
type: "enum",
enumeration: ["deposit", "withdraw", "consume", "consumeBack", "loan", "repay", "withdrawBack", "earn", "cutoffRefundable", "tax", "taxRefund", "refund", "refundFailure", "preSettle", "settle"]
enumeration: ["deposit", "withdraw", "consume", "consumeBack", "loan", "repay", "withdrawBack", "earn", "cutoffRefundable", "tax", "taxRefund", "refund", "refundFailure", "settle"]
},
totalPlus: {
notNull: true,

View File

@ -14,7 +14,6 @@ export const style = {
cutoffRefundable: '#2E4053',
refund: '#CC3333',
refundFailure: '#009933',
preSettle: '#8A631D',
settle: '#157d5a',
}
}

View File

@ -3,7 +3,7 @@ import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, ExprOp,
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
import { AppendOnlyAction } from "oak-domain/lib/actions/action";
import { Price, String } from "oak-domain/lib/types/DataType";
type Type = "deposit" | "withdraw" | "consume" | "consumeBack" | "loan" | "repay" | "withdrawBack" | "earn" | "cutoffRefundable" | "tax" | "taxRefund" | "refund" | "refundFailure" | "preSettle" | "settle";
type Type = "deposit" | "withdraw" | "consume" | "consumeBack" | "loan" | "repay" | "withdrawBack" | "earn" | "cutoffRefundable" | "tax" | "taxRefund" | "refund" | "refundFailure" | "settle";
export type OpSchema = EntityShape & {
accountId: ForeignKey<"account">;
type: Type;

View File

@ -27,7 +27,6 @@
"cutoffRefundable": "削减可自由退额度",
"refund": "退款",
"refundFailure": "退款失败",
"preSettle": "预分账",
"settle": "分账"
}
}

View File

@ -64,6 +64,9 @@ export declare const actionDefDict: {
settlement: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};
settlePlan: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};
ship: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};

View File

@ -24,6 +24,7 @@ import { actionDefDict as order } from "./Order/Action";
import { actionDefDict as pay } from "./Pay/Action";
import { actionDefDict as refund } from "./Refund/Action";
import { actionDefDict as settlement } from "./Settlement/Action";
import { actionDefDict as settlePlan } from "./SettlePlan/Action";
import { actionDefDict as ship } from "./Ship/Action";
import { actionDefDict as user } from "./User/Action";
import { actionDefDict as withdraw } from "./Withdraw/Action";
@ -58,6 +59,7 @@ export const actionDefDict = {
pay,
refund,
settlement,
settlePlan,
ship,
user,
withdraw,

View File

@ -72,6 +72,7 @@ import * as BaseOrder from "./Order/_baseSchema";
import * as BasePay from "./Pay/_baseSchema";
import * as BaseRefund from "./Refund/_baseSchema";
import * as BaseSettlement from "./Settlement/_baseSchema";
import * as BaseSettlePlan from "./SettlePlan/_baseSchema";
import * as BaseShip from "./Ship/_baseSchema";
import * as BaseShipCompany from "./ShipCompany/_baseSchema";
import * as BaseShipOrder from "./ShipOrder/_baseSchema";
@ -4411,11 +4412,11 @@ export type Order = {
system: System["Schema"];
address?: Address["Schema"];
pay$order?: Array<Omit<Pay["Schema"], "order">>;
settlement$order?: Array<Omit<Settlement["Schema"], "order">>;
settlePlan$order?: Array<Omit<SettlePlan["Schema"], "order">>;
shipOrder$order?: Array<Omit<ShipOrder["Schema"], "order">>;
accountOper$entity?: Array<Omit<AccountOper["Schema"], "entity">>;
pay$order$$aggr?: AggregationResult<Omit<Pay["Schema"], "order">>;
settlement$order$$aggr?: AggregationResult<Omit<Settlement["Schema"], "order">>;
settlePlan$order$$aggr?: AggregationResult<Omit<SettlePlan["Schema"], "order">>;
shipOrder$order$$aggr?: AggregationResult<Omit<ShipOrder["Schema"], "order">>;
accountOper$entity$$aggr?: AggregationResult<Omit<AccountOper["Schema"], "entity">>;
};
@ -4429,11 +4430,11 @@ export type Order = {
pay$order$$aggr?: DeduceAggregation<Omit<Pay["Projection"], "order">, Omit<Pay["Filter"], "order">, Pay["Sorter"]> & {
$entity: "pay";
};
settlement$order?: OakSelection<"select", Omit<Settlement["Projection"], "order">, Omit<Settlement["Filter"], "order">, Settlement["Sorter"]> & {
$entity: "settlement";
settlePlan$order?: OakSelection<"select", Omit<SettlePlan["Projection"], "order">, Omit<SettlePlan["Filter"], "order">, SettlePlan["Sorter"]> & {
$entity: "settlePlan";
};
settlement$order$$aggr?: DeduceAggregation<Omit<Settlement["Projection"], "order">, Omit<Settlement["Filter"], "order">, Settlement["Sorter"]> & {
$entity: "settlement";
settlePlan$order$$aggr?: DeduceAggregation<Omit<SettlePlan["Projection"], "order">, Omit<SettlePlan["Filter"], "order">, SettlePlan["Sorter"]> & {
$entity: "settlePlan";
};
shipOrder$order?: OakSelection<"select", Omit<ShipOrder["Projection"], "order">, Omit<ShipOrder["Filter"], "order">, ShipOrder["Sorter"]> & {
$entity: "shipOrder";
@ -4453,7 +4454,7 @@ export type Order = {
system: MakeFilter<System["FilterUnit"]>;
address: MakeFilter<Address["FilterUnit"]>;
pay$order: MakeFilter<Omit<Pay["FilterUnit"], "order">> & SubQueryPredicateMetadata;
settlement$order: MakeFilter<Omit<Settlement["FilterUnit"], "order">> & SubQueryPredicateMetadata;
settlePlan$order: MakeFilter<Omit<SettlePlan["FilterUnit"], "order">> & SubQueryPredicateMetadata;
shipOrder$order: MakeFilter<Omit<ShipOrder["FilterUnit"], "order">> & SubQueryPredicateMetadata;
accountOper$entity: MakeFilter<Omit<AccountOper["FilterUnit"], "entity">> & SubQueryPredicateMetadata;
};
@ -4475,7 +4476,7 @@ export type Order = {
system?: OakOperation<"create", System["CreateOperationData"]> | OakOperation<BaseSystem.OpUpdateAction, System["UpdateOperationData"], System["Filter"]>;
address?: OakOperation<"create", Address["CreateOperationData"]> | OakOperation<BaseAddress.OpUpdateAction, Address["UpdateOperationData"], Address["Filter"]>;
pay$order?: (OakOperation<"create", Omit<Pay["CreateOperationData"], "order"> | Omit<Pay["CreateOperationData"], "order">[]> | OakOperation<BasePay.OpUpdateAction, Omit<Pay["UpdateOperationData"], "order">, Pay["Filter"]>)[];
settlement$order?: (OakOperation<"create", Omit<Settlement["CreateOperationData"], "order"> | Omit<Settlement["CreateOperationData"], "order">[]> | OakOperation<BaseSettlement.OpUpdateAction, Omit<Settlement["UpdateOperationData"], "order">, Settlement["Filter"]>)[];
settlePlan$order?: (OakOperation<"create", Omit<SettlePlan["CreateOperationData"], "order"> | Omit<SettlePlan["CreateOperationData"], "order">[]> | OakOperation<BaseSettlePlan.OpUpdateAction, Omit<SettlePlan["UpdateOperationData"], "order">, SettlePlan["Filter"]>)[];
shipOrder$order?: (OakOperation<"create", Omit<ShipOrder["CreateOperationData"], "order"> | Omit<ShipOrder["CreateOperationData"], "order">[]> | OakOperation<BaseShipOrder.OpUpdateAction, Omit<ShipOrder["UpdateOperationData"], "order">, ShipOrder["Filter"]>)[];
accountOper$entity?: OakOperation<"create", Omit<AccountOper["CreateOperationData"], "order"> | Omit<AccountOper["CreateOperationData"], "order">[]>[];
}>;
@ -4487,7 +4488,7 @@ export type Order = {
system?: OakOperation<"create", System["CreateOperationData"]> | OakOperation<BaseSystem.OpUpdateAction, System["UpdateOperationData"], System["Filter"]> | OakOperation<"remove", System["RemoveOperationData"], System["Filter"]>;
address?: OakOperation<"create", Address["CreateOperationData"]> | OakOperation<BaseAddress.OpUpdateAction, Address["UpdateOperationData"], Address["Filter"]> | OakOperation<"remove", Address["RemoveOperationData"], Address["Filter"]>;
pay$order?: (OakOperation<"create", Omit<Pay["CreateOperationData"], "order"> | Omit<Pay["CreateOperationData"], "order">[]> | OakOperation<BasePay.OpUpdateAction, Omit<Pay["UpdateOperationData"], "order">, Pay["Filter"]> | OakOperation<"remove", Pay["RemoveOperationData"], Pay["Filter"]>)[];
settlement$order?: (OakOperation<"create", Omit<Settlement["CreateOperationData"], "order"> | Omit<Settlement["CreateOperationData"], "order">[]> | OakOperation<BaseSettlement.OpUpdateAction, Omit<Settlement["UpdateOperationData"], "order">, Settlement["Filter"]> | OakOperation<"remove", Settlement["RemoveOperationData"], Settlement["Filter"]>)[];
settlePlan$order?: (OakOperation<"create", Omit<SettlePlan["CreateOperationData"], "order"> | Omit<SettlePlan["CreateOperationData"], "order">[]> | OakOperation<BaseSettlePlan.OpUpdateAction, Omit<SettlePlan["UpdateOperationData"], "order">, SettlePlan["Filter"]> | OakOperation<"remove", SettlePlan["RemoveOperationData"], SettlePlan["Filter"]>)[];
shipOrder$order?: (OakOperation<"create", Omit<ShipOrder["CreateOperationData"], "order"> | Omit<ShipOrder["CreateOperationData"], "order">[]> | OakOperation<BaseShipOrder.OpUpdateAction, Omit<ShipOrder["UpdateOperationData"], "order">, ShipOrder["Filter"]> | OakOperation<"remove", ShipOrder["RemoveOperationData"], ShipOrder["Filter"]>)[];
accountOper$entity?: OakOperation<"create", Omit<AccountOper["CreateOperationData"], "order"> | Omit<AccountOper["CreateOperationData"], "order">[]>[];
}>;
@ -4684,13 +4685,13 @@ export type Settlement = {
Action: BaseSettlement.OpAction;
Schema: BaseSettlement.OpSchema & {
account: Account["Schema"];
order: Order["Schema"];
plan: SettlePlan["Schema"];
accountOper$entity?: Array<Omit<AccountOper["Schema"], "entity">>;
accountOper$entity$$aggr?: AggregationResult<Omit<AccountOper["Schema"], "entity">>;
};
Projection: BaseSettlement.OpProjection & {
account?: Account["Projection"];
order?: Order["Projection"];
plan?: SettlePlan["Projection"];
accountOper$entity?: OakSelection<"select", Omit<AccountOper["Projection"], "settlement">, Omit<AccountOper["Filter"], "settlement">, AccountOper["Sorter"]> & {
$entity: "accountOper";
};
@ -4700,13 +4701,13 @@ export type Settlement = {
};
FilterUnit: BaseSettlement.OpFilter & {
account: MakeFilter<Account["FilterUnit"]>;
order: MakeFilter<Order["FilterUnit"]>;
plan: MakeFilter<SettlePlan["FilterUnit"]>;
accountOper$entity: MakeFilter<Omit<AccountOper["FilterUnit"], "entity">> & SubQueryPredicateMetadata;
};
Filter: MakeFilter<Settlement["FilterUnit"]>;
SortAttr: Partial<BaseSettlement.OpSortAttr | {
account: Account["SortAttr"];
order: Order["SortAttr"];
plan: SettlePlan["SortAttr"];
}>;
SortNode: {
$attr: Settlement["SortAttr"];
@ -4717,7 +4718,7 @@ export type Settlement = {
Aggregation: DeduceAggregation<Settlement["Projection"], Settlement["Filter"], Settlement["Sorter"]>;
CreateOperationData: FormCreateData<BaseSettlement.OpSchema & {
account?: OakOperation<"create", Account["CreateOperationData"]> | OakOperation<BaseAccount.OpUpdateAction, Account["UpdateOperationData"], Account["Filter"]>;
order?: OakOperation<"create", Order["CreateOperationData"]> | OakOperation<BaseOrder.OpUpdateAction, Order["UpdateOperationData"], Order["Filter"]>;
plan?: OakOperation<"create", SettlePlan["CreateOperationData"]> | OakOperation<BaseSettlePlan.OpUpdateAction, SettlePlan["UpdateOperationData"], SettlePlan["Filter"]>;
accountOper$entity?: OakOperation<"create", Omit<AccountOper["CreateOperationData"], "settlement"> | Omit<AccountOper["CreateOperationData"], "settlement">[]>[];
}>;
CreateSingle: OakOperation<"create", Settlement["CreateOperationData"]>;
@ -4725,7 +4726,7 @@ export type Settlement = {
Create: Settlement["CreateSingle"] | Settlement["CreateMulti"];
UpdateOperationData: FormUpdateData<BaseSettlement.OpSchema & {
account?: OakOperation<"create", Account["CreateOperationData"]> | OakOperation<BaseAccount.OpUpdateAction, Account["UpdateOperationData"], Account["Filter"]> | OakOperation<"remove", Account["RemoveOperationData"], Account["Filter"]>;
order?: OakOperation<"create", Order["CreateOperationData"]> | OakOperation<BaseOrder.OpUpdateAction, Order["UpdateOperationData"], Order["Filter"]> | OakOperation<"remove", Order["RemoveOperationData"], Order["Filter"]>;
plan?: OakOperation<"create", SettlePlan["CreateOperationData"]> | OakOperation<BaseSettlePlan.OpUpdateAction, SettlePlan["UpdateOperationData"], SettlePlan["Filter"]> | OakOperation<"remove", SettlePlan["RemoveOperationData"], SettlePlan["Filter"]>;
accountOper$entity?: OakOperation<"create", Omit<AccountOper["CreateOperationData"], "settlement"> | Omit<AccountOper["CreateOperationData"], "settlement">[]>[];
}>;
Update: OakOperation<BaseSettlement.OpUpdateAction, Settlement["UpdateOperationData"], Settlement["Filter"], Settlement["Sorter"]>;
@ -4733,6 +4734,54 @@ export type Settlement = {
Remove: OakOperation<"remove", Settlement["RemoveOperationData"], Settlement["Filter"], Settlement["Sorter"]>;
Operation: Settlement["Create"] | Settlement["Update"] | Settlement["Remove"];
};
export type SettlePlan = {
OpSchema: BaseSettlePlan.OpSchema;
Action: BaseSettlePlan.OpAction;
Schema: BaseSettlePlan.OpSchema & {
order: Order["Schema"];
settlement$plan?: Array<Omit<Settlement["Schema"], "plan">>;
settlement$plan$$aggr?: AggregationResult<Omit<Settlement["Schema"], "plan">>;
};
Projection: BaseSettlePlan.OpProjection & {
order?: Order["Projection"];
settlement$plan?: OakSelection<"select", Omit<Settlement["Projection"], "settlePlan">, Omit<Settlement["Filter"], "settlePlan">, Settlement["Sorter"]> & {
$entity: "settlement";
};
settlement$plan$$aggr?: DeduceAggregation<Omit<Settlement["Projection"], "settlePlan">, Omit<Settlement["Filter"], "settlePlan">, Settlement["Sorter"]> & {
$entity: "settlement";
};
};
FilterUnit: BaseSettlePlan.OpFilter & {
order: MakeFilter<Order["FilterUnit"]>;
settlement$plan: MakeFilter<Omit<Settlement["FilterUnit"], "plan">> & SubQueryPredicateMetadata;
};
Filter: MakeFilter<SettlePlan["FilterUnit"]>;
SortAttr: Partial<BaseSettlePlan.OpSortAttr | {
order: Order["SortAttr"];
}>;
SortNode: {
$attr: SettlePlan["SortAttr"];
$direction?: "asc" | "desc";
};
Sorter: SettlePlan["SortNode"][];
Selection: OakSelection<"select", SettlePlan["Projection"], SettlePlan["Filter"], SettlePlan["Sorter"]>;
Aggregation: DeduceAggregation<SettlePlan["Projection"], SettlePlan["Filter"], SettlePlan["Sorter"]>;
CreateOperationData: FormCreateData<BaseSettlePlan.OpSchema & {
order?: OakOperation<"create", Order["CreateOperationData"]> | OakOperation<BaseOrder.OpUpdateAction, Order["UpdateOperationData"], Order["Filter"]>;
settlement$plan?: (OakOperation<"create", Omit<Settlement["CreateOperationData"], "plan"> | Omit<Settlement["CreateOperationData"], "plan">[]> | OakOperation<BaseSettlement.OpUpdateAction, Omit<Settlement["UpdateOperationData"], "plan">, Settlement["Filter"]>)[];
}>;
CreateSingle: OakOperation<"create", SettlePlan["CreateOperationData"]>;
CreateMulti: OakOperation<"create", Array<SettlePlan["CreateOperationData"]>>;
Create: SettlePlan["CreateSingle"] | SettlePlan["CreateMulti"];
UpdateOperationData: FormUpdateData<BaseSettlePlan.OpSchema & {
order?: OakOperation<"create", Order["CreateOperationData"]> | OakOperation<BaseOrder.OpUpdateAction, Order["UpdateOperationData"], Order["Filter"]> | OakOperation<"remove", Order["RemoveOperationData"], Order["Filter"]>;
settlement$plan?: (OakOperation<"create", Omit<Settlement["CreateOperationData"], "plan"> | Omit<Settlement["CreateOperationData"], "plan">[]> | OakOperation<BaseSettlement.OpUpdateAction, Omit<Settlement["UpdateOperationData"], "plan">, Settlement["Filter"]> | OakOperation<"remove", Settlement["RemoveOperationData"], Settlement["Filter"]>)[];
}>;
Update: OakOperation<BaseSettlePlan.OpUpdateAction, SettlePlan["UpdateOperationData"], SettlePlan["Filter"], SettlePlan["Sorter"]>;
RemoveOperationData: {};
Remove: OakOperation<"remove", SettlePlan["RemoveOperationData"], SettlePlan["Filter"], SettlePlan["Sorter"]>;
Operation: SettlePlan["Create"] | SettlePlan["Update"] | SettlePlan["Remove"];
};
export type Ship = {
OpSchema: BaseShip.OpSchema;
Action: BaseShip.OpAction;
@ -5719,6 +5768,7 @@ export type EntityDict = {
pay: Pay;
refund: Refund;
settlement: Settlement;
settlePlan: SettlePlan;
ship: Ship;
shipCompany: ShipCompany;
shipOrder: ShipOrder;

View File

@ -3,7 +3,7 @@ import { GenericAction } from "oak-domain/lib/actions/action";
export type IAction = 'startPaying' | 'payAll' | 'payPartially' | 'payNone' | 'timeout' | 'cancel' | 'startRefunding' | 'refundAll' | 'refundPartially' | 'refundNone' | string;
export type IState = 'unpaid' | 'timeout' | 'cancelled' | 'paying' | 'partiallyPaid' | 'paid' | 'refunding' | 'partiallyRefunded' | 'refunded' | string;
export declare const IActionDef: ActionDef<IAction, IState>;
export type ParticularAction = IAction | 'settle';
export type ParticularAction = IAction;
export declare const actions: string[];
export type Action = GenericAction | ParticularAction | string;
export declare const actionDefDict: {

View File

@ -13,7 +13,7 @@ export const IActionDef = {
},
is: 'unpaid',
};
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "startPaying", "payAll", "payPartially", "payNone", "timeout", "cancel", "startRefunding", "refundAll", "refundPartially", "refundNone", "settle"];
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "startPaying", "payAll", "payPartially", "payNone", "timeout", "cancel", "startRefunding", "refundAll", "refundPartially", "refundNone"];
export const actionDefDict = {
iState: IActionDef
};

View File

@ -13,6 +13,14 @@ export const desc = {
notNull: true,
type: "money"
},
settled: {
notNull: true,
type: "money"
},
settlePlanned: {
notNull: true,
type: "money"
},
title: {
notNull: true,
type: "varchar",
@ -46,10 +54,6 @@ export const desc = {
length: 64
}
},
settled: {
notNull: true,
type: "boolean"
},
allowPartialPay: {
type: "boolean"
},

View File

@ -10,7 +10,6 @@ export const style = {
refundAll: '',
refundNone: '',
refundPartially: '',
settle: '',
},
color: {
iState: {

View File

@ -7,13 +7,14 @@ export type OpSchema = EntityShape & {
price: Price;
paid: Price;
refunded: Price;
settled: Price;
settlePlanned: Price;
title: String<32>;
desc: Text;
timeoutAt?: Datetime | null;
creatorId: ForeignKey<"user">;
entity: String<32>;
entityId: String<64>;
settled: Boolean;
allowPartialPay?: Boolean | null;
systemId: ForeignKey<"system">;
addressId?: ForeignKey<"address"> | null;
@ -31,13 +32,14 @@ export type OpFilter = {
price: Q_NumberValue;
paid: Q_NumberValue;
refunded: Q_NumberValue;
settled: Q_NumberValue;
settlePlanned: Q_NumberValue;
title: Q_StringValue;
desc: Q_StringValue;
timeoutAt: Q_DateValue;
creatorId: Q_StringValue;
entity: Q_StringValue;
entityId: Q_StringValue;
settled: Q_BooleanValue;
allowPartialPay: Q_BooleanValue;
systemId: Q_StringValue;
addressId: Q_StringValue;
@ -54,13 +56,14 @@ export type OpProjection = {
price?: number;
paid?: number;
refunded?: number;
settled?: number;
settlePlanned?: number;
title?: number;
desc?: number;
timeoutAt?: number;
creatorId?: number;
entity?: number;
entityId?: number;
settled?: number;
allowPartialPay?: number;
systemId?: number;
addressId?: number;
@ -75,13 +78,14 @@ export type OpSortAttr = Partial<{
price: number;
paid: number;
refunded: number;
settled: number;
settlePlanned: number;
title: number;
desc: number;
timeoutAt: number;
creatorId: number;
entity: number;
entityId: number;
settled: number;
allowPartialPay: number;
systemId: number;
addressId: number;

View File

@ -4,6 +4,8 @@
"price": "订单金额",
"paid": "已支付金额",
"refunded": "已退款金额",
"settled": "已结算金额",
"settlePlanned": "已计划结算金额",
"iState": "订单状态",
"title": "订单标题",
"desc": "订单描述",
@ -12,7 +14,6 @@
"creator": "创建者",
"entity": "关联对象",
"entityId": "关联对象Id",
"settled": "是否结算",
"opers": "相关帐户操作",
"system": "所属系统",
"address": "收货地址",
@ -28,8 +29,7 @@
"startRefunding": "开始退款",
"refundAll": "完全退款",
"refundNone": "退款失败",
"refundPartially": "部分退款",
"settle": "结算"
"refundPartially": "部分退款"
},
"v": {
"iState": {

View File

@ -0,0 +1,11 @@
import { ActionDef } from "oak-domain/lib/types/Action";
import { GenericAction } from "oak-domain/lib/actions/action";
export type IState = 'unsettled' | 'settled' | 'closed' | string;
export type IAction = 'settle' | 'close' | string;
export declare const IActionDef: ActionDef<IAction, IState>;
export type ParticularAction = IAction;
export declare const actions: string[];
export type Action = GenericAction | ParticularAction | string;
export declare const actionDefDict: {
iState: ActionDef<string, string>;
};

View File

@ -0,0 +1,11 @@
export const IActionDef = {
stm: {
settle: ['unsettled', 'settled'],
close: ['unsettled', 'closed'],
},
is: 'unsettled',
};
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "settle", "close"];
export const actionDefDict = {
iState: IActionDef
};

View File

@ -0,0 +1,19 @@
export * from "./_baseSchema";
import { SettlePlan } from "../EntityDict";
export type Schema = SettlePlan["Schema"];
export type Action = SettlePlan["Action"];
export type Projection = SettlePlan["Projection"];
export type Filter = SettlePlan["Filter"];
export type SortNode = SettlePlan["SortNode"];
export type Sorter = SettlePlan["Sorter"];
export type Selection = SettlePlan["Selection"];
export type Aggregation = SettlePlan["Aggregation"];
export type CreateOperationData = SettlePlan["CreateOperationData"];
export type CreateSingle = SettlePlan["CreateSingle"];
export type CreateMulti = SettlePlan["CreateMulti"];
export type Create = SettlePlan["Create"];
export type UpdateOperationData = SettlePlan["UpdateOperationData"];
export type Update = SettlePlan["Update"];
export type RemoveOperationData = SettlePlan["RemoveOperationData"];
export type Remove = SettlePlan["Remove"];
export type Operation = SettlePlan["Operation"];

View File

@ -0,0 +1 @@
export * from "./_baseSchema";

View File

@ -0,0 +1,3 @@
import { StorageDesc } from "oak-domain/lib/types/Storage";
import { OpSchema } from "./Schema";
export declare const desc: StorageDesc<OpSchema>;

View File

@ -0,0 +1,23 @@
import { actions } from "./Action";
export const desc = {
attributes: {
when: {
type: "datetime"
},
orderId: {
notNull: true,
type: "ref",
ref: "order"
},
price: {
notNull: true,
type: "money"
},
iState: {
type: "enum",
enumeration: ["unsettled", "settled", "closed"]
}
},
actionType: "crud",
actions
};

View File

@ -0,0 +1,3 @@
import { Action, OpSchema } from "./Schema";
import { StyleDef } from "oak-domain/lib/types/Style";
export declare const style: StyleDef<OpSchema, Action>;

View File

@ -0,0 +1,13 @@
export const style = {
icon: {
settle: '',
close: '',
},
color: {
iState: {
unsettled: '#52BE80',
settled: '#2E86C1',
closed: '#8C949C'
},
}
};

View File

@ -0,0 +1,49 @@
import { ForeignKey } from "oak-domain/lib/types/DataType";
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import { Datetime, Price } from "oak-domain/lib/types/DataType";
export type OpSchema = EntityShape & {
when?: Datetime | null;
orderId: ForeignKey<"order">;
price: Price;
iState?: IState | null;
} & {
[A in ExpressionKey]?: any;
};
export type OpAttr = keyof OpSchema;
export type OpFilter = {
id: Q_StringValue;
$$createAt$$: Q_DateValue;
$$seq$$: Q_NumberValue;
$$updateAt$$: Q_DateValue;
when: Q_DateValue;
orderId: Q_StringValue;
price: Q_NumberValue;
iState: Q_EnumValue<IState>;
} & ExprOp<OpAttr | string>;
export type OpProjection = {
"#id"?: NodeId;
[k: string]: any;
id?: number;
$$createAt$$?: number;
$$updateAt$$?: number;
$$seq$$?: number;
when?: number;
orderId?: number;
price?: number;
iState?: number;
} & Partial<ExprOp<OpAttr | string>>;
export type OpSortAttr = Partial<{
id: number;
$$createAt$$: number;
$$seq$$: number;
$$updateAt$$: number;
when: number;
orderId: number;
price: number;
iState: number;
[k: string]: any;
} | ExprOp<OpAttr | string>>;
export type OpAction = OakMakeAction<Action | string>;
export type OpUpdateAction = "update" | ParticularAction | string;

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,20 @@
{
"name": "结算计划",
"attr": {
"when": "结算时间",
"order": "订单",
"price": "结算金额",
"iState": "结算计划状态"
},
"action": {
"settle": "结算",
"close": "关闭"
},
"v": {
"iState": {
"unsettled": "未结算",
"settled": "已结算",
"closed": "已关闭"
}
}
}

View File

@ -1,9 +1,9 @@
import { ActionDef } from "oak-domain/lib/types/Action";
import { GenericAction } from "oak-domain/lib/actions/action";
export type IState = 'unsettled' | 'settled' | string;
export type IAction = 'settle' | string;
export type IState = 'unsettled' | 'settled' | 'closed' | string;
export type IAction = 'settle' | 'close' | string;
export declare const IActionDef: ActionDef<IAction, IState>;
export type ParticularAction = IAction | 'preSettle' | 'refund' | 'refundFailure';
export type ParticularAction = IAction | 'refund' | 'refundFailure';
export declare const actions: string[];
export type Action = GenericAction | ParticularAction | string;
export declare const actionDefDict: {

View File

@ -1,10 +1,11 @@
export const IActionDef = {
stm: {
settle: ['unsettled', 'settled'],
close: ['unsettled', 'closed'],
},
is: 'unsettled',
};
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "settle", "preSettle", "refund", "refundFailure"];
export const actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "settle", "close", "refund", "refundFailure"];
export const actionDefDict = {
iState: IActionDef
};

View File

@ -6,18 +6,18 @@ export const desc = {
type: "ref",
ref: "account"
},
planId: {
notNull: true,
type: "ref",
ref: "settlePlan"
},
price: {
notNull: true,
type: "money"
},
orderId: {
notNull: true,
type: "ref",
ref: "order"
},
iState: {
type: "enum",
enumeration: ["unsettled", "settled"]
enumeration: ["unsettled", "settled", "closed"]
}
},
actionType: "crud",

View File

@ -8,6 +8,7 @@ export const style = {
iState: {
unsettled: '#52BE80',
settled: '#2E86C1',
closed: '#8C949C'
},
}
};

View File

@ -5,8 +5,8 @@ import { Action, ParticularAction, IState } from "./Action";
import { Price } from "oak-domain/lib/types/DataType";
export type OpSchema = EntityShape & {
accountId: ForeignKey<"account">;
planId: ForeignKey<"settlePlan">;
price: Price;
orderId: ForeignKey<"order">;
iState?: IState | null;
} & {
[A in ExpressionKey]?: any;
@ -18,8 +18,8 @@ export type OpFilter = {
$$seq$$: Q_NumberValue;
$$updateAt$$: Q_DateValue;
accountId: Q_StringValue;
planId: Q_StringValue;
price: Q_NumberValue;
orderId: Q_StringValue;
iState: Q_EnumValue<IState>;
} & ExprOp<OpAttr | string>;
export type OpProjection = {
@ -30,8 +30,8 @@ export type OpProjection = {
$$updateAt$$?: number;
$$seq$$?: number;
accountId?: number;
planId?: number;
price?: number;
orderId?: number;
iState?: number;
} & Partial<ExprOp<OpAttr | string>>;
export type OpSortAttr = Partial<{
@ -40,8 +40,8 @@ export type OpSortAttr = Partial<{
$$seq$$: number;
$$updateAt$$: number;
accountId: number;
planId: number;
price: number;
orderId: number;
iState: number;
[k: string]: any;
} | ExprOp<OpAttr | string>>;

View File

@ -2,21 +2,22 @@
"name": "结算明细",
"attr": {
"account": "帐号",
"plan": "结算计划",
"price": "变化金额",
"order": "订单",
"iState": "结算状态",
"opers": "相关帐户操作"
},
"action": {
"preSettle": "预结算",
"settle": "结算",
"close": "关闭",
"refund": "退款",
"refundFailure": "退款失败"
},
"v": {
"iState": {
"unsettled": "未结算",
"settled": "已结算"
"settled": "已结算",
"closed": "已关闭"
}
}
}

View File

@ -70,6 +70,7 @@ import { desc as orderDesc } from "./Order/Storage";
import { desc as payDesc } from "./Pay/Storage";
import { desc as refundDesc } from "./Refund/Storage";
import { desc as settlementDesc } from "./Settlement/Storage";
import { desc as settlePlanDesc } from "./SettlePlan/Storage";
import { desc as shipDesc } from "./Ship/Storage";
import { desc as shipCompanyDesc } from "./ShipCompany/Storage";
import { desc as shipOrderDesc } from "./ShipOrder/Storage";
@ -158,6 +159,7 @@ export const storageSchema = {
pay: payDesc,
refund: refundDesc,
settlement: settlementDesc,
settlePlan: settlePlanDesc,
ship: shipDesc,
shipCompany: shipCompanyDesc,
shipOrder: shipOrderDesc,

View File

@ -36,6 +36,7 @@ import { style as order } from "./Order/Style";
import { style as pay } from "./Pay/Style";
import { style as refund } from "./Refund/Style";
import { style as settlement } from "./Settlement/Style";
import { style as settlePlan } from "./SettlePlan/Style";
import { style as ship } from "./Ship/Style";
import { style as sysAccountOper } from "./SysAccountOper/Style";
import { style as user } from "./User/Style";
@ -84,6 +85,7 @@ export const styleDict = {
pay,
refund,
settlement,
settlePlan,
ship,
sysAccountOper,
user,

View File

@ -19,7 +19,8 @@ export const desc = {
type: "ref",
ref: "platform"
},
folder: {
folder // 提现的loss在用户提现时计算
: {
type: "varchar",
params: {
length: 16

View File

@ -1,5 +1,5 @@
import { Timer } from 'oak-domain/lib/types/Timer';
import { EntityDict } from '../oak-app-domain/index';
import { BRC } from '../types/RuntimeCxt';
declare const _default: Array<Timer<EntityDict, keyof EntityDict, BRC>>;
declare const _default: Timer<EntityDict, keyof EntityDict, BRC>[];
export default _default;

View File

@ -12,6 +12,7 @@ import withdrawTransferTriggers from './withdrawTransfer';
import depositTriggers from './deposit';
import shipTriggers from './ship';
import orderTriggers from './order';
import settlePlanTriggers from './settlePlan';
const triggers = [
...shipTriggers,
...orderTriggers,
@ -27,6 +28,7 @@ const triggers = [
...sysAccountOperTriggers,
...systemTriggers,
...withdrawAccountTriggers,
...settlePlanTriggers,
];
export default triggers;
export const optionalTriggers = [

View File

@ -1,8 +1,6 @@
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { DATA_SUBSCRIBER_KEYS } from '../config/constants';
import assert from 'assert';
const triggers = [
{
/**{
name: '订单全部支付后对关联的settlement执行preSettle,更新settlement关联的type为preSettle的accountOper',
entity: 'order',
action: 'payAll',
@ -20,7 +18,7 @@ const triggers = [
}
}, {
forUpdate: true,
});
})
const settlements = await context.select('settlement', {
data: {
id: 1,
@ -37,38 +35,39 @@ const triggers = [
forUpdate: true,
});
assert(settlements && settlements.length > 0);
let amount = 0;
for (const settlement of settlements) {
const { id: settlementId, price, accountId } = settlement;
amount += price;
amount += price!;
//创建对应的accountOper
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'preSettle',
data: {
accountOper$entity: [{
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
totalPlus: price,
availPlus: 0,
accountId: accountId,
type: 'preSettle',
}
}]
totalPlus: price,
availPlus: 0,
accountId: accountId,
type: 'preSettle',
}
}]
},
filter: {
id: settlementId,
}
}, {});
}, {})
}
assert(order.price === amount);
}
return;
}
},
{
},*/
/**{
name: '订单执行settle前对关联的settlement执行settle,更新settlement关联的type为settle的accountOper',
entity: 'order',
action: 'settle',
@ -77,6 +76,7 @@ const triggers = [
priority: 99,
fn: async ({ operation }, context, option) => {
const { filter } = operation;
let cnt = 0;
const orders = await context.select('order', {
data: {
@ -100,17 +100,20 @@ const triggers = [
forUpdate: true,
dontCollect: true,
});
for (const order of orders) {
const { settlement$order: settlements } = order || {};
assert(settlements && settlements.length > 0);
//将关联的settlement执行settle
for (const settlement of settlements) {
const { id: settlementId, price, accountId } = settlement;
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'settle',
data: {
accountOper$entity: [{
await context.operate('settlement',
{
id: await generateNewIdAsync(),
action: 'settle',
data: {
accountOper$entity: [{
id: await generateNewIdAsync(),
action: 'create',
data: {
@ -121,17 +124,19 @@ const triggers = [
type: 'settle',
}
}]
},
filter: {
id: settlementId,
}
},
filter: {
id: settlementId,
}
}, {});
{}
);
cnt += settlements.length;
}
}
return cnt;
},
},
},*/
{
name: '订单状态改变时,向订阅者发送消息',
entity: 'order',

View File

@ -2,7 +2,7 @@ import { generateNewId, generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { getAccountEntity, getPayClazz } from '../utils/payClazz';
import assert from 'assert';
import { updateWithdrawState } from './withdraw';
import { RefundExceedMax, PayUnRefundable } from '../types/Exception';
import { RefundExceedMax, PayUnRefundable, settlePlanExceed, refundOpersNotEnough, refundOpersExceed } from '../types/Exception';
/**
* 开始退款的逻辑
* @param context
@ -25,17 +25,31 @@ async function startRefunding(context, data) {
},
order: {
id: 1,
paid: 1,
refunded: 1,
settled: 1,
settlement$order: {
$entity: 'settlement',
settlePlanned: 1,
settlePlan$order: {
$entity: 'settlePlan',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
settlement$plan: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled'
}
}
},
filter: {
iState: 'unsettled',
iState: 'unsettled'
}
}
}
@ -74,47 +88,91 @@ async function startRefunding(context, data) {
}
else {
assert(order);
//订单退款的钱要有明确的来源account
const { accountOper$entity: opers } = data;
assert(opers && opers instanceof Array, '订单退款一定要有相应的account来源');
if (order.settled) {
// 已经分账
// 对settlePlan检查
// 未结算的settlePlan的金额总和 = order.paid - order.refunded - order.settled - refunded.price
const { settlePlan$order: settlePlans, paid, refunded, settled } = order;
let settlePlanAmount = 0;
settlePlans.forEach((settlePlan) => settlePlanAmount += settlePlan.price);
const allowPlanPrice = paid - refunded - settled - data.price;
if (settlePlanAmount > allowPlanPrice) {
throw new settlePlanExceed(settlePlanAmount - allowPlanPrice);
}
const settlements = settlePlans?.map((plan) => plan.settlement$plan).flat();
const { accountOper$entity: opers, } = data;
// 对opers检查
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
assert(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, refundablePlus } = data;
const { type, totalPlus, availPlus, accountId } = data;
assert(type === 'refund');
assert(totalPlus === availPlus);
const settlement = settlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
amount += -totalPlus;
});
assert(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
assert(!(opers && opers.length > 0));
}
else {
//未完成分账的订单退款关联的AccountOper的totalPlus总和等于refund的price
//同时更新对应的settlement的price
let amount = 0;
const { settlement$order: settlements } = order;
for (const oper of opers) {
const { action, data } = oper;
assert(action === 'create');
const { type, totalPlus, accountId } = data;
assert(type === 'refund');
amount += -totalPlus;
const settlement = settlements?.find((ele) => ele.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
const settlementNewPrice = settlement.price + totalPlus; //可能为负值
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'refund',
data: {
price: settlementNewPrice,
},
filter: {
id: settlement.id,
}
}, {});
//部分结算
/**
* 部分结算的订单:
* 1. opers的金额总和 <= 订单已结算金额
* 2. opers的金额总和 + unsettled的金额 >= 退款金额
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
opers.forEach(({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
assert(type === 'refund');
assert(totalPlus === availPlus);
const settlement = settlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus;
});
}
assert(amount === data.price);
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new refundOpersExceed(opersPrice - settled);
}
if (opersPrice < data.price - orderUnsettledPrice) {
throw new refundOpersNotEnough(opersPrice - (data.price - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
}
data.pay = {
@ -309,18 +367,6 @@ async function failRefunding(context, refundId) {
order: {
id: 1,
settled: 1,
settlement$order: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled',
}
}
}
},
filter: {
@ -367,51 +413,38 @@ async function failRefunding(context, refundId) {
entityId: refundId,
},
}, { dontCollect: true });
if (order.settled) {
// 已经分账
let amount = 0;
for (const oper of accountOpers) {
const { totalPlus, availPlus, accountId } = oper;
assert(totalPlus < 0 && totalPlus === availPlus);
await context.operate('accountOper', {
let opersPrice = 0;
for (const oper of accountOpers) {
const { totalPlus, availPlus, accountId } = oper;
assert(totalPlus < 0 && totalPlus === availPlus);
await context.operate('accountOper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
totalPlus: -totalPlus,
availPlus: -availPlus,
type: 'refundFailure',
entity: 'refund',
entityId: refundId,
accountId,
}
}, {});
amount += -availPlus;
}
assert(amount === refund.price);
totalPlus: -totalPlus,
availPlus: -availPlus,
type: 'refundFailure',
entity: 'refund',
entityId: refundId,
accountId,
}
}, {});
opersPrice += -availPlus;
}
else {
//未完成分账的订单,需要修改settlement
let amount = 0;
const { settlement$order: settlements } = order;
for (const oper of accountOpers) {
const { totalPlus, availPlus, accountId } = oper;
amount += -totalPlus;
const settlement = settlements?.find((ele) => ele.accountId === accountId);
assert(!!settlement);
const settlementNewPrice = settlement.price - totalPlus;
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'refundFailure',
data: {
price: settlementNewPrice,
},
filter: {
id: settlement.id,
}
}, {});
}
assert(amount === refund.price);
//如果refund有accountOper回退order.settled变化
if (opersPrice > 0) {
const newSettled = order.settled + opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled
},
filter: {
id: order.id,
}
}, {});
}
return 1 + accountOpers.length;
}

5
es/triggers/settlePlan.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Trigger } from 'oak-domain/lib/types/Trigger';
import { EntityDict } from '../oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
declare const triggers: Trigger<EntityDict, 'settlePlan', BRC>[];
export default triggers;

195
es/triggers/settlePlan.js Normal file
View File

@ -0,0 +1,195 @@
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import assert from 'assert';
const triggers = [
{
name: '当settlePlan创建时更新关联order的settlePlanned',
entity: 'settlePlan',
action: 'create',
when: 'before',
asRoot: true,
priority: 99,
fn: async ({ operation }, context, option) => {
const { data } = operation;
let count = 0;
const updateOrderFn = async (settlePlan) => {
const [order] = await context.select('order', {
data: {
id: 1,
paid: 1,
refunded: 1,
settlePlanned: 1,
},
filter: {
id: settlePlan.orderId,
}
}, { forUpdate: true });
const { id, paid, refunded, settlePlanned } = order;
const newSettlePlanned = settlePlan.price + settlePlanned;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settlePlanned: newSettlePlanned,
},
filter: {
id,
}
}, option);
};
if (data instanceof Array) {
for (const d of data) {
await updateOrderFn(d);
count++;
}
}
else {
await updateOrderFn(data);
count++;
}
return count;
},
},
{
name: '当settlePlan执行settle时将关联的settlement执行settle并更新order的settled',
entity: 'settlePlan',
action: 'settle',
when: 'before',
asRoot: true,
priority: 99,
fn: async ({ operation }, context, option) => {
const { filter } = operation;
const settlePlans = await context.select('settlePlan', {
data: {
id: 1,
price: 1,
settlement$plan: {
$entity: 'settlement',
data: {
id: 1,
price: 1,
accountId: 1,
iState: 1,
},
filter: {
iState: 'unsettled',
}
},
orderId: 1,
order: {
id: 1,
paid: 1,
refunded: 1,
settled: 1,
}
},
filter,
}, { forUpdate: true });
for (const settlePlan of settlePlans) {
const { id, price: planPrice, settlement$plan: settlements, orderId, order } = settlePlan;
assert(settlements && settlements.length > 0);
//关联的settlement均执行settle并生成accountOper
for (const settlement of settlements) {
const { id: settlementId, price: settlementPrice, accountId, } = settlement;
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'settle',
data: {
accountOper$entity: [{
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
totalPlus: settlementPrice,
availPlus: settlementPrice,
accountId: accountId,
type: 'settle',
}
}]
},
filter: {
id: settlementId,
}
}, option);
}
//更新order的settled
const newSettledPrice = order?.settled + planPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettledPrice,
},
filter: {
id: orderId,
}
}, option);
}
return settlePlans.length;
},
},
{
name: '当settlePlan执行close时将关联的settlement执行close并更新order的settlePlanned',
entity: 'settlePlan',
action: 'close',
when: 'before',
asRoot: true,
priority: 99,
fn: async ({ operation }, context, option) => {
const { filter } = operation;
const settlePlans = await context.select('settlePlan', {
data: {
id: 1,
price: 1,
settlement$plan: {
$entity: 'settlement',
data: {
id: 1,
price: 1,
accountId: 1,
iState: 1,
},
filter: {
iState: 'unsettled',
}
},
orderId: 1,
order: {
id: 1,
settlePlanned: 1,
}
},
filter,
}, { forUpdate: true });
for (const settlePlan of settlePlans) {
const { id, price: planPrice, settlement$plan: settlements, orderId, order } = settlePlan || {};
assert(settlements && settlements.length > 0);
//关联的settlement均执行close
for (const settlement of settlements) {
const { id: settlementId, } = settlement;
await context.operate('settlement', {
id: await generateNewIdAsync(),
action: 'close',
data: {},
filter: {
id: settlementId,
}
}, option);
}
//更新order的settlePlanned
const newSettlePlanned = order?.settlePlanned - planPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settlePlanned: newSettlePlanned,
},
filter: {
id: orderId,
}
}, option);
}
return settlePlans.length;
},
},
];
export default triggers;

View File

@ -6,6 +6,6 @@ import { BRC } from '../types/RuntimeCxt';
* @param context
* @param refunds
*/
export declare function updateWithdrawState(context: BRC, id: string): Promise<1 | 0>;
export declare function updateWithdrawState(context: BRC, id: string): Promise<0 | 1>;
declare const triggers: Trigger<EntityDict, 'withdraw', BRC>[];
export default triggers;

View File

@ -1,7 +1,73 @@
import { OakException, OpRecord } from 'oak-domain/lib/types';
import { EntityDict } from '../oak-app-domain/index';
/**
* 退opers的price总和小于本次退款金额-
*/
export declare class refundOpersNotEnough<ED extends EntityDict> extends OakException<ED> {
price: number;
constructor(price: number, message?: string, _module?: string, params?: Record<string, any>);
getSerialData(): {
price: number;
name: string;
message: string;
_module: string | undefined;
params: Record<string, any> | undefined;
opRecords: OpRecord<ED>[];
tag1: string | undefined;
tag2: boolean | undefined;
tag3: any;
};
}
/**
* 退opers的price总和大于订单已结算金额
*/
export declare class refundOpersExceed<ED extends EntityDict> extends OakException<ED> {
price: number;
constructor(price: number, message?: string, _module?: string, params?: Record<string, any>);
getSerialData(): {
price: number;
name: string;
message: string;
_module: string | undefined;
params: Record<string, any> | undefined;
opRecords: OpRecord<ED>[];
tag1: string | undefined;
tag2: boolean | undefined;
tag3: any;
};
}
/**
* 退settlePlan金额超出order可计划结算金额
*/
export declare class settlePlanExceed<ED extends EntityDict> extends OakException<ED> {
price: number;
constructor(price: number, message?: string, _module?: string, params?: Record<string, any>);
getSerialData(): {
price: number;
name: string;
message: string;
_module: string | undefined;
params: Record<string, any> | undefined;
opRecords: OpRecord<ED>[];
tag1: string | undefined;
tag2: boolean | undefined;
tag3: any;
};
}
export declare class UploadShipException<ED extends EntityDict> extends OakException<ED> {
constructor(message?: string);
reason: any;
constructor(reason: any, message?: string, _module?: string, params?: Record<string, any>);
getSerialData(): {
reason: any;
name: string;
message: string;
_module: string | undefined;
params: Record<string, any> | undefined;
opRecords: OpRecord<ED>[];
tag1: string | undefined;
tag2: boolean | undefined;
tag3: any;
};
}
export declare class ExternalPayUtilException<ED extends EntityDict> extends OakException<ED> {
reason: any;

View File

@ -1,8 +1,68 @@
import { OakException } from 'oak-domain/lib/types';
import makeDepedentException from './DependentExceptions';
/**
* 退款时级联创建的opers的price总和小于本次退款金额-订单未结算金额
*/
export class refundOpersNotEnough extends OakException {
price; //opers需增加的金额 显示为负数
constructor(price, message, _module, params) {
super(message || 'error::refund.create.opersNotEnough', _module || 'oak-pay-business', params);
this.price = price;
}
getSerialData() {
const data = super.getSerialData();
return {
...data,
price: this.price,
};
}
}
/**
* 退款时级联创建的opers的price总和大于订单已结算金额
*/
export class refundOpersExceed extends OakException {
price; //opers需减少的金额
constructor(price, message, _module, params) {
super(message || 'error::settlePlan.create.opersExceed', _module || 'oak-pay-business', params);
this.price = price;
}
getSerialData() {
const data = super.getSerialData();
return {
...data,
price: this.price,
};
}
}
/**
* 退款时settlePlan金额超出order可计划结算金额
*/
export class settlePlanExceed extends OakException {
price; //超出的可结算金额 显示为负数
constructor(price, message, _module, params) {
super(message || 'error::settlePlan.exceed', _module || 'oak-pay-business', params);
this.price = price;
}
getSerialData() {
const data = super.getSerialData();
return {
...data,
price: this.price,
};
}
}
export class UploadShipException extends OakException {
constructor(message) {
super(message || '调用发货接口失败');
reason;
constructor(reason, message, _module, params) {
super(message || 'error::ship.uploadShipException', _module || 'oak-pay-business', params);
this.reason = reason;
}
getSerialData() {
const data = super.getSerialData();
return {
...data,
reason: this.reason,
};
}
}
export class ExternalPayUtilException extends OakException {

1
es/utils/pay.d.ts vendored
View File

@ -1,3 +1,4 @@
/// <reference types="node" />
import { EntityDict } from '../oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import BackendRuntimeContext from '../context/BackendRuntimeContext';

View File

@ -190,10 +190,10 @@ export default class AliPay extends AliPayDebug {
const qrCodeWidth = this.apProduct.config?.qrCodeWidth || 100;
const params = {
bizContent: {
subject: pay.order?.desc || (pay.orderId ? '订单支付' : '帐户充值'), // 订单标题
out_trade_no: out_trade_no, // 商户订单号64个字符以内、可包含字母、数字、下划线需保证在商户端不重复
total_amount: totalAmount, // 订单总金额,单位为元
product_code: 'FAST_INSTANT_TRADE_PAY', // 销售产品码,商家和支付宝签约的产品码
subject: pay.order?.desc || (pay.orderId ? '订单支付' : '帐户充值'),
out_trade_no: out_trade_no,
total_amount: totalAmount,
product_code: 'FAST_INSTANT_TRADE_PAY',
notify_url: payNotifyUrl,
qr_pay_mode: qrPayMode,
qrcode_width: qrCodeWidth,

View File

@ -189,7 +189,7 @@ export async function uploadShippingInfo(shipId, context) {
const result = await wechatInstance.uploadShippingInfo(shipInfo);
if (result?.errcode !== 0) {
console.error(JSON.stringify(result));
throw new UploadShipException(result?.message);
throw new UploadShipException(result);
}
}
}
@ -242,7 +242,7 @@ export async function uploadShippingInfo(shipId, context) {
const result = await wechatInstance.uploadShippingInfo(shipInfo);
if (result?.errcode !== 0) {
console.error(JSON.stringify(result));
throw new UploadShipException(result?.message);
throw new UploadShipException(result);
}
}
}

View File

@ -157,9 +157,9 @@ export default class WechatMpShipDebug {
name: from.name,
mobile: from.phone,
// country: from!.area!.parent!.parent!.name!, //国家
province: from.area.parent.parent.name, //省份
city: from.area.parent.name, //市
area: from.area.name, // 区
province: from.area.parent.parent.name,
city: from.area.parent.name,
area: from.area.name,
address: from.detail, //详细地址
},
receiver: {

View File

@ -2,10 +2,12 @@ import orderWatchers from './order';
import payWatchers from './pay';
import refundWatchers from './refund';
import shipWatchers from './ship';
import settlePlanWatchers from './settlePlan';
const watchers = [
...orderWatchers,
...payWatchers,
...refundWatchers,
...shipWatchers,
...settlePlanWatchers,
];
export default watchers;

View File

@ -32,7 +32,6 @@ const watchers = [
return results.reduce((prev, cur) => mergeOperationResult(prev, cur));
}
},
/* triggerbug
{
name: '当pay达到禁止退款期限时关闭退款允许',
entity: 'pay',
@ -49,7 +48,7 @@ const watchers = [
actionData: {
refundable: false,
}
}, */
},
{
name: '当pay达到过期期限时关闭pay',
entity: 'pay',

5
es/watchers/settlePlan.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Watcher } from 'oak-domain/lib/types/Watcher';
import { EntityDict } from '../oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
declare const watchers: Watcher<EntityDict, 'settlePlan', BRC>[];
export default watchers;

18
es/watchers/settlePlan.js Normal file
View File

@ -0,0 +1,18 @@
const watchers = [
{
name: '当settlePlan达到结算时间时结算settlePlan',
entity: 'settlePlan',
filter: () => {
const now = Date.now();
return {
iState: 'unsettled',
when: {
$lte: now,
},
};
},
action: 'settle',
actionData: {},
}
];
export default watchers;

View File

@ -1,7 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMpShipState = getMpShipState;
exports.getExpressPrintInfo = getExpressPrintInfo;
exports.getExpressPrintInfo = exports.getMpShipState = void 0;
const types_1 = require("oak-domain/lib/types");
const ship_1 = require("../utils/ship");
const shipClazz_1 = require("../utils/shipClazz");
@ -19,6 +18,7 @@ async function getMpShipState(params, context) {
return shipState;
}
}
exports.getMpShipState = getMpShipState;
/**
* 获取打印面单
*/
@ -43,3 +43,4 @@ async function getExpressPrintInfo(params, context) {
const clazz = await (0, shipClazz_1.getShipClazz)(entity, entityId, context);
return await clazz.getPrintInfo(shipId, context);
}
exports.getExpressPrintInfo = getExpressPrintInfo;

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWithdrawCreateData = getWithdrawCreateData;
exports.getWithdrawCreateData = void 0;
const tslib_1 = require("tslib");
const types_1 = require("oak-domain/lib/types");
const uuid_1 = require("oak-domain/lib/utils/uuid");
@ -211,3 +211,4 @@ async function getWithdrawCreateData(params, context) {
data.loss = totalLoss;
return data;
}
exports.getWithdrawCreateData = getWithdrawCreateData;

View File

@ -1,12 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.accountEntities = void 0;
exports.registerAccountEntity = registerAccountEntity;
exports.accountEntities = exports.registerAccountEntity = void 0;
const uuid_1 = require("oak-domain/lib/utils/uuid");
// 当注入一个新的account entity时将withdrawChannel的删除与之相关联
function registerAccountEntity(entity) {
exports.accountEntities.push(entity);
}
exports.registerAccountEntity = registerAccountEntity;
exports.accountEntities = ['offlineAccount'];
const triggers = [
...exports.accountEntities.filter(ele => !!ele).map((entity) => [

View File

@ -102,22 +102,22 @@ const checkers = [
}
break;
}
case 'preSettle': {
//预分账时账户total增加avail不变refundable不变
if (totalPlus < 0 || availPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
throw new types_1.OakInputIllegalException('accountOper', ['totalPlus', 'availPlus', 'refundablePlus'], 'accountOper为preSettle时其totalPlus必须为非负数、availPlus必须为0refundable必须为空或者0');
}
break;
}
// case 'preSettle': {
// //预分账时账户total增加avail不变refundable不变
// if (totalPlus < 0 || availPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
// throw new OakInputIllegalException('accountOper', ['totalPlus', 'availPlus', 'refundablePlus'], 'accountOper为preSettle时其totalPlus必须为非负数、availPlus必须为0refundable必须为空或者0');
// }
// break;
// }
case 'settle': {
//分账时账户total不变refundable不变
if (totalPlus !== 0 || (refundablePlus && refundablePlus !== 0)) {
throw new types_1.OakInputIllegalException('accountOper', ['totalPlus', 'refundablePlus'], 'accountOper为settle时其totalPlus必须为0refundable必须为空或者0');
//分账时账户total增加avail增加refundable不变
if (totalPlus < 0 || availPlus < 0 || (refundablePlus && refundablePlus !== 0)) {
throw new types_1.OakInputIllegalException('accountOper', ['totalPlus', 'refundablePlus'], 'accountOper为settle时其totalPlus、availPlus必须为非负数refundable必须为空或者0');
}
break;
}
default: {
(0, assert_1.default)(false);
// assert(false);
break;
}
}

View File

@ -12,6 +12,7 @@ const withdrawAccount_1 = tslib_1.__importDefault(require("./withdrawAccount"));
const refund_1 = tslib_1.__importDefault(require("./refund"));
const withdrawTransfer_1 = tslib_1.__importDefault(require("./withdrawTransfer"));
const ship_1 = tslib_1.__importDefault(require("./ship"));
const settlePlan_1 = tslib_1.__importDefault(require("./settlePlan"));
const checkers = [
...refund_1.default,
...withdrawAccount_1.default,
@ -24,5 +25,6 @@ const checkers = [
...wpProduct_1.default,
...withdrawTransfer_1.default,
...ship_1.default,
...settlePlan_1.default,
];
exports.default = checkers;

View File

@ -16,7 +16,8 @@ const checkers = [
}
data.paid = 0;
data.refunded = 0;
data.settled = false;
data.settled = 0;
data.settlePlanned = 0;
if (!data.systemId) {
const contextSystemId = context.getApplication()?.systemId;
(0, assert_1.default)(contextSystemId);
@ -108,51 +109,59 @@ const checkers = [
});
}
},
/**todo settlePlan settle
{
// 订单结算检查存在关联的未结算的settlement其price之和等于订单的已支付与已退款的差值
type: 'logicalData',
entity: 'order',
action: 'settle',
checker: (operation, context) => {
const { data, filter } = operation;
(0, assert_1.default)(typeof filter.id === 'string');
const { data, filter } = operation as EntityDict['order']['Update'];
assert(typeof filter!.id === 'string');
data.settled = true;
return (0, executor_1.pipeline)(() => context.select('order', {
data: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
iState: 1,
settlement$order: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled'
return pipeline(
() => context.select('order', {
data: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
iState: 1,
settlement$order: {
$entity: 'settlement',
data: {
id: 1,
accountId: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled'
}
}
}
},
filter: {
id: filter.id,
},
}, {}), (orders) => {
const [order] = orders;
const { price, paid, refunded, iState, settlement$order: settlements } = order;
(0, assert_1.default)(['paid'].includes(iState) && refunded === 0 && paid === price);
(0, assert_1.default)(settlements && settlements.length > 0, '该结算订单需添加结算明细');
let amount = 0;
settlements.forEach((settlement) => {
amount += settlement.price;
(0, assert_1.default)(settlement.iState === 'unsettled', '订单结算前settlement必须处于未分账状态');
});
(0, assert_1.default)(amount === paid);
});
},
filter: {
id: filter!.id!,
},
}, {}),
(orders: EntityDict['order']['Schema'][]) => {
const [order] = orders;
const { price, paid, refunded, iState, settlement$order: settlements } = order;
assert(['paid'].includes(iState!) && refunded === 0 && paid === price);
assert(settlements && settlements.length > 0, '该结算订单需添加结算明细');
let amount = 0;
settlements.forEach((settlement) => {
amount += settlement.price;
assert(settlement.iState === 'unsettled', '订单结算前settlement必须处于未分账状态');
})
assert(amount === paid);
}
);
}
},
*/
];
exports.default = checkers;

5
lib/checkers/settlePlan.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Checker } from 'oak-domain/lib/types/Auth';
import { EntityDict } from '../oak-app-domain';
import { RuntimeCxt } from '../types/RuntimeCxt';
declare const checkers: Checker<EntityDict, 'settlePlan', RuntimeCxt>[];
export default checkers;

107
lib/checkers/settlePlan.js Normal file
View File

@ -0,0 +1,107 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const executor_1 = require("oak-domain/lib/utils/executor");
const assert_1 = require("oak-domain/lib/utils/assert");
const checkers = [
{
/**
* 1settlement.price总和等于settlePlan.price
* 2settlePlan.price <= order.paid - order.refunded - order.settlePlanned
*/
entity: 'settlePlan',
action: 'create',
type: 'logicalData',
priority: 1,
checker: (operation, context) => {
const { data } = operation;
const checkfn = (settlePlan) => {
return (0, executor_1.pipeline)(() => context.select('order', {
data: {
id: 1,
paid: 1,
refunded: 1,
settlePlanned: 1,
},
filter: {
id: settlePlan.orderId,
},
}, { dontCollect: true }), (orders) => {
const [order] = orders;
const { paid, refunded, settlePlanned } = order;
const orderPrice = paid - refunded - settlePlanned;
(0, assert_1.assert)(settlePlan.price <= orderPrice, '结算计划金额不大于订单可结算金额');
return context.select('settlement', {
data: {
id: 1,
price: 1,
iState: 1,
planId: 1,
},
filter: {
planId: settlePlan.id,
iState: 'unsettled',
},
}, { dontCollect: true });
}, (settlements) => {
let amount = 0;
settlements.forEach((settlement) => amount += settlement.price);
if (settlePlan.settlement$plan && settlePlan.settlement$plan.length > 0) {
for (const settlementOper of settlePlan.settlement$plan) {
const settlementData = settlementOper.data;
if (settlementData instanceof Array) {
settlementData.forEach((s) => amount += s.price ?? 0);
}
else {
amount += settlementData?.price ?? 0;
}
}
}
(0, assert_1.assert)(amount === settlePlan.price, '结算计划金额需等于关联的结算明细金额总和');
});
};
if (data instanceof Array) {
for (const d of data) {
checkfn(d);
}
}
else {
checkfn(data);
}
}
},
{
entity: 'settlePlan',
action: 'close',
type: 'logicalData',
checker: (operation, context) => {
const { filter } = operation;
return (0, executor_1.pipeline)(() => context.select('settlePlan', {
data: {
id: 1,
price: 1,
iState: 1,
settlement$plan: {
$entity: 'settlement',
data: {
id: 1,
price: 1,
iState: 1,
},
filter: {
iState: 'unsettled',
}
}
},
filter,
}, { dontCollect: true }), (settlePlans) => {
for (const settlePlan of settlePlans) {
const { price: planPrice, settlement$plan: settlements } = settlePlan;
let amount = 0;
settlements.forEach((settlement) => amount += settlement.price);
(0, assert_1.assert)(amount === planPrice, '结算计划金额需等于未结算的结算明细金额总和');
}
});
}
}
];
exports.default = checkers;

View File

@ -926,7 +926,9 @@ const i18ns = [
"create": {
"hasAnotherRefunding": "您有一笔退款正在进行中",
"exceedMax": "退款额度大于可用值",
"payUnrefundable": "支付已经不可退款"
"payUnrefundable": "支付已经不可退款",
"opersNotEnough": "指定退款帐户的操作金额总和不足",
"opersExceed": "指定退款帐户的操作金额总和超过订单已结算金额"
}
},
"pay": {
@ -934,7 +936,11 @@ const i18ns = [
"priceOverflow": "支付金额总和大于订单金额"
},
"ship": {
"noClazz": "当前物流未关联外部接口"
"noClazz": "当前物流未关联外部接口",
"uploadShipException": "小程序上传发货信息异常"
},
"settlePlan": {
"exceed": "结算计划总金额超过订单可结算金额上限"
}
}
},

View File

@ -2,7 +2,7 @@ import { String, Price } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as Account } from './Account';
type Type = 'deposit' | 'withdraw' | 'consume' | 'consumeBack' | 'loan' | 'repay' | 'withdrawBack' | 'earn' | 'cutoffRefundable' | 'tax' | 'taxRefund' | 'refund' | 'refundFailure' | 'preSettle' | 'settle';
type Type = 'deposit' | 'withdraw' | 'consume' | 'consumeBack' | 'loan' | 'repay' | 'withdrawBack' | 'earn' | 'cutoffRefundable' | 'tax' | 'taxRefund' | 'refund' | 'refundFailure' | 'settle';
export interface Schema extends EntityShape {
account: Account;
type: Type;

View File

@ -33,7 +33,6 @@ exports.entityDesc = {
cutoffRefundable: '削减可自由退额度',
refund: '退款',
refundFailure: '退款失败',
preSettle: '预分账',
settle: '分账',
},
},
@ -55,7 +54,6 @@ exports.entityDesc = {
cutoffRefundable: '#2E4053',
refund: '#CC3333',
refundFailure: '#009933',
preSettle: '#8A631D',
settle: '#157d5a',
}
}

View File

@ -33,7 +33,7 @@ exports.entityDesc = {
keyType: '指定应用私钥类型',
gateway: "支付宝网关地址",
wsServiceUrl: 'websocket服务地址',
timeout: "网关超时时间", //(单位毫秒),默认值是 5000
timeout: "网关超时时间",
needEncrypt: "是否需要AES加解密",
encryptKey: 'AES密钥', //调用AES加解密相关接口时需要
},

View File

@ -9,13 +9,14 @@ export interface Schema extends EntityShape {
price: Price;
paid: Price;
refunded: Price;
settled: Price;
settlePlanned: Price;
title: String<32>;
desc: Text;
timeoutAt?: Datetime;
creator: User;
entity: String<32>;
entityId: String<64>;
settled: Boolean;
allowPartialPay?: Boolean;
system: System;
address?: Address;
@ -25,7 +26,7 @@ export interface Schema extends EntityShape {
export type IAction = 'startPaying' | 'payAll' | 'payPartially' | 'payNone' | 'timeout' | 'cancel' | 'startRefunding' | 'refundAll' | 'refundPartially' | 'refundNone';
export type IState = 'unpaid' | 'timeout' | 'cancelled' | 'paying' | 'partiallyPaid' | 'paid' | 'refunding' | 'partiallyRefunded' | 'refunded';
export declare const IActionDef: ActionDef<IAction, IState>;
export type Action = IAction | 'settle';
export type Action = IAction;
export declare const entityDesc: EntityDesc<Schema, Action, '', {
iState: IState;
}>;

View File

@ -54,6 +54,8 @@ exports.entityDesc = {
price: '订单金额',
paid: '已支付金额',
refunded: '已退款金额',
settled: '已结算金额',
settlePlanned: '已计划结算金额',
iState: '订单状态',
title: '订单标题',
desc: "订单描述",
@ -62,7 +64,6 @@ exports.entityDesc = {
creator: '创建者',
entity: '关联对象',
entityId: '关联对象Id',
settled: '是否结算',
opers: '相关帐户操作',
system: '所属系统',
address: '收货地址',
@ -79,7 +80,6 @@ exports.entityDesc = {
refundAll: '完全退款',
refundNone: '退款失败',
refundPartially: '部分退款',
settle: '结算',
},
v: {
iState: {
@ -108,7 +108,6 @@ exports.entityDesc = {
refundAll: '',
refundNone: '',
refundPartially: '',
settle: '',
},
color: {
iState: {

Some files were not shown because too many files have changed in this diff Show More