فهرست منبع

1. 动态投资,导入大司空数据
2. 资料归集,授权用户,界面调整

MaiXinRong 2 هفته پیش
والد
کامیت
0137549647

+ 78 - 0
app/base/base_budget_service.js

@@ -466,6 +466,84 @@ class BaseBudget extends TreeService {
         const result = await this.db.queryOne(sql, [bid]);
         return result.total_price;
     }
+
+    async importYbpData(budget, ybpData, needGcl) {
+        const filterGcl = function(data, compareData, isXmj) {
+            return !isXmj;
+        };
+        const YbpTrees = require('../lib/ybp_tree');
+        const gatherTreeSetting = {
+            id: 'ledger_id', pid: 'ledger_pid', order: 'order', full_path: 'full_path', level: 'level', rootId: -1,
+            calcFields: ['total_price'],
+            calc(node, helper, decimal) {
+                node.quantity = helper.round(node.quantity, decimal.qty);
+                node.unit_price = helper.round(node.unit_price, decimal.up);
+                if ((!node.children || node.children.length === 0)) {
+                    node.total_price = node.b_code ? helper.mul(node.quantity, node.unit_price, decimal.tp) : helper.round(node.total_fee, decimal.tp);
+                }
+            },
+        };
+        const ybpTreeSetting = { id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' };
+        const helper = this.ctx.helper;
+
+        const mergeCompliation = ['5b52b027fd3bb0000b257cf8', '5c66649650da2d000d8d37ba'];
+        const ybpImportType = mergeCompliation.indexOf(ybpData.subjects[0].project.compilationID) >= 0 ? YbpTrees.YbpImportType.flow : YbpTrees.YbpImportType.merge;
+        const gatherTree = new YbpTrees.YbpImportTree(gatherTreeSetting, ybpImportType, helper, budget.decimal);
+        for (const subject of ybpData.subjects) {
+            if (!subject.bills || subject.bills.length === 0) continue;
+
+            const ybpTree = new YbpTrees.YbpTree(ybpTreeSetting);
+            ybpTree.loadDatas(subject.bills);
+            gatherTree.importTree(ybpTree, subject.project.name, null, needGcl ? null : filterGcl);
+        }
+        gatherTree.sort(false);
+        gatherTree.calculateAll();
+
+        const bills = [];
+        for (const n of gatherTree.nodes) {
+            const billsId = this.uuid.v4();
+            const isLeaf = !n.children || n.children.length === 0;
+            bills.push({
+                id: billsId,
+                bid: budget.id,
+                tree_id: n.ledger_id,
+                tree_pid: n.ledger_pid,
+                level: n.level,
+                order: n.order,
+                is_leaf: isLeaf,
+                full_path: n.full_path,
+                code: n.code || '',
+                b_code: n.b_code || '',
+                name: n.name || '',
+                unit: n.unit || '',
+                unit_price: isLeaf ? n.unit_price || 0 : 0,
+                dgn_qty1: n.dgn_qty1 || 0,
+                dgn_qty2: n.dgn_qty2 || 0,
+                memo: n.remark,
+                drawing_code: n.drawing_code,
+                quantity: isLeaf ? n.quantity || 0 : 0,
+                total_price: isLeaf ? n.total_price || 0 : 0,
+            });
+        }
+        const conn = await this.db.beginTransaction();
+        try {
+            await conn.delete(this.tableName, { bid: budget.id });
+            await conn.insert(this.tableName, bills);
+            await conn.commit();
+            return bills;
+        } catch (err) {
+            await conn.rollback();
+            throw err;
+        }
+    }
+
+    async importYbp(budget, ybp, needGcl) {
+        const YBP = require('../lib/ybp');
+        const ybpAnalysis = new YBP(this.ctx);
+        const ybpData = ybpAnalysis.decryptBuffer(ybp);
+
+        return await this.importYbpData(budget, ybpData, needGcl);
+    }
 }
 
 module.exports = BaseBudget;

+ 57 - 0
app/controller/budget_controller.js

@@ -16,6 +16,14 @@ const auditConst = require('../const/audit');
 const changeConst = require('../const/change');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
