浏览代码

套期交易-补单关联

li.shaoyi 3 年之前
父节点
当前提交
13e7bd9931

+ 28 - 0
public/proto/mtp.proto

@@ -2348,4 +2348,32 @@ message InnerTradeLinkRsp {
 	optional string RetDesc = 3; // 描述信息
 		optional uint64 TradeId = 4; // 内部成交单号
 		optional string ClientTicket = 5; // 客户端流水号
+}
+
+// 补录内部成交单请求
+message ManageAmendOrderReq {
+	optional MessageHead Header = 1;
+		optional uint64 OutTradeID = 2; // 外部成交单号
+		optional uint64 AccountID = 3; // 内部期货子账户
+		optional uint32 BuyOrSell = 4; // 方向-0:买
+		optional uint32 GoodsID = 5; // 商品ID
+		optional uint32 ChannelBuildType = 6; // 下单类型(开平标志-0:无
+		optional uint32 CloseType = 7; // 平仓类型(平仓标志-0:平仓
+		optional double TradePrice = 8; // 成交价格
+		optional uint64 TradeQty = 9; // 成交数量
+		optional uint32 CreatorSrc = 10; // 创建人来源-1:管理端
+		optional uint64 CreatorID = 11; // 创建人ID
+		optional uint32 HedgeFlag = 12; // 4:套期保值5:单边6:移仓
+		optional uint64 SpotContractID = 13; // RelatedTradeType=4:套期保值时
+		optional uint32 SaleUserID = 14; // 业务员ID
+		optional uint32 BizSubjectID = 15; // 归属业务部门ID
+}
+
+// 补录内部成交单响应
+message ManageAmendOrderRsp {
+	optional MessageHead Header = 1; // 消息头
+	optional int32 RetCode = 2; // 返回码
+	optional string RetDesc = 3; // 描述信息
+		optional uint64 OutTradeID = 4; // 外部成交单号
+		optional uint64 AccountID = 5; // 内部期货子账户
 }

+ 5 - 5
src/common/constants/enumCommon.ts

