Explorar el Código

Merge branch 'uat'

MaiXinRong hace 3 años
padre
commit
39abe059be
Se han modificado 100 ficheros con 5658 adiciones y 1660 borrados
  1. 37 2
      app/const/account_permission.js
  2. 0 18
      app/const/external_data.js
  3. 29 0
      app/const/fun_set.js
  4. 1 0
      app/const/material.js
  5. 3 0
      app/const/page_show.js
  6. 17 2
      app/const/spread.js
  7. 2 2
      app/const/tender.js
  8. 19 0
      app/const/tender_info.js
  9. 3 3
      app/controller/change_controller.js
  10. 1 1
      app/controller/datacollect_controller.js
  11. 3 31
      app/controller/ledger_audit_controller.js
  12. 7 46
      app/controller/ledger_controller.js
  13. 117 38
      app/controller/material_controller.js
  14. 36 0
      app/controller/measure_controller.js
  15. 28 33
      app/controller/report_controller.js
  16. 38 42
      app/controller/revise_controller.js
  17. 9 0
      app/controller/schedule_controller.js
  18. 15 1
      app/controller/setting_controller.js
  19. 109 84
      app/controller/stage_controller.js
  20. 8 6
      app/controller/stage_rela_controller.js
  21. 7 1
      app/controller/standard_lib_controller.js
  22. 29 10
      app/controller/tender_controller.js
  23. 13 9
      app/controller/wap_controller.js
  24. 2 2
      app/extend/helper.js
  25. 5 0
      app/lib/analysis_excel.js
  26. 21 6
      app/lib/bills_pos_convert.js
  27. 3 1
      app/lib/budget_final.js
  28. 80 1
      app/lib/ledger.js
  29. 71 0
      app/lib/pm.js
  30. 345 0
      app/lib/revise_price.js
  31. 140 58
      app/lib/rm/material.js
  32. 37 4
      app/lib/spread_setting.js
  33. 33 18
      app/lib/stage_im.js
  34. 5 3
      app/lib/tender_info.js
  35. 3 1
      app/middleware/material_check.js
  36. 1 0
      app/middleware/revise_check.js
  37. 1 0
      app/middleware/session_auth.js
  38. 49 21
      app/public/css/main.css
  39. 50 9
      app/public/js/change_information.js
  40. 1 2
      app/public/js/change_information_set.js
  41. 1 1
      app/public/js/change_revise.js
  42. 2 1
      app/public/js/component/menu.js
  43. 28 13
      app/public/js/gcl_gather.js
  44. 36 2
      app/public/js/global.js
  45. 25 10
      app/public/js/ledger.js
  46. 520 214
      app/public/js/material.js
  47. 8 0
      app/public/js/material_audit.js
  48. 116 4
      app/public/js/material_checklist.js
  49. 57 38
      app/public/js/material_exponent.js
  50. 858 492
      app/public/js/material_list.js
  51. 161 15
      app/public/js/measure_compare.js
  52. 175 74
      app/public/js/measure_material.js
  53. 83 1
      app/public/js/path_tree.js
  54. 5 4
      app/public/js/revise.js
  55. 3 2
      app/public/js/revise_compare.js
  56. 2 2
      app/public/js/revise_gcl_compare.js
  57. 483 0
      app/public/js/revise_price.js
  58. 8 7
      app/public/js/schedule_stage_tp.js
  59. 6 0
      app/public/js/setting.js
  60. 93 0
      app/public/js/shares/cs_gcl_gather.js
  61. 184 2
      app/public/js/shares/cs_tools.js
  62. 18 1
      app/public/js/shares/gcl_gather_compare.js
  63. 73 0
      app/public/js/shares/show_level.js
  64. 13 1
      app/public/js/shares/sjs_setting.js
  65. 132 0
      app/public/js/spreadjs_rela/spreadjs_zh.js
  66. 29 7
      app/public/js/sr_detail.js
  67. 174 24
      app/public/js/stage.js
  68. 0 3
      app/public/js/stage_bwtz.js
  69. 17 1
      app/public/js/stage_compare.js
  70. 10 7
      app/public/js/stage_gather.js
  71. 29 20
      app/public/js/stage_im.js
  72. 11 1
      app/public/js/stage_pay.js
  73. 37 1
      app/public/js/sub_menu.js
  74. 9 0
      app/public/js/tender.js
  75. 11 2
      app/public/js/tender_list.js
  76. 5 3
      app/public/js/tender_list_info.js
  77. 2 0
      app/public/js/tender_list_manage.js
  78. 4 2
      app/public/js/tender_list_progress.js
  79. 80 2
      app/public/js/tender_showhide.js
  80. 8 0
      app/public/jspdf/SmartSimsun-normal2.js
  81. 8 1
      app/public/report/js/rpt_custom.js
  82. 4 2
      app/public/report/js/rpt_jspdf.js
  83. 60 9
      app/public/report/js/rpt_main.js
  84. 20 7
      app/public/report/js/rpt_other_stage.js
  85. 6 4
      app/public/report/js/rpt_signature.js
  86. 8 2
      app/router.js
  87. 1 0
      app/service/category_value.js
  88. 2 2
      app/service/change.js
  89. 11 1
      app/service/change_audit_list.js
  90. 38 0
      app/service/glj_lib.js
  91. 8 42
      app/service/ledger.js
  92. 29 47
      app/service/ledger_history.js
  93. 27 9
      app/service/ledger_revise.js
  94. 4 7
      app/service/ledger_tag.js
  95. 35 4
      app/service/material.js
  96. 0 16
      app/service/material_audit.js
  97. 378 68
      app/service/material_bills.js
  98. 104 21
      app/service/material_list.js
  99. 21 6
      app/service/material_list_notjoin.js
  100. 0 0
      app/service/material_list_self.js

+ 37 - 2
app/const/account_permission.js

@@ -29,8 +29,28 @@ const permission = {
             { title: '创建标段', value: 1 },
             { title: '查阅所有标段', value: 2 },
             { title: '维护签约清单', value: 3, hint: '开启该选项,台账审批通过后,可上传签约清单', hintIcon: 'fa-question-circle' },
-            { title: '变更意向', value: 5, hint: '开启该选项,变更立项可新建变更意向书', hintIcon: 'fa-question-circle' },
-            { title: '材差清单设置', value: 4, hint: '开启该选项,当前账号可设置允许调差的清单', hintIcon: 'fa-question-circle' },
+            { title: '批量设置材差清单', value: 4, show: false, hint: '开启该选项,当前账号可设置允许调差的清单', hintIcon: 'fa-question-circle' },
+            { title: '变更意向', value: 5, show: false, hint: '开启该选项,变更立项可新建变更意向书', hintIcon: 'fa-question-circle' },
+            { title: '查看项目管理报表数据', value: 6, hint: '开启该选项,当前账号在报表下可查看项目管理数据', hintIcon: 'fa-question-circle' },
+        ],
+        tips: '勾选「创建标段」该用户默认具有「新建标段」及标段内「台账分解」「创建台账修订」「创建计量期」「创建工程变更」的权限。',
+    },
+    change: {
+        class: 'fa fa-retweet',
+        title: '工程变更',
+        type: 'checkbox',
+        children: [
+            { title: '变更意向', value: 1, hint: '开启该选项,变更立项可新建变更意向书', hintIcon: 'fa-question-circle' },
+        ],
+    },
+    material: {
+        class: 'fa fa-line-chart fa-fw',
+        title: '材料调差',
+        type: 'checkbox',
+        children: [
+            { title: '批量设置材差清单', value: 1, hint: '开启该选项,当前账号可设置允许调差的清单', hintIcon: 'fa-question-circle' },
+            { title: '修改调差工料消耗量', value: 2, hint: '开启该选项,可在新材差期修改工料的消耗量', hintIcon: 'fa-question-circle' },
+            // { title: '修改材料税税率', value: 3, hint: '开启该选项,可在新材差期修改材料税税率', hintIcon: 'fa-question-circle' },
         ],
     },
     // cooperation: {
@@ -41,6 +61,7 @@ const permission = {
     //         { title: '启用', value: 1 },
     //         { title: '关闭', value: 0 },
     //     ],
+    //     tips: '启用「协作办公」,则该用户可以为他创建的标段添加其他用户进行协作办公。',
     // },
     project_msg: {
         class: '',
@@ -53,8 +74,22 @@ const permission = {
     },
 };
 
+const PermissionCheck = {
+    check: function (permission, key) {
+        if (!permission) return false;
+        switch (key) {
+            case 'viewPmData': return permission.tender.indexOf('6') >= 0;
+            default: return false;
+        }
+    },
+    viewPmData: function (permission) {
+        return this.check(permission, 'viewPmData');
+    },
+};
+
 module.exports = {
     tenderPermission: create_tender,
     tenderPermissionList: create_tender_group,
     permission,
+    PermissionCheck,
 };

+ 0 - 18
app/const/external_data.js

@@ -1,18 +0,0 @@
-'use strict';
-
-/**
- *
- *
- * @author Mai
- * @date
- * @version
- */
-
-const Tag = {
-    FuLong: {
-        exType: 'fulong',
-        exFields: { wbsCode: 'wbs-code' }
-    }
-};
-
-module.exports = Tag;

+ 29 - 0
app/const/fun_set.js

@@ -0,0 +1,29 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const endMonth = [
+    { val: 1, name: '本月' },
+    { val: 2, name: '次月' },
+    // { val: 3, name: '隔月' },
+];
+const parseInfo = ['stage_start'];
+const defaultInfo = {
+    // 合同信息
+    stage_start: {
+        start_day: 0,
+        end_month: 0,
+        end_day: 0,
+    },
+};
+
+module.exports = {
+    parseInfo,
+    endMonth,
+    defaultInfo,
+};

+ 1 - 0
app/const/material.js

@@ -21,6 +21,7 @@ const m_type = [
     { text: '油料', value: 4 },
     { text: '水泥', value: 5 },
     { text: '半成品', value: 6 },
+    { text: '砂石料', value: 8 },
     { text: '其他', value: 7 },
 ];
 // 指数调差类型

+ 3 - 0
app/const/page_show.js

@@ -39,12 +39,15 @@ const defaultSetting = {
     closeWapYfSf: 0,
     openManagement: 0,
     openMaterialChecklist: 0,
+    openMaterialSelf: 0,
+    openMaterialEditForAudit: 0,
     close1stStageCheckDealParam: 0,
     openChangeProject: 0,
     openChangeApply: 0,
     openChangePlan: 0,
     isPreset: 0,
     isOnlyChecked: 1,
+    openStageStart: 0,
 };
 
 

+ 17 - 2
app/const/spread.js

@@ -8,11 +8,12 @@
  * @version
  */
 
-const tzWithoutCols = ['deal_qty', 'deal_tp'];
+const dealCols = ['deal_qty', 'deal_tp'];
 const dgnCols = ['dgn_qty1', 'dgn_qty2', 'dgn_price'];
 const clCols = ['sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'];
 const stageDgnCols = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2', 'final_dgn_price'];
 const realCompleteCols = ['real_qty', 'estimate_qty'];
+const priceDiffCols = ['org_price', 'pc_tp'];
 const thirdPartyCols = {
     gxby: ['gxby'],
     dagl: ['dagl']
@@ -31,6 +32,8 @@ const withCl = {
             {title: '项目节数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {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},
             {title: '设计错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
@@ -85,6 +88,8 @@ const withoutCl = {
             {title: '项目节数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {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},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
@@ -199,6 +204,8 @@ const stageTz = {
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', readOnly: true },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -280,6 +287,8 @@ const stageCl = {
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number',},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', readOnly: true },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -361,6 +370,8 @@ const stageNoCl = {
             {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number',},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', readOnly: true },
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
@@ -440,6 +451,8 @@ const stageGather = {
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
@@ -499,6 +512,8 @@ const stageCompare = {
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
         ],
         extraCols: [
             {title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty%s', hAlign: 2, width: 60, type: 'Number'},
@@ -599,7 +614,7 @@ module.exports = {
     stageNoCl,
     stageGather,
     stageCompare,
-    filterCols: { tzWithoutCols, dgnCols, clCols, stageDgnCols, realCompleteCols, thirdPartyCols, minusNoValueCols},
+    filterCols: { dealCols, dgnCols, clCols, stageDgnCols, realCompleteCols, thirdPartyCols, minusNoValueCols, priceDiffCols },
     measure,
     blank,
 };

+ 2 - 2
app/const/tender.js

@@ -59,9 +59,9 @@ const measureType = {
 const valuationField = function (mt) {
     switch (mt) {
         case measureType.tz.value:
-            return { std_bills: 'bill_id', std_xmj: 'chapter_id', template: 'template_id' };
+            return { std_bills: 'bill_id', std_xmj: 'chapter_id', template: 'template_id', glj_lib: 'glj_lib_id' };
         case measureType.gcl.value:
-            return { std_bills: 'list_bill_id', std_xmj: 'list_chapter_id', template: 'list_template_id' };
+            return { std_bills: 'list_bill_id', std_xmj: 'list_chapter_id', template: 'list_template_id', glj_lib: 'list_glj_lib_id' };
         default:
             return null;
     }

+ 19 - 0
app/const/tender_info.js

@@ -47,6 +47,11 @@ const defaultInfo = {
             corporation: '',
             date: '',
         },
+        detect: {
+            company: '',
+            corporation: '',
+            date: '',
+        },
     },
     // 技术参数
     tech_param: {
@@ -106,13 +111,16 @@ const defaultInfo = {
     // 显示设置
     display: {
         ledger: {
+            deal: false,
             dgnQty: false,
             clQty: false,
         },
+        exMemo: true,
         thousandth: false,
         stage: {
             realComplete: false,
             correct: true,
+            priceDiff: false,
         },
         dayMode: false,
     },
@@ -177,9 +185,20 @@ const defaultInfo = {
         },
     },
 };
+const gclDefaultInfo = (function () {
+    const result = JSON.parse(JSON.stringify(defaultInfo));
+    result.display.ledger.deal = true;
+    return result;
+})(defaultInfo);
+const tzDefaultInfo = (function () {
+    const result = JSON.parse(JSON.stringify(defaultInfo));
+    return result;
+})(defaultInfo);
 
 module.exports = {
     parseInfo,
     arrayInfo,
     defaultInfo,
+    gclDefaultInfo,
+    tzDefaultInfo,
 };

+ 3 - 3
app/controller/change_controller.js

@@ -854,7 +854,7 @@ module.exports = app => {
                         responseData.data = changeList;
                         break;
                     case 'update_tp':
-                        await ctx.service.change.saveInfo({ total_price: data.updateData });
+                        await ctx.service.change.saveInfo(data.updateData);
                         break;
                     case 'order_by':
                         const result = await ctx.service.change.saveOrderBy(data.updateData, data.newLedgerList);
@@ -1617,7 +1617,7 @@ module.exports = app => {
 
         async _getDefaultReviseInfoData(ctx, change, edit) {
             const [ledgerSpread, posSpread] = this._getSpreadSetting(change, edit);
-            const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+            const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
             this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
             const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(
                 ctx.tender.data.valuation, ctx.tender.data.measure_type);
@@ -1661,7 +1661,7 @@ module.exports = app => {
                 pos.readOnly = true;
             }
             if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
+                removeFieldCols(ledger, spreadConst.filterCols.dealCols);
             }
             if (!tender.info.display.ledger.dgnQty) {
                 removeFieldCols(ledger, spreadConst.filterCols.dgnCols);

+ 1 - 1
app/controller/datacollect_controller.js

@@ -126,7 +126,7 @@ module.exports = app => {
                         t.total_price = sum.total_price;
                         t.deal_tp = sum.deal_tp;
                     }
-                    t.change_tp = await ctx.service.change.getChangeTp(t.id);
+                    [t.change_tp, t.change_p_tp, t.change_n_tp] = await ctx.service.change.getChangeTp(t.id);
                     // t.material_tp = await ctx.service.material.getSumMaterial(t.id);
                     // 获取本标段 本月计量期审批通过数目,变更令审批通过数目,台账修订通过数目,材料调差通过数目
                     t.month_stage_num = await ctx.service.stageAudit.getNumByMonth(t.id, startMonth, endMonth);

+ 3 - 31
app/controller/ledger_audit_controller.js

@@ -8,9 +8,9 @@
  * @version
  */
 const auditConst = require('../const/audit').ledger;
-const spreadConst = require('../const/spread');
 const shenpiConst = require('../const/shenpi');
 const measureType = require('../const/tender').measureType;
+const spreadSetting = require('../lib/spread_setting');
 
 module.exports = app => {
     class LedgerAuditController extends app.BaseController {
@@ -26,34 +26,6 @@ module.exports = app => {
         }
 
         /**
-         * 获取SpreadSetting
-         * @private
-         */
-        _getSpreadSetting() {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            const tender = this.ctx.tender;
-            const setting = tender.data.measure_type === measureType.tz.value
-                ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
-                : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
-            const ledger = JSON.parse(JSON.stringify(setting.ledger));
-            const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
-            ledger.readOnly = true;
-            pos.readOnly = true;
-            if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
-            }
-            if (!tender.info.display.ledger.dgnQty) {
-                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
-            }
-            return [ledger, pos];
-        }
-
-        /**
          * 台账审批页面(get)
          *
          * @param ctx
@@ -68,8 +40,8 @@ module.exports = app => {
                     preUrl: '/tender/' + ctx.tender.id,
                     measureType,
                 };
-                const [ledgerSpread, posSpread] = this._getSpreadSetting();
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, ctx.tender.id, true);
+                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
 
                 const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(ctx.tender.id, ctx.tender.data.ledger_times);

+ 7 - 46
app/controller/ledger_controller.js

@@ -18,9 +18,7 @@ const moment = require('moment');
 const auditConst = audit.ledger;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
-const spreadConst = require('../const/spread');
 const shenpiConst = require('../const/shenpi');
-const externalDataConst = require('../const/external_data.js');
 const fs = require('fs');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
@@ -30,6 +28,7 @@ const billsPosConvert = require('../lib/bills_pos_convert');
 const xlsx = require('js-xlsx');
 const stdConst = require('../const/standard');
 const sendToWormhole = require('stream-wormhole');
+const spreadSetting = require('../lib/spread_setting');
 
 module.exports = app => {
 
@@ -72,52 +71,13 @@ module.exports = app => {
                 (tender.ledger_status === auditConst.status.checked && isAuditor && upPermission);
         }
 
-        /**
-         * 获取SpreadSetting
-         * @private
-         */
-        _getSpreadSetting() {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            function hideFieldCols(setting, cols) {
-                for (const c of setting.cols) {
-                    if (cols.indexOf(c.field) > -1) {
-                        c.visible = false;
-                    }
-                }
-            }
-            const tender = this.ctx.tender;
-            const setting = tender.data.measure_type === measureType.tz.value
-                ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
-                : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
-            const ledger = JSON.parse(JSON.stringify(setting.ledger));
-            const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
-
-            if (this._ledgerReadOnly(tender.data)) {
-                ledger.readOnly = true;
-                pos.readOnly = true;
-            }
-            if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
-            }
-            if (!tender.info.display.ledger.dgnQty) {
-                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
-            }
-
-            return [ledger, pos];
-        }
-
         _getLedgerColumn(sjsRela) {
             const tender = this.ctx.tender;
             const ledgerColumn = [
                 'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
                 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price',
                 'sgfh_qty', 'sgfh_expr', 'sgfh_tp', 'memo', 'drawing_code', 'node_type'];
-            if (tender.data.measure_type === measureType.gcl.value) ledgerColumn.push('deal_qty', 'deal_tp');
+            if (tender.info.display.ledger.deal) ledgerColumn.push('deal_qty', 'deal_tp');
             if (tender.info.display.ledger.dgnQty) ledgerColumn.push('dgn_qty1', 'dgn_qty2');
             if (tender.info.display.ledger.clQty) ledgerColumn.push('sjcl_qty', 'qtcl_qty', 'sjcl_expr', 'qtcl_expr', 'sjcl_tp', 'qtcl_tp');
             const posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'sgfh_qty', 'sgfh_expr', 'add_stage_order', 'drawing_code', 'quantity'];
@@ -140,8 +100,9 @@ module.exports = app => {
         async explode(ctx) {
             try {
                 const tender = ctx.tender;
-                const [ledgerSpread, posSpread] = await this._getSpreadSetting();
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, tender.id,
+                    this._ledgerReadOnly(tender.data));
+                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
                 const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
 
@@ -481,7 +442,7 @@ module.exports = app => {
          */
         async loadExplodeData(ctx) {
             try {
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 const [ledgerColumn, posColumn] = this._getLedgerColumn(sjsRela);
                 const ledgerData = ctx.tender.ledgerReadOnly && ctx.tender.his
                     ? await ctx.helper.loadLedgerDataFromOss(ctx.tender.his.bills_file)
@@ -692,7 +653,7 @@ module.exports = app => {
          */
         async bwtz(ctx) {
             try {
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 const renderData = {
                     tender: ctx.tender.data,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.ledger.bwtz),

+ 117 - 38
app/controller/material_controller.js

@@ -378,6 +378,7 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
+                await this._setEditTaxPermission(ctx);
                 renderData.materialBillsData = await this._getMaterialBillsData(ctx);
                 // 取对应期的截取上期的调差金额和应耗数量
                 if (ctx.material.highOrder !== ctx.material.order) {
@@ -395,8 +396,24 @@ module.exports = app => {
                 // if (!ctx.material.readOnly) {
                 const stage_list = await ctx.service.stage.getStageMsgByStageId(ctx.material.stage_id);
                 renderData.calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
-                // }
 
+                renderData.materialStageData = ctx.material.is_stage_self ? await ctx.service.materialStage.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                renderData.materialStageBillsData = ctx.material.is_stage_self ? await ctx.service.materialStageBills.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                if (ctx.material.is_stage_self) {
+                    const calcBaseList = [];
+                    for (const ms of renderData.materialStageData) {
+                        const stage_info = await ctx.service.stage.getStageMsgByStageId(ms.sid);
+                        calcBaseList.push({
+                            ms_id: ms.id,
+                            calcBase: await ctx.service.stage.getMaterialCalcBase(stage_info, ctx.tender.info),
+                        });
+                    }
+                    renderData.calcBase = calcBaseList;
+                }
+                // }
+                const [stdBills, stdChapters, gljLists] = await this.ctx.service.valuation.getValuationStdList(
+                    ctx.tender.data.valuation, ctx.tender.data.measure_type);
+                renderData.gljLists = gljLists;
                 // 取当前期截止上期含税金额
                 renderData.material.m_tax_tp = renderData.material.m_tax_tp ? renderData.material.m_tax_tp : renderData.material.m_tp;
                 renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
@@ -426,6 +443,7 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
+                await this._setEditListPermission(ctx);
                 // 根据期判断需要获取的工料信息值表
                 const searchsql = { tid: ctx.tender.id };
                 let midList = [];
@@ -448,6 +466,9 @@ module.exports = app => {
                         });
                     }
                 }
+
+                renderData.materialStageData = ctx.material.is_stage_self ? await ctx.service.materialStage.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                renderData.materialStageBillsData = ctx.material.is_stage_self ? await ctx.service.materialStageBills.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
                 // 取所有已被调用的工料清单表
                 // renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
                 // renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
@@ -473,7 +494,7 @@ module.exports = app => {
          */
         async loadListsData(ctx) {
             try {
-                // const data = JSON.parse(ctx.request.body.data);
+                const data = JSON.parse(ctx.request.body.data);
                 // const filter = data.filter.split(';');
                 const responseData = { err: 0, msg: '', data: {} };
                 // 取所有已被调用的工料清单表
@@ -484,8 +505,39 @@ module.exports = app => {
                 responseData.data.ledger = await ctx.service.ledger.getData(ctx.tender.id);
                 responseData.data.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 // 获取所选期数据并合并相加同类清单项
-                responseData.data.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
-                responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id, 'list');
+                if (ctx.material.is_stage_self && data.sid) {
+                    const curLedgerPcData = await ctx.service.stageBillsPc.getStagesData(ctx.tender.id, data.sid.toString());
+                    const curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, data.sid.toString());
+                    for (const cl of curLedgerData) {
+                        const clpc = ctx.helper._.find(curLedgerPcData, { lid: cl.lid });
+                        ctx.helper._.assign(cl, clpc);
+                    }
+                    responseData.data.curLedgerData = curLedgerData;
+                    responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, data.sid.toString(), 'list');
+                    const ledgerListData = [];
+                    const posListData = [];
+                    for (const s of ctx.material.stage_id.split(',')) {
+                        const curLedgerPcData = await ctx.service.stageBillsPc.getStagesData(ctx.tender.id, s.toString());
+                        const curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, s.toString());
+                        for (const cl of curLedgerData) {
+                            const clpc = ctx.helper._.find(curLedgerPcData, { lid: cl.lid });
+                            ctx.helper._.assign(cl, clpc);
+                        }
+                        ledgerListData.push(curLedgerData);
+                        posListData.push(await ctx.service.stagePos.getStagesData(ctx.tender.id, s.toString(), 'list'));
+                    }
+                    responseData.data.ledgerListData = ledgerListData;
+                    responseData.data.posListData = posListData;
+                } else {
+                    const curLedgerPcData = await ctx.service.stageBillsPc.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                    const curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                    for (const cl of curLedgerData) {
+                        const clpc = ctx.helper._.find(curLedgerPcData, { lid: cl.lid });
+                        ctx.helper._.assign(cl, clpc);
+                    }
+                    responseData.data.curLedgerData = curLedgerData;
+                    responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id, 'list');
+                }
                 // 获取清单设置已选清单
                 const materialChecklistData = await ctx.service.materialChecklist.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                 responseData.data.materialChecklistData = materialChecklistData.sort(function(a, b) {
@@ -551,9 +603,20 @@ module.exports = app => {
                 responseData.data.ledger = await ctx.service.ledger.getData(ctx.tender.id);
                 responseData.data.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 // 获取所选期数据并合并相加同类清单项
-                responseData.data.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, data.stage_id.join(','));
-                responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, data.stage_id.join(','), 'list');
-
+                if (data.is_stage_self) {
+                    const stage_id_list = data.stage_id;
+                    const curLedgerData = [];
+                    const curPosData = [];
+                    for (const sid of stage_id_list) {
+                        curLedgerData.push({ sid, ledgerData: await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, sid) });
+                        curPosData.push({ sid, posData: await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, sid) });
+                    }
+                    responseData.data.curLedgerData = curLedgerData;
+                    responseData.data.curPosData = curPosData;
+                } else {
+                    responseData.data.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, data.stage_id.join(','));
+                    responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, data.stage_id.join(','), 'list');
+                }
                 // 获取gclidlist值
                 const lastMaterial = await ctx.service.material.getLastestCompleteMaterial(ctx.tender.id);
                 // responseData.data.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
@@ -632,7 +695,7 @@ module.exports = app => {
                     }
                     renderData.ex_calc = ex_calc;
                 }
-
+                renderData.materialBillsData = await this._getMaterialBillsData(ctx);
                 renderData.materialExponentData = await this._getMaterialExponentData(ctx);
                 // 取对应期的截取上期的调差金额和应耗数量
                 if (ctx.material.highOrder !== ctx.material.order) {
@@ -667,6 +730,7 @@ module.exports = app => {
             try {
                 await this._getMaterialAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
+                renderData.materialBillsData = await this._getMaterialBillsData(ctx);
                 const whiteList = this.ctx.app.config.multipart.whitelist;
                 // 获取当前标段所有附件
                 // const searchsql = { tid: ctx.tender.id };
@@ -697,16 +761,20 @@ module.exports = app => {
                 }
                 const newDecimalUp = parseInt(data.up);
                 const newDecimalTp = parseInt(data.tp);
+                const newDecimalQty = parseInt(data.qty);
                 if (ctx.app._.isNaN(newDecimalUp) || newDecimalUp > 6 || newDecimalUp < 0) {
                     throw '单价小数位数设置不能大于6或小于0';
                 }
                 if (ctx.app._.isNaN(newDecimalTp) || newDecimalTp > 6 || newDecimalTp < 0) {
                     throw '金额小数位数设置不能大于6或小于0';
                 }
-                if (ctx.material.decimal.up === newDecimalUp && ctx.material.decimal.tp === newDecimalTp) {
+                if (ctx.app._.isNaN(newDecimalQty) || newDecimalQty > 6 || newDecimalQty < 0) {
+                    throw '数量小数位数设置不能大于6或小于0';
+                }
+                if (ctx.material.decimal.up === newDecimalUp && ctx.material.decimal.tp === newDecimalTp && ctx.material.decimal.qty === newDecimalQty) {
                     throw '小数位数设置未发生变化';
                 }
-                const result = await ctx.service.material.saveDecimal(newDecimalUp, newDecimalTp);
+                const result = await ctx.service.material.saveDecimal(newDecimalUp, newDecimalTp, newDecimalQty);
                 if (!result) {
                     throw '小数位数设置失败';
                 }
@@ -736,12 +804,16 @@ module.exports = app => {
                 if (ctx.session.sessionProject.page_show.openMaterialChecklist && ctx.app._.indexOf(notControlList, data.type) === -1) {
                     throw '清单设置功能已启动,请前往清单设置页操作清单内容';
                 }
+                const selfControlList = ['self', 'noself', 'add', 'del', 'update', 'paste'];
+                if (!ctx.session.sessionProject.page_show.openMaterialSelf && ctx.app._.indexOf(selfControlList, data.type) !== -1) {
+                    throw '单独设置工料功能已关闭,无法设置';
+                }
                 switch (data.type) {
                     case 'add':
-                        responseData.data = await ctx.service.materialList.add(data.postData);
+                        responseData.data = await ctx.service.materialList.add(data.postData, data.ms_id);
                         break;
                     case 'del':
-                        await ctx.service.materialList.del(data.id, data.mb_id);
+                        await ctx.service.materialList.del(data.id, data.mb_id, data.ms_id);
                         break;
                     case 'update':
                         if (data.updateData.quantity === '' || data.updateData.quantity === null) {
@@ -751,25 +823,25 @@ module.exports = app => {
                         if (isNaN(data.updateData.quantity)) {
                             throw '不能输入其它非数字类型字符';
                         }
-                        await ctx.service.materialList.save(data.updateData);
+                        await ctx.service.materialList.save(data.updateData, data.ms_id);
                         break;
                     case 'useOther':
                         responseData.data = await ctx.service.materialList.addOther(data.postData);
                         break;
                     case 'join':
-                        await ctx.service.materialListNotjoin.del(data.select.id);
+                        await ctx.service.materialListNotjoin.del(data.select.id, data.ms_id);
                         break;
                     case 'notjoin':
-                        responseData.data = await ctx.service.materialListNotjoin.add(data.select);
+                        responseData.data = await ctx.service.materialListNotjoin.add(data.select, data.ms_id);
                         break;
                     case 'self':
                         responseData.data = await ctx.service.materialListSelf.add(data.select);
                         break;
                     case 'noself':
-                        responseData.data = await ctx.service.materialListSelf.del(data.select.id);
+                        responseData.data = await ctx.service.materialListSelf.del(data.select.id, data.ms_id, data.select.gather_qty);
                         break;
                     case 'paste':
-                        await ctx.service.materialList.saveDatas(data.updateData);
+                        await ctx.service.materialList.saveDatas(data.updateData, data.ms_id);
                         // 取所有工料表
                         responseData.data = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
                         break;
@@ -777,7 +849,7 @@ module.exports = app => {
                         responseData.data = await ctx.service.materialList.adds(data.postData);
                         break;
                     case 'dels':
-                        responseData.data = await ctx.service.materialList.dels(data.postData);
+                        responseData.data = await ctx.service.materialList.dels(data.postData, false, false, data.ms_id);
                         break;
                     case 'updates':
                         if (data.updateData.quantity === '' || data.updateData.quantity === null) {
@@ -787,15 +859,14 @@ module.exports = app => {
                         if (isNaN(data.updateData.quantity)) {
                             throw '不能输入其它非数字类型字符';
                         }
-                        responseData.data = await ctx.service.materialList.saves(data.updateData);
+                        responseData.data = await ctx.service.materialList.saves(data.updateData, false, data.ms_id);
                         break;
                     case 'pastes':
-                        responseData.data = await ctx.service.materialList.savePastes(data.updateData);
+                        responseData.data = await ctx.service.materialList.savePastes(data.updateData, false, data.ms_id);
                         // 取所有工料表
                         break;
                     default: throw '参数有误';
                 }
-
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -821,10 +892,10 @@ module.exports = app => {
                         responseData.data = await ctx.service.materialBills.add();
                         break;
                     case 'del':
-                        responseData.data.m_tp = await ctx.service.materialBills.del(data.id);
+                        responseData.data = await ctx.service.materialBills.del(data.id);
                         break;
                     case 'changeOrder':
-                        await ctx.service.materialBills.changeOrder(data.id1, data.id2);
+                        await ctx.service.materialBills.changeOrder(data.postData);
                         break;
                     case 'update':
                         if (data.updateData.code === '' || data.updateData.code === null) {
@@ -842,7 +913,7 @@ module.exports = app => {
                         if (billData.length > 1 || (billData.length > 0 && billData[0].id !== data.updateData.id)) {
                             throw '该编号已存在,请重新输入。';
                         }
-                        responseData.data.m_tp = await ctx.service.materialBills.save(data.updateData);
+                        responseData.data = await ctx.service.materialBills.save(data.updateData, data.ms_id);
                         break;
                     case 'rate':
                         // 判断数量是否为数字
@@ -855,18 +926,10 @@ module.exports = app => {
                         await ctx.service.material.changeRate(data.rate);
                         break;
                     case 'expr':
-                        const materialCalculator = new MaterialCalculator(ctx, ctx.material.stage_id, ctx.tender.info);
-                        const quantity = await materialCalculator.calculateExpr(data.expr);
-                        // 更新quantity值并重新返回计算本期金额,截止本期金额
-                        const updateData = {
-                            id: data.id,
-                            quantity: quantity !== 0 ? ctx.helper.round(quantity, ctx.material.decimal.qty) : null,
-                            expr: data.expr,
-                        };
-                        responseData.data = await ctx.service.materialBills.updateFYQuantity(updateData);
+                        responseData.data = await ctx.service.materialBills.updateFYQuantity(data);
                         break;
                     case 'paste':
-                        responseData.data.m_tp = await ctx.service.materialBills.saveDatas(data.updateData);
+                        responseData.data = await ctx.service.materialBills.saveDatas(data.updateData, data.ms_id);
                         // 根据期判断需要获取的工料信息值
                         // const searchsql = { tid: ctx.tender.id };
                         // if (ctx.material.highOrder !== ctx.material.order) {
@@ -877,6 +940,9 @@ module.exports = app => {
                         // responseData.data.info = await ctx.service.materialBills.getAllDataByCondition({ where: searchsql, orders: [['order', 'asc']] });
                         responseData.data.info = await this._getMaterialBillsData(ctx);
                         break;
+                    case 'add-glj':
+                        responseData.data = await ctx.service.materialBills.addByGlj(data.postData, data.order);
+                        break;
                     default: throw '参数有误';
                 }
                 if (ctx.material.material_tax) {
@@ -1384,12 +1450,22 @@ module.exports = app => {
             }
         }
 
+        async _setEditListPermission(ctx) {
+            const permission = ctx.session.sessionUser.permission;
+            ctx.material.editListPermission = permission && permission.material !== undefined && permission.material.indexOf('2') !== -1;
+        }
+
+        async _setEditTaxPermission(ctx) {
+            const permission = ctx.session.sessionUser.permission;
+            ctx.material.editTaxPermission = permission && permission.material !== undefined && permission.material.indexOf('3') !== -1;
+        }
+
         async _setChecklistPermission(ctx) {
             // 清单设置权限判断
             ctx.material.checklistPermission = false;
             if (ctx.session.sessionProject.page_show.openMaterialChecklist && ctx.material.highOrder === ctx.material.order && ctx.material.status !== auditConst.status.checked) {
                 const permission = ctx.session.sessionUser.permission;
-                if ((permission && permission.tender !== undefined && permission.tender.indexOf('4') !== -1) || (ctx.material.order === 1 && ctx.session.sessionUser.accountId === ctx.material.user_id && (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo))) {
+                if ((permission && ((permission.tender !== undefined && permission.tender.indexOf('4') !== -1) || (permission.material !== undefined && permission.material.indexOf('1') !== -1))) || (ctx.material.order === 1 && ctx.session.sessionUser.accountId === ctx.material.user_id && (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo))) {
                     ctx.material.checklistPermission = true;
                 }
             }
@@ -1427,6 +1503,9 @@ module.exports = app => {
                         });
                     }
                 }
+
+                renderData.materialStageData = ctx.material.is_stage_self ? await ctx.service.materialStage.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
+                renderData.materialStageBillsData = ctx.material.is_stage_self ? await ctx.service.materialStageBills.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } }) : [];
                 // 取所有已被调用的工料清单表
                 // renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
                 // renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
@@ -1471,7 +1550,7 @@ module.exports = app => {
                         responseData.data = await ctx.service.materialList.adds(data.postData, data.checklist);
                         break;
                     case 'dels':
-                        responseData.data = await ctx.service.materialList.dels(data.postData, data.checklist, true);
+                        responseData.data = await ctx.service.materialList.dels(data.postData, data.checklist, true, data.ms_id);
                         break;
                     case 'updates':
                         if (data.updateData.quantity === '' || data.updateData.quantity === null) {
@@ -1481,10 +1560,10 @@ module.exports = app => {
                         if (isNaN(data.updateData.quantity)) {
                             throw '不能输入其它非数字类型字符';
                         }
-                        responseData.data = await ctx.service.materialList.saves(data.updateData, true);
+                        responseData.data = await ctx.service.materialList.saves(data.updateData, true, data.ms_id);
                         break;
                     case 'pastes':
-                        responseData.data = await ctx.service.materialList.savePastes(data.updateData, true);
+                        responseData.data = await ctx.service.materialList.savePastes(data.updateData, true, data.ms_id);
                         // 取所有工料表
                         break;
                     case 'resetChecklist':

+ 36 - 0
app/controller/measure_controller.js

@@ -46,6 +46,7 @@ module.exports = app => {
                     auditConst2: JSON.stringify(auditConst),
                 };
                 renderData.stages = await ctx.service.stage.getValidStages(ctx.tender.id);
+                if (renderData.stages.length > 0) await this.ctx.service.stage.checkStageGatherData(renderData.stages[0], this.ctx.session.sessionUser.is_admin);
                 for (const s of renderData.stages) {
                     // s.curAuditor = null;
                     // 根据期状态返回展示用户
@@ -199,6 +200,37 @@ module.exports = app => {
             }
         }
 
+        joinData(main, idField, rela) {
+            const index = {},
+                indexPre = 'id_';
+            const loadFields = function(datas, fields, prefix, relaId) {
+                for (const d of datas) {
+                    const key = indexPre + d[relaId];
+                    let m = index[key];
+                    if (!m) {
+                        m = {};
+                        m[idField] = d[relaId];
+                        main.push(m);
+                        index[indexPre + d[relaId]] = m;
+                    }
+                    for (const f of fields) {
+                        if (d[f] !== undefined) {
+                            m[prefix + f] = d[f];
+                        }
+                    }
+                }
+            };
+            for (const m of main) {
+                index[indexPre + m[idField]] = m;
+                for (const r of rela) {
+                    if (r.defaultData) _.assignIn(m, r.defaultData);
+                }
+            }
+            for (const r of rela) {
+                loadFields(r.data, r.fields, r.prefix, r.relaId);
+            }
+        }
+
         /**
          * 多期比较 - 获取数据(Ajax)
          * @param ctx
@@ -221,6 +253,10 @@ module.exports = app => {
                         const data = { order, bills: [], pos: [] };
                         const stage = await this.ctx.service.stage.getDataByCondition({ tid: ctx.tender.id, order });
                         data.bills = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stage.id);
+                        const bpcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+                        this.joinData(data.bills, 'lid', [
+                            { data: bpcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                        ]);
                         data.pos = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, stage.id);
                         result.stages.push(data);
                     }

+ 28 - 33
app/controller/report_controller.js

@@ -18,6 +18,7 @@ const fs = require('fs');
 const rptCronJob = require('../reports/util/rpt_tmp_file_sweep');
 const RPT_DEF_PROPERTIES = require('../const/report_defined_properties');
 const reportConst = require('../const/report');
+const advanceConst = require('../const/advance');
 // const stringUtil = require('../public/js/string_util_light');
 const needCustomTables = [
     'mem_custom_select',
@@ -26,6 +27,7 @@ const needCustomTables = [
     'mem_jh_gather_im_change', 'mem_jh_im_change', 'mem_jh_gather_stage_bills_compare',
     'mem_material_sum_gl',
 ];
+const PermissionCheck = require('../const/account_permission').PermissionCheck;
 
 const STD_COMP_STAMP_SIZE_WIDTH = Math.round(5 * 96 / 2.54); // 公章标准尺寸(宽4.2厘米)转成像素
 const STD_COMP_STAMP_SIZE_HEIGHT = STD_COMP_STAMP_SIZE_WIDTH; // 公章标准尺寸(高4.2厘米)转成像素
@@ -178,7 +180,7 @@ module.exports = app => {
                     if (t.ledger_status === auditConst.ledger.status.checked) {
                         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
                         // if (t.lastStage) {
-                        //     await this.ctx.service.stage.checkStageGatherData(t.lastStage);
+                        //     await this.ctx.service.stage.checkStageGatherData(t.lastStage, this.ctx.session.sessionUser.is_admin);
                         // }
                     }
                 }
@@ -276,6 +278,16 @@ module.exports = app => {
                     const groupList = accountList.filter(item => item.account_group === idx);
                     return { groupName: item, groupList };
                 });
+                const where = { tid: this.ctx.tender.id };
+                const advance = await ctx.service.advance.getAllDataByCondition({
+                    columns: ['id', 'order', 'status', 'selected', 'type'],
+                    where,
+                    orders: [['type', 'asc'], ['create_time', 'desc']],
+                });
+                advance.forEach(x => {
+                    x.statusStr = auditConst.advance.statusString[x.status];
+                    x.typeStr = advanceConst.typeColMap[x.type].text;
+                });
 
                 const renderData = {
                     accountGroup: newAccountGroup,
@@ -319,7 +331,9 @@ module.exports = app => {
                     lastAuditor,
                     rpt_id: ctx.query.rpt_id,
                     isAdmin,
+                    prePay: JSON.stringify(advance),
                     OSS_PATH: ctx.app.config.fujianOssPath,
+                    viewPmData: PermissionCheck.viewPmData(this.ctx.session.sessionUser.permission),
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 // await this.layout('report/index.ejs', renderData);
@@ -370,6 +384,7 @@ module.exports = app => {
                 // console.log('pageSize: ' + pageSize);
                 const renderData = {
                     size: pageSize,
+                    OSS_PATH: ctx.app.config.fujianOssPath,
                     // orientation: params.orientation,
                 };
                 await ctx.render('report/rpt_individual.ejs', renderData);
@@ -433,6 +448,7 @@ module.exports = app => {
             });
 
             // console.log('encodeSignatureDataUri!');
+            // fsUtil.writeObjToFile(pageRst, 'D:/GitHome/Temp/testBuiltPageResult2.js');
             return {
                 data: pageRst,
                 signatureRelInfo: roleRel,
@@ -457,38 +473,6 @@ module.exports = app => {
                 // console.log('in getReport');
                 const params = JSON.parse(ctx.request.body.params);
                 ctx.body = await this._getReport(ctx, params);
-                // // console.log(params);
-                // let rptTpl = await ctx.service.rptTpl.getTplById(params.rpt_tpl_id);
-                // if (!rptTpl || rptTpl.length !== 1) {
-                //     throw '获取模板失败';
-                // }
-                // rptTpl = JSON.parse(rptTpl[0].rpt_content);
-                // // console.log('get the template!');
-                // const pageRst = await getAllPagesCommon(ctx, rptTpl, params, JV.PAGING_OPTION_NORMAL, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir);
-                // // console.log(pageRst);
-                // // const roleRel = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id)) : [];
-                // const roleRel = await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id); // 新需求中,允许在非审核状态下设置签名
-                // const stgAudit = await ctx.service.stageAudit.getStageAudit(params.stage_id, params.stage_times);
-                // // console.log('after role stage!');
-                // // console.log(roleRel);
-                // await encodeSignatureDataUri(roleRel, this.app.baseDir);
-                // await encodeDummySignatureDataUri(pageRst, this.app.baseDir);
-                // const stageFlow = await ctx.service.stageAudit.getAuditGroupByListWithOwner(params.stage_id, params.stage_times);
-                // const customSelect = await ctx.service.rptCustomDefine.getDataByCondition({
-                //     tid: params.tender_id, sid: params.stage_id, rid: params.rpt_tpl_id
-                // });
-                //
-                // // console.log('encodeSignatureDataUri!');
-                // ctx.body = {
-                //     data: pageRst,
-                //     signatureRelInfo: roleRel,
-                //     stageAudit: stgAudit,
-                //     debugInfo: ctx.app.config.is_debug ? ctx.debugInfo : null,
-                //     customDefine: rptTpl[JV.NODE_CUSTOM_DEFINE],
-                //     stageFlow,
-                //     customSelect,
-                // };
-                // // ctx.body = { data: { msg: 'test the network' } };
                 ctx.status = 201;
             } catch (ex) {
                 console.log(ex);
@@ -496,6 +480,17 @@ module.exports = app => {
             }
         }
 
+        async getTestReport(ctx) {
+            try {
+                const file = 'D:/GitHome/Temp/导出PDF_格式样本1.js';
+                const tplObj = JSON.parse(fs.readFileSync(file, 'utf-8'));
+                ctx.body = { data: tplObj };
+                ctx.status = 201;
+            } catch (ex) {
+                this.setMessage(ex.toString(), this.messageType.ERROR);
+            }
+        }
+
         /**
          * 获取多批次报表数据
          *

+ 38 - 42
app/controller/revise_controller.js

@@ -17,11 +17,10 @@ const audit = require('../const/audit');
 const accountGroup = require('../const/account_group').group;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
-const spreadConst = require('../const/spread');
 const shenpiConst = require('../const/shenpi');
-const fs = require('fs');
 const LzString = require('lz-string');
 const stdConst = require('../const/standard');
+const spreadSetting = require('../lib/spread_setting');
 
 module.exports = app => {
     class ReviseController extends app.BaseController {
@@ -257,41 +256,9 @@ module.exports = app => {
             }
         }
 
-        /**
-         * 获取SpreadSetting
-         * @private
-         */
-        _getSpreadSetting(revise) {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            const tender = this.ctx.tender;
-            const setting = tender.data.measure_type === measureType.tz.value
-                ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
-                : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
-            const ledger = JSON.parse(JSON.stringify(setting.ledger));
-            const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
-
-            if (revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked) {
-                ledger.readOnly = true;
-                pos.readOnly = true;
-            }
-            if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
-            }
-            if (!tender.info.display.ledger.dgnQty) {
-                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
-            }
-            return [ledger, pos];
-        }
-
         async _getDefaultReviseInfoData(ctx, revise) {
-            const [ledgerSpread, posSpread] = this._getSpreadSetting(revise);
-            const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
-            this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
+            const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, revise.tid,
+                revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked);
             const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(
                 ctx.tender.data.valuation, ctx.tender.data.measure_type);
             const curAuditor = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times);
@@ -449,7 +416,8 @@ module.exports = app => {
                     }
                 }
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
-                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags } };
+                const price = !revise.readOnly ? await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }) : [];
+                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags, price } };
             } catch (err) {
                 ctx.helper.log(err);
                 this.ajaxErrorBody(err, '加载台账修订数据错误');
@@ -474,7 +442,7 @@ module.exports = app => {
                 reviseCheck.checkBillsTp([
                     {qty: 'sgfh_qty', tp: 'sgfh_tp'}, {qty: 'qtcl_qty', tp: 'qtcl_tp'},
                     {qty: 'sjcl_qty', tp: 'sjcl_tp'}, {qty: 'quantity', tp: 'total_price'}
-                ], this.ctx.tender.info.decimal);
+                ], this.ctx.tender.info.decimal, x => { return !x.check_calc; });
                 ctx.body = { err: 0, msg: '', data: reviseCheck.checkResult };
             } catch (err) {
                 this.log(err);
@@ -486,8 +454,9 @@ module.exports = app => {
             try {
                 const revise = ctx.revise;
 
-                const [ledgerSpread, posSpread] = this._getSpreadSetting(revise);
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, revise.tid,
+                    revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked);
+                const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
                 ledgerSpread.readOnly = true;
                 posSpread.readOnly = true;
@@ -844,7 +813,7 @@ module.exports = app => {
          */
         async check(ctx) {
             try {
-                const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+                const revise = ctx.revise;
                 if (!revise || revise.status !== audit.revise.status.checking) throw '台账修订数据有误';
 
                 const curAudit = await ctx.service.reviseAudit.getCurAuditor(revise.id, revise.times);
@@ -853,7 +822,7 @@ module.exports = app => {
                 const checkType = parseInt(ctx.request.body.checkType);
                 if (!checkType || isNaN(checkType)) throw '提交数据错误';
 
-                await ctx.service.reviseAudit.check(revise, checkType, ctx.request.body.opinion, revise.times);
+                await ctx.service.reviseAudit.check(ctx.revise, checkType, ctx.request.body.opinion, revise.times);
 
                 ctx.redirect(ctx.request.headers.referer);
             } catch (err) {
@@ -979,6 +948,9 @@ module.exports = app => {
                     return spec;
                 case 'tags':
                     return await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
+                case 'price':
+                    return await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: ctx.revise.id } });
+                default: throw '请求的数据不存在';
             }
         }
 
@@ -1014,6 +986,30 @@ module.exports = app => {
             };
             await this.layout('revise/gcl_compare.ejs', renderData);
         }
+
+
+        async price(ctx) {
+            if (!ctx.revise) throw '台账修订数据有误';
+            const renderData = {
+                preUrl: ctx.url.replace('/price', ''),
+                revise: ctx.revise,
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.price),
+            };
+            await this.layout('revise/price.ejs', renderData);
+        }
+
+        async priceUpdate(ctx) {
+            try {
+                if (!ctx.revise) throw '台账修订数据有误';
+
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.revisePrice.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存数据失败');
+            }
+        }
     }
 
     return ReviseController;

+ 9 - 0
app/controller/schedule_controller.js

@@ -284,9 +284,11 @@ module.exports = app => {
             } else {
                 preStageData = [];
             }
+            const pcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stageInfo.id } });
             this.ctx.helper.assignRelaData(ledgerData, [
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid' },
+                { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
             ]);
             // 获取进度最新期的数据
             ledgerData = await this._addFinalStageData(ctx, ledgerData);
@@ -313,9 +315,11 @@ module.exports = app => {
                 } else {
                     prefinalStageData = [];
                 }
+                const finalPcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: finalStageInfo.id } });
                 this.ctx.helper.assignRelaData(ledgerData, [
                     { data: finalStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: 'final_', relaId: 'lid' },
                     { data: prefinalStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_final_', relaId: 'lid' },
+                    { data: finalPcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: 'final_', relaId: 'lid' },
                 ]);
             }
             return ledgerData;
@@ -368,6 +372,11 @@ module.exports = app => {
                 // }), 'id');
                 const stageIdList = _.map(stageList, 'id');
                 curYearStageData = await ctx.service.stageBills.getStagesData(tender.id, stageIdList.join(','));
+                const curYearStagePcData = await ctx.service.stageBillsPc.getStagesData(tender.id, stageIdList.join(','));
+                for (const cys of curYearStageData) {
+                    const cyspc = ctx.helper._.find(curYearStagePcData, { lid: cys.lid });
+                    if (cyspc) ctx.helper._.assign(cys, cyspc);
+                }
             }
             return { slmList, nextSlmList, endSlmList, yearSlmList, curYearStageData, scheduleMonth, stageOrderList, scheduleStage, curScheduleStage };
         }

+ 15 - 1
app/controller/setting_controller.js

@@ -19,6 +19,7 @@ const S2b = require('../lib/s2b');
 const measureType = require('../const/tender').measureType;
 const sendToWormhole = require('stream-wormhole');
 const path = require('path');
+const funSet = require('../const/fun_set');
 
 module.exports = app => {
 
@@ -915,11 +916,15 @@ module.exports = app => {
                 if (ctx.session.sessionUser.is_admin === 0) {
                     throw '没有访问权限';
                 }
+                const fun_set = await ctx.service.project.getFunSet(projectData.fun_set);
+                console.log(fun_set);
                 await this.layout('setting/fun.ejs', {
                     projectData,
                     funRela,
                     imType,
-                })
+                    endMonth: funSet.endMonth,
+                    funSet: fun_set,
+                });
             } catch (error) {
                 ctx.helper.log(error);
                 ctx.redirect('/dashboard');
@@ -954,8 +959,17 @@ module.exports = app => {
                 this.ctx.session.sessionProject.page_show.openChangePlan = data.openChangePlan ? 1 : 0;
                 this.ctx.session.sessionProject.page_show.openMaterialTax = data.openMaterialTax ? 1 : 0;
                 this.ctx.session.sessionProject.page_show.openMaterialChecklist = data.openMaterialChecklist ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialSelf = data.openMaterialSelf ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialEditForAudit = data.openMaterialEditForAudit ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openStageStart = data.openStageStart ? 1 : 0;
                 const result2 = await ctx.service.project.updatePageshow(projectId);
                 if (!result2) throw '保存数据失败';
+                if (data.addFunSet) {
+                    const funSet = projectData.fun_set ? JSON.parse(projectData.fun_set) : {};
+                    ctx.helper._.defaultsDeep(data.addFunSet, funSet);
+                    const result3 = await ctx.service.project.updateFunSet(projectId, data.addFunSet);
+                    if (!result3) throw '保存数据失败';
+                }
 
                 ctx.body = { err: 0, msg: '', data: null };
             } catch (error) {

+ 109 - 84
app/controller/stage_controller.js

@@ -15,7 +15,6 @@ const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const shenpiConst = require('../const/shenpi');
 const payConst = require('../const/deal_pay.js');
-const externalDataConst = require('../const/external_data.js');
 const changeConst = require('../const/change');
 const measureType = tenderConst.measureType;
 const path = require('path');
@@ -26,6 +25,8 @@ const billsPosConvert = require('../lib/bills_pos_convert');
 const fs = require('fs');
 const stdConst = require('../const/standard');
 const LzString = require('lz-string');
+const spreadSetting = require('../lib/spread_setting');
+const funSetConst = require('../const/fun_set');
 
 module.exports = app => {
     class StageController extends app.BaseController {
@@ -85,57 +86,6 @@ module.exports = app => {
         }
 
         /**
-         * 获取SpreadSetting
-         * @private
-         */
-        _getSpreadSetting(funInfo) {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            function hiddenFieldCols(setting, cols) {
-                setting.cols.forEach(x => {
-                    if (cols.indexOf(x.field) >= 0) {
-                        x.defaultVisible = false;
-                        x.visible = false;
-                    }
-                });
-            }
-            const tender = this.ctx.tender,
-                stage = this.ctx.stage;
-            const stageSetting = tender.data.measure_type === measureType.tz.value
-                ? spreadConst.stageTz
-                : (tender.info.display.ledger.clQty ? spreadConst.stageCl : spreadConst.stageNoCl);
-            const ledger = JSON.parse(JSON.stringify(stageSetting.ledger));
-            if (!tender.info.display.ledger.dgnQty) {
-                removeFieldCols(ledger, spreadConst.filterCols.stageDgnCols);
-            }
-            const pos = JSON.parse(JSON.stringify(stageSetting.pos));
-            if (!tender.info.display.stage.realComplete) {
-                removeFieldCols(pos, spreadConst.filterCols.realCompleteCols);
-            }
-            if (!this.ctx.session.sessionProject.gxby) {
-                removeFieldCols(ledger, spreadConst.filterCols.thirdPartyCols.gxby);
-                removeFieldCols(pos, spreadConst.filterCols.thirdPartyCols.gxby);
-            }
-            if (!this.ctx.session.sessionProject.dagl) {
-                removeFieldCols(ledger, spreadConst.filterCols.thirdPartyCols.dagl);
-                removeFieldCols(pos, spreadConst.filterCols.thirdPartyCols.dagl);
-            }
-            if (!funInfo.minusNoValue || !tender.info.fun_rela.stage_change.minusNoValue) {
-                hiddenFieldCols(ledger, spreadConst.filterCols.minusNoValueCols);
-                hiddenFieldCols(pos, spreadConst.filterCols.minusNoValueCols);
-            }
-            if (this.ctx.stage.readOnly || this.ctx.stage.revising) {
-                ledger.readOnly = true;
-                pos.readOnly = true;
-            }
-            return [ledger, pos];
-        }
-
-        /**
          * 获取审批界面所需的 原报、审批人数据等
          * @param ctx
          * @return {Promise<void>}
@@ -159,6 +109,46 @@ module.exports = app => {
             if (status === auditConst.status.uncheck || status === auditConst.status.checkNo) {
                 ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
             }
+            await this._checkStageStart(ctx);
+        }
+
+        async _checkStageStart(ctx) {
+            // 上报日期获取,及上报权限开关
+            if (ctx.session.sessionProject.page_show.openStageStart) {
+                const stageStart = ctx.session.sessionProject.funSet.stage_start;
+                const thisYear = moment().year();
+                // const thisYear = 2021;
+                const thisMonth = moment().month();
+                // const thisMonth = 11;
+                const thisDate = new Date();
+                let startEndDay = stageStart.end_day + '号';
+                let startDay = moment([thisYear, thisMonth, stageStart.start_day]).isValid() ? [thisYear, thisMonth, stageStart.start_day] : [thisYear, thisMonth + 1, 1];
+                const maxDay = moment(thisDate).format('YYYY-MM-DD') >= moment(startDay).format('YYYY-MM-DD');
+                startEndDay = maxDay && stageStart.end_month === funSetConst.endMonth[1].val ? funSetConst.endMonth[stageStart.end_month - 1].name + startEndDay : startEndDay;
+                if (moment(thisDate).format('YYYY-MM-DD') < moment(startDay).format('YYYY-MM-DD') && stageStart.end_month === funSetConst.endMonth[1].val) {
+                    startDay = thisMonth !== 0 && moment([thisYear, thisMonth - 1, stageStart.start_day]).isValid() ? [thisYear, thisMonth - 1, stageStart.start_day] : (thisMonth !== 0 ? [thisYear, thisMonth - 1, 1] : [thisYear - 1, 11, stageStart.start_day]);
+                }
+                ctx.stage.startEndDay = startEndDay; // 上报截止时间
+                let endDay = [thisYear, thisMonth, stageStart.end_day];
+                if (stageStart.end_month === funSetConst.endMonth[0].val && !moment(endDay).isValid()) {
+                    endDay = thisMonth === 11 ? [thisYear + 1, 0, 1] : [thisYear, thisMonth + 1, 1];
+                    endDay = moment(endDay).subtract(1, 'days');
+                } else if (stageStart.end_month === funSetConst.endMonth[1].val && maxDay) {
+                    if (!moment(endDay).isValid()) {
+                        endDay = [thisYear, thisMonth + 1, 1];
+                        endDay = moment(endDay).subtract(1, 'days');
+                    } else {
+                        endDay = thisMonth === 11 ? [thisYear + 1, 0, stageStart.end_day] : [thisYear, thisMonth + 1, stageStart.end_day];
+                    }
+                }
+                if (!moment(endDay).isValid()) {
+                    endDay = [thisYear, thisMonth + 1, 1];
+                    endDay = moment(endDay).subtract(1, 'days');
+                }
+                // console.log(moment(thisDate).format('YYYY-MM-DD'), moment(startDay).format('YYYY-MM-DD'), moment(endDay).format('YYYY-MM-DD'));
+                const startPermission = moment(moment(thisDate).format('YYYY-MM-DD')).isBetween(moment(startDay).format('YYYY-MM-DD'), moment(endDay).format('YYYY-MM-DD'), null, '[]');
+                ctx.stage.startPermission = startPermission;
+            }
         }
 
         _checkStageCanModify(ctx) {
@@ -181,9 +171,9 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
-                [renderData.ledgerSpread, renderData.posSpread] = this._getSpreadSetting(projectFunInfo);
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
-                this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [renderData.ledgerSpread, renderData.posSpread]);
+                renderData.minusNoValue = projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue;
+                [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, ctx.tender.id,
+                    this.ctx.stage.readOnly || this.ctx.stage.revising, {minusNoValue: renderData.minusNoValue});
                 renderData.changeConst = changeConst;
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.index);
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
@@ -206,7 +196,6 @@ module.exports = app => {
                 renderData.sfData = sfData;
                 // 收方单附件删除权限
                 renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.findIndex(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
-                renderData.minusNoValue = projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue;
                 renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
@@ -256,10 +245,12 @@ module.exports = app => {
             if (this.ctx.session.sessionProject.dagl) this.posExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit');
 
             if (!sjsRela) return;
-            for (const field of sjsRela.ledgerCol) {
-                if (field.show) {
-                    this.ledgerColumn.push(field.field);
-                    this.posColumn.push(field.field);
+            if (sjsRela) {
+                for (const field of sjsRela.ledgerCol) {
+                    if (field.show) {
+                        this.ledgerColumn.push(field.field);
+                        this.posColumn.push(field.field);
+                    }
                 }
             }
         }
@@ -270,6 +261,7 @@ module.exports = app => {
                 : await ctx.service.ledger.getAllDataByCondition({ columns: this.ledgerColumn, where: { tender_id: ctx.tender.id } });
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
             const extraData = await ctx.service.ledgerExtra.getData(ctx.tender.id, this.ledgerExtraColumn);
+            const pcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } });
             const importData = await ctx.service.stageImportChange.getImportLid(ctx.stage.id);
             let curStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
@@ -290,6 +282,7 @@ module.exports = app => {
                 { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid' },
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used'], prefix: 'pre_', relaId: 'lid' },
+                { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
             ]);
             return ledgerData;
         }
@@ -340,7 +333,7 @@ module.exports = app => {
                 const filter = data.filter.split(';');
                 const responseData = { err: 0, msg: '', data: {}, hpack: [] };
                 const hpack = true;
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 this._getLedgerColumn(sjsRela);
                 for (const f of filter) {
                     switch (f) {
@@ -435,6 +428,7 @@ module.exports = app => {
                 checkData.checkBillsTp([
                     { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
                 ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
+                checkData.checkBillsQty(['contract_qty', 'qc_qty']);
                 ctx.body = { err: 0, msg: '', data: checkData.checkResult };
             } catch (err) {
                 this.log(err);
@@ -576,6 +570,44 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 调用变更令 (Ajax-Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async autoUseChange(ctx) {
+            try {
+                this._checkStageCanModify(ctx);
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.autoType) throw '参数错误';
+
+                let result;
+                switch (data.autoType) {
+                    case 'bills':
+                        if (!data.cid || !data.cbid) throw '参数错误';
+                        result = await ctx.service.stageChange.autoUseChangeBills(this.ctx.tender, this.ctx.stage, data.bills);
+                        result.change = { target: [] };
+                        for (const b of data.bills) {
+                            result.change.push({ lid: b.lid, pid: b.pid });
+                        }
+                        result.change.data = await ctx.service.stageChange.getLastestStageData(ctx.tender.id,
+                            ctx.stage.id, data.target.pos.lid, data.target.pos.id);
+                        break;
+                    case 'all':
+                        result = await ctx.service.stageChange.autoUseAllChange(this.ctx.tender, this.ctx.stage);
+                        break;
+                    default: throw '参数错误';
+                }
+                await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true);
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
         /**
          * 查询变更令 明细数据(包括附件、变更清单、累计使用情况、本期使用情况) (Ajax-Post)
          * @param ctx
@@ -1226,6 +1258,11 @@ module.exports = app => {
                     throw '该期数据当前无法上报';
                 }
 
+                await this._checkStageStart(ctx);
+                if (ctx.session.sessionProject.page_show.openStageStart && !ctx.stage.startPermission) {
+                    throw '非指定日期范围内无法上报';
+                }
+
                 await ctx.service.stageAudit.start(ctx.stage.id, ctx.stage.times);
                 ctx.body = { err: 0, msg: '', data: [] };
             } catch (err) {
@@ -1322,25 +1359,6 @@ module.exports = app => {
             }
         }
 
-        // 清单汇总相关
-        _getGatherSpreadSetting() {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            const gcl = JSON.parse(JSON.stringify(spreadConst.stageGather.gcl));
-
-            const leafXmj = JSON.parse(JSON.stringify(spreadConst.stageGather.leafXmj));
-            const tender = this.ctx.tender;
-            if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(gcl, spreadConst.filterCols.tzWithoutCols);
-            } else {
-                removeFieldCols(leafXmj, ['quantity']);
-            }
-            return [gcl, leafXmj];
-        }
         /**
          * 清单汇总 页面 (Get)
          * @param ctx
@@ -1350,7 +1368,7 @@ module.exports = app => {
             try {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
-                [renderData.gclSpread, renderData.leafXmjSpread] = this._getGatherSpreadSetting();
+                [renderData.gclSpread, renderData.leafXmjSpread] = await spreadSetting.getStageGatherSpreadSetting(ctx);
                 const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
                 renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
 
@@ -1398,10 +1416,17 @@ module.exports = app => {
                 if (data.main) {
                     result.main = {};
                     result.main.ledger = ctx.stage.ledgerHis
-                        ? await ctx.helper.loadLedgerDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.bills_file)
+                        ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file)
                         : await ctx.service.ledger.getData(ctx.tender.id);
+                    const bpcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } });
+                    if (bpcData.length > 0) {
+                        this.ctx.helper.assignRelaData(result.main.ledger, [
+                            { data: bpcData, fields: ['pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
+                        ]);
+                    }
+
                     result.main.pos = ctx.stage.ledgerHis
-                        ? await ctx.helper.loadLedgerDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.pos_file)
+                        ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.pos_file)
                         : await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 }
                 for (const order of data.roles) {
@@ -1425,7 +1450,7 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.bwtz);
-                const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
                 renderData.ex_memo1 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo1'; });
                 renderData.ex_memo2 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo2'; });
                 renderData.ex_memo3 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo3'; });

+ 8 - 6
app/controller/stage_rela_controller.js

@@ -96,7 +96,10 @@ module.exports = app => {
                 if (!relaStage) throw '不存在该关联标段';
                 const renderData = await this._getDefaultRenderData(ctx);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stageRela.detail);
-                [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, null, true);
+                const relaTenderInfo = await this.ctx.service.tenderInfo.getTenderInfo(relaStage.rela_tid);
+                const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
+                renderData.minusNoValue = projectFunInfo.minusNoValue && relaTenderInfo.fun_rela.stage_change.minusNoValue;
+                [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, null, true, {minusNoValue: renderData.minusNoValue});
                 renderData.measureType = measureType;
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
                 renderData.curAuditor = ctx.stage.curAuditor;
@@ -134,8 +137,8 @@ module.exports = app => {
             });
             this.ctx.helper.assignRelaData(ledgerData, [
                 { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
-                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
-                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', ], prefix: 'end_', relaId: 'lid' },
+                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
+                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty',], prefix: 'end_', relaId: 'lid' },
             ]);
             return ledgerData;
         }
@@ -149,8 +152,8 @@ module.exports = app => {
                 where: {sid: ctx.stage.id, rela_tid: relaStage.rela_tid},
             });
             this.ctx.helper.assignRelaData(posData, [
-                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'pid' },
-                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', ], prefix: 'end_', relaId: 'pid' },
+                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'pid' },
+                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty' ], prefix: 'end_', relaId: 'pid' },
             ]);
             return posData;
         }
@@ -236,7 +239,6 @@ module.exports = app => {
                 const result = await ctx.service.stageDetailAtt.addFiles(stage, baseInfo, uploadFiles);
                 ctx.body = { err: 0, mgs: '', data: result };
             } catch (err) {
-                console.log(err);
                 // 失败需要消耗掉stream 以防卡死
                 if (stream) await sendToWormhole(stream);
                 ctx.log(err);

+ 7 - 1
app/controller/standard_lib_controller.js

@@ -30,6 +30,9 @@ module.exports = app => {
                     case 'xmj':
                         responseData.data = await this.ctx.service.stdXmj.getData(data.list_id);
                         break;
+                    case 'glj':
+                        responseData.data = await this.ctx.service.gljLib.getData(data.list_id);
+                        break;
                     default:
                         throw '查询的标准清单不存在';
                 }
@@ -66,6 +69,9 @@ module.exports = app => {
                     case 'xmj':
                         responseData.data = await this.ctx.service.stdXmj.getAllDataByCondition({ where: condition });
                         break;
+                    case 'glj':
+                        responseData.data = await this.ctx.service.gljLib.getAllDataByCondition({ where: condition });
+                        break;
                     default:
                         throw '查询的标准清单不存在';
                 }
@@ -79,4 +85,4 @@ module.exports = app => {
     }
 
     return StandardLibController;
-};
+};

+ 29 - 10
app/controller/tender_controller.js

@@ -140,7 +140,7 @@ module.exports = app => {
                             t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
                             t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
                         }
-                        if (t.lastStage) await this.ctx.service.stage.checkStageGatherData(t.lastStage);
+                        if (t.lastStage) await this.ctx.service.stage.checkStageGatherData(t.lastStage, this.ctx.session.sessionUser.is_admin);
                         t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
                         if ((!bCalcTp) && t.measure_type === measureType.gcl.value) {
                             bCalcTp = t.lastStage && t.lastStage.status !== auditConst.stage.status.checked && !t.lastStage.readOnly;
@@ -399,8 +399,12 @@ module.exports = app => {
                     tender.ledger_status === auditConst.ledger.status.checkNo || tender.ledger_status === auditConst.ledger.status.uncheck);
                 const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
                 const lastStage = stages.length > 0 ? stages[0] : null; // await ctx.service.stage.getLastestStage(ctx.tender.id);
+                const [change_tp, change_p_tp, change_n_tp] = await ctx.service.change.getChangeTp(tender.id);
+                tender.change_tp = change_tp;
+                tender.change_p_tp = change_p_tp;
+                tender.change_n_tp = change_n_tp;
                 if (lastStage) {
-                    await this.ctx.service.stage.checkStageGatherData(lastStage);
+                    await this.ctx.service.stage.checkStageGatherData(lastStage, this.ctx.session.sessionUser.is_admin);
 
                     if ((!bCalcTp) && tender.measure_type === measureType.gcl.value) {
                         bCalcTp = lastStage.status !== auditConst.stage.status.checked && !lastStage.readOnly;
@@ -410,17 +414,16 @@ module.exports = app => {
                         tender.total_price = sum.total_price;
                         tender.deal_tp = sum.deal_tp;
                     }
-
-                    tender.gather_tp = ctx.helper.add(lastStage.contract_tp, lastStage.qc_tp);
-                    tender.end_contract_tp = ctx.helper.add(lastStage.contract_tp, lastStage.pre_contract_tp);
-                    tender.end_qc_tp = ctx.helper.add(lastStage.qc_tp, lastStage.pre_qc_tp);
+                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
+                    tender.gather_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.qc_tp, lastStage.pc_tp]);
+                    tender.end_contract_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.pre_contract_tp, lastStage.contract_pc_tp]);
+                    tender.end_qc_tp = ctx.helper.sum([lastStage.qc_tp, lastStage.pre_qc_tp, lastStage.qc_pc_tp]);
+                    tender.end_positive_qc_tp = ctx.helper.sum([lastStage.positive_qc_tp, lastStage.pre_positive_qc_tp, lastStage.positive_qc_pc_tp]);
+                    tender.end_negative_qc_tp = ctx.helper.sum([lastStage.negative_qc_tp, lastStage.pre_negative_qc_tp, lastStage.negative_qc_pc_tp]);
                     tender.end_gather_tp = ctx.helper.add(tender.end_contract_tp, tender.end_qc_tp);
                     tender.pre_gather_tp = ctx.helper.add(lastStage.pre_contract_tp, lastStage.pre_qc_tp);
                     tender.yf_tp = lastStage.yf_tp;
-                    const change_tp = await ctx.service.change.getChangeTp(tender.id);
-                    tender.change_tp = change_tp;
                     tender.qc_ratio = ctx.helper.mul(ctx.helper.div(tender.end_qc_tp, ctx.tender.info.deal_param.contractPrice, 2), 100);
-                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
                     tender.pre_ratio = ctx.helper.mul(ctx.helper.div(tender.pre_gather_tp, tender.sum, 2), 100);
                     tender.cur_ratio = ctx.helper.mul(ctx.helper.div(tender.gather_tp, tender.sum, 2), 100);
                     tender.other_tp = ctx.helper.sub(ctx.helper.sub(tender.sum, tender.pre_gather_tp), tender.gather_tp);
@@ -453,7 +456,7 @@ module.exports = app => {
                         tender.total_price = sum.total_price;
                         tender.deal_tp = sum.deal_tp;
                     }
-
+                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
                     if (tender.ledger_status !== auditConst.ledger.status.uncheck) {
                         const status_name = await this.ctx.service.ledgerAudit.getStatusName(tender.id, tender.ledger_times);
                         tender.status_users = status_name ? status_name.name : '';
@@ -552,6 +555,7 @@ module.exports = app => {
                         map_json.lat = mapInfo.lat;
                     }
                 }
+
                 const renderData = {
                     tenders,
                     categoryData,
@@ -1454,11 +1458,25 @@ module.exports = app => {
                                 orders: [['type', 'asc'], ['create_time', 'desc']],
                             });
                             advance.forEach(x => {
+                                x.orderStr = `第${x.order}期`;
                                 x.statusStr = auditConst.advance.statusString[x.status];
                                 x.typeStr = advanceConst.typeColMap[x.type].text;
                             });
                             responseData.data[f] = advance;
                             break;
+                        case 'pm_deal':
+                            if (accountPermission.PermissionCheck.viewPmData(this.ctx.session.sessionUser.permission)) {
+                                const selects = await this.ctx.service.project.getPmDealCache(this.ctx.session.sessionProject.id);
+                                const pm = require('../lib/pm');
+                                const tenders = await pm.dealCatagory(ctx, this.ctx.session.sessionProject.code);
+                                tenders.forEach(x => {
+                                    x.selected = selects.indexOf(x.bidsectionId + '') >= 0;
+                                });
+                                responseData.data[f] = tenders;
+                            } else {
+                                throw '您无权查看该数据';
+                            }
+                            break;
                         default:
                             throw '未知数据类型';
                     }
@@ -1479,6 +1497,7 @@ module.exports = app => {
                 if (data.change_project) await this.ctx.service.changeProject.defaultUpdateRows(data.change_project);
                 if (data.change_plan) await this.ctx.service.changePlan.defaultUpdateRows(data.change_plan);
                 if (data.advance) await this.ctx.service.advance.defaultUpdateRows(data.advance);
+                if (data.pm_deal) await this.ctx.service.project.setPmDealCache(this.ctx.session.sessionProject.id, data.pm_deal);
                 ctx.body = responseData;
             } catch (err) {
                 ctx.log(err);

+ 13 - 9
app/controller/wap_controller.js

@@ -124,11 +124,11 @@ module.exports = app => {
                     const isLastage = await ctx.service.stage.isLastStage(audit.tid, audit.id);
                     if (isLastage) await this.ctx.service.stage.checkStageGatherData(audit);
                 }
-                audit.gather_tp = ctx.helper.add(audit.contract_tp, audit.qc_tp);
-                audit.end_contract_tp = ctx.helper.add(audit.contract_tp, audit.pre_contract_tp);
-                audit.end_qc_tp = ctx.helper.add(audit.qc_tp, audit.pre_qc_tp);
-                audit.end_gather_tp = ctx.helper.add(audit.end_contract_tp, audit.end_qc_tp);
+                audit.gather_tp = ctx.helper.sum([audit.contract_tp, audit.qc_tp, audit.pc_tp]);
+                audit.end_contract_tp = ctx.helper.sum([audit.contract_tp, audit.pre_contract_tp, audit.contract_pc_tp]);
+                audit.end_qc_tp = ctx.helper.sum([audit.qc_tp, audit.pre_qc_tp, audit.qc_pc_tp]);
                 audit.pre_gather_tp = ctx.helper.add(audit.pre_contract_tp, audit.pre_qc_tp);
+                audit.end_gather_tp = ctx.helper.add(audit.gather_tp, audit.pre_gather_tp);
             }
             // 获取待审批的变更期
             const auditChanges = await ctx.service.changeAudit.getAuditChangeByWap(ctx.session.sessionUser.accountId);
@@ -223,18 +223,22 @@ module.exports = app => {
                     tender.total_price = sum.total_price;
                     tender.deal_tp = sum.deal_tp;
                 }
+                const [change_tp, change_p_tp, change_n_tp] = await ctx.service.change.getChangeTp(ctx.tender.id);
+                tender.change_tp = change_tp;
+                tender.change_p_tp = change_p_tp;
+                tender.change_n_tp = change_n_tp;
                 const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
                 const lastStage = stages.length > 0 ? stages[0] : null; //await ctx.service.stage.getLastestStage(ctx.tender.id);
                 if (lastStage) {
                     await this.ctx.service.stage.checkStageGatherData(lastStage);
-                    tender.gather_tp = ctx.helper.add(lastStage.contract_tp, lastStage.qc_tp);
-                    tender.end_contract_tp = ctx.helper.add(lastStage.contract_tp, lastStage.pre_contract_tp);
-                    tender.end_qc_tp = ctx.helper.add(lastStage.qc_tp, lastStage.pre_qc_tp);
-                    tender.end_gather_tp = ctx.helper.add(tender.end_contract_tp, tender.end_qc_tp);
+                    tender.gather_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.qc_tp, lastStage.pc_tp]);
+                    tender.end_contract_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.pre_contract_tp, lastStage.contract_pc_tp]);
+                    tender.end_qc_tp = ctx.helper.sum([lastStage.qc_tp, lastStage.pre_qc_tp, lastStage.qc_pc_tp]);
                     tender.pre_gather_tp = ctx.helper.add(lastStage.pre_contract_tp, lastStage.pre_qc_tp);
+                    tender.end_gather_tp = ctx.helper.add(tender.gather_tp, tender.pre_gather_tp);
                     tender.yf_tp = lastStage.yf_tp;
                     tender.qc_ratio = ctx.helper.mul(ctx.helper.div(tender.end_qc_tp, ctx.tender.info.deal_param.contractPrice, 2), 100);
-                    tender.sum = ctx.helper.add(tender.total_price, tender.end_qc_tp);
+                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
                     tender.pre_ratio = ctx.helper.mul(ctx.helper.div(tender.pre_gather_tp, tender.sum, 2), 100);
                     tender.cur_ratio = ctx.helper.mul(ctx.helper.div(tender.gather_tp, tender.sum, 2), 100);
                     tender.other_tp = ctx.helper.sub(ctx.helper.sub(tender.sum, tender.pre_gather_tp), tender.gather_tp);

+ 2 - 2
app/extend/helper.js

@@ -409,9 +409,9 @@ module.exports = {
      * @param {Number} value2
      * @return {boolean}
      */
-    checkNumberEqual(value1, value2) {
+    numEqual(value1, value2) {
         if (value1 && value2) {
-            return Math.abs(value2 - value1) > zeroRange;
+            return Math.abs(value2 - value1) < zeroRange;
         }
         return (!value1 && !value2);
 

+ 5 - 0
app/lib/analysis_excel.js

@@ -516,6 +516,8 @@ class AnalysisExcelTree {
             pos: {value: ['计量单元'], type: colDefineType.match},
             name: {value: ['名称'], type: colDefineType.match},
             unit: {value: ['单位'], type: colDefineType.match},
+            deal_qty: {value: ['签约数量'], type: colDefineType.match},
+            deal_tp: {value: ['签约金额'], type: colDefineType.match},
             quantity: {value: ['清单数量'], type: colDefineType.match},
             dgn_qty1: {value: ['设计数量1'], type: colDefineType.match},
             dgn_qty2: {value: ['设计数量2'], type: colDefineType.match},
@@ -552,6 +554,7 @@ class AnalysisExcelTree {
             node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
             node.dgn_qty1 = aeUtils.toNumber(row[this.colsDef.dgn_qty1]);
             node.dgn_qty2 = aeUtils.toNumber(row[this.colsDef.dgn_qty2]);
+            node.deal_tp = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.deal_tp]), this.decimal.tp);
             node.total_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.total_price]), this.decimal.tp);
             node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
             node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
@@ -583,11 +586,13 @@ class AnalysisExcelTree {
         node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
         node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
         const precision = this.ctx.helper.findPrecision(this.precision, node.unit);
+        node.deal_qty = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.deal_qty]), precision.value);
         node.quantity = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.quantity]), precision.value);
         node.sgfh_qty = node.quantity;
         node.unit_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.unit_price]), this.decimal.up);
         node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
         node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
+        node.deal_tp = node.deal_qty && node.unit_price ? this.ctx.helper.mul(node.deal_qty, node.unit_price, this.decimal.tp) : 0;
         if (node.quantity && node.unit_price) {
             node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.decimal.tp);
         } else {

+ 21 - 6
app/lib/bills_pos_convert.js

@@ -19,7 +19,7 @@ class BillsPosConvert {
         this.ctx = ctx;
         this._ = this.ctx.helper._;
 
-        this.tpFields = ['total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+        this.tpFields = ['total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
             'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp'];
         this.baseCalcFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty',
             'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'end_contract_qty', 'end_qc_qty', 'end_gather_qty'];
@@ -85,6 +85,8 @@ class BillsPosConvert {
         node.quantity = this.ctx.helper.add(node.quantity, data.quantity);
         node.total_price = this.ctx.helper.add(node.total_price, this.ctx.helper.mul(node.unit_price, data.quantity, tpDecimal));
 
+        const org_price = node.org_price || node.unit_price;
+
         node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
         node.contract_tp = this.ctx.helper.add(node.contract_tp, this.ctx.helper.mul(node.unit_price, data.contract_qty, tpDecimal));
         node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
@@ -92,9 +94,9 @@ class BillsPosConvert {
         node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
 
         node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);
-        node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, this.ctx.helper.mul(node.unit_price, data.pre_contract_qty, tpDecimal));
+        node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, this.ctx.helper.mul(org_price, data.pre_contract_qty, tpDecimal));
         node.pre_qc_qty = this.ctx.helper.add(node.pre_qc_qty, data.pre_qc_qty);
-        node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, this.ctx.helper.mul(node.unit_price, data.pre_qc_qty, tpDecimal));
+        node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, this.ctx.helper.mul(org_price, data.pre_qc_qty, tpDecimal));
         node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
 
         node.real_qty = this.ctx.helper.add(node.real_qty, data.real_qty);
@@ -111,6 +113,7 @@ class BillsPosConvert {
         node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
         node.qc_tp = this.ctx.helper.add(node.qc_tp, data.qc_tp);
         node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
+        node.gather_tp = this.ctx.helper.add(node.gather_tp, data.gather_tp);
 
         node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);
         node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, data.pre_contract_tp);
@@ -142,7 +145,7 @@ class BillsPosConvert {
                 if (p.ex_memo3) posUnit.ex_memo3.push(p.ex_memo3);
 
                 const gclUnit = xmj.unitTree.addNode({pos_name: '',
-                    b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price
+                    b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, org_price: node.org_price,
                 }, posUnit);
                 if (node.drawing_code && gclUnit.drawing_code.indexOf(node.drawing_code) < 0)
                     gclUnit.drawing_code.push(node.drawing_code);
@@ -164,7 +167,7 @@ class BillsPosConvert {
         } else {
             const unit = xmj.unitTree.addNode({
                 pos_name: '',
-                b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price,
+                b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, org_price: node.org_price,
             });
             if (node.drawing_code && unit.drawing_code.indexOf(node.drawing_code) < 0)
                 unit.drawing_code.push(node.drawing_code);
@@ -248,12 +251,24 @@ class BillsPosConvert {
         child.final_1_tp = this.ctx.helper.mul(child.unit_price, child.final_1_qty, tpDecimal);
         child.end_final_1_tp = this.ctx.helper.add(child.final_1_tp, child.end_qc_tp);
         child.end_final_1_percent = this.ctx.helper.mul(this.ctx.helper.div(child.end_gather_tp, child.end_final_1_tp, 4), 100);
+
+        const priceDiff = child.org_price ? this.ctx.helper.sub(child.unit_price, child.org_price) : 0;
+        if (priceDiff && (child.pre_contract_qty || child.pre_qc_qty)) {
+            child.contract_pc_tp = this.ctx.helper.sub(this.ctx.helper.mul(child.unit_price, child.pre_contract_qty, this.ctx.tender.info.decimal.tp), child.pre_contract_tp);
+            child.qc_pc_tp = this.ctx.helper.sub(this.ctx.helper.mul(child.unit_price, child.pre_qc_qty, this.ctx.tender.info.decimal.tp), child.pre_qc_tp);
+            child.pc_tp = this.ctx.helper.add(child.contract_pc_tp, child.qc_pc_tp);
+            child.gather_tp = this.ctx.helper.add(child.gather_tp, child.pc_tp);
+            child.end_contract_tp = this.ctx.helper.add(child.end_contract_tp, child.contract_pc_tp);
+            child.end_qc_tp = this.ctx.helper.add(child.end_qc_tp, child.qc_pc_tp);
+            child.end_gather_tp = this.ctx.helper.add(child.end_contract_tp, child.end_qc_tp);
+        }
     }
     _calculateNode(node, children) {
         for (const child of children) {
             node.total_price = this.ctx.helper.add(node.total_price, child.total_price);
             node.contract_tp = this.ctx.helper.add(node.contract_tp, child.contract_tp);
             node.qc_tp = this.ctx.helper.add(node.qc_tp, child.qc_tp);
+            node.pc_tp = this.ctx.helper.add(node.pc_tp, child.pc_tp);
             node.gather_tp = this.ctx.helper.add(node.gather_tp, child.gather_tp);
             node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, child.pre_contract_tp);
             node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, child.pre_qc_tp);
@@ -372,7 +387,7 @@ class BillsPosConvert {
         switch (filter) {
             case 'cur':
                 return this._getResultData(function (node) {
-                    for (const field of ['contract_tp', 'qc_tp', 'gather_tp']) {
+                    for (const field of ['contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp']) {
                         if (node[field]) {
                             return false;
                         }

+ 3 - 1
app/lib/budget_final.js

@@ -162,13 +162,15 @@ class BudgetFinal {
                 ? await this.ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
                 : await this.ctx.service.stageBills.getLastestStageData2(id, stage.id);
             const preBills = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order - 1) : [];
+            const bpcData = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
             helper.assignRelaData(bills, [
                 { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
                 { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
                 { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+                { data: bpcData, fields: ['pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
             ]);
             bills.forEach(b => {
-                b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp]);
+                b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp, b.pc_tp]);
             });
             this.final.tender_info.push({ id, stageOrder: stage.order, stageStatus: stage.status, stageFlow: stage.curTimes + '-' + stage.curOrder });
         }

+ 80 - 1
app/lib/ledger.js

@@ -847,7 +847,7 @@ class checkData {
     }
     checkBillsTp(field, decimal, filter) {
         for (const b of this.checkBills.nodes) {
-            if ((b.children && b.children.length > 0) || !b.check_calc) continue;
+            if ((b.children && b.children.length > 0)) continue;
             if (filter && filter(b)) continue;
 
             const checkData = {}, calcData = {};
@@ -944,6 +944,84 @@ class checkData {
     }
 }
 
+class reviseTree extends billsTree {
+    constructor (ctx, setting) {
+        super(ctx, setting);
+        this.price = [];
+    }
+    loadRevisePrice(price, decimal) {
+        this.decimal = decimal;
+        this.price = price || [];
+    }
+    checkRevisePrice(d) {
+        const helper = this.ctx.helper;
+        const p = this.price.find(x => {
+            return x.b_code === d.b_code &&
+                ((!x.name && !d.name) || x.name === d.name) &&
+                ((!x.unit && !d.unit) || x.unit === d.unit) &&
+                helper.checkZero(x.org_price - d.unit_price);
+        });
+        if (!p) return false;
+        d.org_price = p.org_price;
+        d.unit_price = p.new_price;
+        d.deal_tp = helper.mul(d.deal_qty, d.unit_price, this.decimal.tp);
+        d.sgfh_tp = helper.mul(d.sgfh_qty, d.unit_price, this.decimal.tp);
+        d.sjcl_tp = helper.mul(d.sjcl_qty, d.unit_price, this.decimal.tp);
+        d.qtcl_tp = helper.mul(d.qtcl_qty, d.unit_price, this.decimal.tp);
+        d.total_price = helper.mul(d.quantity, d.unit_price, this.decimal.tp);
+        return true;
+    }
+    loadDatas(datas) {
+        super.loadDatas(datas);
+        if (this.price.length > 0) {
+            for (const d of this.datas) {
+                if (d.children && d.children.length > 0) continue;
+                if (!d.b_code) continue;
+                this.checkRevisePrice(d);
+            }
+        }
+    }
+    getUpdateReviseData() {
+        return this.datas.map(x => {
+            if (x.children && x.children.length > 0) {
+                return {
+                    id: x.id, tender_id: x.tender_id, crid: x.crid,
+                    ledger_id: x.ledger_id, ledger_pid: x.ledger_pid, full_path: x.full_path, order: x.order, level: x.level, is_leaf: x.is_leaf,
+                    node_type: x.node_type, check_calc: x.check_calc,
+                    code: x.code, b_code: x.b_code, name: x.name, unit: x.unit, position: x.position,
+                    drawing_code: x.drawing_code, memo: x.memo, add_user: x.add_user, in_time: x.in_time,
+                    unit_price: 0, dgn_qty1: x.dgn_qty1, dgn_qty2: x.dgn_qty2,
+                    quantity: 0, total_price: 0,
+                    sgfh_qty: 0, sgfh_tp: 0, sgfh_expr: '',
+                    sjcl_qty: 0, sjcl_tp: 0, sjcl_expr: '',
+                    qtcl_qty: 0, qtcl_tp: 0, qtcl_expr: '',
+                };
+            } else {
+                return {
+                    id: x.id, tender_id: x.tender_id, crid: x.crid,
+                    ledger_id: x.ledger_id, ledger_pid: x.ledger_pid, full_path: x.full_path, order: x.order, level: x.level, is_leaf: x.is_leaf,
+                    node_type: x.node_type, check_calc: x.check_calc,
+                    code: x.code, b_code: x.b_code, name: x.name, unit: x.unit, position: x.position,
+                    drawing_code: x.drawing_code, memo: x.memo, add_user: x.add_user, in_time: x.in_time,
+                    unit_price: x.unit_price, dgn_qty1: x.dgn_qty1, dgn_qty2: x.dgn_qty2,
+                    quantity: x.quantity, total_price: x.total_price,
+                    sgfh_qty: x.sgfh_qty, sgfh_tp: x.sgfh_tp, sgfh_expr: x.sgfh_expr,
+                    sjcl_qty: x.sjcl_qty, sjcl_tp: x.sjcl_tp, sjcl_expr: x.sjcl_expr,
+                    qtcl_qty: x.qtcl_qty, qtcl_tp: x.qtcl_tp, qtcl_expr: x.qtcl_expr,
+                };
+            }
+        });
+    }
+    sum() {
+        const result = { total_price: 0 };
+        for (const d of this.datas) {
+            if (d.children && d.children.length > 0) continue;
+            result.total_price = this.ctx.helper.add(result.total_price, d.total_price);
+        }
+        return result;
+    }
+}
+
 module.exports = {
     billsTree,
     pos,
@@ -951,4 +1029,5 @@ module.exports = {
     filterGatherTree,
     gatherTree,
     checkData,
+    reviseTree,
 };

+ 71 - 0
app/lib/pm.js

@@ -0,0 +1,71 @@
+'use strict';
+
+/**
+ * 标准库std 相关接口
+ *
+ * @author Mai
+ * @date 2020/3/15
+ * @version
+ */
+
+// 加密类
+const jwt = require('jsonwebtoken');
+const privateKey = 'jl2pm3850key888sc';
+const dealCatagoryApi = '/api/external/jlbb/folder';
+const dealDataApi = '/api/external/jlbb/contract';
+
+const pmUtils = {
+    getJwt() {
+        return jwt.sign({}, privateKey, {expiresIn: '10m', algorithm: 'HS256'});
+    },
+    async postData(ctx, url, data) {
+        const res = await ctx.curl(url, {
+            method: 'GET', //'POST',
+            contentType: 'json',
+            data,
+            dataType: 'json',
+            timeout: [2000, 10000],
+            timing: true,
+        });
+        if (res.status !== 200) throw '请求项目管理数据异常';
+        if (!res.data || res.data.code) throw '项目管理返回数据异常';
+        return res.data.data;
+    },
+};
+
+const filterLeaf = function(node, result) {
+    if (node.children) {
+        for (const child of node.children) {
+            filterLeaf(child, result);
+        }
+    } else if (node.isfolder !== undefined && !node.isfolder) result.push(node);
+};
+
+module.exports = {
+    async dealCatagory(ctx, pCode) {
+        // mock
+        // return [ {id: 1, name: 'A标'}, {id: 2, name: 'B标'}, {id: 3, name: 'C标'}];
+        const url = ctx.app.config.managementProxyPath + dealCatagoryApi;
+        try {
+            const data = await pmUtils.postData(ctx, url, { code: pCode, token: pmUtils.getJwt() });
+            const result = [];
+            filterLeaf(data, result);
+            return result;
+        } catch (err) {
+            ctx.log(err);
+            return [];
+        }
+    },
+    async dealData(ctx, pCode, selects) {
+        if (!selects || selects.length === 0) return {};
+
+        const url = ctx.app.config.managementProxyPath + dealDataApi;
+        try {
+            const data = { code: pCode, token: pmUtils.getJwt(), key: ['contracts', 'tree_contracts'], bidsectionid: selects };
+            return await pmUtils.postData(ctx, url, data);
+        } catch (err) {
+            ctx.log(err);
+            return {};
+        }
+    }
+};

+ 345 - 0
app/lib/revise_price.js

@@ -0,0 +1,345 @@
+'use strict';
+
+/**
+ *
+ * 单价调整计算:
+ * 1. 台账修订上报,保存台账修订历史时,使用当前单价调整计算一次
+ * 2. 台账修订完成:计算台账、应用到未审完成期、应用到所有工程变更
+ * 3. 新增期:检查是否存在未应用的单价变更
+ * 4. 期审批完成:所有未应用的单价调整,记录应用到该期(记录revise_price.use_stage/use_stage_order) - 一条sql即可
+ * 5. 期重现审批:上一次应用的所有单价调整,回到未应用状态(revise_price.use_stage/use_stage_order均回到0) - 一条sql即可
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('./ledger');
+const audit = require('../const/audit');
+
+class revisePriceCalc {
+    constructor(ctx) {
+        this.ctx = ctx;
+    }
+
+    findPrice(b_code, name, unit, unit_price) {
+        const helper = this.ctx.helper;
+        return this.price.find(x => {
+            return b_code === x.b_code && name === x.name && unit === x.unit && helper.numEqual(unit_price, x.org_price);
+        });
+    }
+
+    /**
+     * 新增一期计量,检查单价调整
+     * @param {Object} newStage - 新计量期
+     * @param {Object} preStage - 上一计量期
+     * @return { inseretPosData, insertBillsData }
+     */
+    async newStagePriceChange(newStage, preStage, transaction) {
+        // 获取未执行的单价变更,无单价变更不执行
+        const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: newStage.tid, valid: 1, use_stage: 0 } });
+        if (price.length === 0) return;
+        // 无截止上期数据不执行
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { sid: preStage.id } });
+        if (preBillsData.length === 0) return;
+
+        // 加载树结构
+        const bills = await this.ctx.service.ledger.getData(newStage.tid);
+        this.ctx.helper.assignRelaData(bills, [
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+
+        // 计算
+        const result = { ibData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        billsTree.calculateAll(node => {
+            if (!node.pre_id) return;
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+            node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+            node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+            node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp);
+            node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp);
+            result.ibData.push({
+                tid: newStage.tid, sid: newStage.id, sorder: newStage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price,
+                contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+                positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp,
+            });
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.ibData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0;
+        for (const ibc of result.ibData) {
+            contract_pc_tp = helper.add(contract_pc_tp, ibc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, ibc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, ibc.pc_tp);
+            positive_qc_pc_tp = helper.add(positive_qc_pc_tp, ibc.positive_qc_pc_tp);
+            negative_qc_pc_tp = helper.add(negative_qc_pc_tp, ibc.negative_qc_pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName,
+            { id: newStage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+
+    /**
+     * 期,重新审批,检查单价调整
+     * @param {Object} stage - 期
+     * @param {Number} auditOrder - 重新审批,最新审批人序号
+     * @param {Object} transaction - 事务
+     */
+    async stageCheckAgainPriceChange(stage, auditOrder, transaction) {
+        // 获取未执行的单价变更,无单价变更不执行
+        const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: stage.tid, valid: 1, use_stage: 0 } });
+        if (price.length === 0) return;
+
+        const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } });
+
+        // 加载树结构
+        const bills = await this.ctx.service.ledger.getData(stage.tid);
+        this.ctx.helper.assignRelaData(bills, [
+            { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'positive_qc_qty', 'negative_qc_qty', 'postil', 'times', 'order'], prefix: 'cur_', relaId: 'lid' },
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+
+        const stageChange = await this.ctx.service.stageChange.getFinalStageData(stage.tid, stage.id);
+
+        // 计算 insertBills billsPriceChange stageChange
+        const result = { ibData: [], bpcData: [], scData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        const said = this.ctx.session.sessionUser.accountId;
+        billsTree.calculateAll(node => {
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            if (node.cur_id) {
+                node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
+                node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
+                node.cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp);
+                node.cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp);
+                result.ibData.push({
+                    tid: stage.tid, sid: stage.id, said,
+                    lid: node.id,
+                    times: stage.times, order: auditOrder,
+                    contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp,
+                    qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp,
+                    positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: node.cur_positive_qc_tp,
+                    negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: node.cur_negative_qc_tp,
+                    postil: node.postil,
+                });
+            }
+            if (node.pre_id) {
+                node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+                node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+                node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+                node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp);
+                node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp);
+                result.bpcData.push({
+                    tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price,
+                    contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+                    positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp,
+                });
+            }
+            const scDetail = stageChange.filter(x => { return x.lid === node.id; });
+            for (const scd of scDetail) {
+                result.scData.push({ id: scd.id, unit_price: node.unit_price });
+            }
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData);
+        await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id });
+        if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData);
+        if (result.scData.length > 0) await transaction.updateRows(this.ctx.service.stageChange.tableName, result.scData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0;
+        for (const bpc of result.bpcData) {
+            contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, bpc.pc_tp);
+            positive_qc_pc_tp = helper.add(positive_qc_pc_tp, bpc.positive_qc_pc_tp);
+            negative_qc_pc_tp = helper.add(negative_qc_pc_tp, bpc.negative_qc_pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName,
+            { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+
+    /**
+     * 重算工程变更(调整单价)
+     * @param {Object} change - 工程变更
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcChange(change, transaction) {
+        const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: change.cid } });
+        const updateBills = [];
+        let total_price = 0, positive_tp = 0, negative_tp = 0;
+        for (const b of changeBills) {
+            const p = this.findPrice(b.code, b.name, b.unit, b.unit_price);
+            let bills_tp;
+            if (p) {
+                updateBills.push({ id: b.id, unit_price: p.new_price });
+                bills_tp = this.ctx.helper.mul(p.new_price, b.spamount, change.tp_decimal);
+            } else {
+                bills_tp = this.ctx.helper.mul(b.unit_price, b.spamount, change.tp_decimal);
+            }
+            total_price = this.ctx.helper.add(total_price, bills_tp);
+            if (b.spamount >= 0) {
+                positive_tp = this.ctx.helper.add(positive_tp, bills_tp);
+            } else {
+                negative_tp = this.ctx.helper.add(negative_tp, bills_tp);
+            }
+        }
+        if (updateBills.length > 0) {
+            await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateBills);
+            await transaction.update(this.ctx.service.change.tableName, { total_price, positive_tp, negative_tp }, { where: { cid: change.cid } });
+        }
+    }
+    /**
+     * 重算标段下所有工程变更(调整单价)
+     * @param {Number} tid - 标段id
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcAllChanges(tid, transaction) {
+        const change = await this.ctx.service.change.getAllDataByCondition({ where: { tid, valid: 1 } });
+        if (change.length === 0) return;
+        for (const c of change) {
+            await this.calcChange(c, transaction);
+        }
+    }
+    async _calcStage(stage, bills, transaction) {
+        // 无单价变更不执行
+        if (this.price.length === 0) return;
+
+        const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
+        const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } });
+
+        // 加载树结构
+        this.ctx.helper.assignRelaData(bills, [
+            { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'positive_qc_qty', 'negative_qc_qty', 'times', 'order', 'postil'], prefix: 'cur_', relaId: 'lid' },
+            { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' },
+        ]);
+        const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] });
+        billsTree.loadDatas(bills);
+        const stageChange = await this.ctx.service.stageChange.getFinalStageData(stage.tid, stage.id);
+
+        // 计算 insertBills/updateBills billsPriceChange StageChange
+        const result = { ibData: [], ubData: [], bpcData: [], scData: [] };
+        const helper = this.ctx.helper;
+        const decimal = this.ctx.tender.info.decimal;
+        const said = this.ctx.session.sessionUser.accountId;
+        billsTree.calculateAll(node => {
+            if (node.children && node.children.length > 0) return;
+            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            if (!priceDiff) return;
+            node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+            node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+            node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
+            node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp);
+            node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp);
+            if (node.cur_id) {
+                node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
+                node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
+                node.cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp);
+                node.cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp);
+                if (node.cur_times === stage.times && node.cur_order === 0) {
+                    result.ubData.push({
+                        id: node.cur_id,
+                        contract_tp: node.cur_contract_tp, qc_tp: node.cur_qc_tp,
+                        positive_qc_tp: node.cur_positive_qc_tp, negative_qc_tp: node.cur_negative_qc_tp,
+                    });
+                } else {
+                    result.ibData.push({
+                        tid: stage.tid, sid: stage.id, said,
+                        lid: node.id, times: stage.times, order: 0,
+                        contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp,
+                        qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp,
+                        positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: node.cur_positive_qc_tp,
+                        negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: node.cur_negative_qc_tp,
+                        postil: node.cur_postil,
+                    });
+                }
+            }
+            if (node.pre_id) {
+                result.bpcData.push({
+                    tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price,
+                    contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
+                    positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp,
+                });
+            }
+            const scDetail = stageChange.filter(x => { return x.lid === node.id; });
+            for (const scd of scDetail) {
+                result.scData.push({ id: scd.id, unit_price: node.unit_price });
+            }
+        });
+        if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData);
+        if (result.ubData.length > 0) await transaction.updateRows(this.ctx.service.stageBills.tableName, result.ubData);
+        await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id });
+        if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData);
+        if (result.scData.length > 0) await transaction.updateRows(this.ctx.service.stageChange.tableName, result.scData);
+
+        let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0;
+        for (const bpc of result.bpcData) {
+            contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp);
+            qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp);
+            pc_tp = helper.add(pc_tp, bpc.pc_tp);
+            positive_qc_pc_tp = helper.add(positive_qc_pc_tp, bpc.positive_qc_pc_tp);
+            negative_qc_pc_tp = helper.add(negative_qc_pc_tp, bpc.negative_qc_pc_tp);
+        }
+        await transaction.update(this.ctx.service.stage.tableName,
+            { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() });
+    }
+    /**
+     * 计算修订台账
+     * @param {Object}revise - 最新一次台账修订(此处不检查)
+     * @param {Object} transaction - 事务 (无则非事务提交)
+     */
+    async calcReviseLedger(revise, transaction) {
+        const xmj = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.bills_file);
+        xmj.forEach(x => {
+            delete x.is_tp;
+            delete x.gxby_status;
+            delete x.gxby_limit;
+            delete x.gxby_ratio;
+            delete x.gxby_url;
+            delete x.dagl_status;
+            delete x.dagl_limit;
+            delete x.dagl_ratio;
+            delete x.dagl_url;
+        });
+        const pos = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.pos_file);
+        pos.forEach(p => {
+            p.in_time = new Date(p.in_time);
+            delete p.gxby_status;
+            delete p.gxby_limit;
+            delete p.gxby_ratio;
+            delete p.gxby_url;
+            delete p.dagl_status;
+            delete p.dagl_limit;
+            delete p.dagl_ratio;
+            delete p.dagl_url;
+        });
+        await transaction.delete(this.ctx.service.ledger.tableName, { tender_id: revise.tid });
+        if (xmj.length > 0) await transaction.insert(this.ctx.service.ledger.tableName, xmj);
+        await transaction.delete(this.ctx.service.pos.tableName, { tid: revise.tid });
+        if (pos.length > 0) await transaction.insert(this.ctx.service.pos.tableName, pos);
+
+        // 应用到未审完成期
+        const latestStage = await this.ctx.service.stage.getLastestStage(revise.tid, true);
+        if (latestStage && latestStage.status !== audit.stage.status.checked) await this._calcStage(latestStage, xmj, transaction);
+    }
+    async calcRevise(revise, transaction) {
+        if (revise.tid !== this.ctx.tender.id) throw '数据错误';
+
+        this.price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } });
+        await this.calcReviseLedger(revise, transaction);
+        // 引用到所有工程变更
+        await this.calcAllChanges(revise.tid, transaction);
+    }
+}
+
+module.exports = revisePriceCalc;

+ 140 - 58
app/lib/rm/material.js

@@ -150,24 +150,55 @@ class ReportMemoryMaterial {
             orders: [['order', 'desc']],
         });
         if (materials.length > 0) {
-            let result;
+            let result, material;
             if (materials[0].order === material_order) {
-                result = await this.ctx.service.materialBills.getAllDataByCondition({
-                    where: {tid: tender_id}
-                });
+                material = materials[0];
+                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' +
+                        `  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 {
                 const material = this.ctx.helper._.find(materials, {order: material_order});
                 if (!material) return [];
 
-                const sql = 'SELECT mb.id, mb.tid, mb.mid, mb.order, 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' +
-                    '  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 = ?';
-                result = await this.ctx.app.mysql.query(sql, [tender_id, material.id]);
+                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' +
+                        `  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' +
+                        '  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);
             return result;
@@ -233,6 +264,62 @@ class ReportMemoryMaterial {
         }
     }
 
+    async _getMaterialStageGatherBills(tender_id, stage_id, stage_order, stageSelf) {
+        const decimal = this.materialGatherBase.decimal;
+        const billsData = this.ctx.helper.clone(this.materialGatherBase.billsData);
+        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'], prefix: '', relaId: 'lid' },
+        ]);
+        const billsTree = this._getNewBillsTree();
+        billsTree.loadDatas(billsData);
+        billsTree.calculateAll();
+
+        const posData = this.ctx.helper.clone(this.materialGatherBase.posData);
+        const curStage = await this.ctx.service.stagePos.getStagesData(tender_id, stage_id);
+        this.ctx.helper.assignRelaData(posData, [
+            { data: curStage, fields: ['contract_qty', 'qc_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;
+        const materialNotJoin = this.materialGatherBase.materialNotJoin;
+
+        const helper = this.ctx.helper;
+        for (const g of gatherUtil.gclList) {
+            g.sid = stage_id;
+            g.sorder = stage_order;
+            g.jiacha = 0;
+            for (const x of g.leafXmjs) {
+                x.sid = stage_id;
+                x.sorder = stage_order;
+                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;
+                if (mnj) continue;
+                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);
+                });
+                for (const l of list) {
+                    x.jiacha = helper.add(x.jiacha, helper.mul(helper.mul(x.gather_qty, l.quantity), l.m_spread));
+                }
+                x.jiacha = helper.round(x.jiacha, decimal.tp);
+                g.jiacha = helper.add(g.jiacha, x.jiacha);
+            }
+        }
+        return [gatherUtil.gclList, gatherUtil.leafXmjs ];
+    }
+
     async getMaterialGatherBills(tender_id, material_order) {
         const materials = await this.ctx.service.material.getAllDataByCondition({
             where: { tid: tender_id },
@@ -241,60 +328,55 @@ class ReportMemoryMaterial {
         if (materials.length === 0) return {};
 
         const material = await this.ctx.service.material.getDataByCondition({ tid: tender_id, order: material_order });
-        const decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
+        this.materialGatherBase = {};
+        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 } });
 
-            const billsData = await this.ctx.service.ledger.getData(tender_id);
-            const curStageBills = await this.ctx.service.stageBills.getStagesData(tender_id, material.stage_id);
-            this.ctx.helper.assignRelaData(billsData, [
-                { data: curStageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
-            ]);
-            const billsTree = this._getNewBillsTree();
-            billsTree.loadDatas(billsData);
-            billsTree.calculateAll();
-
-            const posData = await this.ctx.service.pos.getPosData({ tid: tender_id });
-            const curStage = await this.ctx.service.stagePos.getStagesData(tender_id, material.stage_id);
-            this.ctx.helper.assignRelaData(posData, [
-                { data: curStage, fields: ['contract_qty', 'qc_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 = 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);
-            const materialNotJoin = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { mid: material.id } });
-
-            const helper = this.ctx.helper;
-            for (const g of gatherUtil.gclList) {
-                g.jiacha = 0;
-                for (const x of g.leafXmjs) {
-                    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);
-                    });
-                    if (mnj) continue;
-                    const list = materialGl.filter(g => {
-                        return g.gcl_id === x.org_gcl_id && g.xmj_id === x.id && (x.mx_id && x.mx_id !== x.id ? x.mx_id === g.mx_id : true);
-                    });
-                    for (const l of list) {
-                        x.jiacha = helper.add(x.jiacha, helper.mul(helper.mul(x.gather_qty, l.quantity), l.m_spread));
-                    }
-                    x.jiacha = helper.round(x.jiacha, decimal.tp);
-                    g.jiacha = helper.add(g.jiacha, x.jiacha);
+            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);
+                    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: gatherUtil.gclList, mem_material_gather_xmj: gatherUtil.leafXmjs, mem_material_gather_gl: materialGl };
+            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.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;

+ 37 - 4
app/lib/spread_setting.js

@@ -13,6 +13,8 @@ const _ = require('lodash');
 const measureType = require('../const/tender').measureType;
 
 const getCtxTender = async function (ctx, tid) {
+    if (tid === ctx.tender.id) return ctx.tender;
+
     const tender = { id: tid };
     tender.data = await ctx.service.tender.getDataById(tid);
     tender.info = await ctx.service.tenderInfo.getTenderInfo(tid);
@@ -25,6 +27,15 @@ function removeFieldCols(setting, cols) {
     });
 };
 
+function hiddenFieldCols(setting, cols) {
+    setting.cols.forEach(x => {
+        if (cols.indexOf(x.field) >= 0) {
+            x.defaultVisible = false;
+            x.visible = false;
+        }
+    });
+};
+
 function refreshSpreadShow(sjsRela, sjsSetting) {
     const sjsSettings = sjsSetting instanceof Array ? sjsSetting : [sjsSetting];
     for (const field of sjsRela) {
@@ -54,16 +65,16 @@ const getLedgerSpreadSetting = async function(ctx, tid, readOnly) {
     const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
     ledger.readOnly = readOnly;
     pos.readOnly = readOnly;
-    if (tender.data.measure_type === measureType.tz.value) removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
+    if (!tender.info.display.ledger.deal) removeFieldCols(ledger, spreadConst.filterCols.dealCols);
     if (!tender.info.display.ledger.dgnQty) removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
 
-    const sjsRela = await ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+    const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
     refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
 
     return [ledger, pos];
 };
 
-const getStageSpreadSetting = async function (ctx, tid, readOnly) {
+const getStageSpreadSetting = async function (ctx, tid, readOnly, funInfo) {
     const tender = tid ? await getCtxTender(ctx, tid) : ctx.tender;
 
     const stageSetting = tender.data.measure_type === measureType.tz.value
@@ -85,16 +96,38 @@ const getStageSpreadSetting = async function (ctx, tid, readOnly) {
         removeFieldCols(ledger, spreadConst.filterCols.thirdPartyCols.dagl);
         removeFieldCols(pos, spreadConst.filterCols.thirdPartyCols.dagl);
     }
+    if (!funInfo.minusNoValue || !tender.info.fun_rela.stage_change.minusNoValue) {
+        hiddenFieldCols(ledger, spreadConst.filterCols.minusNoValueCols);
+        hiddenFieldCols(pos, spreadConst.filterCols.minusNoValueCols);
+    }
+    if (!tender.info.display.stage.priceDiff) hiddenFieldCols(ledger, spreadConst.filterCols.priceDiffCols);
     ledger.readOnly = readOnly;
     pos.readOnly = readOnly;
 
-    const sjsRela = await ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+    const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
     refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
 
     return [ledger, pos];
 };
 
+const getStageGatherSpreadSetting = async function (ctx, tid) {
+    const tender = tid ? await getCtxTender(ctx, tid) : ctx.tender;
+    const gcl = JSON.parse(JSON.stringify(spreadConst.stageGather.gcl));
+    const leafXmj = JSON.parse(JSON.stringify(spreadConst.stageGather.leafXmj));
+
+    // if (tender.data.measure_type === measureType.tz.value && !tender.info.display.ledger.deal)
+    //     removeFieldCols(gcl, spreadConst.filterCols.dealCols);
+    if (!tender.info.display.stage.priceDiff) {
+        removeFieldCols(gcl, spreadConst.filterCols.priceDiffCols);
+        hiddenFieldCols(leafXmj, spreadConst.filterCols.priceDiffCols);
+    }
+
+    if (tender.data.measure_type === measureType.gcl.value) removeFieldCols(leafXmj, ['quantity']);
+    return [gcl, leafXmj];
+};
+
 module.exports = {
     getLedgerSpreadSetting,
     getStageSpreadSetting,
+    getStageGatherSpreadSetting,
 };

+ 33 - 18
app/lib/stage_im.js

@@ -27,7 +27,7 @@ class StageIm {
             rootId: -1,
             keys: ['id', 'tender_id', 'ledger_id'],
             stageId: 'id',
-            calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp'],
+            calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp'],
             calc(node) {
                 if (node.children && node.children.length === 0) {
                     node.pre_gather_qty = self.ctx.helper.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -38,9 +38,9 @@ class StageIm {
                     node.end_gather_qty = self.ctx.helper.add(node.pre_gather_qty, node.gather_qty);
                 }
                 node.pre_gather_tp = self.ctx.helper.add(node.pre_contract_tp, node.pre_qc_tp);
-                node.gather_tp = self.ctx.helper.add(node.contract_tp, node.qc_tp);
-                node.end_contract_tp = self.ctx.helper.add(node.pre_contract_tp, node.contract_tp);
-                node.end_qc_tp = self.ctx.helper.add(node.pre_qc_tp, node.qc_tp);
+                node.gather_tp = self.ctx.helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+                node.end_contract_tp = self.ctx.helper.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+                node.end_qc_tp = self.ctx.helper.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
                 node.end_gather_tp = self.ctx.helper.add(node.pre_gather_tp, node.gather_tp);
             },
         });
@@ -76,10 +76,12 @@ class StageIm {
                 this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder)
             : await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
         const preStage = this.ctx.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
+        const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
 
         this.ctx.helper.assignRelaData(billsData, [
             { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: '', relaId: 'lid' },
             { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'pre_', relaId: 'lid' },
+            { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
         ]);
         this.billsTree.loadDatas(billsData);
         this.billsTree.calculateAll();
@@ -308,8 +310,8 @@ class StageIm {
                         (!im.code || im.code === d.code) &&
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
-                        (!im.pid || im.pid === d.pid) &&
-                        (!im.pos_name || im.pos_name === d.pos_name);
+                        ((!im.pid && !d.pid) || im.pid === d.pid) &&
+                        ((!im.pos_name && !d.pos_name) || im.pos_name === d.pos_name);
                 });
                 break;
             case imType.zl.value:
@@ -319,8 +321,8 @@ class StageIm {
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
                         self.ctx.helper.checkZero(self.ctx.helper.sub(im[self.up_field], d[self.up_field])) &&
-                        (!im.pid || im.pid === d.pid) &&
-                        (!im.pos_name || im.pos_name === d.pos_name);
+                        ((!im.pid && !d.pid) || im.pid === d.pid) &&
+                        ((!im.pos_name && !d.pos_name) || im.pos_name === d.pos_name);
                 });
                 break;
             case imType.bw.value:
@@ -330,8 +332,8 @@ class StageIm {
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
                         self.ctx.helper.checkZero(self.ctx.helper.sub(im[self.up_field], d[self.up_field])) &&
-                        (!im.pid || im.pid === d.pid) &&
-                        (!im.pos_name || im.pos_name === d.pos_name);
+                        ((!im.pid && !d.pid) || im.pid === d.pid) &&
+                        ((!im.pos_name && !d.pos_name) || im.pos_name === d.pos_name);
                 });
                 break;
             case imType.bb.value:
@@ -339,8 +341,8 @@ class StageIm {
                     return im.lid === d.lid &&
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
-                        (!im.pid || im.pid === d.pid) &&
-                        (!im.pos_name || im.pos_name === d.pos_name);
+                        ((!im.pid && !d.pid) || im.pid === d.pid) &&
+                        ((!im.pos_name && !d.pos_name) || im.pos_name === d.pos_name);
                 });
                 break;
         }
@@ -364,10 +366,10 @@ class StageIm {
         if (im.calc_memo !== undefined && im.calc_memo !== null && im.calc_memo !== '') return;
 
         if (im.leafXmjs && im.leafXmjs.length > 0) {
-            const memo = ['本期计量:' + (im.jl ? 0 : im.jl) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit];
+            const memo = ['本期计量:' + (im.jl || 0) + (im.qc_minus_jl ? (` (不计价 ${im.qc_minus_jl}) `) : ' ') + im.unit];
             for (const lx of im.leafXmjs) {
                 for (const p of lx.pos) {
-                    memo.push(p.name + ':' + p.jl + (p.qc_minus_jl ? (` (不计价 ${p.qc_minus_jl}) `) : ' ') + im.unit);
+                    memo.push(p.name + ':' + (p.jl || 0) + (p.qc_minus_jl ? (` (不计价 ${p.qc_minus_jl}) `) : ' ') + im.unit);
                 }
             }
             im.calc_memo = memo.join('\n');
@@ -377,10 +379,10 @@ class StageIm {
                 if (b.pos && b.pos.length > 0) {
                     memo.push('清单' + (i + 1) + ':' + b.b_code + ' ' + b.name);
                     for (const p of b.pos) {
-                        memo.push(p.name + ':' + p.jl + (p.qc_minus_jl ? (` (不计价 ${p.qc_minus_jl}) `) : ' ') + b.unit);
+                        memo.push(p.name + ':' + (p.jl || 0) + (p.qc_minus_jl ? (` (不计价 ${p.qc_minus_jl}) `) : ' ') + b.unit);
                     }
                 } else {
-                    memo.push('清单' + (i+1) + ':' + b.b_code + ' ' + b.name + ':' + (b.jl ? 0 : b.jl) + (b.qc_minus_jl ? (` (不计价 ${b.qc_minus_jl}) `) : ' ') + b.unit);
+                    memo.push('清单' + (i+1) + ':' + b.b_code + ' ' + b.name + ':' + (b.jl || 0) + (b.qc_minus_jl ? (` (不计价 ${b.qc_minus_jl}) `) : ' ') + b.unit);
                 }
             }
             im.calc_memo = memo.join('\n');
@@ -573,7 +575,7 @@ class StageIm {
                 helper.checkZero(helper.sub(bills.unit_price, x.unit_price));
         });
         if (!gcl) {
-            gcl = {b_code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price};
+            gcl = { b_code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price, org_price: bills.org_price };
             im.gclBills.push(gcl);
         }
         if (pos) {
@@ -639,6 +641,7 @@ class StageIm {
         }
     }
     _calculateBwBillsIm(im) {
+        const helper = this.ctx.helper;
         const tp_decimal = this.ctx.tender.info.decimal.tp;
         im.contract_jl = 0;
         im.qc_jl = 0;
@@ -650,8 +653,20 @@ class StageIm {
             im.qc_jl = this.ctx.helper.add(im.qc_jl, this.ctx.helper.mul(b.qc_jl, b.unit_price, tp_decimal));
             im.pre_qc_jl = this.ctx.helper.add(im.pre_qc_jl, this.ctx.helper.mul(b.pre_qc_jl, b.unit_price, tp_decimal));
             im.end_qc_jl = this.ctx.helper.add(im.end_qc_jl, this.ctx.helper.mul(b.end_qc_jl, b.unit_price, tp_decimal));
+
+            const priceDiff = b.org_price ? helper.sub(b.unit_price, b.org_price) : 0;
+            if (priceDiff) {
+                const contract_pc_tp = helper.sub(helper.mul(b.unit_price, b.pre_contract_jl, tp_decimal), helper.mul(b.org_price, b.pre_contract_jl, tp_decimal));
+                const qc_pc_tp = helper.sub(helper.mul(b.unit_price, b.pre_qc_jl, tp_decimal), helper.mul(b.org_price, b.pre_qc_jl, tp_decimal));
+                const pc_tp = helper.add(contract_pc_tp, qc_pc_tp);
+                im.contract_pc_jl = helper.add(contract_pc_tp, im.contract_pc_jl);
+                im.qc_pc_jl = helper.add(qc_pc_tp, im.qc_pc_jl);
+                im.pc_jl = helper.add(pc_tp, im.pc_jl);
+                im.end_contract_jl = helper.add(contract_pc_tp, im.end_contract_jl);
+                im.end_qc_jl = helper.add(qc_pc_tp, im.end_qc_jl);
+            }
         }
-        im.jl = this.ctx.helper.add(im.contract_jl, im.qc_jl);
+        im.jl = this.ctx.helper.sum([im.contract_jl, im.qc_jl, im.pc_jl]);
         im.pre_jl = this.ctx.helper.add(im.pre_contract_jl, im.pre_qc_jl);
         im.end_jl = this.ctx.helper.add(im.end_contract_jl, im.end_qc_jl);
     }

+ 5 - 3
app/lib/tender_info.js

@@ -46,9 +46,11 @@ class TenderInfo {
                 : await this.ctx.service.stageBills.getLastestStageData2(this.tender.id, this.stage.id);
 
             const preStage = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.tender, this.stage.order - 1) : [];
+            const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.stage.id } });
             this.ctx.helper.assignRelaData(billsData, [
                 { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
                 { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
             ]);
         }
         return billsData;
@@ -83,8 +85,8 @@ class TenderInfo {
             const chapter = this._findGclChapter(gclChapter, b, 'b_code');
             if (!chapter) continue;
             chapter.total_price = this.ctx.helper.add(chapter.total_price, b.total_price);
-            chapter.contract_tp = this.ctx.helper.add(chapter.contract_tp, b.contract_tp);
-            chapter.qc_tp = this.ctx.helper.add(chapter.qc_tp, b.qc_tp);
+            chapter.contract_tp = this.ctx.helper.sum([chapter.contract_tp, b.contract_tp, b.contract_pc_tp]);
+            chapter.qc_tp = this.ctx.helper.sum([chapter.qc_tp, b.qc_tp, b.qc_pc_tp]);
             chapter.pre_contract_tp = this.ctx.helper.add(chapter.pre_contract_tp, b.pre_contract_tp);
             chapter.pre_qc_tp = this.ctx.helper.add(chapter.pre_qc_tp, b.pre_qc_tp);
         }
@@ -103,4 +105,4 @@ class TenderInfo {
     }
 }
 
-module.exports = TenderInfo;
+module.exports = TenderInfo;

+ 3 - 1
app/middleware/material_check.js

@@ -121,7 +121,9 @@ module.exports = options => {
                 tid: this.tender.id,
             });
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
-            material.readOnly = !((material.status === status.uncheck || material.status === status.checkNo) && accountId === material.user_id);
+            material.readOnly = !(((material.status === status.uncheck || material.status === status.checkNo) && accountId === material.user_id)
+                || (this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId));
+            material.editForAudit = this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId;
             material.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
             this.material = material;
             // 根据状态判断是否需要更新审批人列表

+ 1 - 0
app/middleware/revise_check.js

@@ -37,6 +37,7 @@ module.exports = options => {
             }
             revise.readOnly = revise.uid !== this.session.sessionUser.accountId ||
                 revise.status === auditConst.status.checking || revise.status === auditConst.status.checked;
+            revise.priceCount = yield this.service.revisePrice.count({ rid: revise.id });
             this.revise = revise;
             yield next;
         } catch (err) {

+ 1 - 0
app/middleware/session_auth.js

@@ -37,6 +37,7 @@ module.exports = options => {
             this.session.sessionProject.custom = projectData.custom;
             this.session.sessionProject.dataCollect = projectData.data_collect;
             this.session.sessionProject.customType = projectData.customType;
+            this.session.sessionProject.funSet = projectData.fun_set ? JSON.parse(projectData.fun_set) : null;
             // 判断是否有权限查看决策大屏
             let showDataCollect = 0;
             if (projectData.data_collect) {

+ 49 - 21
app/public/css/main.css

@@ -87,7 +87,7 @@ font-size: .875rem;
 }
 .custom-control-warning-input:checked ~ .custom-control-warning-label::before{
   border-color:#da9500 ;
-  background-color:#da9500 
+  background-color:#da9500
 }
 .custom-control-warning-label{
   color:#da9500;
@@ -192,26 +192,30 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 .in-4{padding-left:63px!important}
 .in-5{padding-left:84px!important}
 .in-6{padding-left:105px!important}
+.in-7{padding-left:126px!important}
+.in-8{padding-left:147px!important}
+.in-9{padding-left:168px!important}
+.in-10{padding-left:189px!important}
 /*滚动条*/
 /* 滚动条 */
 /*水平滚动条的样式*/
-/*::-webkit-scrollbar-thumb:horizontal { 
+/*::-webkit-scrollbar-thumb:horizontal {
 	width: 5px;
 	background-color: #e9ecef;
 	-webkit-border-radius: 0;
 }*/
 /*滚动条的背景颜色,滚动条的圆角宽度*/
 /*::-webkit-scrollbar-track-piece {
-	background-color: #efefef; 
-	-webkit-border-radius: 0; 
+	background-color: #efefef;
+	-webkit-border-radius: 0;
 }*/
 /*滚动条的宽度,滚动条的高度*/
 /*::-webkit-scrollbar {
-	width: 14px; 
-	height: 14px; 
+	width: 14px;
+	height: 14px;
 }*/
 /*垂直滚动条的样式*/
-/*::-webkit-scrollbar-thumb:vertical { 
+/*::-webkit-scrollbar-thumb:vertical {
 	height: 50px;
 	background-color: #e9ecef;
 	-webkit-border-radius: 0;
@@ -220,7 +224,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 	border: 1px solid #ced4da;
 }*/
 /*滚动条的hover样式*/
-/*::-webkit-scrollbar-thumb:hover { 
+/*::-webkit-scrollbar-thumb:hover {
 	height: 50px;
 	background-color: #ced4da;
 	-webkit-border-radius: 0;
@@ -538,7 +542,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
     box-sizing: border-box;
 }
 .panel-sidebar .scrollbar-auto {
-    height: calc(100vh - 100px);
+    height: calc(100vh - 50px);
     width: 100%;
     overflow-y: auto;
     position: static;
@@ -874,7 +878,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
   font-size: 14px
 }
 .bd-toc {
-  
+
     position: sticky;
     top:3rem;
     height: calc(100vh - 10rem);
@@ -1032,7 +1036,7 @@ body{
   line-height: 30px;
 }
 .panel-title > .title-main .btn.pull-right {
-    margin: 5px 0 0 0 
+    margin: 5px 0 0 0
 }
 .panel-content{
   padding-top:35px;
@@ -1162,9 +1166,33 @@ legend {
 .min-side .side-menu{
   padding-bottom:10px;
 }
+.side-show {
+  position: absolute;
+  z-index:8;
+  right:0;
+  top: 0;
+  width: 15px;
+  height: calc(100vh);
+}
 .side-fold {
-  right:50px;
-  bottom:5px;
+  /*right:50px;*/
+  /*bottom:5px;*/
+  display:none;
+  z-index:9;
+  border-radius:2px;
+  right:0;
+  bottom:50%;
+  width:6px;
+  height:60px;
+  line-height:60px;
+  text-align:center;
+  cursor:pointer;
+  background-color: #2E6BE5;
+  color: #fff;
+}
+.side-fold i{
+  font-size: 20px;
+  vertical-align: text-bottom;
 }
 .side-fold a{
   font-size:24px;
@@ -1251,7 +1279,7 @@ a.maintain-icon .fa{
     }
 }
 
-a.maintain-icon:hover .fa{ 
+a.maintain-icon:hover .fa{
     animation-iteration-count:0
 }
 /*审批列表*/
@@ -1374,10 +1402,10 @@ overflow-y: auto;
   position: relative;
 }
 .circle{
-  width: 62px; 
+  width: 62px;
   height: 62px;
   border-radius: 50%;
-  background: none; 
+  background: none;
   border: 4px solid #D7B014;
 }
 .circle-num{
@@ -1918,8 +1946,8 @@ overflow-y: auto;
   font-size: 36px;
 }
 .list-text-vertical{
-  overflow:hidden; 
-  text-overflow:ellipsis; 
+  overflow:hidden;
+  text-overflow:ellipsis;
   white-space:nowrap;
 }
 .about-text i{
@@ -1943,12 +1971,12 @@ overflow-y: auto;
 }
 /*@media (min-width: 768px){
   .weixin-erweima img{
-    width:90%; 
+    width:90%;
     height:auto;
   }
 }*/
 .weixin-erweima img{
-  width:75%; 
+  width:75%;
   height:auto;
 }
 .weixin-erweima span{
@@ -2017,4 +2045,4 @@ animation:shake 1s .2s ease both;}
 }
 .card-gk-active .card-gk-bottom{
   display: inline-block;
-}
+}

+ 50 - 9
app/public/js/change_information.js

@@ -87,15 +87,16 @@ $(document).ready(() => {
 
     // 计算最新的变更总额和change的total_price是否一致,不一致则更新
     if (changeStatus !== auditConst.status.checked) {
-        let new_tp = 0;
-        for (const c of changeList) {
-            new_tp = ZhCalc.add(new_tp, ZhCalc.round(ZhCalc.mul(ZhCalc.round(c.spamount, findDecimal(c.unit)), ZhCalc.round(c.unit_price, unitPriceUnit)), totalPriceUnit));
-        }
-        console.log(changeTp, new_tp);
-        if (changeTp !== new_tp) {
-            postData(window.location.pathname + '/save', { type:'update_tp', updateData: new_tp }, function (result) {
-            });
-        }
+        calcChangePrice();
+        // let new_tp = 0;
+        // for (const c of changeList) {
+        //     new_tp = ZhCalc.add(new_tp, ZhCalc.round(ZhCalc.mul(ZhCalc.round(c.spamount, findDecimal(c.unit)), ZhCalc.round(c.unit_price, unitPriceUnit)), totalPriceUnit));
+        // }
+        // console.log(changeTp, new_tp);
+        // if (changeTp !== new_tp) {
+        //     postData(window.location.pathname + '/save', { type:'update_tp', updateData: new_tp }, function (result) {
+        //     });
+        // }
     }
 
     //tab change
@@ -496,6 +497,46 @@ $(document).ready(() => {
         xmjSpread.refresh();
     }
 });
+
+function calcChangePrice() {
+    let positive_tp = 0;
+    let negative_tp = 0;
+    let new_tp = 0;
+    for (const c of changeList) {
+        if (c.spamount) {
+            const price = ZhCalc.round(ZhCalc.mul(ZhCalc.round(c.spamount, findDecimal(c.unit)), ZhCalc.round(c.unit_price, unitPriceUnit)), totalPriceUnit);
+            new_tp = ZhCalc.add(new_tp, price);
+            if (price >= 0) {
+                positive_tp = ZhCalc.add(positive_tp, price);
+            } else {
+                negative_tp = ZhCalc.add(negative_tp, price);
+            }
+        }
+    }
+    const updateTpList = {};
+    let updateFlag = false;
+    if (changeTp !== new_tp) {
+        updateTpList.total_price = new_tp;
+        updateFlag = true;
+    }
+    if (positive_tp !== changePp) {
+        updateTpList.positive_tp = positive_tp;
+        updateFlag = true;
+    }
+    if (negative_tp !== changeNp) {
+        updateTpList.negative_tp = negative_tp;
+        updateFlag = true;
+    }
+    if (updateFlag) {
+        console.log(updateTpList);
+        postData(window.location.pathname + '/save', { type:'update_tp', updateData: updateTpList }, function () {
+            changePp = positive_tp;
+            changeNp = negative_tp;
+            changeTp = new_tp;
+        });
+    }
+}
+
 function findDecimal(unit) {
     let value = precision.other.value;
     const changeUnits = precision;

+ 1 - 2
app/public/js/change_information_set.js

@@ -1128,7 +1128,6 @@ $(document).ready(() => {
             $('#table-list-select tr').attr('data-charu', '');
             $('#addlist').modal('hide');
         });
-
     });
 
     // 选中input所有值
@@ -1841,7 +1840,7 @@ function remakeChangeSpread(cOrder = changeOrder) {
                 mx_id,
             };
             const radionInfo = changeList.find(function (info) {
-                return info.code === code && (info.lid == lid || parseInt(info.lid) === parseInt(lindex)) && gcl_id == info.gcl_id && (info.bwmx === bwmx || info.bwmx === xmj_jldy) && parseInt(info.oamount) === parseInt(oamount);
+                return info.code === code && (info.lid == lid || parseInt(info.lid) === parseInt(lindex)) && gcl_id == info.gcl_id && (info.bwmx === bwmx || (info.bwmx === xmj_jldy && info.mx_id && info.mx_id === mx_id)) && parseInt(info.oamount) === parseInt(oamount);
             });
             if (radionInfo) {
                 trlist.camount = radionInfo.camount;

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

@@ -191,7 +191,7 @@ $(document).ready(() => {
         treeSetting.calcFields.push('deal_tp');
     }
     treeSetting.calcFun = function (node) {
-        if (node && node.b_code) {
+        if (node && node.b_code && isTz) {
             const posData = pos.getLedgerPos(node.id) || [];
             if (posData.length > 0) {
                 let sgfh_qty = 0;

+ 2 - 1
app/public/js/component/menu.js

@@ -1,7 +1,7 @@
 'use strict';
 // 导航栏
 Vue.component('nav-menu', {
-    props: ['title', 'url', 'active', 'tclass', 'icon', 'ml', 'hint'],
+    props: ['title', 'url', 'active', 'tclass', 'icon', 'ml', 'hint', 'hinticon'],
     template: '' +
     '<div class="nav-box" data-toggle="tooltip" data-placement="right" :data-original-title="[ hint ]">' +
         '<ul class="nav-list list-unstyled">' +
@@ -9,6 +9,7 @@ Vue.component('nav-menu', {
                 '<a :href="url" :class="[ tclass ]">' +
                     '<i v-if="icon" class="fa" :class="icon"></i>' +
                     '<span :class="[ \'ml-\' + ml ]">{{ title }}</span>' +
+                    '<i v-if="hinticon" class="fa text-danger ml-2" :class="hinticon"></i>' +
                 '</a>' +
             '</li>' +
         '</ul>' +

+ 28 - 13
app/public/js/gcl_gather.js

@@ -11,11 +11,11 @@
 
 const gclGatherModel = (function () {
     // 需要汇总计算的字段
-    const ledgerGatherFields = ['quantity', 'total_price', 'deal_qty', 'deal_tp',
-        'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty',
+    let ledgerGatherFields = ['quantity', 'total_price', 'deal_qty', 'deal_tp',
+        'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
         'pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_qc_minus_qty',
         'end_contract_qty', 'end_contract_tp', 'end_qc_qty', 'end_qc_tp', 'end_qc_minus_qty'];
-    const posGatherFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty','qc_minus_qty',
+    let posGatherFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty','qc_minus_qty',
         'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'pre_qc_minus_qty',
         'end_contract_qty', 'end_qc_qty', 'end_gather_qty', 'end_qc_minus_qty'];
     // 初始化 清单树
@@ -28,7 +28,7 @@ const gclGatherModel = (function () {
         keys: ['id', 'tender_id', 'ledger_id'],
         stageId: 'id',
     };
-    gsTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'];
+    gsTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'qc_minus_qty'];
     const gsTree = createNewPathTree('stage', gsTreeSetting);
     // 初始化 部位明细
     const posSetting = {
@@ -46,6 +46,11 @@ const gclGatherModel = (function () {
         tpDecimal = decimal.tp;
     }
 
+    function loadGatherField(ledger, pos) {
+        if (ledger) ledgerGatherFields = ledger;
+        if (pos) posGatherFields = pos;
+    }
+
     /**
      * 将所有数据加载至树结构
      *
@@ -82,8 +87,16 @@ const gclGatherModel = (function () {
 
     function gatherfields(obj, src, fields) {
         if (obj && src) {
-            for (const f of fields) {
-                obj[f] = ZhCalc.add(obj[f], src[f]);
+            if (fields instanceof Array) {
+                for (const f of fields) {
+                    obj[f] = ZhCalc.add(obj[f], src[f]);
+                }
+            } else if (typeof fields === "function") {
+                for (const prop in src) {
+                    if (fields(prop)) {
+                        obj[prop] = ZhCalc.add(obj[prop], src[prop]);
+                    }
+                }
             }
         }
     }
@@ -99,6 +112,7 @@ const gclGatherModel = (function () {
             name: node.name,
             unit: node.unit,
             unit_price: node.unit_price,
+            org_price: node.org_price,
             leafXmjs: [],
         };
         gclList.push(gcl);
@@ -337,7 +351,7 @@ const gclGatherModel = (function () {
             for (const node of change) {
                 node.b_code = node.code;
                 node.quantity = parseFloat(node.samount);
-                node.total_price = ZhCalc.mul(node.quantity, node.unit_price, node.tp_decimal);
+                node.total_price = ZhCalc.mul(node.quantity, node.unit_price, node.tp_decimal || tpDecimal);
                 const gcl = getGclNode(node);
                 gcl.change_bills_qty = ZhCalc.add(gcl.change_bills_qty, node.quantity);
                 gcl.change_bills_tp = ZhCalc.add(gcl.change_bills_tp, node.total_price);
@@ -353,22 +367,22 @@ const gclGatherModel = (function () {
             gcl.end_contract_qty = ZhCalc.add(gcl.pre_contract_qty, gcl.contract_qty);
             gcl.end_qc_qty = ZhCalc.add(gcl.pre_qc_qty, gcl.qc_qty);
             gcl.end_gather_qty = ZhCalc.add(gcl.pre_gather_qty, gcl.gather_qty);
-            gcl.gather_tp = ZhCalc.add(gcl.contract_tp, gcl.qc_tp);
             gcl.end_qc_minus_qty = ZhCalc.add(gcl.pre_qc_minus_qty, gcl.qc_minus_qty);
 
-            gcl.end_contract_tp = ZhCalc.add(gcl.pre_contract_tp, gcl.contract_tp);
-            gcl.end_qc_tp = ZhCalc.add(gcl.pre_qc_tp, gcl.qc_tp);
+            gcl.gather_tp = ZhCalc.sum([gcl.contract_tp, gcl.qc_tp, gcl.pc_tp]);
+            gcl.end_contract_tp = ZhCalc.sum([gcl.pre_contract_tp, gcl.contract_tp, gcl.contract_pc_tp]);
+            gcl.end_qc_tp = ZhCalc.sum([gcl.pre_qc_tp, gcl.qc_tp, gcl.qc_pc_tp]);
             gcl.end_gather_tp = ZhCalc.add(gcl.pre_gather_tp, gcl.gather_tp);
             gcl.dgn_price = ZhCalc.round(ZhCalc.div(gcl.total_price, gcl.dgn_qty1), 2);
             gcl.end_final_qty = ZhCalc.add(gcl.end_qc_qty, gcl.quantity);
             gcl.end_final_tp = ZhCalc.add(gcl.end_qc_tp, gcl.total_price);
             gcl.final_qty = ZhCalc.add(gcl.quantity, gcl.change_bills_qty);
             gcl.final_tp = ZhCalc.add(gcl.total_price, gcl.change_bills_tp);
-            gcl.deal_final_qty = ZhCalc.add(gcl.deal_qty, gcl.change_bills_qty);
-            gcl.deal_final_tp = ZhCalc.add(gcl.deal_tp, gcl.change_bills_tp);
+            gcl.deal_final_qty = ZhCalc.add(gcl.deal_bills_qty, gcl.change_bills_qty);
+            gcl.deal_final_tp = ZhCalc.add(gcl.deal_bills_tp, gcl.change_bills_tp);
             gcl.final_1_qty = ZhCalc.add(gcl.quantity, gcl.end_qc_minus_qty);
             gcl.final_1_tp = ZhCalc.mul(gcl.unit_price, gcl.final_1_qty, tpDecimal);
-            gcl.deal_final_1_qty = ZhCalc.add(gcl.deal_qty, gcl.end_qc_minus_qty);
+            gcl.deal_final_1_qty = ZhCalc.add(gcl.deal_bills_qty, gcl.end_qc_minus_qty);
             gcl.deal_final_1_tp = ZhCalc.add(gcl.unit_price, gcl.deal_final_1_qty, tpDecimal);
             gcl.end_final_1_qty = ZhCalc.add(gcl.final_1_qty, gcl.end_qc_qty);
             gcl.end_final_1_tp = ZhCalc.add(gcl.final_1_tp, gcl.end_qc_tp);
@@ -573,6 +587,7 @@ const gclGatherModel = (function () {
     }
 
     return {
+        loadGatherField,
         loadDecimal,
         loadLedgerData,
         loadPosData,

+ 36 - 2
app/public/js/global.js

@@ -1,6 +1,8 @@
 /*全局自适应高度*/
 /*全局自适应高度*/
 function autoFlashHeight(){
+    const select = $(".scrollbar-auto .active");
+    if (select.length > 0) $(".scrollbar-auto")[0].scrollTop = select[0].offsetTop - 72;
     function getObjHeight(select) {
         return select.length > 0 ? select.height() : 0;
     }
@@ -1055,7 +1057,39 @@ const checkUtils = {
                     : data.end_contract_qty < data.deal_final_1_qty || data.end_contract_qty > 0;
             }
         }
-    }
+    },
+    compareCode(str1, str2, symbol = '-') {
+        if (!str1) {
+            return 1;
+        } else if (!str2) {
+            return -1;
+        }
+
+        function compareSubCode(code1, code2) {
+            if (numReg.test(code1)) {
+                if (numReg.test(code2)) {
+                    return parseInt(code1) - parseInt(code2);
+                } else {
+                    return -1
+                }
+            } else {
+                if (numReg.test(code2)) {
+                    return 1;
+                } else {
+                    return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
+                }
+            }
+        }
+        const numReg = /^[0-9]+$/;
+        const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
+        for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+            const iCompare = compareSubCode(aCodes[i], bCodes[i]);
+            if (iCompare !== 0) {
+                return iCompare;
+            }
+        }
+        return aCodes.length - bCodes.length;
+    },
 };
 
 Number.prototype.format2Str = function (pattern) {
@@ -1165,4 +1199,4 @@ const spreadColor = {
 
 $(document).ready(function () {
   $('.modal').draggable({handle: '.modal-header'});
-});
+});

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

@@ -76,12 +76,13 @@ $(document).ready(function() {
         // markFoldSubKey: window.location.pathname.split('/')[2],
         markExpandKey: 'bills-expand',
         markExpandSubKey: window.location.pathname.split('/')[2],
+        calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
     };
-    if (checkTzMeasureType()) {
-        treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
-    } else {
-        treeSetting.calcFields = ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
-    }
+    // if (checkTzMeasureType()) {
+    //     treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
+    // } else {
+    //     treeSetting.calcFields = ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
+    // }
     treeSetting.calcFun = function (node) {
         node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
     };
@@ -140,6 +141,12 @@ $(document).ready(function() {
         },
     });
 
+    const gclGather = $.cs_gclGather({
+        selector: '#gcl-gather',
+        id: 'gcl-gather',
+        relaSheet: ledgerSpread.getActiveSheet(),
+    });
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -176,6 +183,7 @@ $(document).ready(function() {
             if (checkList) {
                 checkList.spread.refresh();
             }
+            if (gclGather) gclGather.spread.refresh();
         }
     });
 
@@ -1070,16 +1078,16 @@ $(document).ready(function() {
     ledgerSpread.bind(spreadNS.Events.SelectionChanged, treeOperationObj.selectionChanged);
     ledgerSpread.bind(spreadNS.Events.TopRowChanged, treeOperationObj.topRowChanged);
 
+    ledgerSpread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
+        const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
+        SpreadJsObj.Clipboard.setCopyData(copyText);
+    });
     if (!ledgerSpreadSetting.readOnly) {
         ledgerSpread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
             treeOperationObj.refreshOperationValid(info.sheet, info.newSelections);
         });
         ledgerSpread.bind(spreadNS.Events.EditEnded, treeOperationObj.editEnded);
         SpreadJsObj.addDeleteBind(ledgerSpread, treeOperationObj.deletePress);
-        ledgerSpread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
-            const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
-            SpreadJsObj.Clipboard.setCopyData(copyText);
-        });
         ledgerSpread.bind(spreadNS.Events.ClipboardPasting, treeOperationObj.clipboardPasting);
         ledgerSpread.bind(spreadNS.Events.EditStarting, treeOperationObj.editStarting);
         SpreadJsObj.addCutEvents(ledgerSpread, treeOperationObj.cut);
@@ -1677,8 +1685,8 @@ $(document).ready(function() {
             }
         });
         sjsSettingObj.setGridSelectStyle(posSpreadSetting);
-        SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     }
+    SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     //绑定计量单元编辑事件
     const posOperationObj = {
         /**
@@ -2221,6 +2229,7 @@ $(document).ready(function() {
 
     postData(window.location.pathname + '/load', {}, function (data) {
         ledgerTree.loadDatas(data.bills);
+        console.log(data.bills.find(x => { return x.b_code === '102-4'}));
         treeCalc.calculateAll(ledgerTree);
         for (const t of data.tags) {
             t.node = ledgerTree.datas.find(x => {return x.id === t.lid});
@@ -2266,6 +2275,7 @@ $(document).ready(function() {
             if (checkList) {
                 checkList.spread.refresh();
             }
+            if (gclGather) gclGather.spread.refresh();
         }
     });
     const stdLibCellDoubleClick = function (e, info) {
@@ -2468,6 +2478,8 @@ $(document).ready(function() {
               const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
               getNodeList(node.id);
               getAllList();
+            } else if (tab.attr('content') === '#gcl-gather') {
+                gclGather.spread.refresh();
             }
         } else { // 收起工具栏
             tab.removeClass('active');
@@ -3435,6 +3447,8 @@ $(document).ready(function() {
                 {title: '计量单元', colSpan: '1', rowSpan: '1', field: 'pos_code', hAlign: 1, width: 70, formatter: '@'},
                 {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 300, formatter: '@'},
                 {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '签约数量', colSpan: '1', rowSpan: '1', field: 'deal_qty', hAlign: 2, width: 80, type: 'Number'},
+                {title: '签约金额', colSpan: '1', rowSpan: '1', field: 'deal_tp', hAlign: 2, width: 80, type: 'Number'},
                 {title: '清单数量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
                 {title: '设计数量1', colSpan: '1', rowSpan: '1', field: 'dgn_qty1', hAlign: 2, width: 80, type: 'Number'},
                 {title: '设计数量2', colSpan: '1', rowSpan: '1', field: 'dgn_qty2', hAlign: 2, width: 80, type: 'Number'},
@@ -3456,6 +3470,7 @@ $(document).ready(function() {
             data.push({
                 code: node.code, b_code: node.b_code, name: node.name, unit: node.unit,
                 unit_price: node.unit_price, quantity: node[qtyF], total_price: node[tpF],
+                deal_qty: node.deal_qty, deal_tp: node.deal_tp,
                 dgn_qty1: node.dgn_qty1, dgn_qty2: node.dgn_qty2,
                 drawing_code: node.drawing_code, memo: node.memo
             });

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 520 - 214
app/public/js/material.js


+ 8 - 0
app/public/js/material_audit.js

@@ -224,6 +224,14 @@ function checkAuditorFrom () {
         }
         return false;
     }
+    // 判断是否存在工料编号不能为空的情况
+    const nullList = _.filter(materialBillsData, function (item) {
+        return item.code === '' || item.code === null;
+    });
+    if (nullList.length > 0) {
+        toastr.error('信息价调差存在编号为空的工料,请添加编号后再上报');
+        return false;
+    }
     $('#hide-all').show();
 }
 // texterea换行

+ 116 - 4
app/public/js/material_checklist.js

@@ -125,7 +125,7 @@ $(document).ready(() => {
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
 
     // 加载清单数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
-    postData(window.location.pathname + '/load', {}, function (result) {
+    postData(window.location.pathname + '/load', { sid: isStageSelf ? materialStageData[0].sid : null }, function (result) {
         ledger = result.ledger;
         curLedgerData = result.curLedgerData;
         pos = result.pos;
@@ -134,6 +134,19 @@ $(document).ready(() => {
         notJoinList = result.materialNotJoinListData;
         materialChecklistData = result.materialChecklistData;
         gclList = result.gclList;
+        if (isStageSelf) {
+            // updateBillsData(ms_id);
+            const newGclGatherListData = [];
+            for (const [index, s] of result.ledgerListData.entries()) {
+                gclGatherModel.loadLedgerData(_.cloneDeep(ledger), s);
+                gclGatherModel.loadPosData(_.cloneDeep(pos), result.posListData[index]);
+                const oneGclGatherData = gclGatherModel.gatherGclData().filter(item => {
+                    return item.qc_qty || item.contract_qty
+                });
+                newGclGatherListData.push(oneGclGatherData);
+            }
+            gclGatherListData = newGclGatherListData;
+        }
         // 解析清单汇总数据
         gclGatherModel.loadLedgerData(ledger, curLedgerData);
         gclGatherModel.loadPosData(pos, curPosData);
@@ -408,6 +421,7 @@ $(document).ready(() => {
         const select = SpreadJsObj.getSelectObject(sheet);
         const gclIndex = _.findIndex(gclGatherData, { b_code: select.b_code, name: select.name, unit: select.unit, unit_price: select.unit_price });
         const gcl = gclGatherData[gclIndex].leafXmjs;
+        const ms_id = isStageSelf ? materialStageData[0].id : null;
         const index = materialChecklistData.indexOf(select);
         const datas = [];
         for (const xmj of gcl) {
@@ -419,8 +433,33 @@ $(document).ready(() => {
                 gather_qty: xmj.gather_qty,
                 is_join: notx === undefined ? 1 : 0,
             };
+            if (ms_id) data.ms_id = ms_id;
             datas.push(data);
         }
+        if (isStageSelf) {
+            // 取所有的gclGatherData才行,然后获取下的值
+            const gclData = gclGatherData[gclIndex];
+            for (const [index, ms] of materialStageData.entries()) {
+                if (ms.id !== ms_id) {
+                    const gclOther = _.find(gclGatherListData[index], { b_code: gclData.b_code, name: gclData.name, unit: gclData.unit, unit_price: gclData.unit_price });
+                    if (gclOther) {
+                        const leafXmjs = gclOther.leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                        for (const xmj of leafXmjs) {
+                            const notx = findNotJoinLeafXmj(xmj);
+                            const data = {
+                                xmj_id: xmj.id,
+                                gcl_id: xmj.gcl_id,
+                                mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                gather_qty: xmj.gather_qty,
+                                is_join: notx === undefined ? 1 : 0,
+                                ms_id: ms.id,
+                            };
+                            datas.push(data);
+                        }
+                    }
+                }
+            }
+        }
         // 上传到数据库
         console.log(datas, gcl);
         postData(window.location.pathname + '/save', {type: 'adds', checklist: { id: select.id, had_bills: 1 }, postData: {xmjs: datas, mbIds: mb_id}}, function (result) {
@@ -445,14 +484,38 @@ $(document).ready(() => {
                 const gclIndex = _.findIndex(gclGatherData, { b_code: select.b_code, name: select.name, unit: select.unit, unit_price: select.unit_price });
                 const gcl = gclGatherData[gclIndex].leafXmjs;
                 const datas = [];
+                const ms_id = isStageSelf ? materialStageData[0].id : null;
                 for (const xmj of gcl) {
                     const data = {
                         xmj_id: xmj.id,
                         gcl_id: xmj.gcl_id,
                         mx_id: xmj.mx_id !== undefined ? xmj.mx_id : '',
                     };
+                    if (ms_id) data.ms_id = ms_id;
                     datas.push(data);
                 }
+                if (isStageSelf) {
+                    // 取所有的gclGatherData才行,然后获取下的值
+                    const gclData = gclGatherData[gclIndex];
+                    for (const [index, ms] of materialStageData.entries()) {
+                        if (ms.id !== ms_id) {
+                            const gclOther = _.find(gclGatherListData[index], { b_code: gclData.b_code, name: gclData.name, unit: gclData.unit, unit_price: gclData.unit_price });
+                            if (gclOther) {
+                                const leafXmjs = gclOther.leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                                for (const xmj of leafXmjs) {
+                                    const data = {
+                                        xmj_id: xmj.id,
+                                        gcl_id: xmj.gcl_id,
+                                        mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                    };
+                                    if (_.indexOf(datas, data) === -1) {
+                                        datas.push(data);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
                 const xmj = gcl[0];
                 const materialCount = _.size(_.filter(gclList, function (m) {
                     return m.gcl_id === xmj.gcl_id;
@@ -468,7 +531,7 @@ $(document).ready(() => {
                     }
                 }
                 console.log(datas, materialSelect.mb_id, checklist);
-                postData(window.location.pathname + '/save', {type: 'dels', checklist, postData: { xmjs: datas, mb_id: materialSelect.mb_id }}, function (result) {
+                postData(window.location.pathname + '/save', {type: 'dels', checklist, postData: { xmjs: datas, mb_id: materialSelect.mb_id }, ms_id: isStageSelf ? materialStageData[0].id : null }, function (result) {
                     // materialListData = result;
                     gclList = result;
                     if (checklist) materialChecklistData[index].had_bills = checklist.had_bills;
@@ -531,16 +594,40 @@ $(document).ready(() => {
                     const gclIndex = _.findIndex(gclGatherData, { b_code: ledgerSelect.b_code, name: ledgerSelect.name, unit: ledgerSelect.unit, unit_price: ledgerSelect.unit_price });
                     const gcl = gclGatherData[gclIndex].leafXmjs;
                     const datas = [];
+                    const ms_id = isStageSelf ? materialStageData[0].id : null;
                     for (const xmj of gcl) {
                         const data = {
                             xmj_id: xmj.id,
                             gcl_id: xmj.gcl_id,
                             mx_id: xmj.mx_id !== undefined ? xmj.mx_id : '',
                         };
+                        if (ms_id) data.ms_id = ms_id;
                         datas.push(data);
                     }
+                    if (isStageSelf) {
+                        // 取所有的gclGatherData才行,然后获取下的值
+                        const gclData = gclGatherData[gclIndex];
+                        for (const [index, ms] of materialStageData.entries()) {
+                            if (ms.id !== ms_id) {
+                                const gclOther = _.find(gclGatherListData[index], { b_code: gclData.b_code, name: gclData.name, unit: gclData.unit, unit_price: gclData.unit_price });
+                                if (gclOther) {
+                                    const leafXmjs = gclOther.leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                                    for (const xmj of leafXmjs) {
+                                        const data = {
+                                            xmj_id: xmj.id,
+                                            gcl_id: xmj.gcl_id,
+                                            mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                        };
+                                        if (_.indexOf(datas, data) === -1) {
+                                            datas.push(data);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
                     console.log(exprQuantity, datas, select.mb_id);
-                    postData(window.location.pathname + '/save', { type:'updates', updateData: { xmjs: datas, expr: exprQuantity.expr, quantity: exprQuantity.quantity, mb_id: select.mb_id } }, function (result) {
+                    postData(window.location.pathname + '/save', { type:'updates', updateData: { xmjs: datas, expr: exprQuantity.expr, quantity: exprQuantity.quantity, mb_id: select.mb_id }, ms_id: isStageSelf ? materialStageData[0].id : null }, function (result) {
                         // materialListData = result;
                         gclList = result;
                         loadMaterialData(gclIndex, 0);
@@ -629,17 +716,42 @@ $(document).ready(() => {
                 const gclIndex = _.findIndex(gclGatherData, { b_code: ledgerSelect.b_code, name: ledgerSelect.name, unit: ledgerSelect.unit, unit_price: ledgerSelect.unit_price });
                 const gcl = gclGatherData[gclIndex].leafXmjs;
                 const datas = [];
+                const ms_id = isStageSelf ? materialStageData[0].id : null;
                 for (const xmj of gcl) {
                     const data2 = {
                         xmj_id: xmj.id,
                         gcl_id: xmj.gcl_id,
                         mx_id: xmj.mx_id !== undefined ? xmj.mx_id : '',
                     };
+                    if (ms_id) data2.ms_id = ms_id;
                     datas.push(data2);
                 }
+                if (isStageSelf) {
+                    // 取所有的gclGatherData才行,然后获取下的值
+                    const gclData = gclGatherData[gclIndex];
+                    for (const [index, ms] of materialStageData.entries()) {
+                        if (ms.id !== ms_id) {
+                            const gclOther = _.find(gclGatherListData[index], { b_code: gclData.b_code, name: gclData.name, unit: gclData.unit, unit_price: gclData.unit_price });
+                            console.log(gclOther);
+                            if (gclOther) {
+                                const leafXmjs = gclOther.leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                                for (const xmj of leafXmjs) {
+                                    const data = {
+                                        xmj_id: xmj.id,
+                                        gcl_id: xmj.gcl_id,
+                                        mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                    };
+                                    if (_.indexOf(datas, data) === -1) {
+                                        datas.push(data);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
                 console.log(data, datas);
                 // 更新至服务器
-                postData(window.location.pathname + '/save', { type:'pastes', updateData: { xmjs: datas, pasteData: data } }, function (result) {
+                postData(window.location.pathname + '/save', { type:'pastes', updateData: { xmjs: datas, pasteData: data }, ms_id: isStageSelf ? materialStageData[0].id : null }, function (result) {
                     // materialListData = result;
                     gclList = result;
                     loadMaterialData(gclIndex, 0);

+ 57 - 38
app/public/js/material_exponent.js

@@ -15,9 +15,9 @@ function getPasteHint (str, row = '') {
 function resetExTpTable() {
     const rate = $('#rateInput').val();
     const bqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), materialDecimal.tp);
-    const jzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, bqhs), materialDecimal.tp);
+    const jzbqhs = ZhCalc.add(ex_pre_tp_hs, bqhs);
     $('#tp_set').find('td').eq(3).text(ZhCalc.round(ex_tp, materialDecimal.tp));
-    $('#tp_set').find('td').eq(4).text(ZhCalc.round(ZhCalc.add(ex_pre_tp, ex_tp), materialDecimal.tp));
+    $('#tp_set').find('td').eq(4).text(ZhCalc.add(ex_pre_tp, ZhCalc.round(ex_tp, materialDecimal.tp)));
     $('#rate_set').find('td').eq(3).text(bqhs !== 0 ? bqhs : '');
     $('#rate_set').find('td').eq(4).text(jzbqhs !== 0 ? jzbqhs : '');
     // $('#ex_expr').html(ex_expr);
@@ -191,7 +191,7 @@ $(document).ready(() => {
                         validText = ZhCalc.round(num, 3);
                         num = ZhCalc.round(num, 3);
                     }
-                    const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
+                    const total_weight = ZhCalc.add(ZhCalc.sub(ZhCalc.sum(_.map(materialExponentData, 'weight_num')), parseFloat(orgValue)), num);
                     if (total_weight > 1) {
                         toastr.error('加权系数总和不能大于1');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -431,52 +431,53 @@ $(document).ready(() => {
         }
     };
     materialExponentSpreadObj.refreshActn();
-    materialExponentSpread.bind(spreadNS.Events.SelectionChanged, materialExponentSpreadObj.selectionChanged);
-
 
 
     if (!readOnly) {
         $('#add').click(materialExponentSpreadObj.add);
         $('#del').click(materialExponentSpreadObj.del);
+        materialExponentSpread.bind(spreadNS.Events.SelectionChanged, materialExponentSpreadObj.selectionChanged);
         materialExponentSpread.bind(spreadNS.Events.EditEnded, materialExponentSpreadObj.editEnded);
         materialExponentSpread.bind(spreadNS.Events.ButtonClicked, materialExponentSpreadObj.buttonClicked);
         materialExponentSpread.bind(spreadNS.Events.ClipboardPasted, materialExponentSpreadObj.clipboardPasted);
-        SpreadJsObj.addDeleteBind(materialExponentSpread, materialExponentSpreadObj.deletePress);
-        // 右键菜单
-        $.contextMenu({
-            selector: '#material-exponent-spread',
-            build: function ($trigger, e) {
-                const target = SpreadJsObj.safeRightClickSelection($trigger, e, materialExponentSpread);
-                return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
-            },
-            items: {
-                'create': {
-                    name: '新增',
-                    icon: 'fa-sign-in',
-                    callback: function (key, opt) {
-                        materialExponentSpreadObj.add(materialExponentSpread.getActiveSheet());
-                    },
+        if (!editForAudit) {
+            SpreadJsObj.addDeleteBind(materialExponentSpread, materialExponentSpreadObj.deletePress);
+            // 右键菜单
+            $.contextMenu({
+                selector: '#material-exponent-spread',
+                build: function ($trigger, e) {
+                    const target = SpreadJsObj.safeRightClickSelection($trigger, e, materialExponentSpread);
+                    return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
                 },
-                'delete': {
-                    name: '删除',
-                    icon: 'fa-remove',
-                    callback: function (key, opt) {
-                        materialExponentSpreadObj.del(materialExponentSpread.getActiveSheet());
+                items: {
+                    'create': {
+                        name: '新增',
+                        icon: 'fa-sign-in',
+                        callback: function (key, opt) {
+                            materialExponentSpreadObj.add(materialExponentSpread.getActiveSheet());
+                        },
                     },
-                    disabled: function (key, opt) {
-                        const sheet = materialExponentSpread.getActiveSheet();
-                        const select = SpreadJsObj.getSelectObject(sheet);
-                        const sel = sheet.getSelections()[0];
-                        materialExponentSpreadObj.refreshActn(sel.rowCount);
-                        if (!readOnly && select && materialExponentBase.isUsed(select) && sel.rowCount === 1) {
-                            return false;
-                        } else {
-                            return true;
+                    'delete': {
+                        name: '删除',
+                        icon: 'fa-remove',
+                        callback: function (key, opt) {
+                            materialExponentSpreadObj.del(materialExponentSpread.getActiveSheet());
+                        },
+                        disabled: function (key, opt) {
+                            const sheet = materialExponentSpread.getActiveSheet();
+                            const select = SpreadJsObj.getSelectObject(sheet);
+                            const sel = sheet.getSelections()[0];
+                            materialExponentSpreadObj.refreshActn(sel.rowCount);
+                            if (!readOnly && select && materialExponentBase.isUsed(select) && sel.rowCount === 1) {
+                                return false;
+                            } else {
+                                return true;
+                            }
                         }
-                    }
-                },
-            }
-        });
+                    },
+                }
+            });
+        }
 
         // 调差基数选中
         $('.calc_select').on('click', function () {
@@ -651,4 +652,22 @@ $(document).ready(() => {
     function getObjHeight(select) {
         return select.length > 0 ? select.height() : 0;
     }
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            materialExponentSpread.refresh();
+        }
+    });
 });

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 858 - 492
app/public/js/material_list.js


+ 161 - 15
app/public/js/measure_compare.js

@@ -19,8 +19,8 @@ const billsSpreadSetting = {
         {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number', },
     ],
     extraCols: [
-        {title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty%s', hAlign: 2, width: 60, type: 'Number', },
-        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp%s', hAlign: 2, width: 60, type: 'Number', },
+        {title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: '{%s}_qty{%d}', hAlign: 2, width: 60, type: 'Number', },
+        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: '{%s}_tp{%d}', hAlign: 2, width: 60, type: 'Number', },
     ],
     emptyRows: 3,
     headRows: 2,
@@ -37,7 +37,7 @@ const posSpreadSetting = {
         {title: '台账数量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 60},
     ],
     extraCols: [
-        {title: '%s数量', colSpan: '1', rowSpan: '1', field: 'gather_qty%s', hAlign: 2, width: 60},
+        {title: '%s数量', colSpan: '1', rowSpan: '1', field: '{%s}_qty{%d}', hAlign: 2, width: 60},
     ],
     emptyRows: 3,
     headRows: 1,
@@ -49,13 +49,63 @@ const posSpreadSetting = {
     readOnly: true,
     selectedBackColor: '#fffacd',
 };
+
+const gclSpreadSetting = {
+    baseCols: [
+        {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
+        {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+        {title: '签约清单|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_bills_qty', hAlign: 2, width: 60, type: 'Number'},
+        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_bills_tp', hAlign: 2, width: 60, type: 'Number'},
+        {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
+        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number'},
+    ],
+    extraCols: [
+        {title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: '{%s}_qty{%d}', hAlign: 2, width: 60, type: 'Number', },
+        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: '{%s}_tp{%d}', hAlign: 2, width: 60, type: 'Number', },
+    ],
+    emptyRows: 0,
+    headRows: 2,
+    headRowHeight: [25, 25],
+    headColWidth: [30],
+    defaultRowHeight: 21,
+    headerFont: '12px 微软雅黑',
+    font: '12px 微软雅黑',
+    readOnly: true,
+};
+const leafXmjSpreadSetting = {
+    baseCols: [
+        {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+        {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
+        {title: '分部工程', colSpan: '1', rowSpan: '2', field: 'fbgc', hAlign: 0, width: 80, formatter: '@'},
+        {title: '分项工程', colSpan: '1', rowSpan: '2', field: 'fxgc', hAlign: 0, width: 80, formatter: '@'},
+        {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 80, formatter: '@'},
+        {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
+        {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+        {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
+    ],
+    extraCols: [
+        {title: '%s数量', colSpan: '1', rowSpan: '2', field: '{%s}_qty{%d}', hAlign: 2, width: 60},
+    ],
+    emptyRows: 0,
+    headRows: 2,
+    headRowHeight: [25, 25],
+    headColWidth: [30],
+    defaultRowHeight: 21,
+    headerFont: '12px 微软雅黑',
+    font: '12px 微软雅黑',
+    readOnly: true,
+};
+
 function initSpreadSettingWithRoles(compareRoles) {
     function setSpreadSettingCols(setting, fieldSufs, Roles) {
         function addExtraCols(fieldSuf, Role) {
+            const sourceType = $('[name=compare-data]:checked').val();
             for (const ec of setting.extraCols) {
                 const col = JSON.parse(JSON.stringify(ec));
                 col.title = _.replace(col.title, '%s', Role);
-                col.field = _.replace(col.field, '%s', fieldSuf);
+                col.field = _.replace(_.replace(col.field, '{%s}', sourceType), '{%d}', fieldSuf);
                 setting.cols.push(col);
             }
         }
@@ -63,6 +113,8 @@ function initSpreadSettingWithRoles(compareRoles) {
         for (const col of setting.baseCols) {
             setting.cols.push(col);
         }
+        setting.frozenColCount = setting.baseCols.length;
+        setting.frozenLineColor = '#93b5e4';
         for (const index in fieldSufs) {
             addExtraCols(fieldSufs[index], Roles[index]);
         }
@@ -79,11 +131,15 @@ function initSpreadSettingWithRoles(compareRoles) {
     }
     setSpreadSettingCols(billsSpreadSetting, fieldSufs, roles);
     setSpreadSettingCols(posSpreadSetting, fieldSufs, roles);
+    setSpreadSettingCols(gclSpreadSetting, fieldSufs, roles);
+    setSpreadSettingCols(leafXmjSpreadSetting, fieldSufs, roles);
 }
 function calculateStageLedgerData(datas) {
     for (const d of datas) {
         d.gather_qty = ZhCalc.add(d.contract_qty, d.qc_qty);
-        d.gather_tp = ZhCalc.add(d.contract_tp, d.qc_tp);
+        d.gather_tp = ZhCalc.sum([d.contract_tp, d.qc_tp, d.pc_tp]);
+        // d.pc_tp = ZhCalc.add(d.qc_tp, d.qc_pc_tp);
+        // d.contract_tp = ZhCalc.add(d.contract_tp, d.contract_pc_tp);
     }
 }
 function calculateStagePosData(datas) {
@@ -105,6 +161,17 @@ $(document).ready(() => {
     if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);
 
+    let gclGatherData;
+    const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
+    const gclSheet = gclSpread.getActiveSheet();
+    sjsSettingObj.setFxTreeStyle(gclSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
+    SpreadJsObj.initSheet(gclSheet, gclSpreadSetting);
+    const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
+    const leafXmjSheet = leafXmjSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
+    SpreadJsObj.initSheet(leafXmjSheet, leafXmjSpreadSetting);
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -128,11 +195,20 @@ $(document).ready(() => {
         select: '#main-resize',
         callback: function () {
             billsSpread.refresh();
-            let bcontent = $(".bcontent-wrap").length > 0 ? $(".bcontent-wrap").height() : 0;
-            $(".sp-wrap").height(bcontent-30);
+            let bcontent = $("#main-bottom").length > 0 ? $("#main-bottom").height() : 0;
+            $("#pos-spread").height(bcontent-30);
             posSpread.refresh();
         }
     });
+    $.divResizer({
+        select: '#gcl-resize',
+        callback: function () {
+            gclSpread.refresh();
+            let bcontent = $("#leafxmj-bottom").length > 0 ? $("#leafxmj-bottom").height() : 0;
+            $("#leaf-xmj-spread").height(bcontent-30);
+            leafXmjSpread.refresh();
+        }
+    });
 
     const cTree = createNewPathTree('master', {
         id: 'ledger_id',
@@ -159,17 +235,38 @@ $(document).ready(() => {
         cPos.loadDatas(result.main.pos);
         SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, cTree);
         loadPosData(0);
+
+        const checkField = function (x) {
+            if (x === 'quantity') return true;
+            if (x === 'total_price') return true;
+            if (x.indexOf('_qty') >= 0) return true;
+            if (x.indexOf('_tp') >= 0) return true;
+            return false;
+        };
+        gclGatherModel.loadGatherField(checkField, checkField);
+        gclGatherModel.loadLedgerData(result.main.ledger);
+        gclGatherModel.loadPosData(result.main.pos);
+        gclGatherData = gclGatherModel.gatherGclData();
+        SpreadJsObj.loadSheetData(gclSheet, SpreadJsObj.DataType.Data, gclGatherData);
+        loadLeafXmjData(0);
     }, null, true);
     function loadPosData(iRow) {
         const node = iRow ? billsSheet.zh_tree.nodes[iRow] : SpreadJsObj.getSelectObject(billsSheet);
-        if (node) {
-            SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, cPos.getLedgerPos(node.id));
+        const posRange = node ? (cPos.getLedgerPos(node.id) || []) : [];
+        SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, posRange);
+        SpreadJsObj.resetTopAndSelect(posSheet);
+    }
+    function loadLeafXmjData(iRow) {
+        const gcl = iRow ? gclSheet.zh_data[iRow] : SpreadJsObj.getSelectObject(gclSheet);
+        if (gcl) {
+            SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, gcl.leafXmjs);
         } else {
-            SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, []);
+            SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, []);
         }
-        SpreadJsObj.resetTopAndSelect(posSheet);
+        SpreadJsObj.resetTopAndSelect(leafXmjSheet);
     }
     billsSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+        console.log(SpreadJsObj.getSelectObject(info.sheet));
         if (info.newSelections) {
             const iNewRow = info.newSelections[0].row;
             if (info.oldSelections) {
@@ -179,26 +276,52 @@ $(document).ready(() => {
                     loadPosData(iNewRow);
                 }
             } else {
+                SpreadJsObj.resetTopAndSelect(leafXmjSheet);
                 loadPosData(iNewRow);
             }
         }
     });
+    gclSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+        if (info.newSelections) {
+            const iNewRow = info.newSelections[0].row;
+            if (info.oldSelections) {
+                const iOldRow = info.oldSelections[0].row;
+                if (iNewRow !== iOldRow) {
+                    SpreadJsObj.resetTopAndSelect(leafXmjSheet);
+                    loadLeafXmjData(iNewRow);
+                }
+            } else {
+                SpreadJsObj.resetTopAndSelect(leafXmjSheet);
+                loadLeafXmjData(iNewRow);
+            }
+        }
+    });
+    const compareStages = [];
     $('#select-qi-ok').click(function () {
         function refreshView () {
-            const compareStages = [];
+            compareStages.length = 0;
             for (let order = 0, iLength = trs.length; order < iLength; order++) {
                 const tr = trs[order];
                 if ($('input', tr)[0].checked) {
                     compareStages.push(order + 1);
                 }
             }
-            //setLocalCache(cCacheKey, compareStages.join(','));
+            // setLocalCache(cCacheKey, compareStages.join(','));
             initSpreadSettingWithRoles(compareStages);
             SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
             treeCalc.calculateAll(cTree);
             SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, cTree);
             SpreadJsObj.initSheet(posSheet, posSpreadSetting);
             loadPosData();
+
+            SpreadJsObj.reLoadSheetHeader(gclSheet);
+            SpreadJsObj.reLoadSheetHeader(leafXmjSheet);
+
+            gclGatherModel.loadLedgerData(cTree.datas);
+            gclGatherModel.loadPosData(cPos.datas);
+            gclGatherData = gclGatherModel.gatherGclData();
+            SpreadJsObj.loadSheetData(gclSheet, SpreadJsObj.DataType.Data, gclGatherData);
+            loadLeafXmjData(0);
         }
         let loadData = [], showData = [], trs = $('tr[stage-id]');
         for (let order = 0, iLength = trs.length; order < iLength; order++) {
@@ -214,10 +337,10 @@ $(document).ready(() => {
             postData(window.location.pathname + '/load', {stages: loadData}, function (result) {
                 for (const aData of result.stages) {
                     calculateStageLedgerData(aData.bills);
-                    cTree.loadMinorData(aData.bills, aData.order + '', ['gather_qty', 'gather_tp'], ['gather_tp']);
+                    cTree.loadMinorData(aData.bills, aData.order + '', ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp'], ['contract_tp', 'qc_tp', 'gather_tp']);
                     treeCalc.calculateAll(cTree);
                     calculateStagePosData(aData.pos);
-                    cPos.loadMinorData(aData.pos, aData.order + '', ['gather_qty']);
+                    cPos.loadMinorData(aData.pos, aData.order + '', ['contract_qty', 'qc_qty', 'gather_qty']);
                 }
                 refreshView();
                 $('#select-qi').modal('hide');
@@ -268,4 +391,27 @@ $(document).ready(() => {
 
         SpreadExcelObj.exportSimpleXlsxSheet(billsSpreadSetting, data, $('.sidebar-title').attr('data-original-title') + "-多期比较.xlsx");
     });
+
+    $('[name=compare-data]').click(function () {
+        initSpreadSettingWithRoles(compareStages);
+        SpreadJsObj.reLoadSheetHeader(billsSheet);
+        SpreadJsObj.reloadColData(billsSheet, billsSpreadSetting.baseCols.length, compareStages.length * billsSpreadSetting.extraCols.length);
+        SpreadJsObj.reLoadSheetHeader(posSheet);
+        SpreadJsObj.reloadColData(posSheet, posSpreadSetting.baseCols.length, compareStages.length * posSpreadSetting.extraCols.length);
+
+        SpreadJsObj.reLoadSheetHeader(gclSheet);
+        SpreadJsObj.reloadColData(gclSheet, gclSpreadSetting.baseCols.length, compareStages.length * gclSpreadSetting.extraCols.length);
+        SpreadJsObj.reLoadSheetHeader(leafXmjSheet);
+        SpreadJsObj.reloadColData(leafXmjSheet, leafXmjSpreadSetting.baseCols.length, compareStages.length * leafXmjSpreadSetting.extraCols.length);
+    });
+    $('[name=compareType]').click(function () {
+        $('[name=compareType]').removeClass('active');
+        $(this).addClass('active');
+        $('#compareType').children().removeClass('active');
+        $(this.getAttribute('href')).addClass('active');
+        billsSpread.refresh();
+        posSpread.refresh();
+        gclSpread.refresh();
+        leafXmjSpread.refresh();
+    })
 });

+ 175 - 74
app/public/js/measure_material.js

@@ -233,6 +233,15 @@ $(function () {
             $('#show_order').html('第<b class="mx-2">' + order_array.join(',') + '</b>期');
             $('#show_order').show();
             $('#s_order').val(order_array.join(','));
+            if (stageList.length > 1) {
+                if($('#material_unitPrice').is(':hidden')){
+                    $('#unitPrice_dl').prop('checked', true);
+                }
+                $('#material_unitPrice').show();
+            } else {
+                $('#material_unitPrice').hide();
+                $('#unitPrice_gy').prop('checked', true);
+            }
         }
     });
     let interval;
@@ -251,10 +260,12 @@ $(function () {
         $('#add-qi input[name="stage_id[]"]:checked').each(function () {
             stage_id.push(parseInt($(this).val()));
         });
+        const is_stage_self = parseInt($('input[name="is_stage_self"]:checked').val());
         const newMaterialData = {
             s_order: $('#s_order').val(),
             period: $('#add-qi input[name="period"]').val(),
             stage_id,
+            is_stage_self,
         };
         if (lastMaterialListNum === 0) {
             console.log(newMaterialData);
@@ -268,93 +279,183 @@ $(function () {
         $('#add-qi').modal('hide');
         $('#okedit').modal('show');
         interval = setInterval(progress, 50);
-        postData(preUrl + '/measure/material/gcl/load', { stage_id }, function (result) {
+        postData(preUrl + '/measure/material/gcl/load', { stage_id, is_stage_self }, function (result) {
             // console.log(result);
             const ledger = result.ledger;
-            const curLedgerData = result.curLedgerData;
             const pos = result.pos;
-            const curPosData = result.curPosData;
             const gclList = result.gclList;
             const selfList = result.selfList;
-            const materialListForSelf = result.materialListForSelf;
-            gclGatherModel.loadLedgerData(ledger, curLedgerData);
-            gclGatherModel.loadPosData(pos, curPosData);
-            const gclGatherData = gclGatherModel.gatherGclData();
-            // const gclGatherData = gclGatherModel.gatherGclData().filter(item => {
-            //     return item.qc_qty || item.contract_qty
-            // });
-            // 获取需要新增的工料关联清单
-            const insertGcl = [];
-            const removeGclList = [];
+            // 整理material,针对上期是独立单价并多个不同值的单独设置值进行部分值去除
+            const materialListForSelf = _.uniqWith(_.orderBy(result.materialListForSelf, ['ms_id'], ['desc']), function(item1, item2) {
+                return item1.xmj_id === item2.xmj_id && item1.gcl_id === item2.gcl_id && item1.mx_id === item2.mx_id && item1.mb_id === item2.mb_id;
+            });
+            console.log(materialListForSelf, result.materialListForSelf);
+            const insertGclList = [];
+            const insertList = [];
             const insertSelfList = [];// 需要单独添加的明细清单工料含量列表
-            for (const g of gclList) {
-                const gcl = _.find(gclGatherData, function (item) {
-                    return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id : g.gcl_id }) !== -1;
-                });
-                if (gcl) {
-                    const ig = _.find(insertGcl, { gcl });
-                    if (ig) {
-                        if(_.findIndex(ig.bills, { mb_id: g.mb_id }) === -1) {
-                            ig.bills.push(g);
+            const removeGclList = [];
+            const hadQtySelfList = [];
+            if (is_stage_self) {
+                for (const sid of stage_id) {
+                    const curLedger = _.find(result.curLedgerData, {sid: sid});
+                    const curPos = _.find(result.curPosData, {sid: sid});
+                    gclGatherModel.loadLedgerData(_.cloneDeep(ledger), curLedger.ledgerData);
+                    gclGatherModel.loadPosData(_.cloneDeep(pos), curPos.posData);
+                    const gclGatherData = gclGatherModel.gatherGclData();
+                    // console.log(gclGatherData);
+                    const insertGcl = [];
+                    for (const g of gclList) {
+                        const gcl = _.find(gclGatherData, function (item) {
+                            return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, {gcl_id: g.gcl_id}) !== -1;
+                        });
+                        if (gcl) {
+                            const ig = _.find(insertGcl, {gcl});
+                            if (ig) {
+                                if (_.findIndex(ig.bills, {mb_id: g.mb_id}) === -1) {
+                                    ig.bills.push(g);
+                                }
+                            } else {
+                                insertGcl.push({
+                                    bills: [g],
+                                    gcl,
+                                    leafXmjs: _.filter(gcl.leafXmjs, function (item) {
+                                        return item.gather_qty !== undefined && item.gather_qty !== null
+                                    }),
+                                })
+                            }
+                        } else {
+                            removeGclList.push(g);
+                        }
+                    }
+                    for (const one of insertGcl) {
+                        if (one.leafXmjs && one.leafXmjs.length > 0) {
+                            for (const xmj of one.leafXmjs) {
+                                const is_self = _.findIndex(selfList, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : '' }) !== -1;// 区分单独计量的明细工料含量
+                                if (is_self) {
+                                    const billsList = _.filter(materialListForSelf, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : '' });
+                                    for (const bill of billsList) {
+                                        if (!_.find(hadQtySelfList, bill)) {
+                                            hadQtySelfList.push(bill);
+                                        }
+                                        insertSelfList.push({
+                                            gcl_id: xmj.gcl_id,
+                                            mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                            xmj_id: xmj.id ? xmj.id : null,
+                                            gather_qty: xmj.gather_qty,
+                                            quantity: bill.quantity,
+                                            expr: bill.expr,
+                                            mb_id: bill.mb_id,
+                                            order: bill.order,
+                                            sid,
+                                        });
+                                    }
+                                } else {
+                                    const newgcl = _.find(gclList, { gcl_id: xmj.gcl_id });
+                                    for (const bill of one.bills) {
+                                        insertList.push({
+                                            gcl_id: xmj.gcl_id,
+                                            mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                            xmj_id: xmj.id ? xmj.id : null,
+                                            gather_qty: xmj.gather_qty,
+                                            quantity: bill.quantity,
+                                            expr: bill.expr,
+                                            mb_id: bill.mb_id,
+                                            order: bill.order,
+                                            sid,
+                                        });
+                                        if (!newgcl) {
+                                            if (_.findIndex(insertGclList, { gcl_id: xmj.gcl_id }) === -1) {
+                                                insertGclList.push({
+                                                    gcl_id: xmj.gcl_id,
+                                                    quantity: bill.quantity,
+                                                    expr: bill.expr,
+                                                    old_quantity: bill.quantity,
+                                                    old_expr: bill.expr,
+                                                    mb_id: bill.mb_id,
+                                                    order: bill.order,
+                                                });
+                                            }
+                                        }
+                                    }
+                                }
+                            }
                         }
-                    } else {
-                        insertGcl.push({
-                            bills: [g],
-                            gcl,
-                            leafXmjs: _.filter(gcl.leafXmjs, function(item) { return item.gather_qty !== undefined && item.gather_qty !== null }),
-                        })
                     }
-                } else {
-                    removeGclList.push(g);
                 }
-            }
-            const insertList = [];
-            const insertGclList = [];
-            const hadQtySelfList = [];
-            for (const one of insertGcl) {
-                if (one.leafXmjs && one.leafXmjs.length > 0) {
-                    for (const xmj of one.leafXmjs) {
-                        const is_self = _.findIndex(selfList, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : null }) !== -1;// 区分单独计量的明细工料含量
-                        if (is_self) {
-                            const billsList = _.filter(materialListForSelf, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : null });
-                            console.log(billsList);
-                            for (const bill of billsList) {
-                                hadQtySelfList.push(bill);
-                                insertSelfList.push({
-                                    gcl_id: xmj.gcl_id,
-                                    mx_id: xmj.mx_id ? xmj.mx_id : '',
-                                    xmj_id: xmj.id ? xmj.id : null,
-                                    gather_qty: xmj.gather_qty,
-                                    quantity: bill.quantity,
-                                    expr: bill.expr,
-                                    mb_id: bill.mb_id,
-                                    order: bill.order,
-                                });
+            } else {
+                const curLedgerData = result.curLedgerData;
+                const curPosData = result.curPosData;
+                gclGatherModel.loadLedgerData(_.cloneDeep(ledger), curLedgerData);
+                gclGatherModel.loadPosData(_.cloneDeep(pos), curPosData);
+                const gclGatherData = gclGatherModel.gatherGclData();
+                const insertGcl = [];
+                for (const g of gclList) {
+                    const gcl = _.find(gclGatherData, function (item) {
+                        return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, {gcl_id: g.gcl_id}) !== -1;
+                    });
+                    if (gcl) {
+                        const ig = _.find(insertGcl, {gcl});
+                        if (ig) {
+                            if (_.findIndex(ig.bills, {mb_id: g.mb_id}) === -1) {
+                                ig.bills.push(g);
                             }
                         } else {
-                            const newgcl = _.find(gclList, { gcl_id: xmj.gcl_id });
-                            for (const bill of one.bills) {
-                                insertList.push({
-                                    gcl_id: xmj.gcl_id,
-                                    mx_id: xmj.mx_id ? xmj.mx_id : '',
-                                    xmj_id: xmj.id ? xmj.id : null,
-                                    gather_qty: xmj.gather_qty,
-                                    quantity: bill.quantity,
-                                    expr: bill.expr,
-                                    mb_id: bill.mb_id,
-                                    order: bill.order,
-                                });
-                                if (!newgcl) {
-                                    insertGclList.push({
+                            insertGcl.push({
+                                bills: [g],
+                                gcl,
+                                leafXmjs: _.filter(gcl.leafXmjs, function (item) {
+                                    return item.gather_qty !== undefined && item.gather_qty !== null
+                                }),
+                            })
+                        }
+                    } else {
+                        removeGclList.push(g);
+                    }
+                }
+                for (const one of insertGcl) {
+                    if (one.leafXmjs && one.leafXmjs.length > 0) {
+                        for (const xmj of one.leafXmjs) {
+                            const is_self = _.findIndex(selfList, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : null }) !== -1;// 区分单独计量的明细工料含量
+                            if (is_self) {
+                                const billsList = _.filter(materialListForSelf, { gcl_id: xmj.gcl_id, xmj_id: xmj.id, mx_id: xmj.mx_id ? xmj.mx_id : null });
+                                for (const bill of billsList) {
+                                    hadQtySelfList.push(bill);
+                                    insertSelfList.push({
                                         gcl_id: xmj.gcl_id,
+                                        mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                        xmj_id: xmj.id ? xmj.id : null,
+                                        gather_qty: xmj.gather_qty,
                                         quantity: bill.quantity,
                                         expr: bill.expr,
-                                        old_quantity: bill.quantity,
-                                        old_expr: bill.expr,
                                         mb_id: bill.mb_id,
                                         order: bill.order,
                                     });
                                 }
+                            } else {
+                                const newgcl = _.find(gclList, { gcl_id: xmj.gcl_id });
+                                for (const bill of one.bills) {
+                                    insertList.push({
+                                        gcl_id: xmj.gcl_id,
+                                        mx_id: xmj.mx_id ? xmj.mx_id : '',
+                                        xmj_id: xmj.id ? xmj.id : null,
+                                        gather_qty: xmj.gather_qty,
+                                        quantity: bill.quantity,
+                                        expr: bill.expr,
+                                        mb_id: bill.mb_id,
+                                        order: bill.order,
+                                    });
+                                    if (!newgcl) {
+                                        insertGclList.push({
+                                            gcl_id: xmj.gcl_id,
+                                            quantity: bill.quantity,
+                                            expr: bill.expr,
+                                            old_quantity: bill.quantity,
+                                            old_expr: bill.expr,
+                                            mb_id: bill.mb_id,
+                                            order: bill.order,
+                                        });
+                                    }
+                                }
                             }
                         }
                     }
@@ -387,11 +488,11 @@ $(function () {
                 window.location.href = preUrl + '/measure/material/' + result.order;
             }, function () {
                 stop = true;
-                // clearInterval(interval);
-                // console.log(_self.parents('div[id="add-qi"]'));
-                // $('#add-qi').modal('show');
-                // $('#okedit').modal('hide');
-                // _self.parents('div[id="add-qi"]').modal('show');
+                clearInterval(interval);
+                console.log(_self.parents('div[id="add-qi"]'));
+                $('#add-qi').modal('show');
+                $('#okedit').modal('hide');
+                _self.parents('div[id="add-qi"]').modal('show');
                 _self.attr('disabled', false).text('确认添加');
             });
             return;

+ 83 - 1
app/public/js/path_tree.js

@@ -146,9 +146,13 @@ class PosData {
     }
 
     resortLedgerPos(ledgerPos) {
+        const sortRule = this.setting.sort || [['porder', 'asc']];
         if (ledgerPos instanceof Array) {
             ledgerPos.sort(function (a, b) {
-                return a.porder - b.porder;
+                for (const sr of sortRule) {
+                    const iSort = sr[1] === 'asc' ? a[sr[0]] - b[sr[0]] : b[sr[0]] - a[sr[0]];
+                    if (iSort) return iSort;
+                }
             })
         }
     }
@@ -162,6 +166,13 @@ class PosData {
             this.setting.calcFun(pos);
         }
     }
+
+    set sort(sort) {
+        this.setting.sort = sort;
+        for (const key in this.ledgerPos) {
+            this.resortLedgerPos(this.ledgerPos[key]);
+        }
+    }
 }
 
 class StagePosData extends PosData {
@@ -1043,6 +1054,7 @@ const createNewPathTree = function (type, setting) {
     }
 
     class LedgerTree extends FxTree {
+
         /**
          *
          * @param parent
@@ -1117,6 +1129,10 @@ const createNewPathTree = function (type, setting) {
     }
 
     class ReviseTree extends LedgerTree {
+        constructor (setting) {
+            super(setting);
+            this.price = [];
+        }
         checkNodeUsed(node, pos) {
             if (node.children && node.children.length > 0) {
                 for (const child of node.children) {
@@ -1134,6 +1150,72 @@ const createNewPathTree = function (type, setting) {
             }
             return false;
         }
+        loadRevisePrice(price, decimal) {
+            this.decimal = decimal;
+            this.price = price || [];
+        }
+        checkRevisePrice(d) {
+            const p = this.price.find(x => {
+                return x.b_code === d.b_code &&
+                    ((!x.name && !d.name) || x.name === d.name) &&
+                    ((!x.unit && !d.unit) || x.unit === d.unit) &&
+                    checkZero(x.org_price - d.unit_price);
+            });
+            if (!p) return false;
+            d.org_price = p.org_price;
+            d.unit_price = p.new_price;
+            d.deal_tp = ZhCalc.mul(d.deal_qty, d.unit_price, this.decimal.tp);
+            d.sgfh_tp = ZhCalc.mul(d.sgfh_qty, d.unit_price, this.decimal.tp);
+            d.sjcl_tp = ZhCalc.mul(d.sjcl_qty, d.unit_price, this.decimal.tp);
+            d.qtcl_tp = ZhCalc.mul(d.qtcl_qty, d.unit_price, this.decimal.tp);
+            d.total_price = ZhCalc.mul(d.quantity, d.unit_price, this.decimal.tp);
+            return true;
+        }
+        loadDatas(datas) {
+            super.loadDatas(datas);
+            if (this.price.length > 0) {
+                for (const d of this.datas) {
+                    if (d.children && d.children.length > 0) continue;
+                    if (!d.b_code) continue;
+                    this.checkRevisePrice(d);
+                }
+            }
+        }
+        loadPostReivsePrice(data) {
+            let result = false;
+            if (!this.price) return result;
+            for (const d of data) {
+                if (!d.is_leaf || !d.b_code) continue;
+                if (this.checkRevisePrice(d)) result = true;
+            }
+            return result;
+        }
+        loadPostData(data) {
+            const result = {}, reCalcNodes = [];
+            if (!data) return result;
+            if (data.delete) {
+                result.delete = this._freeData(data.delete);
+                this._getReCalcNodes(reCalcNodes, result.delete);
+            }
+            if (data.create) {
+                result.create = this._loadData(data.create);
+                this.loadPostReivsePrice(result.create);
+                this._getReCalcNodes(reCalcNodes, result.create);
+            }
+            if (data.update) {
+                result.update = this._updateData(data.update);
+                this.loadPostReivsePrice(result.update);
+                this._getReCalcNodes(reCalcNodes, result.update);
+            }
+            reCalcNodes.sort((a, b) => {
+                return b.level - a.level;
+            });
+            for (const node of reCalcNodes) {
+                treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+            }
+            result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes;
+            return result;
+        }
     }
 
     class StageTree extends FxTree {

+ 5 - 4
app/public/js/revise.js

@@ -1027,13 +1027,13 @@ $(document).ready(() => {
         billsSpread.bind(spreadNS.Events.EditStarting, billsTreeSpreadObj.editStarting);
         billsSpread.bind(spreadNS.Events.EditEnded, billsTreeSpreadObj.editEnded);
         billsSpread.bind(spreadNS.Events.ClipboardPasting, billsTreeSpreadObj.clipboardPasting);
-        billsSpread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
-            const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
-            SpreadJsObj.Clipboard.setCopyData(copyText);
-        });
         SpreadJsObj.addDeleteBind(billsSpread, billsTreeSpreadObj.deletePress);
         SpreadJsObj.addCutEvents(billsSpread, billsTreeSpreadObj.cut);
     }
+    billsSpread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
+        const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
+        SpreadJsObj.Clipboard.setCopyData(copyText);
+    });
     // 右键菜单
     let batchInsertObj;
     $.contextMenu.types.batchInsert = function (item, opt, root) {
@@ -1942,6 +1942,7 @@ $(document).ready(() => {
 
     // 加载清单&计量单元数据
     postData(window.location.pathname + '/load', {}, function (result) {
+        billsTree.loadRevisePrice(result.price, decimal);
         billsTree.loadDatas(result.bills);
         treeCalc.calculateAll(billsTree);
         for (const t of result.tags) {

+ 3 - 2
app/public/js/revise_compare.js

@@ -247,7 +247,7 @@ $(document).ready(() => {
     };
 
     // 加载清单&计量单元数据
-    postData('load', {filter: 'bills;pos;reviseBills;revisePos'}, function (result) {
+    postData('load', {filter: 'bills;pos;reviseBills;revisePos;price'}, function (result) {
         const tenderTreeSetting = {
             id: 'ledger_id',
             pid: 'ledger_pid',
@@ -258,9 +258,10 @@ $(document).ready(() => {
             calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
         };
         const reviseLedger = {
-            billsTree: createNewPathTree('ledger', tenderTreeSetting),
+            billsTree: createNewPathTree('revise', tenderTreeSetting),
             pos: new PosData({ id: 'id', ledgerId: 'lid', }),
         };
+        reviseLedger.billsTree.loadRevisePrice(result.price, decimal);
         reviseLedger.billsTree.loadDatas(result.reviseBills);
         reviseLedger.pos.loadDatas(result.revisePos);
         const orgLedger = {

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

@@ -150,7 +150,7 @@ $(document).ready(() => {
         $('#chapter-list').html(html.join(''));
     }
 
-    postData('load', {filter: 'bills;pos;reviseBills;revisePos;dealBills;spec'}, function (data) {
+    postData('load', {filter: 'bills;pos;reviseBills;revisePos;dealBills;spec;price'}, function (data) {
         const setting = {
             tree: {
                 id: 'ledger_id',
@@ -168,7 +168,7 @@ $(document).ready(() => {
         };
         gclCompareModel.init(gclData, chapter, data.spec);
         setting.prefix = 'new_';
-        gclCompareModel.gatherLedgerData(data.reviseBills, data.revisePos, setting);
+        gclCompareModel.gatherReviseLedgerData(data.reviseBills, data.revisePos, setting, data.price, decimal);
         setting.prefix = 'org_';
         gclCompareModel.gatherLedgerData(data.bills, data.pos, setting);
         gclCompareModel.gatherDealBills(data.dealBills);

+ 483 - 0
app/public/js/revise_price.js

@@ -0,0 +1,483 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const showSideTools = function (show) {
+    const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+    if (show) {
+        right.show();
+        autoFlashHeight();
+        const percent = 100 - right.outerWidth() /parent.width() * 100;
+        left.css('width', percent + '%');
+    } else {
+        left.width(parent.width());
+        right.hide();
+    }
+};
+
+const setPriceHint = function (show) {
+    const hinticon = show ? 'fa-bell' : undefined;
+    subMiniMenu.$children[2].hinticon = hinticon;
+    subMenu.$children[2].hinticon = hinticon;
+};
+
+$(document).ready(() => {
+    const ledgerGclSpreadSetting = {
+        cols: [
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@' },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 200, formatter: '@' },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@' },
+            { title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number' },
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+    };
+    const ledgerXmjSpreadSetting = {
+        cols: [
+            {title: '项目节编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+            {title: '单位工程', colSpan: '1', rowSpan: '1', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
+            {title: '分部工程', colSpan: '1', rowSpan: '1', field: 'fbgc', hAlign: 0, width: 80, formatter: '@'},
+            {title: '分项工程', colSpan: '1', rowSpan: '1', field: 'fxgc', hAlign: 0, width: 80, formatter: '@'},
+            {title: '细目', colSpan: '1', rowSpan: '1', field: 'jldy', hAlign: 0, width: 80, formatter: '@'},
+            {title: '计量单元', colSpan: '1', rowSpan: '1', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+    };
+    const priceSpreadSetting = {
+        cols: [
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 100, formatter: '@', readOnly: true },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 210, formatter: '@', readOnly: true },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true },
+            { title: '当前单价', colSpan: '1', rowSpan: '2', field: 'org_price', hAlign: 2, width: 80, type: 'Number', readOnly: true },
+            { title: '调整后单价', colSpan: '1', rowSpan: '2', field: 'new_price', hAlign: 2, width: 80, type: 'Number' },
+            { title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 2, width: 150, formatter: '@' },
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly,
+    };
+    autoFlashHeight();
+    const priceSpread = SpreadJsObj.createNewSpread($('#price-spread')[0]);
+    const priceSheet = priceSpread.getActiveSheet();
+
+    SpreadJsObj.initSheet(priceSheet, priceSpreadSetting);
+
+    class RevisePrice {
+        constructor () {
+            this.data = [];
+        }
+        resortData() {
+            this.data.sort(function (a, b) {
+                return a.order - b.order;
+            });
+        }
+        loadDatas(datas) {
+            this.data = datas;
+            this.resortData();
+        }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+            this.resortData();
+        }
+    }
+    const revisePrice = new RevisePrice();
+    const priceOprObj = {
+        addRevisePrice(data) {
+            const op = revisePrice.data.find(x => {
+                return x.b_code === data.b_code && x.name === x.name && x.unit === x.unit && checkZero(ZhCalc.sub(x.org_price, data.unit_price));
+            });
+            if (op) {
+                toastr.warning('已存在该单价调整');
+                SpreadJsObj.locateData(priceSheet, op);
+                return;
+            }
+            postData(window.location.pathname + '/update', { add: { b_code: data.b_code, name: data.name, unit: data.unit, unit_price: data.unit_price } }, result => {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadSheetData(priceSheet);
+                setPriceHint(revisePrice.data.length > 0);
+            });
+        },
+        /**
+         * 删除按钮响应事件
+         * @param sheet
+         */
+        deletePress: function (sheet) {
+            if (!sheet.zh_setting || readOnly) return;
+
+            const sortData = sheet.zh_data;
+            const datas = [];
+            const sels = sheet.getSelections();
+            if (!sels || !sels[0]) return;
+
+            for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                let bDel = false;
+                const node = sortData[iRow];
+                if (node) {
+                    const data = {id: node.id};
+                    for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].colCount; iCol++) {
+                        const style = sheet.getStyle(iRow, iCol);
+                        if (!style.locked) {
+                            const colSetting = sheet.zh_setting.cols[iCol];
+                            data[colSetting.field] = null;
+                            bDel = true;
+                        }
+                    }
+                    if (bDel) {
+                        datas.push(data);
+                    }
+                }
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/update', {update: datas}, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                });
+            }
+        },
+        delete: function (sheet) {
+            if (!sheet.zh_setting || readOnly) return;
+
+            const sortData = sheet.zh_data;
+            const datas = [];
+            const sels = sheet.getSelections();
+            if (!sels || !sels[0]) return;
+
+            for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                const node = sortData[iRow];
+                datas.push(node.id);
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/update', {del: datas}, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                    setPriceHint(revisePrice.data.length > 0);
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(priceSheet);
+                });
+            }
+        },
+        editEnded: function (e, info) {
+            if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+
+            const node = info.sheet.zh_data[info.row];
+            if (!node) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            const data = { update: { id: node.id, org_price: node.org_price } };
+            const oldValue = node ? node[col.field] : null;
+            const newValue = trimInvalidChar(info.editingText);
+            if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (col.type === 'Number') {
+                const num = _.toNumber(newValue);
+                if (num) data.update[col.field] = num;
+            } else {
+                data.update[col.field] = newValue;
+            }
+
+            postData(window.location.pathname + '/update', data, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadSheetData(info.sheet);
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+        clipboardPasting(e, info) {
+            const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+            info.cancel = true;
+
+            if (!setting || !sortData) return;
+            const pasteData = info.pasteData.html
+                ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                : (info.pasteData.text === ''
+                    ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                    : SpreadJsObj.analysisPasteText(info.pasteData.text));
+
+            const uDatas = [];
+            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                const curRow = info.cellRange.row + iRow;
+                const node = sortData[curRow];
+
+                let bPaste = false;
+                const data = {};
+                for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                    const curCol = info.cellRange.col + iCol;
+                    const colSetting = setting.cols[curCol];
+                    const value = trimInvalidChar(pasteData[iRow][iCol]);
+                    if (colSetting.type === 'Number') {
+                        const num = _.toNumber(value);
+                        if (num) {
+                            data[colSetting.field] = num;
+                            bPaste = true;
+                        }
+                    } else {
+                        data[colSetting.field] = value;
+                        bPaste = true;
+                    }
+                }
+                if (bPaste) {
+                    data.id = node.id;
+                    uDatas.push(data);
+                }
+            }
+            const updateData = {};
+            if (uDatas.length > 0) updateData.update = uDatas;
+            if (uDatas.length > 0) {
+                postData(window.location.pathname + '/update', updateData, function (result) {
+                    revisePrice.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                });
+            } else {
+                SpreadJsObj.reLoadSheetData(info.sheet);
+            }
+        },
+        upMove: function () {
+            const sels = priceSheet.getSelections(), sortData = priceSheet.zh_data;
+            const node = sortData[sels[0].row];
+            const preNode = sortData[sels[0].row - 1];
+            const data = [
+                {id: node.id, order: preNode.order},
+                {id: preNode.id, order: node.order}
+            ];
+            postData(window.location.pathname + '/update', {update: data}, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadRowsData(priceSheet, [sels[0].row, sels[0].row - 1]);
+                priceSheet.setSelection(sels[0].row - 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+            });
+        },
+        downMove: function () {
+            const sels = priceSheet.getSelections(), sortData = priceSheet.zh_data;
+            const node = sortData[sels[0].row];
+            const nextNode = sortData[sels[0].row + 1];
+            const data = [
+                {id: node.id, order: nextNode.order},
+                {id: nextNode.id, order: node.order}
+            ];
+            postData(window.location.pathname + '/update', {update: data}, function (result) {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadRowsData(priceSheet, [sels[0].row, sels[0].row + 1]);
+                priceSheet.setSelection(sels[0].row + 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+            });
+        }
+    };
+    if (!readOnly) {
+        priceSheet.bind(spreadNS.Events.EditEnded, priceOprObj.editEnded);
+        priceSheet.bind(spreadNS.Events.ClipboardPasting, priceOprObj.clipboardPasting);
+        SpreadJsObj.addDeleteBind(priceSpread, priceOprObj.deletePress);
+        $.contextMenu({
+            selector: '#price-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, priceSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        priceOprObj.delete(priceSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(priceSheet);
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                sprDel: '------------',
+                upMove: {
+                    name: '上移',
+                    icon: 'fa-arrow-up',
+                    callback: function (key, opt) {
+                        priceOprObj.upMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = priceSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row === 0) return true;
+
+                        const row = sels[0].row;
+                        const node = revisePrice.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                downMove: {
+                    name: '下移',
+                    icon: 'fa-arrow-down',
+                    callback: function (key, opt) {
+                        priceOprObj.downMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = priceSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row >= revisePrice.data.length - 1) return true;
+
+                        const row = sels[0].row;
+                        const node = revisePrice.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                }
+            },
+        });
+    }
+
+    class LedgerGcl {
+        constructor(setting) {
+            this.setting = setting;
+            this.spread = SpreadJsObj.createNewSpread($(this.setting.selector)[0]);
+            this.sheet = this.spread.getActiveSheet();
+            SpreadJsObj.initSheet(this.sheet, this.setting.spreadSetting);
+            this.xmjSpread = SpreadJsObj.createNewSpread($(this.setting.xmjSelector)[0]);
+            this.xmjSheet = this.xmjSpread.getActiveSheet();
+            SpreadJsObj.initSheet(this.xmjSheet, this.setting.xmjSpreadSetting);
+            if (!readOnly) {
+                this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
+                    const gcl = SpreadJsObj.getSelectObject(info.sheet);
+                    priceOprObj.addRevisePrice(gcl);
+                });
+            }
+            const self = this;
+            this.spread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+                self.loadLeafXmj();
+            });
+        }
+        loadData(bills, pos) {
+            gclGatherModel.loadLedgerData(bills);
+            gclGatherModel.loadPosData(pos);
+            this.gcl = gclGatherModel.gatherGclData();
+            this.sheet && SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, this.gcl);
+            this.loadLeafXmj(0);
+        }
+        loadLeafXmj(iGclRow) {
+            const gcl = iGclRow ? this.gcl[iGclRow] : SpreadJsObj.getSelectObject(this.sheet);
+            SpreadJsObj.resetTopAndSelect(this.xmjSheet);
+            if (gcl) {
+                SpreadJsObj.loadSheetData(this.xmjSheet, SpreadJsObj.DataType.Data, gcl.leafXmjs);
+            } else {
+                SpreadJsObj.loadSheetData(this.xmjSheet, SpreadJsObj.DataType.Data, []);
+            }
+        }
+    }
+    const ledgerGcl = new LedgerGcl({
+        selector: '#ledger-gcl-spread',
+        spreadSetting: ledgerGclSpreadSetting,
+        xmjSelector: '#ledger-xmj-spread',
+        xmjSpreadSetting: ledgerXmjSpreadSetting,
+    });
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            priceSpread.refresh();
+            ledgerGcl.spread.refresh();
+        }
+    });
+
+    $.divResizer({
+        select: '#revise-right-spr',
+        callback: function () {
+            priceSpread.refresh();
+            ledgerGcl.spread.refresh();
+        }
+    });
+    $.divResizer({
+        select: '#gcl-spr',
+        callback: function () {
+            priceSpread.refresh();
+            ledgerGcl.spread.refresh();
+            ledgerGcl.xmjSpread.refresh();
+        }
+    });
+
+    postData('load', { filter: 'bills;pos;price' }, result => {
+        revisePrice.loadDatas(result.price);
+        SpreadJsObj.loadSheetData(priceSheet, SpreadJsObj.DataType.Data, revisePrice.data);
+        ledgerGcl.loadData(result.bills, result.pos);
+        $("[content='#ledgerGcl']").click();
+    });
+
+    $('a', '#side-menu').bind('click', function (e) {
+        e.preventDefault();
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        // 展开工具栏、切换标签
+        if (!tab.hasClass('active')) {
+            $('a', '#side-menu').removeClass('active');
+            tab.addClass('active');
+            $('.tab-content .tab-pane').removeClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            ledgerGcl.spread.refresh();
+            ledgerGcl.xmjSpread.refresh();
+        } else {// 收起工具栏
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        priceSpread.refresh();
+    });
+});

+ 8 - 7
app/public/js/schedule_stage_tp.js

@@ -86,15 +86,15 @@ $(function () {
     if (curScheduleStage && curScheduleStage.order) {
         postData('/tender/' + getTenderId() + '/schedule/stage/' + curScheduleStage.order + '/load', {}, function (data) {
             const calcList = ['year_gather_tp',
-                'contract_tp', 'qc_qty', 'qc_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+                'qc_qty', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
                 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp',
-                'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
+                'final_contract_tp', 'final_qc_tp', 'final_pc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
             const showList = ['plan_gcl', 'plan_tp', 'next_plan_gcl', 'next_plan_tp', 'end_plan_gcl', 'end_plan_tp', 'year_plan_gcl', 'year_plan_tp',
                 'year_contract_qty', 'year_gather_tp',
                 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
                 'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
                 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp',
-                'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
+                'final_contract_tp', 'final_qc_tp', 'final_pc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
             const baseLedgerTreeSetting = {
                 id: 'ledger_id',
                 pid: 'ledger_pid',
@@ -104,7 +104,7 @@ $(function () {
                 fullPath: 'full_path',
                 calcFields: calcList,
             };
-            baseLedgerTreeSetting.updateFields = ['year_contract_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'final_contract_tp', 'final_qc_tp', 'postil', 'used', 'contract_expr'];
+            baseLedgerTreeSetting.updateFields = ['year_contract_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'final_contract_tp', 'final_qc_tp', 'final_pc_tp', 'postil', 'used', 'contract_expr'];
             baseLedgerTreeSetting.calcFun = function (node) {
                 if (!node.children || node.children.length === 0) {
                     node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -114,7 +114,7 @@ $(function () {
                     node.end_gather_qty = ZhCalc.add(node.pre_gather_qty, node.gather_qty);
                 }
                 node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
-                node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
+                node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
                 node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
                 node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
                 node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
@@ -132,8 +132,9 @@ $(function () {
                 node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
 
                 node.pre_final_gather_tp = ZhCalc.add(node.pre_final_contract_tp, node.pre_final_qc_tp);
-                node.final_gather_tp = ZhCalc.add(node.final_contract_tp, node.final_qc_tp);
+                node.final_gather_tp = ZhCalc.sum([node.final_contract_tp, node.final_qc_tp, node.final_pc_tp]);
                 node.end_final_gather_tp = ZhCalc.add(node.pre_final_gather_tp, node.final_gather_tp);
+                console.log(node)
             };
             const baseLedgerTree = createNewPathTree('base', baseLedgerTreeSetting);
             const newLedgerList = setMonthToLedger(data.ledgerData, slmList, nextSlmList, endSlmList, yearSlmList, curYearStageData);
@@ -294,7 +295,7 @@ function setMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm, yearLedgerD
             });
             if (index !== undefined && index !== -1) {
                 ledgerList[index].year_contract_qty = ZhCalc.add(yl.contract_qty, yl.qc_qty);
-                ledgerList[index].year_gather_tp = ZhCalc.add(yl.contract_tp, yl.qc_tp);
+                ledgerList[index].year_gather_tp = ZhCalc.sum([yl.contract_tp, yl.qc_tp, yl.pc_tp]);
             }
         }
     }

+ 6 - 0
app/public/js/setting.js

@@ -149,6 +149,12 @@ $(document).ready(() => {
                 } else {
                     $('#edit-user2 input[name="cooperation"]').attr('disabled', true);
                 }
+                if (pm === 'tender' && permission[pm].indexOf('5') !== -1) {
+                    $('#edit-user2 input:checkbox[id="change_1"]').prop('checked', true);
+                }
+                if (pm === 'tender' && permission[pm].indexOf('4') !== -1) {
+                    $('#edit-user2 input:checkbox[id="material_1"]').prop('checked', true);
+                }
                 if (allPermission[pm].type === 'checkbox') {
                     for (const index of permission[pm]) {
                         $('#edit-user2 input:checkbox[id="' + pm + '_' + index + '"]').prop('checked', true);

+ 93 - 0
app/public/js/shares/cs_gcl_gather.js

@@ -0,0 +1,93 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+(function($){
+    $.cs_gclGather = function (setting) {
+        if (!setting.selector) return;
+
+        if (!setting.spreadSetting) {
+            setting.spreadSetting = {
+                cols: [
+                    {title: '清单编号', field: 'b_code', width: 80, formatter: '@'},
+                    {title: '名称', field: 'name', width: 150, formatter: '@'},
+                    {title: '单位', field: 'unit', width: 50, formatter: '@', hAlign: 1, },
+                    {title: '单价', field: 'unit_price', width: 60, hAlign: 2, },
+                    {title: '数量', field: 'quantity', width: 60, hAlign: 2, },
+                    {title: '金额', field: 'total_price', width: 60, hAlign: 2, },
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                selectedBackColor: '#fffacd',
+                readOnly: true,
+            };
+        }
+        if (!setting.gatherFields) setting.gatherFields = ['quantity', 'total_price'];
+
+        const resultId = setting.id + '-spread';
+        const obj = $(setting.selector);
+        obj.html(
+            '<div class="sjs-bar">\n' +
+            `    <div class="pb-1 d-flex"><button class="btn btn-primary btn-sm" id="gcl-gather-refresh">汇总</button>\n` +
+            '        <span class="pl-2" id="' + setting.id + '-info"></span>\n' +
+            '    </div>\n' +
+            '</div>' +
+            '<div id="' + resultId + '" class="sjs-sh">\n' +
+            '</div>'
+        );
+        autoFlashHeight();
+
+        const spread = SpreadJsObj.createNewSpread($('#' + resultId)[0]);
+        const sheet = spread.getActiveSheet();
+        const result = [];
+        SpreadJsObj.initSheet(sheet, setting.spreadSetting);
+        SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Data, result);
+
+        const recursiveGather = function(nodes) {
+            for (const node of nodes) {
+                if (node.children && node.children.length > 0) {
+                    recursiveGather(node.children);
+                } else {
+                    if (!node.b_code) continue;
+                    let gcl = result.find(x => {
+                        return x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price;
+                    });
+                    if (!gcl) {
+                        gcl = { b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price };
+                        result.push(gcl);
+                    }
+                    for (const f of setting.gatherFields) {
+                        gcl[f] = ZhCalc.add(gcl[f], node[f]);
+                    }
+                }
+            }
+        };
+
+        const gather = function (node) {
+            $(`#${setting.id}-info`).html(`${node.code || ''}${node.b_code || ''} ${node.name || ''} - ${moment(new Date()).format('YYYY-MM-DD HH:mm:ss')}`);
+            result.length = 0;
+            recursiveGather([node]);
+            result.sort((a, b) => {
+                return checkUtils.compareCode(a.b_code, b.b_code);
+            });
+            SpreadJsObj.reLoadSheetData(sheet);
+        };
+
+        $('#gcl-gather-refresh').click(() => {
+            gather(SpreadJsObj.getSelectObject(setting.relaSheet));
+        });
+
+        return { spread, sheet, gather}
+    };
+
+})(jQuery);

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

@@ -685,6 +685,7 @@ const showSelectTab = function(select, spread, afterShow) {
         };
 
         const getTagDisplayHtml = function (tag) {
+            console.log(tag);
             const tagClass = classIndexes.find(x => {return x.color === tag.color}) || {};
             const tagHtml = [];
             tagHtml.push('<div name="tag-view">');
@@ -693,7 +694,7 @@ const showSelectTab = function(select, spread, afterShow) {
             tagHtml.push('</div>');
             tag.node && tagHtml.push((tag.node.code || '') + (tag.node.b_code || ''), ' / ', tag.node.name || '');
             if (tag.share) {
-                tagHtml.push('<i class="fa fa-users pull-right text-warning" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="所有参与台账审批管理的用户都可以看到这条书签"></i>')
+                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.ledger_id : -1;
@@ -1172,7 +1173,6 @@ const showSelectTab = function(select, spread, afterShow) {
         const sheet = spread.getActiveSheet();
         SpreadJsObj.initSheet(sheet, spreadSetting);
 
-
         if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, setting.cellDoubleClick);
         sheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
@@ -1307,4 +1307,186 @@ const showSelectTab = function(select, spread, afterShow) {
 
         return { spread }
     };
+
+    $.gljLib = function (setting) {
+        if (!setting.selector) return;
+        const obj = $(setting.selector);
+        const stdLibHtml = setting.libs.map(l => {
+            return `<option value="${l.id}" >${l.name}</option>`;
+        });
+        const relaSelect = {
+            showLevel: `std-${setting.stdType}-sl`,
+            searchText: `std-${setting.stdType}-st`,
+            searchResult: `std-${setting.stdType}-sr`,
+            searchPre: `std-${setting.stdType}-sp`,
+            searchNext: `std-${setting.stdType}-sn`,
+            searchClose: `std-${setting.stdType}-sc`,
+        };
+        obj.html(
+            '<div class="sjs-bar d-flex">\n' +
+            `    <div class="input-group input-group-sm pr-1"><select class="form-control form-control-sm col-auto">${stdLibHtml.join('')}</select></div>\n` +
+            '    <div class="ml-1">\n' +
+            '        <div class="dropdown">\n' +
+            '            <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+            '                <i class="fa fa-search"></i>\n' +
+            '            </button>\n' +
+            '            <div class="dropdown-menu dropdown-menu-right">\n' +
+            '                <div class="px-2 border-primary border-1 d-flex" style="width: 300px">\n' +
+            '                    <div class="d-inline-block">\n' +
+            '                        <div class="input-group input-group-sm mr-1">' +
+            `                            <input type="text" class="form-control" placeholder="输入编号/名称查找" id="${relaSelect.searchText}">\n` +
+            `                            <div class="input-group-append" ><span class="input-group-text" id="${relaSelect.searchResult}">结果:0</span></div>\n` +
+            '                            <div class="input-group-append" >\n' +
+            `                                <button class="btn btn-outline-secondary" type="button" title="上一个" id="${relaSelect.searchPre}"><i class="fa fa-angle-double-left"></i></button>\n` +
+            `                                <button class="btn btn-outline-secondary" type="button" title="下一个" id="${relaSelect.searchNext}"><i class="fa fa-angle-double-right"></i></button>\n` +
+            '                            </div>\n' +
+            '                        </div>\n' +
+            '                    </div>\n' +
+            `                    <div class="d-inline-block"><button class="btn btn-light text-danger btn-sm ml-1" type="button" id="${relaSelect.searchClose}"><i class="fa fa-remove"></i></button></div>\n` +
+            '                </div>\n' +
+            '            </div>\n' +
+            '        </div>\n' +
+            '    </div>' +
+            '</div>\n' +
+            `<div id="std-${setting.stdType}-spread" class="${setting.spreadClassCss ? setting.spreadClassCss : 'sjs-sh'}"></div>\n`
+        );
+        autoFlashHeight();
+        const pathTree = createNewPathTree('base', setting.treeSetting);
+
+        const spreadSetting = {
+            cols: [
+                {title: '名称', field: 'name', hAlign: 0, width: 180, formatter: '@'},
+                {title: '单位', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+            ],
+            treeCol: 0,
+            emptyRows: 0,
+            headRows: 1,
+            headRowHeight: [32],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+            headColWidth: [30],
+            selectedBackColor: '#fffacd',
+            readOnly: true,
+            stdType: setting.stdType,
+        };
+        spreadSetting.cols.unshift(setting.stdType === 'xmj'
+            ? {title: '项目节编号', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'}
+            : {title: '清单编号', field: 'b_code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'});
+
+        const spread = SpreadJsObj.createNewSpread($(`#std-${setting.stdType}-spread`)[0]);
+        const sheet = spread.getActiveSheet();
+        SpreadJsObj.initSheet(sheet, setting.spreadSetting ? setting.spreadSetting : spreadSetting);
+
+        if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, setting.cellDoubleClick);
+        sheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+            if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
+                SpreadJsObj.saveTopAndSelect(info.sheet, cacheKey.node);
+            }
+        });
+        sheet.bind(spreadNS.Events.TopRowChanged, function (e, info) {
+            SpreadJsObj.saveTopAndSelect(info.sheet, cacheKey.node);
+        });
+
+        const cacheLib = [];
+        const cacheKey = {
+            lib: setting.page + '-' + setting.tid + '-' + setting.stdType,
+        };
+
+        $('select', setting.selector).change(function () {
+            loadLib(parseInt(this.value), true);
+        });
+
+        const loadLib = function(listId, reload = false) {
+            cacheKey.node = cacheLib.lib + '-' + listId;
+            const locateMemory = function () {
+                if (!reload) {
+                    SpreadJsObj.loadTopAndSelect(sheet, cacheKey.node);
+                } else {
+                    removeLocalCache(cacheKey.node);
+                }
+            };
+            const cacheData = cacheLib.find(function (lib) {
+                return lib.id === listId;
+            });
+            if (cacheData) {
+                pathTree.loadDatas(cacheData.data);
+                SpreadJsObj.loadSheetData(sheet, 'tree', pathTree);
+                locateMemory();
+                setLocalCache(cacheKey.lib, listId);
+            } else {
+                postData(`/std-lib/get-data`, {stdType: setting.stdType, list_id: listId}, function (data) {
+                    cacheLib.push({id: listId, data: data});
+                    pathTree.loadDatas(data);
+                    SpreadJsObj.loadSheetData(sheet, 'tree', pathTree);
+                    locateMemory();
+                    setLocalCache(cacheKey.lib, listId);
+                });
+            }
+        };
+
+        const _loadCacheLib = function() {
+            let libId = getLocalCache(cacheKey.lib);
+            if (libId) {
+                $('select', setting.selector).val(libId);
+                loadLib(parseInt(libId));
+            } else {
+                loadLib(parseInt($('select', setting.selector).val()));
+            }
+        };
+        _loadCacheLib();
+
+        const searchObj = {
+            result: [],
+            cur: 0,
+            searchStdNode: function() {
+                const keyword = $(`#${relaSelect.searchText}`).val();
+                searchObj.result = keyword ? pathTree.nodes.filter(x => {
+                    return x.code.indexOf(keyword) >= 0 || x.name.indexOf(keyword) >= 0;
+                }) : [];
+                // searchObj.result = [];
+                // for (const x of pathTree.nodes) {
+                //     if (x.code.indexOf(keyword) >= 0 || x.b_code.indexOf(keyword) >= 0 || x.name.indexOf(keyword) >= 0) {
+                //         searchObj.result.push(x);
+                //     }
+                // }
+                $(`#${relaSelect.searchResult}`)[0].innerText = `结果:${searchObj.result.length}`;
+                searchObj.cur = 0;
+                if (searchObj.result.length > 0) {
+                    SpreadJsObj.locateTreeNode(sheet, pathTree.getNodeKey(searchObj.result[searchObj.cur]));
+                }
+            },
+            searchPre: function () {
+                if (searchObj.result.length === 0) return;
+                searchObj.cur = searchObj.cur === 0 ? searchObj.result.length - 1 : this.cur - 1;
+                SpreadJsObj.locateTreeNode(sheet, pathTree.getNodeKey(searchObj.result[searchObj.cur]), true);
+            },
+            searchNext: function () {
+                if (searchObj.result.length === 0) return;
+                searchObj.cur = searchObj.cur === searchObj.result.length - 1 ? 0 : searchObj.cur + 1;
+                SpreadJsObj.locateTreeNode(sheet, pathTree.getNodeKey(searchObj.result[searchObj.cur]), true);
+            },
+            clear: function () {
+                $(`#${relaSelect.searchText}`).val('');
+                $(`#${relaSelect.searchResult}`)[0].innerText = `结果:${0}`;
+                searchObj.result = [];
+                searchObj.cur = 0;
+            }
+        };
+        $(`#${relaSelect.searchText}`).change(searchObj.searchStdNode);
+        $(`#${relaSelect.searchPre}`).click(function (e) {
+            searchObj.searchPre();
+            e.stopPropagation();
+        });
+        $(`#${relaSelect.searchNext}`).click(function (e) {
+            searchObj.searchNext();
+            e.stopPropagation();
+        });
+        $(`#${relaSelect.searchClose}`).click(function (e) {
+            searchObj.clear();
+            e.stopPropagation();
+        });
+
+        return { spread }
+    };
 })(jQuery);

+ 18 - 1
app/public/js/shares/gcl_gather_compare.js

@@ -397,6 +397,23 @@ const gclCompareModel = (function () {
         ledgerSetting = null;
     }
 
+    function gatherReviseLedgerData(bills, pos, setting, price, decimal) {
+        ledgerSetting = setting;
+        try {
+            if (leafXmjs.length > 0) leafXmjs.length = 0;
+            gsTree = createNewPathTree('revise', setting.tree);
+            gsTree.loadRevisePrice(price, decimal);
+            gsTree.loadDatas(bills);
+            const gsPos = new PosData(setting.pos);
+            gsPos.loadDatas(pos);
+            recursiveGatherGclData(gsTree.children, null, gsPos);
+            _gatherChapter();
+        } catch(err) {
+            console.log(err);
+        }
+        ledgerSetting = null;
+    }
+
     function compareCode(str1, str2, symbol = '-') {
         if (!str1) {
             return 1;
@@ -483,7 +500,7 @@ const gclCompareModel = (function () {
 
     return {
         init,
-        gatherLedgerData, gatherDealBills, checkDiffer,
+        gatherLedgerData, gatherReviseLedgerData, gatherDealBills, checkDiffer,
         chapterData
     };
 })();

+ 73 - 0
app/public/js/shares/show_level.js

@@ -0,0 +1,73 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+$.cs_showLevel = function (setting) {
+    const sortTitle = ['第一层', '第二层', '第三层','第四层', '第五层'];
+    if (!setting.selector) throw '未指定模块';
+    if (!setting.levels) {
+        setting.levels = [
+            { type: 'sort', count: 5, },
+            { type: 'last', title: '最底层' },
+            // { type: 'leafXmj', title: '只显示项目节' },
+            // { type: 'curMeasure', title: '只显示本期计量' },
+        ];
+    }
+
+    const initShowLevel = function () {
+        const obj = $(setting.selector);
+        const html = [];
+        html.push('<button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' +
+            '<i class="fa fa-list-ol"></i> 显示层级</button>');
+        html.push('<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" id="showLevelList">');
+        for (const l of setting.levels) {
+            if (l.type === 'sort') {
+                let count = typeof l.count === 'function' ? l.count() : l.count;
+                count = Math.min(count, 5);
+                for (let i = 1; i <= l.count; ++i) {
+                    html.push(`<a class="dropdown-item" name="showLevel" tag="${i}" href="javascript: void(0);">${sortTitle[i-1]}</a>`);
+                }
+            } else {
+                html.push(`<a class="dropdown-item" name="showLevel" tag="${l.type}" href="javascript: void(0);">${l.title}</a>`);
+            }
+        }
+        html.push('</div>');
+        obj.html(html.join(''));
+
+
+        $('a[name=showLevel]').click(function () {
+            setTimeout(() => {
+                const tag = $(this).attr('tag');
+                showWaitingView();
+                setting.showLevel(tag);
+                closeWaitingView();
+            });
+        });
+        refreshMenuVisible();
+    };
+
+    const refreshMenuVisible = function () {
+        const showMenu = function (tag, visible) {
+            if (visible) $(`[tag=${tag}]`).show();
+            if (!visible) $(`[tag=${tag}]`).hide();
+        };
+        for (const l of setting.levels) {
+            if (l.type === 'sort') {
+                let visibleCount = typeof l.visible_count === 'function' ? l.visible_count() : l.visible_count;
+                for (let i = 1; i <= l.count; ++i) {
+                    showMenu(i, i <= visibleCount);
+                }
+            } else {
+                const visible = typeof l.visible === 'function' ? l.visible() : l.visible;
+                showMenu(l.type, visible);
+            }
+        }
+    };
+
+    return { initShowLevel, refreshMenuVisible } ;
+};

+ 13 - 1
app/public/js/shares/sjs_setting.js

@@ -92,5 +92,17 @@ const sjsSettingObj = (function () {
             col.showImage = r.showImage;
         }
     };
-    return {setFxTreeStyle, FxTreeStyle, setGridSelectStyle, setTpThousandthFormat, setThousandthFormat, setTpColsThousandthFormat, setPropValue, set3FCols, setQcCols};
+    const setOrgPriceCol = function (cols, rela) {
+        for (const r of rela) {
+            const col = _.find(cols, {field: r.field});
+            if (col) {
+                col.getValue = function (data) { return data.contract_pc_tp || data.qc_pc_tp || data.pc_tp ? data.org_price : null; };
+            }
+        }
+    };
+    return {
+        setFxTreeStyle, FxTreeStyle, setGridSelectStyle,
+        setTpThousandthFormat, setThousandthFormat, setTpColsThousandthFormat,
+        setPropValue, set3FCols, setQcCols, setOrgPriceCol,
+    };
 })();

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

@@ -406,6 +406,7 @@ const SpreadJsObj = {
             SpreadJsObj.selChangedRefreshBackColor(sheet);
         }
         this.endMassOperation(sheet);
+        if (!setting.invalidCopyFilterHiddenRow) this.copyFilterHiddenRow(sheet);
     },
     reLoadSheetHeader: function (sheet) {
         if (sheet.zh_setting) {
@@ -660,6 +661,12 @@ const SpreadJsObj = {
             }
             sheet.getRange(-1, col, -1, 1).cellType(sheet.extendCellType.autoTip);
         }
+        if (colSetting.cellType === 'delayTip') {
+            if (!sheet.extendCellType.tip) {
+                sheet.extendCellType.tip = this.CellType.getDelayTipCellType();
+            }
+            sheet.getRange(-1, col, -1, 1).cellType(sheet.extendCellType.tip);
+        }
         if (colSetting.cellType === 'checkbox') {
             if (!sheet.extendCellType.checkbox) {
                 sheet.extendCellType.checkbox = new spreadNS.CellTypes.CheckBox();
@@ -987,6 +994,11 @@ const SpreadJsObj = {
         }
         return result;
     },
+    copyFilterHiddenRow: function (sheet) {
+        sheet.bind(spreadNS.Events.ClipboardChanged, function (e, info) {
+            SpreadJsObj.Clipboard.setSysClipboard(SpreadJsObj.getFilterCopyText(info.sheet));
+        });
+    },
     /**
      * 树表结构,定位至指定的节点
      * @param {GC.Spread.Sheets.Worksheet} sheet - 需要定位的sheet
@@ -1565,6 +1577,104 @@ const SpreadJsObj = {
 
             return new AutoTipCellType();
         },
+        getDelayTipCellType: function () {
+            const maxHintWidth = 200, indent = 15, borderIndent = 10;
+            const TipCellType = function () {};
+            // 继承 SpreadJs定义的 普通的TextCellType
+            TipCellType.prototype = new spreadNS.CellTypes.Text();
+            const proto = TipCellType.prototype;
+            proto.getTextDisplayWidth = function(hitinfo, str, font) {
+                const xs = hitinfo.sheet.getParent().xs;
+                const ctx = xs.childNodes[0].getContext("2d");
+                if (font && font !== '') {
+                    ctx.font = font;
+                } else {
+                    ctx.font = hitinfo.cellStyle.font;
+                }
+                return ctx.measureText(str).width;
+            };
+            proto.showTip = function (hitinfo, text) {
+                return text && text !== '';
+            };
+            /**
+             * 获取点击信息
+             * @param {Number} x
+             * @param {Number} y
+             * @param {Object} cellStyle
+             * @param {Object} cellRect
+             * @param {Object} context
+             * @returns {{x: *, y: *, row: *, col: *|boolean|*[]|number|{}|UE.dom.dtd.col, cellStyle: *, cellRect: *, sheet: *|StyleSheet, sheetArea: *}}
+             */
+            proto.getHitInfo = function (x, y, cellStyle, cellRect, context) {
+                return {
+                    x: x,
+                    y: y,
+                    row: context.row,
+                    col: context.col,
+                    cellStyle: cellStyle,
+                    cellRect: cellRect,
+                    sheet: context.sheet,
+                    sheetArea: context.sheetArea,
+                    ctx: context.sheet.getParent().xs,
+                };
+            };
+            /**
+             * 鼠标进入单元格事件 - 显示悬浮提示
+             * @param {Object} hitinfo - 见getHitInfo返回值
+             */
+            proto.processMouseEnter = function (hitinfo) {
+                let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
+                const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
+                if (col.getTip && Object.prototype.toString.apply(col.getTip) === "[object Function]") {
+                    const sortData = SpreadJsObj.getSortData(hitinfo.sheet);
+                    text = col.getTip(sortData[hitinfo.row]);
+                }
+                const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
+                if (pos && this.showTip(hitinfo, text)) {
+                    if (!this._toolTipElement) {
+                        let div = $('#autoTip')[0];
+                        if (!div) {
+                            div = document.createElement("div");
+                            $(div).css("position", "absolute")
+                                .css("border", "1px #C0C0C0 solid")
+                                .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
+                                .css("font", "9pt Arial")
+                                .css("background", "white")
+                                .css("padding", 5)
+                                .css("z-index", 999)
+                                .css("max-width", maxHintWidth)
+                                .css("word-wrap", "break-word")
+                                .attr("id", 'autoTip');
+                            document.body.insertBefore(div, null);
+                        }
+                        const validWidth = $(window).width() - (pos.x + hitinfo.x + indent) - borderIndent;
+                        const textWidth = this.getTextDisplayWidth(hitinfo, text, "9pt Arial");
+                        if (validWidth >= maxHintWidth || textWidth <= validWidth) {
+                            $(div).html(text).css("top", pos.y + hitinfo.y + indent).css("left", pos.x + hitinfo.x + indent);
+                        } else if (textWidth > maxHintWidth) {
+                            $(div).html(text).css("top", pos.y + hitinfo.y + indent).css("left", pos.x + hitinfo.x - indent - maxHintWidth);
+                        } else {
+                            $(div).html(text).css("top", pos.y + hitinfo.y + indent).css("left", pos.x + hitinfo.x - indent - textWidth);
+                        }
+                        this._toolTipElement = div;
+                        const self = this;
+                        setTimeout(()=>{ if (self._toolTipElement) $(self._toolTipElement).show("fast");}, 300);
+                    }
+                }
+            };
+            /**
+             * 鼠标移出单元格事件 - 隐藏悬浮提示
+             * @param {Object} hitinfo - 见getHitInfo返回值
+             */
+            proto.processMouseLeave = function (hitinfo) {
+                if (this._toolTipElement) {
+                    $(this._toolTipElement).hide();
+                    this._toolTipElement = null;
+                }
+            };
+
+            return new TipCellType();
+        },
         /**
          * 获取 带图片的cellType(图片需在document中定义好img,并写入col的img属性)
          *
@@ -2634,10 +2744,32 @@ const SpreadJsObj = {
         const getAnalysisPasteHtml = function () {
             return SpreadJsObj.analysisPasteHtml(cHtml);
         };
+        const setSysClipboard = function(text) {
+            let textArea = document.createElement("textarea");
+            textArea.style.position = 'fixed';
+            textArea.style.top = 0;
+            textArea.style.left = 0;
+            textArea.style.width = '2em';
+            textArea.style.height = '2em';
+            textArea.style.padding = 0;
+            textArea.style.border = 'none';
+            textArea.style.outline = 'none';
+            textArea.style.boxShadow = 'none';
+            textArea.style.background = 'transparent';
+            textArea.value = text;
+            document.body.appendChild(textArea);
+            textArea.select();
+            try {
+                document.execCommand('copy');
+            } catch (err) {
+            }
+            document.body.removeChild(textArea);
+        };
         return {
             setCopyData, getPasteData, clearData,
             setSheetFilterCopyData,
             getAnalysisPasteText, getAnalysisPasteHtml,
+            setSysClipboard
         }
     })(),
 };

+ 29 - 7
app/public/js/sr_detail.js

@@ -36,7 +36,7 @@ function customColDisplay () {
         { title: '本期完成计量', fields: ['gather_qty', 'gather_tp'], visible: true },
         { title: '截止本期计量合同', fields: ['end_contract_qty', 'end_contract_tp'], visible: true },
         { title: '截止本期数量变更', fields: ['end_qc_qty', 'end_qc_tp', 'end_qc_bgl'], visible: true },
-        { title: '截止本期完成计量', fields: ['end_gather_qty', 'end_gather_tp', 'end_gather_percent'], visible: true },
+        { title: '截止本期完成计量', fields: ['end_gather_qty', 'end_gather_tp', 'end_gather_percent', 'end_final_1_percent'], visible: true },
         { title: '本期批注', fields: ['postil'], visible: true },
         { title: '图册号', fields: ['drawing_code'], visible: true },
         { title: '备注', fields: ['memo'], visible: true },
@@ -165,16 +165,26 @@ $(document).ready(() => {
         markExpandSubKey: window.location.pathname.split('/')[2],
     };
     // 台账树结构计算相关设置
-    stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
-    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'];
+    stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil', 'used', 'contract_expr'];
+    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+        'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp', 'final_1_tp', 'end_final_1_tp'];
     stageTreeSetting.calcFun = function (node) {
         if (!node.children || node.children.length === 0) {
             node.gather_qty = ZhCalc.add(node.contract_qty, node.qc_qty);
             node.end_gather_qty = ZhCalc.add(node.end_contract_qty, node.end_qc_qty);
+            node.end_qc_minus_qty = ZhCalc.add(node.pre_qc_minus_qty, node.qc_minus_qty);
+            node.final_1_qty = ZhCalc.add(node.end_qc_minus_qty, node.quantity);
+            node.final_1_tp = ZhCalc.mul(node.final_1_qty, node.unit_price, tenderInfo.decimal.tp);
+            node.deal_final_1_qty = ZhCalc.add(node.end_qc_minus_qty, node.deal_qty);
+            node.end_final_1_qty = ZhCalc.add(node.end_qc_qty, node.final_1_qty);
         }
         node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
+        node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
+        node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
+        node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
         node.end_gather_tp = ZhCalc.add(node.end_contract_tp, node.end_qc_tp);
         node.end_final_tp = ZhCalc.add(node.end_qc_tp, node.total_price);
+        node.end_final_1_tp = ZhCalc.add(node.end_qc_tp, node.final_1_tp);
         if (!node.children || node.children.length === 0) {
             if (node.end_contract_qty) {
                 node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));
@@ -185,6 +195,8 @@ $(document).ready(() => {
         node.end_gather_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_tp), 100, 2);
         node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
         node.final_dgn_price = ZhCalc.round(ZhCalc.div(node.end_gather_tp, ZhCalc.add(node.deal_dgn_qty1, node.c_dgn_qty1)), tenderInfo.decimal.up);
+        node.end_final_1_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_1_tp), 100, 2);
+        node.end_correct_1_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_1_tp), 100, 2);
     };
     const stageTree = createNewPathTree('stage', stageTreeSetting);
     // 初始化 计量单元 数据结构
@@ -192,19 +204,23 @@ $(document).ready(() => {
     stagePosSetting.calcFun = function (pos) {
         pos.gather_qty = ZhCalc.add(pos.contract_qty, pos.qc_qty);
         pos.end_gather_qty = ZhCalc.add(pos.end_contract_qty, pos.end_qc_qty);
+        pos.end_qc_minus_qty = ZhCalc.add(pos.pre_qc_minus_qty, pos.qc_minus_qty);
         pos.sum = ZhCalc.add(pos.end_qc_qty, pos.quantity);
         pos.end_gather_percent = ZhCalc.mul(ZhCalc.div(pos.end_gather_qty, pos.sum), 100, 2);
         pos.estimate_qty = !checkZero(pos.real_qty)
             ? ZhCalc.sub(ZhCalc.sub(pos.real_qty, pos.quantity), pos.end_qc_qty)
             : null;
+        pos.final_1_qty = ZhCalc.add(pos.end_qc_minus_qty, pos.quantity);
+        pos.deal_final_1_qty = ZhCalc.add(pos.end_qc_minus_qty, pos.deal_qty);
+        pos.end_final_1_qty = ZhCalc.add(pos.end_qc_qty, pos.final_1_qty);
     };
     const stagePos = new StagePosData(stagePosSetting);
 
     // 初始化 台账 spread
     const slSpread = SpreadJsObj.createNewSpread($('#stage-ledger')[0]);
     customizeStageTreeSetting(ledgerSpreadSetting, customColDisplay());
-    const ratioCol = ledgerSpreadSetting.cols.find(x => {return x.field === 'end_gather_percent' || x.field === 'end_correct_percent'});
-    ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_percent' : 'end_gather_percent';
+    const ratioCol = ledgerSpreadSetting.cols.find(x => {return x.field === 'end_final_1_percent' || x.field === 'end_correct_1_percent'});
+    if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_1_percent' : 'end_final_1_percent';
     ledgerSpreadSetting.imageClick = function (data, hitinfo) {
         const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
         if (col.field === 'dagl') data.dagl_url && window.open(data.dagl_url);
@@ -894,14 +910,20 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
                 $('[name=type-title-contract]').text('本期合同计量金额');
                 $('[name=type-title-qc]').text('本期变更计量金额');
-                $('#show-qc-minus-jl').parent().hidden();
-                $('#qc-minus-jl').parent().hidden();
+                if ($('#show-qc-minus-jl').length > 0) {
+                    $('#show-qc-minus-jl').parent().hide();
+                    $('#qc-minus-jl').parent().hide();
+                }
             } else {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量数量';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
                 $('[name=type-title-contract]').text('本期合同计量数量');
                 $('[name=type-title-qc]').text('本期变更计量数量');
+                if ($('#show-qc-minus-jl').length > 0) {
+                    $('#show-qc-minus-jl').parent().show();
+                    $('#qc-minus-jl').parent().show();
+                }
             }
             if (relaStage.im_type === imType.bb.value || relaStage.im_type === imType.bw.value) {
                 $('#show-jldy').parent().show();

+ 174 - 24
app/public/js/stage.js

@@ -155,8 +155,8 @@ function getNodeList(node) {
     }
     $('#nodelist-table').html(html);
     $('#nodelist-table').on('click', 'tr', function() {
-        $('#nodelist-table tr').removeClass('bg-light')
-        $(this).addClass('bg-light')
+        $('#nodelist-table tr').removeClass('bg-light');
+        $(this).addClass('bg-light');
     })
 }
 
@@ -268,7 +268,7 @@ $(document).ready(() => {
     };
     // 台账树结构计算相关设置
     stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil', 'used', 'contract_expr'];
-    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
         'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp', 'final_1_tp', 'end_final_1_tp'];
     stageTreeSetting.calcFun = function (node) {
         if (!node.children || node.children.length === 0) {
@@ -284,9 +284,9 @@ $(document).ready(() => {
             node.end_final_1_qty = ZhCalc.add(node.end_qc_qty, node.final_1_qty);
         }
         node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
-        node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
-        node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
-        node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
+        node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+        node.end_contract_tp = ZhCalc.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+        node.end_qc_tp = ZhCalc.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
         node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
         node.end_final_tp = ZhCalc.add(node.end_qc_tp, node.total_price);
         node.end_final_1_tp = ZhCalc.add(node.end_qc_tp, node.final_1_tp);
@@ -659,6 +659,7 @@ $(document).ready(() => {
         {field: 'qc_qty', showImage: qcColShowImage},
         {field: 'qc_minus_qty', showImage: qcColShowImage},
     ]);
+    sjsSettingObj.setOrgPriceCol(ledgerSpreadSetting.cols, [ {field: 'org_price'} ]);
     // const ratioCol = ledgerSpreadSetting.cols.find(x => {return x.field === 'end_gather_percent' || x.field === 'end_correct_percent'});
     // if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_percent' : 'end_gather_percent';
     const ratioCol = ledgerSpreadSetting.cols.find(x => {return x.field === 'end_final_1_percent' || x.field === 'end_correct_1_percent'});
@@ -1377,7 +1378,7 @@ $(document).ready(() => {
                         gather_qty: node.gather_qty, gather_tp: node.gather_tp,
                         end_contract_qty: node.end_contract_qty, end_contract_tp: node.end_contract_tp,
                         end_qc_qty: node.end_qc_qty, end_qc_tp: node.end_qc_tp,
-                        end_gather_qty: node.end_gather_qty, end_gather_tp: node.end_gather_tp, end_gather_percent: node.end_gather_percent,
+                        end_gather_qty: node.end_gather_qty, end_gather_tp: node.end_gather_tp, end_final_1_percent: node.end_final_1_percent,
                         deal_dgn_qty1: node.deal_dgn_qty1, deal_dgn_qty2: node.deal_dgn_qty2,
                         c_dgn_qty1: node.c_dgn_qty1, c_dgn_qty2: node.c_dgn_qty2,
                         final_dgn_price: node.final_dgn_price,
@@ -1784,7 +1785,7 @@ $(document).ready(() => {
             if (info.sheet.zh_setting) {
                 const sortData = info.sheet.zh_data;
                 const range = info.cellRange;
-                const validField = ['contract_qty', 'qc_qty', 'qc_minus_qty', 'postil', 'real_qty', 'ex_memo1', 'ex_memo2', 'ex_memo3'];
+                const validField = ['contract_qty', 'postil', 'real_qty', 'ex_memo1', 'ex_memo2', 'ex_memo3'];
                 if (!checkTzMeasureType()) {
                     validField.push('name', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'position', 'drawing_code');
                 }
@@ -2017,7 +2018,6 @@ $(document).ready(() => {
         },
         selectionChanged: function (e, info) {
             stagePosSpreadObj.loadExprToInput(info.sheet);
-            console.log(SpreadJsObj.getSelectObject(info.sheet));
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -2191,6 +2191,23 @@ $(document).ready(() => {
                     stagePosSpreadObj.deletePos(spSpread.getActiveSheet());
                 }
             },
+            'sort': {
+                name: '排序',
+                items: {
+                    "addStageReverse": {
+                        name: '按新增期倒序',
+                        icon: 'fa-sort-amount-desc',
+                        callback: function (key, opt) {
+                            stagePos.sort = [['add_stage_order', 'desc'], ['porder', 'asc']];
+                            const spSheet = spSpread.getActiveSheet();
+                            if (spSheet.zh_data) {
+                                spSheet.zh_data.sort((x, y) => { return y.add_stage_order - x.add_stage_order || x.porder - y.porder; });
+                                SpreadJsObj.reLoadSheetData(spSheet);
+                            }
+                        }
+                    }
+                }
+            },
             'merge-peg': {
                 name: '合并起讫桩号',
                 visible: function (key, opt) {
@@ -2921,16 +2938,20 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
                 $('[name=type-title-contract]').text('本期合同计量金额');
                 $('[name=type-title-qc]').text('本期变更计量金额');
-                $('#show-qc-minus-jl').parent().hide();
-                $('#qc-minus-jl').parent().hide();
+                if ($('#show-qc-minus-jl').length > 0) {
+                    $('#show-qc-minus-jl').parent().hide();
+                    $('#qc-minus-jl').parent().hide();
+                }
             } else {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量数量';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
                 $('[name=type-title-contract]').text('本期合同计量数量');
                 $('[name=type-title-qc]').text('本期变更计量数量');
-                $('#show-qc-minus-jl').parent().show();
-                $('#qc-minus-jl').parent().show();
+                if ($('#show-qc-minus-jl').length > 0) {
+                    $('#show-qc-minus-jl').parent().show();
+                    $('#qc-minus-jl').parent().show();
+                }
             }
             if (stage.im_type === imType.bb.value || stage.im_type === imType.bw.value) {
                 $('#show-jldy').parent().show();
@@ -3020,16 +3041,20 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadSheetHeader(self.sheet);
                         $('[name=type-title-contract]').text('本期合同计量金额');
                         $('[name=type-title-qc]').text('本期变更计量金额');
-                        $('#show-qc-minus-jl').parent().hide();
-                        $('#qc-minus-jl').parent().hide();
+                        if ($('#show-qc-minus-jl').length > 0) {
+                            $('#show-qc-minus-jl').parent().hide();
+                            $('#qc-minus-jl').parent().hide();
+                        }
                     } else {
                         const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                         jlCol.title = '本期计量数量';
                         SpreadJsObj.reLoadSheetHeader(self.sheet);
                         $('[name=type-title-contract]').text('本期合同计量数量');
                         $('[name=type-title-qc]').text('本期变更计量数量');
-                        $('#show-qc-minus-jl').parent().show();
-                        $('#qc-minus-jl').parent().show();
+                        if ($('#show-qc-minus-jl').length > 0) {
+                            $('#show-qc-minus-jl').parent().show();
+                            $('#qc-minus-jl').parent().show();
+                        }
                     }
                     if (stage.im_type === imType.bb.value || stage.im_type === imType.bw.value) {
                         $('#show-jldy').parent().show();
@@ -3714,13 +3739,13 @@ $(document).ready(() => {
 
             this.changeBillsSpreadSetting = {
                 cols: [
-                    {title: '清单编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 80, formatter: '@', cellType: 'tip', getTip: getTipText},
-                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 150, type: 'Number', cellType: 'tip', getTip: getTipText},
-                    {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'tip', getTip: getTipText},
-                    {title: '单价', colSpan: '1', rowSpan: '1', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', cellType: 'tip', getTip: getTipText},
-                    {title: '数量', colSpan: '1', rowSpan: '1', field: 'qty', hAlign: 2, width: 60, formatter: '@', cellType: 'tip', getTip: getTipText},
-                    {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 60, formatter: '@', cellType: 'tip', getTip: getTipText},
-                    {title: '变更部位', colSpan: '1', rowSpan: '1', field: 'bwmx', hAlign: 0, width: 100, formatter: '@', cellType: 'tip', getTip: getTipText},
+                    {title: '清单编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 80, formatter: '@'},
+                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 150, type: 'Number', cellType: 'delayTip', getTip: getTipText},
+                    {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
+                    {title: '单价', colSpan: '1', rowSpan: '1', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+                    {title: '数量', colSpan: '1', rowSpan: '1', field: 'qty', hAlign: 2, width: 60, formatter: '@'},
+                    {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 60, formatter: '@'},
+                    {title: '变更部位', colSpan: '1', rowSpan: '1', field: 'bwmx', hAlign: 0, width: 100, formatter: '@'},
                 ],
                 emptyRows: 0,
                 headRows: 1,
@@ -3812,6 +3837,99 @@ $(document).ready(() => {
                             return !changeBills;
                         }
                     },
+                    'autoUse': {
+                        name: '调用',
+                        icon: 'fa-play',
+                        callback: function (key, opt) {
+                            const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
+                            const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
+                            const billsPos = self.findBillsPos(curChange, changeBills);
+                            if (!billsPos) toastr.warning('无可调用的清单或计量单元');
+
+                            const data = { autoType: 'bills', bills: [{ ...billsPos, cid: changeBills.cid, cbid: changeBills.cbid }] };
+                            postData(window.location.pathname + '/auto-change', data, function(result) {
+                                if (result.pos) {
+                                    stagePos.loadCurStageData(result.pos.curStageData);
+                                }
+                                const nodes = stageTree.loadPostStageData(result.bills);
+                                stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                                stagePosSpreadObj.loadCurPosData();
+                                if (detail) {
+                                    detail.loadStageChangeUpdateData(result, nodes);
+                                } else {
+                                    stageIm.loadUpdateChangeData(result, nodes)
+                                }
+                            });
+                        },
+                        disable: function (key, opt) {
+                            const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
+                            const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
+                            return !changeBills || curChange.is_import;
+                        },
+                        visible: function (key, opt) {
+                            return false;
+                        }
+                    },
+                }
+            });
+
+            $.contextMenu({
+                selector: '#' + setting.changeObj.attr('id'),
+                build: function ($trigger, e) {
+                    const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.changeSpread);
+                    return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+                },
+                items: {
+                    'autoUseCur': {
+                        name: '自动调用',
+                        icon: 'fa-play',
+                        callback: function (key, opt) {
+                            const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
+                            const changeBills = SpreadJsObj.zh_data;
+                            const data = { bills: [], autoType: 'bills' };
+                            for (const cb of changeBills) {
+                                const billsPos = self.findBillsPos(curChange, cb);
+                                if (billsPos) data.push({ ...billsPos, cid: curChange.cid, cbid: cb.cbid })
+                            }
+                            postData(window.location.pathname + '/auto-use-change', data, function(result) {
+                                if (result.pos) {
+                                    stagePos.loadCurStageData(result.pos.curStageData);
+                                }
+                                const nodes = stageTree.loadPostStageData(result.bills);
+                                stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                                stagePosSpreadObj.loadCurPosData();
+                                if (detail) {
+                                    detail.loadStageChangeUpdateData(result, nodes);
+                                } else {
+                                    stageIm.loadUpdateChangeData(result, nodes)
+                                }
+                            });
+                        },
+                        disable: function (key, opt) {
+                            const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
+                            const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
+                            return !changeBills || curChange.is_import;
+                        },
+                        visible: function (key, opt) {
+                            return false;
+                        }
+                    },
+                    'autoUseAll': {
+                        name: '自动调用(全部变更令)',
+                        icon: 'fa-play',
+                        callback: function (key, opt) {
+                            postData(window.location.pathname + '/auto-use-change', { autoType: 'all' }, function(result) {
+                                window.location.reload();
+                            });
+                        },
+                        disable: function (key, opt) {
+                            const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
+                            return !curChange || curChange.is_import;
+                        },
+                        visible: function (key, opt) {
+                            return is_debug && stage.status === 1;
+                        }
+                    }
                 }
             });
         }
@@ -3886,6 +4004,38 @@ $(document).ready(() => {
                 this._analyzeCommon(change);
             }
         }
+        findBillsPos(change, changeBills) {
+            if (change.is_import) {
+                return null;
+            } else if (changeBills.gcl_id) {
+                const node = stageTree.nodes.find(x => {return x.id === changeBills.gcl_id});
+                posData = stagePos.getLedgerPos(node.id);
+                const changePos = posData.find(x => { return x.name === changeBills.bwmx; });
+                return { lid: node.id, pid: changePos ? changePos.id : (posData.length > 0 ? posData[0].id : -1) };
+            } else {
+                const cb = {
+                    b_code: changeBills.code || '',
+                    name: changeBills.name || '',
+                    unit: changeBills.unit || '',
+                    unit_price: changeBills.unit_price || 0,
+                };
+                for (const node of stageTree.nodes) {
+                    if (node.children && node.children.length > 0) continue;
+
+                    const b = {
+                        b_code: node.b_code || '',
+                        name: node.name || '',
+                        unit: node.unit || '',
+                        unit_price: node.unit_price || 0,
+                    };
+                    if (_.isMatch(cb, b)) {
+                        posData = stagePos.getLedgerPos(node.id);
+                        return { lid: node.id, pid: posData.length > 0 ? posData[0].id : -1 };
+                    }
+                }
+                return null;
+            }
+        }
     }
     // 展开收起附件
     $('a', '.right-nav').bind('click', function () {

+ 0 - 3
app/public/js/stage_bwtz.js

@@ -90,9 +90,6 @@ $(document).ready(() => {
     xmjSpread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
         unitTreeObj.loadCurUnitData();
     });
-    unitSpread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
-        console.log(SpreadJsObj.getSelectObject(info.sheet));
-    });
     const loadData = function (dataType) {
         postData(window.location.pathname + '/load', {filter: dataType}, function (result) {
             const setting = {

+ 17 - 1
app/public/js/stage_compare.js

@@ -87,6 +87,7 @@ $(document).ready(function () {
     const ledgerSheet = ledgerSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    sjsSettingObj.setOrgPriceCol(ledgerSpreadSetting.cols, [{ field: 'org_price' }]);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     // 初始化部位
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
@@ -133,7 +134,7 @@ $(document).ready(function () {
         keys: ['id', 'tender_id', 'ledger_id'],
         masterId: 'id',
         minorId: 'lid',
-        calcFields: [],
+        calcFields: ['pc_tp'],
         autoExpand: 3,
     };
     const scTree = createNewPathTree('master', scTreeSetting);
@@ -275,6 +276,7 @@ $(document).ready(function () {
             $('#select-qi').modal('hide');
         }
     });
+
     // 显示层次
     (function (select, sheet) {
         $(select).click(function () {
@@ -304,6 +306,20 @@ $(document).ready(function () {
                         tree.expandByCalcFields();
                         SpreadJsObj.refreshTreeRowVisible(sheet);
                         break;
+                    case "hasDiffer":
+                        tree.expandByCustom((data) => {
+                            const fieldSufs = sheet.zh_setting.fieldSufs;
+                            if (fieldSufs.length <= 1) return false;
+                            const field = (!data.children || data.children.length === 0) && !data.is_tp ? 'gather_qty' : 'gather_tp';
+                            const base = data[field + fieldSufs[0]];
+                            for (let i = 1; i< fieldSufs.length; i++) {
+                                const compare = data[field + fieldSufs[i]];
+                                if ((base || compare) && (compare !== base)) return true;
+                            }
+                            return false;
+                        });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
                 }
                 closeWaitingView();
             }, 100);

+ 10 - 7
app/public/js/stage_gather.js

@@ -60,6 +60,7 @@ $(document).ready(function () {
         if (!data) return defaultColor;
         return data.overRange && hintOver ? spreadColor.stage.over : data.differ ? spreadColor.gcl.differ : defaultColor;
     };
+    sjsSettingObj.setOrgPriceCol(gclSpreadSetting.cols, [{ field: 'org_price' }]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     // 初始化所属项目节
@@ -97,6 +98,7 @@ $(document).ready(function () {
         const nPercent = Math.min(Math.max(ZhCalc.div(parseFloat($('#over-percent').val()), 100), 0.5), 1);
         for (const node of data) {
             if (node) {
+                if (node.b_code === '103-4')console.log(node);
                 const bOverRangeTz = billsGatherOver(node, 'final_1_qty', 'final_1_tp', nPercent);
                 const bOverRangeDeal = billsGatherOver(node, 'deal_final_1_qty', 'deal_final_1_tp', nPercent);
                 node.overRange = bQty ? bOverRangeTz : (bDealQty ? bOverRangeDeal : bOverRangeTz || bOverRangeDeal);
@@ -134,15 +136,15 @@ $(document).ready(function () {
         gclGatherData = gclGatherModel.gatherGclData();
         gclGatherModel.checkDiffer(gclGatherData);
         checkOverRange(gclGatherData);
-        // 加载清单数据        
+        // 加载清单数据
         SpreadJsObj.loadSheetData(gclSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
         loadLeafXmjData(0);
         // 章节合计
-        const chapterData = gclGatherModel.gatherChapterData(chapter, result.spec, ['total_price', 'contract_tp', 'qc_tp', 'pre_contract_tp', 'pre_qc_tp']);
+        const chapterData = gclGatherModel.gatherChapterData(chapter, result.spec, ['total_price', 'contract_tp', 'qc_tp', 'pre_contract_tp', 'pre_qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp']);
         for (const c of chapterData) {
-            c.gather_tp = ZhCalc.add(c.contract_tp, c.qc_tp);
-            c.end_contract_tp = ZhCalc.add(c.contract_tp, c.pre_contract_tp);
-            c.end_qc_tp = ZhCalc.add(c.qc_tp, c.pre_qc_tp);
+            c.gather_tp = ZhCalc.sum([c.contract_tp, c.qc_tp, c.pc_tp]);
+            c.end_contract_tp = ZhCalc.sum([c.contract_tp, c.pre_contract_tp, c.contract_pc_tp]);
+            c.end_qc_tp = ZhCalc.sum([c.qc_tp, c.pre_qc_tp, c.qc_pc_tp]);
             c.end_gather_tp = ZhCalc.add(c.end_contract_tp, c.end_qc_tp);
             c.end_final_tp = ZhCalc.add(c.end_qc_tp, c.total_price);
             c.end_final_ratio = ZhCalc.mul(ZhCalc.div(c.end_gather_tp, c.end_final_tp), 100, 2);
@@ -282,7 +284,7 @@ $(document).ready(function () {
                 end_qc_qty: gcl.end_qc_qty, end_qc_tp: gcl.end_qc_tp,
                 end_gather_qty: gcl.end_gather_qty, end_gather_tp: gcl.end_gather_tp,
                 end_gather_percent: gcl.end_gather_percent,
-                end_final_qty: gcl.end_final_qty, end_final_tp: gcl.end_final_tp,
+                end_final_qty: gcl.end_final_qty, end_final_tp: gcl.end_final_tp, end_final_1_percent: gcl.end_final_1_percent,
             });
             if (gcl.leafXmjs && gcl.leafXmjs.length > 0) {
                 for (const xmj of gcl.leafXmjs) {
@@ -294,6 +296,7 @@ $(document).ready(function () {
                         end_gather_qty: xmj.end_gather_qty, end_gather_percent: xmj.end_gather_percent,
                         dwgc: xmj.dwgc, fbgc: xmj.fbgc, fxgc: xmj.fxgc,
                         jldy: xmj.jldy, bwmx: xmj.bwmx, drawing_code: xmj.drawing_code,
+                        end_final_1_percent: xmj.end_final_1_percent,
                     });
                 }
             }
@@ -301,4 +304,4 @@ $(document).ready(function () {
 
         SpreadExcelObj.exportSimpleXlsxSheet(setting, data, $('.sidebar-title').attr('data-original-title') + "-清单汇总.xlsx");
     });
-});
+});

+ 29 - 20
app/public/js/stage_im.js

@@ -25,7 +25,8 @@ const stageIm = (function () {
         stageId: 'id',
     };
     gsTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'];
-    gsTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp'];
+    gsTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
+        'end_contract_tp', 'end_qc_tp', 'end_gather_tp'];
     gsTreeSetting.calcFun = function (node) {
         if (node.children && node.children.length === 0) {
             node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -35,9 +36,9 @@ const stageIm = (function () {
             node.end_gather_qty = ZhCalc.add(node.pre_gather_qty, node.gather_qty);
         }
         node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
-        node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
-        node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
-        node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
+        node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
+        node.end_contract_tp = ZhCalc.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
+        node.end_qc_tp = ZhCalc.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
         node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
     };
     const gsTree = createNewPathTree('stage', gsTreeSetting);
@@ -306,8 +307,8 @@ const stageIm = (function () {
                 (!data.code || data.code === d.code) &&
                 (!data.name || data.name === d.name) &&
                 (!data.unit || data.unit === d.unit) &&
-                (!data.pid || data.pid === d.pid) &&
-                (!data.pos_name || data.pos_name === d.pos_name);
+                ((!data.pid && !d.pid) || data.pid === d.pid) &&
+                ((!data.pos_name && !d.pos_name) || data.pos_name === d.pos_name);
         });
     }
     function findZlRela(rela, data) {
@@ -317,8 +318,8 @@ const stageIm = (function () {
                 (!data.name || data.name === d.name) &&
                 (!data.unit || data.unit === d.unit) &&
                 checkZero(ZhCalc.sub(data.unit_price, d.unit_price)) &&
-                (!data.pid || data.pid === d.pid) &&
-                (!data.pos_name || data.pos_name === d.pos_name);
+                ((!data.pid && !d.pid) || data.pid === d.pid) &&
+                ((!data.pos_name && !d.pos_name) || data.pos_name === d.pos_name);
         });
     }
 
@@ -329,19 +330,20 @@ const stageIm = (function () {
                 (!data.name || data.name === d.name) &&
                 (!data.unit || data.unit === d.unit) &&
                 checkZero(ZhCalc.sub(data.unit_price, d.unit_price)) &&
-                (!data.pid || data.pid === d.pid) &&
-                (!data.pos_name || data.pos_name === d.pos_name);
+                ((!data.pid && !d.pid) || data.pid === d.pid) &&
+                ((!data.pos_name && !d.pos_name) || data.pos_name === d.pos_name);
         });
     }
 
     function findBbRela(rela, data) {
-        return _.find(rela, function (d) {
+        const result = _.find(rela, function (d) {
             return data.lid === d.lid &&
                 (!data.name || data.name === d.name) &&
                 (!data.unit || data.unit === d.unit) &&
-                (!data.pid || data.pid === d.pid) &&
-                (!data.pos_name || data.pos_name === d.pos_name);
+                ((!data.pid && !d.pid) || data.pid === d.pid) &&
+                ((!data.pos_name && !d.pos_name) || data.pos_name === d.pos_name);
         });
+        return result;
     }
 
     function checkTzCustomDetail(im) {
@@ -622,13 +624,14 @@ const stageIm = (function () {
                 checkZero(ZhCalc.sub(bills.unit_price, x.unit_price));
         });
         if (!gcl) {
-            gcl = {b_code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price};
+            gcl = {b_code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price, org_price: bills.org_price};
             im.gclBills.push(gcl);
         }
         gcl.contract_jl = ZhCalc.add(gcl.contract_jl, bills.contract_jl);
         gcl.qc_jl = ZhCalc.add(gcl.qc_jl, bills.qc_jl);
         gcl.qc_minus_jl = ZhCalc.add(gcl.qc_minus_jl, bills.qc_minus_jl);
         gcl.jl = ZhCalc.add(gcl.jl, bills.jl);
+        gcl.pre_jl = ZhCalc.add(gcl.pre_jl, bills.pre_jl);
     }
     function calculateBwBillsIm(im) {
         im.contract_jl = 0;
@@ -637,8 +640,13 @@ const stageIm = (function () {
         for (const b of im.gclBills) {
             im.contract_jl = ZhCalc.add(im.contract_jl, ZhCalc.mul(b.contract_jl, b.unit_price, decimal.tp));
             im.qc_jl = ZhCalc.add(im.qc_jl, ZhCalc.mul(b.qc_jl, b.unit_price, decimal.tp));
+            const priceDiff = b.org_price ? ZhCalc.sub(b.unit_price, b.org_price) : 0;
+            if (priceDiff) {
+                const pc_jl = ZhCalc.sub(ZhCalc.mul(b.unit_price, b.pre_jl, decimal.tp), ZhCalc.mul(b.org_price, b.pre_jl, decimal.tp));
+                im.pc_jl = ZhCalc.add(im.pc_jl, pc_jl);
+            }
         }
-        im.jl = ZhCalc.add(im.contract_jl, im.qc_jl);
+        im.jl = ZhCalc.sum([im.contract_jl, im.qc_jl, im.pc_jl]);
     }
     function generateBwBillsImData (node) {
         if (checkUsed(node)) {
@@ -692,8 +700,8 @@ const stageIm = (function () {
                             }
                         }
                         addBwBillsGclBills(im, {
-                            b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
-                            jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty, qc_minus_jl: pp.qc_minus_qty,
+                            b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, org_price: p.org_price,
+                            jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty, qc_minus_jl: pp.qc_minus_qty, pre_jl: pp.pre_gather_qty,
                         });
 
                         if (pp.drawing_code && im.drawing_code instanceof Array)
@@ -712,16 +720,17 @@ const stageIm = (function () {
                     imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_tp);
                     imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_tp);
                     imDefault.qc_minus_jl = 0;
+                    imDefault.pc_jl = ZhCalc.add(imDefault.pc_jl, p.pc_tp);
                     imDefault.used = true;
 
                     addBwBillsGclBills(imDefault, {
-                        b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
-                        jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_qty, qc_minus_jl: p.qc_qty,
+                        b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, org_price: p.org_price,
+                        jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_qty, qc_minus_jl: p.qc_qty, pre_jl: p.pre_gather_qty
                     });
                 }
             }
             if (imDefault.used) {
-                imDefault.jl = ZhCalc.add(imDefault.contract_jl, imDefault.qc_jl);
+                imDefault.jl = ZhCalc.sum([imDefault.contract_jl, imDefault.qc_jl, imDefault.pc_jl]);
                 ImData.push(imDefault);
             }
             for (const im of nodeImData) {

+ 11 - 1
app/public/js/stage_pay.js

@@ -107,7 +107,17 @@ $(document).ready(() => {
 
     const paySpreadSetting = {
         cols: [
-            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: 'readOnly.name'},
+            {
+                title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 150, formatter: '@'
+                , readOnly: 'readOnly.name', cellType: 'tip', getTip: function(data) {
+                    const tips = [];
+                    if (data) {
+                        if (data.pause) tips.push('当前项已停用');
+                        if (!data.is_yf) tips.push('当前项不参与本期应付计算');
+                    }
+                    return tips.join('<br/>');
+                }
+            },
             {title: '扣款', colSpan: '1', rowSpan: '1', field: 'minus', hAlign: 1, width: 50, cellType: 'checkbox', readOnly: 'readOnly.minus'},
             {title: '本期金额(表达式)', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 120, readOnly: 'readOnly.tp', type: 'Number', /*cellType: 'tip', getTip: function (data) {return data ? data.expr : '';}*/},
             {title: '截止上期金额',  colSpan: '1', rowSpan: '1', field: 'pre_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number',},

+ 37 - 1
app/public/js/sub_menu.js

@@ -47,6 +47,9 @@
             }
         });
         miniMenu.mouseenter(function () {
+            toMenu.show();
+            toMenu.siblings('i').hide();
+            // miniMenu.find('.side-switch i').addClass('fa-indent text-primary').removeClass('fa-bars');
             $(setting.miniMenuList).show();
 
             miniHint.popover('hide');
@@ -55,7 +58,40 @@
             }
         });
         miniMenu.mouseleave(function () {
+            toMenu.hide();
+            toMenu.siblings('i').show();
+            // miniMenu.find('.side-switch i').addClass('fa-bars').removeClass('fa-indent text-primary');
             $(setting.miniMenuList).hide();
         });
+        let timeMake = false;
+        $(setting.menu).find('.side-show').mouseenter(function () {
+            $(setting.menu).find('.side-fold').show();
+            timeMake = false;
+        });
+
+        $(setting.menu).find('.side-show').mouseleave(function () {
+            timeMake = true;
+            setTimeout(function () {
+                if (timeMake) {
+                    $(setting.menu).find('.side-fold').hide();
+                }
+            }, 500);
+
+        });
+
+        $(setting.menu).find('.side-fold').mouseenter(function () {
+            timeMake = false;
+            $(this).css('width', '15px');
+        });
+
+        $(setting.menu).find('.side-fold').mouseleave(function () {
+            timeMake = true;
+            setTimeout(function () {
+                if (timeMake) {
+                    $(setting.menu).find('.side-fold').hide();
+                }
+            }, 500);
+            $(this).css('width', '6px');
+        })
     }
-})(jQuery);
+})(jQuery);

+ 9 - 0
app/public/js/tender.js

@@ -33,6 +33,10 @@ function loadCommonProperty () {
     $('#supervision2-company').val(property.construction_unit.supervision2.company);
     $('#supervision2-corporation').val(property.construction_unit.supervision2.corporation);
     $('#supervision2-date').val(property.construction_unit.supervision2.date);
+    // 检测单位
+    $('#detect-company').val(property.construction_unit.detect.company);
+    $('#detect-corporation').val(property.construction_unit.detect.corporation);
+    $('#detect-date').val(property.construction_unit.detect.date);
 
     // 技术参数
     $('#loadLevel').val(property.tech_param.loadLevel);
@@ -512,6 +516,11 @@ $(document).ready(function() {
                     corporation: $('#supervision2-corporation').val(),
                     date: $('#supervision2-date').val(),
                 },
+                detect: {
+                    company: $('#detect-company').val(),
+                    corporation: $('#detect-corporation').val(),
+                    date: $('#detect-date').val(),
+                },
             },
             tech_param: {
                 loadLevel: _.toNumber($('#loadLevel').val()),

+ 11 - 2
app/public/js/tender_list.js

@@ -287,6 +287,12 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
         html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
     }
     html.push('</td>');
+    // 计量模式
+    html.push('<td style="width: 5%" class="text-center">');
+    if (node.measure_type) {
+        html.push(node.measure_type === measureType.tz.value ? '0号台账' : '工程量清单');
+    }
+    html.push('</td>');
     // 创建人
     html.push('<td style="width: 10%" class="text-center">', node.user_name ? node.user_name : '', '</td>');
     // 创建时间
@@ -322,9 +328,10 @@ function getTenderTreeHtml () {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
-        html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
+        html.push('<th class="text-center" style="width: 40%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '计量模式', '</th>');
         html.push('<th class="text-center" style="width: 10%">', '创建人', '</th>');
-        html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');
         html.push('<th class="text-center" style="width: 10%">', '计量期数', '</th>');
         html.push('<th class="text-center" style="width: 20%">', '审批状态', '</th>');
         html.push('</tr>', '</thead>');
@@ -369,6 +376,7 @@ $(document).ready(() => {
     // initTenderTree();
     // $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
+    tenderTreeShowLevel.initShowLevel();
     // localHideList();
 
     // 分类
@@ -398,6 +406,7 @@ $(document).ready(() => {
             $('.c-body').html(getTenderTreeHtml());
             localHideList();
             $('#cate-set').modal('hide');
+            tenderTreeShowLevel.refreshMenuVisible();
         });
     });
     // 新增标段

+ 5 - 3
app/public/js/tender_list_info.js

@@ -267,9 +267,9 @@ function initTenderTree () {
     }
     function calculateTender(tender) {
         if (tender.lastStage) {
-            tender.gather_tp = ZhCalc.add(tender.lastStage.contract_tp, tender.lastStage.qc_tp);
-            tender.end_contract_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.contract_tp);
-            tender.end_qc_tp = ZhCalc.add(tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp);
+            tender.gather_tp = ZhCalc.sum([tender.lastStage.contract_tp, tender.lastStage.qc_tp, tender.lastStage.pc_tp]);
+            tender.end_contract_tp = ZhCalc.sum([tender.lastStage.pre_contract_tp, tender.lastStage.contract_tp, tender.lastStage.contract_pc_tp]);
+            tender.end_qc_tp = ZhCalc.sum([tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp, tender.lastStage.qc_pc_tp]);
             tender.end_gather_tp = ZhCalc.add(tender.end_contract_tp, tender.end_qc_tp);
             tender.pre_gather_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.pre_qc_tp);
             tender.yf_tp = ZhCalc.add(tender.lastStage.yf_tp);
@@ -448,6 +448,7 @@ $(document).ready(() => {
     $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
     localHideList();
+    tenderTreeShowLevel.initShowLevel();
     // 分类
     $('#cate-set').on('show.bs.modal', function () {
         createTree();
@@ -475,6 +476,7 @@ $(document).ready(() => {
             $('.c-body').html(getTenderTreeHtml());
             localHideList();
             $('#cate-set').modal('hide');
+            tenderTreeShowLevel.refreshMenuVisible();
         });
     });
     // 新增标段

+ 2 - 0
app/public/js/tender_list_manage.js

@@ -384,6 +384,7 @@ $(document).ready(() => {
     $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
     localHideList();
+    tenderTreeShowLevel.initShowLevel();
     // 分类
     $('#cate-set').on('show.bs.modal', function () {
         createTree();
@@ -411,6 +412,7 @@ $(document).ready(() => {
             $('.c-body').html(getTenderTreeHtml());
             localHideList();
             $('#cate-set').modal('hide');
+            tenderTreeShowLevel.refreshMenuVisible();
         });
     });
 

+ 4 - 2
app/public/js/tender_list_progress.js

@@ -262,9 +262,9 @@ function initTenderTree () {
     }
     function calculateTender(tender) {
         if (tender.lastStage) {
-            tender.end_qc_tp = ZhCalc.add(tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp);
+            tender.end_qc_tp = ZhCalc.sum([tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp, tender.lastStage.qc_pc_tp]);
             tender.pre_gather_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.pre_qc_tp);
-            tender.gather_tp = ZhCalc.add(tender.lastStage.contract_tp, tender.lastStage.qc_tp);
+            tender.gather_tp = ZhCalc.sum([tender.lastStage.contract_tp, tender.lastStage.qc_tp, tender.lastStage.pc_tp]);
             tender.sum_tp = ZhCalc.add(tender.total_price, tender.end_qc_tp);
         } else {
             tender.sum_tp = tender.total_price;
@@ -420,6 +420,7 @@ $(document).ready(() => {
     $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
     localHideList();
+    tenderTreeShowLevel.initShowLevel();
     // 分类
     $('#cate-set').on('show.bs.modal', function () {
         createTree();
@@ -447,6 +448,7 @@ $(document).ready(() => {
             $('.c-body').html(getTenderTreeHtml());
             localHideList();
             $('#cate-set').modal('hide');
+            tenderTreeShowLevel.refreshMenuVisible();
         });
     });
     // 新增标段

+ 80 - 2
app/public/js/tender_showhide.js

@@ -18,7 +18,7 @@ const findTenderTreeNode = function(sortId, tree) {
             findTenderTreeNode(sortId, item.children);
         }
     });
-}
+};
 
 function removeValueToCate(cate) {
     const changeCate = JSON.parse(JSON.stringify(cate));
@@ -29,6 +29,40 @@ function removeValueToCate(cate) {
     }
     return newCate;
 }
+
+function recursiveExpand(nodes, parent, checkFun) {
+    for (const node of nodes) {
+        const expanded = checkFun(node);
+        if (!expanded && node.sort_id) hideList.push({sort_id: node.sort_id});
+        if (node.expanded !== expanded) {
+            node.expanded = expanded;
+            if (node.sort_id) {
+                if (node.expanded) {
+                    $('.c-body tr td span[cid="' + node.sort_id + '"]').children('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
+                    $('.c-body tr td span[cid="' + node.sort_id + '"]').attr('title', '收起');
+                } else {
+                    $('.c-body tr td span[cid="' + node.sort_id + '"]').children('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
+                    $('.c-body tr td span[cid="' + node.sort_id + '"]').attr('title', '展开');
+                }
+                doTrStatus(node, node.expanded ? 'show' : 'hide');
+            }
+        }
+        node.visible = parent ? (parent.expanded && parent.visible) : true;
+        if (node.children) recursiveExpand(node.children, node, checkFun);
+    }
+}
+function expandByCustom(checkFun) {
+    hideList = [];
+    recursiveExpand(tenderTree, null, checkFun);
+}
+
+function expandByLevel(level) {
+    expandByCustom(function (n) {
+        return n.level < level;
+    });
+    setLocalCache(uphlname, JSON.stringify(hideList));
+}
+
 // 根据标段类别设置排序
 function sortTenderTree(teTree = tenderTree) {
     for (const tender of teTree) {
@@ -120,6 +154,17 @@ function doTrStatus(node, status, all = '') {
     }
 }
 
+let tenderTreeShowLevel;
+const getChildrenLevel = function (node) {
+    let iLevel = node.level;
+    if (node.children && node.children.length > 0) {
+        for (const c of node.children) {
+            iLevel = Math.max(iLevel, getChildrenLevel(c));
+        }
+    }
+    return iLevel;
+};
+
 $(document).ready(() => {
     // 展开和收起
     $('body').on('click', '.fold-switch', function () {
@@ -169,7 +214,40 @@ $(document).ready(() => {
             }
         }
         setTopTr();
-    })
+    });
+
+    tenderTreeShowLevel = $.cs_showLevel({
+        selector: '#show-level',
+        levels: [
+            {
+                type: 'sort', count: 5, visible_count: function () {
+                    return tenderTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                }
+            },
+            {
+                type: 'last', title: '最底层', visible: function () {
+                    const count = tenderTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                    return count > 0;
+                }
+            },
+        ],
+        showLevel: function (tag) {
+
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    expandByLevel(parseInt(tag));
+                    break;
+                case "last":
+                    expandByLevel(20);
+                    break;
+                default: return;
+            }
+        }
+    });
 });
 
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 8 - 0
app/public/jspdf/SmartSimsun-normal2.js


+ 8 - 1
app/public/report/js/rpt_custom.js

@@ -363,6 +363,13 @@ const rptCustomObj = (function () {
             rptCustomObj.resetStageSelect(resolve);
             // $("#stage-select").modal('hide');
         });
+        $('#stage-select-all').unbind('click');
+        $('#stage-select-all').bind('click', function () {
+            const material = $('[name=stage-select-check]');
+            for (const m of material) {
+                m.checked = this.checked;
+            }
+        });
     };
     const initChangeSelect = function (gsSetting, rptName, resolve = null) {
         changeObj.show('选择工程变更' + (rptName ? '-' + rptName : ''), resolve);
@@ -398,7 +405,7 @@ const rptCustomObj = (function () {
             for (const m of material) {
                 m.checked = this.checked;
             }
-        })
+        });
     };
     const init = function (cDefine, sfData, cSelect, rptName, resolve = null) {
         stageFlow = sfData;

+ 4 - 2
app/public/report/js/rpt_jspdf.js

@@ -71,7 +71,8 @@ let JpcJsPDFHelper = {
         }
 
         let newPageMergeBand = private_getIniPageMergeBorder(pageObj[JV.BAND_PROP_MERGE_BAND]);
-        if (current_stage_status === 3) {
+        let ppStatus = zTreeOprObj._chkPrePayStatus();
+        if (current_stage_status === 3 || ppStatus === 3) {
             rptSignatureHelper.mergeSignDate(pageObj, signatureRelArr, false);
             // rptSignatureHelper.mergeSignature(pageObj, signatureRelArr); // 这里merge的意义不大
             rptSignatureHelper.mergeSignAudit(pageObj, signatureRelArr, signAuditArr);
@@ -107,8 +108,9 @@ let JpcJsPDFHelper = {
                     private_drawCell(doc, ctx, cell, fonts, styles, controls, newPageMergeBand);
                 }
                 // 计量有电子签名,要单独处理
+                let ppStatus = zTreeOprObj._chkPrePayStatus();
                 for (let cell of page.signature_cells) {
-                    if (current_stage_status === 3) {
+                    if (current_stage_status === 3 || ppStatus === 3) {
                         private_drawSignature(doc, ctx, cell, styles, controls, newPageMergeBand, false, signatureRelArr);
                     } else {
                         private_drawSignature(doc, ctx, cell, styles, controls, newPageMergeBand, true, signatureRelArr);

+ 60 - 9
app/public/report/js/rpt_main.js

@@ -34,6 +34,7 @@ let rptTplObj = {
             indexDbOprObj.iniDb('tmpForPDF', 2);
 
             // dynamicLoadJs('/public/jspdf/SmartSimsun-normal.js');
+            // dynamicLoadJs('/public/jspdf/SmartSimsun-normal2.js');
             // dynamicLoadJs('/public/jspdf/SmartSimsun-bold.js', me.pdfFontSimsunCallBack);
         }
     },
@@ -417,6 +418,29 @@ let zTreeOprObj = {
     _setupArchive: function() {
         //
     },
+    _chkPrePayStatus: function() {
+        let rst = -1;
+        let me = zTreeOprObj;
+        if (me.currentNode && me.currentNode.hasOwnProperty('flags') && me.currentNode.flags.hasOwnProperty('rptTplType')) {
+            let prePayArr = PRE_PAY;
+            // 返回一个数组,得自己判断,有2种type,0:开工预付款,1:材料预付款; status与stage的status对应(3表示审核通过);order表示顺序(以同类型的最大选择顺序为判断基准),selected表示是否选择(0,1)
+            let ppType = -1;
+            if (me.currentNode['flags']['rptTplType'] === 'tplType_prePay1') {
+                // 开工预付款
+                ppType = 0;
+            } else if (me.currentNode['flags']['rptTplType'] === 'tplType_prePay2') {
+                // 材料预付款
+                ppType = 1;
+            }
+            for (let ppItem of prePayArr) {
+                if (ppItem.type === ppType && ppItem.selected) {
+                    rst = ppItem.status;
+                    break;
+                }
+            }
+        }
+        return rst;
+    },
     requestNormalReport: async function (params) {
         let me = zTreeOprObj;
         if (COMMON_WATER_MARK_PIC_DATA === null || COMMON_WATER_MARK_PIC_DATA === '') {
@@ -438,7 +462,7 @@ let zTreeOprObj = {
                     CURRENT_ROLE_REL_ID = result.signatureRelInfo[0].id;
                     ROLE_REL_LIST = me._parseRoleRelList(result.signatureRelInfo[0].rel_content);
                     rptSignatureHelper.originalRoleRelList = me._parseRoleRelList(result.signatureRelInfo[0].rel_content);
-                    if (current_stage_status === 3) {
+                    if (getStageStatus() === 3) {
                         rptSignatureHelper.mergeSignDate(pageRst, ROLE_REL_LIST, true);
                         rptSignatureHelper.mergeSignature(pageRst, ROLE_REL_LIST, true);
                         await rptSignatureHelper.resetDummySignature(pageRst, ROLE_REL_LIST); // 这里重新整理签章坐标信息(因签章大小在后台暂时获取不到,挪到前端处理)
@@ -610,11 +634,10 @@ let rptControlObj = {
     },
     getTplIdsCommon: function (refRptTplIds, rpt_names) {
         for (let node of zTreeOprObj.checkedRptTplNodes) {
+            refRptTplIds.push(node.refId);
+            if (rpt_names) rpt_names.push(node.name);
             if (node.hasOwnProperty('flags') && node.flags.hasOwnProperty('reportType') && node['flags']['reportType'] !== 'NA') {
-                // 未来可能会有这些处理,目前空着
-            } else {
-                refRptTplIds.push(node.refId);
-                if (rpt_names) rpt_names.push(node.name);
+                // 未来可能会有额外处理,目前空着
             }
         }
     },
@@ -840,7 +863,7 @@ let rptControlObj = {
                             }
                             signatureRelArr.push(tmpRel);
                         }
-                        if (current_stage_status === 3) {
+                        if (getStageStatus() === 3) {
                             // 统一安排merge(除草图外)
                             for (let idx = 0; idx < pageDataArr.length; idx++) {
                                 const pageObj = pageDataArr[idx];
@@ -914,8 +937,11 @@ let rptControlObj = {
         let me = rptControlObj;
         me.isLoading = true;
         // dynamicLoadJs('/public/jspdf/SmartSimsun-normal.js', 'normal', me.getPdfFontCallbackLight);
+        // dynamicLoadJs('/public/jspdf/SmartSimsun-normal2.js', 'normal', me.getPdfFontCallbackLight);
         // dynamicLoadJs('/public/jspdf/SmartSimsun-bold.js', 'bold', me.getPdfFontCallbackLight);
-        dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal.js', 'normal', me.getPdfFontCallbackLight);
+        
+        // dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal.js', 'normal', me.getPdfFontCallbackLight);
+        dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal2.js', 'normal', me.getPdfFontCallbackLight);
         dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-bold.js', 'bold', me.getPdfFontCallbackLight);
     },
     getPDFPre: function () {
@@ -931,12 +957,35 @@ let rptControlObj = {
             } else {
                 $.bootstrapLoading.start();
                 // dynamicLoadJs('/public/jspdf/SmartSimsun-normal.js',"normal", me.getPdfFontCallback);
+                // dynamicLoadJs('/public/jspdf/SmartSimsun-normal2.js',"normal", me.getPdfFontCallback);
                 // dynamicLoadJs('/public/jspdf/SmartSimsun-bold.js',"bold", me.getPdfFontCallback);
-                dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal.js', 'normal', me.getPdfFontCallback);
+                
+                // dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal.js', 'normal', me.getPdfFontCallback);
+                dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-normal2.js', 'normal', me.getPdfFontCallback);
                 dynamicLoadJs('https://d2.smartcost.com.cn/cach/SmartSimsun-bold.js', 'bold', me.getPdfFontCallback);
             }
         }
     },
+    getTestPDFPre: function() {
+        const params = {};
+        $.bootstrapLoading.start();
+        CommonAjax.postXsrfEx("/tender/report_api/getTestReport", params, WAIT_TIME_EXPORT, true, getCookie('csrfToken_j'),
+            async function(result){
+                const pageSize = 'A4';
+                const rpt_names = ['测试PDF格式'];
+                $.bootstrapLoading.end();
+                downloadPDFReport([result.data], pageSize, rpt_names, [], [], [], STAGE_AUDIT);
+            },
+            function(failRst){
+                $.bootstrapLoading.end();
+                console.log(failRst);
+            },
+            function(exceptionRst){
+                $.bootstrapLoading.end();
+                console.log(exceptionRst);
+            }
+        );
+    },
     getPDFEx: async function () {
         let me = rptControlObj;
         if (zTreeOprObj.checkedRptTplNodes && zTreeOprObj.checkedRptTplNodes.length > 0 && PAGE_SHOW['closeExportPdf'] !== 1) {
@@ -1288,7 +1337,9 @@ function dynamicLoadJs(url, type, callback) {
 }
 
 function getStageStatus() {
-    return current_stage_status;
+    let ppStatus = zTreeOprObj._chkPrePayStatus();
+    if (ppStatus === 3) return ppStatus
+    else return current_stage_status;
 }
 
 function getStageId() {

+ 20 - 7
app/public/report/js/rpt_other_stage.js

@@ -1,24 +1,36 @@
 const rptOtherStage = (function (){
     const info = {
-        advance: { title: '请选择预付款', colHeader: ['选择', '类型', '期', '审批状态'] },
+        advance: { title: '请选择预付款', colHeader: ['类型', '期', '审批状态'], colValue: ['typeStr', 'orderStr', 'statusStr'], key: 'id' },
+        pm_deal: { title: '请选择合同管理', colHeader: ['名称'], colValue: ['name'], active: true, key: 'bidsectionId' },
     };
     const data = {};
     let curType = '';
     const initList = function () {
-        const header = info[curType].colHeader;
-        const type = info[curType].type;
-        $('#sos-header').html(`<tr class="text-center"><th>${header[0]}</th><th>${header[1]}</th><th>${header[2]}</th><th>${header[3]}</th></tr>`);
+        const header = info[curType].colHeader, value = info[curType].colValue, key = info[curType].key;
+        const headerHtml = [];
+        headerHtml.push('<tr class="text-center"><th>选择</th>');
+        for (const h of header) {
+            headerHtml.push(`<th>${h}</th>`);
+        }
+        headerHtml.push('</tr>');
+        $('#sos-header').html(headerHtml.join());
         const html = [], arr = data[curType];
         for (const a of arr) {
+            const aHtml = [];
             const checked = a.selected ? 'checked' : '';
-            html.push('<tr class="text-center">', `<td><input type="checkbox" name="sos-check" ${checked} value="${a.id}"></td>`, `<td>${a.typeStr}</td>`, `<td>第${a.order}期</td>`, `<td>${a.statusStr}</td>`,  '</tr>');
+            aHtml.push(`<tr class="text-center"><td><input type="checkbox" name="sos-check" ${checked} value="${a[key]}"></td>`);
+            for (const v of value) {
+                aHtml.push(`<td>${a[v]}</td>`);
+            }
+            aHtml.push('</tr>');
+            html.push(aHtml.join());
         }
         $('#sos-list').html(html.join(''));
     };
     const showOtherStage = async function (type) {
         curType = type;
         document.getElementById('sos-title').innerText = info[type].title;
-        if (!data[type]) {
+        if (!data[type] || info[curType].active) {
             const result = await postDataAsync(`/tender/${window.location.pathname.split('/')[2]}/load`, { filter: curType });
             data[curType] = result[curType];
         }
@@ -30,10 +42,11 @@ const rptOtherStage = (function (){
         })
     };
     const updateOtherStage = async function () {
+        const key = info[curType].key;
         const updateData = {};
         updateData[curType] = [];
         $('[name=sos-check]').each((i, c) => {
-            const ci = data[curType].find(x => { return x.id == c.value });
+            const ci = data[curType].find(x => { return x[key] == c.value });
             const checked = c.checked ? 1 : 0;
             if (ci && ci.selected !== checked) {
                 updateData[curType].push({id: c.value, selected: c.checked})

+ 6 - 4
app/public/report/js/rpt_signature.js

@@ -53,8 +53,9 @@ let rptSignatureHelper = {
                     prj_sel_option_accounts[keyIdx].push('<optgroup label=" ' + companyKey + '">');
                 }
                 //push item
-                prj_accounts[keyIdx].push('<li class="add-sign-list-item"><a href="javascript:void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx(' + accIdx + ')" class="btn-link pull-right" title="添加" data-dismiss="modal"><i class="fa fa-plus"></i></a>' +
-                    prjAccount.name + '-<small class="text-muted">' + roleKey + '</small></li>');
+                // prj_accounts[keyIdx].push('<li class="add-sign-list-item"><a href="javascript:void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx(' + accIdx + ')" class="btn-link pull-right" title="添加" data-dismiss="modal"><i class="fa fa-plus"></i></a>' +
+                    // prjAccount.name + '-<small class="text-muted">' + roleKey + '</small></li>');
+                prj_accounts[keyIdx].push(`<a class="dropdown-item" href="javascript: void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx('${accIdx}')" data-dismiss="modal">${prjAccount.name}-<small class="text-muted">${roleKey}</small></a>`);
                 prj_sel_option_accounts[keyIdx].push('<option value="' + accIdx + '">' + prjAccount.name + '-' + roleKey + '</option>');
             }
         }
@@ -93,8 +94,9 @@ let rptSignatureHelper = {
                 roleKey = DFT_ROLE_NAME;
             }
             //push item
-            prj_accounts.push('<li class="add-sign-list-item"><a href="javascript:void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx(' + accIdx + ')" class="btn-link pull-right" title="添加" data-dismiss="modal"><i class="fa fa-plus"></i></a>' +
-                prjAccount.name + '-<small class="text-muted">' + roleKey + '</small></li>');
+            // prj_accounts.push('<li class="add-sign-list-item"><a href="javascript:void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx(' + accIdx + ')" class="btn-link pull-right" title="添加" data-dismiss="modal"><i class="fa fa-plus"></i></a>' +
+                // prjAccount.name + '-<small class="text-muted">' + roleKey + '</small></li>');
+            prj_accounts.push(`<a class="dropdown-item" href="javascript: void(0)" onclick="rptSignatureHelper.createEsignatureByAccIdx('${accIdx}')" data-dismiss="modal">${prjAccount.name}-<small class="text-muted">${roleKey}</small></a>`);            
         }
         accDiv.append(prj_accounts.join(''));
     },

+ 8 - 2
app/router.js

@@ -239,18 +239,22 @@ module.exports = app => {
     app.get('/tender/:id/revise/:rid/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.compare');
     app.get('/tender/:id/revise/:rid/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.gclCompare');
     app.post('/tender/:id/revise/:rid/load', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.loadData');
+    // 单价调整
+    app.get('/tender/:id/revise/:rid/price', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.price');
+    app.post('/tender/:id/revise/:rid/price/update', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.priceUpdate');
 
     // 查看修订数据
     app.get('/tender/:id/revise/history/:rid/info', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.history');
     app.post('/tender/:id/revise/history/:rid/load', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.loadData');
     app.get('/tender/:id/revise/history/:rid/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.gclCompare');
     app.get('/tender/:id/revise/history/:rid/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.compare');
+    app.get('/tender/:id/revise/history/:rid/price', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.price');
 
     // 修订审批
     app.post('/tender/:id/revise/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.addAuditor');
     app.post('/tender/:id/revise/audit/remove', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.removeAuditor');
-    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.start');
-    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.check');
+    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.start');
+    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.check');
 
     // 签约清单
     app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.getData');
@@ -281,6 +285,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.updateStageData');
     app.post('/tender/:id/measure/stage/:order/valid-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.searchValidChange');
     app.post('/tender/:id/measure/stage/:order/use-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.useChange');
+    app.post('/tender/:id/measure/stage/:order/auto-use-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.autoUseChange');
     app.post('/tender/:id/measure/stage/:order/check', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.check');
     app.post('/tender/:id/measure/stage/:order/save/cooperation', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveCooperationData');
     app.post('/tender/:id/measure/stage/:order/im-file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.uploadImFile');
@@ -398,6 +403,7 @@ module.exports = app => {
     app.post('/tender/report_api/getReport', sessionAuth, 'reportController.getReport');
     app.post('/tender/report_api/getDirectReport', sessionAuth, 'reportController.createExcelFilesFromDirectData');
     app.post('/tender/report_api/getMultiReports', sessionAuth, 'reportController.getMultiReportsEx');
+    app.post('/tender/report_api/getTestReport', sessionAuth, 'reportController.getTestReport');
 
     app.post('/tender/report_api/createExcelFiles', sessionAuth, 'reportController.createExcelFilesEx');
     app.post('/tender/report_api/createExcelFilesInOneBook', sessionAuth, 'reportController.createExcelFilesInOneBookEx');

+ 1 - 0
app/service/category_value.js

@@ -92,6 +92,7 @@ module.exports = app => {
                         let cate = this._.find(tender.category, function (c) { return c.cid === cid; });
                         if (!cate) {
                             cate = {cid: cid};
+                            if (!tender.category) tender.category = [];
                             tender.category.push(cate);
                         }
                         cate.value = v.id;

+ 2 - 2
app/service/change.js

@@ -500,10 +500,10 @@ module.exports = app => {
          * @return {void}
          */
         async getChangeTp(tenderId) {
-            const sql = 'SELECT SUM(`total_price`) AS tp FROM ?? WHERE tid = ? AND status = ?';
+            const sql = 'SELECT SUM(`total_price`) AS tp, SUM(`positive_tp`) AS p_tp, SUM(`negative_tp`) AS n_tp FROM ?? WHERE tid = ? AND status = ?';
             const sqlParam = [this.tableName, tenderId, audit.flow.status.checked];
             const result = await this.db.queryOne(sql, sqlParam);
-            return result ? result.tp : 0;
+            return result ? [result.tp, result.p_tp, result.n_tp] : [0, 0, 0];
         }
 
         /**

+ 11 - 1
app/service/change_audit_list.js

@@ -396,12 +396,22 @@ module.exports = app => {
             const sqlParam = [this.tableName, this.ctx.change.cid];
             const changeList = await transaction.query(sql, sqlParam);
             let total_price = 0;
+            let positive_tp = 0;
+            let negative_tp = 0;
             const tp_decimal = this.ctx.change.tp_decimal ? this.ctx.change.tp_decimal : this.ctx.tender.info.decimal.tp;
             for (const cl of changeList) {
-                total_price = this.ctx.helper.accAdd(total_price, this.ctx.helper.mul(cl.unit_price, cl.spamount, tp_decimal));
+                const price = this.ctx.helper.mul(cl.unit_price, cl.spamount, tp_decimal);
+                total_price = this.ctx.helper.accAdd(total_price, price);
+                if (price >= 0) {
+                    positive_tp = this.ctx.helper.accAdd(positive_tp, price);
+                } else {
+                    negative_tp = this.ctx.helper.accAdd(negative_tp, price);
+                }
             }
             const updateData = {
                 total_price,
+                positive_tp,
+                negative_tp,
             };
             if (updateTpDecimal) {
                 updateData.tp_decimal = tp_decimal;

+ 38 - 0
app/service/glj_lib.js

@@ -0,0 +1,38 @@
+'use strict';
+
+/**
+ * 标准清单业务逻辑
+ *
+ * @author Mai
+ * @date 2018/3/13
+ * @version
+ */
+
+const StandardLib = require('./standard_lib');
+module.exports = app => {
+
+    class StdBills extends StandardLib {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx, {
+                mid: 'list_id',
+                kid: 'glj_id',
+                pid: 'glj_pid',
+                order: 'order',
+                level: 'level',
+                isLeaf: 'is_leaf',
+                fullPath: 'full_path',
+                keyPre: 'revise_bills_maxLid:'
+            }, 'glj_lib');
+            this.stdType = 'glj';
+        }
+    }
+
+    return StdBills;
+};

+ 8 - 42
app/service/ledger.js

@@ -645,37 +645,6 @@ module.exports = app => {
             return result;
         }
 
-        async _importCacheTreeNodes(transaction, nodes) {
-            const datas = [];
-            for (const node of nodes) {
-                datas.push({
-                    id: node.id,
-                    tender_id: this.ctx.tender.id,
-                    ledger_id: node.ledger_id,
-                    ledger_pid: node.ledger_pid,
-                    level: node.level,
-                    order: node.order,
-                    is_leaf: !node.children || node.children.length === 0,
-                    full_path: node.full_path,
-                    code: node.code,
-                    b_code: node.b_code,
-                    name: node.name,
-                    unit: node.unit,
-                    sgfh_qty: node.sgfh_qty,
-                    sgfh_tp: node.sgfh_tp,
-                    quantity: node.quantity,
-                    unit_price: node.unit_price,
-                    total_price: node.total_price,
-                    dgn_qty1: node.dgn_qty1,
-                    dgn_qty2: node.dgn_qty2,
-                    memo: node.memo,
-                    drawing_code: node.drawing_code,
-                });
-            }
-            await transaction.insert(this.tableName, datas);
-            return datas;
-        }
-
         /**
          * 导入Excel数据
          * @param excelData
@@ -712,18 +681,15 @@ module.exports = app => {
                         memo: node.memo,
                         drawing_code: node.drawing_code,
                         node_type: node.node_type,
+                        // 项目节不导入金额
+                        // todo 最底层项目节其实应该导入,但是由于目前项目节不能输入金额,也不能计量,导入金额就会有矛盾,故暂时不导入
+                        sgfh_qty: node.quantity,
+                        sgfh_tp: node.b_code ? node.total_price : 0,
+                        quantity: node.quantity,
+                        total_price: node.b_code ? node.total_price : 0,
+                        deal_qty: node.deal_qty,
+                        deal_tp: node.b_code ? node.deal_tp : 0,
                     };
-                    // 项目节不导入金额
-                    // todo 最底层项目节其实应该导入,但是由于目前项目节不能输入金额,也不能计量,导入金额就会有矛盾,故暂时不导入
-                    if (this.ctx.tender.data.measure_type === measureType.tz.value) {
-                        data.sgfh_qty = node.quantity;
-                        data.sgfh_tp = node.b_code ? node.total_price : 0;
-                        data.quantity = node.quantity;
-                        data.total_price = node.b_code ? node.total_price : 0;
-                    } else if (this.ctx.tender.data.measure_type === measureType.gcl.value) {
-                        data.deal_qty = node.quantity;
-                        data.deal_tp = node.b_code ? node.total_price : 0;
-                    }
                     datas.push(data);
                 }
                 await transaction.insert(this.tableName, datas);

+ 29 - 47
app/service/ledger_history.js

@@ -7,6 +7,7 @@
  * @date
  * @version
  */
+const Ledger = require('../lib/ledger');
 
 module.exports = app => {
 
@@ -70,25 +71,25 @@ module.exports = app => {
          * @return {Promise<void>} - 新增备份id
          * @private
          */
-        async backupStageLedgerHistory(stage) {
-            const sbCount = await this.ctx.service.ledger.count({ tender_id: this.ctx.stage.tid });
-            const spCount = await this.ctx.service.pos.count({ tid: this.ctx.stage.tid });
-            const ledgerHis = await this.ctx.service.ledgerHistory.getLatestHistory(this.ctx.tender.id);
+        async checkBackupLedgerHistory(tid, sid = 0, rid = '') {
+            const sbCount = await this.ctx.service.ledger.count({ tender_id: tid });
+            const spCount = await this.ctx.service.pos.count({ tid: tid });
+            const ledgerHis = await this.ctx.service.ledgerHistory.getLatestHistory(tid);
             if (sbCount === ledgerHis.bills_count && spCount === ledgerHis.pos_count) return ledgerHis.id;
 
             const now = new Date();
             const timestamp = (now).getTime();
 
-            const billsHis = `${this.ctx.session.sessionProject.id}/${stage.tid}/ledger/bills${timestamp}-s.json`;
-            const bills = await this.ctx.service.ledger.getData(stage.tid);
+            const billsHis = `${this.ctx.session.sessionProject.id}/${tid}/ledger/bills${timestamp}-s.json`;
+            const bills = await this.ctx.service.ledger.getData(tid);
             await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
 
-            const posHis = `${this.ctx.session.sessionProject.id}/${stage.tid}/ledger/pos${timestamp}-s.json`;
-            const pos = await this.ctx.service.pos.getPosData({ tid: stage.tid });
+            const posHis = `${this.ctx.session.sessionProject.id}/${tid}/ledger/pos${timestamp}-s.json`;
+            const pos = await this.ctx.service.pos.getPosData({ tid: tid });
             await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
 
             const result = await this.db.insert(this.tableName, {
-                pid: this.ctx.session.sessionProject.id, tid: stage.tid, sid: stage.id,
+                pid: this.ctx.session.sessionProject.id, tid, sid, rid,
                 in_time: now,
                 bills_file: billsHis, pos_file: posHis,
                 bills_count: bills.length, pos_count: pos.length,
@@ -107,13 +108,29 @@ module.exports = app => {
             const now = new Date();
             const timestamp = (now).getTime();
 
+            const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } });
+            let sum = { total_price: 0 };
+
             const billsHis = `${this.ctx.session.sessionProject.id}/${revise.tid}/ledger/bills${timestamp}-r.json`;
             const bills = await this.ctx.service.reviseBills.getData(revise.tid);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
 
             const posHis = `${this.ctx.session.sessionProject.id}/${revise.tid}/ledger/pos${timestamp}-r.json`;
             const pos = await this.ctx.service.revisePos.getData(revise.tid);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            if (price.length === 0) {
+                for (const b of bills) {
+                    if (!b.is_leaf) continue;
+                    sum.total_price = this.ctx.helper.add(sum.total_price, b.total_price);
+                }
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            } else {
+                const reviseTree = new Ledger.reviseTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+                reviseTree.loadRevisePrice(price, this.ctx.tender.info.decimal);
+                reviseTree.loadDatas(bills);
+                sum = reviseTree.sum();
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(reviseTree.getUpdateReviseData()), 'utf8'));
+                await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
+            }
 
             const result = await this.db.insert(this.tableName, {
                 pid: this.ctx.session.sessionProject.id, tid: revise.tid,
@@ -123,42 +140,7 @@ module.exports = app => {
                 bills_count: bills.length, pos_count: pos.length,
             });
 
-            return result.insertId;
-        }
-
-        /**
-         * 备份 (预留功能)
-         * @param {Object} transaction - 事务
-         * @param {Object} change - 工程变更
-         * @param {Array} newBillsNode - 新增项目节节点
-         * @param {Array} newPosNode - 新增计量单元节点
-         * @return {Promise<void>} - 新增备份id
-         * @private
-         */
-        async backupChangeHistory(transaction, change, newBillsNodes, newPosNodes) {
-            if ((newBillsNodes || newBillsNodes === 0) && (newPosNodes || newPosNodes.length === 0)) return;
-            const now = new Date();
-            const timestamp = (now).getTime();
-
-            const billsHis = `${this.ctx.session.sessionProject.id}/${change.tid}/ledger/bills${timestamp}-c.json`;
-            const bills = await this.ctx.service.ledger.getData(change.tid);
-            if (newBillsNodes.length > 0) bills.push(...newBillsNodes);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + billsHis, Buffer.from(JSON.stringify(bills), 'utf8'));
-
-            const posHis = `${this.ctx.session.sessionProject.id}/${change.tid}/ledger/pos${timestamp}-c.json`;
-            const pos = await this.ctx.service.pos.getPosData({ tid: change.tid });
-            if (newPosNodes.length > 0) pos.push(...newPosNodes);
-            await this.ctx.hisOss.put(this.ctx.hisOssPath + posHis, Buffer.from(JSON.stringify(pos), 'utf8'));
-
-            const result = await transaction.insert(this.tableName, {
-                pid: this.ctx.session.sessionProject.id, tid: change.tid,
-                cid: change.cid,
-                in_time: now,
-                bills_file: billsHis, pos_file: posHis,
-                bills_count: bills.length, pos_count: pos.length,
-            });
-
-            return result.insertId;
+            return [result.insertId, sum];
         }
     }
 

+ 27 - 9
app/service/ledger_revise.js

@@ -24,6 +24,14 @@ module.exports = app => {
             this.tableName = 'ledger_revise';
         }
 
+        _analysisData(data) {
+            if (!data) return;
+            const datas = data instanceof Array ? data : [data];
+            for (const d of datas) {
+                if (d.sum) d.sum = JSON.parse(d.sum);
+            }
+        }
+
         /**
          * 获取标段下,修订(分页,且按时间倒序)
          * @param {Number}tid - 标段id
@@ -39,7 +47,9 @@ module.exports = app => {
                 '  LIMIT ?, ?';
             const Len = this.app.config.pageSize;
             const sqlParam = [tid, (this.ctx.page - 1) * Len, Len];
-            return await this.db.query(sql, sqlParam);
+            const result = await this.db.query(sql, sqlParam);
+            this._analysisData(result);
+            return result;
         }
 
         /**
@@ -55,7 +65,9 @@ module.exports = app => {
                 '  WHERE lc.tid = ?' +
                 '  ORDER BY lc.in_time DESC';
             const sqlParam = [tid];
-            return await this.db.query(sql, sqlParam);
+            const result = await this.db.query(sql, sqlParam);
+            this._analysisData(result);
+            return result;
         }
 
         async getLastestRevise(tid, valid = true) {
@@ -67,7 +79,9 @@ module.exports = app => {
                 '  ORDER BY lc.in_time DESC' +
                 '  LIMIT 0, 1';
             const sqlParam = [tid];
-            return await this.db.queryOne(sql, sqlParam);
+            const result = await this.db.queryOne(sql, sqlParam);
+            this._analysisData(result);
+            return result;
         }
 
         async getRevise(tid, rid) {
@@ -79,7 +93,9 @@ module.exports = app => {
                 '  ORDER BY lc.in_time DESC' +
                 '  LIMIT 0, 1';
             const sqlParam = [tid, rid];
-            return await this.db.queryOne(sql, sqlParam);
+            const result = await this.db.queryOne(sql, sqlParam);
+            this._analysisData(result);
+            return result;
         }
 
         /**
@@ -160,11 +176,11 @@ module.exports = app => {
                 throw '数据错误';
             }
             const maxOrder = await this.getNewOrder(tid);
-            const latest = await this.ctx.service.ledgerHistory.getLatestHistory(tid);
-            if (!latest) throw '台账历史数据有误';
+            const preHisId = await this.ctx.service.ledgerHistory.checkBackupLedgerHistory(tid);
+            if (!preHisId) throw '台账历史数据有误';
             const data = {
                 id: this.uuid.v4(), tid: tid, uid: uid,
-                corder: maxOrder + 1, in_time: new Date(), status: audit.status.uncheck, pre_his_id: latest.id,
+                corder: maxOrder + 1, in_time: new Date(), status: audit.status.uncheck, pre_his_id: preHisId,
             };
             const transaction = await this.db.beginTransaction();
             try {
@@ -191,17 +207,19 @@ module.exports = app => {
          * @returns {Promise<void>}
          */
         async cancelRevise(revise) {
-            const his_id = await this.ctx.service.ledgerHistory.backupReviseLedgerHistory(revise);
+            const [his_id, sum] = await this.ctx.service.ledgerHistory.backupReviseLedgerHistory(revise);
             const transaction = await this.db.beginTransaction();
             try {
                 const result = await transaction.update(this.tableName, {
                     id: revise.id, valid: false, end_time: new Date(),
-                    his_id,
+                    his_id, sum: JSON.stringify(sum),
                 });
                 await transaction.update(this.ctx.service.ledgerHistory.tableName, { id: his_id, valid: 0 });
                 if (revise.his_id > 0) await transaction.update(this.ctx.service.ledgerHistory.tableName, { id: revise.his_id, valid: 0 });
                 // 投资进度改变状态
                 await transaction.update(this.ctx.service.schedule.tableName, { revising: 0 }, { where: { tid: this.ctx.tender.id } });
+                // 作废单价调整
+                await transaction.update(this.ctx.service.revisePrice.tableName, { valid: 0 }, { where: { rid: revise.id } });
                 await transaction.commit();
                 return result.affectedRows === 1;
             } catch (err) {

+ 4 - 7
app/service/ledger_tag.js

@@ -31,13 +31,9 @@ module.exports = app => {
          * @returns {Promise<void>}
          */
         async getDatas(tid, sid = -1) {
-            // return await this.db.select(this.tableName, {
-            //     where: {tid: tid, sid: -1},
-            //     columns: ['id', 'uid', 'lid', 'share', 'color', 'comment'],
-            //     orders: [['create_time', 'desc']],
-            // });
-            const sql = 'SELECT id, uid, lid, share, color, comment FROM ' + this.tableName +
-                '  WHERE tid = ? and sid = ? and (uid = ? or share) ORDER BY create_time DESC';
+            const sql = 'SELECT la.id, la.uid, la.lid, 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.uid = ? or la.share) ORDER BY la.create_time DESC';
             return await this.db.query(sql, [tid, sid, this.ctx.session.sessionUser.accountId]);
         }
 
@@ -67,6 +63,7 @@ module.exports = app => {
             }
             const result = await this.db.insert(this.tableName, data);
             data.id = result.insertId;
+            data.u_name = this.ctx.session.sessionUser.name;
             return data;
         }
 

+ 35 - 4
app/service/material.js

@@ -149,6 +149,7 @@ module.exports = app => {
                 material_tax: this.ctx.session.sessionProject.page_show.openMaterialTax,
                 decimal: preMaterial && preMaterial.decimal ? preMaterial.decimal : JSON.stringify(materialConst.decimal),
                 is_new: 1,
+                is_stage_self: data.is_stage_self,
             };
             const transaction = await this.db.beginTransaction();
             try {
@@ -166,6 +167,25 @@ module.exports = app => {
                 } else {
                     throw '新增期数据失败';
                 }
+                const insertMaterialStage = [];
+                if (data.is_stage_self) {
+                    // 新建多期单独单价表,工料表
+                    for (const [i, d] of data.stage_id.entries()) {
+                        insertMaterialStage.push({
+                            tid: tenderId,
+                            mid: newMaterial.id,
+                            sid: d,
+                            order: data.s_order.split(',')[i],
+                        });
+                    }
+                    if (insertMaterialStage.length > 0) {
+                        const resultStage = await transaction.insert(this.ctx.service.materialStage.tableName, insertMaterialStage);
+                        // 获取刚批量添加的所有list
+                        for (let j = 0; j < insertMaterialStage.length; j++) {
+                            insertMaterialStage[j].id = resultStage.insertId + j;
+                        }
+                    }
+                }
                 // 存在上一期时,复制上一期审批流程、不参与调差的清单、上期清单并算本期有效价差,本期应耗数量,并算本期总金额
                 if (preMaterial) {
                     const auditResult = await this.ctx.service.materialAudit.copyPreMaterialAuditors(transaction, preMaterial, newMaterial);
@@ -180,13 +200,19 @@ module.exports = app => {
                     await this.ctx.service.materialListSelf.copyNewStageSelfList(transaction, preSelfList, newMaterial.id);
                     // 复制调差清单工料关联表
                     // await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
-                    await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, data.material_self_list, preNotJoinList, newMaterial);
+                    await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, data.material_self_list, preNotJoinList, newMaterial, insertMaterialStage);
                     // 新增或删除list_gcl表
                     await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
                     // 设置list_gcl表old=>new更新
                     await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
-                    const [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
+                    let m_tp = null;
+                    let m_tax_tp = null;
+                    if (data.is_stage_self) {
+                        [m_tp, m_tax_tp] = await this.ctx.service.materialStageBills.insertBills(transaction, this.ctx.tender.id, newMaterial.id, newMaterial.stage_id, insertMaterialStage, JSON.parse(newMaterial.decimal));
+                    } else {
+                        [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
+                    }
                     // 修改现行价格指数,并返回调差基数json
                     const ex_calc = await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc, JSON.parse(newMaterial.decimal));
                     // 计算得出本期总金额
@@ -273,6 +299,10 @@ module.exports = app => {
                 await this.ctx.service.materialListGcl.setNewOldData(transaction, this.ctx.tender.id, 'old2new');
                 // 还要从material_list表更新gcl的old数据,更新方法
                 await this.ctx.service.materialListGcl.setOldFromLast(transaction, this.ctx.tender.id, materialInfo.order - 2);
+                if (materialInfo.is_stage_self) {
+                    await transaction.delete(this.ctx.service.materialStage.tableName, { mid: id });
+                    await transaction.delete(this.ctx.service.materialStageBills.tableName, { mid: id });
+                }
                 await transaction.delete(this.tableName, { id });
                 // 记录删除日志
                 await this.ctx.service.projectLog.addProjectLog(transaction, projectLogConst.type.material, projectLogConst.status.delete, '第' + materialInfo.order + '期');
@@ -417,12 +447,13 @@ module.exports = app => {
             return result && result.count !== 0;
         }
 
-        async saveDecimal(newUp, newTp) {
+        async saveDecimal(newUp, newTp, newQty) {
             const transaction = await this.db.beginTransaction();
             try {
-                await this.ctx.service.materialBills.resetDecimal(transaction, newUp, newTp);
+                await this.ctx.service.materialBills.resetDecimal(transaction, newUp, newTp, newQty);
                 this.ctx.material.decimal.up = newUp;
                 this.ctx.material.decimal.tp = newTp;
+                this.ctx.material.decimal.qty = newQty;
                 const m_tp = await this.ctx.service.materialBills.calcMaterialMTp(transaction);
                 let update_calc = false;
                 if (this.ctx.material.ex_calc) {

+ 0 - 16
app/service/material_audit.js

@@ -345,11 +345,6 @@ module.exports = app => {
                     // }
                 } else {
                     // 本期结束
-                    // 生成截止本期数据 final数据
-                    // console.time('generatePre');
-                    // await this.ctx.service.stageBillsFinal.generateFinalData(transaction, this.ctx.tender, this.ctx.stage);
-                    // await this.ctx.service.stagePosFinal.generateFinalData(transaction, this.ctx.tender, this.ctx.stage);
-                    // console.timeEnd('generatePre');
                     // 同步 期信息
                     await transaction.update(this.ctx.service.material.tableName, {
                         id: materialId, status: checkData.checkType,
@@ -519,11 +514,6 @@ module.exports = app => {
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
 
-                // 计算该审批人最终数据
-                // await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
-                // 复制一份最新数据给原报
-                // await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times + 1, 0, transaction);
-
                 // 添加短信通知-审批退回提醒功能
                 // const mobile_array = [];
                 // const stageInfo = await this.ctx.service.stage.getDataById(stageId);
@@ -704,13 +694,7 @@ module.exports = app => {
                 });
                 await transaction.insert(this.tableName, newAuditors);
 
-                // 复制一份最新数据给下一人
-                // await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 2, transaction);
-
                 // 本期结束
-                // 生成截止本期数据 final数据
-                // await this.ctx.service.stageBillsFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
-                // await this.ctx.service.stagePosFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
                 // 同步 期信息
                 await transaction.update(this.ctx.service.material.tableName, {
                     id: materialId, status: auditConst.status.checking,

+ 378 - 68
app/service/material_bills.js

@@ -36,6 +36,7 @@ module.exports = app => {
             const order = await this._getMaxOrder(this.ctx.tender.id);
             const transaction = await this.db.beginTransaction();
             try {
+                const resultData = {};
                 const newBills = {
                     tid: this.ctx.tender.id,
                     mid: this.ctx.material.id,
@@ -47,6 +48,10 @@ module.exports = app => {
                 if (result.affectedRows !== 1) {
                     throw '新增工料数据失败';
                 }
+                if (this.ctx.material.is_stage_self) {
+                    await this.ctx.service.materialStageBills.add(transaction, result.insertId);
+                    resultData.pushStageBillsData = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id: result.insertId } });
+                }
                 const insertArray = [];
                 const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
                 for (const ym of material_month) {
@@ -61,7 +66,8 @@ module.exports = app => {
                 }
                 if (insertArray.length !== 0) await transaction.insert(this.ctx.service.materialMonth.tableName, insertArray);
                 await transaction.commit();
-                return await this.getDataById(result.insertId);
+                resultData.info = await this.getDataById(result.insertId);
+                return resultData;
             } catch (error) {
                 console.log(error);
                 await transaction.rollback();
@@ -69,6 +75,93 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 添加工料
+         * @return {void}
+         */
+        async addByGlj(data, order = null) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const newOrder = this._.isNumber(order) ? parseInt(order) + 1 : await this._getMaxOrder(this.ctx.tender.id);
+            const transaction = await this.db.beginTransaction();
+            try {
+                // order以下的工料+1
+                await this._syncOrder(transaction, this.ctx.tender.id, newOrder, '+');
+                const resultData = {};
+                const newBills = {
+                    tid: this.ctx.tender.id,
+                    mid: this.ctx.material.id,
+                    code: data.code,
+                    name: data.name,
+                    unit: data.unit,
+                    m_up_risk: data.rise_range,
+                    m_down_risk: data.fall_range,
+                    spec: data.spec,
+                    m_type: data.type,
+                    remark: data.memo,
+                    order: newOrder,
+                    in_time: new Date(),
+                };
+                // 新增工料
+                const result = await transaction.insert(this.tableName, newBills);
+                if (result.affectedRows !== 1) {
+                    throw '新增工料数据失败';
+                }
+                if (this.ctx.material.is_stage_self) {
+                    await this.ctx.service.materialStageBills.add(transaction, result.insertId, data.memo);
+                    resultData.pushStageBillsData = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id: result.insertId } });
+                }
+                const insertArray = [];
+                const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
+                for (const ym of material_month) {
+                    const one_month = {
+                        tid: this.ctx.tender.id,
+                        mid: this.ctx.material.id,
+                        mb_id: result.insertId,
+                        msg_tp: null,
+                        yearmonth: ym,
+                    };
+                    insertArray.push(one_month);
+                }
+                if (insertArray.length !== 0) await transaction.insert(this.ctx.service.materialMonth.tableName, insertArray);
+                await transaction.commit();
+                resultData.info = await this.getDataById(result.insertId);
+                return resultData;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                throw error;
+            }
+        }
+
+        /**
+         * 移除清单时,同步其后清单order
+         * @param transaction - 事务
+         * @param {Number} cid - 变更cid
+         * @param {Number} order - order之后的
+         * @return {Promise<*>}
+         * @private
+         */
+        async _syncOrder(transaction, tid, order, selfOperate = '-', num = 1) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('tid', {
+                value: this.db.escape(tid),
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('order', {
+                value: order,
+                operate: '>=',
+            });
+            this.sqlBuilder.setUpdateData('order', {
+                value: num,
+                selfOperate,
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
+            const data = await transaction.query(sql, sqlParam);
+            return data;
+        }
+
         async _getMaxOrder(tenderId) {
             const sql = 'SELECT Max(??) As value FROM ?? Where tid = ' + tenderId;
             const sqlParam = ['order', this.tableName];
@@ -88,19 +181,37 @@ module.exports = app => {
             // 判断t_type是否为费用,且存在quantity,m_spread值
             const transaction = await this.db.beginTransaction();
             try {
+                // 防止多页面操作时,清单工料含量存在时工料可删
+                const materialListNum = await this.ctx.service.materialList.count({ tid: this.ctx.tender.id, mb_id: id });
+                if (materialListNum > 0) {
+                    throw '该工料已存在对应的清单工料含量,删除失败';
+                }
                 const mbInfo = await this.getDataById(id);
                 await transaction.delete(this.tableName, { id });
-                let m_tp = this.ctx.material.m_tp;
-                if (mbInfo.t_type === materialConst.t_type[1].value && mbInfo.quantity !== null && mbInfo.m_spread !== null) {
+                const m_tp = this.ctx.material.m_tp;
+                const result = { m_tp };
+                if (this.ctx.material.is_stage_self) {
+                    await transaction.delete(this.ctx.service.materialStageBills.tableName, { mb_id: id });
                     // 金额发生变化,则重新计算本期金额
-                    m_tp = await this.calcMaterialMTp(transaction);
+                    for (const sid of this.ctx.material.stage_id.split(',')) {
+                        const msInfo = await transaction.get(this.ctx.service.materialStage.tableName, { tid: this.ctx.tender.id, sid });
+                        await this.ctx.service.materialStage.updateMtp(transaction, msInfo.id);
+                    }
+                    result.stageBillsData = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id } });
+                    result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                    result.m_tp = await this.calcMaterialMTp(transaction);
+                } else if (mbInfo.t_type === materialConst.t_type[1].value && mbInfo.quantity !== null && mbInfo.m_spread !== null) {
+                    // 金额发生变化,则重新计算本期金额
+                    result.m_tp = await this.calcMaterialMTp(transaction);
                 }
                 const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
                 if (material_month.length > 0) {
                     await transaction.delete(this.ctx.service.materialMonth.tableName, { mb_id: id });
                 }
+                // order以下的清单-1
+                await this._syncOrder(transaction, this.ctx.tender.id, mbInfo.order, '-');
                 await transaction.commit();
-                return m_tp;
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -113,23 +224,24 @@ module.exports = app => {
          * @param {Number} id2 - 工料2的id
          * @returns {Promise<void>}
          */
-        async changeOrder(id1, id2) {
+        async changeOrder(datas) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
-            const bill1 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id1 });
-            const bill2 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id2 });
-            if (!bill1 || !bill2) {
-                throw '数据错误';
-            }
+            // const bill1 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id1 });
+            // const bill2 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id2 });
+            // if (!bill1 || !bill2) {
+            //     throw '数据错误';
+            // }
 
             const transaction = await this.db.beginTransaction();
             try {
-                const order = bill1.order;
-                bill1.order = bill2.order;
-                bill2.order = order;
-                await transaction.update(this.tableName, { id: bill1.id, order: bill1.order });
-                await transaction.update(this.tableName, { id: bill2.id, order: bill2.order });
+                // const order = bill1.order;
+                // bill1.order = bill2.order;
+                // bill2.order = order;
+                // await transaction.update(this.tableName, { id: bill1.id, order: bill1.order });
+                // await transaction.update(this.tableName, { id: bill2.id, order: bill2.order });
+                await transaction.updateRows(this.tableName, datas);
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -143,7 +255,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async save(data) {
+        async save(data, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -153,16 +265,90 @@ module.exports = app => {
             // 判断t_type是否为费用
             const transaction = await this.db.beginTransaction();
             try {
+                const result = {};
+                if (this.ctx.material.is_stage_self) {
+                    if (!ms_id) {
+                        throw '期参数有误';
+                    }
+                    const needUp = await this.updateOneBillsData(transaction, data, ms_id);
+                    const [one_m_tp, one_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, data, ms_id);
+                    // 更新materialStage 值
+                    data.m_tp = this.ctx.helper.round(one_m_tp, this.ctx.material.decimal.tp);
+                    data.m_tax_tp = this.ctx.helper.round(one_tax_tp, this.ctx.material.decimal.tp);
+                    data.quantity = null;
+                    data.msg_tp = null;
+                    data.msg_times = null;
+                    data.msg_spread = null;
+                    data.m_spread = null;
+                    delete data.ms_id;
+
+                    // 更新materialStage 值
+                    await this.ctx.service.materialStage.updateMtp(transaction, ms_id);
+                    result.stageBillsData = this.ctx.material.is_stage_self && needUp ?
+                        await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id: data.id } }) :
+                        await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, ms_id, mb_id: data.id } });
+                    result.stageData = this.ctx.material.is_stage_self && needUp ?
+                        await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) :
+                        await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id, id: ms_id } });
+                }
                 await transaction.update(this.tableName, data);
-                const m_tp = await this.calcMaterialMTp(transaction);
+                result.m_tp = await this.calcMaterialMTp(transaction);
                 await transaction.commit();
-                return m_tp;
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
         }
 
+        async updateOneBillsData(transaction, data, ms_id) {
+            // 当以下值和bills值不相同时,需要同步更新最新的计算值到stageBills表里
+            const updateColsArray = ['t_type', 'm_tax', 'basic_price', 'm_up_risk', 'm_down_risk', 'is_summary'];
+            let needUp = null;
+            const mbInfo = await this.getDataById(data.id);
+            for (const uc of updateColsArray) {
+                if (data[uc] !== undefined && data[uc] !== mbInfo[uc]) {
+                    needUp = uc;
+                    break;
+                }
+            }
+            if (needUp) {
+                const msList = await this.ctx.service.materialStage.getAllDataByCondition({ where: { mid: this.ctx.material.id } });
+                for (const ms of msList) {
+                    if (ms.id !== parseInt(ms_id)) {
+                        const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({
+                            mid: this.ctx.material.id,
+                            ms_id: ms.id,
+                            mb_id: data.id,
+                        });
+                        const updateData = {
+                            id: msbInfo.id,
+                        };
+                        if (needUp === 'm_tax') {
+                            updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(msbInfo.m_tp, (1 + this.ctx.helper.div(data.m_tax, 100))), this.ctx.material.decimal.tp);
+                        } else if (needUp === 'm_up_risk' || needUp === 'm_down_risk' || needUp === 'basic_price') {
+                            const basic_price = needUp === 'basic_price' ? data.basic_price : mbInfo.basic_price;
+                            const [msg_spread, m_spread] = await this.getSpread(mbInfo, msbInfo.msg_tp, this.ctx.material.decimal.up, basic_price);
+                            updateData.msg_spread = msg_spread;
+                            updateData.m_spread = m_spread;
+                            const newTp = this.ctx.helper.round(this.ctx.helper.mul(msbInfo.quantity, m_spread), this.ctx.material.decimal.tp);
+                            updateData.m_tp = newTp;
+                            updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp);
+                        } else if (needUp === 't_type') {
+                            updateData.quantity = null;
+                            updateData.m_tp = null;
+                            updateData.m_tax_tp = null;
+                        } else if (needUp === 'is_summary') {
+                            updateData.is_summary = data.is_summary;
+                        }
+                        await transaction.update(this.ctx.service.materialStageBills.tableName, updateData);
+                        await this.ctx.service.materialStage.updateMtp(transaction, ms.id);
+                    }
+                }
+            }
+            return needUp;
+        }
+
         async saveOrigin(data) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
@@ -190,7 +376,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async saveDatas(datas) {
+        async saveDatas(datas, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -200,15 +386,34 @@ module.exports = app => {
             try {
                 for (const data of datas) {
                     delete data.in_time;
+                    let needUp = null;
+                    if (this.ctx.material.is_stage_self) {
+                        needUp = await this.updateOneBillsData(transaction, data, ms_id);
+                        const [one_m_tp, one_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, data, ms_id);
+                        // 更新materialStage 值
+                        data.m_tp = this.ctx.helper.round(one_m_tp, this.ctx.material.decimal.tp);
+                        data.m_tax_tp = this.ctx.helper.round(one_tax_tp, this.ctx.material.decimal.tp);
+                        data.quantity = null;
+                        data.msg_tp = null;
+                        data.msg_times = null;
+                        data.msg_spread = null;
+                        data.msg_spread = null;
+                        data.m_spread = null;
+                    }
                     // delete data.m_tp;
                     // console.log(data);
                     await transaction.update(this.tableName, data);
                 }
-                // console.log(datas);
-                // await transaction.update(this.tableName, datas);
-                const m_tp = await this.calcMaterialMTp(transaction);
+                // 更新materialStage 值
+                const result = {};
+                if (this.ctx.material.is_stage_self) {
+                    await this.ctx.service.materialStage.updateMtp(transaction, ms_id);
+                    result.stageBillsData = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id } });
+                    result.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                }
+                result.m_tp = await this.calcMaterialMTp(transaction);
                 await transaction.commit();
-                return m_tp;
+                return result;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -297,10 +502,11 @@ module.exports = app => {
          * @param data
          * @returns {Promise<void>}
          */
-        async getSpread(data, msg_tp, newDecimalUp = this.ctx.material.decimal.up) {
+        async getSpread(data, msg_tp, newDecimalUp = this.ctx.material.decimal.up, basic_price = null) {
             data.msg_tp = msg_tp;
-            const msg_spread = this.ctx.helper.round(this.ctx.helper.sub(data.msg_tp, data.basic_price), newDecimalUp);
-            const cor = msg_spread >= 0 ? this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_up_risk, 100)) : this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_down_risk, 100));
+            const newBp = basic_price ? basic_price : data.basic_price;
+            const msg_spread = this.ctx.helper.round(this.ctx.helper.sub(data.msg_tp, newBp), newDecimalUp);
+            const cor = msg_spread >= 0 ? this.ctx.helper.mul(newBp, this.ctx.helper.div(data.m_up_risk, 100)) : this.ctx.helper.mul(newBp, this.ctx.helper.div(data.m_down_risk, 100));
             const m_spread = Math.abs(msg_spread) > Math.abs(cor) ? (msg_spread > 0 ? this.ctx.helper.round(this.ctx.helper.sub(msg_spread, cor), newDecimalUp) : this.ctx.helper.round(this.ctx.helper.add(msg_spread, cor), newDecimalUp)) : 0;
             return [msg_spread, m_spread];
         }
@@ -316,19 +522,60 @@ module.exports = app => {
             }
             const transaction = await this.db.beginTransaction();
             try {
-                const mbInfo = await this.getDataById(data.id);
-                data.m_tp = this.ctx.helper.round(this.ctx.helper.mul(data.quantity, mbInfo.m_spread), this.ctx.material.decimal.tp);
-                data.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(data.m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp);
-                await transaction.update(this.tableName, data);
-                let m_tp = this.ctx.material.m_tp;
-                if (mbInfo.quantity !== data.quantity) {
-                    m_tp = await this.calcMaterialMTp(transaction);
+                const returnData = {};
+                returnData.m_tp = this.ctx.material.m_tp;
+                if (this.ctx.material.is_stage_self) {
+                    const mbInfo = await this.getDataById(data.id);
+                    let all_m_tp = 0;
+                    let all_tax_tp = 0;
+                    for (const sid of this.ctx.material.stage_id.split(',')) {
+                        const materialCalculator = new MaterialCalculator(this.ctx, sid, this.ctx.tender.info);
+                        const quantity = await materialCalculator.calculateExpr(data.expr);
+                        const msInfo = await this.ctx.service.materialStage.getDataByCondition({ mid: this.ctx.material.id, sid });
+                        const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({ mid: this.ctx.material.id, mb_id: data.id, ms_id: msInfo.id });
+                        const newQuantity = quantity !== 0 ? this.ctx.helper.round(quantity, this.ctx.material.decimal.qty) : null;
+                        const m_tp = newQuantity ? this.ctx.helper.round(this.ctx.helper.mul(newQuantity, msbInfo.m_spread), this.ctx.material.decimal.tp) : null;
+                        const updateData = {
+                            id: data.id,
+                            quantity: newQuantity,
+                            m_tp,
+                            m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                        };
+                        const [one_bill_m_tp, one_bill_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, updateData, msInfo.id);
+                        await this.ctx.service.materialStage.updateMtp(transaction, msInfo.id);
+                        all_m_tp = this.ctx.helper.round(one_bill_m_tp, this.ctx.material.decimal.tp);
+                        all_tax_tp = this.ctx.helper.round(one_bill_tax_tp, this.ctx.material.decimal.tp);
+                    }
+                    const updateBillsData = {
+                        id: data.id,
+                        expr: data.expr,
+                        m_tp: all_m_tp ? all_m_tp : null,
+                        m_tax_tp: all_tax_tp ? all_tax_tp : null,
+                    };
+                    console.log(all_m_tp);
+                    await transaction.update(this.tableName, updateBillsData);
+                    returnData.m_tp = await this.calcMaterialMTp(transaction);
+                    returnData.stageBillsData = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id: data.id } });
+                    returnData.stageData = await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } });
+                } else {
+                    const materialCalculator = new MaterialCalculator(this.ctx, this.ctx.material.stage_id, this.ctx.tender.info);
+                    const quantity = await materialCalculator.calculateExpr(data.expr);
+                    // 更新quantity值并重新返回计算本期金额,截止本期金额
+                    const updateData = {
+                        id: data.id,
+                        quantity: quantity !== 0 ? this.ctx.helper.round(quantity, this.ctx.material.decimal.qty) : null,
+                        expr: data.expr,
+                    };
+                    const mbInfo = await this.getDataById(updateData.id);
+                    updateData.m_tp = this.ctx.helper.round(this.ctx.helper.mul(updateData.quantity, mbInfo.m_spread), this.ctx.material.decimal.tp);
+                    updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(updateData.m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp);
+                    await transaction.update(this.tableName, updateData);
+                    if (mbInfo.quantity !== updateData.quantity) {
+                        returnData.m_tp = await this.calcMaterialMTp(transaction);
+                    }
                 }
                 await transaction.commit();
-                const returnData = {
-                    m_tp,
-                    info: await this.getDataById(data.id),
-                }
+                returnData.info = await this.getDataById(data.id);
                 return returnData;
             } catch (err) {
                 await transaction.rollback();
@@ -356,52 +603,115 @@ module.exports = app => {
         }
 
         // 小数位变化更新单价和金额
-        async resetDecimal(transaction, newDecimalUp, newDecimalTp) {
+        async resetDecimal(transaction, newDecimalUp, newDecimalTp, newDecimalQty) {
             const mbList = await transaction.select(this.tableName, { where: { tid: this.ctx.tender.id }, orders: [['order', 'asc']] });
             const updateList = [];
             const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
             const updateMonthList = [];
+            const materialStageList = this.ctx.material.is_stage_self ? await transaction.select(this.ctx.service.materialStage.tableName, { where: { mid: this.ctx.material.id } }) : [];
             for (const mb of mbList) {
                 const updateData = {
                     id: mb.id,
                 };
-                if (newDecimalUp !== this.ctx.material.decimal.up) {
-                    let newmsg_tp = this.ctx.helper.round(mb.msg_tp, newDecimalUp);
-                    mb.msg_tp = newmsg_tp;
-                    // 判断是否有月信息价,如果有则msg_tp值由月信息价的平均单价获得,并更新月信息价单价
-                    if (material_month.length > 0) {
-                        const monthList = await transaction.select(this.ctx.service.materialMonth.tableName, { where: { mb_id: mb.id, mid: this.ctx.material.id } });
-                        if (monthList.length !== 0) {
-                            for (const m of monthList) {
-                                // 更新月信息单价小数位
-                                const newMonthMsgTP = this.ctx.helper.round(m.msg_tp, newDecimalUp);
-                                if (m.msg_tp && newMonthMsgTP !== m.msg_tp) {
-                                    m.msg_tp = newMonthMsgTP;
-                                    updateMonthList.push({ id: m.id, msg_tp: m.msg_tp });
+                if (this.ctx.material.is_stage_self) {
+                    const updateStageBillsList = [];
+                    for (const ms of materialStageList) {
+                        const msb = await transaction.get(this.ctx.service.materialStageBills.tableName, { mid: this.ctx.material.id, mb_id: mb.id, ms_id: ms.id });
+                        msb.m_up_risk = mb.m_up_risk;
+                        msb.m_down_risk = mb.m_down_risk;
+                        const updateStageBillData = {
+                            id: msb.id,
+                        };
+                        if (newDecimalQty !== this.ctx.material.decimal.qty) {
+                            // 通过管理重新算出quantity并保留小数位
+                            const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.ctx.service.materialList.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `ms_id`=? AND `is_join`=1';
+                            const sqlParam = [this.ctx.material.id, mb.id, ms.id];
+                            const mb_quantity = await transaction.queryOne(sql, sqlParam);
+                            const newQuantity = this.ctx.helper.round(mb_quantity.quantity, newDecimalQty);
+                            if (newQuantity !== msb.quantity) {
+                                updateStageBillData.quantity = newQuantity;
+                                msb.quantity = newQuantity;
+                            }
+                        }
+                        if (newDecimalUp !== this.ctx.material.decimal.up) {
+                            const newmsg_tp = this.ctx.helper.round(msb.msg_tp, newDecimalUp);
+                            msb.msg_tp = newmsg_tp;
+                            const newbasic_price = this.ctx.helper.round(mb.basic_price, newDecimalUp);
+                            const [newmsg_spread, newm_spread] = await this.getSpread(msb, msb.msg_tp, newDecimalUp, newbasic_price);
+                            msb.m_spread = newm_spread;
+                            updateStageBillData.msg_tp = newmsg_tp;
+                            updateStageBillData.msg_spread = newmsg_spread;
+                            updateStageBillData.m_spread = newm_spread;
+                        }
+                        const newTp = this.ctx.helper.round(this.ctx.helper.mul(msb.quantity, msb.m_spread), newDecimalTp);
+                        updateStageBillData.m_tp = newTp;
+                        updateStageBillData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), newDecimalTp);
+                        updateStageBillsList.push(updateStageBillData);
+                    }
+                    if (newDecimalUp !== this.ctx.material.decimal.up) {
+                        const newbasic_price = this.ctx.helper.round(mb.basic_price, newDecimalUp);
+                        updateData.basic_price = newbasic_price;
+                    }
+                    if (updateStageBillsList.length > 0) await transaction.updateRows(this.ctx.service.materialStageBills.tableName, updateStageBillsList);
+                    const sql = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.ctx.service.materialStageBills.tableName + ' WHERE `tid` = ? AND `mid` = ? AND `mb_id` = ? AND `is_summary` = 1';
+                    const sqlParam = [this.ctx.tender.id, this.ctx.material.id, mb.id];
+                    const tp = await transaction.queryOne(sql, sqlParam);
+                    updateData.m_tp = this.ctx.helper.round(tp.total_price, newDecimalTp);
+                    updateData.m_tax_tp = this.ctx.helper.round(tp.tax_total_price, newDecimalTp);
+                    updateList.push(updateData);
+                } else {
+                    if (newDecimalUp !== this.ctx.material.decimal.up) {
+                        let newmsg_tp = this.ctx.helper.round(mb.msg_tp, newDecimalUp);
+                        mb.msg_tp = newmsg_tp;
+                        // 判断是否有月信息价,如果有则msg_tp值由月信息价的平均单价获得,并更新月信息价单价
+                        if (material_month.length > 0) {
+                            const monthList = await transaction.select(this.ctx.service.materialMonth.tableName, { where: { mb_id: mb.id, mid: this.ctx.material.id } });
+                            if (monthList.length !== 0) {
+                                for (const m of monthList) {
+                                    // 更新月信息单价小数位
+                                    const newMonthMsgTP = this.ctx.helper.round(m.msg_tp, newDecimalUp);
+                                    if (m.msg_tp && newMonthMsgTP !== m.msg_tp) {
+                                        m.msg_tp = newMonthMsgTP;
+                                        updateMonthList.push({ id: m.id, msg_tp: m.msg_tp });
+                                    }
                                 }
+                                const mb_msg_tp_sum = this._.sumBy(monthList, 'msg_tp');
+                                const month_num = material_month.length - this.ctx.helper.arrayCount(this._.map(monthList, 'msg_tp'), [null, '', 0]);
+                                newmsg_tp = month_num !== 0 ? this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, month_num), newDecimalUp) : null;
+                                mb.msg_tp = newmsg_tp;
                             }
-                            const mb_msg_tp_sum = this._.sumBy(monthList, 'msg_tp');
-                            const month_num = material_month.length - this.ctx.helper.arrayCount(this._.map(monthList, 'msg_tp'), [null, '', 0]);
-                            newmsg_tp = month_num !== 0 ? this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, month_num), newDecimalUp) : null;
-                            mb.msg_tp = newmsg_tp;
                         }
+                        const newbasic_price = this.ctx.helper.round(mb.basic_price, newDecimalUp);
+                        mb.basic_price = newbasic_price;
+                        const [newmsg_spread, newm_spread] = await this.getSpread(mb, mb.msg_tp, newDecimalUp);
+                        mb.m_spread = newm_spread;
+                        updateData.basic_price = newbasic_price;
+                        updateData.msg_tp = newmsg_tp;
+                        updateData.msg_spread = newmsg_spread;
+                        updateData.m_spread = newm_spread;
+                    }
+                    if (newDecimalQty !== this.ctx.material.decimal.qty) {
+                        // 通过管理重新算出quantity并保留小数位
+                        const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.ctx.service.materialList.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `is_join`=1';
+                        const sqlParam = [this.ctx.material.id, mb.id];
+                        const mb_quantity = await transaction.queryOne(sql, sqlParam);
+                        const newQuantity = this.ctx.helper.round(mb_quantity.quantity, newDecimalQty);
+                        mb.quantity = newQuantity;
+                        updateData.quantity = newQuantity;
                     }
-                    const newbasic_price = this.ctx.helper.round(mb.basic_price, newDecimalUp);
-                    mb.basic_price = newbasic_price;
-                    const [newmsg_spread, newm_spread] = await this.getSpread(mb, mb.msg_tp, newDecimalUp);
-                    mb.m_spread = newm_spread;
-                    updateData.basic_price = newbasic_price;
-                    updateData.msg_tp = newmsg_tp;
-                    updateData.msg_spread = newmsg_spread;
-                    updateData.m_spread = newm_spread;
+                    const newTp = this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, mb.m_spread), newDecimalTp);
+                    updateData.m_tp = newTp;
+                    updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), newDecimalTp);
+                    updateList.push(updateData);
                 }
-                const newTp = this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, mb.m_spread), newDecimalTp);
-                updateData.m_tp = newTp;
-                updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), newDecimalTp);
-                updateList.push(updateData);
             }
             if (updateMonthList.length > 0) await transaction.updateRows(this.ctx.service.materialMonth.tableName, updateMonthList);
             if (updateList.length > 0) await transaction.updateRows(this.tableName, updateList);
+            if (this.ctx.material.is_stage_self) {
+                for (const ms of materialStageList) {
+                    await this.ctx.service.materialStage.updateMtp(transaction, ms.id);
+                }
+            }
         }
     }
 

+ 104 - 21
app/service/material_list.js

@@ -27,7 +27,7 @@ module.exports = app => {
          * 添加工料清单关联
          * @return {void}
          */
-        async add(data) {
+        async add(data, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -44,6 +44,7 @@ module.exports = app => {
                     gather_qty: data.gather_qty,
                     is_join: data.is_join,
                     is_self: 1,
+                    ms_id: ms_id ? ms_id : null,
                     in_time: new Date(),
                 };
                 list.push(newLists);
@@ -62,7 +63,7 @@ module.exports = app => {
          * @param {int} id 工料id
          * @return {void}
          */
-        async del(id, mb_id) {
+        async del(id, mb_id, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -70,7 +71,7 @@ module.exports = app => {
             try {
                 // 判断是否可删
                 await transaction.delete(this.tableName, { id });
-                await this.calcQuantityByML(transaction, mb_id);
+                await this.calcQuantityByML(transaction, mb_id, ms_id);
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -85,7 +86,7 @@ module.exports = app => {
          * @param {int} order 期数
          * @return {void}
          */
-        async save(data, order) {
+        async save(data, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -95,7 +96,7 @@ module.exports = app => {
                 const mb_id = data.mb_id;
                 delete data.mb_id;
                 await transaction.update(this.tableName, data);
-                await this.calcQuantityByML(transaction, mb_id);
+                await this.calcQuantityByML(transaction, mb_id, ms_id);
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -109,7 +110,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async saveDatas(datas) {
+        async saveDatas(datas, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -121,7 +122,7 @@ module.exports = app => {
                     const mb_id = data.mb_id;
                     delete data.mb_id;
                     await transaction.update(this.tableName, data);
-                    await this.calcQuantityByML(transaction, mb_id);
+                    await this.calcQuantityByML(transaction, mb_id, ms_id);
                 }
                 await transaction.commit();
                 return true;
@@ -209,25 +210,76 @@ module.exports = app => {
          * @param mb_id
          * @return {Promise<*>}
          */
-        async calcQuantityByML(transaction, mb_id) {
+        async calcQuantityByML(transaction, mb_id, ms_id = null, updateAllStage = '') {
             // 修改material_bills值
             const mbInfo = await this.ctx.service.materialBills.getDataById(mb_id);
             if (!mbInfo) {
                 throw '不存在该工料';
             }
-            const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `is_join`=1';
+            let m_spread = mbInfo.m_spread;
+            let updateId = mb_id;
+            if (ms_id) {
+                const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({ mid: this.ctx.material.id, mb_id, ms_id });
+                m_spread = msbInfo.m_spread;
+                updateId = msbInfo.id;
+            }
+            const msSql = ms_id ? ' AND `ms_id` = ' + ms_id : '';
+            const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.tableName + ' WHERE `mid`=? AND `mb_id`=?' + msSql + ' AND `is_join`=1';
             const sqlParam = [this.ctx.material.id, mb_id];
             const mb_quantity = await transaction.queryOne(sql, sqlParam);
             console.log(mb_quantity);
             const newQuantity = this.ctx.helper.round(mb_quantity.quantity, this.ctx.material.decimal.qty);
-            const newTp = this.ctx.helper.round(this.ctx.helper.mul(newQuantity, mbInfo.m_spread), this.ctx.material.decimal.tp);
+            const newTp = this.ctx.helper.round(this.ctx.helper.mul(newQuantity, m_spread), this.ctx.material.decimal.tp);
             const updateData = {
-                id: mb_id,
+                id: updateId,
                 quantity: newQuantity,
                 m_tp: newTp,
                 m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
             };
-            await transaction.update(this.ctx.service.materialBills.tableName, updateData);
+            if (ms_id) {
+                await transaction.update(this.ctx.service.materialStageBills.tableName, updateData);
+                await this.ctx.service.materialStage.updateMtp(transaction, ms_id);
+                if (updateAllStage === 'all') {
+                    const updateDatas = [];
+                    const updateMsIds = [];
+                    const msbList = await transaction.select(this.ctx.service.materialStageBills.tableName, { where: { mid: this.ctx.material.id, mb_id } });
+                    for (const msb of msbList) {
+                        if (msb.ms_id !== parseInt(ms_id)) {
+                            const sql4 = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `ms_id`=? AND `is_join`=1';
+                            const sqlParam4 = [this.ctx.material.id, mb_id, msb.ms_id];
+                            const mb_quantity4 = await transaction.queryOne(sql4, sqlParam4);
+                            const newQuantity4 = this.ctx.helper.round(mb_quantity4.quantity, this.ctx.material.decimal.qty);
+                            const newTp4 = this.ctx.helper.round(this.ctx.helper.mul(newQuantity4, msb.m_spread), this.ctx.material.decimal.tp);
+                            const updateData4 = {
+                                id: msb.id,
+                                quantity: newQuantity4,
+                                m_tp: newTp4,
+                                m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp4, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                            };
+                            updateDatas.push(updateData4);
+                            updateMsIds.push(msb.ms_id);
+                        }
+                    }
+                    if (updateDatas.length > 0) {
+                        await transaction.updateRows(this.ctx.service.materialStageBills.tableName, updateDatas);
+                        for (const msId of updateMsIds) {
+                            await this.ctx.service.materialStage.updateMtp(transaction, msId);
+                        }
+                    }
+                }
+                // 还要更新bills表的m_tp和m_tax_tp值
+                const sql3 = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.ctx.service.materialStageBills.tableName + ' WHERE `tid` = ? AND `mid` = ? AND `mb_id` = ? AND `is_summary` = 1';
+                const sqlParam3 = [this.ctx.tender.id, this.ctx.material.id, mb_id];
+                const tp3 = await transaction.queryOne(sql3, sqlParam3);
+                const updateBillsData = {
+                    id: mb_id,
+                    m_tp: tp3.total_price,
+                    m_tax_tp: tp3.tax_total_price,
+                };
+                await transaction.update(this.ctx.service.materialBills.tableName, updateBillsData);
+            } else {
+                await transaction.update(this.ctx.service.materialBills.tableName, updateData);
+            }
             // 计算本期总金额
             const sql2 = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.ctx.service.materialBills.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
             const sqlParam2 = [this.ctx.tender.id];
@@ -248,7 +300,7 @@ module.exports = app => {
          * @return {void}
          */
         async getMaterialData(tid, mid) {
-            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`tid`, ml.`mid`, mb.m_spread' +
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mb.m_spread, ml.ms_id' +
                 ' FROM ' + this.tableName + ' as ml' +
                 ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb' +
                 ' ON ml.`mb_id` = mb.`id`' +
@@ -258,7 +310,7 @@ module.exports = app => {
         }
 
         async getPreMaterialData(tid, mid) {
-            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`tid`, ml.`mid`, mbh.m_spread' +
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mbh.m_spread, ml.ms_id' +
                 ' FROM ' + this.tableName + ' as ml' +
                 ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb ON ml.`mb_id` = mb.`id`' +
                 ' LEFT JOIN ' + this.ctx.service.materialBillsHistory.tableName + ' as mbh ON ml.`mb_id` = mbh.`mb_id` and mbh.mid = ?' +
@@ -267,6 +319,28 @@ module.exports = app => {
             return await this.db.query(sql, sqlParam);
         }
 
+        async getMaterialStageData(tid, mid) {
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, msb.m_spread, ml.ms_id, ms.sid, ms.order as s_order' +
+                ' FROM ' + this.tableName + ' as ml' +
+                ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb ON ml.`mb_id` = mb.`id`' +
+                ' LEFT JOIN ' + this.ctx.service.materialStageBills.tableName + ' as msb ON ml.mb_id = msb.mb_id AND ml.ms_id = msb.ms_id' +
+                ' LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' as ms ON ml.`ms_id` = ms.`id`' +
+                ' WHERE ml.`tid` = ? AND ml.`mid` = ?';
+            const sqlParam = [tid, mid];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        async getPreMaterialStageData(tid, mid) {
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, msb.m_spread, ml.ms_id, ms.sid, ms.order as s_order' +
+                ' FROM ' + this.tableName + ' as ml' +
+                ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb ON ml.`mb_id` = mb.`id`' +
+                ' LEFT JOIN ' + this.ctx.service.materialStageBills.tableName + ' as msb ON ml.`mb_id` = msb.mb_id AND ml.ms_id = msb.ms_id And ml.mid = msb.mid' +
+                ' LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' as ms ON ml.`ms_id` = ms.`id`' +
+                ' WHERE ml.`tid` = ? AND ml.`mid` = ?';
+            const sqlParam = [tid, mid];
+            return await this.db.query(sql, sqlParam);
+        }
+
         /**
          * 复制上一期并生成新一期清单工料关联,计算新一期小计值
          * @param transaction
@@ -311,7 +385,7 @@ module.exports = app => {
          * @param newMid
          * @return {Promise<void>}
          */
-        async copyPreMaterialList2(transaction, materialListData, materialSelfListData, notJoinList, newMaterial) {
+        async copyPreMaterialList2(transaction, materialListData, materialSelfListData, notJoinList, newMaterial, materialStageData) {
             if (materialListData && materialListData.length > 0) {
                 const copyMLArray = [];
                 for (const ml of materialListData) {
@@ -330,6 +404,10 @@ module.exports = app => {
                         is_join: is_join ? 0 : 1,
                         in_time: new Date(),
                     };
+                    if (ml.sid) {
+                        const ms = this._.find(materialStageData, { sid: ml.sid });
+                        if (ms && ms.id) newMaterialList.ms_id = ms.id;
+                    }
                     copyMLArray.push(newMaterialList);
                 }
                 if (copyMLArray.length !== 0) await transaction.insert(this.tableName, copyMLArray);
@@ -353,6 +431,10 @@ module.exports = app => {
                         is_self: 1,
                         in_time: new Date(),
                     };
+                    if (ml.sid) {
+                        const ms = this._.find(materialStageData, { sid: ml.sid });
+                        if (ms && ms.id) newMaterialList.ms_id = ms.id;
+                    }
                     copyMLArray2.push(newMaterialList);
                 }
                 if (copyMLArray2.length !== 0) await transaction.insert(this.tableName, copyMLArray2);
@@ -398,6 +480,7 @@ module.exports = app => {
                                 gcl_id: xmj.gcl_id,
                                 xmj_id: xmj.xmj_id,
                                 mx_id: xmj.mx_id,
+                                ms_id: xmj.ms_id ? xmj.ms_id : null,
                                 gather_qty: xmj.gather_qty,
                                 in_time: new Date(),
                                 is_join: xmj.is_join,
@@ -463,7 +546,7 @@ module.exports = app => {
          * @param {int} id 工料id
          * @return {void}
          */
-        async dels(datas, checklist = false, fromCheckList = false) {
+        async dels(datas, checklist = false, fromCheckList = false, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -479,7 +562,7 @@ module.exports = app => {
                     }
                 }
                 // await transaction.delete(this.tableName, { id });
-                await this.calcQuantityByML(transaction, datas.mb_id);
+                await this.calcQuantityByML(transaction, datas.mb_id, ms_id, 'all');
                 if (checklist) {
                     await this.ctx.service.materialChecklist.updateHadBills(transaction, checklist.id, checklist.had_bills);
                 }
@@ -502,7 +585,7 @@ module.exports = app => {
          * @param {int} order 期数
          * @return {void}
          */
-        async saves(datas, checklist = false) {
+        async saves(datas, checklist = false, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -550,7 +633,7 @@ module.exports = app => {
                 }
                 if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
                 if (updateListGcl.length > 0) await transaction.updateRows(this.service.materialListGcl.tableName, updateListGcl);
-                await this.calcQuantityByML(transaction, mb_id);
+                await this.calcQuantityByML(transaction, mb_id, ms_id, 'all');
                 await transaction.commit();
                 const gclList = await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
                 return checklist ? gclList : {
@@ -568,7 +651,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async savePastes(datas, checklist = false) {
+        async savePastes(datas, checklist = false, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -617,7 +700,7 @@ module.exports = app => {
                     }
                     if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
                     if (updateListGcl.length > 0) await transaction.updateRows(this.service.materialListGcl.tableName, updateListGcl);
-                    await this.calcQuantityByML(transaction, data.mb_id);
+                    await this.calcQuantityByML(transaction, data.mb_id, ms_id, 'all');
                 }
                 await transaction.commit();
                 const gclList = await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });

+ 21 - 6
app/service/material_list_notjoin.js

@@ -26,7 +26,7 @@ module.exports = app => {
          * 添加不参与调差的清单
          * @return {void}
          */
-        async add(data) {
+        async add(data, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -42,7 +42,7 @@ module.exports = app => {
                 };
                 data.xmj_id = data.id;
                 data.mx_id = data.mx_id ? data.mx_id : '';
-                await this.updateAllMaterials(transaction, data, 'add');
+                await this.updateAllMaterials(transaction, data, 'add', ms_id);
                 // 新增不参与调差清单
                 const result = await transaction.insert(this.tableName, newListNotJoin);
                 if (result.affectedRows === 0) {
@@ -61,14 +61,14 @@ module.exports = app => {
          * @param {int} id 工料id
          * @return {void}
          */
-        async del(id) {
+        async del(id, ms_id = null) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
             const transaction = await this.db.beginTransaction();
             try {
                 const notJoinInfo = await this.getDataById(id);
-                await this.updateAllMaterials(transaction, notJoinInfo, 'del');
+                await this.updateAllMaterials(transaction, notJoinInfo, 'del', ms_id);
                 // 判断是否可删
                 const result = await transaction.delete(this.tableName, { id });
                 await transaction.commit();
@@ -84,7 +84,7 @@ module.exports = app => {
          * @param transaction
          * @returns {Promise<void>}
          */
-        async updateAllMaterials(transaction, data, type) {
+        async updateAllMaterials(transaction, data, type, ms_id = null) {
             // 先判断material_list是否存在值并quantity不为null
             const searchSql = {
                 mid: this.ctx.material.id,
@@ -115,7 +115,7 @@ module.exports = app => {
                 }
                 // 重新计算金额
                 for (const mb_id of mbIdList) {
-                    await this.service.materialList.calcQuantityByML(transaction, mb_id);
+                    await this.service.materialList.calcQuantityByML(transaction, mb_id, ms_id, 'all');
                 }
             }
         }
@@ -146,6 +146,21 @@ module.exports = app => {
             // 复制上一期不参与调差的清单
             return notJoinlist.length > 0 ? await transaction.insert(this.tableName, notJoinlist) : true;
         }
+
+        async getJoinMsg(transaction, mid, datas) {
+            const searchData = {
+                tid: this.ctx.tender.id,
+                mid,
+                gcl_id: datas.gcl_id,
+                xmj_id: datas.xmj_id,
+                mx_id: datas.mx_id,
+            };
+            const info = await transaction.get(this.tableName, searchData);
+            if (info) {
+                return 0;
+            }
+            return 1;
+        }
     }
     return MaterialListNotJoin;
 };

+ 0 - 0
app/service/material_list_self.js


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio