Browse Source

台账分解,检查计算相关

MaiXinRong 5 years ago
parent
commit
f844bee3b0

+ 2 - 3
app/controller/ledger_audit_controller.js

@@ -203,11 +203,10 @@ module.exports = app => {
 
 
                 await ctx.service.ledgerAudit.start(ctx.tender.id, ctx.tender.data.ledger_times);
                 await ctx.service.ledgerAudit.start(ctx.tender.id, ctx.tender.data.ledger_times);
 
 
-                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+                ctx.body = {err: 0, msg: '', data: {url: '/tender/' + ctx.tender.id + '/ledger'}}; //ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
             } catch (err) {
             } catch (err) {
                 this.log(err);
                 this.log(err);
-                ctx.session.postError = err.toString();
-                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+                ctx.body = this.ajaxErrorBody(err, '上报失败,请刷新页面重试');
             }
             }
         }
         }
 
 

+ 3 - 3
app/controller/stage_controller.js

@@ -190,7 +190,7 @@ module.exports = app => {
             }
             }
         }
         }
 
 
-        _checkUniaData(data, field) {
+        _checkUniqData(data, field) {
             const groupData = this.ctx.helper._.groupBy(data, field);
             const groupData = this.ctx.helper._.groupBy(data, field);
             const surplus = [];
             const surplus = [];
             for (const gd in groupData) {
             for (const gd in groupData) {
@@ -221,7 +221,7 @@ module.exports = app => {
                 curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
                 curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
             } else {
             } else {
                 curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id);
                 curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id);
-                const surplus = this._checkUniaData(curStageData, 'lid');
+                const surplus = this._checkUniqData(curStageData, 'lid');
                 if (surplus.length > 0) {
                 if (surplus.length > 0) {
                     await ctx.service.stageBills.deleteById(surplus);
                     await ctx.service.stageBills.deleteById(surplus);
                 }
                 }
@@ -250,7 +250,7 @@ module.exports = app => {
             } else {
             } else {
                 curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id);
                 curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id);
 
 
-                const surplus = this._checkUniaData(curStageData, 'pid');
+                const surplus = this._checkUniqData(curStageData, 'pid');
                 if (surplus.length > 0) {
                 if (surplus.length > 0) {
                     await ctx.service.stagePos.deleteById(surplus);
                     await ctx.service.stagePos.deleteById(surplus);
                 }
                 }

+ 11 - 11
app/public/css/spreadjs/sheets/gc.spread.sheets.excelsmartcost.css

@@ -4,7 +4,7 @@
     z-index: 2013;
     z-index: 2013;
 }
 }
 .gc-grayArea {
 .gc-grayArea {
-    background-color: white;
+    background-color: #f1f1f1;
 }
 }
 .gc-corner-hover {
 .gc-corner-hover {
     background-color: white;
     background-color: white;
@@ -59,9 +59,9 @@
     background-image: none;
     background-image: none;
     background-color:#e9ecef;
     background-color:#e9ecef;
     border-style:solid;
     border-style:solid;
-    border-left-color: #cccccc !important;
-    border-right-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
+    border-left-color: #dee2e6 !important;
+    border-right-color: #dee2e6 !important;
+    border-bottom-color: #dee2e6 !important;
 }
 }
 .gc-columnHeader-hover {
 .gc-columnHeader-hover {
     color: #444444;
     color: #444444;
@@ -95,9 +95,9 @@
     background-color: #e9ecef;
     background-color: #e9ecef;
     background-image: none;
     background-image: none;
     border-style:solid;
     border-style:solid;
-    border-top-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
-    border-right-color: #cccccc !important;
+    border-top-color: #efefef !important;
+    border-bottom-color: #d5ded5 !important;
+    border-right-color: #dee2e6 !important;
 }
 }
 .gc-rowHeader-hover {
 .gc-rowHeader-hover {
     color: #73a2e3;
     color: #73a2e3;
@@ -113,9 +113,9 @@
     background-color: #dddfe1;
     background-color: #dddfe1;
     background-image: none;
     background-image: none;
     border-style:solid;
     border-style:solid;
-    border-top-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
-    border-right-color: #cccccc !important;
+    border-top-color: #dee2e6 !important;
+    border-bottom-color: #dee2e6 !important;
+    border-right-color: #dee2e6 !important;
 }
 }
 .gc-rowHeader-highlight {
 .gc-rowHeader-highlight {
     color: #73a2e3;
     color: #73a2e3;
@@ -145,7 +145,7 @@
     border-color: #217346;
     border-color: #217346;
 }
 }
 .gc-gridlineColor {
 .gc-gridlineColor {
-    border-color: #cccccc;
+    border-color: #dee2e6;
 }
 }
 .gc-group {
 .gc-group {
     background-color: white;
     background-color: white;

+ 102 - 25
app/public/js/ledger.js

@@ -56,6 +56,45 @@ $(document).ready(function() {
     });
     });
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
 
 
+
+    const showSideTools = function (show) {
+        const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+        if (show) {
+            right.show();
+            autoFlashHeight();
+            /**
+             * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+             * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+             * 故需要通过最终的parent.width再计算一次left.width
+             *
+             * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+             * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+             *
+             */
+                //left.css('width', parent.width() - right.outerWidth());
+                //left.css('width', parent.width() - right.outerWidth());
+            const percent = 100 - right.outerWidth() /parent.width() * 100;
+            left.css('width', percent + '%');
+        } else {
+            left.width(parent.width());
+            right.hide();
+        }
+    };
+    const errorList = $.cs_errorList({
+        tabSelector: '#error-list-tab',
+        selector: '#error-list',
+        relaSpread: ledgerSpread,
+        storeKey: 'ledger-error-' + getTenderId(),
+        afterLocated:  function () {
+            posOperationObj.loadCurPosData();
+        },
+        afterShow: function () {
+            ledgerSpread.refresh();
+            if (posSpread) posSpread.refresh();
+        },
+        showSideTools: showSideTools,
+    });
+
     $.subMenu({
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -86,6 +125,9 @@ $(document).ready(function() {
             if (searchLedger) {
             if (searchLedger) {
                 searchLedger.spread.refresh();
                 searchLedger.spread.refresh();
             }
             }
+            if (errorList) {
+                errorList.spread.refresh();
+            }
         }
         }
     });
     });
 
 
