Преглед изворни кода

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

Tony Kang пре 2 недеља
родитељ
комит
4a1abcca59

+ 51 - 0
app/base/base_tree_service.js

@@ -509,6 +509,57 @@ class TreeService extends Service {
         }
     }
 
+    async addChildBatch(mid, kid, data, count = 1) {
+        if (!mid || !kid) return null;
+        const select = await this.getDataByKid(mid, kid);
+        if (!select) throw '新增节点数据错误';
+        const children = await this.getChildrenByParentId(mid, kid);
+        const maxId = await this._getMaxLid(mid);
+
+        this.transaction = await this.db.beginTransaction();
+        try {
+            const newDatas = [];
+            for (let i = 1; i < count + 1; i++) {
+                const newData = Object.assign({}, data);
+                if (this.setting.uuid) newData.id = this.uuid.v4();
+                newData[this.setting.kid] = maxId + i;
+                newData[this.setting.pid] = select[this.setting.kid];
+                newData[this.setting.mid] = mid;
+                newData[this.setting.level] = select[this.setting.level] + 1;
+                newData[this.setting.order] = children.length > 0 ? children[children.length - 1].order + i : i;
+                newData[this.setting.fullPath] = select[this.setting.fullPath] + '-' + newData[this.setting.kid];
+                newData[this.setting.isLeaf] = true;
+                newDatas.push(newData);
+            }
+            const insertResult = await this.transaction.insert(this.tableName, newDatas);
+            this._cacheMaxLid(mid, maxId + count);
+
+            if (select[this.setting.isLeaf] || children.length === 0) {
+                const updateData = { id: select.id };
+                updateData[this.setting.isLeaf] = false;
+                this.clearParentingData(updateData);
+                await this.transaction.update(this.tableName, updateData);
+            }
+
+            if (insertResult.affectedRows !== count) throw '新增节点数据错误';
+            await this.transaction.commit();
+            this.transaction = null;
+        } catch (err) {
+            await this.transaction.rollback();
+            this.transaction = null;
+            throw err;
+        }
+
+        if (select[this.setting.isLeaf] || children.length === 0) {
+            const createData = await this.getDataByKidAndCount(mid, maxId + 1, count);
+            const updateData = await this.getDataByKid(mid, select[this.setting.id]);
+            return {create: createData, update: updateData};
+        } else {
+            const createData = await this.getDataByKidAndCount(mid, maxId + 1, count);
+            return {create: createData};
+        }
+    }
+
     /**
      * 删除相关数据 用于继承
      * @param mid

+ 1 - 1
app/const/account_permission.js

@@ -54,7 +54,7 @@ const permission = {
             { title: '批量设置材差清单', value: 1, hint: '开启该选项,当前账号可设置允许调差的清单', hintIcon: 'fa-question-circle' },
             { title: '修改调差工料消耗量', value: 2, hint: '开启该选项,可在新材差期修改工料的消耗量', hintIcon: 'fa-question-circle' },
             // { title: '修改材料税税率', value: 3, hint: '开启该选项,可在新材差期修改材料税税率', hintIcon: 'fa-question-circle' },
-            { title: '修改调差数量', value: 4, hint: '开启该选项,可在调差清单页修改本期调差数量', hintIcon: 'fa-question-circle' },
+            // { title: '修改调差数量', value: 4, hint: '开启该选项,可在调差清单页修改本期调差数量', hintIcon: 'fa-question-circle' },
         ],
     },
     other: {

+ 1 - 0
app/const/sp_page_show.js

@@ -97,6 +97,7 @@ const defaultSetting = {
     openPayment: 1,
     openConstruction: 1,
     openMaterialStageRepeat: 0,
+    openMaterialListQty: 0,
     openContract: 1,
     openFinancial: 1,
     openTenderContract: 1,

+ 10 - 4
app/controller/ledger_controller.js

@@ -52,7 +52,7 @@ module.exports = app => {
          */
         _ledgerReadOnly() {
             const tender = this.ctx.tender.data;
-            return tender.user_id !== this.ctx.session.sessionUser.accountId ||
+            return (tender.user_id !== this.ctx.session.sessionUser.accountId && !this.ctx.tender.isAssUser) ||
                 (tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked);
         }
 
