瀏覽代碼

1. 清单对比,附属工程量汇总
2. 台账分解,批量替换清单
3. 台账修订,批量替换清单
4. 树结构底层修改

MaiXinRong 1 年之前
父節點
當前提交
6400468e9a

+ 96 - 46
app/base/base_bills_service.js

@@ -160,8 +160,7 @@ class BaseBillsSerivce extends TreeService {
      * @return {Promise<*>}
      */
     async addStdNode(tenderId, selectId, stdData, reviseId) {
-        const newData = this._filterStdData(stdData);
-        const result = await this.addBillsNode(tenderId, selectId, newData, reviseId);
+        const result = await this.addBillsNode(tenderId, selectId, stdData, reviseId);
         return result;
     }
 
@@ -173,8 +172,7 @@ class BaseBillsSerivce extends TreeService {
      * @returns {Promise<*>}
      */
     async addStdNodeAsChild(tenderId, selectId, stdData, reviseId) {
-        const newData = this._filterStdData(stdData);
-        const result = await this.addChild(tenderId, selectId, newData, reviseId);
+        const result = await this.addChild(tenderId, selectId, stdData, reviseId);
         return result;
     }
 
@@ -226,12 +224,12 @@ class BaseBillsSerivce extends TreeService {
      * @return {Promise<void>}
      * @private
      */
-    async _addChildAutoOrder(tenderId, parentData, data, reviseId) {
+    async _addChildAutoOrder(tenderId, parentData, data, orderField, reviseId) {
         const self = this;
         const findPreData = function(list, a) {
             if (!list || list.length === 0) { return null; }
             for (let i = 0, iLen = list.length; i < iLen; i++) {
-                if (billsUtils.compareCode(list[i].code, a.code) > 0) {
+                if (billsUtils.compareCode(list[i][orderField], a[orderField]) > 0) {
                     return i > 0 ? list[i - 1] : null;
                 }
             }
@@ -258,51 +256,36 @@ class BaseBillsSerivce extends TreeService {
      * @param {StandardLib} stdLib - 标准库
      * @return {Promise<void>}
      */
-    async addStdNodeWithParent(tenderId, stdData, stdLib, reviseId) {
-        // 查询完整标准清单,并按层次排序
-        const fullLevel = await stdLib.getFullLevelDataByFullPath(stdData.list_id, stdData.full_path);
-        fullLevel.sort(function(x, y) {
-            return x.level - y.level;
-        });
-
-        let isNew = false,
-            node,
-            firstNew,
-            updateParent,
-            addResult;
+    async addStdNodeWithParent(tenderId, stdData, reviseId) {
+        let node, firstNew, updateParent, addResult;
         const expandIds = [];
         this.transaction = await this.db.beginTransaction();
         try {
             // 从最顶层节点依次查询是否存在,否则添加
-            for (let i = 0, len = fullLevel.length; i < len; i++) {
-                const stdNode = fullLevel[i];
+            for (let i = 0, len = stdData.length; i < len; i++) {
+                const stdNode = stdData[i];
+                const parent = node;
+                node = await this.getDataByCondition({
+                    tender_id: tenderId, ledger_pid: parent ? parent.ledger_id : rootId,
+                    code: stdNode.code, name: stdNode.name,
+                });
+                if (node) {
+                    expandIds.push(node.ledger_id);
+                    continue;
+                }
 
-                if (isNew) {
-                    const newData = this._filterStdData(stdNode);
-                    newData.is_leaf = (i === len - 1);
-                    [addResult, node] = await this._addChildNodeData(tenderId, node, newData, reviseId);
+                if (firstNew) {
+                    stdNode.is_leaf = (i === len - 1);
+                    [addResult, node] = await this._addChildNodeData(tenderId, parent, stdNode, reviseId);
                 } else {
-                    const parent = node;
-                    node = await this.getDataByCondition({
-                        tender_id: tenderId,
-                        ledger_pid: parent ? parent.ledger_id : rootId,
-                        code: stdNode.code,
-                        name: stdNode.name,
-                    });
-                    if (!node) {
-                        isNew = true;
-                        const newData = this._filterStdData(stdNode);
-                        newData.is_leaf = (i === len - 1);
-                        [addResult, node] = await this._addChildAutoOrder(tenderId, parent, newData, reviseId);
-                        if (parent && parent.is_leaf) {
-                            await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
-                                unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
-                            updateParent = parent;
-                        }
-                        firstNew = node;
-                    } else {
-                        expandIds.push(node.ledger_id);
+                    stdNode.is_leaf = (i === len - 1);
+                    [addResult, node] = await this._addChildAutoOrder(tenderId, parent, stdNode, 'code', reviseId);
+                    if (parent && parent.is_leaf) {
+                        await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
+                            unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
+                        updateParent = parent;
                     }
+                    firstNew = node;
                 }
             }
             await this.transaction.commit();
@@ -312,8 +295,75 @@ class BaseBillsSerivce extends TreeService {
         }
 
         // 查询应返回的结果
-        let createData = [],
-            updateData = [];
+        let createData = [], updateData = [];
+        if (firstNew) {
+            createData = await this.getDataByFullPath(tenderId, firstNew.full_path + '%');
+            updateData = await this.getNextsData(tenderId, firstNew.ledger_pid, firstNew.order);
+            if (updateParent) {
+                updateData.push(await this.getDataByCondition({ id: updateParent.id }));
+            }
+        }
+        return { create: createData, update: updateData };
+    }
+
+    async getLeafXmj(select) {
+        const relaId = select.full_path.split('-');
+        const parents = await this.getAllDataByCondition({ where: { tender_id: select.tender_id, ledger_id: relaId }, orders: [['level', 'asc']]});
+        const xmjs = parents.filter(x => { return !!x.code; });
+        return xmjs[xmjs.length - 1];
+    }
+
+    async addGclStdNode(tenderId, selectId, stdData, reviseId) {
+        const selectNode = await this.ctx.service.ledger.getDataByNodeId(tenderId, selectId);
+        if (selectNode.b_code) {
+            return await this.ctx.service.ledger.addStdNode(tenderId, selectId, stdData, reviseId);
+        } else {
+            return await this.ctx.service.ledger.addStdNodeAsChild(tenderId, selectId, stdData, reviseId);
+        }
+    }
+
+    async addGclStdNodeWithParent(tenderId, selectId, stdData, reviseId) {
+        const selectData = await this.getDataByKid(tenderId, selectId);
+        if (!selectData) throw '新增节点数据错误,请刷新后重试';
+
+        let leafXmj = selectData.b_code ? await this.getLeafXmj(selectData) : selectData;
+        if (!leafXmj) throw '找不到可插入清单的项目节,请刷新后重试';
+
+        let node = leafXmj, firstNew, updateParent, addResult;
+        this.transaction = await this.db.beginTransaction();
+        try {
+            // 从最顶层节点依次查询是否存在,否则添加
+            for (let i = 0, len = stdData.length; i < len; i++) {
+                const stdNode = stdData[i];
+                if (!stdNode.b_code) continue;
+
+                const parent = node;
+                node = await this.getDataByCondition({
+                    tender_id: tenderId, ledger_pid: parent.ledger_id,
+                    code: stdNode.code, name: stdNode.name,
+                });
+                if (node) continue;
+
+                stdNode.is_leaf = (i === len - 1);
+                if (firstNew) {
+                    [addResult, node] = await this._addChildNodeData(selectData.tender_id, parent, stdNode, reviseId);
+                } else {
+                    [addResult, node] = await this._addChildAutoOrder(selectData.tender_id, parent, stdNode, 'b_code', reviseId);
+                    if (parent && parent.is_leaf) {
+                        await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
+                            unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
+                        updateParent = parent;
+                    }
+                    firstNew = node;
+                }
+            }
+            await this.transaction.commit();
+        } catch (err) {
+            await this.transaction.rollback();
+            throw err;
+        }
+
+        let createData = [], updateData = [];
         if (firstNew) {
             createData = await this.getDataByFullPath(tenderId, firstNew.full_path + '%');
             updateData = await this.getNextsData(tenderId, firstNew.ledger_pid, firstNew.order);

+ 8 - 32
app/controller/ledger_controller.js

@@ -7,12 +7,6 @@
  * @date 2017/11/30
  * @version
  */
-
-const stdDataAddType = {
-    withParent: 1,
-    child: 2,
-    next: 3,
-};
 const audit = require('../const/audit');
 const moment = require('moment');
 const auditConst = audit.ledger;
@@ -254,39 +248,19 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async _addStd(ctx, data) {
-            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdData) throw '参数错误';
             // todo 校验项目是否使用该库的权限
 
-            let stdLib,
-                addType;
             switch (data.stdType) {
                 case 'xmj':
-                    stdLib = ctx.service.stdXmj;
-                    addType = stdDataAddType.withParent;
-                    break;
+                    return await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, data.stdData);
                 case 'gcl':
-                    stdLib = ctx.service.stdGcl;
-                    const selectNode = await ctx.service.ledger.getDataByNodeId(ctx.tender.id, data.id);
-                    if (selectNode.b_code) {
-                        addType = stdDataAddType.next;
-                    } else {
-                        addType = stdDataAddType.child;
-                    }
-                    break;
+                    return data.withParent
+                        ? await ctx.service.ledger.addGclStdNodeWithParent(ctx.tender.id, data.id, data.stdData)
+                        : await ctx.service.ledger.addGclStdNode(ctx.tender.id, data.id, data.stdData[data.stdData.length - 1]);
                 default:
                     throw '未知标准库';
             }
-            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
-            switch (addType) {
-                case stdDataAddType.child:
-                    return await ctx.service.ledger.addStdNodeAsChild(ctx.tender.id, data.id, stdData);
-                case stdDataAddType.next:
-                    return await ctx.service.ledger.addStdNode(ctx.tender.id, data.id, stdData);
-                case stdDataAddType.withParent:
-                    return await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, stdData, stdLib);
-                default:
-                    throw '未知添加方式';
-            }
         }
         /**
          * 从签约清单添加节点
@@ -744,10 +718,12 @@ module.exports = app => {
                 const posData = this.ctx.tender.data.measure_type === measureType.tz.value
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
                 const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id } });
+                const ancGcl = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.ancillaryGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } }) : [];
                 const zlj = JSON.parse(JSON.stringify(stdConst.zlj));
                 zlj.deal_bills_tp = ctx.tender.info.deal_param.zanLiePrice;
                 ctx.body = { err: 0, msg: '', data: {
-                    bills: billsData, pos: posData, dealBills, spec: {zlj: zlj, jrg: stdConst.jrg},
+                    bills: billsData, pos: posData, ancGcl, dealBills, spec: {zlj: zlj, jrg: stdConst.jrg},
                 }};
             } catch (err) {
                 this.log(err);

+ 5 - 26
app/controller/revise_controller.js

@@ -585,40 +585,19 @@ module.exports = app => {
             return await this.ctx.service.reviseBills.pasteBlockData(this.ctx.tender.id, data.id, data.block, { crid: revise.id });
         }
         async _addStd(revise, data) {
-            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdData) throw '参数错误';
             // todo 校验项目是否使用该库的权限
 
-            let stdLib,
-                addType;
             switch (data.stdType) {
                 case 'xmj':
-                    stdLib = this.ctx.service.stdXmj;
-                    addType = stdDataAddType.withParent;
-                    break;
+                    return await ctx.service.reviseBills.addStdNodeWithParent(revise.tid, data.stdData, revise.id);
                 case 'gcl':
-                    stdLib = this.ctx.service.stdGcl;
-                    const selectNode = await this.ctx.service.reviseBills.getDataByKid(revise.tid, data.id);
-                    if (selectNode.b_code) {
-                        addType = stdDataAddType.next;
-                    } else {
-                        addType = stdDataAddType.child;
-                    }
-                    break;
+                    return data.withParent
+                        ? await ctx.service.reviseBills.addGclStdNodeWithParent(revise.tid, data.id, data.stdData, revise.id)
+                        : await ctx.service.reviseBills.addGclStdNode(revise.tid, data.id, data.stdData[data.stdData.length - 1], revise.id);
                 default:
                     throw '未知标准库';
             }
-            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
-            switch (addType) {
-                case stdDataAddType.child:
-                    return await this.ctx.service.reviseBills.addStdNodeAsChild(revise.tid, data.id, stdData, revise.id);
-                    break;
-                case stdDataAddType.next:
-                    return await this.ctx.service.reviseBills.addStdNode(revise.tid, data.id, stdData, revise.id);
-                case stdDataAddType.withParent:
-                    return await this.ctx.service.reviseBills.addStdNodeWithParent(revise.tid, stdData, stdLib, revise.id);
-                default:
-                    throw '未知添加方式';
-            }
         }
         async _addDeal(revise, data) {
             if (!data.type || !data.dealBills) throw '数据错误';

+ 3 - 6
app/public/js/change_revise.js

@@ -3005,13 +3005,10 @@ $(document).ready(() => {
     billsSpread.refresh();
     if (posSpread) posSpread.refresh();
 
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
+    const stdLibCellDoubleClick = function (updateData, stdNode, stdTree) {
         const mainSheet = billsSheet;
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
+        if (!stdTree || !mainSheet.zh_tree) { return; }
 
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
         const mainTree = mainSheet.zh_tree;
         const sel = mainSheet.getSelections()[0];
         const mainNode = mainTree.nodes[sel.row];
@@ -3042,7 +3039,7 @@ $(document).ready(() => {
             postData: {
                 id: mainTree.getNodeKey(mainNode),
                 tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
+                stdType: updateData.postData.stdType,
                 stdLibId: stdNode.list_id,
                 stdNode: stdTree.getNodeKey(stdNode)
             }

+ 50 - 0
app/public/js/gcl_gather.js

@@ -18,6 +18,7 @@ const gclGatherModel = (function () {
     let posGatherFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty','qc_minus_qty',
         'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'pre_qc_minus_qty',
         'end_contract_qty', 'end_qc_qty', 'end_gather_qty', 'end_qc_minus_qty'];
+    let ancillaryGclGatherFields = ['quantity'];
     // 初始化 清单树
     const gsTreeSetting = {
         id: 'ledger_id',
@@ -36,6 +37,7 @@ const gclGatherModel = (function () {
         updateFields: ['contract_qty', 'qc_qty', 'qc_minus_qty'],
     };
     const gsPos = new StagePosData(posSetting);
+    const gsAncGcl = createAncillaryGcl({ id: 'id', masterId: 'lid', sort: [['g_order', 'asc']] });
     let deal = [], change;
 
     const gclList = [], leafXmjs = [];
@@ -77,6 +79,10 @@ const gclGatherModel = (function () {
         gsPos.loadPreStageData(prePos);
     }
 
+    function loadAncillaryGclData(datas) {
+        gsAncGcl.loadDatas(datas);
+    }
+
     function loadDealBillsData(dealBills) {
         deal = dealBills;
     }
@@ -114,6 +120,7 @@ const gclGatherModel = (function () {
             unit_price: node.unit_price,
             org_price: node.org_price,
             leafXmjs: [],
+            ancGcl: [],
         };
         gclList.push(gcl);
         return gcl;
@@ -290,6 +297,22 @@ const gclGatherModel = (function () {
         }
     }
 
+    function gatherAncGclData(node, gcl, cacheLeafXmj) {
+        const gclPart = gsAncGcl.getPartData(node.id);
+        if (!gclPart || gclPart.length === 0) return;
+
+        for (const ag of gclPart) {
+            const condition = _.assign({ is_aux: ag.is_aux, name: ag.name, unit: ag.unit }, cacheLeafXmj);
+            let nag = _.find(gcl.ancGcl, condition);
+            if (!nag) {
+                nag = condition;
+                gcl.ancGcl.push(condition);
+            }
+            gatherfields(nag, ag, ancillaryGclGatherFields);
+        }
+
+    }
+
     /**
      * 汇总节点
      * @param node - 最底层 工程量清单 节点
@@ -317,6 +340,7 @@ const gclGatherModel = (function () {
             dx.settle_status = (posRange && posRange.length > 0 ? d.settle_status : node.settle_status) || 0 ;
             gcl.leafXmjs.push(dx);
         }
+        gatherAncGclData(node, gcl, cacheLeafXmj);
     }
 
     /**
@@ -631,16 +655,42 @@ const gclGatherModel = (function () {
         });
     }
 
+    function _gatherAncillaryGcl(gcl, fields = ['jldy']) {
+        fields.push('is_aux', 'name', 'unit');
+        gcl.gatherAncGcl = [];
+        for (const lx of gcl.ancGcl) {
+            const condition = {};
+            for (const f of fields) {
+                condition[f] = lx[f];
+            }
+            let glx = _.find(gcl.gatherAncGcl, condition);
+            if (!glx) {
+                glx = JSON.parse(JSON.stringify(lx));
+                gcl.gatherAncGcl.push(glx);
+            } else {
+                gatherfields(glx, lx, ancillaryGclGatherFields);
+            }
+        }
+    }
+
+    function reGatherAncillaryGcl(fields) {
+        gclList.forEach(g => {
+            _gatherAncillaryGcl(g, fields);
+        });
+    }
+
     return {
         loadGatherField,
         loadDecimal,
         loadLedgerData,
         loadPosData,
+        loadAncillaryGclData,
         loadDealBillsData,
         loadChangeBillsData,
         gatherGclData,
         checkDiffer,
         gatherChapterData,
         reGatherLeafXmj,
+        reGatherAncillaryGcl,
     };
 })();

+ 60 - 34
app/public/js/ledger.js

@@ -1056,6 +1056,29 @@ $(document).ready(function() {
                 })
             }
         },
+        batchReplace: function(orgInfo, newInfo) {
+            const updateData = [];
+            for (const d of ledgerTree.nodes) {
+                if (!d.b_code) continue;
+                const checkInfo = { code: d.b_code || '', name: d.name || '', unit: d.unit || '', unit_price: d.unit_price || 0 };
+                if (_.isMatch(checkInfo, orgInfo)) {
+                    const data = ledgerTree.getNodeKeyData(d);
+                    data.b_code = newInfo.code;
+                    data.name = newInfo.name;
+                    data.unit = newInfo.unit;
+                    data.unit_price = newInfo.unit_price;
+                    updateData.push(data);
+                }
+            }
+            if (updateData.length === 0) {
+                toastr.warning('没有可以替换的数据');
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update', postData: updateData}, function (result) {
+                const refreshNode = ledgerTree.loadPostData(result);
+                treeOperationObj.refreshTree(ledgerSheet, refreshNode);
+            });
+        },
     };
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
@@ -1422,6 +1445,23 @@ $(document).ready(function() {
                 return !readOnly;
             }
         };
+        billsContextMenuOptions.items.batchReplace = {
+            name: '批量替换清单',
+            icon: 'fa-sign-in',
+            callback: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                if (!node) return;
+
+                BatchReplace.load(node.b_code, node.name, node.unit, node.unit_price, treeOperationObj.batchReplace);
+            },
+            disabled: function (key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                return !node || !node.b_code;
+            },
+            visible: function (key, opt) {
+                return !readOnly;
+            }
+        };
         billsContextMenuOptions.items.sprBatch = '----';
     }
     billsContextMenuOptions.items.copyBlock = {
@@ -2829,54 +2869,40 @@ $(document).ready(function() {
             if (gclGather) gclGather.spread.refresh();
         }
     });
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
-        const mainSheet = ledgerSpread.getActiveSheet();
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
-
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
-
-        const mainTree = mainSheet.zh_tree;
-        const sel = mainSheet.getSelections()[0];
-        const mainNode = mainTree.nodes[sel.row];
-        if (!stdNode) { return; }
-        if (info.sheet.zh_setting.stdType === 'gcl') {
-            if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
+    const stdLibCellDoubleClick = function (updateData, stdNode) {
+        if (!stdNode) return;
+        const mainNode = SpreadJsObj.getSelectObject(ledgerSheet);
+        if (!mainNode) return;
+        if (updateData.postData.stdType === 'gcl') {
+            if (!!mainNode.code && !ledgerTree.isLeafXmj(mainNode)) {
                 toastr.warning('非最底层项目下,不应添加清单');
                 return;
             }
         }
 
-        postData(window.location.pathname + '/update', {
-            postType: 'add-std',
-            postData: {
-                id: ledgerTree.getNodeKey(mainNode),
-                tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
-                stdLibId: stdNode.list_id,
-                stdNode: stdTree.getNodeKey(stdNode)
-            }
-        }, function (result) {
-            const refreshNode = mainTree.loadPostData(result);
-            treeOperationObj.refreshTree(mainSheet, refreshNode);
+        updateData.postData.id = ledgerTree.getNodeKey(mainNode);
+        updateData.postData.tender_id = mainNode.tender_id;
+
+        postData(window.location.pathname + '/update', updateData, function (result) {
+            const sel = ledgerSheet.getSelections()[0];
+            const refreshNode = ledgerTree.loadPostData(result);
+            treeOperationObj.refreshTree(ledgerSheet, refreshNode);
             if (refreshNode.create && refreshNode.create.length > 0) {
-                mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
+                ledgerSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
                 const refreshRow = [sel.row, refreshNode.create[refreshNode.create.length - 1].index];
-                const curIndex = mainTree.nodes.indexOf(mainNode);
+                const curIndex = ledgerTree.nodes.indexOf(mainNode);
                 if (sel.row !== curIndex && curIndex >= 0) refreshRow.push(curIndex);
-                SpreadJsObj.reloadRowsBackColor(mainSheet, refreshRow);
+                SpreadJsObj.reloadRowsBackColor(ledgerSheet, refreshRow);
             } else {
                 const node = _.find(ledgerTree.nodes, {code: stdNode.code, name: stdNode.name});
                 if (node) {
-                    mainSheet.setSelection(ledgerTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, ledgerTree.nodes.indexOf(node)]);
+                    ledgerSheet.setSelection(ledgerTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(ledgerSheet, [sel.row, ledgerTree.nodes.indexOf(node)]);
                 }
             }
-            treeOperationObj.refreshOperationValid(mainSheet);
+            treeOperationObj.refreshOperationValid(ledgerSheet);
             ledgerSpread.focus();
-            posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
+            treeOperationObj.loadRelaData();
         });
     };
     const stdXmjSetting = {

+ 71 - 1
app/public/js/ledger_gather.js

@@ -131,6 +131,35 @@ $(document).ready(() => {
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
     SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
 
+    const gatherAncGclSpread = SpreadJsObj.createNewSpread($('#anc-gcl-gather-spread')[0]);
+    const gatherAncGclSpreadSetting = {
+        cols: [
+            {title: '单位工程', colSpan: '1', rowSpan: '1', field: 'dwgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分部工程', colSpan: '1', rowSpan: '1', field: 'fbgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分项工程', colSpan: '1', rowSpan: '1', field: 'fxgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '细目', colSpan: '1', rowSpan: '1', field: 'jldy', hAlign: 0, width: 100, formatter: '@'},
+            {title: '辅材', colSpan: '1', rowSpan: '1', field: 'is_aux', hAlign: 1, width: 60, formatter: '@', fixed: 1, cellType: 'checkbox'},
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 100, formatter: '@', fixed: 1},
+            {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@', fixed: 1},
+            {title: '设计量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number', fixed: 1},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        localCache: {
+            key: 'ledger-gather-ancGcl',
+            colWidth: true,
+        },
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherAncGclSpreadSetting);
+    const gatherAncGclSheet = gatherAncGclSpread.getActiveSheet();
+    SpreadJsObj.initSheet(gatherAncGclSheet, gatherAncGclSpreadSetting);
+
     let gclGatherData;
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -145,13 +174,22 @@ $(document).ready(() => {
     function loadGatherLeafXmjData(iGclRow) {
         const gcl = iGclRow ? gclGatherData[iGclRow] : SpreadJsObj.getSelectObject(gclSheet);
         SpreadJsObj.resetTopAndSelect(gatherLeafXmjSheet);
-        console.log(gcl.gatherLeafXmjs);
         if (gcl) {
             SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, gcl.gatherLeafXmjs);
         } else {
             SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, []);
         }
     }
+    function loadGatherAncGclData(iGclRow) {
+        const gcl = iGclRow ? gclGatherData[iGclRow] : SpreadJsObj.getSelectObject(gclSheet);
+        SpreadJsObj.resetTopAndSelect(gatherAncGclSheet);
+        console.log(gcl.ancGcl, gcl.gatherAncGcl);
+        if (gcl) {
+            SpreadJsObj.loadSheetData(gatherAncGclSheet, SpreadJsObj.DataType.Data, gcl.gatherAncGcl);
+        } else {
+            SpreadJsObj.loadSheetData(gatherAncGclSheet, SpreadJsObj.DataType.Data, []);
+        }
+    }
     // 标红显示 签约-台账≠0
     function checkCompareData() {
         const sheet = gclSheet;
@@ -173,6 +211,7 @@ $(document).ready(() => {
         if (!info.oldSelections || iNewRow !== info.oldSelections[0].row) {
             loadLeafXmjData(iNewRow);
             loadGatherLeafXmjData(iNewRow);
+            loadGatherAncGclData(iNewRow);
         }
     });
 
@@ -205,9 +244,11 @@ $(document).ready(() => {
         gclGatherModel.loadLedgerData(data.bills);
         gclGatherModel.loadPosData(data.pos);
         gclGatherModel.loadDealBillsData(data.dealBills);
+        gclGatherModel.loadAncillaryGclData(data.ancGcl);
         gclGatherData = gclGatherModel.gatherGclData();
         gclGatherModel.checkDiffer(gclGatherData);
         gclGatherModel.reGatherLeafXmj();
+        gclGatherModel.reGatherAncillaryGcl();
         for (const gcl of gclGatherData) {
             gcl.compare_qty = ZhCalc.sub(gcl.quantity, gcl.deal_bills_qty);
             gcl.compare_tp = ZhCalc.sub(gcl.total_price, gcl.deal_bills_tp);
@@ -217,6 +258,7 @@ $(document).ready(() => {
         checkCompareData();
         loadLeafXmjData(0);
         loadGatherLeafXmjData(0);
+        loadGatherAncGclData(0);
 
         const chapterData = gclGatherModel.gatherChapterData(chapter, data.spec, ['total_price']);
         for (const c of chapterData) {
@@ -264,6 +306,7 @@ $(document).ready(() => {
         afterLocated: function () {
             loadLeafXmjData();
             loadGatherLeafXmjData();
+            loadGatherAncGclData();
         },
         calcSum: function (result) {
             const sum = { name: '合计', searchIndex: -1 };
@@ -293,6 +336,7 @@ $(document).ready(() => {
         gclSpread.refresh();
         leafXmjSpread.refresh();
         gatherLeafXmjSpread.refresh();
+        gatherAncGclSpread.refresh();
     });
 
     // 下部设置显示相关
@@ -306,6 +350,11 @@ $(document).ready(() => {
                 $(ot).hide();
             }
         }
+        setTimeout(function() {
+            leafXmjSpread.refresh();
+            gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
+        }, 100);
     });
     $('[name=gather-xmj]').change(function() {
         const checkOption = $('[name=gather-xmj]:checked');
@@ -325,6 +374,24 @@ $(document).ready(() => {
         SpreadJsObj.refreshColumnVisible(gatherLeafXmjSheet);
         loadGatherLeafXmjData();
     });
+    $('[name=gather-anc-gcl]').change(function() {
+        const checkOption = $('[name=gather-anc-gcl]:checked');
+        if (checkOption.length === 0) {
+            toastr.warning('请至少选择一个汇总条件');
+            return;
+        }
+
+        const fields = [];
+        for (const co of checkOption) {
+            fields.push(co.value);
+        }
+        for (const col of gatherAncGclSpreadSetting.cols) {
+            col.visible = col.fixed || fields.indexOf(col.field) >= 0;
+        }
+        gclGatherModel.reGatherAncillaryGcl(fields);
+        SpreadJsObj.refreshColumnVisible(gatherAncGclSheet);
+        loadGatherAncGclData();
+    });
 
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
@@ -343,6 +410,7 @@ $(document).ready(() => {
             gclSpread.refresh();
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $.divResizer({
@@ -353,6 +421,7 @@ $(document).ready(() => {
             $(".sp-wrap").height(bcontent-30);
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $.divResizer({
@@ -361,6 +430,7 @@ $(document).ready(() => {
             gclSpread.refresh();
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $('#exportExcel').click(function () {

+ 57 - 29
app/public/js/revise.js

@@ -915,7 +915,6 @@ $(document).ready(() => {
                     info.cancel = posRange && posRange.length > 0;
                     break;
                 case 'unit_price':
-                    console.log(node);
                     info.cancel = (node.children && node.children.length > 0) || node.used || node.settle_status === settleStatus.finish;
                     break;
                 case 'sgfh_qty':
@@ -1021,7 +1020,31 @@ $(document).ready(() => {
                     billsTreeSpreadObj.refreshTree(sheet, refreshNode);
                 })
             }
-        }
+        },
+        batchReplace: function(orgInfo, newInfo) {
+            const updateData = [];
+            for (const d of billsTree.nodes) {
+                if (!d.b_code) continue;
+                if (d.used || d.settle_status === settleStatus.finish) continue;
+                const checkInfo = { code: d.b_code || '', name: d.name || '', unit: d.unit || '', unit_price: d.unit_price || 0 };
+                if (_.isMatch(checkInfo, orgInfo)) {
+                    const data = billsTree.getNodeKeyData(d);
+                    data.b_code = newInfo.code;
+                    data.name = newInfo.name;
+                    data.unit = newInfo.unit;
+                    data.unit_price = newInfo.unit_price;
+                    updateData.push(data);
+                }
+            }
+            if (updateData.length === 0) {
+                toastr.warning('没有可以替换的数据');
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update', postData: updateData}, function (result) {
+                const refreshNode = billsTree.loadPostData(result);
+                billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+            });
+        },
     };
     billsTreeSpreadObj.refreshOperationValid(billsSheet);
     billsTreeSpreadObj.loadExprToInput(billsSheet);
@@ -1247,6 +1270,23 @@ $(document).ready(() => {
                 $('#batch').modal('show');
             }
         };
+        billsContextMenuOptions.items.batchReplace = {
+            name: '批量替换清单',
+            icon: 'fa-sign-in',
+            callback: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(billsSheet);
+                if (!node) return;
+
+                BatchReplace.load(node.b_code, node.name, node.unit, node.unit_price, billsTreeSpreadObj.batchReplace);
+            },
+            disabled: function (key, opt) {
+                const node = SpreadJsObj.getSelectObject(billsSheet);
+                return !node || !node.b_code;
+            },
+            visible: function (key, opt) {
+                return !readOnly;
+            }
+        };
         billsContextMenuOptions.items.sprBatch = '----';
     }
     billsContextMenuOptions.items.copyBlock = {
@@ -2704,19 +2744,13 @@ $(document).ready(() => {
     billsSpread.refresh();
     if (posSpread) posSpread.refresh();
 
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
-        const mainSheet = billsSheet;
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
-
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
-        const mainTree = mainSheet.zh_tree;
-        const sel = mainSheet.getSelections()[0];
-        const mainNode = mainTree.nodes[sel.row];
+    const stdLibCellDoubleClick = function (updateData, stdNode, stdTree) {
         if (!stdNode) return;
+        const mainTree = billsTree;
+        const mainNode = SpreadJsObj.getSelectObject(billsSheet);
+        if (!mainNode) return;
 
-        if (info.sheet.zh_setting.stdType === 'gcl') {
+        if (updateData.postData.stdType === 'gcl') {
             if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
                 toastr.warning('非最底层项目下,不应添加节点');
                 return;
@@ -2736,31 +2770,25 @@ $(document).ready(() => {
             }
         }
 
-        postData(window.location.pathname + '/update', {
-            postType: 'add-std',
-            postData: {
-                id: mainTree.getNodeKey(mainNode),
-                tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
-                stdLibId: stdNode.list_id,
-                stdNode: stdTree.getNodeKey(stdNode)
-            }
-        }, function (result) {
+        updateData.postData.id = billsTree.getNodeKey(mainNode);
+        updateData.postData.tender_id = mainNode.tender_id;
+        postData(window.location.pathname + '/update', updateData, function (result) {
             const refreshNode = mainTree.loadPostData(result);
-            billsTreeSpreadObj.refreshTree(mainSheet, refreshNode);
+            billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+            const sel = billsSheet.getSelections()[0];
             if (sel) {
                 if (refreshNode.create && refreshNode.create.length > 0) {
-                    mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
-                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
+                    billsSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(billsSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
                 } else {
                     const node = _.find(mainTree.nodes, {code: stdNode.code, name: stdNode.name});
                     if (node) {
-                        mainSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                        SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(node)]);
+                        billsSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                        SpreadJsObj.reloadRowsBackColor(billsSheet, [sel.row, mainTree.nodes.indexOf(node)]);
                     }
                 }
             }
-            billsTreeSpreadObj.refreshOperationValid(mainSheet);
+            billsTreeSpreadObj.refreshOperationValid(billsSheet);
             billsSpread.focus();
             posSpreadObj.loadCurPosData();
         });

+ 36 - 1
app/public/js/shares/cs_tools.js

@@ -1312,7 +1312,9 @@ const showSelectTab = function(select, spread, afterShow) {
             searchPre: `std-${setting.stdType}-sp`,
             searchNext: `std-${setting.stdType}-sn`,
             searchClose: `std-${setting.stdType}-sc`,
+            addParent: `std-${setting.stdType}-wp`
         };
+        const hiddenOption = setting.showAddType ? '' : 'style="display: none;"';
         obj.html(
             '<div class="sjs-bar d-flex">\n' +
             '    <div class="dropdown mr-1">\n' +
@@ -1351,6 +1353,21 @@ const showSelectTab = function(select, spread, afterShow) {
             '            </div>\n' +
             '        </div>\n' +
             '    </div>' +
+            `    <div class="ml-1" ${hiddenOption}>\n` +
+            '        <div class="dropdown">\n' +
+            '            <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+            '                <i class="fa fa-cog"></i>\n' +
+            '            </button>\n' +
+            '            <div class="dropdown-menu dropdown-menu-right">\n' +
+            '                <div class="px-2 border-primary border-1 d-flex" style="width: 100px">\n' +
+            '                     <div class="form-check form-check-inline">\n' +
+            `                          <input class="form-check-input pt-1" type="checkbox" id="${relaSelect.addParent}">\n` +
+            `                          <label class="form-check-label" for="${relaSelect.addParent}">添加父项</label>\n` +
+            '                     </div>' +
+            '                </div>\n' +
+            '            </div>\n' +
+            '        </div>\n' +
+            '    </div>' +
             '</div>\n' +
             `<div id="std-${setting.stdType}-spread" class="sjs-sh"></div>\n`
         );
@@ -1383,7 +1400,25 @@ const showSelectTab = function(select, spread, afterShow) {
         if (setting.spreadSetting) setting.spreadSetting.stdType = setting.stdType;
         SpreadJsObj.initSheet(sheet, setting.spreadSetting || spreadSetting);
 
-        if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, setting.cellDoubleClick);
+        if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, function(e, info) {
+            const stdSheet = info.sheet;
+            if (!stdSheet.zh_setting || !stdSheet.zh_tree) return;
+
+            const stdTree = stdSheet.zh_tree;
+            const stdNode = stdTree.nodes[info.row];
+            if (!stdNode) { return; }
+
+            const updateData = { postType: 'add-std', postData: { stdType: stdSheet.zh_setting.stdType, withParent: $(`#${relaSelect.addParent}`)[0].checked } };
+            const fullPath = stdTree.getAllParents(stdNode);
+            fullPath.push(stdNode);
+            fullPath.sort((x, y) => { return x.level - y.level; });
+            updateData.postData.stdData = fullPath.map(x => {
+                return { code: x.code || '', b_code: x.b_code || '', name: x.name, unit: x.unit, source: x.source, node_type: x.node_type }
+            });
+            updateData.postData.stdNode = stdNode;
+
+            setting.cellDoubleClick(updateData, stdNode, stdTree);
+        });
         sheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
                 SpreadJsObj.saveTopAndSelect(info.sheet, cacheKey.node);

+ 1 - 0
app/view/ledger/explode_modal.ejs

@@ -518,3 +518,4 @@
 <% include ../shares/db2full_code.ejs %>
 <% include ./audit_modal.ejs %>
 <% include ../shares/hint_modal.ejs %>
+<% include ../shares/batch_replace_modal.ejs %>

+ 31 - 1
app/view/ledger/gather.ejs

@@ -32,6 +32,9 @@
                             <li class="nav-item">
                                 <a class="nav-link" data-toggle="tab" href="#xmjGather" role="tab">项目节汇总</a>
                             </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#ancGclGather" role="tab">附属工程量汇总</a>
+                            </li>
                             <li class="nav-item" id="optionTab">
                                 <div rela-tab="#xmjGather" style="display: none;">
                                     <div class="d-inline-block ml-2 mt-1">
@@ -60,6 +63,29 @@
                                         </div>
                                     </div>
                                 </div>
+                                <div rela-tab="#ancGclGather" style="display: none;">
+                                    <div class="d-inline-block ml-2 mt-1">
+                                        <span>汇总条件:</span>
+                                        <div class="d-inline-block" style="vertical-align: middle">
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input pt-1" type="checkbox" id="gather-anc-gcl-dwgc" value="dwgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-dwgc">单位工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-fbgc" value="fbgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-fbgc">分部工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-fxgc" value="fxgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-fxgc">分项工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-xm" value="jldy" checked name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-xm">细目</label>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
                             </li>
                         </ul>
                     </div>
@@ -68,10 +94,14 @@
                             <div class="sp-wrap" id="leaf-xmj-spread">
                             </div>
                         </div>
-                        <div class="tab-pane active" id="xmjGather">
+                        <div class="tab-pane" id="xmjGather">
                             <div class="sp-wrap" id="leaf-xmj-gather-spread">
                             </div>
                         </div>
+                        <div class="tab-pane" id="ancGclGather">
+                            <div class="sp-wrap" id="anc-gcl-gather-spread">
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>

+ 2 - 1
app/view/revise/info_modal.ejs

@@ -740,13 +740,14 @@
             </div>
         </div>
     </div>
+<% include ../shares/batch_replace_modal.ejs %>
     <% include ../shares/merge_peg_modal.ejs %>
     <% include ../shares/import_excel_modal.ejs %>
     <% include ../shares/delete_hint_modal.ejs %>
     <% include ../shares/check_data_modal.ejs %>
     <% include ../shares/check_modal2.ejs %>
 <% include ../shares/new_tag_modal.ejs %>
-    <% include ../shares/tender_select_modal.ejs %>
+    <% include ../shares/tender_select_modal.ejs %>';
     <% if(ctx.session.sessionUser.accountId === revise.uid && (revise.status === auditConst.status.uncheck || revise.status === auditConst.status.checkNo)) { %>
     <script>
         const cur_uid = '<%- ctx.session.sessionUser.accountId %>';

+ 60 - 0
app/view/shares/batch_replace_modal.ejs

@@ -0,0 +1,60 @@
+<div class="modal fade" id="batch-replace" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量替换清单</h5>
+            </div>
+            <div class="modal-body">
+                原清单:
+                <table class="table table-bordered">
+                    <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
+                    <tr><td id="br-org-code"></td><td id="br-org-name"></td><td id="br-org-unit"></td><td id="br-org-up">单价</td></tr>
+                </table>
+
+                替换为:
+                <table class="table table-bordered">
+                    <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
+                    <tr>
+                        <td><input id="br-new-code" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-name" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-unit" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-up" type="text" class="form-control form-control-sm"></td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <a href="javascript: void(0);" class="btn btn-sm btn-primary" id="batch-replace-ok">确定</a>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const BatchReplace = (function() {
+        const orgValue = { code: '', name: '', unit: '', unit_price: 0 };
+        let doReplace;
+        const load = function(code, name, unit, unit_price, callback) {
+            orgValue.code = code || '';
+            orgValue.name = name || '';
+            orgValue.unit = unit || '';
+            orgValue.unit_price = unit_price || 0;
+            $('#br-org-code').html(orgValue.code);
+            $('#br-org-name').html(orgValue.name);
+            $('#br-org-unit').html(orgValue.unit);
+            $('#br-org-up').html(orgValue.unit_price || '');
+            $('#br-new-code').val(orgValue.code);
+            $('#br-new-name').val(orgValue.name);
+            $('#br-new-unit').val(orgValue.unit);
+            $('#br-new-up').val(orgValue.unit_price || '');
+            doReplace = callback;
+            $('#batch-replace').modal('show');
+        }
+        $('#batch-replace-ok').click(function() {
+            if (doReplace) {
+                const newValue = { code: $('#br-new-code').val(), name: $('#br-new-name').val(), unit: $('#br-new-unit').val(), unit_price: parseFloat($('#br-new-up').val()), };
+                doReplace(orgValue, newValue);
+            }
+            $('#batch-replace').modal('hide');
+        });
+        return { load };
+    })();
+</script>