@@ -1882,29 +1924,6 @@ $(document).ready(function() {
     $('a', '#side-menu').bind('click', function (e) {
     $('a', '#side-menu').bind('click', function (e) {
         e.preventDefault();
         e.preventDefault();
         const tab = $(this), tabPanel = $(tab.attr('content'));
         const tab = $(this), tabPanel = $(tab.attr('content'));
-        const showSideTools = function (show) {
-            const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
-            if (show) {
-                right.show();
-                autoFlashHeight();
-                /**
-                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
-                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
-                 * 故需要通过最终的parent.width再计算一次left.width
-                 *
-                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
-                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
-                 *
-                 */
-                //left.css('width', parent.width() - right.outerWidth());
-                //left.css('width', parent.width() - right.outerWidth());
-                const percent = 100 - right.outerWidth() /parent.width() * 100;
-                left.css('width', percent + '%');
-            } else {
-                left.width(parent.width());
-                right.hide();
-            }
-        };
         // 展开工具栏、切换标签
         // 展开工具栏、切换标签
         if (!tab.hasClass('active')) {
         if (!tab.hasClass('active')) {
             const close = $('.active', '#side-menu').length === 0;
             const close = $('.active', '#side-menu').length === 0;
@@ -1948,7 +1967,7 @@ $(document).ready(function() {
                     dealBills.loadData();
                     dealBills.loadData();
                 }
                 }
                 dealBills.spread.refresh();
                 dealBills.spread.refresh();
-            } else if (tab.attr('content') === '#search' && !searchLedger) {
+            } else if (tab.attr('content') === '#search') {
                 if (!searchLedger) {
                 if (!searchLedger) {
                     searchLedger = $.billsSearch({
                     searchLedger = $.billsSearch({
                         selector: '#search',
                         selector: '#search',
@@ -1976,6 +1995,8 @@ $(document).ready(function() {
                     });
                     });
                 }
                 }
                 searchLedger.spread.refresh();
                 searchLedger.spread.refresh();
+            } else if (tab.attr('content') === '#error-list') {
+                errorList.spread.refresh();
             }
             }
         } else { // 收起工具栏
         } else { // 收起工具栏
             tab.removeClass('active');
             tab.removeClass('active');
@@ -2581,6 +2602,60 @@ $(document).ready(function() {
 
 
         SpreadExcelObj.exportSimpleXlsxSheet(setting, data, "台账分解.xlsx");
         SpreadExcelObj.exportSimpleXlsxSheet(setting, data, "台账分解.xlsx");
     });
     });
+
+    const dataChecker = DataChecker({
+        loadUrl: window.location.pathname + '/load',
+        loadData: {},
+        checkFun: function (data, progress) {
+            ledgerTree.loadDatas(data.bills);
+            treeCalc.calculateAll(ledgerTree);
+            SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
+
+            pos.loadDatas(data.pos);
+            posOperationObj.loadCurPosData();
+
+            const checkFields = ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'], result = [];
+            const iLen = data.bills.length;
+            for (const [i, b] of data.bills.entries()) {
+                const pr = _.filter(data.pos, {lid: b.id});
+                if (pr && pr.length > 0) {
+                    const checkData = {}, calcData = {};
+                    for (const field of checkFields) {
+                        checkData[field] = b[field] ? b[field] : 0;
+                    }
+                    for (p of pr) {
+                        for (const field of checkFields) {
+                            calcData[field] = ZhCalc.add(calcData[field], p[field]);
+                        }
+                    }
+                    for (const field of checkFields) {
+                        calcData[field] = calcData[field] ? calcData[field] : 0;
+                    }
+                    if (!_.isMatch(checkData, calcData)) {
+                        result.push({
+                            ledger_id: b.ledger_id,
+                            b_code: b.b_code,
+                            name: b.name,
+                            serialNo: ledgerTree.getNodeIndex(ledgerTree.getItems(b.ledger_id)) + 1,
+                            error: {checkData: checkData, calcData: calcData}
+                        })
+                    }
+                    progress(parseInt((i+1)/iLen*100));
+                }
+            }
+            return result;
+        },
+        errorList: errorList,
+    });
+    $('[name=audit-start]').submit(function (e) {
+        if (checkAuditorFrom()) {
+            $(this).parent().parent().parent().modal('hide');
+            const formData = new FormData();
+            dataChecker.checkAndPost(this.action, formData);
+            $('#hide-all').hide();
+        }
+        return false;
+    });
 });
 });
 
 
 // 检查上报情况
 // 检查上报情况
@@ -2588,7 +2663,9 @@ function checkAuditorFrom () {
     if ($('#auditors li').length === 0) {
     if ($('#auditors li').length === 0) {
         toastr.error('请先选择审批人,再上报数据');
         toastr.error('请先选择审批人,再上报数据');
         return false;
         return false;
+    } else {
+        $('#hide-all').show();
+        return true;
     }
     }
-    $('#hide-all').show();
 }
 }
 
 

+ 95 - 0
app/public/js/shares/cs_tools.js

@@ -0,0 +1,95 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+(function($){
+    /**
+     *
+     * @param setting
+     * {
+     *      tabSelector: 'a[content=#error-list]',
+     *      selector: '#error-list',
+     *      relaSpread: ledgerSpread,
+     *      storeKey: 'ledger-error-' + tenderId,
+     * }
+     * @returns {{spread: *}}
+     */
+    $.cs_errorList = function (setting) {
+        if (!setting.selector || !setting.relaSpread) return;
+        if (!setting.spreadSetting) {
+            setting.spreadSetting = {
+                cols: [
+                    {title: '行号', field: 'serialNo', width: 80, formatter: '@'},
+                    {title: '清单编号', field: 'b_code', width: 150, formatter: '@'},
+                    {title: '清单名称', field: 'name', width: 230, formatter: '@'},
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                selectedBackColor: '#fffacd',
+                readOnly: true,
+            };
+        }
+
+        const resultId = setting.id + '-spread';
+        const obj = $(setting.selector);
+        obj.html(
+            '                        <div id="' + resultId + '" class="sjs-sh">\n' +
+            '                        </div>'
+        );
+        autoFlashHeight();
+
+        const spread = SpreadJsObj.createNewSpread($('#' + resultId)[0]);
+        const sheet = spread.getActiveSheet();
+        SpreadJsObj.initSheet(sheet, setting.spreadSetting);
+        SpreadJsObj.forbiddenSpreadContextMenu('#' + resultId, spread);
+
+        spread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) {
+            const sheet = info.sheet;
+            const data = sheet.zh_data;
+            if (!data) { return }
+
+            const curBills = data[info.row];
+            if (!curBills) { return }
+
+            SpreadJsObj.locateTreeNode(setting.relaSpread.getActiveSheet(), curBills.ledger_id, true);
+            console.log(curBills);
+            if (setting.afterLocated) {
+                setting.afterLocated();
+            }
+        });
+
+        const loadErrorData = function (data, his = false) {
+            SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Data, data);
+            if (!his && setting.storeKey) {
+                setLocalCache(setting.storeKey, JSON.stringify(data));
+            }
+            $(setting.tabSelector).show();
+        };
+        if (setting.storeKey) {
+            const storeStr = getLocalCache(setting.storeKey);
+            const storeData = storeStr ? JSON.parse(storeStr) : [];
+            if (storeData.length > 0) loadErrorData(storeData, true);
+        }
+        const showErrorList = function () {
+            const tab = $(setting.tabSelector), tabPanel = $(tab.attr('content'));
+            $('a', '#side-menu').removeClass('active');
+            tab.addClass('active');
+            $('.tab-content .tab-pane').removeClass('active');
+            tabPanel.addClass('active');
+            setting.showSideTools(true);
+            spread.refresh();
+            if (setting.afterShow) setting.afterShow();
+        };
+        return {spread: spread, loadErrorData: loadErrorData, show: showErrorList};
+    };
+})(jQuery);

