浏览代码

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

Tony Kang 1 年之前
父节点
当前提交
866fcb04b4
共有 55 个文件被更改,包括 1693 次插入401 次删除
  1. 28 27
      app/const/spread.js
  2. 8 1
      app/controller/change_controller.js
  3. 1 1
      app/controller/stage_controller.js
  4. 4 2
      app/lib/dsk.js
  5. 61 0
      app/lib/rm/advance.js
  6. 119 100
      app/lib/rm/material.js
  7. 2 2
      app/lib/rm/tender.js
  8. 451 0
      app/lib/rm/tender_material.js
  9. 4 4
      app/lib/stage_im.js
  10. 1 1
      app/middleware/change_apply_check.js
  11. 1 1
      app/middleware/change_check.js
  12. 1 1
      app/middleware/change_plan_check.js
  13. 1 1
      app/middleware/change_project_check.js
  14. 226 5
      app/public/js/change_information_approval.js
  15. 74 41
      app/public/js/change_information_set.js
  16. 1 1
      app/public/js/change_information_show.js
  17. 161 14
      app/public/js/change_revise.js
  18. 39 0
      app/public/js/gcl_gather.js
  19. 164 57
      app/public/js/ledger.js
  20. 66 0
      app/public/js/ledger_gather.js
  21. 4 4
      app/public/js/material.js
  22. 3 3
      app/public/js/material_checklist.js
  23. 3 3
      app/public/js/material_list.js
  24. 53 6
      app/public/js/shares/cs_tools.js
  25. 12 4
      app/public/js/shares/new_tag.js
  26. 1 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  27. 2 2
      app/public/js/stage.js
  28. 4 4
      app/public/js/stage_im.js
  29. 5 3
      app/service/change.js
  30. 3 3
      app/service/change_apply.js
  31. 4 2
      app/service/change_apply_audit.js
  32. 9 2
      app/service/change_audit.js
  33. 3 3
      app/service/change_plan.js
  34. 4 2
      app/service/change_plan_audit.js
  35. 2 2
      app/service/change_project.js
  36. 4 2
      app/service/change_project_audit.js
  37. 2 2
      app/service/ledger_tag.js
  38. 1 1
      app/service/project_account.js
  39. 1 1
      app/service/project_spread.js
  40. 32 16
      app/service/report.js
  41. 1 0
      app/service/stage_audit.js
  42. 2 0
      app/service/sub_project.js
  43. 2 2
      app/service/tender.js
  44. 3 3
      app/view/change/apply_information_modal.ejs
  45. 2 2
      app/view/change/information_modal.ejs
  46. 3 3
      app/view/change/plan_information_modal.ejs
  47. 3 3
      app/view/change/project_information_modal.ejs
  48. 1 1
      app/view/change/revise.ejs
  49. 1 1
      app/view/change/revise_modal.ejs
  50. 34 0
      app/view/ledger/gather.ejs
  51. 1 1
      app/view/profile/modal.ejs
  52. 1 0
      app/view/profile/sms.ejs
  53. 1 1
      app/view/tender/manage_modal.ejs
  54. 2 59
      sql/update.sql
  55. 71 0
      sql/update20240529.sql

+ 28 - 27
app/const/spread.js