+const ValidDskProject = {
+    gu: [15, 16],
+    gai: [5],
+    yu: [1, 18],
+    zb: [4, 18, 19],
+};
+const DSK = require('../lib/dsk');
+
 module.exports = app => {
     class BudgetController extends app.BaseController {
 
@@ -189,6 +197,7 @@ module.exports = app => {
                     spreadSetting: this._getSpreadSetting(needGcl),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.budget.detail),
                     needGcl,
+                    validDskProjType: ValidDskProject[ctx.params.btype],
                 };
                 [renderData.stdBills, renderData.stdChapters] = await ctx.service.budgetStd.getStdList(ctx.budget.std_id, ctx.params.btype);
                 await this.layout('budget/detail.ejs', renderData, 'budget/detail_modal.ejs');
@@ -333,6 +342,54 @@ module.exports = app => {
             }
         }
 
+        async uploadYbp(ctx) {
+            const stream = await ctx.getFileStream();
+            try {
+                const relaService = this._getRelaService(ctx.params.btype);
+                const needGcl = await this._getNeedGcl();
+                // 读取字节流
+                const parts = await streamToArray(stream);
+                // 转化为buffer
+                const buffer = Buffer.concat(parts);
+                const bills = relaService.importYbp(ctx.budget, buffer.toString(), needGcl);
+                ctx.body = { err: 0, msg: '', data: bills };
+            } catch (err) {
+                await sendToWormhole(stream);
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入计价文件出错');
+            }
+        }
+
+        async importDsk(ctx) {
+            try {
+                const relaService = this._getRelaService(ctx.params.btype);
+                const needGcl = await this._getNeedGcl();
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.subjects || data.subjects.length === 0) throw '无导入计价数据';
+
+                // 拼凑ybp数据
+                const ybpData = { header: {}, subjects: [] };
+                const dsk = new DSK(ctx);
+                for (const s of data.subjects) {
+                    const subject = { project: { name: s.name, compilationID: s.compilationId }, rations: [], };
+                    subject.bills = await dsk.getProjectBills(s.compilationId, s.subjectId);
+                    // 拼凑fees.marketCommon.totalFee,fees.marketLabour.totalFee防止出错
+                    subject.bills.forEach(b => {
+                        b.fees = {
+                            marketCommon: { unitPrice: b.unitPrice, totalFee: b.totalPrice },
+                            marketLabour: { unitPrice: 0, totalFee: 0 },
+                        };
+                    });
+                    ybpData.subjects.push(subject);
+                }
+                const bills = await relaService.importYbpData(ctx.budget, ybpData, needGcl);
+                ctx.body = { err: 0, msg: '', data: bills };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入计价分段出错');
+            }
+        }
+
         async decimal(ctx) {
             try {
                 if (!ctx.budget) throw '项目数据错误';

+ 6 - 4
app/lib/ybp_tree.js

@@ -162,7 +162,8 @@ class YbpImportTree {
         //       x.unit_price === data.unit_price;
         // });
     }
-    _importNode(node, parent, loadRelaFun) {
+    _importNode(node, parent, loadRelaFun, filter) {
+
         const setting = this.setting;
         const compareData = { kind: node.kind };
         const hasUp = (!node.children || node.children.length === 0) && defaultMerge.indexOf(compareData.kind) < 0;
@@ -175,6 +176,7 @@ class YbpImportTree {
         compareData.unit_price = hasUp
             ? (node.fees && node.fees.marketCommon ? node.fees.marketCommon.unitPrice : 0)
             : 0;
+        if (filter && filter(node, compareData, isXmj)) return;
 
         let cur = (this.importType === YbpImportType.merge && defaultMerge.indexOf(compareData.kind) >= 0)
             ? this._findNode(compareData, parent)
@@ -214,16 +216,16 @@ class YbpImportTree {
         cur.source.push(this.unitName);
         if (node.children) {
             for (const c of node.children) {
-                this._importNode(c, cur, loadRelaFun,);
+                this._importNode(c, cur, loadRelaFun, filter);
             }
         }
         return cur;
     }
-    importTree(tree, unitName, loadRelaFun) {
+    importTree(tree, unitName, loadRelaFun, filter) {
         const parent = this.importType === YbpImportType.flow ? this._importNode({name: unitName}) : null;
         this.unitName = unitName;
         for (const n of tree.children) {
-            this._importNode(n, parent, loadRelaFun);
+            this._importNode(n, parent, loadRelaFun, filter);
         }
     }
 

+ 52 - 1
app/public/js/budget_detail.js

@@ -877,5 +877,56 @@ $(document).ready(() => {
             budgetTreeOpr.refreshTree(budgetSheet, refreshNodes);
             $('#budget-set').modal('hide');
         })
-    })
+    });
+
+    $('#upload-ybp-file').click(function () {
+        const file = $('#ybp-file')[0];
+        const formData = new FormData();
+        formData.append('file', file.files[0]);
+        postDataWithFile(window.location.pathname + '/ybp', formData, function (result) {
+            budgetTree.loadDatas(result);
+            treeCalc.calculateAll(budgetTree);
+            SpreadJsObj.reLoadSheetData(budgetSheet);
+            checkShowLast(result.length);
+            $('#upload-ybp').modal('hide');
+        }, function () {
+            $('#upload-ybp').modal('hide');
+        });
+    });
+    $('#id-upload-ybp').click(function() {
+        $('#import-dsk').modal('hide');
+        $('#upload-ybp').modal('show');
+    });
+    $('#id-post-dsk').click(async function() {
+        if (await dsk.checkBind()) {
+            $('#import-dsk').modal('hide');
+            dsk.chooseSubject({
+                filterProjectType: validDskProjType,
+                validCheck: function (node, tree) {
+                    const checkType = node.type !== dsk.projectTypeKey.folder;
+                    if (!checkType) {
+                        toastr.warning('请勿选择文件夹');
+                        return false;
+                    }
+                    const choosed = tree.datas.filter(x => { return !!x.selected; });
+                    if (choosed.length === 0) return true;
+                    if (node.project_id !== choosed[0].project_id) {
+                        toastr.warning('请勿跨建设项目选择');
+                        return false;
+                    }
+                    return true;
+                },
+                callback: async function (subjects) {
+                    postData(window.location.pathname + '/dsk', {subjects}, function (result) {
+                        budgetTree.loadDatas(result);
+                        treeCalc.calculateAll(budgetTree);
+                        SpreadJsObj.reLoadSheetData(budgetSheet);
+                        checkShowLast(result.length);
+                    });
+                }
+            });
+        } else {
+            $('#binddskuser').modal('show');
+        }
+    });
 });