@@ -9,7 +9,7 @@ export enum BuyOrSell {
 /**
  * 下单类型
  */
-export const enum BuildType {
+export enum BuildType {
     open = 1, // 建仓
     close, // 平仓
     closeAndOpen, // 先平后建
@@ -18,7 +18,7 @@ export const enum BuildType {
 /**
  * 摘牌类型
  */
-export const enum DelistingType {
+export enum DelistingType {
     price = 1, // 价格最优
     selected, // 点选成交
 }
@@ -26,7 +26,7 @@ export const enum DelistingType {
 /**
  * 挂牌点选类型
  */
-export const enum ListingSelectType {
+export enum ListingSelectType {
     listing = 1, // 挂牌
     delisting, // 摘牌
     delistingAndListing, // 先摘后挂
@@ -35,7 +35,7 @@ export const enum ListingSelectType {
 /**
  * 期权类型
  */
-export const enum OptionType {
+export enum OptionType {
     Type_C = 1, // 认购(看涨)
     Type_P, // 认沽(看跌)
     Type_CP, // 认购+认沽
@@ -44,7 +44,7 @@ export const enum OptionType {
 /**
  * 操作类型
  */
-export const enum OperateType {
+export enum OperateType {
     ApplyFor = 1, // 申请
     Revoke, // 撤销
 }

+ 2 - 0
src/services/funcode/index.ts

@@ -263,4 +263,6 @@ export const funCode: Code = {
     HedgedItemRelatedPlanRsp: 1179682, // 项目关联计划响应
     InnerTradeLinkReq: 1179660, // 内部成交单关联请求
     InnerTradeLinkRsp: 1179661, // 内部成交单关联响应
+    ManageAmendOrderReq: 196707, // 补录内部成交单请求
+    ManageAmendOrderRsp: 196708, // 补录内部成交单响应
 }

+ 23 - 1
src/services/proto/hedgedItem/index.ts

@@ -1,5 +1,20 @@
 import { protoMiddleware } from "@/services/socket/protobuf/buildReq"
-import { HedgedItemAddReq, HedgedItemAddRsp, HedgedItemOperateReq, HedgedItemOperateRsp, HedgedItemEndReq, HedgedItemEndRsp, ContractRelatedHedgedItemReq, ContractRelatedHedgedItemRsp, HedgedItemRelatedPlanReq, HedgedItemRelatedPlanRsp, InnerTradeLinkReq, InnerTradeLinkRsp } from './interface'
+import {
+    HedgedItemAddReq,
+    HedgedItemAddRsp,
+    HedgedItemOperateReq,
+    HedgedItemOperateRsp,
+    HedgedItemEndReq,
+    HedgedItemEndRsp,
+    ContractRelatedHedgedItemReq,
+    ContractRelatedHedgedItemRsp,
+    HedgedItemRelatedPlanReq,
+    HedgedItemRelatedPlanRsp,
+    InnerTradeLinkReq,
+    InnerTradeLinkRsp,
+    ManageAmendOrderReq,
+    ManageAmendOrderRsp,
+} from './interface'
 
 /**
  * 新增套期项目请求
@@ -41,4 +56,11 @@ export const hedgedItemRelatedPlan = (param: HedgedItemRelatedPlanReq): Promise<
  */
 export const innerTradeLink = (param: InnerTradeLinkReq): Promise<InnerTradeLinkRsp> => {
     return protoMiddleware(param, 'InnerTradeLinkReq', 'InnerTradeLinkRsp', 2)
+}
+
+/**
+ * 补录内部成交单请求
+ */
+export const manageAmendOrder = (param: ManageAmendOrderReq): Promise<ManageAmendOrderRsp> => {
+    return protoMiddleware(param, 'ManageAmendOrderReq', 'ManageAmendOrderRsp', 2)
 }

+ 26 - 0
src/services/proto/hedgedItem/interface.ts

@@ -125,4 +125,30 @@ export interface InnerTradeLinkRsp {
     RetDesc: string; // 描述信息
     TradeId: number; // 内部成交单号
     ClientTicket: string; // 客户端流水号
+}
+
+// 补录内部成交单请求
+export interface ManageAmendOrderReq {
+    OutTradeID?: number; // 外部成交单号
+    AccountID?: number; // 内部期货子账户
+    BuyOrSell?: number; // 方向-0:买
+    GoodsID?: number; // 商品ID
+    ChannelBuildType?: number; // 下单类型(开平标志-0:无
+    CloseType: number; // 平仓类型(平仓标志-0:平仓
+    TradePrice?: number; // 成交价格
+    TradeQty?: number; // 成交数量
+    CreatorSrc: number; // 创建人来源-1:管理端
+    CreatorID: number; // 创建人ID
+    HedgeFlag: number; // 4:套期保值5:单边6:移仓
+    SpotContractID: number; // RelatedTradeType=4:套期保值时
+    SaleUserID?: number; // 业务员ID
+    BizSubjectID?: number; // 归属业务部门ID
+}
+
+// 补录内部成交单响应
+export interface ManageAmendOrderRsp {
+    RetCode: number; // 返回码
+    RetDesc: string; // 描述信息
+    OutTradeID: number; // 外部成交单号
+    AccountID: number; // 内部期货子账户
 }

+ 68 - 102
src/views/hedgeditem/futures/in/components/add/form.ts

@@ -1,90 +1,71 @@
-import { ref, computed } from 'vue'
-import { v4 } from 'uuid'
+import { ref, reactive, computed } from 'vue'
 import Long from 'long'
-import { message } from 'ant-design-vue'
-import { geLoginID_number } from '@/services/bus/login'
-import { queryTableList, handleComposeTable } from '@/common/export/commonTable'
-import { InternalEnableTradeDetailReq, InternalEnableTradeDetailRsp, InternalUncorrelatedTradeDetailRsp } from '@/services/go/ermcp/hedgedItem/interface'
+import { validateAction } from '@/common/setup/form'
+import { requestResultLoadingAndInfo } from '@/common/methods/request/resultInfo'
+import { InternalEnableTradeDetailReq, InternalEnableTradeDetailRsp } from '@/services/go/ermcp/hedgedItem/interface'
 import { queryInternalEnableTradeDetail } from '@/services/go/ermcp/hedgedItem'
-import { InnerTradeLinkRsp } from '@/services/proto/hedgedItem/interface'
-import { innerTradeLink } from '@/services/proto/hedgedItem'
+import { ManageAmendOrderReq } from '@/services/proto/hedgedItem/interface'
+import { manageAmendOrder } from '@/services/proto/hedgedItem'
 import { getAreaUserId } from '@/services/bus/user'
+import { getGoodsQuoteList } from '@/services/bus/goods'
+import { getTableColumns } from "@/common/export/table"
+import { geLoginID_number } from '@/services/bus/login'
+import { useTradeAccount } from '@/hooks/account'
 
-export function useForm(selectedRow: InternalUncorrelatedTradeDetailRsp) {
-    const { tradeid, goodsid, tradelot } = selectedRow;
-    const { tableList, queryTable } = queryTableList<InternalEnableTradeDetailRsp>(true, 2); // 表格列表数据
+export function useForm() {
+    const { futuresAccountList: accountList } = useTradeAccount();
     const loading = ref<boolean>(false);
+    const formElement = ref<HTMLElement>();
+    const tableList = ref<InternalEnableTradeDetailRsp[]>([]);
+    const goodsList = ref(getGoodsQuoteList()); // 合约列表
     const selectedRowKeys = ref<number[]>([]); // 表格选中的 rowKey 数据 :rowKey="(record,index)=>index"
 
-    // 计算当前关联数量
-    const currentQty = computed(() => {
-        return tableList.value.reduce((pre, cur, index) => {
-            if (selectedRowKeys.value.includes(index)) {
-                pre += cur?.relatedlot ?? 0;
-            }
-            return pre;
-        }, 0);
+    const formData = reactive<ManageAmendOrderReq>({
+        AccountID: undefined, // 内部期货子账户
+        BuyOrSell: undefined, // 方向-0:买
+        GoodsID: undefined, // 商品ID
+        ChannelBuildType: undefined, // 下单类型(开平标志-0:无
+        CloseType: 0, // 平仓类型(平仓标志-0:平仓
+        TradePrice: undefined, // 成交价格
+        TradeQty: undefined, // 成交数量
+        CreatorSrc: 2, // 创建人来源-1:管理端
+        CreatorID: geLoginID_number()!, // 创建人ID
+        HedgeFlag: 0, // 4:套期保值5:单边6:移仓
+        SpotContractID: 0, // RelatedTradeType=4:套期保值时
     })
 
+    const rules = {
+        GoodsID: [{ required: true, type: 'number', message: '请选择期货合约' }],
+        BuyOrSell: [{ required: true, type: 'number', message: '请选择方向' }],
+        AccountID: [{ required: true, type: 'number', message: '请选择账户' }],
+        ChannelBuildType: [{ required: true, type: 'number', message: '请选择下单类型' }],
+        TradePrice: [{ required: true, trigger: 'blur', type: 'number', message: '请输入价格' }],
+        TradeQty: [{ required: true, trigger: 'blur', type: 'number', message: '请输入数量' }]
+    }
+
     // 自定义表格选择项
     const rowSelection = computed(() => ({
         columnTitle: '选择',
+        type: 'radio',
         selectedRowKeys: selectedRowKeys.value,
         onChange: (keys: number[]) => {
             selectedRowKeys.value = keys;
         },
-        onSelect: (record: InternalEnableTradeDetailRsp, selected: boolean) => {
-            if (!selected) {
-                // 未选中的取消关联量
-                record.relatedlot = undefined;
-            }
-        },
     }))
 
-    // 计算本次关联数量
-    const calcRelatedqty = (record: InternalEnableTradeDetailRsp) => {
-        const { relatedlot, agreeunit, convertratio } = record;
-        if (relatedlot) {
-            return relatedlot * agreeunit * convertratio;
-        }
-        return 0;
-    }
-
-    // 表格通用逻辑
-    const composeTable = handleComposeTable<InternalEnableTradeDetailRsp>({
-        tableName: 'table_pcweb_hedgeditem_futures_in_relation',
-        queryFn: () => {
+    // 选择期货合约
+    const changeGoods = () => {
+        tableList.value = [];
+        if (formData.GoodsID) {
             const param: InternalEnableTradeDetailReq = {
                 areauserid: getAreaUserId(),
-                goodsid: goodsid,
+                goodsid: formData.GoodsID,
             }
-            queryTable(queryInternalEnableTradeDetail, param);
-        },
-    })
-
-    // 表格列
-    const columns = computed(() => {
-        const result = composeTable.columns.value;
-        result.push(...[
-            {
-                title: '关联手数',
-                align: 'center',
-                key: 'relatedlot',
-                slots: {
-                    customRender: 'relatedlot'
-                }
-            },
-            {
-                title: '关联数量',
-                align: 'center',
-                key: 'relatedqty',
-                slots: {
-                    customRender: 'relatedqty'
-                }
-            }
-        ])
-        return result;
-    })
+            queryInternalEnableTradeDetail(param).then((res) => {
+                tableList.value = res;
+            })
+        }
+    }
 
     // 表单提交
     const formSubmit = (callback?: () => void) => {
@@ -92,52 +73,37 @@ export function useForm(selectedRow: InternalUncorrelatedTradeDetailRsp) {
         const seledtedRows = tableList.value.filter((e, index) => keys.includes(index));
 
         if (seledtedRows.length) {
-            if (seledtedRows.every((e) => e.relatedlot && e.relatedlot > 0)) {
-                if (currentQty.value > tradelot) {
-                    message.error('关联数量不能大于成交手数');
-                } else {
-                    loading.value = true;
-                    const result: Promise<InnerTradeLinkRsp>[] = [];
-
-                    seledtedRows.forEach((e) => {
-                        result.push(innerTradeLink({
-                            TradeId: Long.fromString(tradeid), // 内部成交单号
-                            HedgeFlag: 13, // 投机套保标志
-                            SpotContractID: Long.fromString(e.hedgeditemid), // 现货合同ID
-                            RelatedLot: e.relatedlot!, // 关联手数
-                            RelatedMode: 2, // 关联模式
-                            CreatorSrc: 2, // 创建来源
-                            CreatorID: geLoginID_number()!, // 创建人
-                            ClientTicket: v4() // 客户端流水号
-                        }))
-                    })
-
-                    Promise.all(result).then(() => {
-                        message.success('关联成功');
-                        callback && callback();
-                    }).catch((err) => {
-                        message.error('关联失败:' + err);
-                    }).finally(() => {
-                        loading.value = false;
-                    })
-                }
-
-            } else {
-                message.error('请输入关联手数');
-            }
+            formData.HedgeFlag = 13;
+            formData.SpotContractID = Long.fromString(seledtedRows[0].hedgeditemid);
         } else {
-            message.error('请选择关联订单');
+            formData.HedgeFlag = 0;
+            formData.SpotContractID = 0;
         }
+
+        validateAction(formElement, formData).then((param) => {
+            requestResultLoadingAndInfo(manageAmendOrder, param, loading, ['补单关联成功', '补单关联失败:']).then(() => {
+                callback && callback();
+            }).catch(() => {
+                loading.value = false;
+            })
+        })
     }
 
+    const { columns, registerColumn } = getTableColumns();
+    registerColumn('table_pcweb_hedgeditem_futures_in_relation');
+
     return {
+        formElement,
+        formData,
         loading,
+        rules,
+        goodsList,
+        accountList,
         tableList,
         selectedRowKeys,
         rowSelection,
-        currentQty,
         columns,
-        calcRelatedqty,
+        changeGoods,
         formSubmit,
     }
 }

+ 55 - 27
src/views/hedgeditem/futures/in/components/add/index.vue

@@ -1,41 +1,61 @@
 <template>
     <!-- 套期交易-期货成交关联-补单关联 -->
-    <a-modal class="commonModal custom-detail" title="期现单据关联" v-model:visible="visible" centered @cancel="cancel(false)"
-        :maskClosable="false" width="1000px">
-        <a-form class="inlineForm">
+    <a-modal class="commonModal custom-detail" title="补单关联" v-model:visible="visible" centered @cancel="cancel(false)"
+        :maskClosable="false" width="980px">
+        <a-form class="inlineForm" ref="formElement" :model="formData" :rules="rules">
             <fieldset class="formFieldSet">
                 <legend>成交单信息</legend>
                 <a-row :gutter="24">
                     <a-col :span="12">
-                        <a-form-item label="期货合约">
-                            <span class="white">{{ selectedRow.accountname + '/' + selectedRow.accountid }}</span>
+                        <a-form-item label="期货合约" name="GoodsID">
+                            <a-select class="inlineFormSelect" placeholder="请选择合约" :filterOption="filterOption"
+                                v-model:value="formData.GoodsID" @change="changeGoods" show-search style="width:200px">
+                                <a-select-option v-for="item in goodsList" :value="item.goodsid" :key="item.goodsid">{{
+                                        item.goodsname
+                                }}</a-select-option>
+                            </a-select>
                         </a-form-item>
                     </a-col>
                     <a-col :span="12">
-                        <a-form-item label="方向">
-                            <span class="white">{{ selectedRow.goodscode + '/' + selectedRow.goodsname }}</span>
+                        <a-form-item label="方向" name="BuyOrSell">
+                            <a-select class="inlineFormSelect" placeholder="请选择方向" v-model:value="formData.BuyOrSell"
+                                style="width:200px">
+                                <a-select-option :value="BuyOrSell.buy">买</a-select-option>
+                                <a-select-option :value="BuyOrSell.sell">卖</a-select-option>
+                            </a-select>
                         </a-form-item>
                     </a-col>
                     <a-col :span="12">
-                        <a-form-item label="期货子账户">
-                            <span class="white">{{ getChannelBuildName(selectedRow.channelbuildtype) }} {{
-                                    getBuyOrSellName(selectedRow.buyorsell)
-                            }}</span>
+                        <a-form-item label="期货子账户" name="AccountID">
+                            <a-select class="inlineFormSelect" placeholder="请选择账户" v-model:value="formData.AccountID"
+                                style="width:200px">
+                                <a-select-option v-for="item in accountList" :value="item.accountid"
+                                    :key="item.accountid">
+                                    {{ item.accountid }}/{{ item.accountname }}</a-select-option>
+                            </a-select>
                         </a-form-item>
                     </a-col>
                     <a-col :span="12">
-                        <a-form-item label="建平">
-                            <span class="white">{{ formatValue(selectedRow.tradeprice) }}</span>
+                        <a-form-item label="建平" name="ChannelBuildType">
+                            <a-select class="inlineFormSelect" placeholder="请选择下单类型"
+                                v-model:value="formData.ChannelBuildType" style="width:200px">
+                                <a-select-option :value="BuildType.open">建仓</a-select-option>
+                                <a-select-option :value="BuildType.close">平仓</a-select-option>
+                            </a-select>
                         </a-form-item>
                     </a-col>
                     <a-col :span="12">
-                        <a-form-item label="成交价">
-                            <span class="white">{{ formatValue(selectedRow.tradelot) }}</span>
+                        <a-form-item label="成交价" name="TradePrice">
+                            <a-input-number class="dialogInput" placeholder="请输入价格" v-model:value="formData.TradePrice"
+                                style="width:200px" />
                         </a-form-item>
                     </a-col>
                     <a-col :span="12">
-                        <a-form-item label="成交数量">
-                            <span class="white">{{ formatValue(selectedRow.tradetime) }}</span>
+                        <a-form-item label="成交数量" name="TradeQty">
+                            <span class="white">
+                                <a-input-number class="dialogInput" placeholder="请输入数量" :precision="0"
+                                    v-model:value="formData.TradeQty" style="width:200px" />
+                            </span>
                         </a-form-item>
                     </a-col>
                 </a-row>
@@ -48,13 +68,8 @@
                         <template #wrstandardname="{ record }">
                             <span>{{ record.deliverygoodsname }}/{{ record.wrstandardname }}</span>
                         </template>
-                        <template #relatedlot="{ record, index }">
-                            <a-input-number class="dialogInput" size="small"
-                                :disabled="!selectedRowKeys.includes(index)" :precision="0"
-                                v-model:value="record.relatedlot" style="width:80px" />
-                        </template>
-                        <template #relatedqty="{ record }">
-                            <span>{{ calcRelatedqty(record) }}</span>
+                        <template #unexehedgeqty="{ text }">
+                            <span>{{ text.toFixed(2) }}</span>
                         </template>
                     </a-table>
                 </div>
@@ -75,6 +90,7 @@ import { formatValue } from '@/common/methods'
 import { getChannelBuildName, getBuyOrSellName } from '@/common/constants/enumsName'
 import { InternalUncorrelatedTradeDetailRsp } from '@/services/go/ermcp/hedgedItem/interface'
 import { useForm } from './form'
+import { BuildType, BuyOrSell } from '@/common/constants/enumCommon';
 
 export default defineComponent({
     emits: ['cancel'],
@@ -86,20 +102,32 @@ export default defineComponent({
     },
     setup(props, context) {
         const { visible, cancel } = _closeModal(context);
-        const { loading, tableList, columns, currentQty, selectedRowKeys, rowSelection, calcRelatedqty, formSubmit } = useForm(props.selectedRow);
+        const { formData, rules, formElement, loading, tableList, accountList, goodsList, columns, selectedRowKeys, rowSelection, changeGoods, formSubmit } = useForm();
+
+        // 搜索商品合约
+        function filterOption(input: string, option: any) {
+            return option.children[0].children.includes(input);
+        }
 
         return {
             visible,
             loading,
             tableList,
+            rules,
             columns,
             cancel,
             formatValue,
-            currentQty,
+            goodsList,
+            accountList,
+            formData,
             selectedRowKeys,
             rowSelection,
-            calcRelatedqty,
+            formElement,
+            BuildType,
+            BuyOrSell,
             formSubmit,
+            filterOption,
+            changeGoods,
             getBuyOrSellName,
             getChannelBuildName,
         }