+ 0 - 16
app/service/stage.js

@@ -509,22 +509,6 @@ module.exports = app => {
             }
             }
             return list;
             return list;
         }
         }
-
-        async deleteInvalidData(sid) {
-            const sqlParam = [sid];
-            const billsSql = 'DELETE FROM ' + this.ctx.service.stageBills.tableName +
-                '  WHERE (ISNULL(`contract_qty`) or `contract_qty` = 0) and ' +
-                '    (ISNULL(`contract_tp`) or `contract_tp` = 0) and ' +
-                '    (ISNULL(`qc_qty`) or `qc_qty` = 0) and ' +
-                '    (ISNULL(`qc_tp`) or `qc_tp` = 0) and ' +
-                '    (ISNULL(`postil`) or `postil` = \'\') and sid = ?';
-            await this.db.query(billsSql, sqlParam);
-            const posSql = 'DELETE FROM ' + this.ctx.service.stagePos.tableName +
-                '  WHERE (ISNULL(`contract_qty`) or `contract_qty` = 0) and' +
-                '    (qc_qty = 0 or ISNULL(qc_qty)) and' +
-                '    (ISNULL(`postil`) or `postil` = \'\') and sid = ?';
-            await this.db.query(posSql, sqlParam);
-        }
     }
     }
 
 
     return Stage;
     return Stage;