@@ -130,6 +130,8 @@ module.exports = app => {
                 const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
                 tender.data.hasStage = !!stage;
                 const posCalcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(ctx.subProject.id, 'posCalc');
+                tender.data.isAssUser = tender.isAssUser;
+                tender.data.assLedger = tender.assLedger;
                 const renderData = {
                     tender: tender.data,
                     tenderInfo: tender.info,
@@ -173,7 +175,8 @@ module.exports = app => {
 
                 await this.layout('ledger/explode.ejs', renderData, 'ledger/explode_modal.ejs');
             } catch (err) {
-                ctx.helper.log(err);
+                console.log(err);
+                ctx.log(err);
                 this.postError(err, '标段数据错误');
                 // await this.redirect('/dashboard');
                 ctx.redirect(ctx.request.header.referer);
@@ -212,6 +215,7 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async _base(ctx, type, data) {
+            console.log(1);
             if (isNaN(data.id) || data.id <= 0) throw '数据错误';
             if (type !== 'add') {
                 if (isNaN(data.count) || data.count <= 0) data.count = 1;
@@ -219,6 +223,8 @@ module.exports = app => {
             switch (type) {
                 case 'add':
                     return await ctx.service.ledger.addNodeBatch(ctx.tender.id, data.id, {}, data.count);
+                case 'add-child':
+                    return await ctx.service.ledger.addChildBatch(ctx.tender.id, data.id, {}, data.count);
                 case 'delete':
                     return await ctx.service.ledger.delete(ctx.tender.id, data.id, data.count);
                 case 'up-move':
@@ -310,7 +316,7 @@ module.exports = app => {
         async update(ctx) {
             try {
                 if (!ctx.tender.data) throw '标段数据错误';
-                if (ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) throw '您无权进行该操作';
+                if (this._ledgerReadOnly()) throw '您无权进行该操作';
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.postType || !data.postData) throw '数据错误';
 
@@ -321,6 +327,7 @@ module.exports = app => {
                 };
                 switch (data.postType) {
                     case 'add':
+                    case 'add-child':
                     case 'delete':
                     case 'up-move':
                     case 'down-move':
@@ -657,7 +664,6 @@ module.exports = app => {
         //         }
         //         ctx.body = responseData;
         //     } catch (err) {
-        //         console.log(err);
         //         this.log(err);
         //         // 失败需要消耗掉stream 以防卡死
         //         if (stream) await sendToWormhole(stream);

+ 3 - 2
app/controller/material_controller.js

@@ -1740,8 +1740,9 @@ module.exports = app => {
         }
 
         async _setEditQtyPermission(ctx) {
-            const permission = ctx.session.sessionUser.permission;
-            ctx.material.editQtyPermission = permission && permission.material !== undefined && permission.material.indexOf('4') !== -1;
+            // const permission = ctx.session.sessionUser.permission;
+            // ctx.material.editQtyPermission = permission && permission.material !== undefined && permission.material.indexOf('4') !== -1;
+            ctx.material.editQtyPermission = !!ctx.subProject.page_show.openMaterialListQty;
         }
 
         async _setChecklistPermission(ctx) {

+ 1 - 0
app/controller/sub_proj_setting_controller.js

@@ -398,6 +398,7 @@ module.exports = app => {
                 this.ctx.subProject.page_show.openMaterialEditForAudit = data.openMaterialEditForAudit ? 1 : 0;
                 this.ctx.subProject.page_show.openStageStart = data.openStageStart ? 1 : 0;
                 this.ctx.subProject.page_show.openMaterialStageRepeat = data.openMaterialStageRepeat ? 1 : 0;
+                this.ctx.subProject.page_show.openMaterialListQty = data.openMaterialListQty ? 1 : 0;
                 this.ctx.subProject.page_show.openContractExpr = data.openContractExpr ? 1 : 0;
                 this.ctx.subProject.page_show.correctCalcContractTp = data.correctCalcContractTp ? 1 : 0;
                 this.ctx.subProject.page_show.close1stStageCheckDealParam = data.close1stStageCheckDealParam ? 1 : 0;

+ 28 - 0
app/controller/tender_controller.js

@@ -803,6 +803,34 @@ module.exports = app => {
             }
         }
 
+        checkAssPermissionByType(type) {
+            if (!type) return false;
+            if (type === 'ledger') return this.ctx.session.sessionUser.accountId === this.ctx.tender.data.user_id;
+            return false;
+        }
+        async loadAss(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!this.checkAssPermissionByType(data.type)) throw '您无权查看该协作数据';
+                const assData = await ctx.service.tenderAss.getAssData(ctx.tender.id, data.type, data.rela_id);
+                ctx.body = { err: 0, msg: '', data: assData };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '加载协作数据错误');
+            }
+        }
+        async saveAss(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!this.checkAssPermissionByType(data.type)) throw '您无权修改该协作数据';
+                await ctx.service.tenderAss.saveAssData(ctx.tender.id, data.type, data.rela_id, data.updateData);
+                ctx.body = {err: 0, msg: '', data: null};
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存协作数据错误');
+            }
+        }
+
         /**
          * 添加标段操作
          *

+ 6 - 1
app/middleware/tender_check.js

@@ -92,6 +92,11 @@ module.exports = options => {
             tender.isTourist = isTenderTourist !== null;
             // 游客权限
             tender.touristPermission = yield this.service.tenderTourist.getTouristPermission(isTenderTourist);
+            const ledgerAss = yield this.service.tenderAss.getUserAssData(tender.id, 'ledger', this.session.sessionUser.accountId);
+            if (ledgerAss && ledgerAss.ass_id.length > 0) {
+                tender.isAssUser = true;
+                tender.assLedger = ledgerAss.ass_id;
+            }
             // tender_permission表查看权限
             if (this.session.sessionUser.is_admin) {
                 tender.user_permission = this.service.tenderPermission.getAdminPermission();
@@ -111,7 +116,7 @@ module.exports = options => {
                 throw '您无权查看该项目';
             }
 
-            tender.ledgerReadOnly = this.session.sessionUser.accountId !== tender.data.user_id ||
+            tender.ledgerReadOnly = (this.session.sessionUser.accountId !== tender.data.user_id && !tender.isAssUser) ||
                 tender.data.ledger_status === auditConst.status.checking || tender.data.ledger_status === auditConst.status.checked;
             tender.advanceAuditorsId = advanceAuditorsId;
             tender.ledgerUsers = tender.ledger_status === auditConst.status.uncheck ? [tender.data.user_id] : [tender.data.user_id, ...auditorsId];

+ 4 - 21
app/middleware/uncheck_tender_check.js

@@ -23,7 +23,7 @@ module.exports = options => {
     return function* uncheckTenderCheck(next) {
         try {
             if (this.tender.data.ledger_status === auditConst.status.uncheck) {
-                if (this.tender.data.user_id !== this.session.sessionUser.accountId && !this.session.sessionUser.is_admin && this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) === -1 && !this.tender.isTourist) {
+                if (this.tender.data.user_id !== this.session.sessionUser.accountId && !this.session.sessionUser.is_admin && this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) === -1 && !this.tender.isTourist && !this.tender.assLedger) {
                     throw '您无权查看该项目';
                 } else if (this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) !== -1 && !this.session.sessionUser.is_admin && !this.tender.isTourist) {
                     throw '您无权查看该内容';
@@ -32,28 +32,11 @@ module.exports = options => {
             yield next;
         } catch (err) {
             // 输出错误到日志
-            if (err.stack) {
-                this.logger.error(err);
-            } else {
-                this.session.message = {
-                    type: messageType.ERROR,
-                    icon: 'exclamation-circle',
-                    message: err,
-                };
-                this.getLogger('fail').info(JSON.stringify({
-                    error: err,
-                    project: this.session.sessionProject,
-                    user: this.session.sessionUser,
-                    body: this.session.body,
-                }));
-            }
+            this.log(err);
             if (this.helper.isAjax(this.request)) {
-                if (err.stack) {
-                    this.body = {err: 4, msg: '标段数据未知错误', data: null};
-                } else {
-                    this.body = {err: 3, msg: err.toString(), data: null};
-                }
+                this.ajaxErrorBody(err, '查看标段数据错误');
             } else {
+                this.postError(err, '查看标段数据错误');
                 if (this.helper.isWap(this.request)) {
                     this.redirect('/wap/subproj');
                 } else {

+ 7 - 1
app/public/js/cost_tmpl.js

@@ -459,7 +459,7 @@ $(document).ready(() => {
         };
         const delTemplate = function(id){
             postData('save', {del: id, type: 'cost'}, function(result) {
-                $(`dd[templateId=${result.del}]`).remove();
+                $(`dd[templateId=${id}]`).remove();
                 const tIndex = templates.findIndex(x => { return x.id === id; });
                 templates.splice(tIndex, 1);
                 if (curTemplate.id === id) {
@@ -516,6 +516,12 @@ $(document).ready(() => {
         $(`.table-file[templateId=${templateId}]`).html(templateObj.getTemplateCaptionHtml(template));
     });
 
+    $('body').on('click', 'a[name=delTemplate]', function(e) {
+        e.stopPropagation();
+        const templateId = $(this).parents('.table-file').attr('templateId');
+        templateObj.delTemplate(templateId);
+    });
+
     $('#export').click(function() {
         detailObj.export();
     });

+ 80 - 29
app/public/js/ledger.js

@@ -669,18 +669,18 @@ $(document).ready(function() {
             const preNode = tree.getPreSiblingNode(first);
             const valid = !sheet.zh_setting.readOnly;
 
-            setObjEnable($('#insert'), valid && first && first.level > 1);
-            setObjEnable($('#delete'), valid && first && sameParent && !(first.level === 1 && first.node_type));
-            setObjEnable($('#up-move'), valid && first && sameParent && first.level > 1 && preNode);
-            setObjEnable($('#down-move'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
+            setObjEnable($('#insert'), valid && first && !first._invalid && first.level > 1);
+            setObjEnable($('#delete'), valid && first && !first._invalid && sameParent && !(first.level === 1 && first.node_type));
+            setObjEnable($('#up-move'), valid && first && !first._invalid  && sameParent && first.level > 1 && preNode);
+            setObjEnable($('#down-move'), valid && first && !first._invalid  && sameParent && first.level > 1 && !tree.isLastSibling(last));
             if (checkTzMeasureType()) {
                 const posRange = last ? pos.getLedgerPos(last.id) : [];
-                setObjEnable($('#up-level'), valid && first && sameParent && tree.getParent(first) && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
+                setObjEnable($('#up-level'), valid && first && !first._invalid  && sameParent && tree.getParent(first) && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
                 const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
-                setObjEnable($('#down-level'), valid && first && sameParent && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0));
+                setObjEnable($('#down-level'), valid && first && !first._invalid  && sameParent && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0));
             } else {
-                setObjEnable($('#up-level'), valid && first && sameParent && first.level > 2 && tree.getParent(first));
-                setObjEnable($('#down-level'), valid && first && sameParent && first.level > 1 && preNode);
+                setObjEnable($('#up-level'), valid && first && !first._invalid  && sameParent && first.level > 2 && tree.getParent(first));
+                setObjEnable($('#down-level'), valid && first && !first._invalid  && sameParent && first.level > 1 && preNode);
             }
             setObjEnable($('#cut'), valid);
             setObjEnable($('#paste'), valid);
@@ -752,10 +752,12 @@ $(document).ready(function() {
             if (!tree) { return; }
 
             const node = sheet.zh_tree.nodes[row];
-            if (!node) { return; }
+            if (!node) return;
+            if (node._invalid && !node._insertValid) return;
 
+            const postType = (node._invalid && node._insertValid) ? 'add-child' : 'add';
             postData(window.location.pathname + '/update', {
-                postType: 'add',
+                postType,
                 postData: {
                     id: tree.getNodeKey(node),
                     count: count,
@@ -1380,7 +1382,7 @@ $(document).ready(function() {
             const tree = info.sheet.zh_tree;
             const col = info.sheet.zh_setting.cols[info.col];
             const node = info.sheet.zh_tree.nodes[info.row];
-            if (!node) {
+            if (!node || node._invalid) {
                 info.cancel = true;
                 return;
             }
@@ -1472,7 +1474,7 @@ $(document).ready(function() {
         batchReplace: function(orgInfo, newInfo) {
             const updateData = [];
             for (const d of ledgerTree.nodes) {
-                if (!d.b_code) continue;
+                if (!d.b_code || d._invalid) 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);
@@ -1728,12 +1730,16 @@ $(document).ready(function() {
             },
             disabled: function (key, opt) {
                 const sheet = ledgerSpread.getActiveSheet();
+                if (sheet.zh_setting.readOnly) return true;
+
                 const selection = sheet.getSelections();
                 const sel = selection ? selection[0] : sheet.getSelections()[0];
                 const row = sel ? sel.row : -1;
                 const tree = sheet.zh_tree;
                 if (!tree) return true;
                 const first = sheet.zh_tree.nodes[row];
+                if (!first || first.level <= 1) return true;
+
                 let last = first, sameParent = true;
                 if (sel.rowCount > 1) {
                     for (let r = 1; r < sel.rowCount; r++) {
@@ -1746,8 +1752,7 @@ $(document).ready(function() {
                         last = rNode;
                     }
                 }
-                const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && first.level > 1);
+                return first._invalid ? !first._insertValid : false;
             },
             visible: function (key, opt) {
                 return !readOnly;
@@ -1780,7 +1785,7 @@ $(document).ready(function() {
                     }
                 }
                 const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && sameParent && !(first.level === 1 && first.node_type));
+                return !(valid && first && !first._invalid && sameParent && !(first.level === 1 && first.node_type));
             },
             visible: function (key, opt) {
                 return !readOnly;
@@ -1808,12 +1813,16 @@ $(document).ready(function() {
             },
             disabled: function (key, opt) {
                 const sheet = ledgerSpread.getActiveSheet();
+                if (sheet.zh_setting.readOnly) return true;
+
                 const selection = sheet.getSelections();
                 const sel = selection ? selection[0] : sheet.getSelections()[0];
                 const row = sel ? sel.row : -1;
                 const tree = sheet.zh_tree;
                 if (!tree) return true;
                 const first = sheet.zh_tree.nodes[row];
+                if (!first || first.level <= 1) return true;
+
                 let last = first, sameParent = true;
                 if (sel.rowCount > 1) {
                     for (let r = 1; r < sel.rowCount; r++) {
@@ -1826,8 +1835,7 @@ $(document).ready(function() {
                         last = rNode;
                     }
                 }
-                const valid = !sheet.zh_setting.readOnly;
-                return !(valid && first && first.level > 1);
+                return first._invalid ? !first._insertValid : false;
             },
             visible: function (key, opt) {
                 return !readOnly;
@@ -1842,15 +1850,13 @@ $(document).ready(function() {
                 const selection = sheet.getSelections();
                 const row = selection[0].row;
                 const select = ledgerTree.nodes[row];
-                if (select) {
-                    if (select.code && select.code !== '') {
-                        return !ledgerTree.isLeafXmj(select);
-                    } else {
-                        const parent = ledgerTree.getParent(select);
-                        return !(parent && ledgerTree.isLeafXmj(parent));
-                    }
+                if (!select || select._invalid) return true;
+
+                if (select.code && select.code !== '') {
+                    return !ledgerTree.isLeafXmj(select);
                 } else {
-                    return true;
+                    const parent = ledgerTree.getParent(select);
+                    return !(parent && ledgerTree.isLeafXmj(parent));
                 }
             },
             callback: function (key, opt) {
@@ -2018,7 +2024,10 @@ $(document).ready(function() {
             icon: 'fa-sort-numeric-asc',
             disabled: function (key, opt) {
                 const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                return !node || !node.code || !node.children || node.children === 0 || node.code.length < 3;
+                if (!node) return true;
+                if (node._invalid && !node._insertValid) return true;
+
+                return !node.code || !node.children || node.children === 0 || node.code.length < 3;
             },
             callback: function (key, opt) {
                 treeOperationObj.sortCode(ledgerSpread.getActiveSheet());
@@ -2037,6 +2046,9 @@ $(document).ready(function() {
                         treeCalc.calculateAll(ledgerTree);
                         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
                     });
+                },
+                disabled: function(key, opt) {
+                    return tender.isAssUser;
                 }
             };
             billsContextMenuOptions.items.applySgfh2Deal = {
@@ -2048,6 +2060,9 @@ $(document).ready(function() {
                         treeCalc.calculateAll(ledgerTree);
                         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
                     });
+                },
+                disabled: function(key, opt) {
+                    return tender.isAssUser;
                 }
             };
         }
@@ -2068,7 +2083,7 @@ $(document).ready(function() {
             name: '导入分项清单Excel',
             icon: 'fa-file-excel-o',
             disabled: function (key, opt) {
-                return readOnly;
+                return readOnly || tender.isAssUser;
             },
             callback: function (key, opt) {
                 importExcel.doImport({
@@ -2108,7 +2123,7 @@ $(document).ready(function() {
             icon: 'fa-file-excel-o',
             disabled: function (key, opt) {
                 const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                return readOnly || !node
+                return readOnly || !node || !node._invalid
                     || (node.children && node.children.length > 0)
                     || (!_.isNil(node.b_code) && node.b_code !== '');
             },
@@ -2146,7 +2161,7 @@ $(document).ready(function() {
             icon: 'fa-link',
             disabled: function (key, opt) {
                 const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                return readOnly || !node
+                return readOnly || !node || !node._invalid
                     || (node.children && node.children.length > 0)
                     || !ledgerTree.isLeafXmj(node);
             },
@@ -2743,6 +2758,7 @@ $(document).ready(function() {
         selectionChanged: function (e, info) {
             posOperationObj.loadExprToInput();
             posOperationObj.refreshOperationValid(posSpread.getActiveSheet());
+            posCalcDetail.loadCurDetailData();
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -3280,6 +3296,10 @@ $(document).ready(function() {
 
     postData(window.location.pathname + '/load', {}, function (data) {
         ledgerTree.loadDatas(data.bills);
+        if (tender.assLedger) {
+            const assIds = ledgerTree.nodes.filter(x => { return tender.assLedger.indexOf(x.id) >= 0; }).map(x => { return x.ledger_id; });
+            ledgerTree.loadAssData(assIds);
+        }
         treeCalc.calculateAll(ledgerTree);
         checkShowLast(data.bills.length);
         pos.loadDatas(data.pos);
@@ -3501,6 +3521,7 @@ $(document).ready(function() {
                             selectedBackColor: '#fffacd',
                             readOnly: true,
                         },
+                        skipFilter: true,
                         locate: function(cur) {
                             if (!cur.lid) return;
 
@@ -5132,6 +5153,35 @@ $(document).ready(function() {
         });
     });
 
+    $('#ledger-ass').click(function() {
+        TreeAss.init({
+            spreadSetting: {
+                cols: [
+                    { title: '项目节编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 145, formatter: '@', cellType: 'tree', readOnly: true },
+                    { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 225, formatter: '@', readOnly: true },
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+            },
+            loadUrl: 'ass/load',
+            saveUrl: 'ass/save',
+            type: 'ledger',
+            rela_id: '',
+            keyField: 'id',
+            filterUser: [tender.user_id],
+        });
+        TreeAss.show(ledgerTree.nodes.filter(x => { return !x.b_code; }).map(x => {
+            return {
+                id: x.id, tree_id: x.ledger_id, tree_pid: x.ledger_pid, order: x.order, level: x.level, is_leaf: x.is_leaf, full_path: x.full_path,
+                code: x.code, name: x.name,
+            }
+        }));
+    });
+
     // 切换附件里节点和所有附件
     $('#fujian .nav-link').on('click', function () {
       const tabPanel = $(this).attr('fujian-content');
@@ -5522,6 +5572,7 @@ $(document).ready(function() {
             $('#binddskuser').modal('show');
         }
     });
+
 });
 // 生成当前节点列表
 function getNodeList(node) {

+ 17 - 0
app/public/js/path_tree.js

@@ -452,6 +452,23 @@ const createNewPathTree = function (type, setting) {
                 node.visible = parent ? (parent.expanded && parent.visible && !node.filter) : !node.filter;
             }
         }
+        loadAssValid(select) {
+            const keyField = this.setting.id;
+            const posterity = [];
+            for (const s of select) {
+                const node = this.getItems(s);
+                node._insertValid = true;
+                const sp = this.getPosterity(node);
+                posterity.push(...sp.map(x => { return x[keyField]; }));
+            }
+            this.nodes.forEach(x => {
+                x._invalid = posterity.indexOf(x[keyField]) < 0;
+            });
+        }
+        loadAssData(ass) {
+            this.loadFilter(ass.join(','));
+            this.loadAssValid(ass);
+        }
 
         getItemsByIndex(index) {
             return this.nodes[index];

+ 13 - 1
app/public/js/pos_calc_tmpl.js

@@ -334,7 +334,7 @@ $(document).ready(() => {
         };
         const delTemplate = function(id){
             postData('save', {del: id, type: 'posCalc'}, function(result) {
-                $(`dd[templateId=${result.del}]`).remove();
+                $(`dd[templateId=${id}]`).remove();
                 const tIndex = templates.findIndex(x => { return x.id === id; });
                 templates.splice(tIndex, 1);
                 if (curTemplate.id === id) {
@@ -390,6 +390,18 @@ $(document).ready(() => {
 
         $(`.table-file[templateId=${templateId}]`).html(templateObj.getTemplateCaptionHtml(template));
     });
+    $('body').on('click', 'a[name=delTemplate]', function(e) {
+        e.stopPropagation();
+        const templateId = $(this).parents('.table-file').attr('templateId');
+        templateObj.delTemplate(templateId);
+    });
+
+    $('#export').click(function() {
+        detailObj.export();
+    });
+    $('#import').click(function() {
+        detailObj.import();
+    });
 
     $('#addTemplate').click(function() {
         templateObj.addTemplate();

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

@@ -806,6 +806,8 @@ const showSelectTab = function(select, spread, afterShow) {
         SpreadJsObj.initSheet(resultSpread.getActiveSheet(), setting.resultSpreadSetting);
         let searchResult = [];
         const defaultCheck = function(node, keyword) {
+            if (setting.skipFilter && node.filter === true) return false;
+
             const keyNum = _.toNumber(keyword);
             return (node.code && node.code.indexOf(keyword) > -1) ||
                 (node.b_code && node.b_code.indexOf(keyword) > -1) ||

+ 120 - 0
app/public/js/shares/tree_ass.js

@@ -0,0 +1,120 @@
+const TreeAss = (function() {
+    let setting;
+    let assSpread, assSheet, orgAssData, assData;
+    const assTree =  createNewPathTree('base', {
+        id: 'tree_id',
+        pid: 'tree_pid',
+        order: 'order',
+        level: 'level',
+        full_path: 'full_path',
+        rootId: -1,
+    });
+    const refreshAssTable = function() {
+        const html = [];
+        for (const ass of assData) {
+            html.push('<tr>', `<td>${ass.name}</td>`, `<td>${ass.company}</td>`, `<td class="text-center">${ass.ass_id.length}</td>`, '</tr>');
+        }
+        $('#ass-table').html(html.join(''));
+    };
+    const getAssSelect = function() {
+        const result = assData ? assData.map(x => { return { value: x.user_id, text: x.name }; }) : [];
+        result.unshift({ value: 0, text: '' });
+        return result;
+    };
+    const refreshAssSelect = function() {
+        const assCol = setting.spreadSetting.cols.find(x => { return x.field === 'ass_user'; });
+        if (assCol) {
+            assCol.comboItems = getAssSelect();
+            SpreadJsObj.refreshColumnCombo(assSheet, assCol);
+        }
+    };
+    const reCalcAssData = function() {
+        const keyField = setting.keyField;
+        for (const ass of assData) {
+            const assNode = assTree.nodes.filter(x => { return x.ass_user === ass.user_id; });
+            ass.ass_id = assNode.map(x => { return x[keyField]; });
+        }
+    };
+    const init = function(s) {
+        if (!$('#tree-ass').data('bs.modal')) {
+            setting = s;
+            if (setting.keyField === undefined) setting.keyField = 'tree_id';
+            $('#tree-ass').on('shown.bs.modal', function () {
+                if (!assSpread) {
+                    assSpread = SpreadJsObj.createNewSpread($('#tree-ass-spread')[0]);
+                    assSheet = assSpread.getActiveSheet();
+                    setting.spreadSetting.cols.push(
+                        { title: '协同人', colSpan: '1', rowSpan: '2', field: 'ass_user', hAlign: 1, width: 100, formatter: '@', cellType: 'customizeCombo', comboItems: getAssSelect(), },
+                    );
+                    SpreadJsObj.initSheet(assSheet, setting.spreadSetting);
+                    assSpread.bind(spreadNS.Events.EditEnded, function(e, info) {
+                        const col = info.sheet.zh_setting.cols[info.col];
+                        const select = SpreadJsObj.getSelectObject(info.sheet);
+                        if (col.field === 'ass_user') {
+                            select.ass_user = info.editingText;
+                            const parents = assTree.getAllParents(select);
+                            parents.forEach(p => { delete p.ass_user; });
+                            const posterity = assTree.getPosterity(select);
+                            posterity.forEach(p => { delete p.ass_user; });
+                            SpreadJsObj.reloadColData(info.sheet, info.col);
+                            reCalcAssData();
+                            refreshAssTable();
+                        }
+                    })
+                }
+                SpreadJsObj.loadSheetData(assSheet, SpreadJsObj.DataType.Tree, assTree);
+            });
+            $('#tree-ass-ok').click(function() {
+                const updateData = assData.map(x => {
+                    return { user_id: x.user_id, name: x.name, company: x.company, role: x.role, ass_id: x.ass_id.join(',') };
+                });
+                postData(setting.saveUrl, { type: setting.type, rela_id: setting.rela_id, updateData }, function(){
+                    $('#tree-ass').modal('hide');
+                });
+            });
+            $('body').on('click', '#ass-user_dropdownMenu dd', function() {
+                const userId = parseInt(this.getAttribute('data-id'));
+                if (setting.filterUser.indexOf(userId) >= 0) {
+                    toastr.warning('不可添加');
+                    return;
+                }
+                const exist = assData.find(x => { return x.user_id === userId; });
+                if (exist) {
+                    toastr.warning('请勿重复添加');
+                    return;
+                }
+                const user = accountList.find(x => { return x.id === userId; });
+                assData.push({ user_id: userId, name: user.name, company: user.company, role: user.role, ass_id: [] });
+                refreshAssTable();
+                refreshAssSelect();
+            });
+        }
+    };
+    const resetAssData = function() {
+        const keyField = setting.keyField;
+        assData = JSON.parse(JSON.stringify(orgAssData));
+        for (const ass of assData) {
+            const assId = ass.ass_id ? ass.ass_id.split(',') : [];
+            ass.ass_id = [];
+            for (const ai of assId) {
+                const node = assTree.nodes.find(x => { return x[keyField] == ai; });
+                if (node) {
+                    ass.ass_id.push(node[keyField]);
+                    node.ass_user = ass.user_id;
+                }
+            }
+        }
+    };
+    const loadAssData = async function() {
+        orgAssData = await postDataAsync(setting.loadUrl, { type: setting.type, rela_id: setting.rela_id });
+        resetAssData();
+        refreshAssTable();
+        refreshAssSelect();
+    };
+    const show = async function (datas) {
+        if (datas) assTree.loadDatas(datas);
+        await loadAssData();
+        $('#tree-ass').modal('show');
+    };
+    return { show, init };
+})();

+ 12 - 0
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -1326,6 +1326,18 @@ const SpreadJsObj = {
             }
         }
     },
+    refreshColumnCombo: function(sheet, col) {
+        let cellKey;
+        if (col.cellType === 'customizeCombo') {
+            cellKey = col.cellTypeKey ? 'customizeCombo-' + col.cellTypeKey : 'customizeCombo';
+        }
+        if (col.cellType === 'specCombo') {
+            cellKey = col.cellTypeKey ? 'specCombo-' + col.cellTypeKey : 'specCombo';
+        }
+        if (sheet.extendCellType[cellKey]) {
+            sheet.extendCellType[cellKey].items(col.comboItems);
+        }
+    },
     refreshSheetReadOnly: function (sheet) {
         this.beginMassOperation(sheet);
         if (sheet.zh_setting) {

+ 2 - 0
app/router.js

@@ -619,6 +619,8 @@ module.exports = app => {
 
     // 标段协作办公
     app.get('/tender/:id/cooperation', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.tenderCooperation');
+    app.post('/tender/:id/ass/load', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.loadAss');
+    app.post('/tender/:id/ass/save', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.saveAss');
 
     app.get('/sp/:id/template/posCalc', sessionAuth, subProjectCheck, 'templateController.posCalc');
     app.get('/sp/:id/template/cost', sessionAuth, subProjectCheck, 'templateController.cost');

+ 2 - 1
app/service/pos_calc_detail.js

@@ -66,12 +66,13 @@ module.exports = app => {
                 if (updateData[sf] !== undefined) data[sf] = updateData[sf] || '';
             }
             if (updateData.spec !== undefined) {
+                calc = true;
                 data.spec = updateData.spec;
                 const sv = template.specValue.find(x => { return x.spec === data.spec; });
                 data[template.spec_rela] = sv ? sv.value : 0;
             }
             this.ctx.service.calcTmpl.calcByTemplate(data, updateData, orgData, template.calc_expr, { qty: 'expr' });
-            return calc;
+            return calc || (data.qty !== undefined && orgData.qty !== data.qty);
         }
         // 依赖前端计算
         _loadDataResult(data, updateData) {

+ 1 - 1
app/service/sub_project.js

@@ -722,7 +722,7 @@ module.exports = app => {
                 id: id, fun_rela: JSON.stringify({
                     banOver: data.banOver, hintOver: data.hintOver, banMinusChangeBills: data.banMinusChangeBills,
                     imType: data.imType, needGcl: data.needGcl, minusNoValue: data.minusNoValue,
-                    lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol,
+                    lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol, ledgerAss: data.ledgerAss,
                 }),
             });
             return result.affectedRows === 1;

+ 3 - 0
app/service/tender.js

@@ -166,6 +166,8 @@ module.exports = app => {
                     changeProjectSql + changeApplySql + changePlanSql + changeProjectXsSql +
                     // 游客权限的标段
                     '    OR (t.id IN ( SELECT tt.`tid` FROM ?? AS tt WHERE tt.`user_id` = ?))' +
+                    // 协作标段
+                    '    OR (t.id IN ( SELECT tt.`tid` FROM ?? AS tt WHERE tt.`user_id` = ?))' +
                     // 未参与,但可见的标段
                     ') ORDER BY CONVERT(t.`name` USING GBK) ASC';
                 sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, subProject.id, session.sessionUser.accountId,
@@ -179,6 +181,7 @@ module.exports = app => {
                     this.ctx.service.materialAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.advanceAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.tenderTourist.tableName, session.sessionUser.accountId,
+                    this.ctx.service.tenderAss.tableName, session.sessionUser.accountId,
                 ];
             }
             const list = await this.db.query(sql, sqlParam);

+ 75 - 0
app/service/tender_ass.js

@@ -0,0 +1,75 @@
+'use strict';
+
+/**
+ * 标段协作相关
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class TenderAss extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'tender_ass';
+        }
+
+        async getAssData(tid, type, rela_id = '') {
+            return this.getAllDataByCondition({
+                columns: ['id', 'tid', 'user_id', 'rela_id', 'ass_type', 'ass_id', 'name', 'company', 'role'],
+                where: { tid, ass_type: type, rela_id: rela_id || '' },
+                orders: [['create_time', 'asc']]
+            });
+        }
+        async getUserAssData(tid, type, user_id, rela_id = '') {
+            const result = await this.getDataByCondition({ tid, ass_type: type, user_id, rela_id });
+            if (result) result.ass_id = result.ass_id ? result.ass_id.split(',') : [];
+            return result;
+        }
+
+        async saveAssData(tid, type, rela_id, data) {
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const orgAssData = await this.getAssData(tid, type, rela_id || '');
+            const updateData = [], deleteData = [], insertData = [];
+            for (const d of data) {
+                const oa = orgAssData.find(x => { return x.user_id === d.user_id; });
+                if (oa) {
+                    updateData.push({ id: oa.id, ass_id: d.ass_id, update_user_id: accountId });
+                } else {
+                    insertData.push({
+                        tid, id: d.user_id, rela_id: rela_id || '', ass_type: type, ass_id: d.ass_id,
+                        user_id: d.user_id, name: d.name, company: d.company, role: d.role,
+                        create_user_id: accountId, update_user_id: accountId,
+                    });
+                }
+            }
+            for (const oa of orgAssData) {
+                const d = data.find(x => { return x.user_id === oa.user_id; });
+                if (!d) deleteData.push(oa.id);
+            }
+            if (updateData.length === 0 && deleteData.length === 0 && insertData.length === 0) return;
+
+            const conn = await this.db.beginTransaction();
+            try {
+                if (insertData.length > 0) await conn.insert(this.tableName, insertData);
+                if (deleteData.length > 0) await conn.delete(this.tableName, { id: deleteData });
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                await conn.commit();
+            } catch (err) {
+                this.ctx.log(err);
+                await conn.rollback();
+                throw '保存协作数据错误';
+            }
+        }
+
+    }
+
+    return TenderAss;
+};

+ 8 - 3
app/view/ledger/explode.ejs

@@ -47,16 +47,21 @@
                 <div class="d-inline-block ml-3">
                     <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">导出台账Excel</a>
                 </div>
-                <% if (tender.ledger_status === auditConst.status.uncheck ) { %>
+                <% if (tender.ledger_status === auditConst.status.uncheck && !ctx.tender.assLedger) { %>
                 <div class="d-inline-block ml-1">
                     <a href="#import-dsk" data-toggle="modal" data-target="#import-dsk" class="btn btn-sm btn-primary">导入计价文件</a>
                 </div>
                 <% } %>
-                <% if (syncLedgerUrl) { %>
+                <% if (syncLedgerUrl && !ctx.tender.assLedger) { %>
                 <div class="d-inline-block ml-1">
                     <a id="sync-ledger" class="btn btn-primary btn-sm" href="javascript: void(0)">同步台账</a>
                 </div>
                 <% } %>
+                <% if (ctx.subProject.fun_rela.ledgerAss && ctx.session.sessionUser.accountId === tender.user_id && (tender.ledger_status === auditConst.status.uncheck || tender.ledger_status === auditConst.status.checkNo)) { %>
+                <div class="d-inline-block ml-1">
+                    <a id="ledger-ass" class="btn btn-primary btn-sm" href="javascript: void(0)"><i class="fa fa-users"></i> 协同设置</a>
+                </div>
+                <% } %>
             </div>
             <div class="ml-auto">
                 <a class="btn btn-sm btn-primary mr-1" id="ledger-check2" href="javascript: void(0);">数据检查</a>
@@ -78,7 +83,7 @@
                 <% if (tender.ledger_status === auditConst.status.checking && tender.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
                 <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm mr-1">审批通过</a>
                 <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm mr-1">审批退回</a>
-                <% }%>
+                <% } %>
                 <% if (tender.ledger_status === auditConst.status.checked && tender.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && !tender.hasStage && !tender.hasRevise) { %>
                 <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm mr-1">重新审批</a>
                 <% }%>

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

@@ -589,3 +589,6 @@
 <% include ../shares/hint_modal.ejs %>
 <% include ../shares/batch_replace_modal.ejs %>
 <% include ../shares/dsk_modal.ejs %>
+<% if (ctx.subProject.fun_rela.ledgerAss && ctx.session.sessionUser.accountId === tender.user_id && (tender.ledger_status === auditConst.status.uncheck || tender.ledger_status === auditConst.status.checkNo)) { %>
+    <% include ../shares/tree_ass_modal.ejs %>
+<% } %>

+ 61 - 0
app/view/shares/tree_ass_modal.ejs

@@ -0,0 +1,61 @@
+<div class="modal fade" id="tree-ass" data-backdrop="static">
+    <div class="modal-dialog modal-xl" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">协同配置</h5>
+            </div>
+            <div class="modal-body">
+                <div class="mb-2">
+                    <div class="dropdown d-inline-block" data-code="ass-user">
+                        <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="ass-user_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            添加协同人
+                        </button>
+                        <div class="dropdown-menu dropdown-menu-left" aria-labelledby="ass-user_dropdownMenuButton" style="width:220px" id="ass-user_dropdownMenu">
+                            <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search" placeholder="姓名/手机 检索" autocomplete="off" data-code="ass-user"></div>
+                            <dl class="list-unstyled book-list">
+                                <% accountGroup.forEach((group, idx) => { %>
+                                <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>" data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                <div class="dd-content" data-toggleid="<%- idx %>">
+                                    <% group.groupList.forEach(item => { %>
+                                    <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>">
+                                        <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                    class="ml-auto"><%- item.mobile %></span></p>
+                                        <span class="text-muted"><%- item.role %></span>
+                                    </dd>
+                                    <% });%>
+                                </div>
+                                <% }) %>
+                            </dl>
+                        </div>
+                    </div>
+                    <div class="d-inline-block">
+                        <button class="btn btn-outline-danger btn-sm" id="del-ass-user">
+                            移除协同人
+                        </button>
+                    </div>
+                </div>
+                <div class="row">
+                    <div class="col-6">
+                        <div class="modal-height-500" style="overflow: auto;">
+                            <table class="table table-hover table-bordered">
+                                <thead class="text-center" >
+                                <tr><th>姓名</th><th>单位</th><th>协同处理</th></tr>
+                                </thead>
+                                <tbody id="ass-table">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <div class="col-6">
+                        <div class="modal-height-500" id="tree-ass-spread">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" data-dismiss="modal" id="tree-ass-ok">保存</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 13 - 0
app/view/sp_setting/fun.ejs

@@ -23,6 +23,10 @@
                                                     <input class="form-check-input" type="checkbox" id="pos_calc_detail" name="pos_calc_detail" <% if(ctx.subProject.page_show.posCalcDetail) { %>checked<% } %> onchange="updateSetting();">
                                                     <label class="form-check-label" for="pos_calc_detail">设计量明细</label>
                                                 </div>
+                                                <div class="form-check">
+                                                    <input class="form-check-input" type="checkbox" id="ledger_ass" name="ledger_ass" <% if(funRela.ledgerAss) { %>checked<% } %> onchange="updateSetting();">
+                                                    <label class="form-check-label" for="ledger_ass">台账协同</label>
+                                                </div>
                                             </div>
                                         </div>
                                     </div>
@@ -340,6 +344,13 @@
                                                 <label class="form-check-label" for="openMaterialStageRepeat">开启「材料调差重复使用计量期」</label>
                                             </div>
                                         </div>
+                                        <div class="form-group mb-1">
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="openMaterialListQty" <% if(ctx.subProject.page_show.openMaterialListQty) { %>checked<% } %> onchange="updateSetting();">
+                                                <label class="form-check-label" for="openMaterialListQty">开启「修改调差数量」功能
+                                                    <a href="javascript:void(0);"  data-toggle="tooltip" data-placement="bottom" title="" data-original-title="开启该选项,可在调差清单页修改本期调差数量"><i class="fa fa-question-circle "></i></a></label>
+                                            </div>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
@@ -641,6 +652,7 @@
             openMaterialSelf: $('#openMaterialSelf')[0].checked,
             openMaterialEditForAudit: $('#openMaterialEditForAudit')[0].checked,
             openMaterialStageRepeat: $('#openMaterialStageRepeat')[0].checked,
+            openMaterialListQty: $('#openMaterialListQty')[0].checked,
             openStageStart: $('#openStageStart')[0].checked,
             openContractExpr: $('#openContractExpr')[0].checked,
             correctCalcContractTp: $('#correctCalcContractTp')[0].checked,
@@ -652,6 +664,7 @@
             stageAuditEarly: parseInt($('#stage-audit-early').val()),
             stageAuditWorry: parseInt($('#stage-audit-worry').val()),
             posCalcDetail: $('#pos_calc_detail')[0].checked ? 1 : 0,
+            ledgerAss: $('#ledger_ass')[0].checked,
         }, function (result) {
             if ($('#openChangeState').is(':checked')) {
                 $('#openBgStatus').removeClass('text-secondary');

+ 1 - 0
config/web.js

@@ -224,6 +224,7 @@ const JsFiles = {
                     '/public/js/ledger_tree_col.js',
                     '/public/js/std_lib.js',
                     '/public/js/ledger_check.js',
+                    '/public/js/shares/tree_ass.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tender_select.js',
                     '/public/js/shares/db2full_code.js',