diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
index 7daf7770..7ff6a46c 100644
--- a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
@@ -80,10 +80,13 @@ namespace YLErp.Modules.SwapModule
}
///
/// 互换交易保存
+ /// ignoreMoneyCheck=true:资金不足特批放行(additionalProcessing=LackOfMoney,controller 按 系统参数"允许交易特批"
+ /// 判定后传入,与确认/审批环节 ignoreMoneyCheck 同口径);确认成交时资金校验独立再跑,特批链路在那边仍有兜底。
///
///
+ ///
///
- public trade SaveTrade(trade req)
+ public trade SaveTrade(trade req, bool ignoreMoneyCheck = false)
{
var um = checkUnderlying(req);
trade dbTrade = new trade();
@@ -91,11 +94,12 @@ namespace YLErp.Modules.SwapModule
//资金校验须在其后取值,否则互换表单不填平铺 InitialMargin 时校验会按 0 放行)
var tradeNumberGenerated = PrepareTrade(req, TradeSourceEnum.系统交易, um);
// R4 簿记资金校验:可用资金(现金结存+授信−已使用授信,口径见 RealtimePnlCalc.TradeCanBeConfirm)
- // 需覆盖 应付预付金+成交金额,不足拦截抛错;特批放行发生在确认成交环节(ignoreMoneyCheck)。
+ // 需覆盖 应付预付金+成交金额;不足时抛 TradeLackOfMoneyException——controller 依"允许交易特批"开关
+ // 走 AdditionalProcessing/LackOfMoney 特批协议(与确认/审批环节一致)或按保存失败拦截。
// ExerciseDate 为空(异常数据)时跳过该校验,避免 TradeCanBeConfirm 内部解引用抛错。
- if (req.ExerciseDate.HasValue && !RealtimePnlCalc.TradeCanBeConfirm(req.ClientId, req, out var fundErrorMsg))
+ if (!ignoreMoneyCheck && req.ExerciseDate.HasValue && !RealtimePnlCalc.TradeCanBeConfirm(req.ClientId, req, out var fundErrorMsg))
{
- throw new ServiceException(fundErrorMsg);
+ throw new TradeLackOfMoneyException(fundErrorMsg);
}
var trans = DbContext.Database.BeginTransaction();
diff --git a/YLErpDAL/Modules/SwapModule/TradeLackOfMoneyException.cs b/YLErpDAL/Modules/SwapModule/TradeLackOfMoneyException.cs
new file mode 100644
index 00000000..dd8ecf3a
--- /dev/null
+++ b/YLErpDAL/Modules/SwapModule/TradeLackOfMoneyException.cs
@@ -0,0 +1,17 @@
+using BaseOUDAL;
+
+namespace YLErp.Modules.SwapModule
+{
+ ///
+ /// R4 簿记资金校验:保存交易时资金不足(走现金部分超现金结存)的标记异常。
+ /// controller 识别本类型后按确认/审批环节同一协议处理——系统参数 允许交易特批(SpecialOperateForTrade) 开启时
+ /// 返回 AdditionalProcessing/LackOfMoney 结构化响应(UI 弹"交易特批",带 additionalProcessing=LackOfMoney 重提放行),
+ /// 开关关闭时按普通保存失败拦截。此前保存环节直接抛 ServiceException 无特批出口,资金不足的交易无法落库走到确认环节。
+ ///
+ public class TradeLackOfMoneyException : ServiceException
+ {
+ public TradeLackOfMoneyException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/YLErpWeb/Controllers/SwapTrade2Controller.cs b/YLErpWeb/Controllers/SwapTrade2Controller.cs
index 6378b5f7..7dbb0d69 100644
--- a/YLErpWeb/Controllers/SwapTrade2Controller.cs
+++ b/YLErpWeb/Controllers/SwapTrade2Controller.cs
@@ -477,9 +477,10 @@ namespace YLErp.Web.Controllers
/// 框架合约保存
///
///
+ /// 特批重提标记:LackOfMoney=资金不足交易特批放行(须系统参数"允许交易特批"开启)
///
[HttpPost]
- public JsonResult tradeEditJson(trade req)
+ public JsonResult tradeEditJson(trade req, string additionalProcessing = null)
{
if (req == null)
{
@@ -490,16 +491,31 @@ namespace YLErp.Web.Controllers
{
req.id = DecryptInt(req.EncryptId);
}
+ //特批放行判定与确认/审批环节同口径(processtradelogController/ApprovalService):
+ //系统参数 允许交易特批(SpecialOperateForTrade) 开启 且 显式带 LackOfMoney 标记重提
+ var ignoreMoneyCheck = valuedateBLL.SystemDate.SpecialOperateForTrade == 1 && additionalProcessing == tradeBLL.LackOfMoney;
try
{
bool edit = req.id != 0;
- var r= swapTradeService.SaveTrade(req);
+ var r= swapTradeService.SaveTrade(req, ignoreMoneyCheck);
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
return JsonSuccess("更新成功", r);
}
+ catch (TradeLackOfMoneyException e)
+ {
+ //保存环节资金不足:开关开启时按确认/审批同一协议返回 AdditionalProcessing/LackOfMoney,
+ //UI 弹"交易特批"确认后带 additionalProcessing=LackOfMoney 重提放行;开关关闭时按保存失败拦截
+ if (valuedateBLL.SystemDate.SpecialOperateForTrade == 1)
+ {
+ LogFactory.GetLogger("交易保存").Info("保存资金不足待特批:" + e.Message);
+ return JsonSuccessData(new { proccessType = "AdditionalProcessing", type = tradeBLL.LackOfMoney, message = e.Message });
+ }
+ LogFactory.GetLogger("交易保存").Error(e);
+ return JsonError("保存失败:" + e.Message);
+ }
catch (Exception e)
{
LogFactory.GetLogger("交易保存").Error(e);
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
index 38e6045e..70d0d69e 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
@@ -817,12 +817,41 @@ const vue = new Vue({
"补充协议编号": $("#SupProtocolCode").val()
};
this.trade.trade_extend.ExtendJson = JSON.stringify(this.trade.trade_extend.ExtendObj);
- main.post("/swaptrade2/tradeEditJson", this.trade).done(function (resp) {
- window.location.href = "/swaptrade2/tradeview?enid=" + resp.obj.EncryptId;
- if (window.parent && window.parent.reloadtrade) {
- window.parent.reloadtrade();
+ //R4 保存环节资金校验:资金不足且系统开启"允许交易特批"时,服务端按确认/审批同一协议返回
+ //AdditionalProcessing/LackOfMoney——弹"交易特批"确认,带 additionalProcessing=LackOfMoney 重提放行
+ var thisObj = this;
+ var doSave = function (additionalProcessing) {
+ var url = "/swaptrade2/tradeEditJson";
+ if (!main.isEmpty(additionalProcessing)) {
+ url += "?additionalProcessing=" + additionalProcessing;
}
- });
+ main.post(url, thisObj.trade).done(function (resp) {
+ if (resp.obj && resp.obj.proccessType == "AdditionalProcessing") {
+ if (resp.obj.type == "LackOfMoney") {
+ var htmlContent = '
' + resp.obj.message + '
';
+ var lackMoneyConfirmLayer = main.open2("提示",
+ htmlContent,
+ {
+ area: ["430px", "175px"],
+ btn: ['交易特批', '取消'],
+ yes: function (index, layero) {
+ var layerIndex = lackMoneyConfirmLayer;
+ main.confirm("客户资金或授信不足,强制保存会导致本机构产生风险!要继续特批保存?", function () {
+ layer.close(layerIndex);
+ doSave("LackOfMoney");
+ });
+ }
+ });
+ }
+ return;
+ }
+ window.location.href = "/swaptrade2/tradeview?enid=" + resp.obj.EncryptId;
+ if (window.parent && window.parent.reloadtrade) {
+ window.parent.reloadtrade();
+ }
+ });
+ };
+ doSave();
},
changeMainProtocolCode() {
this.getSupProtocolCode();