+ 5 - 0
app/view/ledger/explode.ejs

@@ -138,6 +138,8 @@
                         <div id="deal-bills-spread" class="sjs-sh-4">
                         <div id="deal-bills-spread" class="sjs-sh-4">
                         </div>
                         </div>
                     </div>
                     </div>
+                    <div id="error-list" class="tab-pane">
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
@@ -156,6 +158,9 @@
                 <li class="nav-item">
                 <li class="nav-item">
                     <a class="nav-link" content="#deal-bills" href="javascript: void(0);">签约清单</a>
                     <a class="nav-link" content="#deal-bills" href="javascript: void(0);">签约清单</a>
                 </li>
                 </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#error-list" id="error-list-tab" href="javascript: void(0);" style="display: none;">错误列表</a>
+                </li>
             </ul>
             </ul>
         </div>
         </div>
     </div>
     </div>

+ 4 - 3
app/view/ledger/explode_modal.ejs

@@ -129,10 +129,10 @@
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
-            <form class="modal-footer" method="post" action="/tender/<%- tender.id %>/ledger/audit/start" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" method="post" action="/tender/<%- tender.id %>/ledger/audit/start" name="audit-start">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
-                <button class="btn btn-primary btn-sm" type="submit">确认上报</button>
+                <button class="btn btn-primary btn-sm" type="submit" >确认上报</button>
             </form>
             </form>
         </div>
         </div>
     </div>
     </div>