+ 7 - 1
app/public/js/shares/dsk.js

@@ -130,6 +130,7 @@ const dsk = (function () {
             this.compilationObj.html(html.join(''));
         },
         analysisSubjectTree(compilation) {
+            const filter = this.setting.filterProjectType;
             const subjectTree = createNewPathTree('gather', {
                 id: 'id',
                 pid: 'pid',
@@ -158,6 +159,9 @@ const dsk = (function () {
                 }
             };
             const loadTempTreeNode = function (data, parent) {
+                if (data.type === projectTypeKey.project && filter) {
+                    if (filter.indexOf(data.property.fileType) < 0) return;
+                }
                 const type = projectType.find(x => { return x.value === data.type; });
                 const node = { type: type.value, type_str: type.name, file_type_str: data.property ? data.property.fileTypeStr || '' : '', dsk_id: data.ID, name: data.name, compilation_id: compilation.ID };
                 const cur = subjectTree.addNode(node, parent);
@@ -188,7 +192,9 @@ const dsk = (function () {
             if (!compilation) return;
             if (!compilation.subjectTree) {
                 for (const p of compilation.project) {
-                    if (p.type === projectTypeKey.project) p.subjects = await loadProjectTree(compilation.ID, p.ID, false);
+                    if (p.type === projectTypeKey.project) {
+                        p.subjects = await loadProjectTree(compilation.ID, p.ID, false);
+                    }
                 }
                 this.analysisSubjectTree(compilation);
             }

+ 2 - 0
app/router.js

@@ -382,6 +382,8 @@ module.exports = app => {
     app.post('/sp/:id/budget/:btype/load', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailLoad');
     app.post('/sp/:id/budget/:btype/update', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailUpdate');
     app.post('/sp/:id/budget/:btype/upload-excel/:ueType', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.detailUploadExcel');
+    app.post('/sp/:id/budget/:btype/ybp', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.uploadYbp');
+    app.post('/sp/:id/budget/:btype/dsk', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.importDsk');
     app.post('/sp/:id/budget/decimal', sessionAuth, subProjectCheck, budgetCheck, 'budgetController.decimal');
 
     // 支付审批

+ 2 - 0
app/view/budget/detail.ejs

@@ -30,6 +30,7 @@
                 <div class="d-inline-block ml-3">
                     <% if (!ctx.budget.readOnly) { %>
                     <a class="btn btn-sm btn-primary" href="javascript: void(0);" id="budget-import">导入</a>
+                    <a href="#import-dsk" data-toggle="modal" data-target="#import-dsk" class="btn btn-sm btn-primary">导入计价文件</a>
                     <% } %>
                 </div>
             </div>
@@ -99,4 +100,5 @@
     const needGcl = <%- needGcl %>;
     const spreadSetting = JSON.parse('<%- JSON.stringify(spreadSetting) %>');
     let decimal = JSON.parse('<%- JSON.stringify(ctx.budget.decimal) %>');
+    const validDskProjType = JSON.parse('<%- JSON.stringify(validDskProjType) %>');
 </script>

+ 66 - 0
app/view/budget/detail_modal.ejs

@@ -47,3 +47,69 @@
         </div>
     </div>
 </div>
+
+<!--上传计价文件-->
+<div class="modal fade" id="import-dsk" data-backdrop="static" aria-modal="true">
+    <div class="modal-dialog" role="document" style="width:400px">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择计价文件</h5>
+                <div class="btn-group ml-auto">
+                    <a href="" data-toggle="dropdown" title="文件类型说明"><i class="fa fa-question-circle"></i></a>
+                    <div class="dropdown-menu bg-dark">
+                        <a class="dropdown-item text-light" href="#">1、yup/ybpx文件:纵横在线云计价大司空导出的项目或者标段文件</a>
+                        <a class="dropdown-item text-light in-3" href="#">yup为一个分段对应一个台账。</a>
+                        <a class="dropdown-item text-light in-3" href="#">ybpx为合并所有分段为一个台账。</a>
+                        <a class="dropdown-item text-light" href="#">2、在线计价文件:绑定纵横在线云计价大司空账号,获取账号下的项目文件。</a>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-body">
+                <p>请选择合适的方式导入文件</p>
+                <div class="d-flex justify-content-between">
+                    <div></div>
+                    <div class="card card-upload-width p-2" id="id-upload-ybp">
+                        <input type="file" id="btn_file" style="display:none">
+                        <div class="card-body p-0">
+                            <div class="m-2"><img src="/public/images/ybpx.png" class="rounded m-auto d-block"></div>
+                            <div class="text-center my-3">上传yup/ybpx文件</div>
+                        </div>
+                    </div>
+                    <div class="card card-upload-width p-2" id="id-post-dsk">
+                        <div class="card-body p-0">
+                            <div class="m-2"><img src="/public/images/dsk.png" class="rounded m-auto d-block"></div>
+                            <div class="text-center mt-3 ">获取在线计价文件</div>
+                        </div>
+                    </div>
+                    <div></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <div class="ml-auto">
+                    <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-sm btn-primary">确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="upload-ybp" data-backdrop="static" enctype="multipart/form-data">
+    <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="form-group">
+                    <label for="exampleFormControlFile1">导入计价YBPX/YUP文件</label>
+                    <input type="file" class="form-control-file" id="ybp-file">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="upload-ybp-file">确认上传</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% include ../shares/dsk_modal.ejs %>

+ 1 - 1
app/view/file/file_modal.ejs

@@ -27,7 +27,7 @@
                             </div>
                             <div class="p-2"><a href="javascript: void(0);" id="batch-del-filing" class="text-danger">批量删除</a></div>
                             <div class="p-2"><a href="javascript: void(0);" id="batch-add-filing">批量添加至其他类别</a></div>
-                            <div class="p-2"><a href="javascript: void(0);" id="sync-filing">同步授权至其他类别</a></div>
+                            <div class="p-2"><a href="javascript: void(0);" id="sync-filing">覆盖账号至其他类别</a></div>
                         </div>
                         <div class="modal-height-400 scroll-y">
                             <table class="table table-bordered">

+ 1 - 0
config/web.js

@@ -1472,6 +1472,7 @@ const JsFiles = {
                     '/public/js/component/menu.js',
                 ],
                 mergeFiles: [
+                    '/public/js/shares/dsk.js',
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',