@@ -268,7 +268,7 @@ const glSpreadTemplate = {
         { key: 'dgn_qty', valid: 1 },
         { key: 'dgn_price', valid: 1 },
         { key: 'deal_calc', valid: 1, },
-        { key: 'tz_calc', valid: 0 },
+        { key: 'tz_calc', valid: 1 },
         { key: 'drawing_code', valid: 1 },
         { key: 'node_type', valid: 1 },
         { key: 'memo', valid: 1 },
@@ -363,7 +363,7 @@ const szSpreadTemplate = {
         { key: 'dgn_qty', valid: 1 },
         { key: 'dgn_price', valid: 1 },
         { key: 'deal_calc', valid: 1, },
-        { key: 'tz_calc', valid: 0 },
+        { key: 'tz_calc', valid: 1 },
         { key: 'drawing_code', valid: 1 },
         { key: 'node_type', valid: 1 },
         { key: 'memo', valid: 1 },
@@ -458,7 +458,7 @@ const fjSpreadTemplate = {
         { key: 'dgn_qty', valid: 1 },
         { key: 'dgn_price', valid: 1 },
         { key: 'deal_calc', valid: 1, },
-        { key: 'tz_calc', valid: 0 },
+        { key: 'tz_calc', valid: 1 },
         { key: 'drawing_code', valid: 1 },
         { key: 'node_type', valid: 1 },
         { key: 'memo', valid: 1 },
@@ -528,7 +528,6 @@ const BaseSpreadColSetting = {
                 {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             ],
             drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', textIndent: 1}],
-            node_type: [{title: '费用类别', colSpan: '1', rowSpan: '2', field: 'node_type', hAlign: 0, width: 100, cellType: 'customizeCombo'}],
             memo: [{title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}, ],
             ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}, ],
@@ -538,13 +537,13 @@ const BaseSpreadColSetting = {
             name: [{title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'}],
             position: [{title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@'}],
             tz_calc: [
-                {title: '台账数量|设计量', colSpan: '4|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '台账数量|设计量', colSpan: '5|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '|设计量公式', colSpan: '|1', rowSpan: '|1', field: 'sgfh_expr', hAlign: 2, width: 100},
                 {title: '|设计错漏增减', colSpan: '|1', rowSpan: '|1', field: 'sjcl_qty', hAlign: 2, width: 100, type: 'Number'},
                 {title: '|其他错漏增减', colSpan: '|1', rowSpan: '|1', field: 'qtcl_qty', hAlign: 2, width: 100, type: 'Number'},
                 {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             ],
             drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', textIndent: 1}],
-            node_type: [{title: '费用类别', colSpan: '1', rowSpan: '2', field: 'node_type', hAlign: 0, width: 100, cellType: 'customizeCombo'}],
             ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}]
@@ -629,8 +628,8 @@ const BaseSpreadColSetting = {
             ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             add_stage_order: [{title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage_order', hAlign:1, width: 80, readOnly: true}],
-            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
-            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
+            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
         }
     },
     gcl_ledger_set: {
@@ -670,7 +669,8 @@ const BaseSpreadColSetting = {
             name: [{title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'}],
             position: [{title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@'}],
             tz_calc: [
-                {title: '台账数量|设计量', colSpan: '4|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '台账数量|设计量', colSpan: '5|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '|设计量公式', colSpan: '|1', rowSpan: '|1', field: 'sgfh_expr', hAlign: 2, width: 100},
                 {title: '|设计错漏增减', colSpan: '|1', rowSpan: '|1', field: 'sjcl_qty', hAlign: 2, width: 100, type: 'Number'},
                 {title: '|其他错漏增减', colSpan: '|1', rowSpan: '|1', field: 'qtcl_qty', hAlign: 2, width: 100, type: 'Number'},
                 {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: true},
@@ -728,8 +728,8 @@ const BaseSpreadColSetting = {
             ],
             final_dgn_price: [{title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'final_dgn_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},],
             is_tp: [{title: '总额计量', colSpan: '1', rowSpan: '2', field: 'is_tp', hAlign: 1, width: 60, cellType: 'checkbox'}],
-            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
-            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
+            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
             drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', textIndent: 1}],
             memo: [{title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             postil: [{title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},],
@@ -760,20 +760,21 @@ const BaseSpreadColSetting = {
             ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
             add_stage_order: [{title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage_order', hAlign:1, width: 80, readOnly: true}],
-            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
-            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
+            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true}],
         }
     },
 };
 const withoutClReplace = {
     indexField: 'sgfh_qty',
-    remove: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'],
+    remove: ['sgfh_qty', 'sgfh_expr', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'],
     billsCols: [
         {title: '设计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
         {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sgfh_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
     ],
     posCols: [
-        {title: '设计量', colSpan: '1', rowSpan: '2', field: 'sgfh_qty', hAlign: 2, width: 120, type: 'Number'},
+        {title: '台账数量|设计量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+        {title: '|设计量公式', colSpan: '|1', rowSpan: '|1', field: 'sgfh_expr', hAlign: 2, width: 100},
     ],
 };
 const SpreadSpec = {
@@ -999,8 +1000,8 @@ const stageTz = {
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '总额计量', colSpan: '1', rowSpan: '2', field: 'is_tp', hAlign: 1, width: 60, cellType: 'checkbox'},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -1030,8 +1031,8 @@ const stageTz = {
             {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 3,
         headRows: 2,
@@ -1082,8 +1083,8 @@ const stageCl = {
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '总额计量', colSpan: '1', rowSpan: '2', field: 'is_tp', hAlign: 1, width: 60, cellType: 'checkbox'},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -1113,8 +1114,8 @@ const stageCl = {
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage_order', hAlign:1, width: 80, readOnly: true},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 20,
         headRows: 2,
@@ -1165,8 +1166,8 @@ const stageNoCl = {
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '总额计量', colSpan: '1', rowSpan: '2', field: 'is_tp', hAlign: 1, width: 60, cellType: 'checkbox'},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -1196,8 +1197,8 @@ const stageNoCl = {
             {title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage_order', hAlign:1, width: 80, readOnly: true},
-            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
-            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 0, width: 80, formatter: '@', readOnly: true},
         ],
         emptyRows: 20,
         headRows: 2,

+ 8 - 1
app/controller/change_controller.js

@@ -1484,7 +1484,7 @@ module.exports = app => {
                 // 获取原报dsk数据
                 const accountInfo = await ctx.service.projectAccount.getDataById(change.uid);
                 renderData.dskAccountData = accountInfo && accountInfo.dsk_account ? JSON.parse(accountInfo.dsk_account) : {};
-                renderData.dskProjects = accountInfo && accountInfo.dsk_projects ? JSON.parse(accountInfo.dsk_projects) : [];
+                renderData.dskProjects = accountInfo && accountInfo.dsk_account && accountInfo.dsk_projects ? JSON.parse(accountInfo.dsk_projects) : [];
                 // 台账只读、使用数据
                 renderData.readOnly = !edit;
                 renderData.changing = changing;
@@ -1658,6 +1658,13 @@ module.exports = app => {
         async _addDeal(ctx, data) {
             if (!data.type || !data.dealBills) throw '数据错误';
             data.dealBills.unit_price = this.ctx.helper.round(data.dealBills.unit_price, ctx.tender.info.decimal.up);
+            if (data.dealBills.quantity && data.dealBills.unit) {
+                const precision = ctx.helper.findPrecision(ctx.tender.info.precision, data.dealBills.unit);
+                data.dealBills.quantity = ctx.helper.round(data.dealBills.quantity, precision.value);
+                data.dealBills.sgfh_qty = data.dealBills.quantity;
+                data.dealBills.total_price = ctx.helper.mul(data.dealBills.quantity, data.dealBills.unit_price, ctx.tender.info.decimal.tp);
+                data.dealBills.sgfh_tp = data.dealBills.total_price;
+            }
             if (data.type === 'child') {
                 return await ctx.service.changeLedger.addChild(ctx.tender.id, data.id, data.dealBills, ctx.change.cid);
             } else if (data.type === 'next') {

+ 1 - 1
app/controller/stage_controller.js

@@ -1441,7 +1441,7 @@ module.exports = app => {
                 }
 
                 const data = JSON.parse(ctx.request.body.data);
-                await ctx.service.stageAudit.saveAudit(ctx.stage.id, ctx.stage.times, data);
+                await ctx.service.stageAudit.saveAudit(ctx.stage.id, ctx.stage.times, 0, data);
 
                 const auditors = await ctx.service.stageAudit.getUniqUserGroup(ctx.stage.id, ctx.stage.times);
                 ctx.body = { err: 0, msg: '', data: auditors };

+ 4 - 2
app/lib/dsk.js

@@ -71,7 +71,7 @@ class DSK {
         const url = this.url + 'api/compilation/external/list';
         const postData = {};
         const result = this.dealWith(await this.ctx.helper.sendRequest(url, postData, 'GET', 'json', true));
-        return result.filter(item => { return item.type === 'highway'; });
+        return result.filter(item => { return item.type === 'highway' && (item.name === '全国公路(2018)' || item.name === '广东公路(2018)'); });
     }
 
     async getProjectList(mobile, compilationId) {
@@ -82,7 +82,9 @@ class DSK {
             token,
             mobile,
         };
-        return this.dealWith(await this.ctx.helper.sendRequest(url, postData, 'POST', 'json', true));
+        const result = this.dealWith(await this.ctx.helper.sendRequest(url, postData, 'POST', 'json', true));
+        // return result.filter(item => { return !item.property || !item.property.fileType || (item.property.fileType && [1, 5, 15, 16].indexOf(item.property.fileType) === -1); });
+        return result;
     }
 
     async getProjectTree(compilationId, projectId) {

+ 61 - 0
app/lib/rm/advance.js

@@ -0,0 +1,61 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const RptMemBase = require('./base');
+const bindData = {};
+
+class rptMemChange extends RptMemBase {
+    constructor(ctx) {
+        super(ctx, bindData);
+    }
+
+    async doCheckAdvance(advanceId) {
+        if (this.ctx.advance) return;
+        this.ctx.advance = await this.ctx.service.advance.getDataByCondition({ cid: advanceId });
+    }
+
+    async doCheckTender(tenderId) {
+        if (this.ctx.tender) return;
+        this.ctx.tender = { id: tenderId };
+        this.ctx.tender.data = await this.ctx.service.tender.getTender(tenderId);
+        this.ctx.tender.info = await this.ctx.service.tenderInfo.getTenderInfo(tenderId);
+    }
+
+    async doBeforeLoadReport(params) {
+        await this.doCheckAdvance(params.advance_id);
+        await this.doCheckTender(this.ctx.change.tid);
+    }
+
+    async _getAdvanceAudit() {
+        const advanceAudit = await this.ctx.service.advanceAudit.getAllDataByCondition({ where: { vid: this.ctx.advance.id, times: this.ctx.advance.times }});
+        return this.ctx.helper.filterLastestData(advanceAudit, ['audit_id']);
+    }
+
+    getCommonData(params, tableName, fields, customDefine, customSelect) {
+        switch (tableName) {
+            case 'mem_advance':
+                return [this.ctx.advance];
+            case 'mem_advance_audit':
+                return this._getAdvanceAudit();
+            case 'mem_advance_file':
+                return this.ctx.service.advanceFile.getAllDataByCondition({ where: { vid: this.ctx.advance.id }});
+            case 'mem_project':
+                return this.ctx.service.project.getDataByCondition({ id: this.ctx.session.sessionProject.id });
+            case 'mem_tender':
+                return [this.ctx.tender.data];
+            case 'mem_tender_info':
+                return [this.ctx.tender.info];
+            default:
+                return [];
+        }
+    }
+}
+
+module.exports = rptMemChange;

+ 119 - 100
app/lib/rm/material.js

@@ -8,8 +8,12 @@
  * @version
  */
 
-const materialConst = require('../../const/material');
+const RptMemBase = require('./base');
+const bindData = {
+    materialGather: ['mem_material_gather_bills', 'mem_material_gather_xmj', 'mem_material_gather_gl'],
+};
 
+const materialConst = require('../../const/material');
 const Ledger = require('../../lib/ledger');
 
 const billsFields = (function () {
@@ -39,9 +43,26 @@ const posFields = (function () {
     return {cur, pre, end, final, stage, stageEnd, bgl};
 })();
 
-class ReportMemoryMaterial {
+class rptMemChange extends RptMemBase {
     constructor(ctx) {
-        this.ctx = ctx;
+        super(ctx, bindData);
+    }
+
+    async doCheckMaterial(materialId) {
+        if (this.ctx.material) return;
+        this.ctx.material = await this.ctx.service.material.getDataByCondition({ id: materialId });
+    }
+
+    async doCheckTender(tenderId) {
+        if (this.ctx.tender) return;
+        this.ctx.tender = { id: tenderId };
+        this.ctx.tender.data = await this.ctx.service.tender.getTender(tenderId);
+        this.ctx.tender.info = await this.ctx.service.tenderInfo.getTenderInfo(tenderId);
+    }
+
+    async doBeforeLoadReport(params) {
+        await this.doCheckMaterial(params.material_id);
+        await this.doCheckTender(this.ctx.material.tid);
     }
 
     _getNewPos(updateFields) {
@@ -59,7 +80,6 @@ class ReportMemoryMaterial {
             }
         });
     }
-
     _getNewBillsTree(calcFields) {
         return new Ledger.billsTree(this.ctx, {
             id: 'ledger_id',
@@ -91,17 +111,7 @@ class ReportMemoryMaterial {
         });
     }
 
-    _checkFieldsExist(source, check) {
-        for (const s of source) {
-            if (check.indexOf(s) >= 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    async getSelectMaterialAuditors(tid, material_order, fields) {
-        await this.ctx.service.material.checkMaterial(tid, material_order);
+    async getMaterialAuditors() {
         const auditors = await this.ctx.service.materialAudit.getFinalAuditGroup(this.ctx.material.id, this.ctx.material.curTimes);
         const user = await this.ctx.service.projectAccount.getDataById(this.ctx.material.user_id);
         const result = [{
@@ -120,17 +130,22 @@ class ReportMemoryMaterial {
         return result;
     }
 
-    async getMaterial(tender_id, material_order, fields) {
-        const result = await this.ctx.service.material.getValidMaterials(tender_id);
-        if (this._checkFieldsExist(fields, ['checked_time'])) {
-            for (const r of result) {
-                const auditors = await this.ctx.service.materialAudit.getFinalAuditGroup(r.id, r.curTimes || r.times);
-                r.checked_time = !r.curTimes ? auditors[auditors.length - 1].end_time : null;
-            }
+    async _loadMaterialMonth(material, gl) {
+        const materialMonth = await this.ctx.service.materialMonth.getAllDataByCondition({
+            where: { mid: material.id },
+            orders: [['mb_id', 'asc'], ['yearmonth', 'asc']],
+        });
+
+        const month = this.ctx.helper._.uniq(materialMonth.map(x => { return x.yearmonth; }));
+        let g;
+        for (const mm of materialMonth) {
+            if (!g || g.id !== mm.mb_id) g = gl.find(x => { return mm.mb_id === x.id; });
+            if (!g.month_msg_tp) g.month_msg_tp = [];
+            if (!g.month) g.month = month.concat([]);
+            const index = month.indexOf(mm.yearmonth);
+            if (index >= 0) g.month_msg_tp[index] = mm.msg_tp;
         }
-        return result;
     }
-
     _completeMaterialGl(materialGl, decimal) {
         const tTypeStr = [], mTypeStr = [];
         for (const t of materialConst.t_type) {
@@ -146,34 +161,18 @@ class ReportMemoryMaterial {
             gl.end_tp = this.ctx.helper.add(gl.tp, gl.pre_tp);
         }
     }
+    async getMaterialGl(fields) {
+        if (!this.ctx.material) return [];
 
-    async _loadMaterialMonth(material, gl) {
-        const materialMonth = await this.ctx.service.materialMonth.getAllDataByCondition({
-            where: { mid: material.id },
-            orders: [['mb_id', 'asc'], ['yearmonth', 'asc']],
-        });
-
-        const month = this.ctx.helper._.uniq(materialMonth.map(x => { return x.yearmonth; }));
-        let g;
-        for (const mm of materialMonth) {
-            if (!g || g.id !== mm.mb_id) g = gl.find(x => { return mm.mb_id === x.id; });
-            if (!g.month_msg_tp) g.month_msg_tp = [];
-            if (!g.month) g.month = month.concat([]);
-            const index = month.indexOf(mm.yearmonth);
-            if (index >= 0) g.month_msg_tp[index] = mm.msg_tp;
-        }
-    }
-
-    async getMaterialGl(tender_id, material_order, fields) {
         const materials = await this.ctx.service.material.getAllDataByCondition({
-            where: {tid: tender_id},
+            where: {tid: this.ctx.material.tid},
             orders: [['order', 'desc']],
         });
         if (materials.length === 0) return [];
 
         let result, material, decimal;
-        if (materials[0].order === material_order) {
-            material = materials[0];
+        material = this.ctx.material;
+        if (materials[0].order === material.order) {
             decimal = material.decimal ? JSON.parse(material.decimal) : { qty: 3, up: 3, tp: 2 };
             if (material.is_stage_self) {
                 const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
@@ -189,13 +188,10 @@ class ReportMemoryMaterial {
                 result = await this.ctx.app.mysql.query(sql, [material.id]);
             } else {
                 result = await this.ctx.service.materialBills.getAllDataByCondition({
-                    where: {tid: tender_id}
+                    where: {tid: material.tid}
                 });
             }
         } else {
-            material = this.ctx.helper._.find(materials, {order: material_order});
-            if (!material) return [];
-
             decimal = material.decimal ? JSON.parse(material.decimal) : { tp: 2 };
             if (material.is_stage_self) {
                 const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
@@ -220,7 +216,7 @@ class ReportMemoryMaterial {
                     '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON mbh.mb_id = mb.id ' +
                     '  WHERE mbh.tid = ? And mbh.mid = ?'+
                     '  ORDER By mb.order';
-                result = await this.ctx.app.mysql.query(sql, [tender_id, material.id]);
+                result = await this.ctx.app.mysql.query(sql, [material.tid, material.id]);
             }
         }
         this._completeMaterialGl(result, decimal);
@@ -229,26 +225,25 @@ class ReportMemoryMaterial {
         return result;
     }
 
-    async getMaterialGlDetail(tender_id, material_order, fields) {
-        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+    async getMaterialGlDetail(fields) {
+        const material = this.ctx.material;
         if (!material) return [];
 
         if (material.is_stage_self) {
-            return await this.ctx.service.materialList.getMaterialStageData(tender_id, material.id);
+            return await this.ctx.service.materialList.getMaterialStageData(material.tid, material.id);
         } else {
-            return await this.ctx.service.materialList.getMaterialData(tender_id, material.id);
+            return await this.ctx.service.materialList.getMaterialData(material.tid, material.id);
         }
     }
 
-    async getMaterialBills(tender_id, material_order, fields, showLevel) {
-        await this.ctx.service.tender.checkTender(tender_id);
-        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+    async getMaterialBills(fields, showLevel) {
+        const material = this.ctx.material;
         if (!material) return [];
 
         try {
-            const billsData = await this.ctx.service.ledger.getData(tender_id);
+            const billsData = await this.ctx.service.ledger.getData(material.tid);
             if (this._checkFieldsExist(fields, billsFields.stage)) {
-                const curStage = await this.ctx.service.stageBills.getStagesData(tender_id, material.stage_id);
+                const curStage = await this.ctx.service.stageBills.getStagesData(material.tid, material.stage_id);
                 this.ctx.helper.assignRelaData(billsData, [
                     {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid'}
                 ]);
@@ -265,14 +260,14 @@ class ReportMemoryMaterial {
         }
     }
 
-    async getMaterialPos(tender_id, material_order, fields) {
-        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+    async getMaterialPos(fields) {
+        const material = this.ctx.material;
         if (!material) return [];
 
         try {
-            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: {tid: tender_id }});
+            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: {tid: material.tid }});
             if (this._checkFieldsExist(fields, posFields.stage)) {
-                const curPosStage = await this.ctx.service.stagePos.getStagesData(tender_id, material.stage_id);
+                const curPosStage = await this.ctx.service.stagePos.getStagesData(material.tid, material.stage_id);
                 this.ctx.helper.assignRelaData(posData, [
                     {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
                 ]);
@@ -288,23 +283,51 @@ class ReportMemoryMaterial {
         }
     }
 
-    getMaterialCalcQty(qtySource, info, is_join) {
-        let qty = 0;
-        switch(qtySource) {
-            case materialConst.qty_source_value.gather_qty: qty = info.gather_qty; break;
-            case materialConst.qty_source_value.contract_qty: qty = info.contract_qty; break;
-            case materialConst.qty_source_value.gather_minus_qty: qty = info.gather_minus_qty; break;
-            default: throw '未配置计量来源';
+    async getMaterialStage(fields) {
+        const material = this.ctx.material;
+        if (!material) return [];
+
+        if (material.is_stage_self) {
+            return await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid: material.id } });
+        } else {
+            return [{
+                id: -1, tid: material.id, mid: material.id, sid: material.stage_id, order: material.stage_order,
+                m_tp: material.m_tp, m_tax_tp: material.m_tax_tp,
+            }];
         }
-        if (qtySource !== materialConst.qty_source_value.contract_qty && is_join === 2) {
-            qty = info.contract_qty;
+    }
+
+    getCommonData(params, tableName, fields, customDefine, customSelect) {
+        switch (tableName) {
+            case 'mem_project':
+                return this.ctx.service.project.getDataByCondition({ id: this.ctx.session.sessionProject.id });
+            case 'mem_tender':
+                return [this.ctx.tender.data];
+            case 'mem_tender_info':
+                return [this.ctx.tender.info];
+            case 'mem_material':
+                return [this.ctx.material];
+            case 'mem_material_audit':
+                return this.getMaterialAuditors();
+            case 'mem_material_gl':
+                return this.getMaterialGl(fields);
+            case 'mem_material_gl_detail':
+                return this.getMaterialGlDetail(fields);
+            case 'mem_material_bills':
+                return this.getMaterialBills(fields);
+            case 'mem_material_bills_filter':
+                return this.getMaterialBills(fields, true);
+            case 'mem_material_pos':
+                return this.getMaterialPos(fields);
+            case 'mem_material_stage':
+                return this.getMaterialStage(fields);
+            default:
+                return [];
         }
-        return qty;
     }
 
     async _getMaterialStageGatherBills(tender_id, stage_id, stage_order, stageSelf, stageIndex = 0) {
         const decimal = this.materialGatherBase.decimal;
-        //const billsData = this.ctx.helper.clone(this.materialGatherBase.billsData);
         const billsData = this.materialGatherBase.billsData;
         billsData.forEach(x => {
             for (const prop of ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty']) {
@@ -319,7 +342,6 @@ class ReportMemoryMaterial {
         billsTree.loadDatas(billsData);
         billsTree.calculateAll();
 
-        //const posData = this.ctx.helper.clone(this.materialGatherBase.posData);
         const posData = this.materialGatherBase.posData;
         posData.forEach(x => {
             for (const prop of ['contract_qty', 'qc_qty', 'qc_minus_qty']) {
@@ -385,29 +407,31 @@ class ReportMemoryMaterial {
         return [gatherUtil.gclList, gatherUtil.leafXmjs];
     }
 
-    async getMaterialGatherBills(tender_id, material_order) {
+    async getMaterialGatherBills() {
+        const material = this.ctx.material;
+        if (!material) return {};
+
         const materials = await this.ctx.service.material.getAllDataByCondition({
-            where: { tid: tender_id },
+            where: { tid: material.tid },
             orders: [['order', 'desc']],
         });
         if (materials.length === 0) return {};
 
-        const material = await this.ctx.service.material.getDataByCondition({ tid: tender_id, order: material_order });
         this.materialGatherBase = {};
         this.materialGatherBase.qtySource = material.qty_source;
         this.materialGatherBase.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
         try {
             // 获取基础数据
-            this.materialGatherBase.billsData = await this.ctx.service.ledger.getData(tender_id);
-            this.materialGatherBase.posData = await this.ctx.service.pos.getPosData({ tid: tender_id });
+            this.materialGatherBase.billsData = await this.ctx.service.ledger.getData(material.tid);
+            this.materialGatherBase.posData = await this.ctx.service.pos.getPosData({ tid: material.tid });
             if (material.is_stage_self) {
-                this.materialGatherBase.materialGl = material_order === materials[0].order
-                    ? await this.ctx.service.materialList.getMaterialStageData(tender_id, material.id)
-                    : await this.ctx.service.materialList.getPreMaterialStageData(tender_id, material.id);
+                this.materialGatherBase.materialGl = material.order === materials[0].order
+                    ? await this.ctx.service.materialList.getMaterialStageData(material.tid, material.id)
+                    : await this.ctx.service.materialList.getPreMaterialStageData(material.tid, material.id);
             } else {
-                this.materialGatherBase.materialGl = material_order === materials[0].order
-                    ? await this.ctx.service.materialList.getMaterialData(tender_id, material.id)
-                    : await this.ctx.service.materialList.getPreMaterialData(tender_id, material.id);
+                this.materialGatherBase.materialGl = material.order === materials[0].order
+                    ? await this.ctx.service.materialList.getMaterialData(material.tid, material.id)
+                    : await this.ctx.service.materialList.getPreMaterialData(material.tid, material.id);
             }
             this.materialGatherBase.materialNotJoin = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { mid: material.id, type: 1 } });
             this.materialGatherBase.materialNotChange = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { mid: material.id, type: 2 } });
@@ -417,12 +441,12 @@ class ReportMemoryMaterial {
                 const stageIds = material.stage_id.split(',');
                 const stageOrders = material.s_order.split(',');
                 for (const [i, sid] of stageIds.entries()) {
-                    const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(tender_id, sid, stageOrders[i], true, i + 1);
+                    const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(material.tid, sid, stageOrders[i], true, i + 1);
                     mem_material_gather_bills.push(...gclList);
                     mem_material_gather_xmj.push(...leafXmjs);
                 }
             } else {
-                const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(tender_id, material.stage_id, material.stage_order, false);
+                const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(material.tid, material.stage_id, material.stage_order, false);
                 mem_material_gather_bills.push(...gclList);
                 mem_material_gather_xmj.push(...leafXmjs);
             }
@@ -433,19 +457,14 @@ class ReportMemoryMaterial {
         }
     }
 
-    async getMaterialStage(tender_id, material_order, fields) {
-        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
-        if (!material) return [];
-
-        if (material.is_stage_self) {
-            return await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid: material.id } });
-        } else {
-            return [{
-                id: -1, tid: material.id, mid: material.id, sid: material.stage_id, order: material.stage_order,
-                m_tp: material.m_tp, m_tax_tp: material.m_tax_tp,
-            }];
+    async getBindData(params, key, fields, customDefine, customSelect) {
+        switch (key) {
+            case 'materialGather':
+                return await this.getMaterialGatherBills();
+            default:
+                return {};
         }
     }
 }
 
-module.exports = ReportMemoryMaterial;
+module.exports = rptMemChange;

+ 2 - 2
app/lib/rm/tender.js

@@ -14,8 +14,8 @@ const bindData = {
     gatherChange: ['mem_gather_change', 'mem_gather_change_bills'],
     fjChange: ['mem_fj_change_progress', 'mem_fj_change_sum'],
 };
-const BudgetSource = require('./budget');
-const MaterialSource = require('./material');
+const BudgetSource = require('./tender_budget');
+const MaterialSource = require('./tender_material');
 const rptCustomData = require('../rptCustomData');
 
 class rptMemPaymentSafe extends RptMemBase {

+ 451 - 0
app/lib/rm/tender_material.js

@@ -0,0 +1,451 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const materialConst = require('../../const/material');
+
+const Ledger = require('../../lib/ledger');
+
+const billsFields = (function () {
+    const cur = ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp', 'postil'];
+    const pre = ['pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_gather_qty', 'pre_gather_tp'];
+    const end = ['end_contract_qty', 'end_contract_tp', 'end_qc_qty', 'end_qc_tp', 'end_gather_qty', 'end_gather_tp'];
+    const final = ['final_tp', 'final_ratio'];
+    const stageDgn = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'];
+
+    const stage = cur.concat(pre, end, final);
+    const stageEnd = pre.concat(end, final);
+    const bgl = ['qc_bgl_code'];
+    const leafXmj = ['leaf_xmj_id'];
+
+    return {cur, pre, end, final, stageDgn, stage, stageEnd, bgl, leafXmj};
+})();
+const posFields = (function () {
+    const cur = ['contract_qty', 'qc_qty', 'gather_qty', 'postil'];
+    const pre = ['pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty'];
+    const end = ['end_contract_qty', 'end_qc_qty', 'end_gather_qty'];
+    const final = ['final_ratio'];
+
+    const stage = cur.concat(pre, end, final);
+    const stageEnd = pre.concat(end, final);
+    const bgl = ['qc_bgl_code'];
+
+    return {cur, pre, end, final, stage, stageEnd, bgl};
+})();
+
+class ReportMemoryMaterial {
+    constructor(ctx) {
+        this.ctx = ctx;
+    }
+
+    _getNewPos(updateFields) {
+        const helper = this.ctx.helper;
+        return new Ledger.pos({
+            id: 'id', ledgerId: 'lid',
+            updateFields: ['contract_qty', 'qc_qty', 'postil'],
+            calc: function (p) {
+                p.pre_gather_qty = helper.add(p.pre_contract_qty, p.pre_qc_qty);
+                p.gather_qty = helper.add(p.contract_qty, p.qc_qty);
+                p.gather_minus_qty = helper.add(p.gather_qty, p.qc_minus_qty);
+                p.end_contract_qty = helper.add(p.pre_contract_qty, p.contract_qty);
+                p.end_qc_qty = helper.add(p.pre_qc_qty, p.qc_qty);
+                p.end_gather_qty = helper.add(p.pre_gather_qty, p.gather_qty);
+            }
+        });
+    }
+
+    _getNewBillsTree(calcFields) {
+        return new Ledger.billsTree(this.ctx, {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+            stageId: 'id',
+            calcFields: calcFields || ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
+            calc: function (node, helper) {
+                if (node.children && node.children.length === 0) {
+                    node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
+                    node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                    node.gather_minus_qty = helper.add(node.gather_qty, node.qc_minus_qty);
+                    node.end_contract_qty = helper.add(node.pre_contract_qty, node.contract_qty);
+                    node.end_qc_qty = helper.add(node.pre_qc_qty, node.qc_qty);
+                    node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
+                }
+                node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
+                node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
+                node.end_contract_tp = helper.add(node.pre_contract_tp, node.contract_tp);
+                node.end_qc_tp = helper.add(node.pre_qc_tp, node.qc_tp);
+                node.end_gather_tp = helper.add(node.pre_gather_tp, node.gather_tp);
+
+                node.final_tp = helper.add(node.total_price, node.end_qc_tp);
+                node.final_ratio = helper.mul(helper.div(node.end_gather_tp, node.final_tp, 4), 100);
+            }
+        });
+    }
+
+    _checkFieldsExist(source, check) {
+        for (const s of source) {
+            if (check.indexOf(s) >= 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    async getSelectMaterialAuditors(tid, material_order, fields) {
+        await this.ctx.service.material.checkMaterial(tid, material_order);
+        const auditors = await this.ctx.service.materialAudit.getFinalAuditGroup(this.ctx.material.id, this.ctx.material.curTimes);
+        const user = await this.ctx.service.projectAccount.getDataById(this.ctx.material.user_id);
+        const result = [{
+            aid: user.id,
+            name: user.name,
+            company: user.company,
+            role: user.role,
+            mobile: user.mobile,
+            telephone: user.telephone,
+            sign_path: user.sign_path,
+            opinion: user.opinion,
+            end_time: auditors && auditors.length > 0 ? auditors[0].begin_time : null,
+            sort: 0,
+            tp_data: this.ctx.material.tp_data,
+        }, ...auditors];
+        return result;
+    }
+
+    async getMaterial(tender_id, material_order, fields) {
+        const result = await this.ctx.service.material.getValidMaterials(tender_id);
+        if (this._checkFieldsExist(fields, ['checked_time'])) {
+            for (const r of result) {
+                const auditors = await this.ctx.service.materialAudit.getFinalAuditGroup(r.id, r.curTimes || r.times);
+                r.checked_time = !r.curTimes ? auditors[auditors.length - 1].end_time : null;
+            }
+        }
+        return result;
+    }
+
+    _completeMaterialGl(materialGl, decimal) {
+        const tTypeStr = [], mTypeStr = [];
+        for (const t of materialConst.t_type) {
+            tTypeStr[t.value] = t.text;
+        }
+        for (const m of materialConst.m_type) {
+            mTypeStr[m.value] = m.text;
+        }
+        for (const gl of materialGl) {
+            gl.tp = this.ctx.helper.mul(gl.quantity, gl.m_spread, decimal.tp);
+            gl.t_type_str = tTypeStr[gl.t_type];
+            gl.m_type_str = mTypeStr[gl.m_type];
+            gl.end_tp = this.ctx.helper.add(gl.tp, gl.pre_tp);
+        }
+    }
+
+    async _loadMaterialMonth(material, gl) {
+        const materialMonth = await this.ctx.service.materialMonth.getAllDataByCondition({
+            where: { mid: material.id },
+            orders: [['mb_id', 'asc'], ['yearmonth', 'asc']],
+        });
+
+        const month = this.ctx.helper._.uniq(materialMonth.map(x => { return x.yearmonth; }));
+        let g;
+        for (const mm of materialMonth) {
+            if (!g || g.id !== mm.mb_id) g = gl.find(x => { return mm.mb_id === x.id; });
+            if (!g.month_msg_tp) g.month_msg_tp = [];
+            if (!g.month) g.month = month.concat([]);
+            const index = month.indexOf(mm.yearmonth);
+            if (index >= 0) g.month_msg_tp[index] = mm.msg_tp;
+        }
+    }
+
+    async getMaterialGl(tender_id, material_order, fields) {
+        const materials = await this.ctx.service.material.getAllDataByCondition({
+            where: {tid: tender_id},
+            orders: [['order', 'desc']],
+        });
+        if (materials.length === 0) return [];
+
+        let result, material, decimal;
+        if (materials[0].order === material_order) {
+            material = materials[0];
+            decimal = material.decimal ? JSON.parse(material.decimal) : { qty: 3, up: 3, tp: 2 };
+            if (material.is_stage_self) {
+                const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    msb.quantity, mb.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    msb.msg_tp, msb.msg_times, msb.msg_spread, mb.m_up_risk, mb.m_down_risk, msb.m_spread, msb.m_tp, mb.pre_tp, msb.m_tax_tp, mb.tax_pre_tp, mb.origin, ' +
+                    '    msb.remark, msb.is_summary, mb.m_tax, mb.in_time, mb.origin' +
+                    `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
+                    `  LEFT JOIN ${this.ctx.service.materialBills.tableName} mb ON msb.mb_id = mb.id` +
+                    '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
+                    `  WHERE msb.mid = ?` +
+                    '  ORDER By msb.ms_id, mb.order';
+                result = await this.ctx.app.mysql.query(sql, [material.id]);
+            } else {
+                result = await this.ctx.service.materialBills.getAllDataByCondition({
+                    where: {tid: tender_id}
+                });
+            }
+        } else {
+            material = this.ctx.helper._.find(materials, {order: material_order});
+            if (!material) return [];
+
+            decimal = material.decimal ? JSON.parse(material.decimal) : { tp: 2 };
+            if (material.is_stage_self) {
+                const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    msb.quantity, mbh.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    msb.msg_tp, msb.msg_times, msb.msg_spread, mbh.m_up_risk, mbh.m_down_risk, msb.m_spread, msb.m_tp, mbh.pre_tp, msb.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
+                    '    msb.remark, msb.is_summary, mbh.m_tax, mb.in_time, mbh.origin' +
+                    `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
+                    '  LEFT JOIN ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ON msb.mb_id = mbh.mb_id AND msb.mid = mbh.mid' +
+                    '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON msb.mb_id = mb.id ' +
+                    '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
+                    '  WHERE msb.mid = ?'+
+                    '  ORDER By msb.ms_id, mb.order';
+                result = await this.ctx.app.mysql.query(sql, [material.id]);
+            } else {
+                const sql = 'SELECT mb.id, mb.tid, mb.mid, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    mbh.quantity, mbh.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    mbh.msg_tp, mbh.msg_times, mbh.msg_spread, mbh.m_up_risk, mbh.m_down_risk, mbh.m_spread, mbh.m_tp, mbh.pre_tp, mbh.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
+                    '    mb.remark, mb.is_summary, mbh.m_tax, mb.in_time, mbh.origin' +
+                    '  FROM ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ' +
+                    '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON mbh.mb_id = mb.id ' +
+                    '  WHERE mbh.tid = ? And mbh.mid = ?'+
+                    '  ORDER By mb.order';
+                result = await this.ctx.app.mysql.query(sql, [tender_id, material.id]);
+            }
+        }
+        this._completeMaterialGl(result, decimal);
+
+        if (this._checkFieldsExist(fields, ['month_msg_tp', 'month'])) await this._loadMaterialMonth(material, result);
+        return result;
+    }
+
+    async getMaterialGlDetail(tender_id, material_order, fields) {
+        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+        if (!material) return [];
+
+        if (material.is_stage_self) {
+            return await this.ctx.service.materialList.getMaterialStageData(tender_id, material.id);
+        } else {
+            return await this.ctx.service.materialList.getMaterialData(tender_id, material.id);
+        }
+    }
+
+    async getMaterialBills(tender_id, material_order, fields, showLevel) {
+        await this.ctx.service.tender.checkTender(tender_id);
+        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+        if (!material) return [];
+
+        try {
+            const billsData = await this.ctx.service.ledger.getData(tender_id);
+            if (this._checkFieldsExist(fields, billsFields.stage)) {
+                const curStage = await this.ctx.service.stageBills.getStagesData(tender_id, material.stage_id);
+                this.ctx.helper.assignRelaData(billsData, [
+                    {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid'}
+                ]);
+            }
+
+            const billsTree = this._getNewBillsTree();
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+
+            return showLevel ? billsTree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : billsTree.getDefaultDatas();
+        } catch(err) {
+            this.ctx.helper.log(err);
+            return [];
+        }
+    }
+
+    async getMaterialPos(tender_id, material_order, fields) {
+        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+        if (!material) return [];
+
+        try {
+            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: {tid: tender_id }});
+            if (this._checkFieldsExist(fields, posFields.stage)) {
+                const curPosStage = await this.ctx.service.stagePos.getStagesData(tender_id, material.stage_id);
+                this.ctx.helper.assignRelaData(posData, [
+                    {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
+                ]);
+            }
+            const pos = this._getNewPos();
+            pos.loadDatas(posData);
+            pos.calculateAll();
+
+            return pos.getDatas();
+        } catch (err) {
+            this.ctx.helper.log(err);
+            return [];
+        }
+    }
+
+    getMaterialCalcQty(qtySource, info, is_join) {
+        let qty = 0;
+        switch(qtySource) {
+            case materialConst.qty_source_value.gather_qty: qty = info.gather_qty; break;
+            case materialConst.qty_source_value.contract_qty: qty = info.contract_qty; break;
+            case materialConst.qty_source_value.gather_minus_qty: qty = info.gather_minus_qty; break;
+            default: throw '未配置计量来源';
+        }
+        if (qtySource !== materialConst.qty_source_value.contract_qty && is_join === 2) {
+            qty = info.contract_qty;
+        }
+        return qty;
+    }
+
+    async _getMaterialStageGatherBills(tender_id, stage_id, stage_order, stageSelf, stageIndex = 0) {
+        const decimal = this.materialGatherBase.decimal;
+        //const billsData = this.ctx.helper.clone(this.materialGatherBase.billsData);
+        const billsData = this.materialGatherBase.billsData;
+        billsData.forEach(x => {
+            for (const prop of ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty']) {
+                x[prop] = undefined;
+            }
+        });
+        const curStageBills = await this.ctx.service.stageBills.getStagesData(tender_id, stage_id);
+        this.ctx.helper.assignRelaData(billsData, [
+            { data: curStageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: '', relaId: 'lid' },
+        ]);
+        const billsTree = this._getNewBillsTree();
+        billsTree.loadDatas(billsData);
+        billsTree.calculateAll();
+
+        //const posData = this.ctx.helper.clone(this.materialGatherBase.posData);
+        const posData = this.materialGatherBase.posData;
+        posData.forEach(x => {
+            for (const prop of ['contract_qty', 'qc_qty', 'qc_minus_qty']) {
+                x[prop] = undefined;
+            }
+        });
+        const curStage = await this.ctx.service.stagePos.getStagesData(tender_id, stage_id);
+        this.ctx.helper.assignRelaData(posData, [
+            { data: curStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: '', relaId: 'pid' },
+        ]);
+        const pos = this._getNewPos();
+        pos.loadDatas(posData);
+        pos.calculateAll();
+
+        const gclGatherModel = require('../gcl_gather').gclGather;
+        const gatherUtil = new gclGatherModel(this.ctx);
+        gatherUtil.gatherObj(billsTree, pos);
+
+        const materialGl = stageSelf
+            ? this.materialGatherBase.materialGl.filter(x => { return x.sid === parseInt(stage_id); })
+            : this.materialGatherBase.materialGl;
+        if (stageIndex) materialGl.forEach(x => { x.s_index = stageIndex });
+        const materialNotJoin = this.materialGatherBase.materialNotJoin;
+        const materialNotChange = this.materialGatherBase.materialNotChange;
+
+        const helper = this.ctx.helper;
+
+        for (const g of gatherUtil.gclList) {
+            if (!g.contract_qty && !g.qc_qty && !g.qc_minus_qty) continue;
+            g.sid = stage_id;
+            g.sorder = stage_order;
+            g.s_index = stageIndex;
+            g.jiacha = 0;
+            g.jiacha_qty = 0;
+            for (const x of g.leafXmjs) {
+                x.sid = stage_id;
+                x.sorder = stage_order;
+                x.s_index = stageIndex;
+                x.jiacha = 0;
+                const mnj = materialNotJoin.find(m => {
+                    return m.gcl_id === x.org_gcl_id && m.xmj_id === x.id && (x.mx_id && x.mx_id !== x.id  ? x.mx_id === m.mx_id : true);
+                });
+                x.is_join = !mnj;
+                const mnc = materialNotChange.find(m => {
+                    return m.gcl_id === x.org_gcl_id && m.xmj_id === x.id && (x.mx_id && x.mx_id !== x.id  ? x.mx_id === m.mx_id : true);
+                });
+                x.is_change = mnc ? 2 : 1;
+                const list = materialGl.filter(gl => {
+                    return gl.gcl_id === x.org_gcl_id && gl.xmj_id === x.id && (x.mx_id && x.mx_id !== x.id ? x.mx_id === gl.mx_id : true);
+                });
+                list.forEach(l => { l.gather_gcl_id = x.gcl_id});
+
+                if (mnj) continue;
+                x.jiacha_qty = this.getMaterialCalcQty(this.materialGatherBase.qtySource, x, x.is_change);
+                for (const l of list) {
+                    x.jiacha = helper.add(x.jiacha, helper.mul(helper.mul(x.jiacha_qty, l.quantity), l.m_spread));
+                }
+                x.jiacha = helper.round(x.jiacha, decimal.tp);
+                g.jiacha = helper.add(g.jiacha, x.jiacha);
+                g.jiacha_qty = helper.add(g.jiacha_qty, x.jiacha_qty);
+            }
+        }
+        return [gatherUtil.gclList, gatherUtil.leafXmjs];
+    }
+
+    async getMaterialGatherBills(tender_id, material_order) {
+        const materials = await this.ctx.service.material.getAllDataByCondition({
+            where: { tid: tender_id },
+            orders: [['order', 'desc']],
+        });
+        if (materials.length === 0) return {};
+
+        const material = await this.ctx.service.material.getDataByCondition({ tid: tender_id, order: material_order });
+        this.materialGatherBase = {};
+        this.materialGatherBase.qtySource = material.qty_source;
+        this.materialGatherBase.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
+        try {
+            // 获取基础数据
+            this.materialGatherBase.billsData = await this.ctx.service.ledger.getData(tender_id);
+            this.materialGatherBase.posData = await this.ctx.service.pos.getPosData({ tid: tender_id });
+            if (material.is_stage_self) {
+                this.materialGatherBase.materialGl = material_order === materials[0].order
+                    ? await this.ctx.service.materialList.getMaterialStageData(tender_id, material.id)
+                    : await this.ctx.service.materialList.getPreMaterialStageData(tender_id, material.id);
+            } else {
+                this.materialGatherBase.materialGl = material_order === materials[0].order
+                    ? await this.ctx.service.materialList.getMaterialData(tender_id, material.id)
+                    : await this.ctx.service.materialList.getPreMaterialData(tender_id, material.id);
+            }
+            this.materialGatherBase.materialNotJoin = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { mid: material.id, type: 1 } });
+            this.materialGatherBase.materialNotChange = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { mid: material.id, type: 2 } });
+
+            const mem_material_gather_bills = [], mem_material_gather_xmj = [];
+            if (material.is_stage_self) {
+                const stageIds = material.stage_id.split(',');
+                const stageOrders = material.s_order.split(',');
+                for (const [i, sid] of stageIds.entries()) {
+                    const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(tender_id, sid, stageOrders[i], true, i + 1);
+                    mem_material_gather_bills.push(...gclList);
+                    mem_material_gather_xmj.push(...leafXmjs);
+                }
+            } else {
+                const [gclList, leafXmjs] = await this._getMaterialStageGatherBills(tender_id, material.stage_id, material.stage_order, false);
+                mem_material_gather_bills.push(...gclList);
+                mem_material_gather_xmj.push(...leafXmjs);
+            }
+            return {mem_material_gather_bills, mem_material_gather_xmj, mem_material_gather_gl: this.materialGatherBase.materialGl};
+        } catch (err) {
+            this.ctx.log(err);
+            return {};
+        }
+    }
+
+    async getMaterialStage(tender_id, material_order, fields) {
+        const material = await this.ctx.service.material.getDataByCondition({tid: tender_id, order: material_order});
+        if (!material) return [];
+
+        if (material.is_stage_self) {
+            return await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid: material.id } });
+        } else {
+            return [{
+                id: -1, tid: material.id, mid: material.id, sid: material.stage_id, order: material.stage_order,
+                m_tp: material.m_tp, m_tax_tp: material.m_tax_tp,
+            }];
+        }
+    }
+}
+
+module.exports = ReportMemoryMaterial;

+ 4 - 4
app/lib/stage_im.js

@@ -412,9 +412,9 @@ class StageIm {
         if (!posRange) { return; }
         for (const p of posRange) {
             if (this.ctx.helper.checkZero(p.contract_qty) && this.ctx.helper.checkZero(p.contract_qty)) { continue; }
-            let lp = this._.find(gclBills.pos, { name: p.name });
+            let lp = this._.find(gclBills.pos, { name: p.name, position: p.position || '' });
             if (!lp) {
-                lp = { name: p.name };
+                lp = { name: p.name, position: p.position || '' };
                 gclBills.pos.push(lp);
             }
             lp.jl = this.ctx.helper.add(lp.jl, p.gather_qty);
@@ -785,9 +785,9 @@ class StageIm {
         if (!posRange) { return; }
         for (const p of posRange) {
             if (!p.contract_qty && !p.qc_qty && !p.qc_minus_qty) { continue; }
-            let lp = this._.find(lx.pos, { name: p.name });
+            let lp = this._.find(lx.pos, { name: p.name, position: p.position || '' });
             if (!lp) {
-                lp = { name: p.name };
+                lp = { name: p.name, position: p.position || '' };
                 lx.pos.push(lp);
             }
             lp.jl = this.ctx.helper.add(lp.jl, p.gather_qty);

+ 1 - 1
app/middleware/change_apply_check.js

@@ -58,7 +58,7 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
+            } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
                 change.curTimes = change.times;
                 change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人

+ 1 - 1
app/middleware/change_check.js

@@ -55,7 +55,7 @@ module.exports = options => {
             if (accountId === change.uid) { // 原报
                 change.curTimes = change.times;
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
+            } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
                 change.curTimes = change.times;
                 change.filePermission = this.tender.touristPermission.file || change.auditorIds.indexOf(accountId) !== -1;
             } else if (change.auditorIds.indexOf(accountId) !== -1) { // 审批人

+ 1 - 1
app/middleware/change_plan_check.js

@@ -59,7 +59,7 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
+            } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
                 change.curTimes = change.times;
                 change.filePermission = this.tender.touristPermission.file || change.auditorIds.indexOf(accountId) !== -1;
             } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人

+ 1 - 1
app/middleware/change_project_check.js

@@ -59,7 +59,7 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
+            } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
                 change.curTimes = change.times;
                 change.filePermission = this.tender.touristPermission.file || change.auditorIds.indexOf(accountId) !== -1;
             } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人

+ 226 - 5
app/public/js/change_information_approval.js

@@ -378,11 +378,54 @@ $(document).ready(() => {
 
     SpreadJsObj.initSpreadSettingEvents(changeSpreadSetting, changeCol);
     SpreadJsObj.initSheet(changeSpreadSheet, changeSpreadSetting);
-    changeSpreadObj.setAuditValue();
-    SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
-    console.log(changeList);
-    changeSpreadObj.makeSjsFooter();
-    changeSpreadObj.resetXmjSpread(SpreadJsObj.getSelectObject(changeSpreadSheet));
+    const preUrl = window.location.pathname.split('/').slice(0, 4).join('/');
+    let changeListData;
+    let gclGatherData;
+    postData(preUrl + '/defaultBills', {}, function (result) {
+        gclGatherModel.loadLedgerData(result.bills);
+        gclGatherModel.loadPosData(result.pos);
+
+        gclGatherData = gclGatherModel.gatherGclData();
+        gclGatherData = _.filter(gclGatherData, function (item) {
+            return item.leafXmjs && item.leafXmjs.length !== 0;
+        });
+        for (const ggd in gclGatherData) {
+            if (gclGatherData[ggd].leafXmjs && gclGatherData[ggd].leafXmjs.length === 0) {
+                gclGatherData.splice(ggd, 1);
+            }
+            gclGatherData[ggd].code = gclGatherData[ggd].b_code;
+            let hadcid = 0;
+            for (const xmj of gclGatherData[ggd].leafXmjs) {
+                const changeLedger = _.find(changeLedgerList, { id: xmj.gcl_id });
+                const changePos = _.find(changePosList, { id: xmj.mx_id, lid: xmj.gcl_id });
+                if (changeLedger || changePos) {
+                    xmj.cid = 1;
+                    xmj.ccid = changeLedger ? changeLedger.ccid : changePos.ccid;
+                    hadcid = 1;
+                }
+            }
+
+            if (hadcid !== 0) gclGatherData[ggd].cid = 1;
+        }
+        console.log(gclGatherData);
+        // 数组去重
+        const dealBillList = result.dealBills;
+        for (const db of gclGatherData) {
+            const exist_index = dealBillList.findIndex(function (item) {
+                return item.code === db.code && item.name === db.name && item.unit === db.unit && item.unit_price === db.unit_price;
+            });
+            if (exist_index !== -1) {
+                dealBillList.splice(exist_index, 1);
+            }
+        }
+        changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
+        checkAndChange(changeListData);
+        changeSpreadObj.setAuditValue();
+        SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
+        console.log(changeList);
+        changeSpreadObj.makeSjsFooter();
+        changeSpreadObj.resetXmjSpread(SpreadJsObj.getSelectObject(changeSpreadSheet));
+    });
     const userIndex = aidList.indexOf(parseInt(accountId));
 
     changeSpread.bind(spreadNS.Events.EditEnded, changeSpreadObj.editEnded);
@@ -496,3 +539,181 @@ function auditCheck(i) {
     }
     return true;
 }
+function checkAndChange(changeListData) {
+    // 根据已添加的清单显示
+    if (changeList.length > 0 && changeList[0]) {
+        const removeList = [];
+        const updateList = [];
+        const updateGclIdList = [];
+        for (const [index,clinfo] of changeList.entries()) {
+            if (clinfo.lid != 0) {
+                let listinfo = changeListData.find(function (item) {
+                    return (item.id !== undefined && item.id == clinfo.lid) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && item.leafXmjs[0].gcl_id == clinfo.lid);
+                });
+                if (listinfo === undefined || (clinfo.lid && clinfo.gcl_id && clinfo.lid !== clinfo.gcl_id)) {
+                    // 有可能这部分台账发生变化,此时要更新清单lid信息,防止数据丢失
+                    const newlistinfo = changeListData.find(function (item) {
+                        return (item.id !== undefined && item.id == clinfo.gcl_id) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && _.find(item.leafXmjs, {gcl_id: clinfo.gcl_id }));
+                    });
+                    if ((listinfo === undefined && newlistinfo) || (listinfo && newlistinfo && !isObjEqual(listinfo, newlistinfo))) {
+                        listinfo = newlistinfo;
+                        updateList.push({id: clinfo.id, lid: newlistinfo.leafXmjs[0].gcl_id});
+                        // 更新lid
+                        changeList[index].lid = newlistinfo.leafXmjs[0].gcl_id;
+                    }
+                }
+                if (listinfo === undefined) {
+                    // 针对旧数据获取清单信息
+                    listinfo = changeListData[clinfo.lid - 1];
+                    if (listinfo === undefined) {
+                        toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
+                        if (changeStatus !== auditConst.status.revise) {
+                            removeList.push(clinfo);
+                        } else {
+                            updateList.push(makeWhiteList(clinfo));
+                        }
+                        continue;
+                    }
+                }
+                const info = makePushBwmx(clinfo, listinfo, removeList, updateList);
+                if (info && _.findIndex(changeLedgerList, { id: clinfo.gcl_id }) !== -1) {
+                    // 可能因为升降级关系:细目,分部分项等会发生变化,更新清单
+                    const updateInfo = {};
+                    if (info.code !== clinfo.xmj_code) {
+                        updateInfo.xmj_code = info.code;
+                        changeList[index].xmj_code = info.code;
+                    }
+                    if (info.jldy !== clinfo.xmj_jldy) {
+                        updateInfo.xmj_jldy = info.jldy;
+                        changeList[index].xmj_jldy = info.jldy;
+                    }
+                    if (info.dwgc !== clinfo.xmj_dwgc) {
+                        updateInfo.xmj_dwgc = info.dwgc;
+                        changeList[index].xmj_dwgc = info.dwgc;
+                    }
+                    if (info.fbgc !== clinfo.xmj_fbgc) {
+                        updateInfo.xmj_fbgc = info.fbgc;
+                        changeList[index].xmj_fbgc = info.fbgc;
+                    }
+                    if (info.fxgc !== clinfo.xmj_fxgc) {
+                        updateInfo.xmj_fxgc = info.fxgc;
+                        changeList[index].xmj_fxgc = info.fxgc;
+                    }
+                    if (!_.isEmpty(updateInfo) && _.indexOf(updateGclIdList, clinfo.gcl_id) === -1) {
+                        updateGclIdList.push(clinfo.gcl_id);
+                        // updateInfo.gcl_id = info.id;
+                        updateList.push({ row: updateInfo, where: { tid: tenderId, gcl_id: clinfo.gcl_id } });
+                    }
+                }
+            }
+        }
+        if(updateList.length > 0) {
+            console.log(updateList);
+            postData(window.location.pathname + '/save', { type:'update_list', updateData: updateList }, function (result) {
+            }, function () {
+            });
+        }
+        if(removeList.length > 0) {
+            _.pullAll(changeList, removeList);
+            postData(window.location.pathname + '/save', { type:'remove_list', updateData: removeList }, function (result) {
+            }, function () {
+            });
+        }
+    }
+}
+
+function makePushBwmx(clinfo, listinfo, removeList, updateList) {
+    let info = '';
+    const checkKey = ['name', 'code', 'unit', 'unit_price'];
+    const checkLeafKey = ['oamount', 'bwmx', 'code', 'dwgc', 'fbgc', 'fxgc', 'jldy'];
+    if (listinfo.leafXmjs !== undefined) {
+        const leafInfo = listinfo.leafXmjs.find(function (item) {
+            // const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id)) && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+            const flag = item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id));
+            if (flag && item.code === clinfo.xmj_code) {
+                return flag && item.code === clinfo.xmj_code;
+            }
+            return flag;
+        });
+        if (leafInfo) {
+            const oneUpdate = { id: clinfo.id };
+            let needUpdate = false;
+            // 判断要不要更新名称,单位,原数量,单价
+            checkKey.forEach(function (key) {
+                if (listinfo[key] !== clinfo[key]) {
+                    oneUpdate[key] = listinfo[key];
+                    clinfo[key] = listinfo[key];
+                    if (key === 'unit') {
+                        const oamount = ZhCalc.round(clinfo.oamount, findDecimal(listinfo[key]));
+                        if (oamount !== clinfo.oamount) {
+                            oneUpdate.oamount = oamount;
+                            clinfo.oamount = oamount;
+                        }
+                        const camount = ZhCalc.round(clinfo.camount, findDecimal(listinfo[key]));
+                        if (camount !== clinfo.camount) {
+                            oneUpdate.camount = camount;
+                            clinfo.camount = camount;
+                        }
+                    }
+                    // else if (key === 'unit_price') {
+                    //     // 可能要更新总金额了
+                    //     oneUpdate[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
+                    //     clinfo[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
+                    // }
+                    needUpdate = true;
+                }
+            });
+            checkLeafKey.forEach(function (key) {
+                // 只有数量是对比leafInfo,其它对比listinfo,且有些值需要重新计算
+                if (key === 'oamount') {
+                    if (leafInfo.quantity !== clinfo[key]) {
+                        oneUpdate[key] = leafInfo.quantity;
+                        clinfo[key] = leafInfo.quantity;
+                        needUpdate = true;
+                    }
+                } else if (key === 'bwmx') {
+                    if (leafInfo[key] !== undefined && leafInfo[key] !== clinfo[key]) {
+                        oneUpdate[key] = leafInfo[key];
+                        clinfo[key] = leafInfo[key];
+                        needUpdate = true;
+                    } else if (leafInfo[key] === undefined && leafInfo.jldy !== clinfo[key]) {
+                        oneUpdate[key] = leafInfo.jldy;
+                        clinfo[key] = leafInfo.jldy;
+                        needUpdate = true;
+                    }
+                } else {
+                    if (leafInfo[key] !== clinfo['xmj_' + key]) {
+                        oneUpdate['xmj_' + key] = leafInfo[key];
+                        clinfo['xmj_' + key] = leafInfo[key];
+                        needUpdate = true;
+                    }
+                }
+            });
+            if (needUpdate) {
+                updateList.push(oneUpdate);
+            }
+        } else {
+            toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
+            if (changeStatus !== auditConst.status.revise) {
+                removeList.push(clinfo);
+            } else {
+                updateList.push(makeWhiteList(clinfo));
+            }
+            return false;
+        }
+    }
+}
+function isObjEqual(o1,o2){
+    var props1 = Object.getOwnPropertyNames(o1);
+    var props2 = Object.getOwnPropertyNames(o2);
+    if (props1.length != props2.length) {
+        return false;
+    }
+    for (var i = 0,max = props1.length; i < max; i++) {
+        var propName = props1[i];
+        if (o1[propName] !== o2[propName]) {
+            return false;
+        }
+    }
+    return true;
+}

+ 74 - 41
app/public/js/change_information_set.js

@@ -846,10 +846,10 @@ $(document).ready(() => {
             }
             if (param.length === 0) return true;
             if (param.length > 1) {
-                if (param[0].value === '-') {
-                    param[1].value = '-' + param[1];
+                if (param[0].value === '-' && param[1].type === 'num') {
+                    param[1].value = '-' + param[1].value;
+                    param.shift();
                 }
-                param.unshift();
             }
             const iLen = param.length;
             let iLeftCount = 0, iRightCount = 0;
@@ -947,7 +947,7 @@ $(document).ready(() => {
 
             if (hadcid !== 0) gclGatherData[ggd].cid = 1;
         }
-        console.log(gclGatherData);
+        // console.log(gclGatherData);
         // 数组去重
         const dealBillList = result.dealBills;
         for (const db of gclGatherData) {
@@ -959,7 +959,7 @@ $(document).ready(() => {
             }
         }
         changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
-        // console.log(changeListData);
+        console.log(changeListData);
         // 先加载台账数据
         let listHtml = '';
         let list_index = 1;
@@ -2026,6 +2026,7 @@ function checkChangeFrom() {
     }
     // 判断非空白变更清单是否有重复值(gcl_id及mx_id),如果存在完全相同则需要提示并删除,上报失败,这功能主要是因为清单数据过多服务器处理不过来偶然出现
     const doubleList = findDuplicates(_.filter(changeList, item => item.lid != 0));
+    console.log(doubleList);
     if (doubleList.length > 0) {
         for (const double of doubleList) {
             const msgIndex = [];
@@ -2056,7 +2057,7 @@ function checkAuditorFrom () {
     return true;
 }
 // 找出数组中相同的清单
-function findDuplicates(arr, keys = ['gcl_id', 'mx_id']) {
+function findDuplicates(arr, keys = ['lid', 'gcl_id', 'mx_id']) {
     const groupedData = _.groupBy(arr, item => JSON.stringify(_.pick(item, keys)));
     const duplicates = _.filter(groupedData, group => group.length > 1);
     return _.values(duplicates);
@@ -2094,6 +2095,7 @@ function tableDataRemake(changeListData) {
                         changeList[index].lid = newlistinfo.leafXmjs[0].gcl_id;
                     }
                 }
+                let lidIsNumber = false;
                 if (listinfo === undefined) {
                     // 针对旧数据获取清单信息
                     listinfo = changeListData[clinfo.lid - 1];
@@ -2106,8 +2108,9 @@ function tableDataRemake(changeListData) {
                         }
                         continue;
                     }
+                    lidIsNumber = true;
                 }
-                const info = makePushBwmx(clinfo, listinfo, removeList, updateList);
+                const info = makePushBwmx(clinfo, listinfo, removeList, updateList, lidIsNumber);
                 if (info && _.findIndex(changeLedgerList, { id: clinfo.gcl_id }) !== -1) {
                     // 可能因为升降级关系:细目,分部分项等会发生变化,更新清单
                     const updateInfo = {};
@@ -2140,7 +2143,7 @@ function tableDataRemake(changeListData) {
             }
         }
         if(updateList.length > 0) {
-            // console.log(updateList);
+            console.log(updateList);
             postData(window.location.pathname + '/save', { type:'update_list', updateData: updateList }, function (result) {
             }, function () {
             });
@@ -2154,59 +2157,77 @@ function tableDataRemake(changeListData) {
     }
 }
 
-function makePushBwmx(clinfo, listinfo, removeList, updateList) {
+function makePushBwmx(clinfo, listinfo, removeList, updateList, lidIsNumber) {
     let info = '';
-    const checkKey = ['name', 'unit', 'unit_price', 'oamount'];
+    const checkKey = ['name', 'code', 'unit', 'unit_price'];
+    const checkLeafKey = ['oamount', 'bwmx', 'code', 'dwgc', 'fbgc', 'fxgc', 'jldy'];
     let pushbwmx = '0*;*0';
     if (listinfo.leafXmjs !== undefined) {
         const leafInfo = listinfo.leafXmjs.find(function (item) {
             // const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id)) && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
-            const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id));
+            const flag = item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id));
             if (flag && item.code === clinfo.xmj_code) {
                 return flag && item.code === clinfo.xmj_code;
             }
             return flag;
         });
         if (leafInfo) {
+            const oneUpdate = { id: clinfo.id };
+            let needUpdate = false;
             // 判断要不要更新名称,单位,原数量,单价
             checkKey.forEach(function (key) {
-                const oneUpdate = { id: clinfo.id };
-                let needUpdate = false;
+                if (listinfo[key] !== clinfo[key]) {
+                    oneUpdate[key] = listinfo[key];
+                    clinfo[key] = listinfo[key];
+                    if (key === 'unit') {
+                        const oamount = ZhCalc.round(clinfo.oamount, findDecimal(listinfo[key]));
+                        if (oamount !== clinfo.oamount) {
+                            oneUpdate.oamount = oamount;
+                            clinfo.oamount = oamount;
+                        }
+                        const camount = ZhCalc.round(clinfo.camount, findDecimal(listinfo[key]));
+                        if (camount !== clinfo.camount) {
+                            oneUpdate.camount = camount;
+                            clinfo.camount = camount;
+                        }
+                    }
+                    // else if (key === 'unit_price') {
+                    //     // 可能要更新总金额了
+                    //     oneUpdate[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
+                    //     clinfo[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
+                    // }
+                    needUpdate = true;
+                }
+            });
+            checkLeafKey.forEach(function (key) {
                 // 只有数量是对比leafInfo,其它对比listinfo,且有些值需要重新计算
                 if (key === 'oamount') {
                     if (leafInfo.quantity !== clinfo[key]) {
-                        oneUpdate[key] = leafInfo[key];
+                        oneUpdate[key] = leafInfo.quantity;
                         clinfo[key] = leafInfo.quantity;
                         needUpdate = true;
                     }
+                } else if (key === 'bwmx') {
+                    if (leafInfo[key] !== undefined && leafInfo[key] !== clinfo[key]) {
+                        oneUpdate[key] = leafInfo[key];
+                        clinfo[key] = leafInfo[key];
+                        needUpdate = true;
+                    } else if (leafInfo[key] === undefined && leafInfo.jldy !== clinfo[key]) {
+                        oneUpdate[key] = leafInfo.jldy;
+                        clinfo[key] = leafInfo.jldy;
+                        needUpdate = true;
+                    }
                 } else {
-                    if (listinfo[key] !== clinfo[key]) {
-                        oneUpdate[key] = listinfo[key];
-                        clinfo[key] = listinfo[key];
-                        if (key === 'unit') {
-                            const oamount = ZhCalc.round(clinfo.oamount, findDecimal(listinfo[key]));
-                            if (oamount !== clinfo.oamount) {
-                                oneUpdate.oamount = oamount;
-                                clinfo.oamount = oamount;
-                            }
-                            const camount = ZhCalc.round(clinfo.camount, findDecimal(listinfo[key]));
-                            if (camount !== clinfo.camount) {
-                                oneUpdate.camount = camount;
-                                clinfo.camount = camount;
-                            }
-                        }
-                        // else if (key === 'unit_price') {
-                        //     // 可能要更新总金额了
-                        //     oneUpdate[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
-                        //     clinfo[key] = ZhCalc.round(listinfo[key], unitPriceUnit);
-                        // }
+                    if (leafInfo[key] !== clinfo['xmj_' + key]) {
+                        oneUpdate['xmj_' + key] = leafInfo[key];
+                        clinfo['xmj_' + key] = leafInfo[key];
                         needUpdate = true;
                     }
                 }
-                if (needUpdate) {
-                    updateList.push(oneUpdate);
-                }
             });
+            if (needUpdate) {
+                updateList.push(oneUpdate);
+            }
             info = leafInfo;
             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
                 (leafInfo.dwgc ? leafInfo.dwgc : '') + '!_!' +
@@ -2227,15 +2248,27 @@ function makePushBwmx(clinfo, listinfo, removeList, updateList) {
     } else {
         pushbwmx = '0*;*' + (listinfo.quantity !== null ? listinfo.quantity : 0);
     }
-    const bwmx = $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx');
+    const bwmx = lidIsNumber ? $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx') : $('#table-list-select tr[data-lid="'+ clinfo.lid +'"]').attr('data-bwmx');
     if (bwmx) {
         const bwmxArray = bwmx.split('$#$');
         bwmxArray.push(pushbwmx);
-        $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx', bwmxArray.join('$#$'));
+        if (lidIsNumber) {
+            $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx', bwmxArray.join('$#$'));
+        } else {
+            $('#table-list-select tr[data-lid="'+ clinfo.lid +'"]').attr('data-bwmx', bwmxArray.join('$#$'));
+        }
+    } else {
+        if (lidIsNumber) {
+            $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx', pushbwmx);
+        } else {
+            $('#table-list-select tr[data-lid="'+ clinfo.lid +'"]').attr('data-bwmx', pushbwmx);
+        }
+    }
+    if (lidIsNumber) {
+        $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').addClass('table-success');
     } else {
-        $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').attr('data-bwmx', pushbwmx);
+        $('#table-list-select tr[data-lid="'+ clinfo.lid +'"]').addClass('table-success');
     }
-    $('#table-list-select tr[data-index="'+ clinfo.lid +'"]').addClass('table-success');
     return info;
 }
 

+ 1 - 1
app/public/js/change_information_show.js

@@ -214,7 +214,7 @@ $(document).ready(() => {
     // 重新审批获取手机验证码
     // 获取验证码
     let isPosting = false;
-    $("#get-code").click(function() {
+    $(".get-code").on('click', function() {
         if (isPosting) {
             return false;
         }

+ 161 - 14
app/public/js/change_revise.js

@@ -2575,8 +2575,14 @@ $(document).ready(() => {
             //     return;
             // }
             const self = this;
-            const data = billsTree.nodes.filter(node => node.is_leaf === 1 && !node.code && node.ccid !== undefined && node.ccid !== '');
-            SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', data);
+            const datas = billsTree.nodes.filter(node => node.is_leaf === 1 && !node.code && node.ccid !== undefined && node.ccid !== '');
+            const showDatas = [];
+            for (const d of datas) {
+                if (_.findIndex(showDatas, { b_code: d.b_code ? d.b_code : '', name: d.name, unit: d.unit, unit_price: d.unit_price }) === -1) {
+                    showDatas.push({ code: d.code, b_code: d.b_code ? d.b_code : '', name: d.name, unit: d.unit, unit_price: d.unit_price });
+                }
+            }
+            SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', _.uniqWith(showDatas, _.isEqual).sort(sortByCode));
             // self.loaded = true;
             if (callback) callback();
         }
@@ -2585,7 +2591,7 @@ $(document).ready(() => {
     const jlzfBills = new Jlzf('#jlzf-spread', {
         cols: [
             // {title: '项目节编号', field: 'code', hAlign: 0, width: 85, formatter: '@'},
-            {title: '清单编号', field: 'b_code', hAlign: 0, width: 85, formatter: '@'},
+            {title: '清单编号', field: 'b_code', hAlign: 0, width: 100, formatter: '@'},
             {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@'},
             {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
             {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
@@ -2620,6 +2626,19 @@ $(document).ready(() => {
             };
             this.obj.html(
                 '<div class="sjs-bar d-flex">\n' +
+                '    <div class="dropdown mr-1">\n' +
+                '        <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+                '             <i class="fa fa-list-ol"></i> 显示层级\n' +
+                '        </button>\n' +
+                '        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">\n' +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="1" name="${relaSelect.showLevel}">第一层</a>\n` +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="2" name="${relaSelect.showLevel}">第二层</a>\n` +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="3" name="${relaSelect.showLevel}">第三层</a>\n` +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="4" name="${relaSelect.showLevel}">第四层</a>\n` +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="5" name="${relaSelect.showLevel}">第五层</a>\n` +
+                `            <a class="dropdown-item" href="javascript: void(0);" tag="last" name="${relaSelect.showLevel}">最底层</a>\n` +
+                '        </div>\n' +
+                '    </div>' +
                 `    <div class="input-group input-group-sm pr-1"><select class="form-control form-control-sm col-auto" id="change-dsk-project">${dskProjectHtml.join('')}</select></div>\n` +
                 `    <div class="input-group input-group-sm pr-1"><select class="form-control form-control-sm col-auto" id="change-dsk-project-tree"></select></div>\n` +
                 '    <div class="ml-1">\n' +
@@ -2659,6 +2678,29 @@ $(document).ready(() => {
             this.spread = SpreadJsObj.createNewSpread($(`#cb-${spreadSetting.stdType}-spread`)[0]);
             const searchSheet = this.spread.getActiveSheet();
             SpreadJsObj.initSheet(searchSheet, this.spreadSetting);
+            (function (select, sheet) {
+                $(select).click(function () {
+                    if (!sheet.zh_tree) return;
+                    const tag = $(this).attr('tag');
+                    const tree = sheet.zh_tree;
+                    setTimeout(() => {
+                        switch (tag) {
+                            case "1":
+                            case "2":
+                            case "3":
+                            case "4":
+                            case "5":
+                                tree.expandByLevel(parseInt(tag));
+                                SpreadJsObj.refreshTreeRowVisible(sheet);
+                                break;
+                            case "last":
+                                tree.expandByCustom(() => { return true; });
+                                SpreadJsObj.refreshTreeRowVisible(sheet);
+                                break;
+                        }
+                    }, 100);
+                });
+            })(`a[name=${relaSelect.showLevel}]`, searchSheet);
             if (!readOnly) {
                 this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
                     const dealSheet = info.sheet;
@@ -2712,7 +2754,7 @@ $(document).ready(() => {
                 searchStdNode: function() {
                     const keyword = $(`#${relaSelect.searchText}`).val();
                     searchObj.result = keyword ? dskProjectBills2Tree.tenderTree.nodes.filter(x => {
-                        return x.code.indexOf(keyword) >= 0 || x.name.indexOf(keyword) >= 0;
+                        return (x.code && x.code.indexOf(keyword) >= 0) || (x.name && x.name.indexOf(keyword) >= 0);
                     }) : [];
                     // searchObj.result = [];
                     // for (const x of pathTree.nodes) {
@@ -2825,7 +2867,7 @@ $(document).ready(() => {
     const dskBills = new Dsk('#dsk-spread', {
         cols: [
             {title: '编号', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'},
-            {title: '名称', field: 'name', hAlign: 0, width: 100, formatter: '@'},
+            {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@'},
             {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
             {title: '单价', field: 'unitPrice', hAlign: 2, width: 80, formatter: '@'},
             {title: '数量', field: 'quantity', hAlign: 2, width: 80, formatter: '@'},
@@ -3282,7 +3324,7 @@ $(document).ready(() => {
                     $('#error-dsk .modal-body').find('h5').eq(1).show();
                     $('#error-dsk .modal-body').find('h5').eq(0).hide();
                     $('#error-dsk').modal('show');
-                    $('#error-dsk .modal-footer').find('a').eq(0).text('注册账号');
+                    $('#error-dsk .modal-footer').find('a').eq(0).text('绑定账号');
                 } else {
                     postData('/profile/dsk/api', { type: 'compilation', getProject: 1, compilationId: getLocalCache(dskCompilation) }, function (result) {
                         let html = '';
@@ -3423,7 +3465,7 @@ $(document).ready(() => {
             const gsSpreadSetting = {
                 cols: [
                     {title: '选择', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox', readOnly: true,},
-                    {title: '名称', field: 'name', hAlign: 0, width: 350, formatter: '@', readOnly: true, folderCell: true, cellType: 'tree'},
+                    {title: '名称', field: 'name', hAlign: 0, width: 400, formatter: '@', readOnly: true, folderCell: true, cellType: 'tree'},
                 ],
                 emptyRows: 0,
                 headRows: 1,
@@ -3441,7 +3483,7 @@ $(document).ready(() => {
             gsObj.grSheet = grSpread.getActiveSheet();
             const grSpreadSetting = {
                 cols: [
-                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true},
+                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true, cellType: 'ellipsisAutoTip', scrollHeightClass: '.modal-height-500'},
                     {title: '所属编办', colSpan: '1', rowSpan: '1', field: 'compilationName', hAlign: 0, width: 150, formatter: '@', readOnly: true},
                 ],
                 emptyRows: 0,
@@ -3504,6 +3546,10 @@ $(document).ready(() => {
                 const tenderChildren = _.filter(tenders, { parentID: node.pid });
                 const checkChildren = _.orderBy([...children, ...tenderChildren], ['seq', 'asc']);
                 for (const c of checkChildren) {
+                    // if (c.type === 1 && _.filter(files, { parentID: c.ID }).length === 0 && _.filter(tenders, { parentID: c.ID }).length === 0) {
+                    //     // 判断文件夹下有无东西,没有就不插入
+                    //     continue;
+                    // }
                     const child = {
                         pid: c.ID,
                         parentID: c.parentID,
@@ -3518,11 +3564,10 @@ $(document).ready(() => {
 
             function convert (projects) {
                 tenderTree.clearDatas();
-
-                // 区分文件夹及项目
-                const topLevel = _.orderBy(_.filter(projects, { parentID: '-1' }), ['seq', 'asc']);
-                const files = _.filter(projects, { type: 1 });
-                const tenders = _.filter(projects, { type: 2 });
+                const result = filterFolders(projects);
+                const topLevel = _.orderBy(_.filter(result, { parentID: '-1' }), ['seq', 'asc']);
+                const files = _.filter(result, { type: 1 });
+                const tenders = _.filter(result, { type: 2 });
 
                 for (const t of topLevel) {
                     const node = {
@@ -3541,6 +3586,52 @@ $(document).ready(() => {
             return { tenderTree, convert }
         })();
 
+        function filterFolders(data) {
+            // 构建ID到节点的映射
+            const idMap = data.reduce((map, node) => {
+                map[node.ID] = node;
+                return map;
+            }, {});
+
+            // 建立父子关系映射
+            const parentMap = data.reduce((map, node) => {
+                if (node.parentID !== '-1') {
+                    if (!map[node.parentID]) {
+                        map[node.parentID] = [];
+                    }
+                    map[node.parentID].push(node.ID);
+                }
+                return map;
+            }, {});
+
+            // 检查节点及其子节点是否包含清单(type=1)
+            const hasChecklist = (id) => {
+                const node = idMap[id];
+                if (node.type === 2) {
+                    return true;
+                }
+                if (!parentMap[id]) {
+                    return false;
+                }
+                return parentMap[id].some(childID => hasChecklist(childID));
+            };
+
+            // 过滤节点
+            const filteredIDs = new Set();
+            data.forEach(node => {
+                if (node.type === 2 || hasChecklist(node.ID)) {
+                    let currentID = node.ID;
+                    while (currentID !== '-1') {
+                        filteredIDs.add(currentID);
+                        currentID = idMap[currentID].parentID;
+                    }
+                }
+            });
+
+            // 返回过滤后的数据
+            return data.filter(node => filteredIDs.has(node.ID));
+        }
+
         $('#set-dsk-project').click(function () {
             postData('/profile/dsk/api', { type: 'save_projects', tid: window.location.pathname.split('/')[2], project_list: gsObj.grArray }, function (result) {
                 // dskAccountData.select_project = result;
@@ -3554,6 +3645,9 @@ $(document).ready(() => {
                 $('#add-dsk').modal('hide');
             });
         });
+        $('#add-dsk').on('hide.bs.modal', function () {
+            $('#autoTip').hide();
+        });
 
         function setDskProjectSelect(projects) {
             let html = '';
@@ -3564,4 +3658,57 @@ $(document).ready(() => {
         }
     }
 });
-
+// 编号排序,多重判断
+function sortByCode(a, b, code = 'b_code') {
+    let code1 = a[code].split('-');
+    let code2 = b[code].split('-');
+    let code1length = code1.length;
+    let code2length = code2.length;
+    for (let i = 0; i < code1length; i ++) {
+        if (i+1 <= code2length) {
+            if (code1[i] != code2[i]) {
+                if (/^\d+$/.test(code1[i]) && /^\d+$/.test(code2[i])) {
+                    return parseInt(code1[i]) - parseInt(code2[i]);
+                } else if (!/^\d+$/.test(code1[i]) && /^\d+$/.test(code2[i])) {
+                    return 1;
+                } else if (/^\d+$/.test(code1[i]) && !/^\d+$/.test(code2[i])) {
+                    return -1;
+                } else {
+                    const str1length = code1[i].length;
+                    const str2length = code2[i].length;
+                    for (let j = 0; j < str1length; j++) {
+                        if (j+1 <= str2length) {
+                            if (code1[i].charAt(j) != code2[i].charAt(j)) {
+                                return code1[i].charAt(j).charCodeAt() - code2[i].charAt(j).charCodeAt();
+                            }  else if (j+1 == str1length && code1[i].charAt(j) == code2[i].charAt(j)) {
+                                if (str1length == str2length) {
+                                    return 0;
+                                } else {
+                                    return str1length - str2length;
+                                }
+                            }
+                        } else {
+                            if (j+1 >= str1length) {
+                                return 1;
+                            } else {
+                                return -1;
+                            }
+                        }
+                    }
+                }
+            } else if (i+1 == code1length && code1[i] == code2[i]) {
+                if (code1length == code2length) {
+                    return 0;
+                } else {
+                    return code1length - code2length;
+                }
+            }
+        } else {
+            if (i+1 >= code1length) {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+    }
+}

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

@@ -308,6 +308,8 @@ const gclGatherModel = (function () {
             if (d.name !== node.name) {
                 dx.bwmx = d.name;
                 dx.mx_id = d.id;
+            } else if (dx.gcl_id !== d.id) {
+                dx.mx_id = d.id;
             }
             if (d.drawing_code) {
                 dx.drawing_code = d.drawing_code;
@@ -593,6 +595,42 @@ const gclGatherModel = (function () {
         return result;
     }
 
+    function _gatherLeafXmj(gcl, fields = ['bwmx']) {
+        gcl.gatherLeafXmjs = [];
+        for (const lx of gcl.leafXmjs) {
+            const condition = {};
+            for (const f of fields) {
+                condition[f] = lx[f];
+            }
+            let glx = _.find(gcl.gatherLeafXmjs, condition);
+            if (!glx) {
+                glx = JSON.parse(JSON.stringify(lx));
+                gcl.gatherLeafXmjs.push(glx);
+            } else {
+                gatherfields(glx, lx, posGatherFields);
+            }
+        }
+        for (const xmj of gcl.gatherLeafXmjs) {
+            xmj.pre_gather_qty = ZhCalc.add(xmj.pre_contract_qty, xmj.pre_qc_qty);
+            xmj.gather_qty = ZhCalc.add(xmj.contract_qty, xmj.qc_qty);
+            xmj.end_contract_qty = ZhCalc.add(xmj.pre_contract_qty, xmj.contract_qty);
+            xmj.end_qc_qty = ZhCalc.add(xmj.pre_qc_qty, xmj.qc_qty);
+            xmj.end_gather_qty = ZhCalc.add(xmj.pre_gather_qty, xmj.gather_qty);
+            xmj.end_final_qty = ZhCalc.add(xmj.end_qc_qty, xmj.quantity);
+            xmj.end_qc_minus_qty = ZhCalc.add(xmj.pre_qc_minus_qty, xmj.qc_minus_qty);
+            xmj.final_1_qty = ZhCalc.add(xmj.quantity, xmj.end_qc_minus_qty);
+            xmj.end_final_1_qty = ZhCalc.add(xmj.final_1_qty, xmj.end_qc_qty);
+            xmj.end_gather_percent = ZhCalc.mul(ZhCalc.div(xmj.end_gather_qty, xmj.end_final_qty), 100, 2);
+            xmj.end_final_1_percent = ZhCalc.mul(ZhCalc.div(xmj.end_gather_qty, xmj.end_final_1_qty), 100, 2);
+        }
+    }
+
+    function reGatherLeafXmj(fields) {
+        gclList.forEach(g => {
+            _gatherLeafXmj(g, fields);
+        });
+    }
+
     return {
         loadGatherField,
         loadDecimal,
@@ -603,5 +641,6 @@ const gclGatherModel = (function () {
         gatherGclData,
         checkDiffer,
         gatherChapterData,
+        reGatherLeafXmj,
     };
 })();

+ 164 - 57
app/public/js/ledger.js

@@ -23,12 +23,13 @@ const invalidFields = {
     posCalc: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp'],
     posXmj: ['code'],
 };
-function getExprInfo (field) {
+function getExprInfo (field, converse = false) {
     const exprField = [
         {qty: 'sgfh_qty', expr: 'sgfh_expr'},
         {qty: 'sjcl_qty', expr: 'sjcl_expr'},
         {qty: 'qtcl_qty', expr: 'qtcl_expr'},
     ];
+    if (converse) return _.find(exprField, { expr: field });
     return _.find(exprField, {qty: field});
 }
 function transExpr(expr) {
@@ -96,12 +97,18 @@ $(document).ready(function() {
     const billsTag = $.billsTag({
         selector: '#bills-tag',
         relaSpread: ledgerSpread,
+        relaPosSpread: posSpread,
         updateUrl: window.location.pathname + '/tag',
         afterModify: function (nodes) {
             SpreadJsObj.repaintNodesRowHeader(ledgerSpread.getActiveSheet(), nodes);
         },
-        afterLocated:  function () {
+        afterLocated:  function (lid, pos_id) {
             posOperationObj.loadCurPosData();
+            if (pos_id) {
+                const posSheet = posSpread.getActiveSheet();
+                const relaPos = posSheet.zh_data.find(x => { return x.id === pos_id; });
+                SpreadJsObj.locateData(posSheet, relaPos);
+            }
         },
         afterShow: function () {
             ledgerSpread.refresh();
@@ -1254,7 +1261,7 @@ $(document).ready(function() {
     //     const $obj = $('<div>' + item.name +)
     // };
     // 右键菜单
-    const addTag = newTag({ledgerSheet: ledgerSpread.getActiveSheet(), billsTag});
+    const addTag = newTag({ledgerSheet: ledgerSpread.getActiveSheet(), posSheet: posSpread.getActiveSheet(), billsTag});
     const billsContextMenuOptions = {
         selector: '#ledger-spread',
         build: function ($trigger, e) {
@@ -1742,6 +1749,36 @@ $(document).ready(function() {
         });
         sjsSettingObj.setGridSelectStyle(posSpreadSetting);
     }
+    posSpreadSetting.headColWidth = [50];
+    posSpreadSetting.rowHeader = [
+        {
+            rowHeaderType: 'tag',
+            setting: {
+                indent: 14,
+                tagSize: 0.8,
+                tagFont: '8px 微软雅黑',
+                getColor: function (index, data) {
+                    if (!data) return;
+                    return billsTag.getPosTagsColor(data.lid, data.id);
+                },
+                getTagHtml: function (index, data) {
+                    if (!data) return;
+                    const getHtml = function (list) {
+                        if (!list || list.length === 0) return '';
+                        const html = [];
+                        for (const l of list) {
+                            html.push('<div class="row mr-1">');
+                            html.push(`<div class="col-auto pr-1 ${l.tagClass}">`, '<i class="fa fa-tag"></i>', '</div>');
+                            html.push('<div class="col p-0">', '<p>', l.comment, '</p>', '</div>');
+                            html.push('</div>');
+                        }
+                        return html.join('');
+                    };
+                    return getHtml(billsTag.getPosTagsInfo(data.lid, data.id));
+                }
+            },
+        },
+    ];
     SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     //绑定计量单元编辑事件
     const posOperationObj = {
@@ -1876,13 +1913,18 @@ $(document).ready(function() {
             if (data.updateData.length > 0) {
                 postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     if (type === 'delete') {
+                        for (const p of result.pos) {
+                            const posInfo = pos.getPos(p);
+                            billsTag.afterDeletePos(posInfo);
+                        }
                         pos.removeDatas(result.pos);
                         sheet.deleteRows(row, count);
                         const loadResult = ledgerTree.loadPostData(result.ledger);
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
                         treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
                     } else {
-                        pos.updateDatas(result.pos);
+                        const updateRst = pos.updateDatas(result.pos);
+                        billsTag.refreshPosTagView(updateRst.update);
 
                         const sel = selection[0];
                         if (sel) {
@@ -1952,12 +1994,30 @@ $(document).ready(function() {
                             return;
                         }
                     }
+                } else if (col.field.indexOf('_expr') > 0) {
+                    const exprInfo = getExprInfo(col.field, true);
+                    if (!exprInfo) return;
+
+                    if (newText) {
+                        try {
+                            data.updateData[exprInfo.qty] = math.evaluate(transExpr(newText));
+                            data.updateData[exprInfo.expr] = newText;
+                        } catch(err) {
+                            toastr.error('输入的表达式非法');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                    } else {
+                        data.updateData[exprInfo.qty] = 0;
+                        data.updateData[exprInfo.expr] = '';
+                    }
                 } else {
                     data.updateData[col.field] = newText;
                 }
 
                 postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     const updateRst = pos.updateDatas(result.pos);
+                    billsTag.refreshPosTagView(updateRst.update);
                     // 刷新当前行, 不适用于新增(在非下一空白行新增)
                     if (updateRst.create.length > 0) {
                         // info.sheet.addRows(info.row, 1);
@@ -1997,9 +2057,16 @@ $(document).ready(function() {
                             const style = sheet.getStyle(iRow, iCol);
                             if (!style.locked) {
                                 const colSetting = sheet.zh_setting.cols[iCol];
-                                data[colSetting.field] = null;
-                                const exprInfo = getExprInfo(colSetting.field);
-                                if (exprInfo) data[exprInfo.expr] = '';
+                                if (colSetting.field.indexOf('_expr') > 0) {
+                                    const exprInfo = getExprInfo(colSetting.field, true);
+                                    if (!exprInfo) continue;
+                                    data[exprInfo.expr] = '';
+                                    data[exprInfo.qty] = 0;
+                                } else {
+                                    data[colSetting.field] = null;
+                                    const exprInfo = getExprInfo(colSetting.field);
+                                    if (exprInfo) data[exprInfo.expr] = '';
+                                }
                                 bDel = true;
                             }
                         }
@@ -2011,7 +2078,8 @@ $(document).ready(function() {
                 }
                 if (datas.length > 0) {
                     postData('/tender/' + getTenderId() + '/pos/update', {updateType: 'update', updateData: datas}, function (result) {
-                        pos.updateDatas(result.pos);
+                        const updateRst = pos.updateDatas(result.pos);
+                        billsTag.refreshPosTagView(updateRst.update);
                         posOperationObj.loadCurPosData();
                         const loadResult = ledgerTree.loadPostData(result.ledger);
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
@@ -2041,6 +2109,10 @@ $(document).ready(function() {
             }
             if (data.updateData.length > 0) {
                 postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
+                    for (const p of result.pos) {
+                        const posInfo = pos.getPos(p);
+                        billsTag.afterDeletePos(posInfo);
+                    }
                     pos.removeDatas(result.pos);
                     sheet.deleteRows(row, count);
                     const loadResult = ledgerTree.loadPostData(result.ledger);
@@ -2112,7 +2184,6 @@ $(document).ready(function() {
                         if (!colSetting) continue;
 
                         posData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
-
                         if (colSetting.type === 'Number') {
                             const num = _.toNumber(posData[colSetting.field]);
                             if (num) {
@@ -2132,6 +2203,15 @@ $(document).ready(function() {
                                     toastMessageUniq(hint.expr);
                                 }
                             }
+                        } else if (colSetting.field.indexOf('_expr') > 0) {
+                            try {
+                                const exprInfo = getExprInfo(colSetting.field, true);
+                                posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                                posData[exprInfo.qty] = math.evaluate(transExpr(posData[exprInfo.expr]));
+                                bPaste = true;
+                            } catch (err) {
+                                toastMessageUniq(hint.expr);
+                            }
                         } else {
                             bPaste = true;
                         }
@@ -2142,7 +2222,8 @@ $(document).ready(function() {
                 }
                 if (data.length > 0) {
                     postData('/tender/' + getTenderId() + '/pos/paste', data, function (result) {
-                        pos.updateDatas(result.pos);
+                        const updateRst = pos.updateDatas(result.pos);
+                        billsTag.refreshPosTagView(updateRst.update);
                         posOperationObj.loadCurPosData();
                         const loadResult = ledgerTree.loadPostData(result.ledger);
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
@@ -2233,69 +2314,95 @@ $(document).ready(function() {
         posSpread.bind(spreadNS.Events.EditStarting, posOperationObj.editStarting);
         posSpread.bind(spreadNS.Events.EditEnded, posOperationObj.editEnded);
         posSpread.bind(spreadNS.Events.ClipboardPasted, posOperationObj.clipboardPasted);
-        const mergePeg = NewMergePeg({ callback: posOperationObj.addPegs });
-        // 右键菜单
-        $.contextMenu({
-            selector: '#pos-spread',
-            build: function ($trigger, e) {
-                const target = SpreadJsObj.safeRightClickSelection($trigger, e, posSpread);
-                return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+    }
+    const mergePeg = NewMergePeg({ callback: posOperationObj.addPegs });
+    // 右键菜单
+    $.contextMenu({
+        selector: '#pos-spread',
+        build: function ($trigger, e) {
+            const target = SpreadJsObj.safeRightClickSelection($trigger, e, posSpread);
+            return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+        },
+        items: {
+            'insert': {
+                name: '插入',
+                icon: 'fa-plus',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    return node && node.children && node.children.length > 0;
+                },
+                callback: function (key, opt) {
+                    posOperationObj.insertPos(posSpread.getActiveSheet());
+                },
+                visible: function(key, opt) {
+                    return !readOnly;
+                }
             },
-            items: {
-                'insert': {
-                    name: '插入',
-                    icon: 'fa-plus',
-                    disabled: function (key, opt) {
-                        const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                        return node && node.children && node.children.length > 0;
-                    },
-                    callback: function (key, opt) {
-                        posOperationObj.insertPos(posSpread.getActiveSheet());
+            'delete': {
+                name: '删除',
+                icon: 'fa-remove',
+                disabled: function (key, opt) {
+                    const sheet = posSpread.getActiveSheet();
+                    if (sheet.zh_data) {
+                        const selection = sheet.getSelections();
+                        return sheet.zh_data.length < selection[0].row + selection[0].rowCount;
+                    } else {
+                        return true;
                     }
                 },
-                'delete': {
-                    name: '删除',
-                    icon: 'fa-remove',
-                    disabled: function (key, opt) {
-                        const sheet = posSpread.getActiveSheet();
-                        if (sheet.zh_data) {
-                            const selection = sheet.getSelections();
-                            return sheet.zh_data.length < selection[0].row + selection[0].rowCount;
-                        } else {
-                            return true;
-                        }
-                    },
-                    callback: function (key, opt) {
-                        posOperationObj.deletePos(posSpread.getActiveSheet());
-                    }
+                callback: function (key, opt) {
+                    posOperationObj.deletePos(posSpread.getActiveSheet());
                 },
-                'merge-peg': {
-                    name: '合并起讫桩号',
-                    disabled: function (key, opt) {
-                        const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                        return _.isNil(node) || _.isNil(node.b_code) || node.b_code === '';
-                    },
-                    callback: function (key, opt) {
-                        mergePeg.show();
-                    }
+                visible: function(key, opt) {
+                    return !readOnly;
+                }
+            },
+            'merge-peg': {
+                name: '合并起讫桩号',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    return _.isNil(node) || _.isNil(node.b_code) || node.b_code === '';
+                },
+                callback: function (key, opt) {
+                    mergePeg.show();
+                },
+                visible: function(key, opt) {
+                    return !readOnly;
+                }
+            },
+            tag: {
+                name: '书签',
+                disabled: function(key, opt) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    const pos = SpreadJsObj.getSelectObject(posSpread.getActiveSheet());
+                    return _.isNil(node) || _.isNil(pos);
+                },
+                callback: function(key, opt) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    const pos = SpreadJsObj.getSelectObject(posSpread.getActiveSheet());
+                    addTag.do(node, pos);
                 }
             }
-        });
-    }
+        }
+    });
 
     postData(window.location.pathname + '/load', {}, function (data) {
         ledgerTree.loadDatas(data.bills);
         treeCalc.calculateAll(ledgerTree);
+        checkShowLast(data.bills.length);
+        pos.loadDatas(data.pos);
+
         for (const t of data.tags) {
-            t.node = ledgerTree.datas.find(x => {return x.id === t.lid});
+            t.node = ledgerTree.datas.find(x => { return x.id === t.lid; });
+            if (t.pos_id) {
+                const posRange = pos.getLedgerPos(t.lid) || [];
+                t.pos = posRange.find(x => { return x.id === t.pos_id; });
+            }
         }
         billsTag.loadDatas(data.tags);
         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
         SpreadJsObj.loadTopAndSelect(ledgerSpread.getActiveSheet(), ckBillsSpread);
 
-        checkShowLast(data.bills.length);
-
-        pos.loadDatas(data.pos);
         posOperationObj.loadCurPosData();
         SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
 

+ 66 - 0
app/public/js/ledger_gather.js

@@ -73,6 +73,7 @@ $(document).ready(() => {
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     const gclSheet = gclSpread.getActiveSheet();
+
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
     const leafXmjSpreadSetting = {
         cols: [
@@ -103,6 +104,34 @@ $(document).ready(() => {
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
     const leafXmjSheet = leafXmjSpread.getActiveSheet();
 
+    const gatherLeafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-gather-spread')[0]);
+    const gatherLeafXmjSpreadSetting = {
+        cols: [
+            {title: '单位工程', colSpan: '1', rowSpan: '1', field: 'dwgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分部工程', colSpan: '1', rowSpan: '1', field: 'fbgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分项工程', colSpan: '1', rowSpan: '1', field: 'fxgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '细目', colSpan: '1', rowSpan: '1', field: 'jldy', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '计量单元', colSpan: '1', rowSpan: '1', field: 'bwmx', hAlign: 0, width: 100, formatter: '@'},
+            {title: '台账数量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        localCache: {
+            key: 'ledger-gather-leafXmj',
+            colWidth: true,
+        },
+    };
+    if (!isTz) gatherLeafXmjSpreadSetting.cols.splice(1, 1);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherLeafXmjSpreadSetting);
+    const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
+    SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
+
     let gclGatherData;
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -114,6 +143,16 @@ $(document).ready(() => {
             SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, []);
         }
     }
+    function loadGatherLeafXmjData(iGclRow) {
+        const gcl = iGclRow ? gclGatherData[iGclRow] : SpreadJsObj.getSelectObject(gclSheet);
+        SpreadJsObj.resetTopAndSelect(gatherLeafXmjSheet);
+        console.log(gcl.gatherLeafXmjs);
+        if (gcl) {
+            SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, gcl.gatherLeafXmjs);
+        } else {
+            SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, []);
+        }
+    }
     // 标红显示 签约-台账≠0
     function checkCompareData() {
         const sheet = gclSheet;
@@ -134,6 +173,7 @@ $(document).ready(() => {
         const iNewRow = info.newSelections[0].row;
         if (!info.oldSelections || iNewRow !== info.oldSelections[0].row) {
             loadLeafXmjData(iNewRow);
+            loadGatherLeafXmjData(iNewRow);
         }
     });
 
@@ -168,6 +208,7 @@ $(document).ready(() => {
         gclGatherModel.loadDealBillsData(data.dealBills);
         gclGatherData = gclGatherModel.gatherGclData();
         gclGatherModel.checkDiffer(gclGatherData);
+        gclGatherModel.reGatherLeafXmj();
         for (const gcl of gclGatherData) {
             gcl.compare_qty = ZhCalc.sub(gcl.quantity, gcl.deal_bills_qty);
             gcl.compare_tp = ZhCalc.sub(gcl.total_price, gcl.deal_bills_tp);
@@ -176,6 +217,7 @@ $(document).ready(() => {
         SpreadJsObj.loadSheetData(gclSheet, SpreadJsObj.DataType.Data, gclGatherData);
         checkCompareData();
         loadLeafXmjData(0);
+        loadGatherLeafXmjData(0);
 
         const chapterData = gclGatherModel.gatherChapterData(chapter, data.spec, ['total_price']);
         for (const c of chapterData) {
@@ -222,6 +264,7 @@ $(document).ready(() => {
         },
         afterLocated: function () {
             loadLeafXmjData();
+            loadGatherLeafXmjData();
         },
         calcSum: function (result) {
             const sum = { name: '合计', searchIndex: -1 };
@@ -250,6 +293,26 @@ $(document).ready(() => {
         }
         gclSpread.refresh();
         leafXmjSpread.refresh();
+        gatherLeafXmjSpread.refresh();
+    });
+
+    $('[name=gather-xmj]').change(function() {
+        const checkOption = $('[name=gather-xmj]:checked');
+        if (checkOption.length === 0) {
+            toastr.warning('请至少选择一个汇总条件');
+            return;
+        }
+
+        const fields = [];
+        for (const co of checkOption) {
+            fields.push(co.value);
+        }
+        for (const col of gatherLeafXmjSpreadSetting.cols) {
+            col.visible = col.type === 'Number' || fields.indexOf(col.field) >= 0;
+        }
+        gclGatherModel.reGatherLeafXmj(fields);
+        SpreadJsObj.refreshColumnVisible(gatherLeafXmjSheet);
+        loadGatherLeafXmjData();
     });
 
     $.subMenu({
@@ -268,6 +331,7 @@ $(document).ready(() => {
             autoFlashHeight();
             gclSpread.refresh();
             leafXmjSpread.refresh();
+            gatherLeafXmjSpread.refresh();
         }
     });
     $.divResizer({
@@ -277,6 +341,7 @@ $(document).ready(() => {
             let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
             $(".sp-wrap").height(bcontent-30);
             leafXmjSpread.refresh();
+            gatherLeafXmjSpread.refresh();
         }
     });
     $.divResizer({
@@ -284,6 +349,7 @@ $(document).ready(() => {
         callback: function () {
             gclSpread.refresh();
             leafXmjSpread.refresh();
+            gatherLeafXmjSpread.refresh();
         }
     });
     $('#exportExcel').click(function () {

+ 4 - 4
app/public/js/material.js

@@ -1554,10 +1554,10 @@ $(document).ready(() => {
                 }
                 if (param.length === 0) return true;
                 if (param.length > 1) {
-                    if (param[0].value === '-') {
-                        param[1].value = '-' + param[1];
+                    if (param[0].value === '-' && param[1].type === 'num') {
+                        param[1].value = '-' + param[1].value;
+                        param.shift();
                     }
-                    param.unshift();
                 }
                 const iLen = param.length;
                 let iLeftCount = 0, iRightCount = 0;
@@ -1574,7 +1574,7 @@ $(document).ready(() => {
                         if (num === undefined || num === null || _.isNaN(num))
                             return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
                         if (i > 0) {
-                            if (param[i - 1].type !== 'calc') {
+                            if (param[i - 1].type !== 'calc' && param[i - 1].type !== 'left') {
                                 return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
                             } else if (param[i - 1].value === '/' && num === 0) {
                                 return [false, '输入的表达式非法:请勿除0'];

+ 3 - 3
app/public/js/material_checklist.js

@@ -843,10 +843,10 @@ $(document).ready(() => {
                 }
                 if (param.length === 0) return true;
                 if (param.length > 1) {
-                    if (param[0].value === '-') {
-                        param[1].value = '-' + param[1];
+                    if (param[0].value === '-' && param[1].type === 'num') {
+                        param[1].value = '-' + param[1].value;
+                        param.shift();
                     }
-                    param.unshift();
                 }
                 const iLen = param.length;
                 let iLeftCount = 0, iRightCount = 0;

+ 3 - 3
app/public/js/material_list.js

@@ -1742,10 +1742,10 @@ $(document).ready(() => {
                 }
                 if (param.length === 0) return true;
                 if (param.length > 1) {
-                    if (param[0].value === '-') {
-                        param[1].value = '-' + param[1];
+                    if (param[0].value === '-' && param[1].type === 'num') {
+                        param[1].value = '-' + param[1].value;
+                        param.shift();
                     }
-                    param.unshift();
                 }
                 const iLen = param.length;
                 let iLeftCount = 0, iRightCount = 0;

+ 53 - 6
app/public/js/shares/cs_tools.js

@@ -823,7 +823,10 @@ const showSelectTab = function(select, spread, afterShow) {
                 '<a class="dropdown-item text-warning " href="javascript: void(0);" name="tag-color"><i class="fa fa-tint"></i></a>',
                 '<a class="dropdown-item text-info " href="javascript: void(0);" name="tag-color"><i class="fa fa-tint"></i></a>', '</div>');
             tagHtml.push('</div>');
-            tag.node && tagHtml.push((tag.node.code || '') + (tag.node.b_code || ''), ' / ', tag.node.name || '');
+            if (tag.node) {
+                const posHint = tag.pos ? ' - ' + tag.pos.name : '';
+                tagHtml.push((tag.node.code || '') + (tag.node.b_code || ''), ' / ', tag.node.name || '', posHint);
+            }
             tagHtml.push('</div>');
 
             tagHtml.push('<div class="card-body p-2">');
@@ -854,13 +857,16 @@ const showSelectTab = function(select, spread, afterShow) {
             tagHtml.push('<div class="card-header p-2"><div class="dropdown">');
             tagHtml.push(`<div class="pull-left mr-2"><i class="fa fa-tag ${tagClass.tagClass}"></i></div>`);
             tagHtml.push('</div>');
-            tag.node && tagHtml.push((tag.node.code || '') + (tag.node.b_code || ''), ' / ', tag.node.name || '');
+            if (tag.node) {
+                const posHint = tag.pos ? ' - ' + tag.pos.name : '';
+                tagHtml.push((tag.node.code || '') + (tag.node.b_code || ''), ' / ', tag.node.name || '', posHint);
+            }
             if (tag.share) {
                 tagHtml.push(`<div class="pull-right"><i class="fa fa-users text-warning" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="所有参与台账审批管理的用户都可以看到这条书签"></i> <span>${tag.u_name}</span></div>`);
             }
             tagHtml.push('<div class="pull-right edit-tag-btn">');
             const lid = tag.node ? tag.node[setting.treeId] : -1;
-            tagHtml.push(`<a class="mr-1" name="bills-tag-locate" href="javascript: void(0);" lid="${lid}"><i class="fa fa-crosshairs"></i> 定位</a>`);
+            tagHtml.push(`<a class="mr-1" name="bills-tag-locate" href="javascript: void(0);" lid="${lid}" pos_id="${tag.pos_id}"><i class="fa fa-crosshairs"></i> 定位</a>`);
             if (tag.uid === userID && !setting.readOnly) tagHtml.push(`<a href="javascript: void(0);" name="bills-tag-edit" tag-id="${tag.id}"><i class="fa fa-edit"></i> 编辑</a>`);
             tagHtml.push('</div>');
             tagHtml.push('<div class="card-body p-2">', '<p class="card-text">', tag.comment, '</p>', '</div>');
@@ -898,6 +904,17 @@ const showSelectTab = function(select, spread, afterShow) {
                 refreshTagView(tag);
             }
         };
+        const refreshPosTagView = function(pos) {
+            const posRange = pos instanceof Array ? pos : [pos];
+            for (const p of posRange) {
+                const bi = billsIndexes[p.lid] || [];
+                const pi = bi.filter(x => { return x.pos_id === p.id; });
+
+                for (const tag of pi) {
+                    refreshTagView(tag);
+                }
+            }
+        };
 
         const reviewTag = function (tag, isTop = false) {
             const obj = $('#bills-tag-' + tag.id);
@@ -1006,7 +1023,6 @@ const showSelectTab = function(select, spread, afterShow) {
             const billsTags = billsIndexes[id] || [];
             return billsTags.length > 0 ? billsTags.map(x => {return x.color}) : undefined;
         };
-
         const getBillsTagsInfo = function (id) {
             const billsTags = billsIndexes[id] || [];
             return billsTags.length > 0 ? billsTags.map(x => {
@@ -1014,6 +1030,19 @@ const showSelectTab = function(select, spread, afterShow) {
                 return {color: x.color, comment: x.comment, tagClass: tagClass.tagClass};
             }) : undefined;
         };
+        const getPosTagsColor = function (lid, pos_id) {
+            const billsTags = billsIndexes[lid] || [];
+            const posTags = billsTags.filter(x => { return x.pos_id === pos_id; });
+            return posTags.length > 0 ? posTags.map(x => {return x.color}) : undefined;
+        };
+        const getPosTagsInfo = function(lid, pos_id) {
+            const billsTags = billsIndexes[lid] || [];
+            const posTags = billsTags.filter(x => { return x.pos_id === pos_id; });
+            return posTags.length > 0 ? posTags.map(x => {
+                const tagClass = classIndexes.find(tc => {return tc.color === x.color}) || {};
+                return {color: x.color, comment: x.comment, tagClass: tagClass.tagClass};
+            }) : undefined;
+        };
 
         const afterDeleteBills = function (nodes) {
             for (const node of nodes) {
@@ -1027,11 +1056,26 @@ const showSelectTab = function(select, spread, afterShow) {
                 }
             }
         };
+        const afterDeletePos = function (pos) {
+            for (const p of pos) {
+                const bi = billsIndexes[p.lid];
+                if (!bi) continue;
+
+                const pi = bi.filter(x => { return x.pos_id === p.id; });
+                for (const piTag of pi) {
+                    const delTag = billsTags.find(x => {return x.id === piTag.id});
+                    billsTags.splice(billsTags.indexOf(delTag), 1);
+                    $('#bills-tag-' + delTag.id).remove();
+                    bi.splice(pi.indexOf(piTag), 1);
+                }
+            }
+        };
 
         $('body').on('click', '[name=bills-tag-locate]', function () {
             const lid = parseInt(this.getAttribute('lid'));
+            const pos_id = this.getAttribute('pos_id');
             SpreadJsObj.locateTreeNode(setting.relaSpread.getActiveSheet(), lid, true);
-            setting.afterLocated && setting.afterLocated();
+            setting.afterLocated && setting.afterLocated(lid, pos_id);
         });
         $('body').on('click', '[name=bills-tag-edit]', function () {
             const tagId = this.getAttribute('tag-id');
@@ -1112,7 +1156,10 @@ const showSelectTab = function(select, spread, afterShow) {
         $('#bills-tag-search').bind('click', () => {searchTagsAndShow();});
         $('#bills-tag-keyword').bind('keydown', e => {if (e.keyCode === 13) searchTagsAndShow();});
 
-        return { loadDatas, updateDatasAndShow, show, getBillsTagsColor, getBillsTagsInfo, refreshBillsTagView, afterDeleteBills }
+        return { loadDatas, updateDatasAndShow, show,
+            getBillsTagsColor, getBillsTagsInfo, getPosTagsColor, getPosTagsInfo,
+            refreshBillsTagView, refreshPosTagView,
+            afterDeleteBills, afterDeletePos }
     };
 
     $.sumLoadMiss = function (setting) {

+ 12 - 4
app/public/js/shares/new_tag.js

@@ -1,12 +1,15 @@
 const newTag = function (setting) {
     const billsTag = setting.billsTag;
     const billsSheet = setting.ledgerSheet;
-    let relaNode;
+    const posSheet = setting.posSheet;
+    let relaNode, relaPos;
 
-    const addTag = function (node) {
+    const addTag = function (node, pos) {
         relaNode = node;
+        relaPos = pos;
         const code = (node.code || '') + (node.b_code || '');
-        $('#addtag-info').html(code + ' / ' + node.name);
+        const posInfo = pos ? ' - ' + pos.name : '';
+        $('#addtag-info').html(code + ' / ' + node.name + posInfo);
         $('#addtag').modal('show');
         $('#addtag-content').val('');
     };
@@ -19,14 +22,19 @@ const newTag = function (setting) {
             add: {
                 color: $('.active[name=addtag-color]').attr('tag-color'),
                 lid: setting.key ? relaNode[setting.key] : relaNode.id,
+                pos_id: relaPos ? (setting.key ? relaPos[setting.key] : relaPos.id) : '',
                 share: $('#addtag-share')[0].checked,
                 comment: $('#addtag-content').val(),
             }
         };
         postData(window.location.pathname + '/tag', data, result => {
-            if (result.add) result.add.node = relaNode;
+            if (result.add) {
+                result.add.node = relaNode;
+                result.add.pos = relaPos;
+            }
             billsTag.updateDatasAndShow(result);
             SpreadJsObj.repaintNodesRowHeader(billsSheet, relaNode);
+            if (relaPos) SpreadJsObj.repaintNodesRowHeader(billsSheet, relaPos);
             $('#addtag').modal('hide');
         });
     });

+ 1 - 1
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -1621,7 +1621,7 @@ const SpreadJsObj = {
                                 .css("font", "9pt Arial")
                                 .css("background", "white")
                                 .css("padding", 5)
-                                .css("z-index", 999)
+                                .css("z-index", 9999)
                                 .css("max-width", maxHintWidth)
                                 .css("word-wrap", "break-word")
                                 .attr("id", 'autoTip');

+ 2 - 2
app/public/js/stage.js

@@ -4211,7 +4211,7 @@ $(document).ready(() => {
                             return !curChange || !changeBills || curChange.is_import;
                         },
                         visible: function (key, opt) {
-                            return is_debug && stage.status === 1 && stage.isCheckFirst;
+                            return stage.status === 1 && stage.isCheckFirst;
                         }
                     },
                     'autoUseAll': {
@@ -4227,7 +4227,7 @@ $(document).ready(() => {
                             return !curChange || curChange.is_import;
                         },
                         visible: function (key, opt) {
-                            return is_debug && stage.status === 1 && stage.isCheckFirst;
+                            return stage.status === 1 && stage.isCheckFirst;
                         }
                     }
                 }

+ 4 - 4
app/public/js/stage_im.js

@@ -463,9 +463,9 @@ const stageIm = (function () {
         if (!posRange) { return }
         for (const p of posRange) {
             if (checkZero(p.contract_qty) && checkZero(p.qc_qty) && checkZero(p.qc_minus_qty)) { continue; }
-            let lp = _.find(gclBills.pos, {name: p.name});
+            let lp = _.find(gclBills.pos, {name: p.name, position: p.position || ''});
             if (!lp) {
-                lp = {name: p.name};
+                lp = {name: p.name, position: p.position || ''};
                 gclBills.pos.push(lp);
             }
             lp.jl = ZhCalc.add(lp.jl, p.gather_qty);
@@ -755,9 +755,9 @@ const stageIm = (function () {
         if (!posRange) { return }
         for (const p of posRange) {
             if (checkZero(p.contract_qty) && checkZero(p.qc_qty) && checkZero(p.qc_minus_qty)) { continue; }
-            let lp = _.find(lx.pos, {name: p.name});
+            let lp = _.find(lx.pos, {name: p.name, position: p.position || ''});
             if (!lp) {
-                lp = {name: p.name};
+                lp = {name: p.name, position: p.position || ''};
                 lx.pos.push(lp);
             }
             lp.jl = ZhCalc.add(lp.jl, p.gather_qty);

+ 5 - 3
app/service/change.js

@@ -294,7 +294,7 @@ module.exports = app => {
             let sql = '';
             let sqlParam = '';
             const stateSql = state ? ' AND a.state = ' + state : '';
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 sql = 'SELECT a.* FROM ?? As a WHERE a.tid = ?' + stateSql;
                 sqlParam = [this.tableName, tenderId];
             } else {
@@ -386,7 +386,7 @@ module.exports = app => {
          */
         async getCountByStatus(tenderId, status, state = 0) {
             const stateSql = state ? ' AND a.state = ' + state : '';
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT count(*) AS count FROM ?? AS a WHERE a.tid = ?' + stateSql + ' ORDER BY a.in_time DESC';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);
@@ -464,7 +464,7 @@ module.exports = app => {
         }
 
         async getTp(tenderId, status) {
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT SUM(cast (total_price as decimal(18,6))) AS total_price FROM ?? WHERE tid = ?';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);
@@ -1630,6 +1630,7 @@ module.exports = app => {
                     tp_decimal: null,
                     up_decimal: null,
                     is_revise: 1,
+                    final_auditor_str: '',
                 };
                 const options = {
                     where: {
@@ -1797,6 +1798,7 @@ module.exports = app => {
                     cin_time: Date.parse(time) / 1000,
                     sin_time: null,
                     total_price,
+                    final_auditor_str: '',
                 };
                 const options = {
                     where: {

+ 3 - 3
app/service/change_apply.js

@@ -157,7 +157,7 @@ module.exports = app => {
         async getListByStatus(tenderId, status = 0, hadlimit = 1, sortBy = '', orderBy = '') {
             let sql = '';
             let sqlParam = '';
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 sql = 'SELECT a.*, p.name as account_name FROM ?? As a LEFT JOIN ?? AS p On a.notice_uid = p.id WHERE a.tid = ?';
                 sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId];
             } else {
@@ -246,7 +246,7 @@ module.exports = app => {
          * @return {void}
          */
         async getCountByStatus(tenderId, status) {
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT count(*) AS count FROM ?? WHERE tid = ? ORDER BY in_time DESC';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);
@@ -319,7 +319,7 @@ module.exports = app => {
          * @return {void}
          */
         async getTp(tenderId, status) {
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT SUM(cast (total_price as decimal(18,6))) AS total_price FROM ?? WHERE tid = ?';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);

+ 4 - 2
app/service/change_apply_audit.js

@@ -389,9 +389,9 @@ module.exports = app => {
             const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`caid`,' +
                 '    m.`status` As `mstatus`, m.`code` As `mcode`,' +
                 '    t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
-                '  FROM ?? AS ma, ?? AS m, ?? As t ' +
+                '  FROM ?? AS ma LEFT JOIN ?? AS m ON  ma.`caid` = m.`id` LEFT JOIN ?? As t ON ma.`tid` = t.`id`' +
                 '  WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' +
-                '    and ma.`caid` = m.`id` and ma.`tid` = t.`id` ORDER BY ma.`begin_time` DESC';
+                '  ORDER BY ma.`begin_time` DESC';
             const sqlParam = [this.tableName, this.ctx.service.changeApply.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.checkNo, auditConst.status.checkNo];
             const result = await this.db.query(sql, sqlParam);
             // 过滤result中存在重复sid的值, 保留最新的一条
@@ -963,6 +963,7 @@ module.exports = app => {
                     decimal: null,
                     status: auditConst.status.revise,
                     times: change.times + 1,
+                    final_auditor_str: '',
                 });
                 await transaction.commit();
                 result = true;
@@ -1077,6 +1078,7 @@ module.exports = app => {
                     notice_code: null,
                     notice_uid: null,
                     decimal: null,
+                    final_auditor_str: '',
                 });
                 //  检查三方特殊推送
                 await this.ctx.service.specMsg.addChangeApplyMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_apply.flow);

+ 9 - 2
app/service/change_audit.js

@@ -218,12 +218,19 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async getAuditChange(uid) {
+            // const sql = 'SELECT ca.`uid`, ca.`times`, ca.`usite`, ca.`usort`, ca.`tid`, ca.`cid`, ca.`begin_time`, ca.`end_time`, ca.`sin_time`, ca.`name` As `caname`, ' +
+            //     '    c.`code` As `ccode`, c.`name` As `cname`, c.`status` As `cstatus`, c.`cin_time`, ' +
+            //     '    t.`name`, t.`type`, t.`user_id` ' +
+            //     '  FROM ?? AS ca, ?? AS c, ?? As t ' +
+            //     '  WHERE ca.`uid` = ? and ca.`status` = ? and c.`status` != ?' +
+            //     '    and ca.`cid` = c.`cid` and ca.`tid` = t.`id` ORDER BY ca.`sin_time` DESC';
             const sql = 'SELECT ca.`uid`, ca.`times`, ca.`usite`, ca.`usort`, ca.`tid`, ca.`cid`, ca.`begin_time`, ca.`end_time`, ca.`sin_time`, ca.`name` As `caname`, ' +
                 '    c.`code` As `ccode`, c.`name` As `cname`, c.`status` As `cstatus`, c.`cin_time`, ' +
                 '    t.`name`, t.`type`, t.`user_id` ' +
-                '  FROM ?? AS ca, ?? AS c, ?? As t ' +
+                '  FROM ?? AS ca LEFT JOIN ?? AS c ON ca.`cid` = c.`cid` ' +
+                '    LEFT JOIN ?? As t ON ca.`tid` = t.`id` ' +
                 '  WHERE ca.`uid` = ? and ca.`status` = ? and c.`status` != ?' +
-                '    and ca.`cid` = c.`cid` and ca.`tid` = t.`id` ORDER BY ca.`sin_time` DESC';
+                '  ORDER BY ca.`sin_time` DESC';
             const sqlParam = [this.tableName, this.ctx.service.change.tableName, this.ctx.service.tender.tableName, uid, auditConst.status.checking, auditConst.status.uncheck];
             const changes = await this.db.query(sql, sqlParam);
             for (const c of changes) {

+ 3 - 3
app/service/change_plan.js

@@ -178,7 +178,7 @@ module.exports = app => {
         async getListByStatus(tenderId, status = 0, hadlimit = 1, sortBy = '', orderBy = '') {
             let sql = '';
             let sqlParam = '';
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 sql = 'SELECT a.*, p.name as account_name FROM ?? As a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ?';
                 sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId];
             } else {
@@ -267,7 +267,7 @@ module.exports = app => {
          * @return {void}
          */
         async getCountByStatus(tenderId, status) {
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT count(*) AS count FROM ?? WHERE tid = ? ORDER BY in_time DESC';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);
@@ -340,7 +340,7 @@ module.exports = app => {
          * @return {void}
          */
         async getTp(tenderId, status) {
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 const sql5 = 'SELECT SUM(cast (total_price as decimal(18,6))) AS total_price FROM ?? WHERE tid = ?';
                 const sqlParam5 = [this.tableName, tenderId];
                 const result5 = await this.db.query(sql5, sqlParam5);

+ 4 - 2
app/service/change_plan_audit.js

@@ -392,9 +392,9 @@ module.exports = app => {
             const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`cpid`,' +
                 '    m.`status` As `mstatus`, m.`code` As `mcode`,' +
                 '    t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
-                '  FROM ?? AS ma, ?? AS m, ?? As t ' +
+                '  FROM ?? AS ma LEFT JOIN ?? AS m ON  ma.`cpid` = m.`id` LEFT JOIN ?? As t ON ma.`tid` = t.`id`' +
                 '  WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' +
-                '    and ma.`cpid` = m.`id` and ma.`tid` = t.`id` ORDER BY ma.`begin_time` DESC';
+                '  ORDER BY ma.`begin_time` DESC';
             const sqlParam = [this.tableName, this.ctx.service.changePlan.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.checkNo, auditConst.status.checkNo];
             const result = await this.db.query(sql, sqlParam);
             // 过滤result中存在重复sid的值, 保留最新的一条
@@ -1040,6 +1040,7 @@ module.exports = app => {
                     decimal: null,
                     status: auditConst.status.revise,
                     times: change.times + 1,
+                    final_auditor_str: '',
                 });
 
                 await transaction.commit();
@@ -1151,6 +1152,7 @@ module.exports = app => {
                 await transaction.update(this.ctx.service.changePlan.tableName, {
                     id: change.id,
                     status: auditConst.status.checking,
+                    final_auditor_str: '',
                 });
                 // 清除上一人的值并调整spamount值
                 const updateList = [];

+ 2 - 2
app/service/change_project.js

@@ -148,7 +148,7 @@ module.exports = app => {
         async getListByStatus(tenderId, status = 0, hadlimit = 1, sortBy = '', orderBy = '') {
             let sql = '';
             let sqlParam = '';
-            if (this.ctx.tender.isTourist && status === 0) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin) && status === 0) {
                 sql = 'SELECT a.*, p.name as account_name FROM ?? As a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ?';
                 sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId];
             } else {
@@ -250,7 +250,7 @@ module.exports = app => {
          * @return {void}
          */
         async getCountByStatus(tenderId, status) {
-            if (this.ctx.tender.isTourist) {
+            if ((this.ctx.tender.isTourist || this.ctx.session.sessionUser.is_admin)) {
                 let touristSql;
                 let touristSqlParam;
                 let touristResult;

+ 4 - 2
app/service/change_project_audit.js

@@ -397,9 +397,9 @@ module.exports = app => {
             const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`cpid`,' +
                 '    m.`status` As `mstatus`, m.`code` As `mcode`,' +
                 '    t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
-                '  FROM ?? AS ma, ?? AS m, ?? As t ' +
+                '  FROM ?? AS ma LEFT JOIN ?? AS m ON  ma.`cpid` = m.`id` LEFT JOIN ?? As t ON ma.`tid` = t.`id`' +
                 '  WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' +
-                '    and ma.`cpid` = m.`id` and ma.`tid` = t.`id` ORDER BY ma.`begin_time` DESC';
+                '  ORDER BY ma.`begin_time` DESC';
             const sqlParam = [this.tableName, this.ctx.service.changeProject.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.back, auditConst.status.back];
             const result = await this.db.query(sql, sqlParam);
             // 过滤result中存在重复cpid的值, 保留最新的一条
@@ -1101,6 +1101,7 @@ module.exports = app => {
                     id: change.id,
                     status: auditConst.status.revise,
                     times: change.times + 1,
+                    final_auditor_str: '',
                 });
 
                 await transaction.commit();
@@ -1212,6 +1213,7 @@ module.exports = app => {
                 await transaction.update(this.ctx.service.changeProject.tableName, {
                     id: change.id,
                     status: auditConst.status.checking,
+                    final_auditor_str: '',
                 });
                 //  检查三方特殊推送
                 await this.ctx.service.specMsg.addChangeProjectMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_project.flow);

+ 2 - 2
app/service/ledger_tag.js

@@ -8,7 +8,7 @@
  * @version
  */
 
-const validField = ['lid', 'share', 'color', 'comment'];
+const validField = ['lid', 'pos_id', 'share', 'color', 'comment'];
 
 module.exports = app => {
 
@@ -31,7 +31,7 @@ module.exports = app => {
          * @returns {Promise<void>}
          */
         async getDatas(tid, sid = -1, settleId = -1) {
-            const sql = 'SELECT la.id, la.uid, la.lid, la.share, la.color, la.comment, pa.name as u_name FROM ' + this.tableName + ' la ' +
+            const sql = 'SELECT la.id, la.uid, la.lid, la.pos_id, la.share, la.color, la.comment, pa.name as u_name FROM ' + this.tableName + ' la ' +
                 '  LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' pa ON la.uid = pa.id' +
                 '  WHERE la.tid = ? and la.sid = ? and la.settle_id = ? and (la.uid = ? or la.share) ORDER BY la.create_time DESC';
             return await this.db.query(sql, [tid, sid, settleId, this.ctx.session.sessionUser.accountId]);

+ 1 - 1
app/service/project_account.js

@@ -1028,7 +1028,7 @@ module.exports = app => {
         }
 
         async unbindDsk(id) {
-            return await this.db.update(this.tableName, { id, dsk_account: null });
+            return await this.db.update(this.tableName, { id, dsk_account: null, dsk_projects: null });
         }
 
         async saveDskProjects(id, projects) {

+ 1 - 1
app/service/project_spread.js

@@ -47,7 +47,7 @@ module.exports = app => {
             for (const col of colSet) {
                 if (!col.valid) continue;
                 const dc = BaseSetCol.find(x => { return x.key === col.key; });
-                if (!dc) continue;
+                if (!dc || !dc[spreadType]) continue;
                 const orgBaseCols = BaseSpreadColSetting[col.key];
                 if (!orgBaseCols) continue;
 

+ 32 - 16
app/service/report.js

@@ -9,7 +9,7 @@
  */
 
 const BudgetSource = require('../lib/rm/tender_budget');
-const MaterialSource = require('../lib/rm/material');
+const MaterialSource = require('../lib/rm/tender_material');
 
 const rptCustomData = require('../lib/rptCustomData');
 const bindData = {
@@ -552,42 +552,58 @@ module.exports = app => {
 
         // params = { sp_id: int, budget_id: int }
         async budget(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
-            const RptPayment = require('../lib/rm/budget');
-            const rptPayment = new RptPayment(this.ctx);
+            const RptBudget = require('../lib/rm/budget');
+            const rptBudget = new RptBudget(this.ctx);
 
-            return rptPayment.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+            return rptBudget.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
         }
 
         // params = { change_id: uuid }
         async change(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
-            const RptPayment = require('../lib/rm/change');
-            const rptPayment = new RptPayment(this.ctx);
+            const RptChange = require('../lib/rm/change');
+            const rptChange = new RptChange(this.ctx);
 
-            return rptPayment.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+            return rptChange.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
         }
 
         // params = { change_plan_id: int }
         async change_plan(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
-            const RptPayment = require('../lib/rm/change_plan');
-            const rptPayment = new RptPayment(this.ctx);
+            const RptChangePlan = require('../lib/rm/change_plan');
+            const rptChangePlan = new RptChangePlan(this.ctx);
 
-            return rptPayment.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+            return rptChangePlan.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
         }
 
         // params = { change_project_id: int }
         async change_project(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
-            const RptPayment = require('../lib/rm/change_project');
-            const rptPayment = new RptPayment(this.ctx);
+            const RptChangeProj = require('../lib/rm/change_project');
+            const rptChangeProj = new RptChangeProj(this.ctx);
 
-            return rptPayment.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+            return rptChangeProj.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
         }
 
         // params = { change_apply_id: int }
         async change_apply(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
-            const RptPayment = require('../lib/rm/change_apply');
-            const rptPayment = new RptPayment(this.ctx);
+            const RptChangeApply = require('../lib/rm/change_apply');
+            const rptChangeApply = new RptChangeApply(this.ctx);
 
-            return rptPayment.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+            return rptChangeApply.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+        }
+
+        // params = { material_id: int }
+        async material(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
+            const RptMaterial = require('../lib/rm/material');
+            const rptMaterial = new RptMaterial(this.ctx);
+
+            return rptMaterial.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
+        }
+
+        // params = { advance_id: int }
+        async advance(params, sourceFilters, memFieldKeys, customDefine, customSelect) {
+            const RptAdvance = require('../lib/rm/advance');
+            const rptAdvance = new RptAdvance(this.ctx);
+
+            return rptAdvance.getReportData(params, sourceFilters, memFieldKeys, customDefine, customSelect);
         }
 
         async getReportData(source_type, params, sourceFilters, memFieldKeys, customDefine, customSelect) {

+ 1 - 0
app/service/stage_audit.js

@@ -1058,6 +1058,7 @@ module.exports = app => {
                     status: auditConst.status.checking,
                     cache_time_r: this.ctx.stage.cache_time_l,
                     tp_history: JSON.stringify(this.ctx.stage.tp_history),
+                    final_auditor_str: '',
                 });
                 // 已经引用到本期的单价变更,全部取消
                 await this.ctx.service.revisePrice.cancelPriceUsed(this.ctx.stage, transaction);

+ 2 - 0
app/service/sub_project.js

@@ -372,6 +372,7 @@ module.exports = app => {
             if (!transaction) throw '未定义事务';
 
             const subProject = await this.getDataById(spid);
+            if (!subProject) throw '所属项目不存在';
             const rela = subProject.rela_tender.split(',');
             if (rela.indexOf(tid + '') >= 0) return;
 
@@ -385,6 +386,7 @@ module.exports = app => {
             if (!transaction) throw '未定义事务';
 
             const subProject = await this.getDataById(spid);
+            if (!subProject) throw '所属项目不存在';
             const rela = subProject.rela_tender.split(',');
             if (rela.indexOf(tid + '') < 0) return;
 

+ 2 - 2
app/service/tender.js

@@ -280,7 +280,7 @@ module.exports = app => {
                     throw '新增标段数据失败';
                 }
                 await this.ctx.service.tenderCache.insertTenderCache(this.transaction, operate.insertId, sessionUser.accountId);
-                if (data.spid) await ctx.service.subProject.addRelaTender(this.transaction, data.spid, operate.insertId);
+                if (data.spid) await this.ctx.service.subProject.addRelaTender(this.transaction, data.spid, operate.insertId);
 
 
                 // 获取合同支付模板 并添加到标段
@@ -334,7 +334,7 @@ module.exports = app => {
             try {
                 if (tender.spid !== postData.spid) {
                     if (postData.spid) await this.ctx.service.subProject.addRelaTender(conn, postData.spid, id);
-                    if (tender.spid) await this.ctx.service.subProject.removeRelaTender(conn, postData.spid, id);
+                    if (tender.spid) await this.ctx.service.subProject.removeRelaTender(conn, tender.spid, id);
                 }
                 rowData.spid = postData.spid || '';
                 const result = await conn.update(this.tableName, rowData);

+ 3 - 3
app/view/change/apply_information_modal.ejs

@@ -796,7 +796,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -849,7 +849,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -1191,7 +1191,7 @@
         // 重新审批获取手机验证码
         // 获取验证码
         let isPosting = false;
-        $("#get-code").click(function() {
+        $(".get-code").on('click', function() {
             if (isPosting) {
                 return false;
             }

+ 2 - 2
app/view/change/information_modal.ejs

@@ -785,7 +785,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -838,7 +838,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>

+ 3 - 3
app/view/change/plan_information_modal.ejs

@@ -788,7 +788,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -841,7 +841,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -1183,7 +1183,7 @@
         // 重新审批获取手机验证码
         // 获取验证码
         let isPosting = false;
-        $("#get-code").click(function() {
+        $(".get-code").on('click', function() {
             if (isPosting) {
                 return false;
             }

+ 3 - 3
app/view/change/project_information_modal.ejs

@@ -839,7 +839,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -892,7 +892,7 @@
                                 <div class="input-group input-group-sm mb-3">
                                     <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
                                     <div class="input-group-append">
-                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                        <button class="btn btn-outline-secondary get-code" type="button">获取验证码</button>
                                     </div>
                                 </div>
                             </div>
@@ -1200,7 +1200,7 @@
         // 重新审批获取手机验证码
         // 获取验证码
         let isPosting = false;
-        $("#get-code").click(function() {
+        $(".get-code").on('click', function() {
             if (isPosting) {
                 return false;
             }

+ 1 - 1
app/view/change/revise.ejs

@@ -119,7 +119,7 @@
                             <div class="ml-auto">
                                 <% if (isYb) { %>
                                     <a href="javascript:void(0);" id="get-dsk-bills-btn" class="btn btn-sm btn-light text-primary pull-right mt-1" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="重新获取大司空造价书"><i class="fa fa-refresh" aria-hidden="true"></i></a>
-                                    <a href="javascript:void(0);" id="get-dsk-btn" class="btn btn-sm btn-primary pull-right mt-1 mr-1" style="display: none;">获取计价单价</a>
+                                    <a href="javascript:void(0);" id="get-dsk-btn" class="btn btn-sm btn-primary pull-right mt-1 mr-1" style="display: none;">获取计价项目</a>
                                 <% } %>
                             </div>
                         </nav>

+ 1 - 1
app/view/change/revise_modal.ejs

@@ -81,7 +81,7 @@
             </div>
             <div class="modal-body">
                 <h5>当前账号未绑定手机,请先绑定手机。</h5>
-                <h5 class="text-danger">当前账号未绑定大司空账号,可先进行<a href="/profile/sms" target="_blank" class="text-primary hide-dsk-modal">注册账号</a>或者<a href="/profile/sms" target="_blank" class="text-primary hide-dsk-modal">切换账号</a>绑定。</h5>
+                <h5 class="text-danger">当前账号未绑定大司空账号,请先<a href="/profile/sms" target="_blank" class="text-primary hide-dsk-modal">绑定大司空账号</a>。</h5>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>

+ 34 - 0
app/view/ledger/gather.ejs

@@ -29,6 +29,36 @@
                             <li class="nav-item">
                                 <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">所属项目节</a>
                             </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#xmjGather" role="tab">项目节汇总</a>
+                            </li>
+                            <li class="nav-item">
+                                <div class="d-inline-block ml-2 mt-1">
+                                    <span>汇总条件:</span>
+                                    <div class="d-inline-block" style="vertical-align: middle">
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input pt-1" type="checkbox" id="gather-xmj-dwgc" value="dwgc" name="gather-xmj">
+                                            <label class="form-check-label" for="gather-xmj-dwgc">单位工程</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="gather-xmj-fbgc" value="fbgc" name="gather-xmj">
+                                            <label class="form-check-label" for="gather-xmj-fbgc">分部工程</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="gather-xmj-fxgc" value="fxgc" name="gather-xmj">
+                                            <label class="form-check-label" for="gather-xmj-fxgc">分项工程</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="gather-xmj-xm" value="jldy" name="gather-xmj">
+                                            <label class="form-check-label" for="gather-xmj-xm">细目</label>
+                                        </div>
+                                        <div class="form-check form-check-inline">
+                                            <input class="form-check-input" type="checkbox" id="gather-xmj-jldy" value="bwmx" checked name="gather-xmj">
+                                            <label class="form-check-label" for="gather-xmj-jldy">计量单元</label>
+                                        </div>
+                                    </div>
+                                </div>
+                            </li>
                         </ul>
                     </div>
                     <div class="tab-content">
@@ -36,6 +66,10 @@
                             <div class="sp-wrap" id="leaf-xmj-spread">
                             </div>
                         </div>
+                        <div class="tab-pane active" id="xmjGather">
+                            <div class="sp-wrap" id="leaf-xmj-gather-spread">
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>

+ 1 - 1
app/view/profile/modal.ejs

@@ -27,7 +27,7 @@
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
-                <button type="submit" class="btn btn-danger btn-sm" id="del-dsk-btn">确定删除</button>
+                <button type="submit" class="btn btn-danger btn-sm" id="del-dsk-btn">确定解绑</button>
             </div>
         </div>
     </div>

+ 1 - 0
app/view/profile/sms.ejs

@@ -116,6 +116,7 @@
                                                 </div>
                                             </div>
                                             <button class="btn btn-sm btn-primary" id="dsk-bind-btn">确认绑定</button>
+                                            <a class="btn btn-sm btn-outline-primary ml-2" href="//dsk.smartcost.com.cn/register" target="_blank">注册大司空账号</a>
                                         </div>
                                         <div class="form-group show-dsk-account" <% if (!accountData.dsk_account) { %>style="display: none"<% } %>>
                                             <label><%- accountData.dsk_account ? accountData.dsk_account.mobile : '' %></label>

+ 1 - 1
app/view/tender/manage_modal.ejs

@@ -15,7 +15,7 @@
                     <select class="form-control form-control-sm" name="spid">
                         <option value="">无</option>
                         <% for (const sp of subProject) { %>
-                        <option name="<%- sp.id %>"><%- sp.name %></option>
+                        <option value="<%- sp.id %>"><%- sp.name %></option>
                         <% } %>
                     </select>
                 </div>

+ 2 - 59
sql/update.sql

@@ -1,59 +1,2 @@
-CREATE TABLE `zh_shenpi_group`  (
-  `id` int NOT NULL AUTO_INCREMENT,
-  `tid` int NULL COMMENT '标段id',
-  `sp_type` tinyint(4) NULL COMMENT '审批流程类型',
-  `name` varchar(255) NULL COMMENT '审批组名称',
-  `is_select` tinyint(1) NULL DEFAULT 0 COMMENT '默认审批组',
-  `change_type` json NULL COMMENT '变更模块json',
-  `create_time` datetime NULL COMMENT '创建时间',
-  PRIMARY KEY (`id`)
-) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '审批流程组表';
-
-ALTER TABLE `zh_shenpi_audit`
-ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '审批组id' AFTER `audit_order`;
-
-ALTER TABLE `zh_change`
-ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `delimit`;
-
-ALTER TABLE `zh_change_project_audit`
-ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
-ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
-
-ALTER TABLE `zh_change_project`
-ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `content`;
-
-ALTER TABLE `zh_change_apply_audit`
-ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
-ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
-
-ALTER TABLE `zh_change_apply`
-ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
-
-ALTER TABLE `zh_change_plan_audit`
-ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
-ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
-
-ALTER TABLE `zh_change_plan`
-ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
-
-ALTER TABLE `zh_stage`
-ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关' AFTER `pre_negative_qc_tp`;
-
-ALTER TABLE `zh_global_log`
-ADD COLUMN `memo` varchar(255) NOT NULL DEFAULT '' AFTER `run_time`;
-
-ALTER TABLE `zh_change`
-ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
-ALTER TABLE `zh_change_apply`
-ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
-ALTER TABLE `zh_change_plan`
-ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
-ALTER TABLE `zh_change_project`
-ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
-
-ALTER TABLE `zh_project_account`
-ADD COLUMN `dsk_account` json NULL COMMENT '大司空账号信息' AFTER `notice_again`,
-ADD COLUMN `dsk_projects` json NULL COMMENT '大司空项目信息' AFTER `dsk_account`;
-
-ALTER TABLE `zh_tender`
-ADD COLUMN `spid` varchar(36) NOT NULL DEFAULT '' COMMENT 'zh_sub_project.id' AFTER `s_type`;
+ALTER TABLE `zh_ledger_tag`
+ADD COLUMN `pos_id` varchar(36) NOT NULL DEFAULT '' COMMENT '关联计量单元id(zh_pos表中的id)' AFTER `id`;

+ 71 - 0
sql/update20240529.sql

@@ -0,0 +1,71 @@
+CREATE TABLE `zh_shenpi_group`  (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `tid` int NULL COMMENT '标段id',
+  `sp_type` tinyint(4) NULL COMMENT '审批流程类型',
+  `name` varchar(255) NULL COMMENT '审批组名称',
+  `is_select` tinyint(1) NULL DEFAULT 0 COMMENT '默认审批组',
+  `change_type` json NULL COMMENT '变更模块json',
+  `create_time` datetime NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '审批流程组表';
+
+ALTER TABLE `zh_shenpi_audit`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '审批组id' AFTER `audit_order`;
+
+ALTER TABLE `zh_change`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `delimit`;
+
+ALTER TABLE `zh_change_project_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_project`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `content`;
+
+ALTER TABLE `zh_change_apply_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_apply`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
+
+ALTER TABLE `zh_change_plan_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_plan`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
+
+ALTER TABLE `zh_stage`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关' AFTER `pre_negative_qc_tp`;
+
+ALTER TABLE `zh_global_log`
+ADD COLUMN `memo` varchar(255) NOT NULL DEFAULT '' AFTER `run_time`;
+
+ALTER TABLE `zh_change`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
+ALTER TABLE `zh_change_apply`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
+ALTER TABLE `zh_change_plan`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
+ALTER TABLE `zh_change_project`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
+
+ALTER TABLE `zh_project_account`
+ADD COLUMN `dsk_account` json NULL COMMENT '大司空账号信息' AFTER `notice_again`,
+ADD COLUMN `dsk_projects` json NULL COMMENT '大司空项目信息' AFTER `dsk_account`;
+
+ALTER TABLE `zh_tender`
+ADD COLUMN `spid` varchar(36) NOT NULL DEFAULT '' COMMENT 'zh_sub_project.id' AFTER `settle_order`;
+
+CREATE TABLE `zh_expr`  (
+  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `tid` int(11) UNSIGNED NOT NULL COMMENT '标段id',
+  `calc_module` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'stage' COMMENT '计算模块',
+  `calc_tag` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'contract' COMMENT '计算标记',
+  `calc_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '计算关联id',
+  `expr` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '计算公式',
+  `expr_bak` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '上一次计算公式',
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `idx_common`(`tid`, `calc_module`, `calc_tag`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;