@@ -257,7 +257,7 @@
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
-            <form class="modal-footer" action="/tender/<%- tender.id %>/ledger/audit/start" method="post" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" action="/tender/<%- tender.id %>/ledger/audit/start" method="post" name="audit-start">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <button type="submit" class="btn btn-primary btn-sm">确认上报</button>
                 <button type="submit" class="btn btn-primary btn-sm">确认上报</button>
@@ -412,3 +412,4 @@
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs %>
 <% include ../shares/delete_hint_modal.ejs %>
 <% include ../shares/delete_hint_modal.ejs %>
+<% include ../shares/check_data_modal.ejs %>

+ 86 - 0
app/view/shares/check_data_modal.ejs

@@ -0,0 +1,86 @@
+<!--上报审批 自检-->
+<div class="modal fade" id="check" 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">
+                <h5>数据计算中,完成后会自动进入审批流程设置。</h5>
+                <div class="progress">
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<!--上报审批 自检 计算错误-->
+<div class="modal fade" id="check-error-hint" 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">
+                <div class="alert alert-danger" role="alert">
+                    部分清单存在问题,请前往错误列表进行查看,并进行修改。
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-primary" data-dismiss="modal" id="show-error-list">查看错误列表</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    /**
+     *
+     * @param setting
+     * {
+     *      loadUrl,
+     *      loadData,
+     *      checkFun,
+     *      errorList
+     * }
+     */
+    const DataChecker = function (setting) {
+        $('#show-error-list').click(function () {
+            $('#check').modal('hide');
+            setting.errorList.show();
+        });
+        const loadCheckData = function () {
+            const promise = new Promise(function (resolve, reject) {
+                postData(setting.loadUrl, setting.loadData, function (result) {
+                    resolve(result);
+                });
+            });
+            return promise;
+        }
+        const progress = function (percent) {
+            $('.progress-bar').attr('aria-valuenow', percent).width(percent + '%').html(percent + '%');
+        }
+        const addProgress = function (percent) {
+            const oldPercent = parseInt($('.progress-bar').attr('aria-valuenow'));
+            progress(oldPercent + percent);
+        }
+        const checkAndPost = async function (postUrl, postForm) {
+            progress(0);
+            $('#check').modal('show');
+            const lastestData = await loadCheckData();
+            progress(50);
+            const result = setting.checkFun(lastestData, addProgress);
+            progress(100);
+            setTimeout(function () {
+                if (result && result.length > 0) {
+                    $('#check-error-hint').modal('show');
+                    setting.errorList.loadErrorData(result);
+                } else {
+                    postDataWithFile(postUrl, postForm, function (data) {
+                        if (data.url) window.location.href = data.url;
+                    });
+                }
+            }, 1000);
+        }
+        return {checkAndPost};
+    }
+</script>

+ 2 - 0
config/web.js

@@ -136,6 +136,7 @@ const JsFiles = {
                     "/public/js/math.min.js",
                     "/public/js/math.min.js",
                     "/public/js/file-saver/FileSaver.js",
                     "/public/js/file-saver/FileSaver.js",
                     "/public/js/shares/export_excel.js",
                     "/public/js/shares/export_excel.js",
+                    "/public/js/shares/cs_tools.js",
                 ],
                 ],
                 mergeFiles: [
                 mergeFiles: [
                     "/public/js/sub_menu.js",
                     "/public/js/sub_menu.js",
@@ -156,6 +157,7 @@ const JsFiles = {
                 files: [
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
                     "/public/js/decimal.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/shares/cs_tools.js",
                 ],
                 ],
                 mergeFiles: [
                 mergeFiles: [
                     "/public/js/sub_menu.js",
                     "/public/js/sub_menu.js",