Browse Source

1. 成本管理,合并上期数据和本期关联计算相关
2. 计算模板,设计量明细,部分bug

MaiXinRong 1 day atrás
parent
commit
51d43558f5

+ 14 - 7
app/base/base_tree_service.js

@@ -804,7 +804,7 @@ class TreeService extends Service {
      * @return {Object}
      * @private
      */
-    async _syncUplevelChildren(select) {
+    async _syncUplevelChildren(select, isParentToTop = false) {
         this.initSqlBuilder();
         this.sqlBuilder.setAndWhere(this.setting.mid, {
             value: this.db.escape(select[this.setting.mid]),
@@ -818,10 +818,17 @@ class TreeService extends Service {
             value: 1,
             selfOperate: '-',
         });
-        this.sqlBuilder.setUpdateData(this.setting.fullPath, {
-            value: [this.setting.fullPath, this.db.escape(`-${select[this.setting.pid]}-`), this.db.escape('-')],
-            literal: 'Replace',
-        });
+        if (isParentToTop) {
+            this.sqlBuilder.setUpdateData(this.setting.fullPath, {
+                value: [this.setting.fullPath, this.db.escape(`${select[this.setting.pid]}-`), this.db.escape('')],
+                literal: 'Replace',
+            });
+        } else {
+            this.sqlBuilder.setUpdateData(this.setting.fullPath, {
+                value: [this.setting.fullPath, this.db.escape(`-${select[this.setting.pid]}-`), this.db.escape('-')],
+                literal: 'Replace',
+            });
+        }
         const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
         const data = await this.transaction.query(sql, sqlParam);
 
@@ -924,7 +931,7 @@ class TreeService extends Service {
                 updateData[this.setting.pid] = parent[this.setting.pid];
                 updateData[this.setting.order] = parent[this.setting.order] + i + 1;
                 updateData[this.setting.level] = s[this.setting.level] - 1;
-                updateData[this.setting.fullPath] = s[this.setting.fullPath].replace(`-${s[this.setting.pid]}-`, '-');
+                updateData[this.setting.fullPath] = s[this.setting.level] === 2 ? s[this.setting.kid] + '' : s[this.setting.fullPath].replace(`-${s[this.setting.pid]}-`, '-');
                 newPath.push(updateData[this.setting.fullPath]);
                 if (s[this.setting.isLeaf] && s.id === last.id) {
                     const nexts = await this.getNextsData(mid, parent[this.setting.kid], last[this.setting.order]);
@@ -935,7 +942,7 @@ class TreeService extends Service {
                 }
                 await this.transaction.update(this.tableName, updateData);
                 // 选中节点--全部子节点(含孙) level-1, fullPath变更
-                await this._syncUplevelChildren(s);
+                await this._syncUplevelChildren(s, s[this.setting.level] === 2);
             }
             // 选中节点--全部后兄弟节点 收编为子节点 修改pid, order, fullPath
             await this._syncUpLevelNexts(last);

+ 25 - 0
app/public/js/cost_tmpl.js

@@ -139,6 +139,9 @@ $(document).ready(() => {
                         comboItems: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'],
                     },
                     { title: '小数位数', colSpan: '1', rowSpan: '1', field: 'decimal', hAlign: 1, width: 60, type: 'Number' },
+                    { title: '期数据', colSpan: '1', rowSpan: '1', field: 'sum', hAlign: 1, width: 60, readOnly: true, cellType: 'signalCheckbox', show: function(data) {
+                        const typeInfo = validColInfo.find(x => { return x.key === data.type; }); return typeInfo.valid.indexOf('sum') >= 0;
+                        } },
                     { title: '计算公式', colSpan: '1', rowSpan: '1', field: 'expr', hAlign: 0, width: 250 },
                 ],
                 emptyRows: 0,
@@ -202,6 +205,23 @@ $(document).ready(() => {
                 }
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
             });
+            this.spread.bind(spreadNS.Events.ButtonClicked, function(e, info) {
+                if (!info.sheet.zh_setting) return;
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field !== 'sum') return;
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const typeInfo = validColInfo.find(x => { return x.key === select.type; });
+                if (typeInfo.valid.indexOf('sum') <= 0) return;
+
+                select.sum = !select.sum;
+                if (select.sum) {
+                    info.sheet.zh_data.forEach(x => {
+                        if (x.field !== select.field) delete x.sum;
+                    })
+                }
+                SpreadJsObj.reloadColData(info.sheet, info.col);
+            });
             SpreadJsObj.addDeleteBind(this.spread, function(sheet){
                 if (!sheet.zh_setting) return;
 
@@ -441,6 +461,11 @@ $(document).ready(() => {
                     }
                 }
             }
+            const sumCol = this.colSetData.find(x => { return x.sum; });
+            if (!sumCol) {
+                toastr.error('未指定期数据显示的数据列');
+                return null;
+            }
             return this.colSetData;
         }
         export() {

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

@@ -343,7 +343,7 @@ $(document).ready(() => {
         initSpread() {
             SpreadJsObj.initSheet(this.sheet, this.templateSpreadSetting);
             this.spread.bind(spreadNS.Events.SelectionChanged, this.selectionChanged);
-            if (readOnly) return;
+            this.spread.bind(spreadNS.Events.EditStarting, this.editStarting);
             this.spread.bind(spreadNS.Events.EditEnded, this.editEnded);
             this.spread.bind(spreadNS.Events.ClipboardPasting, this.clipboardPasting);
             SpreadJsObj.addDeleteBind(this.spread, this.deletePress);
@@ -455,6 +455,10 @@ $(document).ready(() => {
                 }
             }
         }
+        editStarting(e, info) {
+            const select = SpreadJsObj.getSelectObject(info.sheet);
+            info.cancel = !select || select.create_user_id !== userID;
+        }
         editEnded(e, info) {
             const setting = info.sheet.zh_setting;
             if (!setting) return;

+ 4 - 4
app/service/calc_tmpl.js

@@ -80,14 +80,14 @@ const Cost = (function(){
         { key: 'code', name: '编号', fields: ['code'], valid: ['title', 'width'], def: { title: '编号', width: 80, type: 'code'} },
         { key: 'name', name: '名称', fields: ['name'], valid: ['title', 'width'], def: { title: '名称', width: 80, type: 'name'} },
         { key: 'party_b', name: '乙方', fields: ['party_b'], valid: ['title', 'width'], def: { title: '乙方', width: 80, type: 'party_b'} },
-        { key: 'yf_excl_tax_tp', name: '应付(不含税)', fields: ['yf_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal'], def: { title: '已结(不含税)', width: 80, type: 'yf_excl_tax_tp', decimal: 6} },
-        { key: 'in_excl_tax_tp', name: '入账(不含税)', fields: ['in_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal'], def: { title: '入账(不含税)', width: 80, type: 'in_excl_tax_tp', decimal: 6} },
-        { key: 'sf_excl_tax_tp', name: '实付(不含税)', fields: ['sf_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal'], def: { title: '实付(不含税)', width: 80, type: 'sf_excl_tax_tp', decimal: 6} },
+        { key: 'yf_excl_tax_tp', name: '应付(不含税)', fields: ['yf_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal', 'sum'], def: { title: '已结(不含税)', width: 80, type: 'yf_excl_tax_tp', decimal: 6} },
+        { key: 'in_excl_tax_tp', name: '入账(不含税)', fields: ['in_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal', 'sum'], def: { title: '入账(不含税)', width: 80, type: 'in_excl_tax_tp', decimal: 6} },
+        { key: 'sf_excl_tax_tp', name: '实付(不含税)', fields: ['sf_excl_tax_tp'], valid: ['title', 'width', 'calc_code', 'decimal', 'sum'], def: { title: '实付(不含税)', width: 80, type: 'sf_excl_tax_tp', decimal: 6} },
         { key: 'str', name: '文本', fields: ['str1', 'str2', 'str3', 'str4'], valid: ['title', 'width'], def: { title: '文本', width: 80, type: 'str'} },
         {
             key: 'num', name: '数值/计算',
             fields: ['num_a', 'num_b', 'num_c', 'num_d', 'num_e', 'num_f', 'num_g', 'num_h', 'num_i', 'num_j', 'num_k', 'num_l', 'num_m', 'num_n', 'num_o', 'num_p'], // 'num_q', 'num_r', 'num_s', 'num_t', 'num_u', 'num_v', 'num_w', 'num_x', 'num_y', 'num_z'],
-            valid: ['title', 'width', 'calc_code', 'decimal', 'unit', 'expr'],
+            valid: ['title', 'width', 'calc_code', 'decimal', 'unit', 'expr', 'sum'],
             def: { title: '数值/计算', width: 80, decimal: 6, type: 'num', unit: ''}
         },
         { key: 'postil', name: '本期批注', fields: ['postil'], valid: ['title', 'width'], def: { title: '本期批注', width: 120, type: 'postil'} },

+ 1 - 1
app/service/cost_stage.js

@@ -118,7 +118,7 @@ module.exports = app => {
                 await this.ctx.service.costStageAudit.copyPreAuditors(transaction, preStage, data);
                 await transaction.commit();
             } catch(err) {
-                console.log(err);
+                this.ctx.log(err);
                 await transaction.rollback();
                 throw err;
             }

+ 20 - 16
app/service/cost_stage_analysis.js

@@ -17,7 +17,7 @@ const costFields = {
     hisFields: ['calc_his'],
     taxFields: ['tax'],
     treeFields: ['tree_id', 'tree_pid', 'tree_level', 'tree_order', 'tree_full_path', 'tree_is_leaf'],
-    baseFields: ['id', 'cost_id', 'tender_id', 'stage_id', 'node_type'],
+    baseFields: ['id', 'cost_id', 'tender_id', 'stage_id', 'node_type', 'calc_type'],
 };
 costFields.sumFields = [...costFields.calcFields, ...costFields.selfCalcFields];
 costFields.preCopyFields = [...costFields.treeFields, ...costFields.textFields, ...costFields.selfTextFields, ...costFields.calcFields, ...costFields.selfCalcFields, ...costFields.taxFields];
@@ -211,6 +211,7 @@ module.exports = app => {
             const userId = this.ctx.session.sessionUser.accountId;
             const insertData = [], insertDetailData = [];
             let maxId = 0;
+            if (!stage.calcTemplate) stage.calcTemplate = await this.service.calcTmpl.getTemplate(stage.calc_template);
 
             const preBills = await this.getReadData(preStage);
             const preLedger = new Ledger.baseTree(this.ctx, {
@@ -230,6 +231,7 @@ module.exports = app => {
                 this._getDefaultData(idata, stage);
                 idata.cost_id = pl.cost_id;
                 idata.node_type = pl.node_type;
+                idata.calc_type = pl.calc_type;
                 for (const prop of costFields.preCopyFields) {
                     idata[prop] = pl[prop];
                 }
@@ -244,7 +246,7 @@ module.exports = app => {
             for (const [i, node] of costStageLedger.nodes.entries()) {
                 const parent = costStageLedger.getParent(node);
                 const newParent = parent ? insertData.find(x => { return x.tree_id === parent.new_tree_id; }) : costNode;
-                const ln = {};
+                const ln = { calc_type: 2 };
                 this._getDefaultData(ln, stage);
                 ln.node_type = 0;
                 ln.cost_id = node.cost_id;
@@ -273,6 +275,7 @@ module.exports = app => {
                     for (const prop of costFields.selfCalcFields) {
                         ln[prop] = preSource ? preSource[prop] : 0;
                     }
+                    this.ctx.service.calcTmpl.calcByTemplate(ln, ln, ln, stage.calcTemplate.calc_expr);
                 } else {
                     for (const [i, d] of node.detail.entries()) {
                         const ld = {
@@ -290,6 +293,9 @@ module.exports = app => {
                         }
                         for (const prop of costFields.selfCalcFields) {
                             ld[prop] = preDetailSource ? preDetailSource[prop] || 0 : 0;
+                        }
+                        this.ctx.service.calcTmpl.calcByTemplate(ld, ld, ld, stage.calcTemplate.calc_expr);
+                        for (const prop of costFields.selfCalcFields) {
                             ln[prop] = this.ctx.helper.add(ln[prop], ld[prop]);
                         }
                         insertDetailData.push(ld);
@@ -675,20 +681,18 @@ module.exports = app => {
         }
 
         async getSum(stage) {
-            const field = 'num_h';
-            const topNode = await this.db.query(`SELECT * FROM ${this.tableName} where stage_id = ? and tree_level = 1`, [stage.id]);
-            const result = {};
-            for (const tn of topNode) {
-                if (topNode.node_type === 2) {
-                    result.out_tp = tn[field];
-                } else if (topNode.node_type === 3) {
-                    result.profit = tn[field];
-                } else if (topNode.node_type === 4) {
-                    result.profit_percent = tn[field];
-                } else {
-                    result.in_tp = tn[field];
-                }
-            }
+            const sumCol = stage.calcTemplate.col_set.find(x => { return x.sum; });
+            const field = sumCol.field;
+            const inTp = await this.db.queryOne(`SELECT SUM(${field}) as tp FROM ${this.tableName} where stage_id = ? AND calc_type = 1`, [stage.id]);
+            const outTp = await this.db.queryOne(`SELECT SUM(${field}) as tp FROM ${this.tableName} where stage_id = ? AND calc_type = 2`, [stage.id]);
+            const profit = await this.db.queryOne(`SELECT ${field} as tp FROM ${this.tableName} where stage_id = ? AND node_type = 3`, [stage.id]);
+            const profitPer = await this.db.queryOne(`SELECT ${field} as tp FROM ${this.tableName} where stage_id = ? AND node_type = 4`, [stage.id]);
+            const result = {
+                in_tp: inTp ? inTp.tp : 0,
+                out_tp: outTp ? outTp.tp : 0,
+                profit: profit ? profit.tp : 0,
+                profit_percent: profitPer ? profitPer.tp : 0
+            };
             return result;
         }
     }

+ 1 - 1
app/view/template/pos_calc.ejs

@@ -36,7 +36,7 @@
             <div class="row">
                 <div class="col-3">
                     <div id="folder-spread" class="sjs-height-1"></div>
-                    <div class="bcontent-wrap" id="main-bottom" style="height: 600px;">
+                    <div class="bcontent-wrap" id="main-bottom" style="height: 200px;">
                         <div class="bc-bar mb-1 d-flex">
                             <div class="d-inline-block">
                                 <ul class="nav nav-tabs">