Explorar o código

Merge branch 'master' of http://192.168.1.12:3000/SmartCost/ConstructionCost

zhangyin %!s(int64=7) %!d(string=hai) anos
pai
achega
a179165ec7
Modificáronse 100 ficheiros con 4884 adicións e 924 borrados
  1. 1 0
      .gitignore
  2. 15 0
      Dockerfile_pp
  3. 3 1
      config/config.js
  4. 106 4
      config/gulpConfig.js
  5. 176 1
      gulpfile.js
  6. 3 3
      modules/bills_lib/models/bills_lib_schemas.js
  7. 45 0
      modules/common/const/bills_fixed.js
  8. 0 1
      modules/complementary_glj_lib/controllers/gljController.js
  9. 67 4
      modules/complementary_glj_lib/models/gljModel.js
  10. 4 1
      modules/complementary_glj_lib/routes/routes.js
  11. 117 0
      modules/complementary_ration_lib/controllers/compleRationController.js
  12. 36 0
      modules/complementary_ration_lib/controllers/compleSectionTreeController.js
  13. 60 0
      modules/complementary_ration_lib/controllers/compleViewController.js
  14. 29 0
      modules/complementary_ration_lib/controllers/searchController.js
  15. 459 0
      modules/complementary_ration_lib/models/compleRationModel.js
  16. 29 0
      modules/complementary_ration_lib/models/compleViewModel.js
  17. 118 0
      modules/complementary_ration_lib/models/schemas.js
  18. 82 0
      modules/complementary_ration_lib/models/searchModel.js
  19. 88 0
      modules/complementary_ration_lib/models/sectionTreeModel.js
  20. 57 0
      modules/complementary_ration_lib/routes/routes.js
  21. 38 3
      modules/glj/controllers/glj_controller.js
  22. 37 13
      modules/glj/models/glj_list_model.js
  23. 8 10
      modules/glj/models/unit_price_file_model.js
  24. 24 12
      modules/glj/models/unit_price_model.js
  25. 1 0
      modules/glj/routes/glj_router.js
  26. 191 2
      modules/main/controllers/bills_controller.js
  27. 45 0
      modules/main/controllers/installation_controller.js
  28. 3 3
      modules/main/controllers/labour_coe_controller.js
  29. 45 0
      modules/main/controllers/project_controller.js
  30. 1 0
      modules/main/controllers/quantity_detail_controller.js
  31. 60 1
      modules/main/controllers/ration_controller.js
  32. 105 0
      modules/main/facade/bill_facade.js
  33. 1 0
      modules/main/facade/calc_program_facade.js
  34. 156 0
      modules/main/facade/installation_facade.js
  35. 4 3
      modules/main/facade/labour_coe_facade.js
  36. 83 0
      modules/main/facade/project_facade.js
  37. 27 20
      modules/main/facade/quantity_detail_facade.js
  38. 331 0
      modules/main/facade/ration_facade.js
  39. 31 0
      modules/main/facade/ration_installation_facade.js
  40. 45 7
      modules/main/models/bills.js
  41. 2 2
      modules/main/models/glj.js
  42. 54 0
      modules/main/models/installation_fee.js
  43. 4 0
      modules/main/models/project.js
  44. 3 1
      modules/main/models/project_consts.js
  45. 17 5
      modules/main/models/ration.js
  46. 33 0
      modules/main/models/ration_installation.js
  47. 8 1
      modules/main/routes/bills_route.js
  48. 15 0
      modules/main/routes/installation_route.js
  49. 3 0
      modules/main/routes/project_route.js
  50. 3 1
      modules/main/routes/ration_route.js
  51. 59 2
      modules/options/controllers/optionsController.js
  52. 57 4
      modules/options/models/optionTypes.js
  53. 29 1
      modules/options/models/optionsModel.js
  54. 10 1
      modules/options/models/schemas.js
  55. 10 0
      modules/pm/controllers/new_proj_controller.js
  56. 29 13
      modules/pm/controllers/pm_controller.js
  57. 230 109
      modules/pm/models/project_model.js
  58. 6 1
      modules/pm/models/project_property_template.js
  59. 9 1
      modules/pm/models/project_schema.js
  60. 2 2
      modules/pm/models/templates/bills_template_model.js
  61. 3 1
      modules/pm/models/templates/schemas/bills_template.js
  62. 11 7
      modules/ration_glj/facade/glj_calculate_facade.js
  63. 489 401
      modules/ration_glj/facade/ration_glj_facade.js
  64. 2 2
      modules/ration_glj/models/quantity_detail.js
  65. 1 1
      modules/ration_glj/models/ration_coe.js
  66. 2 2
      modules/ration_glj/models/ration_glj.js
  67. 48 18
      modules/ration_glj/models/ration_glj_temp.js
  68. 3 2
      modules/ration_repository/controllers/ration_controller.js
  69. 1 1
      modules/ration_repository/controllers/ration_repository_controller.js
  70. 1 1
      modules/ration_repository/controllers/ration_section_tree_controller.js
  71. 2 2
      modules/ration_repository/controllers/search_controller.js
  72. 14 3
      modules/ration_repository/models/coe.js
  73. 1 1
      modules/ration_repository/models/glj_repository.js
  74. 15 4
      modules/ration_repository/models/ration_item.js
  75. 5 1
      modules/ration_repository/models/ration_section_tree.js
  76. 5 1
      modules/ration_repository/models/repository_map.js
  77. 1 0
      modules/reports/models/tpl_tree_node.js
  78. 2 2
      modules/reports/rpt_component/helper/jpc_helper_area.js
  79. 29 25
      modules/reports/rpt_component/helper/jpc_helper_common_output.js
  80. 1 0
      modules/reports/rpt_component/jpc_bill_tab.js
  81. 53 3
      modules/reports/rpt_component/jpc_flow_tab.js
  82. 2 1
      modules/reports/rpt_component/jpc_rte.js
  83. 153 35
      modules/reports/util/rpt_construct_data_util.js
  84. 45 37
      modules/reports/util/rpt_excel_util.js
  85. 63 27
      public/stringUtil.js
  86. 0 1
      public/web/PerfectLoad.js
  87. 17 0
      public/web/common_util.js
  88. 67 16
      public/web/id_tree.js
  89. 1 1
      public/web/rpt_value_define.js
  90. 11 1
      public/web/scMathUtil.js
  91. 214 36
      public/web/sheet/sheet_common.js
  92. 38 6
      public/web/socket/connection.js
  93. 10 8
      public/web/treeDataHelper.js
  94. 32 1
      public/web/tree_sheet/tree_sheet_controller.js
  95. 52 31
      public/web/tree_sheet/tree_sheet_helper.js
  96. 12 7
      socket.js
  97. 43 5
      test/demo/stringTest.js
  98. 3 0
      test/unit/reports/test_cover_01.js
  99. 118 0
      test/unit/reports/test_cover_02.js
  100. 0 0
      test/unit/reports/test_tpl_09.js

+ 1 - 0
.gitignore

@@ -4,5 +4,6 @@ dist/
 .idea/
 tmp/*.xlsx
 tmp/*.pdf
+tmp/*.jsp
 test/unit/logs
 *.log

+ 15 - 0
Dockerfile_pp

@@ -0,0 +1,15 @@
+FROM costbase:latest
+
+WORKDIR /home/ConstructionCost
+
+RUN git pull http://192.168.1.12:3000/SmartCost/ConstructionCost master
+
+RUN cnpm install
+
+RUN gulp build
+
+EXPOSE 6060
+
+ENV NODE_ENV=pp
+
+ENTRYPOINT babel-node server.js

+ 3 - 1
config/config.js

@@ -2,6 +2,7 @@ module.exports = {
     current: {server: "192.168.1.184", port: "60666",redis:{server:'192.168.1.184',port:'6379',pwd:'smartCost'}},
     local: {server: "localhost", port: "27017"},
     qa: {server: "192.168.1.184", port: "60666"},
+    pp:{server: "172.18.111.228", port: "27017"},
     prod: {server: "", port: ""},
     redis_local:{server:'127.0.0.1',port:'6379',pwd:'smartCost'},
     redis_qa:{server:'192.168.1.184',port:'6379',pwd:'smartCost'},
@@ -21,6 +22,7 @@ module.exports = {
                  "socketOptions": {
                  "connectTimeoutMS": 20000
                 }
-            }
+            }/*,
+           'useMongoClient': true*/ //报 DeprecationWarning: `open()` is deprecated in mongoose这个错时可以用这句配置
         }
 }

+ 106 - 4
config/gulpConfig.js

@@ -7,7 +7,7 @@ module.exports = {
     common_jspaths:[
         'lib/jquery/jquery-3.2.1.min.js',
         'lib/popper/popper.min.js',
-        'lib/jquery-ui/jquery-ui.min.js',
+        'lib/jquery-ui/jquery-0i.min.js',
         'lib/bootstrap/bootstrap.min.js',
         'web/building_saas/js/*.js',
         'public/web/scMathUtil.js',
@@ -27,6 +27,7 @@ module.exports = {
         'public/web/date_util.js',
         'public/web/tree_table/tree_table.js',
         'public/web/common_ajax.js',
+        'lib/JSExpressionEval_src/Date.js',
         'web/building_saas/pm/js/**/*.js',
         'lib/ztree/*.js'
     ],
@@ -40,6 +41,7 @@ module.exports = {
     main_jspaths:[
         'lib/JSExpressionEval_src/*.js',
         '!lib/JSExpressionEval_src/JsHashMap.js',
+        'lib/jquery-ui/jquery-ui.min.js',
         'lib/jquery-ui/jquery-ui-datepickerCN.js',
         'lib/jquery-contextmenu/*.js',
         'lib/lodash/lodash.js',
@@ -77,13 +79,15 @@ module.exports = {
         'web/building_saas/main/js/models/ration_glj.js',
         'web/building_saas/main/js/models/ration_coe.js',
         'web/building_saas/main/js/models/ration_ass.js',
+        'web/building_saas/main/js/models/ration_installation.js',
         // 'web/building_saas/main/js/models/volume_price.js',
         'web/building_saas/main/js/models/labour_coe.js',
+        'web/building_saas/main/js/models/installation_fee.js',
         'public/web/id_tree.js',
         'web/building_saas/main/js/models/cache_tree.js',
         'web/building_saas/main/js/calc/calc_fees.js',
-        'web/building_saas/main/js/calc/ration_calc.js',
-        'web/building_saas/main/js/calc/bills_calc.js',
+        // 'web/building_saas/main/js/calc/ration_calc.js',
+        // 'web/building_saas/main/js/calc/bills_calc.js',
         // 'public/calc_util.js',
         'public/web/tree_sheet/tree_sheet_controller.js',
         'public/web/tree_sheet/tree_sheet_helper.js',
@@ -107,6 +111,8 @@ module.exports = {
         'web/building_saas/main/js/views/glj_view_contextMenu.js',
         'web/building_saas/main/js/views/calc_program_view.js',
         'web/building_saas/main/js/views/confirm_modal.js',
+        'web/building_saas/main/js/views/zlfb_view.js',
+        'web/building_saas/main/js/views/installation_fee_view.js',
         'public/web/rpt_tpl_def.js',
         'public/web/treeDataHelper.js',
         'public/web/ztree_common.js',
@@ -120,7 +126,103 @@ module.exports = {
         'web/building_saas/main/js/views/fee_rate_view.js',
         'web/building_saas/main/js/views/sub_fee_rate_views.js',
         'web/building_saas/main/js/views/calc_base_view.js',
-        'web/building_saas/main/js/views/project_property_labour_coe_view.js'
+        'web/building_saas/main/js/views/project_property_labour_coe_view.js',
+        'web/building_saas/complementary_ration_lib/js/main.js',
+        'public/web/storageUtil.js'
+    ],
+    compleGlj_css: [
+        'lib/jquery-contextmenu/jquery.contextMenu.css',
+        'lib/ztree/css/zTreeStyle.css',
+        'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css'
+    ],
+    compleGlj_jspaths: [
+        'public/web/common_ajax.js',
+        'public/web/treeDataHelper.js',
+        'public/web/QueryParam.js',
+        'public/web/id_tree.js',
+        'public/web/tree_sheet/tree_sheet_controller.js',
+        'public/web/tree_sheet/tree_sheet_helper.js',
+        'web/building_saas/complementary_glj_lib/js/glj.js',
+        'web/building_saas/complementary_glj_lib/js/gljClassTree.js',
+        'web/building_saas/complementary_glj_lib/js/gljComponent.js',
+        'web/building_saas/complementary_glj_lib/js/components.js',
+        'public/web/ztree_common.js',
+        'public/web/sheet/sheet_common.js',
+        'web/building_saas/complementary_glj_lib/js/sheetOpr.js',
+        'public/web/storageUtil.js'
+    ],
+    compleRation_ration_css: [
+        'lib/jquery-contextmenu/jquery.contextMenu.css',
+        'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css'
+    ],
+    compleRation_ration_jspaths:[
+        'web/building_saas/complementary_ration_lib/js/global.js',
+        'public/web/id_tree.js',
+        'public/web/tree_sheet/tree_sheet_controller.js',
+        'public/web/tree_sheet/tree_sheet_helper.js',
+        'public/web/treeDataHelper.js',
+        'public/web/sheet/sheet_common.js',
+        'web/building_saas/complementary_ration_lib/js/sheetsOpr.js',
+        'public/web/QueryParam.js',
+        'public/web/storageUtil.js',
+        'web/building_saas/complementary_ration_lib/js/section_tree.js',
+        'web/building_saas/complementary_ration_lib/js/explanatory.js',
+        'web/building_saas/complementary_ration_lib/js/jobContent.js',
+        'web/building_saas/complementary_ration_lib/js/annotation.js',
+        'public/web/scMathUtil.js',
+        'public/web/common_ajax.js',
+        'public/web/ztree_common.js',
+        'web/building_saas/complementary_ration_lib/js/rationUnits.js',
+        'web/building_saas/complementary_ration_lib/js/ration.js',
+        'web/building_saas/complementary_ration_lib/js/ration_glj.js',
+        'web/building_saas/complementary_ration_lib/js/ration_coe.js',
+        'web/building_saas/complementary_ration_lib/js/ration_assist.js',
+        'web/building_saas/complementary_ration_lib/js/ration_installation.js.js'
+    ],
+    compleRation_glj_css: [
+        'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css'
+    ],
+    compleRation_glj_jspaths: [
+        'web/building_saas/complementary_ration_lib/js/global.js',
+        'public/web/common_ajax.js',
+        'public/web/treeDataHelper.js',
+        'public/web/QueryParam.js',
+        'web/building_saas/complementary_ration_lib/js/repository_glj.js',
+        'public/web/ztree_common.js',
+        'public/web/sheet/sheet_common.js',
+        'web/building_saas/complementary_ration_lib/js/sheetsOpr.js',
+        'public/web/storageUtil.js'
+    ],
+    compleRation_coe_css: [
+        'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css'
+    ],
+    compleRation_coe_jspaths: [
+        'web/building_saas/complementary_ration_lib/js/global.js',
+        'public/web/common_ajax.js',
+        'public/web/treeDataHelper.js',
+        'public/web/QueryParam.js',
+        'public/web/common_util.js',
+        'public/web/sheet/sheet_common.js',
+        'web/building_saas/complementary_ration_lib/js/sheetsOpr.js',
+        'public/web/storageUtil.js',
+        'web/building_saas/complementary_ration_lib/js/coe.js'
+    ],
+    compleRation_inst_css: [
+        'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css',
+        'lib/jquery-contextmenu/jquery.contextMenu.css'
+    ],
+    compleRation_inst_jspaths: [
+        'public/web/QueryParam.js',
+        'public/web/uuid.js',
+        'public/web/common_util.js',
+        'public/web/storageUtil.js',
+        'public/web/id_tree.js',
+        'public/web/tree_sheet/tree_sheet_controller.js',
+        'public/web/tree_sheet/tree_sheet_helper.js',
+        'public/web/sheet/sheet_common.js',
+        'public/web/common_ajax.js',
+        'web/building_saas/complementary_ration_lib/js/global.js',
+        'web/building_saas/complementary_ration_lib/js/installation.js'
     ]
 }
 

+ 176 - 1
gulpfile.js

@@ -16,6 +16,16 @@ let pm_csspaths=config.pm_css;
 let login_jspaths=config.login_jspaths;
 let main_jspaths=config.main_jspaths;
 let main_csspaths=config.main_css;
+let compleGlj_jspaths = config.compleGlj_jspaths;
+let compleGlj_csspaths = config.compleGlj_css;
+let compleRation_ration_jspaths = config.compleRation_ration_jspaths;
+let compleRation_ration_csspaths = config.compleRation_ration_css;
+let compleRation_glj_jspaths = config.compleRation_glj_jspaths;
+let compleRation_glj_csspaths = config.compleRation_glj_css;
+let compleRation_coe_jspaths = config.compleRation_coe_jspaths;
+let compleRation_coe_csspaths = config.compleRation_coe_css;
+let compleRation_inst_csspaths = config.compleRation_inst_css;
+let compleRation_inst_jspaths = config.compleRation_inst_jspaths;
 let version=config.version;
 let cssDest='web/dest/css';
 let scriptsDest='web/dest/scripts';
@@ -77,6 +87,90 @@ let mainOptions={
         'web/dest/css/main.all.min.'+version+'.css']
 }
 
+let compleGljOptions = {
+    version: version,
+    scriptsDest: 'web/dest/scripts',
+    jspaths: compleGlj_jspaths,
+    csspaths: compleGlj_csspaths,
+    concatName: 'compleGlj.all.min',
+    srcHtml: 'web/src/html/complementary_glj_lib/tools-gongliaoji.html',
+    htmlDest: 'web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html',
+    htmlName: 'tools-gongliaoji.html',
+    injectList: [
+        'web/dest/scripts/compleGlj.all.min' + version + '.js',
+        'web/dest/css/compleGlj.all.min' + version + '.css',
+        'web/dest/css/common.all.min.' + version + '.css'
+    ]
+};
+
+let compleRation_rationOptions = {
+    version: version,
+    scriptsDest: 'web/dest/scripts',
+    jspaths: compleRation_ration_jspaths,
+    csspaths: compleRation_ration_csspaths,
+    concatName: 'compleRation_ration.all.min',
+    srcHtml: 'web/src/html/complementary_ration_lib/dinge.html',
+    htmlDest: 'web/building_saas/complementary_ration_lib/html/dinge.html',
+    htmlName: 'dinge.html',
+    injectList: [
+        'web/dest/scripts/compleRation_ration.all.min' + version + '.js',
+        'web/dest/css/compleRation_ration.all.min' + version + '.css',
+        'web/dest/scripts/common.all.min.'+version+'.js',
+        'web/dest/css/common.all.min.' + version + '.css'
+    ]
+};
+
+let compleRation_gljOptions = {
+    version: version,
+    scriptsDest: 'web/dest/scripts',
+    jspaths: compleRation_glj_jspaths,
+    csspaths: compleRation_glj_csspaths,
+    concatName: 'compleRation_glj.all.min',
+    srcHtml: 'web/src/html/complementary_ration_lib/gongliao.html',
+    htmlDest: 'web/building_saas/complementary_ration_lib/html/gongliao.html',
+    htmlName: 'gongliao.html',
+    injectList: [
+        'web/dest/scripts/compleRation_glj.all.min' + version + '.js',
+        'web/dest/scripts/compleRation_glj.all.min' + version + '.css',
+        'web/dest/scripts/common.all.min.'+version+'.js',
+        'web/dest/css/common.all.min.' + version + '.css'
+    ]
+};
+
+let compleRation_coeOptions = {
+    version: version,
+    scriptsDest: 'web/dest/scripts',
+    jspaths: compleRation_coe_jspaths,
+    csspaths: compleRation_coe_csspaths,
+    concatName: 'compleRation_coe.all.min',
+    srcHtml: 'web/src/html/complementary_ration_lib/fuzhu.html',
+    htmlDest: 'web/building_saas/complementary_ration_lib/html/fuzhu.html',
+    htmlName: 'fuzhu.html',
+    injectList: [
+        'web/dest/scripts/compleRation_coe.all.min' + version + '.js',
+        'web/dest/scripts/compleRation_coe.all.min' + version + '.css',
+        'web/dest/scripts/common.all.min.'+version+'.js',
+        'web/dest/css/common.all.min.' + version + '.css'
+    ]
+};
+
+let compleRation_instOptions = {
+    version: version,
+    scriptsDest: 'web/dest/scripts',
+    jspaths: compleRation_inst_jspaths,
+    csspaths: compleRation_inst_csspaths,
+    concatName: 'compleRation_inst.all.min',
+    srcHtml: 'web/src/html/complementary_ration_lib/anzhuang.html',
+    htmlDest: 'web/building_saas/complementary_ration_lib/html/anzhuang.html',
+    htmlName: 'anzhuang.html',
+    injectList: [
+        'web/dest/scripts/compleRation_inst.all.min' + version + '.js',
+        'web/dest/scripts/compleRation_inst.all.min' + version + '.css',
+        'web/dest/scripts/common.all.min.'+version+'.js',
+        'web/dest/css/common.all.min.' + version + '.css'
+    ]
+}
+
 function minify(options) {
     if(options.jspaths){
         return gulp.src(options.jspaths)
@@ -190,5 +284,86 @@ gulp.task('main',['main_inject'], function (){
     return htmlmin(mainOptions);
 });
 
+gulp.task('compleGlj_minify', ['common'], function () {
+    return minify(compleGljOptions);
+});
+
+gulp.task('compleGlj_css', function () {
+    return css(compleGljOptions);
+});
+
+gulp.task('compleGlj_inject', ['compleGlj_minify', 'compleGlj_css'], function () {
+    return inject(compleGljOptions);
+});
+
+gulp.task('compleGlj', ['compleGlj_inject'], function () {
+    return htmlmin(compleGljOptions);
+});
+
+gulp.task('compleRation_ration_minify', ['common'], function () {
+    return minify(compleRation_rationOptions);
+});
+
+gulp.task('compleRation_ration_css', function () {
+    return css(compleRation_rationOptions);
+});
+
+gulp.task('compleRation_ration_inject', ['compleRation_ration_minify', 'compleRation_ration_css'], function () {
+    return inject(compleRation_rationOptions);
+});
+
+gulp.task('compleRation_ration', ['compleRation_ration_inject'], function () {
+    return htmlmin(compleRation_rationOptions);
+});
+
+gulp.task('compleRation_glj_minify', ['common'], function () {
+    return minify(compleRation_gljOptions);
+});
+
+gulp.task('compleRation_glj_css', function () {
+    return css(compleRation_gljOptions);
+});
+
+gulp.task('compleRation_glj_inject', ['compleRation_glj_minify', 'compleRation_glj_css'], function () {
+    return inject(compleRation_gljOptions);
+});
+
+gulp.task('compleRation_glj', ['compleRation_glj_inject'], function () {
+    return htmlmin(compleRation_gljOptions);
+});
+
+gulp.task('compleRation_coe_minify', ['common'], function () {
+    return minify(compleRation_coeOptions);
+});
+
+gulp.task('compleRation_coe_css', function () {
+    return css(compleRation_coeOptions);
+});
+
+gulp.task('compleRation_coe_inject', ['compleRation_coe_minify', 'compleRation_coe_css'], function () {
+    return inject(compleRation_coeOptions);
+});
+
+gulp.task('compleRation_coe', ['compleRation_coe_inject'], function () {
+    return htmlmin(compleRation_coeOptions);
+});
+
+gulp.task('compleRation_inst_minify', ['common'], function () {
+    return minify(compleRation_instOptions);
+});
+
+gulp.task('compleRation_inst_css', function () {
+    return css(compleRation_instOptions);
+});
+
+gulp.task('compleRation_inst_inject', ['compleRation_inst_minify', 'compleRation_inst_css'], function () {
+    return inject(compleRation_instOptions);
+});
+
+gulp.task('compleRation_inst', ['compleRation_inst_inject'], function () {
+    return htmlmin(compleRation_instOptions);
+});
+
+
 
-gulp.task('build',['header','login','pm','main']);
+gulp.task('build',['header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst']);

+ 3 - 3
modules/bills_lib/models/bills_lib_schemas.js

@@ -37,9 +37,9 @@ let itemsSchema = mongoose.Schema({
     {versionKey: false});
 
 let billsSchema = mongoose.Schema({
-        ID: Number,
-        ParentID: Number,
-        NextSiblingID: Number,
+        ID: String,
+        ParentID: String,
+        NextSiblingID: String,
         code: String,
         name: String,
         unit: String,

+ 45 - 0
modules/common/const/bills_fixed.js

@@ -0,0 +1,45 @@
+/**
+ * Created by Zhong on 2017/12/18.
+ */
+const fixedFlag = {
+    // 分部分项工程
+    SUB_ENGINERRING: 1,
+    // 措施项目
+    MEASURE: 2,
+    // 施工技术措施项目
+    CONSTRUCTION_TECH: 3,
+    // 安全文明施工按实计算费用
+    SAFETY_CONSTRUCTION_ACTUAL: 4,
+    // 施工组织措施专项费用
+    CONSTRUCTION_ORGANIZATION: 5,
+    // 安全文明施工专项费用
+    SAFETY_CONSTRUCTION: 6,
+    // 其他项目
+    OTHER: 7,
+    // 暂列金额
+    PROVISIONAL: 8,
+    // 暂估价
+    ESTIMATE: 9,
+    // 材料(工程设备)暂估价
+    MATERIAL_PROVISIONAL: 10,
+    // 专业工程暂估价
+    ENGINEERING_ESITIMATE: 11,
+    // 计日工
+    DAYWORK: 12,
+    // 总承包服务费
+    TURN_KEY_CONTRACT: 13,
+    // 索赔与现场签证
+    CLAIM_VISA: 14,
+    // 规费
+    CHARGE: 15,
+    // 社会保险费及住房公积金 Social insurance fee and housing accumulation fund
+    SOCIAL_INSURANCE_HOUSING_FUND: 16,
+    // 工程排污费 charges for disposing pollutants
+    POLLUTANTS: 17,
+    // 税金
+    TAX: 18,
+    //工程造价
+    ENGINEERINGCOST: 19
+};
+
+export default fixedFlag;

+ 0 - 1
modules/complementary_glj_lib/controllers/gljController.js

@@ -13,7 +13,6 @@ let callback = function(req, res, err, message, data){
 
 class GljController extends BaseController{
     async redirectGlj(req, res){
-        console.log(req.s)
         let gljLibId = null, engineeringId, sessionCompilation = req.session.sessionCompilation,
             rationValuation = sessionCompilation.ration_valuation,
             billValuation = sessionCompilation.bill_valuation,

+ 67 - 4
modules/complementary_glj_lib/models/gljModel.js

@@ -58,12 +58,34 @@ class GljDao {
         }
     }
     //获得用户的补充工料机和用户当前所在编办的标准工料机
-    getGljItems (stdGljLibId, userId, compilationId, callback){
+    async getGljItems (stdGljLibId, userId, compilationId, callback){
         let me = this;
         let rst = {stdGljs: [], complementaryGljs: []};
+        //批量获取异步
+       /* let functions = [];
+        let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
+        let findCount = Math.ceil(count/500);
+        for(let i = 0, len = findCount; i < len; i++){
+            functions.push((function(flag) {
+                return function (cb) {
+                    stdGljModel.find({repositoryId: stdGljLibId, deleted: null}, cb).skip(flag).sort({ID: 1}).limit(500);
+                }
+            })(i*500));
+        }
+        async.parallel(functions,  function (err, results) {
+            if(err){
+                callback(err, null);
+            }
+            else{
+                for(let stdGljs of results){
+                    rst.stdGljs = rst.stdGljs.concat(stdGljs);
+                }
+                callback(0, rst);
+            }
+        });*/
         async.parallel([
-            function (cb) {
-                stdGljModel.find({repositoryId: stdGljLibId}, function (err, stdGljs) {
+           async function (cb) {
+               /* stdGljModel.find({repositoryId: stdGljLibId}, function (err, stdGljs) {
                     if(err){
                         cb(err);
                     }
@@ -72,7 +94,48 @@ class GljDao {
                         rst.stdGljs = stdGljs;
                         cb(null);
                     }
-                });
+                });*/
+               try{
+                   let stdGljs = [];
+                   let first = await stdGljModel.find({repositoryId: stdGljLibId}).sort({ID: 1}).limit(1);
+                   let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
+                   let findCount = Math.ceil(count/500);
+                   let flag = first[0].ID;
+                   //let flag = 0;
+                   //批量获取,非skip
+                   for(let i = 0, len = findCount; i < len; i++){
+                       let tempStdGlj;
+                       if(i === 0){
+                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gte: flag}}).sort({ID: 1}).limit(500);
+                           if(tempStdGlj.length > 0){
+                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
+                           }
+                       }
+                       else {
+                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gt: flag}}).sort({ID: 1}).limit(500);
+                           if(tempStdGlj.length > 0){
+                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
+                           }
+                       }
+                       if(tempStdGlj){
+                           stdGljs = stdGljs.concat(tempStdGlj);
+                       }
+                   }
+               /*    for(let i = 0, len = findCount; i < len; i++){
+                       let tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null}).skip(flag).sort({ID: 1}).limit(500);
+                       if(tempStdGlj.length > 0){
+                           flag = flag + 500;
+                           stdGljs = stdGljs.concat(tempStdGlj);
+                       }
+                   }*/
+                   me.sortToNumber(stdGljs);
+                   rst.stdGljs = stdGljs;
+                   cb(null);
+               }
+               catch (err){
+                   cb(err);
+               }
+
             },
             function (cb) {
                 complementaryGljModel.find({userId: userId, compilationId: compilationId}, function (err, complementaryGljs) {

+ 4 - 1
modules/complementary_glj_lib/routes/routes.js

@@ -3,10 +3,12 @@
  */
 
 import express from "express";
-import GljController from "../controllers/gljController"
+import GljController from "../controllers/gljController";
+import CompleRationController from '../../complementary_ration_lib/controllers/compleRationController';
 
 let router = express.Router();
 let gljController = new GljController();
+let compleRationController = new CompleRationController();
 
 module.exports = function (app) {
     app.get('/complementaryGlj', gljController.init, gljController.redirectGlj);
@@ -21,6 +23,7 @@ module.exports = function (app) {
     router.post("/getGljItems", gljController.init, gljController.getGljItems);
     router.post("/updateComponent", gljController.init, gljController.updateComponent);
     router.post("/mixUpdateGljItems", gljController.init, gljController.mixUpdateGljItems);
+    router.post("/updateRationBasePrc",compleRationController.init, compleRationController.updateRationBasePrc);//更新定额单价
     //router.post("/getGljItemsByIds", gljController.init, gljController.getGljItemsByIds);
     //router.post("/getGljItemsByCodes", gljController.init, gljController.getGljItemsByCodes);
 

+ 117 - 0
modules/complementary_ration_lib/controllers/compleRationController.js

@@ -0,0 +1,117 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import BaseController from '../../common/base/base_controller';
+import CompleRationDao from '../models/compleRationModel';
+
+let compleRationDao = new CompleRationDao();
+let coeListDAO = require('../../ration_repository/models/coe');
+let callback = function (req, res, err, msg, data) {
+    res.json({error: err, message: msg, data: data})
+}
+
+class CompleRationController extends BaseController{
+    getRationItems(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getRationItems(req.session.sessionUser.ssoId, data.rationRepId, data.sectionId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    updateRations(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.updateRation(req.session.sessionUser.ssoId, req.session.sessionCompilation._id, data.updateData, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    //更新补充定额价格
+    updateRationBasePrc(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.updateRationBasePrc(req.session.sessionUser.ssoId, data.basePrcArr, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    mixUpdateRationItems(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.mixUpdateRationItems(req.session.sessionUser.ssoId, req.session.sessionCompilation._id,
+            data.rationRepId, data.sectionId, data.updateItems, data.addItems, data.removeIds, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    //获取所有定额的编号
+    getRationsCodes(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getRationsCodes(req.session.sessionUser.ssoId, data.rationRepId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getGljItems(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getGljItems(data.gljLibId, function (err, data) {
+            callback(req, res, err, '', data);
+        })
+    }
+
+    getGljItemsOccupied(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getGljItemsOccupied(data.gljLibId, data.occupation, function (err, data) {
+            callback(req, res, err, '', data);
+        })
+    }
+
+    getGljItemsByIds(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getGljItemsByIds(req.session.sessionUser.ssoId, data.ids, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getGljItemsByCodes(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getGljItemsByCodes(req.session.sessionUser.ssoId, req.session.sessionCompilation._id, data.rationRepId, data.gljCodes, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getRationGljItemsBySection(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getRationGljItemsBySection(req.session.sessionUser.ssoId, data.sectionId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getCoeList(req, res){
+        let data = JSON.parse(req.body.data);
+        coeListDAO.getCoesByLibID(data.libID, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getCoeItemsByIDs(req, res){
+        let data = JSON.parse(req.body.data);
+        coeListDAO.getCoeItemsByIDs(data, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getCoeItemsByNos(req, res){
+        let data = JSON.parse(req.body.data);
+        coeListDAO.getCoeItemsByNos(data, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getInstallation(req, res){
+        let data = JSON.parse(req.body.data);
+        compleRationDao.getInstallation(data.rationRepId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+}
+
+export default CompleRationController;

+ 36 - 0
modules/complementary_ration_lib/controllers/compleSectionTreeController.js

@@ -0,0 +1,36 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import BaseController from '../../common/base/base_controller';
+import SectionTreeDao from '../models/sectionTreeModel';
+
+let sectionTreeDao = new SectionTreeDao();
+let callback = function (req, res, err, msg, data) {
+    res.json({error: err, message: msg, data: data});
+};
+
+class CompleSectionTreeController extends BaseController{
+    //保证章节树ID唯一,标准树与补充树才可进行拼接
+    getNewTreeID(req, res){
+        sectionTreeDao.getNewTreeID(function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getRationTree(req, res){
+        let data = JSON.parse(req.body.data);
+        sectionTreeDao.getRationTree(req.session.sessionUser.ssoId, data.rationRepId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    updateRationSection(req, res){
+        let data = JSON.parse(req.body.data);
+        sectionTreeDao.updateSection(req.session.sessionUser.ssoId, req.session.sessionCompilation._id, data.updateData, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+}
+
+export default CompleSectionTreeController;

+ 60 - 0
modules/complementary_ration_lib/controllers/compleViewController.js

@@ -0,0 +1,60 @@
+/**
+ * Created by Zhong on 2017/12/22.
+ */
+
+import BaseController from '../../common/base/base_controller';
+import CompleViewModel from '../models/compleViewModel';
+
+let compleViewModel = new CompleViewModel();
+let callback = function (req, res, err, msg, data) {
+    res.json({error: err, message: msg, data: data});
+};
+
+class CompleViewController extends BaseController{
+    redirectMain(req, res){
+        res.render('building_saas/complementary_ration_lib/html/main.html', {
+            userID: req.session.sessionUser.ssoId,
+            compilationId: req.session.sessionCompilation._id
+        });
+    }
+
+    redirectRation(req, res){
+        res.render('building_saas/complementary_ration_lib/html/dinge.html', {
+            userID: req.session.sessionUser.ssoId
+        });
+    }
+
+    redirectGljList(req, res){
+        res.render('building_saas/complementary_ration_lib/html/gongliao.html', {
+            userID: req.session.sessionUser.ssoId
+        });
+    }
+
+    redirectCoeList(req, res){
+        res.render('building_saas/complementary_ration_lib/html/fuzhu.html', {
+            userID: req.session.sessionUser.ssoId
+        });
+    }
+
+    redirectInstallation(req, res){
+        res.render('building_saas/complementary_ration_lib/html/anzhuang.html', {
+            userID: req.session.sessionUser.ssoId
+        });
+    }
+
+    getRationLib(req, res){
+        let data = JSON.parse(req.body.data);
+        compleViewModel.getRationLib(data.rationRepId, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    getRationLibs(req, res){
+        let data = JSON.parse(req.body.data);
+        compleViewModel.getRationLibs(req.session.sessionCompilation._id, data.ids, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+}
+
+export default CompleViewController;

+ 29 - 0
modules/complementary_ration_lib/controllers/searchController.js

@@ -0,0 +1,29 @@
+/**
+ * Created by Zhong on 2018/1/9.
+ */
+
+import BaseController from '../../common/base/base_controller';
+import SearchDao from '../models/searchModel';
+
+let searchDao = new SearchDao();
+let callback = function (req, res, err, message, data) {
+    res.json({error: err, message: message, data: data});
+};
+
+class SearchController extends BaseController{
+    getRationItem(req, res){
+        let data = JSON.parse(req.body.data);
+        searchDao.getRationItem(req.session.sessionUser.ssoId, data.rationRepId, data.code, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+
+    findRation(req, res){
+        let data = JSON.parse(req.body.data);
+        searchDao.findRation(req.session.sessionUser.ssoId, data.rationRepId, data.keyword, function (err, data) {
+            callback(req, res, err, '', data);
+        });
+    }
+}
+
+export default SearchController;

+ 459 - 0
modules/complementary_ration_lib/models/compleRationModel.js

@@ -0,0 +1,459 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import {compleRationModel, installSectionModel, installFeeItemModel} from './schemas';
+import {complementaryGljModel, stdGljModel} from '../../complementary_glj_lib/models/schemas';
+import async from 'async';
+let stdRationModel = require ('../../ration_repository/models/ration_item').Model;
+let counter = require('../../../public/counter/counter');
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
+
+class CompleRatoinDao {
+    async updateRation(userID, compilationId, updateData, callback){
+        try{
+            for(let i = 0, len = updateData.length; i < len; i++){
+                let updateObj = updateData[i];
+                if(updateObj.updateType === 'new'){
+                    updateObj.updateData.userID = userID;
+                    updateObj.updateData.compilationId = compilationId;
+                    await compleRationModel.create(updateObj.updateData);
+                }
+                else if(updateObj.updateType === 'update'){
+                    await compleRationModel.update({userID: userID, rationRepId: updateObj.updateData.rationRepId}, updateObj.updateData);
+                }
+            }
+            callback(0, '');
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    async getRationItems(userID, rationRepId, sectionId, callback){
+        try{
+            let stdRations = await stdRationModel.find({rationRepId: rationRepId, sectionId: sectionId, $or: [{isDeleted: null}, {isDeleted: false}]});
+            //mark std
+            for(let i = 0, len = stdRations.length; i < len; i++){
+                stdRations[i]._doc.type = 'std';
+            }
+            let compleRations = await compleRationModel.find({userId: userID, rationRepId: rationRepId, sectionId: sectionId, deleteInfo: null});
+            //mark complementary
+            for(let i = 0, len = compleRations.length; i < len; i++){
+                compleRations[i]._doc.type = 'complementary';
+            }
+            callback(0, stdRations.concat(compleRations));
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    async getRationsCodes(userID, rationRepId, callback){
+        try{
+            let stdRationCodes = await stdRationModel.find({rationRepId: rationRepId, $or: [{isDeleted: null}, {isDeleted: false}]}, '-_id code');
+            let compleRationCodes = await compleRationModel.find({userId: userID, rationRepId: rationRepId, deleteInfo: null}, '-_id code');
+            let rstCodes = [];
+            stdRationCodes.concat(compleRationCodes).forEach(function (rationItem) {
+                rstCodes.push(rationItem.code);
+            });
+            callback(0, rstCodes);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    async getGljItems(gljLibId, callback){
+        try{
+            let stdGljs = await stdGljModel.find({repositoryId: gljLibId, $or: [{deleted: null}, {deleted: false}]});
+            callback(0, stdGljs);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    async getGljItemsOccupied(gljLibId, occupation, callback){
+        try{
+            let stdGls = await stdGljModel.find({repositoryId: gljLibId, $or: [{deleted: null}, {deleted: false}]}, occupation);
+            callback(0, stdGls);
+        }
+        catch (err){
+            callback(err, null);
+        }
+    }
+
+
+    async getGljItemsByIds(userID, ids, callback){
+        try{
+            let rst = [];
+            for(let i = 0, len = ids.length; i < len; i++){
+                if(ids[i].type === 'std'){
+                    let stdGlj = await stdGljModel.find({ID: ids[i].id, deleteInfo: null});
+                    if(stdGlj.length > 0){
+                        stdGlj[0]._doc.type = 'std';
+                        rst.push(stdGlj[0]);
+                    }
+                }
+                else if(ids[i].type === 'complementary'){
+                    let compleGlj = await complementaryGljModel.find({userId: userID, ID: ids[i].id, deleteInfo: null});
+                    if(compleGlj.length > 0){
+                        compleGlj[0]._doc.type = 'complementary';
+                        rst.push(compleGlj[0]);
+                    }
+                }
+            }
+            callback(0, rst);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    async getGljItemsByCodes(userID, compilationId, rationRepId, codes, callback){
+        try{
+            let rst = [];
+            for(let i = 0, len = codes.length; i < len; i++){
+                let stdGlj = await stdGljModel.find({repositoryId: rationRepId, code: codes[i]});
+                if(stdGlj.length > 0){
+                    stdGlj[0]._doc.type = 'std';
+                    rst.push(stdGlj[0]);
+                }
+                else {
+                    let compleGlj = await complementaryGljModel.find({userId: userID, compilationId: compilationId, code: codes[i]});
+                    if(compleGlj.length > 0){
+                        compleGlj[0]._doc.type = 'complementary';
+                        rst.push(compleGlj[0]);
+                    }
+                }
+            }
+            callback(0, rst);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+    //造价书定额库
+    async getRationGljItemsBySection(userId, sectionId, callback){
+        try{
+            let stdRations = await stdRationModel.find({sectionId: sectionId, $or: [{isDeleted: null}, {isDeleted: false}]});
+            for(let ration of stdRations){
+                ration._doc.type = 'std';
+            }
+            let compleRations = await compleRationModel.find({userId: userId, sectionId: sectionId, deleteInfo: null});
+            for(let ration of compleRations){
+                ration._doc.type = 'complementary';
+            }
+            let rations = stdRations.concat(compleRations);
+            rations.sort(function (a, b) {
+                let rst = 0;
+                if(a.code > b.code){
+                    rst = 1;
+                }
+                else if(a.code < b.code){
+                    rst = -1;
+                }
+                return rst;
+            });
+            for(let ration of rations){
+                let hint = '';
+                for(let rationGlj of ration.rationGljList){
+                    let glj;
+                    if(!isDef(rationGlj.type) || rationGlj.type === 'std'){
+                         glj = await stdGljModel.findOne({ID: rationGlj.gljId});
+                    }
+                    else {
+                         glj = await complementaryGljModel.findOne({uesrId: userId, ID: rationGlj.gljId});
+                    }
+                    if(isDef(glj)){
+                        let unitHint = '' + glj.code + ' ' + glj.name + ' ' + glj.unit + ' ' + rationGlj.consumeAmt + '</br>';
+                        hint += unitHint;
+                    }
+                }
+                ration._doc.hint = hint;
+            }
+            callback(0, rations);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+
+    updateRationBasePrc(userID, basePrcArr, callback){
+        let me  = this;
+        async.each(basePrcArr, function (basePrcObj, finalCb) {
+            let adjGljId = basePrcObj.gljId, adjBasePrice = basePrcObj.basePrice, adjGljType = basePrcObj.gljType;
+            async.waterfall([
+                function (cb) {
+                    if(typeof basePrcObj.delete !== 'undefined' && basePrcObj.delete === 1){
+                        //补充定额
+                        compleRationModel.find({'rationGljList.gljId': adjGljId},{ID: 1, rationGljList: 1}, function (err, compleRst) {
+                            if(err){
+                                cb(err);
+                            }
+                            else {
+                                compleRationModel.update({'rationGljList.gljId': adjGljId}, {$pull: {rationGljList: {gljId: adjGljId}}}, {multi: true}, function (err) {
+                                    if(err){
+                                        cb(err);
+                                    }
+                                    else {
+                                        cb(null, compleRst);
+                                    }
+                                });
+                            }
+                        });
+
+                    }
+                    else{
+                        compleRationModel.find({'rationGljList.gljId': adjGljId}, function (err, compleRst) {
+                            if(err){
+                                cb(err);
+                            }
+                            else {
+                                cb(null, compleRst);
+                            }
+                        });
+                    }
+                },
+                function (result, cb) {
+                    async.each(result, function (rationItem, ecb) {
+                        let rationGljList = rationItem.rationGljList,
+                            gljIds = [];
+                        rationGljList.forEach(function (rationGlj) {
+                            let idObj = Object.create(null);
+                            idObj.id = rationGlj.gljId;
+                            idObj.type = rationGlj.type;
+                            gljIds.push(idObj);
+                        });
+                        me.getGljItemsByIds(userID, gljIds, function(err, gljItems){
+                            if(err){
+                                ecb(err);
+                            }
+                            else{
+                                let gljArr = [];
+                                for(let i=0; i<gljItems.length; i++){
+                                    let gljParentType = -1;
+                                    if(gljItems[i].ID === adjGljId){
+                                        gljItems[i].gljType = adjGljType;
+                                    }
+                                    if(gljItems[i].gljType <= 3){
+                                        gljParentType = gljItems[i].gljType;
+                                    }
+                                    if(gljItems[i].gljType > 200 && gljItems[i].gljType < 300){
+                                        gljParentType = 2;
+                                    }
+                                    if(gljItems[i].gljType > 300 && gljItems[i].gljType < 400){
+                                        gljParentType = 3;
+                                    }
+                                    if(gljItems[i].ID === adjGljId){
+                                        gljArr.push({gljId: gljItems[i].ID, basePrice: adjBasePrice, gljParentType: gljParentType});
+                                    }
+                                    else {
+                                        gljArr.push({gljId: gljItems[i].ID, basePrice: parseFloat(gljItems[i].basePrice), gljParentType: gljParentType});
+                                    }
+                                }
+                                gljArr.forEach(function (gljItem) {
+                                    rationGljList.forEach(function (rationGlj) {
+                                        if(gljItem.gljId === rationGlj.gljId){
+                                            gljItem.consumeAmt = parseFloat(rationGlj.consumeAmt);
+                                        }
+                                    })
+                                });
+                                //recalculate the price of ration
+                                let labourPrc = [], materialPrc = [], machinePrc = [], singlePrc, updatePrc = {labourPrice: 0, materialPrice: 0, machinePrice: 0, basePrice: 0};
+                                gljArr.forEach(function (gljItem) {
+                                    if(gljItem.gljParentType !== -1){
+                                        singlePrc = scMathUtil.roundTo(gljItem.basePrice * gljItem.consumeAmt, -3);
+                                        if(gljItem.gljParentType === 1){
+                                            labourPrc.push(singlePrc);
+                                        }
+                                        else if(gljItem.gljParentType ===2){
+                                            materialPrc.push(singlePrc);
+                                        }
+                                        else{
+                                            machinePrc.push(singlePrc);
+                                        }
+                                    }
+                                });
+                                if(labourPrc.length > 0){
+                                    let sumLaP = 0;
+                                    for(let i=0; i<labourPrc.length; i++){
+                                        sumLaP += labourPrc[i];
+                                    }
+                                    updatePrc.labourPrice = scMathUtil.roundTo(sumLaP, -2);
+                                }
+                                if(materialPrc.length > 0){
+                                    let sumMtP = 0;
+                                    for(let i= 0; i<materialPrc.length; i++){
+                                        sumMtP += materialPrc[i];
+                                    }
+                                    updatePrc.materialPrice = scMathUtil.roundTo(sumMtP, -2);
+                                }
+                                if(machinePrc.length > 0){
+                                    let sumMaP = 0;
+                                    for(let i =0; i< machinePrc.length; i++){
+                                        sumMaP += machinePrc[i];
+                                    }
+                                    updatePrc.machinePrice = scMathUtil.roundTo(sumMaP, -2);
+                                }
+                                updatePrc.basePrice = scMathUtil.roundTo(updatePrc.labourPrice + updatePrc.materialPrice + updatePrc.machinePrice, -2);
+                                //updateDataBase
+                                compleRationModel.update({ID: rationItem.ID}, {$set: {labourPrice: updatePrc.labourPrice.toString(), materialPrice: updatePrc.materialPrice.toString(),
+                                        machinePrice: updatePrc.machinePrice.toString(), basePrice: updatePrc.basePrice.toString()}},
+                                    function (err, result) {
+                                        if(err){
+                                            ecb(err);
+                                        }
+                                        else {
+                                            ecb(null);
+                                        }
+                                    });
+                            }
+                        });
+                    }, function(err){
+                        if(err){
+                            cb(err);
+                        }
+                        else {
+                            cb(null);
+                        }
+                    });
+                },
+            ], function (err) {
+                if(err){
+                    finalCb(err);
+                }
+                else{
+                    finalCb(null);
+                }
+            });
+        }, function (err) {
+            if(err){
+                callback(err, 'Error');
+            }
+            else{
+                callback(0, '');
+            }
+        });
+    }
+
+    mixUpdateRationItems (userID, compilationId, rationLibId, sectionId, updateItems, addItems, rIds, callback){
+        let me = this;
+        if (updateItems.length == 0 && rIds.length == 0) {
+            me.addRationItems(userID, compilationId, rationLibId, sectionId, addItems, callback);
+        } else {
+            me.removeRationItems(rationLibId, rIds, function(err, message, docs) {
+                if (err) {
+                    callback(true, false);
+                } else {
+                    me.updateRationItems(userID, rationLibId, sectionId, updateItems, function(err, results){
+                        if (err) {
+                            callback(true, false);
+                        } else {
+                            if (addItems && addItems.length > 0) {
+                                me.addRationItems(rationLibId, sectionId, addItems, callback);
+                            } else {
+                                callback(0, results);
+                            }
+                        }
+                    });
+                }
+            })
+        }
+    }
+
+     removeRationItems(rationRepId, rIds,callback){
+        if (rIds.length > 0) {
+            compleRationModel.remove({rationRepId: rationRepId, ID: {$in: rIds}}, function(err, docs){
+                if (err) {
+                    callback(true, false);
+                } else {
+                    callback(0, docs);
+                }
+            })
+        } else {
+            callback(0,  null);
+        }
+    }
+
+
+     addRationItems(userID, compilationId, rationLibId, sectionId, items,callback){
+        if (items && items.length > 0) {
+            counter.counterDAO.getIDAfterCount(counter.moduleName.rations, items.length, function(err, result){
+                let maxId = result.value.sequence_value;
+                let arr = [];
+                for (let i = 0; i < items.length; i++) {
+                    let obj = new compleRationModel(items[i]);
+                    obj.ID = (maxId - (items.length - 1) + i);
+                    obj.sectionId = sectionId;
+                    obj.rationRepId = rationLibId;
+                    obj.userId = userID;
+                    obj.compilationId = compilationId;
+                    arr.push(obj);
+                }
+                compleRationModel.collection.insert(arr, null, function(err, docs){
+                    if (err) {
+                        callback(true, false);
+                    } else {
+                        callback(0, docs);
+                    }
+                })
+            });
+        } else {
+            callback(true, "Source error!", false);
+        }
+    }
+
+     updateRationItems(userID, rationLibId, sectionId, items,callback){
+        let functions = [];
+        for (let i=0; i < items.length; i++) {
+            functions.push((function(doc) {
+                return function(cb) {
+                    var filter = {};
+                    if (doc.ID) {
+                        filter.ID = doc.ID;
+                    } else {
+                        filter.sectionId = sectionId;
+                        filter.userId = userID;
+                        if (rationLibId) filter.rationRepId = rationLibId;
+                        filter.code = doc.code;
+                    }
+                    compleRationModel.update(filter, doc, cb);
+                };
+            })(items[i]));
+        }
+        async.parallel(functions, function(err, results) {
+            if(!err){
+                err = 0;
+            }
+            callback(err, results);
+        });
+    }
+
+    async getInstallation(rationRepId, callback){
+        try {
+            let feeItems = await installFeeItemModel.find({rationRepId: rationRepId, $or: [{deleted: false}, {deleted: null}]});
+            for(let feeItem of feeItems){
+                let sids = [];
+                for(let sec of feeItem.section){
+                    sids.push(sec.ID);
+                }
+                if(sids.length > 0){
+                    let sections = await installSectionModel.find({ID: {$in: sids}, $or: [{deleted: false}, {deleted: null}]});
+                    feeItem._doc.section = sections;
+                }
+            }
+            callback(0, feeItems);
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+}
+
+function isDef(v){
+    return v !== undefined && v !== null;
+}
+
+export default CompleRatoinDao;

+ 29 - 0
modules/complementary_ration_lib/models/compleViewModel.js

@@ -0,0 +1,29 @@
+/**
+ * Created by Zhong on 2017/12/22.
+ */
+
+let rationRepositoryModel = require('../../ration_repository/models/repository_map').Model;
+
+class CompleViewModel {
+    async getRationLib(rationRepId, callback){
+        try{
+            let rationLib = await rationRepositoryModel.find({ID: rationRepId, $or: [{deleted: null}, {deleted: false}]});
+            callback(0, rationLib);
+        }
+        catch(err) {
+            callback(err, null);
+        }
+    }
+
+    async getRationLibs(compilationId, ids, callback){
+        try{
+            let rationLibs = await rationRepositoryModel.find({compilationId: compilationId, ID: {$in: ids}, $or: [{deleted: null}, {deleted: false}]});
+            callback(0, rationLibs);
+        }
+        catch(err) {
+            callback(err, null);
+        }
+    }
+}
+
+export default CompleViewModel;

+ 118 - 0
modules/complementary_ration_lib/models/schemas.js

@@ -0,0 +1,118 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import mongoose from 'mongoose';
+
+let deleteSchema = require('../../../public/models/delete_schema');
+let Schema = mongoose.Schema;
+
+//补充定额章节树
+let compleRationSectionTreeSchema = new Schema({
+    //用户名
+    userId: Number,
+    //编办
+    compilationId: String,
+    //标准定额库
+    rationRepId: Number,
+    //名称
+    name: String,
+    //是否是同层第一个节点
+    isFirst: Boolean,
+    ID: Number,
+    NextSiblingID: Number,
+    ParentID: Number,
+    deleteInfo: deleteSchema
+}, {versionKey: false});
+
+//定额工料机
+let compleRationGljItemSchema = new Schema({
+    gljId: Number,
+    consumeAmt: String,
+    type: String    //std or complementary
+
+}, { _id: false });
+
+//辅助定额调整
+let compleRationAssItemSchema = new Schema({
+    name: String,
+    assistID: Number,
+    assistCode: String,
+    stdValue: String,
+    stepValue: String,
+    decimal: Number,
+    carryBit: String,
+    minValue: String,
+    maxValue: String
+}, { _id: false });
+
+//安装增加费-费用规则
+let feeRuleSchema = new Schema({
+    ID: String,
+    code: String,
+    rule: String,
+    base: String,
+    feeRate: Number,
+    labour: Number,
+    material: Number,
+    machine: Number
+});
+
+//定额安装增加费用
+let rationInstSchema = new Schema({
+    feeItemId: String,
+    sectionId: String
+},{_id: false});
+
+//标准安装增加费-分册章节
+let installSectionSchema = new Schema({
+    rationRepId: Number,
+    ID: String,
+    feeItemId: String,
+    name: String,
+    feeRule: [feeRuleSchema],
+    deleted: false
+}, {versionKey: false});
+
+//标准安装增加费-费用项
+let installFeeItemSchema = new Schema({
+    rationRepId: Number,
+    ID: String,
+    feeItem: String, //费用项
+    feeType: String, //费用类型
+    position: String, //记取位置
+    section: [],
+    deleted: false
+}, {versionKey: false});
+
+//补充定额
+let compleRationSchema = new Schema({
+    userId: Number,
+    compilationId: String,
+    rationRepId: Number,
+    ID:Number,
+    code: String,
+    name: String,
+    unit: String,
+    labourPrice: String,
+    materialPrice: String,
+    machinePrice: String,
+    basePrice: String,
+    sectionId: Number,
+    caption: String,
+    feeType: Number,
+    jobContent: String,
+    annotation: String,
+    rationGljList: [compleRationGljItemSchema],
+    rationCoeList: Array,
+    rationAssList: [compleRationAssItemSchema],
+    rationInstList: [rationInstSchema],
+    deleteInfo: deleteSchema
+}, {versionKey: false});
+
+let compleRationSectionTreeModel = mongoose.model('complementary_ration_section_tree', compleRationSectionTreeSchema, 'complementary_ration_section_tree');
+let compleRationModel = mongoose.model('complementary_ration_items', compleRationSchema, 'complementary_ration_items');
+let installSectionModel = mongoose.model("std_ration_lib_installationSection", installSectionSchema, "std_ration_lib_installationSection");
+let installFeeItemModel = mongoose.model("std_ration_lib_installation", installFeeItemSchema, "std_ration_lib_installation");
+
+export {compleRationSectionTreeModel, compleRationModel, installSectionModel, installFeeItemModel};

+ 82 - 0
modules/complementary_ration_lib/models/searchModel.js

@@ -0,0 +1,82 @@
+/**
+ * Created by Zhong on 2018/1/9.
+ */
+
+import {compleRationModel} from './schemas';
+import {complementaryGljModel, stdGljModel} from '../../complementary_glj_lib/models/schemas';
+import {compleRationSectionTreeModel} from './schemas';
+let stdSectionTreeModel = require ('../../ration_repository/models/ration_section_tree').Model;
+let stdRationModel = require ('../../ration_repository/models/ration_item').Model;
+
+class SearchDao{
+    async getRationItem(userId, rationRepId, code, callback){
+        let ration = null;
+        try{
+            let stdRation = await stdRationModel.findOne({rationRepId: rationRepId, code: code, $or: [{isDeleted: null}, {isDeleted: false}]});
+            if(isDef(stdRation)){
+                ration = stdRation._doc;
+                ration.type = 'std';
+            }
+            else{
+                let compleRation = await compleRationModel.findOne({userId: userId, rationRepId: rationRepId, code: code, deleteInfo: null});
+                if(isDef(compleRation)){
+                    ration = compleRation._doc;
+                    ration.type = 'complementary';
+                }
+            }
+            if(isDef(ration)){
+                let stdChapter = await stdSectionTreeModel.findOne({rationRepId: ration.rationRepId, ID: ration.sectionId, $or: [{isDeleted: null}, {isDeleted: false}]});
+                if(isDef(stdChapter)){
+                    ration.chapter = stdChapter._doc;
+                }
+                else{
+                    let compleChapter = await compleRationSectionTreeModel.findOne({userId: userId, ID: ration.sectionId, deleteInfo: null});
+                    if(isDef(compleChapter)){
+                        ration.chapter = compleChapter._doc;
+                    }
+                }
+            }
+            if(callback){
+                callback(0, ration);
+            }
+        }
+        catch(err){
+            if(callback){
+                callback(err, null);
+            }
+        }
+        return ration;
+    }
+
+    async findRation(userId, rationRepId, keyword, callback){
+        try{
+            let filter = {
+                'rationRepId': rationRepId,
+                '$and': [{
+                    '$or': [{'code': {'$regex': keyword, $options: '$i'}}, {'name': {'$regex': keyword, $options: '$i'}}]
+                }, {
+                    '$or': [{'isDeleted': {"$exists":false}}, {'isDeleted': null}, {'isDeleted': false}, {deleteInfo: null}]
+                }]
+            };
+            let stdRations = await stdRationModel.find(filter);
+            for(let i = 0, len = stdRations.length; i < len; i++){
+                stdRations[i]._doc.type = 'std';
+            }
+            filter.userId = userId;
+            let compleRations = await compleRationModel.find(filter);
+            for(let i = 0, len = compleRations.length; i <len; i++){
+                compleRations[i]._doc.type = 'complementary';
+            }
+            callback(0, stdRations.concat(compleRations));
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+}
+
+function isDef(v){
+    return v !== undefined && v !== null;
+}
+
+export default SearchDao;

+ 88 - 0
modules/complementary_ration_lib/models/sectionTreeModel.js

@@ -0,0 +1,88 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import {compleRationSectionTreeModel} from './schemas';
+
+let counter = require('../../../public/counter/counter');
+let stdSectionTreeModel = require ('../../ration_repository/models/ration_section_tree').Model;
+
+class SectionTreeDao {
+    getNewTreeID(callback){
+        counter.counterDAO.getIDAfterCount(counter.moduleName.rationTree, 1, function (err, result) {
+            if(err){
+                callback(err, null);
+            }
+            else {
+                callback(0, result.value.sequence_value);
+            }
+        });
+
+    }
+
+    //获取补充定额拼接章节树
+    async getRationTree(userID, rationRepId, callback){
+        try{
+            let stdSectionTree = await stdSectionTreeModel.find({rationRepId: rationRepId, $or: [{isDeleted: null}, {isDeleted: false}]});
+            let compleSectionTree = await compleRationSectionTreeModel.find({userId: userID, rationRepId: rationRepId, deleteInfo: null});
+            let dropPids = [], rstCompleSectionTree = [];
+            //mark std
+            for(let i = 0, len = stdSectionTree.length; i < len; i++){
+                stdSectionTree[i]._doc.type = 'std';
+            }
+            for(let i = 0, len = compleSectionTree.length; i < len; i++){
+                //mark complementary
+                compleSectionTree[i]._doc.type = 'complementary';
+                if(compleSectionTree[i]['isFirst']){
+                    let updateSection = getUpdateSection(compleSectionTree[i]['ParentID'], stdSectionTree);
+                    if(updateSection) {
+                        updateSection._doc.NextSiblingID = compleSectionTree[i]['ID'];
+                    }
+                    else if(!updateSection && compleSectionTree[i]['ParentID'] !== -1){
+                        dropPids.push(compleSectionTree[i]['ParentID']);
+                    }
+                }
+            }
+            function getUpdateSection(pid, datas){
+                for(let i = 0, len = datas.length; i < len; i++){
+                    if(datas[i]['ParentID'] === pid && datas[i]['NextSiblingID'] === -1){
+                        return datas[i];
+                    }
+                }
+                return null;
+            }
+            //返回父节点未被删除的
+            for(let i = 0, len = compleSectionTree.length; i < len; i++){
+                if(dropPids.indexOf(compleSectionTree[i]['ParentID']) === -1){
+                    rstCompleSectionTree.push(compleSectionTree[i]);
+                }
+            }
+            callback(0, stdSectionTree.concat(rstCompleSectionTree));
+        }
+        catch (err){
+            callback(err, null);
+        }
+    }
+
+    async updateSection(userID, compilationId, updateData, callback){
+        try{
+            for(let i = 0, len = updateData.length; i < len; i++){
+                let updateObj = updateData[i];
+                if(updateObj.updateType === 'new'){
+                    updateObj.updateData.userId = userID;
+                    updateObj.updateData.compilationId = compilationId;
+                    await compleRationSectionTreeModel.create(updateObj.updateData);
+                }
+                else if(updateObj.updateType === 'update'){
+                    await compleRationSectionTreeModel.update({userId: userID, rationRepId: updateObj.updateData.rationRepId, ID: updateObj.updateData.ID}, updateObj.updateData);
+                }
+            }
+            callback(0, 'success');
+        }
+        catch(err){
+            callback(err, null);
+        }
+    }
+}
+
+export default SectionTreeDao;

+ 57 - 0
modules/complementary_ration_lib/routes/routes.js

@@ -0,0 +1,57 @@
+/**
+ * Created by Zhong on 2017/12/21.
+ */
+
+import express from 'express';
+import CompleViewController from '../controllers/compleViewController';
+import CompleSectionTreeController from '../controllers/compleSectionTreeController';
+import CompleRationController from '../controllers/compleRationController';
+import GljController from '../../complementary_glj_lib/controllers/gljController';
+import SearchController from '../controllers/searchController'
+
+let router = express.Router();
+let compleViewController = new CompleViewController();
+let compleSectionTreeController = new CompleSectionTreeController();
+let compleRationController = new CompleRationController();
+let gljController = new GljController();
+let searchController = new SearchController();
+
+module.exports = function (app) {
+    //app.get('/complementaryRation/main', compleViewController.init, compleViewController.redirectMain);
+    app.get('/complementaryRation/ration', compleViewController.init, compleViewController.redirectRation);
+    app.get('/complementaryRation/glj', compleViewController.init, compleViewController.redirectGljList);
+    app.get('/complementaryRation/coe', compleViewController.init, compleViewController.redirectCoeList);
+    app.get('/complementaryRation/installation', compleViewController.init, compleViewController.redirectInstallation);
+
+    router.post('/getRationLib', compleViewController.init, compleViewController.getRationLib);
+    router.post('/getRationLibs', compleViewController.init, compleViewController.getRationLibs);
+
+    router.post('/getNewTreeID', compleSectionTreeController.init, compleSectionTreeController.getNewTreeID);
+    router.post('/getRationTree', compleSectionTreeController.init, compleSectionTreeController.getRationTree);
+    router.post('/updateRationSection', compleSectionTreeController.init, compleSectionTreeController.updateRationSection);
+
+    router.post('/getRationItems', compleRationController.init, compleRationController.getRationItems);
+    router.post('/mixUpdateRationItems', compleRationController.init, compleRationController.mixUpdateRationItems);
+
+    router.post('/getGljDistType', gljController.init, gljController.getGljDistType);
+    router.post('/getGljTree', gljController.init, gljController.getGljTree);
+    router.post('/getGljItems', compleRationController.init, compleRationController.getGljItems);
+    router.post('/getGljItemsOccupied', compleRationController.init, compleRationController.getGljItemsOccupied);
+    router.post('/getRationsCodes', compleRationController.init, compleRationController.getRationsCodes);
+    router.post('/getGljItemsByIds', compleRationController.init, compleRationController.getGljItemsByIds);
+    router.post('/getGljItemsByCodes', compleRationController.init, compleRationController.getGljItemsByCodes);
+
+    router.post('/getCoeList', compleRationController.init, compleRationController.getCoeList);
+    router.post('/getCoeItemsByIDs', compleRationController.init, compleRationController.getCoeItemsByIDs);
+    router.post('/getCoeItemsByNos', compleRationController.init, compleRationController.getCoeItemsByNos);
+
+    //安装增加费
+    router.post('/getInstallation', compleRationController.init, compleRationController.getInstallation);
+
+    //造价书定额库
+    router.post('/getRationItem', searchController.init, searchController.getRationItem);
+    router.post('/findRation', searchController.init, searchController.findRation);
+    router.post('/getRationGljItemsBySection', compleRationController.init, compleRationController.getRationGljItemsBySection);
+
+    app.use('/complementaryRation/api', router);
+};

+ 38 - 3
modules/glj/controllers/glj_controller.js

@@ -13,6 +13,9 @@ import MixRatioModel from "../models/mix_ratio_model";
 import UnitPriceFileModel from "../models/unit_price_file_model";
 let logger = require("../../../logs/log_helper").logger;
 let consts = require('../../main/models/project_consts');
+let glj_type_util = require('../../../public/cache/std_glj_type_util');
+let mongoose = require('mongoose');
+let ration = mongoose.model('ration');
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -125,8 +128,15 @@ class GLJController extends BaseController {
                         if (mixRatioData.length <= 0) {
                             break;
                         }
+                        let indexList = ['code','name','specs','unit','type'];
+                        let keyList = mixRatioData.connect_key.split("|-|");
                         // 更新市场单价和基价单价
-                        let condition = {code: mixRatioData.connect_code, unit_price_file_id: mixRatioData.unit_price_file_id};
+                        let condition = {unit_price_file_id: mixRatioData.unit_price_file_id};
+                        for(let i = 1;i<keyList.length;i++){
+                            if(keyList[i]!='null'){
+                                condition[indexList[i]]=keyList[i];
+                            }
+                        }
                         let unitPriceUpdate = {
                             base_price: basePrice,
                             market_price: marketPrice
@@ -385,7 +395,8 @@ class GLJController extends BaseController {
             if (!copyResult) {
                 throw '复制数据失败';
             }
-
+           await ProjectModel.updateUnitFileToProject(projectId,{id:addResult.id,name:addResult.name});
+            responseData.data = addResult;
         } catch (error) {
             responseData.err = 1;
             responseData.msg = error;
@@ -551,6 +562,28 @@ class GLJController extends BaseController {
         res.json(result);
     }
 
+    async modifyKeyValue(req,res){//修改工料机关键的属性:名称、类型、规格、型号等
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let gljListModel = new GLJListModel();
+            let ration_data = await ration.findOne(data.ration);
+            //  修改项目工料机
+            let projcetGLJ = await gljListModel.modifyGLJ(data.updateData,ration_data);
+
+           // let datas = await gljListModel.addList(data);
+            result.data=projcetGLJ;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    }
+
 }
 
 /**
@@ -595,12 +628,14 @@ async function getGLJListByProjectID(projectId){
         responseData.data.mixRatioConnectData = mixRatioConnectData;
         responseData.data.mixRatioMap = mixRationMap;
         responseData.data.usedTenderList = usedTenderList;
+        let gljTypeMap = glj_type_util.getStdGljTypeCacheObj().innerGljTypeObj;
         responseData.data.constData = {
             materialIdList: gljListModel.materialIdList,
             ownCompositionTypes: gljListModel.ownCompositionTypes,
             roomId: unitPriceFileId,
             GLJTypeConst: JSON.stringify(GLJTypeConst),
-            usedUnitPriceInfo: usedUnitPriceInfo
+            usedUnitPriceInfo: usedUnitPriceInfo,
+            gljTypeMap:gljTypeMap
         };
     } catch (error) {
         console.log(error);

+ 37 - 13
modules/glj/models/glj_list_model.js

@@ -36,7 +36,7 @@ class GLJListModel extends BaseModel {
      * @var {Array}
      */
     ownCompositionTypes = [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO,
-        GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR, GLJTypeConst.GENERAL_MACHINE];
+        GLJTypeConst.GENERAL_MACHINE,GLJTypeConst.MAIN_MATERIAL];
 
     /**
      * 构造函数
@@ -109,11 +109,12 @@ class GLJListModel extends BaseModel {
                 projectGLJIDList: gljIdList
             };
             let quantityData = await RationGLJFacade.getQuantityByProjectGLJ(condition);
+            let rationTypeQuantity = await RationGLJFacade.getRationTypeGLJQuantity(projectId);
             let quantityList = {};
             // 整理数据 得到总定额消耗量
             for (let tmp of quantityData) {
                 let tmpNum = parseFloat(tmp.rationQuantity);
-                tmpNum = isNaN(tmpNum) ||tmpNum==0? 1 : tmpNum;
+                tmpNum = isNaN(tmpNum) ||tmpNum==0? 0 : tmpNum;
                 let tmp_con_key = keyMap[tmp.projectGLJID];
 
                 if (quantityList[tmp_con_key] === undefined) {
@@ -122,13 +123,23 @@ class GLJListModel extends BaseModel {
                     quantityList[tmp_con_key] = scMathUtil.roundTo(quantityList[tmp_con_key]+tmp.quantity * tmpNum,-quantity_decimal);
                 }
             }
+            //计算定额类型工料机的消耗量
+            for(let rq of rationTypeQuantity){
+                let rq_key = keyMap[rq.projectGLJID];
+                let rq_quantity= scMathUtil.roundForObj(rq.quantity,quantity_decimal);
+                if(quantityList[rq_key] === undefined){
+                    quantityList[rq_key] =  scMathUtil.roundTo(rq_quantity,-quantity_decimal);
+                }else {
+                    quantityList[rq_key] = scMathUtil.roundTo(quantityList[rq_key]+rq_quantity,-quantity_decimal);
+                }
+            }
             // 整理获取有组成物的项目工料机的数据
             let connect_keys = [];
             for(let tmp of gljData) {
                 // 有组成物的类型才查找
                 let key = keyMap[tmp.id];
                 if (quantityList[key]!=undefined&&(tmp.type === GLJTypeConst.CONCRETE || tmp.type === GLJTypeConst.MORTAR ||
-                    tmp.type === GLJTypeConst.MIX_RATIO || tmp.type === GLJTypeConst.GENERAL_MACHINE)){
+                    tmp.type === GLJTypeConst.MIX_RATIO || tmp.type === GLJTypeConst.GENERAL_MACHINE|| tmp.type === GLJTypeConst.MAIN_MATERIAL)){
                     connect_keys.push(key);
                 }
             }
@@ -163,10 +174,10 @@ class GLJListModel extends BaseModel {
                     }else {
                         mixRationMap[t_index]=[tmp];
                     }
-                    if (mixRatioConnectData[tmp.glj_id] !== undefined) {
-                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_key);
+                    if (mixRatioConnectData[m_index] !== undefined) {
+                        mixRatioConnectData[m_index].push(tmp.connect_key);
                     } else {
-                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_key];
+                        mixRatioConnectData[m_index] = [tmp.connect_key];
                     }
                 }
             }
@@ -298,9 +309,9 @@ class GLJListModel extends BaseModel {
             }
             let CompositionGLJ=[];
             let unitPriceModel = new UnitPriceModel();
-            // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
+            // 判断类型,如果是混凝土、砂浆、配合比或者主材则查找对应的组成物(前提是没有对应的项目工料机数据)
             if (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
-                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE) {
+                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE||data.type === GLJTypeConst.MAIN_MATERIAL) {
                 //如果是新增
                 if(isAddProjectGLJ ){
                     await this.compositionInit(data, unitPriceFileId);
@@ -377,7 +388,7 @@ class GLJListModel extends BaseModel {
         let unitPriceFileId = unitPriceFile.id;
         let unitPriceModel = new UnitPriceModel();
         let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId,"modify");
-        let gljData={};
+        let gljData=null;
         if(isAdd){ //如果是新增,则新增一条新的项目工料机
             data.code = unitPriceData.code;
             gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
@@ -390,8 +401,15 @@ class GLJListModel extends BaseModel {
                 type:data.type,
                 unit:data.unit
             }
-            gljData = await this.findDataByCondition(condition,{_id: 0});
-            if(!gljData){
+            let gljList = await this.findDataByCondition(condition,{_id: 0},false);
+            if(gljList&&gljList.length>0){
+                for(let tem of gljList){
+                    if(tem.code == unitPriceData.code){
+                        gljData = tem;
+                    }
+                }
+            }
+            if(gljData==null){
                 data.code = unitPriceData.code;
                 gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
             }
@@ -408,7 +426,13 @@ class GLJListModel extends BaseModel {
         if (glj.type === GLJTypeConst.CONCRETE || glj.type === GLJTypeConst.MORTAR ||
             glj.type === GLJTypeConst.MIX_RATIO || glj.type === GLJTypeConst.GENERAL_MACHINE){
             // 配合比数据插入
-            let connect_key =this.getIndex(ration_glj,['code','name','specs','unit','type']);
+            let key_array = [];
+            if(ration_glj.subType){//定额类型的工料机和定额工料机的key有点不同subType
+                key_array=  ['code','name','specs','unit','subType'];
+            }else {
+                key_array=  ['code','name','specs','unit','type'];
+            }
+            let connect_key =this.getIndex(ration_glj,key_array);
             let connect_key_n =this.getIndex(glj,['code','name','specs','unit','type']);
             //先查找配合比数据是否已经存在
             let mixRatioModel = new MixRatioModel();
@@ -663,7 +687,7 @@ class GLJListModel extends BaseModel {
             let projectGLJData = await this.getDataById(projectGLJId,unitPriceFileId);
 
             let allowType = [GLJTypeConst.MIX_RATIO, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR,
-                GLJTypeConst.GENERAL_MACHINE];
+                GLJTypeConst.GENERAL_MACHINE,GLJTypeConst.MAIN_MATERIAL];
 
             if (projectGLJData.unit_price === null || allowType.indexOf(projectGLJData.unit_price.type) < 0) {
                 throw '找不到相关项目工料机';

+ 8 - 10
modules/glj/models/unit_price_file_model.js

@@ -7,6 +7,7 @@
  */
 import BaseModel from "../../common/base/base_model";
 import CounterModel from "./counter_model";
+const ProjectModel = require('../../pm/models/project_model').project;
 import {default as UnitPriceFileSchema, collectionName as collectionName} from "./schemas/unit_price_file";
 
 class UnitPriceFileModel extends BaseModel {
@@ -71,19 +72,16 @@ class UnitPriceFileModel extends BaseModel {
             if (isNaN(projectId) || projectId <= 0) {
                 throw '标段id有误';
             }
-            result = await this.findDataByCondition({project_id: projectId});
-
-            // 如果没有找到则新增一条记录
-            if (!result) {
-                let data = {
-                    // @todo 后续再项目中获取
-                    name: 'projectName',
-                    project_id: projectId
-                };
-                result = await this.add(data);
+
+            let unitPriceFileId =await ProjectModel.getUnitPriceFileId(projectId);
+            if (unitPriceFileId <= 0) {
+                throw '没有对应的单价文件';
             }
+            result = await this.findDataByCondition({id: unitPriceFileId});
+
         } catch (error) {
             console.log('error:' + error);
+            throw '没有对应的单价文件 '
         }
 
         return result;

+ 24 - 12
modules/glj/models/unit_price_model.js

@@ -12,6 +12,7 @@ import MixRatioModel from "./mix_ratio_model";
 import {default as UnitPriceSchema, collectionName as collectionName} from "./schemas/unit_price";
 import _ from "lodash";
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
+let decimal_facade = require('../../main/facade/decimal_facade');
 
 class UnitPriceModel extends BaseModel {
 
@@ -86,10 +87,10 @@ class UnitPriceModel extends BaseModel {
             return [null, false];
         }
         // 先查找是否有原始code相同的记录
-        let unitPriceData = await this.findDataByCondition({original_code: data.original_code, unit_price_file_id: unitPriceFileId}, null, false);
+        let unitPriceData = await this.db.model.find({original_code: data.original_code, unit_price_file_id: unitPriceFileId}).sort('code').exec();
         // 如果有记录,判断是否存在一样的名称,单位...等,有则直接返回数据
         let unitPrice=null;
-        if(operation=='add'){//新增操作时,要把code也一起判断,是否完全一样
+        if(operation=='add'){//新增操作时,要把code也一起判断,是否完全一样。(新增的时候有可能存在编码一样,但是名称规格等不一样的情况,这种情况的话编码不用改变)
             unitPrice =  this.isPropertyInclude(unitPriceData,['code','name','specs','unit','type'],data);
         }else {//修改操作时,code不用加入判断,因为code是需要改变的
             unitPrice =  this.isPropertyInclude(unitPriceData,['name','specs','unit','type'],data);
@@ -121,7 +122,9 @@ class UnitPriceModel extends BaseModel {
         };
         if(data.from=='cpt'){//如果是来自补充工料机,则都添加新增标记
             insertData.is_add=1;
-        }else if (unitPriceData&&unitPriceData.length>0) {// 如果原始编码能找到,但不存在一样的编号,名称,单位.型号等,更改code和添加新增标记
+        }
+
+        if (unitPriceData&&unitPriceData.length>0&&operation!='add') {// 如果原始编码能找到,但不存在一样的编号,名称,单位.型号等,更改code和添加新增标记,新增的时候除外。新增的情况下能到这一步说明有存在编码一致但其它属性不一致的情况,所以不用更改编码
             insertData.code = data.original_code+"-"+unitPriceData.length;
             insertData.is_add=1;
         }
@@ -217,14 +220,20 @@ class UnitPriceModel extends BaseModel {
         // 额外更新数据
         if (extend !== '') {
             extend = JSON.parse(extend);
-            for (let code in extend) {
+            let indexList = ['code','name','specs','unit','type'];
+            for (let conKey in extend) {
                 let extendUpdateData = {
-                    market_price: extend[code].market_price,
+                    market_price: extend[conKey].market_price,
                 };
                 let tmpCondition = {
                     unit_price_file_id: unitPriceData.unit_price_file_id,
-                    code: code
                 };
+                let keyList = conKey.split("|-|");
+                for(let i = 1;i<keyList.length;i++){
+                    if(keyList[i]!='null'){
+                        tmpCondition[indexList[i]]=keyList[i];
+                    }
+                }
                 let extendResult = await this.db.update(tmpCondition, extendUpdateData);
                 if (!extendResult) {
                     throw '更新额外数据,编码为' + code + '的数据失败!';
@@ -254,7 +263,7 @@ class UnitPriceModel extends BaseModel {
         if(mixRatioList&&mixRatioList.length>0){
             for(let m of mixRatioList){
                 if(!connectKeyMap.hasOwnProperty(m.connect_key)){//为了去重复,组成物会与其它项目同步,所以有可能重复。
-                    rList.push(await this.updateParentUnitPrice(m,data.field));
+                    rList.push(await this.updateParentUnitPrice(m,data.field,data.project_id));
                     connectKeyMap[m.connect_key]=true;
                 }
             }
@@ -262,7 +271,10 @@ class UnitPriceModel extends BaseModel {
         return rList;
     }
 
-    async updateParentUnitPrice(mixRatio,fieid){
+    async updateParentUnitPrice(mixRatio,fieid,project_id){
+        let  decimalObject =await decimal_facade.getProjectDecimal(project_id);
+        let quantity_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
+        let price_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.unitPrice)?decimalObject.glj.unitPrice:2;
         //查找该工料机所有组成物
         let indexList = ['code','name','specs','unit','type'];
         let mixRatioModel = new MixRatioModel();
@@ -284,11 +296,11 @@ class UnitPriceModel extends BaseModel {
         let priceMap = await this.findDataByCondition(condition, {_id: 0}, false, indexList);
         let sumPrice=0;
         for(let pk in priceMap){
-            let price = parseFloat(priceMap[pk][fieid]);
-            let consumption = parseFloat(mixRatioMap[pk].consumption);
-            sumPrice +=scMathUtil.roundTo(price*consumption,-6);
+            let price = scMathUtil.roundForObj(priceMap[pk][fieid],price_decimal);
+            let consumption = scMathUtil.roundForObj(mixRatioMap[pk].consumption,quantity_decimal);
+            sumPrice +=scMathUtil.roundForObj(price*consumption,price_decimal);
         }
-        sumPrice= scMathUtil.roundTo(sumPrice,-6);
+        sumPrice= scMathUtil.roundForObj(sumPrice,price_decimal);
         if(sumPrice<=0){
             return null;
         }

+ 1 - 0
modules/glj/routes/glj_router.js

@@ -22,6 +22,7 @@ router.post('/change-file', gljController.init, gljController.changeUnitPriceFil
 router.post('/save-as', gljController.init, gljController.unitPriceSaveAs);
 router.post('/get-composition', gljController.init, gljController.getComposition);
 router.post('/updatePrice', gljController.init, gljController.updateUnitPrice);
+router.post('/modifyKeyValue',gljController.init, gljController.modifyKeyValue);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);

+ 191 - 2
modules/main/controllers/bills_controller.js

@@ -1,8 +1,16 @@
 /**
  * Created by jimiz on 2017/4/7.
  */
+let mongoose = require('mongoose');
 var billsData = require('../models/bills');
-
+let ration_model = require('../models/ration');
+let ProjectsData = require('../../pm/models/project_model').project;
+let logger = require("../../../logs/log_helper").logger;
+let quantity_detail = require("../facade/quantity_detail_facade");
+let bill_detail = require("../facade/bill_facade");
+let ration_glj = mongoose.model('ration_glj');
+let ration_coe = mongoose.model('ration_coe');
+import rationInstallationModel from "../models/ration_installation";
 //统一回调函数
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
@@ -49,6 +57,187 @@ module.exports = {
         billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
             callback(req, res, err, message, null);
         });
+    },
+    updateBill: async function(request, response) {
+        const data = JSON.parse(request.body.data);
+        const findSet = data.findSet;
+        const updateData = data.updateData;
+        let settingData = {};
+        // 筛选出要保存在项目属性的设置
+        for (const index in updateData) {
+            if (updateData[index].field === 'addRule') {
+                settingData = updateData[index].value;
+                delete updateData[index];
+            }
+        }
+        // 更新项目属性
+        const propertyUpdateData = {
+            property: 'addRule',
+            data: settingData
+        };
+        const projectResult = await ProjectsData.updateProjectProperty(findSet.projectID, propertyUpdateData);
+
+        const result = await billsData.updateBill(findSet, updateData);
+        const message = !result || !projectResult ? '修改失败' : '修改成功';
+        const err = !result || !projectResult ? 1 : 0;
+        callback(request, response, err, message, null);
+    },
+    singleDelete:async function(req, res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let tasks = generateSingleDeleteTasks(data);
+            let resultData= await billsData.model.bulkWrite(tasks);
+            //删除工程量明细
+            await quantity_detail.deleteByQuery({projectID: data.projectID, billID: data.ID}) ;
+            result.data=resultData;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    multiDelete:async function(req, res){
+        let result={
+            error:0
+        };
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            result.data=await doBillsOrRationsDelete(data);
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    getSectionInfo:async function(req, res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let sectionInfo= await bill_detail.getSectionInfo(data);
+            result.data=sectionInfo;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    reorganizeFBFX:async function(req,res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let reorganizeResult= await bill_detail.reorganizeFBFX(data);
+            result.data=reorganizeResult;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
     }
 
-};
+};
+
+async function doBillsOrRationsDelete(data) {
+    let billTask = [];
+    let deleteBillIDs = [];
+    let rationTask=[];
+    let deleteRationIDs=[];
+    let qd_query=null;
+    let sub_query=null;
+    if(data['bills']){
+        billTask = generateUpdateTasks(data['bills'],data.projectID,data.user_id);
+        for(let b_key in data['bills']){
+            if(data['bills'][b_key]===true){
+                deleteBillIDs.push(b_key+'');
+            }
+        }
+        if(deleteBillIDs.length>0){
+            qd_query={projectID: data.projectID, billID: {"$in": deleteBillIDs}};
+        }
+    }
+    if(data['ration']){
+        rationTask = generateUpdateTasks(data['ration'],data.projectID,data.user_id);
+        for(let r_key in data['ration']){
+            if(data['ration'][r_key]===true){
+                deleteRationIDs.push(r_key+'');
+            }
+        }
+        if(deleteRationIDs.length>0){
+            if(qd_query==null){//说明没删除清单
+                qd_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
+            }else {
+                qd_query={
+                    "$or":[
+                        {projectID: data.projectID, billID: {"$in": deleteBillIDs}},
+                        {projectID: data.projectID, rationID: {"$in": deleteRationIDs}}
+                    ]
+                }
+            }
+            sub_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
+        }
+    }
+    //先删除工程量明细
+    if(qd_query!=null){
+        await quantity_detail.deleteByQuery(qd_query) ;
+    }
+    if(sub_query!=null){
+        await ration_coe.deleteMany(sub_query);//删除附注条件
+        await ration_glj.deleteMany(sub_query);//删除定额工料机
+        await rationInstallationModel.deleteMany(sub_query);//删除安装增加费
+    }
+    if(rationTask.length>0){
+        await  ration_model.model.bulkWrite(rationTask);//删除定额
+    }
+    if(billTask.length>0){
+        await billsData.model.bulkWrite(billTask);//删除清单
+    }
+    return 'success';
+}
+
+
+
+function  generateSingleDeleteTasks(data) {
+    let updateData = data.updateData;
+    updateData[data.ID]=true;
+    let tasks = generateUpdateTasks(updateData,data.projectID,data.user_id);
+    return tasks;
+}
+
+function  generateUpdateTasks(data,projectID,user_id) {
+    let tasks=[];
+    let updateData = data;
+    let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: user_id};
+    for(let key in updateData){
+        let task={
+            updateOne:{
+                filter:{
+                    ID:key,
+                    projectID:projectID
+                }
+            }
+        };
+        if(updateData[key]===true){
+            task.updateOne.update={
+                deleteInfo:deleteInfo
+            };
+        }else {
+            task.updateOne.update=updateData[key];
+        }
+        tasks.push(task);
+    }
+    return tasks;
+}

+ 45 - 0
modules/main/controllers/installation_controller.js

@@ -0,0 +1,45 @@
+/**
+ * Created by zhang on 2018/2/5.
+ */
+let installation_facade = require('../facade/installation_facade');
+let ration_installation_facade = require('../facade/ration_installation_facade');
+
+let logger = require("../../../logs/log_helper").logger;
+module.exports={
+    updateInstallationFee:updateInstallationFee,
+    updateRationInstallation:updateRationInstallation
+}
+
+async function updateRationInstallation(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await ration_installation_facade.updateRationInstallation(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function  updateInstallationFee(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await installation_facade.updateInstallationFee(data.projectID,data.updateData);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}

+ 3 - 3
modules/main/controllers/labour_coe_controller.js

@@ -44,12 +44,12 @@ async function getStdLabourCoe(req, res) {
 function save(req, res) {
     let result = {error: 0, message: '', data: null};
 
-    labourCoeFacade.save(req.body.data, function (err, message, data) {
-        if (err == 0){
+    labourCoeFacade.save(req.body.data, function (err, data) {
+        if (err == ''){
             result.data = data;
         }else{
             result.error = 1;
-            result.message = message;
+            result.message = err;
         }
     });
 

+ 45 - 0
modules/main/controllers/project_controller.js

@@ -3,6 +3,8 @@
  */
 var Project = require('../models/project');
 let logger = require('../../../logs/log_helper').logger;
+let project_facade = require("../facade/project_facade");
+
 //统一回调函数
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
@@ -33,5 +35,48 @@ module.exports = {
                 callback(req, res, err, message, null);
             }
         });
+    },
+    markUpdateProject:async function (req,res) {
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let resultData= await project_facade.markUpdateProject(data.updateInfo,data.type);
+            result.data=resultData;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    removeProjectMark:async function(req,res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let resultData= await project_facade.removeProjectMark(data.projectID);
+            result.data=resultData;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    updateNodes:function (req,res) {
+        var data = JSON.parse(req.body.data);
+        project_facade.updateNodes(data, function (err, message, result) {
+            if (err) {
+                logger.err(err);
+                callback(req, res, err, message, null);
+            } else {
+                callback(req, res, err, message, result);
+            }
+        });
     }
 };

+ 1 - 0
modules/main/controllers/quantity_detail_controller.js

@@ -27,6 +27,7 @@ async function save(req, res) {
         result.error=1;
         result.message = err.message;
     }
+    console.log(result);
     res.json(result);
 }
 

+ 60 - 1
modules/main/controllers/ration_controller.js

@@ -2,6 +2,9 @@
  * Created by jimiz on 2017/4/9.
  */
 var rationData = require('../models/ration');
+var ration_glj_facade = require('../../ration_glj/facade/ration_glj_facade');
+var ration_facade = require('../facade/ration_facade');
+let logger = require("../../../logs/log_helper").logger;
 
 //统一回调函数
 var callback = function(req, res, err, message, data){
@@ -39,6 +42,62 @@ module.exports = {
                 callback(req, res, err, message, null);
             }
         });
+    },
+    insertGLJAsRation:insertGLJAsRation,
+    replaceRations:replaceRations,
+    addNewRation:addNewRation
+};
+
+async function addNewRation(req,res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        console.log(data);
+        result.data = await ration_facade.addNewRation(data);
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function replaceRations(req,res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let userID = req.session.sessionUser.ssoId;
+        result.data = await ration_facade.replaceRations(userID,data);
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
     }
+    res.json(result);
+};
+
+
+async function insertGLJAsRation(req, res){
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await ration_glj_facade.insertGLJAsRation(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+
 
-};
+}

+ 105 - 0
modules/main/facade/bill_facade.js

@@ -0,0 +1,105 @@
+/**
+ * Created by zhang on 2018/1/22.
+ */
+let quantity_detail = require("./quantity_detail_facade");
+let model = require("../../bills_lib/models/bills_lib_model");
+let Bills_Lib = model.billsMod;
+let bill_Model = require('../models/bills').model;
+let _ = require("lodash");
+module.exports={
+   getSectionInfo : async function (data) {
+        let conditions=[];
+        let fxList=[];
+        let sectionInfo ={};
+        for(let libID in data){
+            let codes=[];
+            let temp={};
+            codes= _.keys(data[libID]);
+            temp['billsLibId']=libID;
+            temp['code'] = {"$in": codes};
+            conditions.push(temp);
+        }
+        if(conditions.length>0){
+            fxList = await Bills_Lib.find({"$or":conditions});
+        }
+        if(fxList.length>0){
+            let sectionIDs = {};
+            for(let f of fxList){
+                if(f._doc.sectionInfo){
+                    f._doc.sectionInfo.first?sectionIDs[f._doc.sectionInfo.first]=true:"";
+                    f._doc.sectionInfo.second?sectionIDs[f._doc.sectionInfo.second]=true:"";
+                    f._doc.sectionInfo.third?sectionIDs[f._doc.sectionInfo.third]=true:"";
+                }
+            }
+            let IDList = _.keys(sectionIDs);
+            let sectionList = await Bills_Lib.find({'ID':{'$in':IDList}});
+            let sectionMap = _.mapKeys(sectionList,'ID');
+            let fxMap =  _.mapKeys(fxList,'code');
+            sectionInfo={
+                fxMap:fxMap,
+                sectionMap :sectionMap
+            }
+            return sectionInfo;
+        }
+        return null;
+    },
+    reorganizeFBFX:async function(data){
+       let result = {};
+       let tasks = generateBillTasks(data);
+       if(data.delete && data.delete.length > 0){
+           let qd_query={projectID: data.projectID, billID: {"$in": data.delete}};
+           await quantity_detail.deleteByQuery(qd_query) ;
+        }
+        if(tasks.length > 0){
+            result =await bill_Model.bulkWrite(tasks);
+        }
+        return result;
+    }
+}
+
+function generateBillTasks(data) {
+    let tasks=[];
+    let user_id = data.user_id,projectID = data.projectID;
+    let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: user_id};
+    if(data.delete && data.delete.length > 0){
+        for(let d_ID of data.delete){
+            let task={
+                updateOne:{
+                    filter:{
+                        ID:d_ID,
+                        projectID:projectID
+                    },
+                    update :{
+                        deleteInfo:deleteInfo
+                    }
+                }
+            };
+            tasks.push(task);
+        }
+    }
+    if(data.update && data.update.length > 0){
+        for(let u_data of data.update){
+            let task ={
+                updateOne:{
+                    filter : {
+                        ID:u_data.ID,
+                        projectID:projectID
+                    },
+                    update : u_data.data
+                }
+            };
+            tasks.push(task);
+        }
+    }
+    if(data.create && data.create.length > 0){
+        for(let n_data of data.create){
+            let task = {
+                insertOne :{
+                    document:n_data
+                }
+            };
+            tasks.push(task);
+        }
+    }
+    return tasks;
+}

+ 1 - 0
modules/main/facade/calc_program_facade.js

@@ -92,6 +92,7 @@ function save (user_id, datas, callback) {
     projectCalcProgramsModel.update({"projectID": 553}, {"libName":"goo—test"}, callback(null, returnData));
 }
 
+// saveCalcItem({projectID: 597, templatesID: 4, calcItem: calcItem})
 function saveCalcItem(dataObj, callback) {
     dataObj=JSON.parse(dataObj);
     projectCalcProgramsModel.findOne({projectID: dataObj.projectID}, function (err, data) {

+ 156 - 0
modules/main/facade/installation_facade.js

@@ -0,0 +1,156 @@
+/**
+ * Created by zhang on 2018/1/31.
+ */
+import {installSectionModel,installFeeItemModel} from "../../complementary_ration_lib/models/schemas";
+import installationFeeModel from "../models/installation_fee";
+import engineeringModel from "../../users/models/schema/engineering_lib";
+const uuidV1 = require('uuid/v1');
+let consts = require('../../main/models/project_consts')
+
+module.exports={
+    copyInstallationFeeFromLib:copyInstallationFeeFromLib,
+    updateInstallationFee:updateInstallationFee,
+    getData:getData
+};
+async function updateInstallationFee(projectID,updateData) {
+    let result = {};
+    let tasks = generateUpdateTask(projectID,updateData);
+    if(tasks.length>0){
+         result =await installationFeeModel.bulkWrite(tasks);
+    }
+    return result;
+}
+
+function generateUpdateTask(projectID,updateData) {
+    let tasks = [];
+    if(updateData){
+        if(updateData instanceof Array){
+            for(let ud of updateData){
+                let [uquery,udoc] = createUpdateQuery(projectID,ud);
+                let task={
+                    updateOne:{
+                        filter:uquery,
+                        update :udoc
+                    }
+                };
+                tasks.push(task);
+            }
+        } else {
+            let [query,doc] = createUpdateQuery(projectID,updateData);
+            let task={
+                updateOne:{
+                    filter:query,
+                    update :doc
+                }
+            };
+            tasks.push(task);
+        }
+    }
+    return tasks;
+
+}
+
+function createUpdateQuery(projectID,data) {
+    let updateKey = data.type+'.ID';
+    let query = {
+        projectID:projectID,
+        ID:data.ID,
+    };
+    query[updateKey] = data.itemID;
+    let udoc = {};
+    for(let property in data.doc){
+        udoc[data.type+'.$.'+property] = data.doc[property];
+    }
+    return [query,udoc];
+}
+
+async function copyInstallationFeeFromLib(projectID,engineering_id) {
+
+    //安装增加费用内嵌文档的方式
+    let engineering = await engineeringModel.findById(engineering_id);
+    let ration_lib = engineering.ration_lib;
+    let installationFeeList = [];
+    for(let rl of ration_lib){
+       let installFeeItems = await installFeeItemModel.find({'rationRepId':rl.id});
+       let installSections = await installSectionModel.find({'rationRepId':rl.id});
+       let positionMap = {};
+       let newInstallationFee = {
+           libID:rl.id,
+           libName:rl.name,
+           projectID:projectID
+       };
+        newInstallationFee.ID = uuidV1();
+       let create = false;
+       if(installFeeItems && installFeeItems.length > 0) {//费用项
+           create = true;
+           let tem_installFeeItem = [];
+           for(let ifee of installFeeItems){
+                let tem_fee ={
+                    feeItem:ifee.feeItem,
+                    feeType:ifee.feeType,
+                    position:ifee.position,
+                    ID:ifee.ID
+                };
+               positionMap[ifee.ID] = ifee.position;//设置选取位置对应表,为给规项赋值
+               tem_installFeeItem.push(tem_fee);
+           }
+           newInstallationFee.installFeeItem = tem_installFeeItem;
+       }
+
+       if(installSections && installSections.length > 0){//章节项
+           create = true;
+           let tem_installSections = [];
+           let tem_feeRules = [];
+           for(let isect of installSections){
+                let tem_sec={
+                    ID:isect.ID,
+                    name:isect.name,
+                    feeItemId:isect.feeItemId
+                };
+                if(isect.feeRule && isect.feeRule.length > 0){//规则项
+                    tem_sec.feeRuleId = isect.feeRule[0].ID; //选中第一个
+                    for(let ifeeR of isect.feeRule){
+                        let tem_feeRule = {
+                            ID: ifeeR.ID,
+                            code: ifeeR.code,
+                            rule: ifeeR.rule,
+                            base: ifeeR.base,
+                            feeRate: ifeeR.feeRate,
+                            labour: ifeeR.labour,
+                            material: ifeeR.material,
+                            machine: ifeeR.machine,
+                            sectionId:isect.ID,
+                            feeItemId:isect.feeItemId,
+                            position:positionMap[isect.feeItemId]
+                        };
+                        tem_feeRules.push(tem_feeRule);
+                    }
+                }else {
+                    tem_sec.feeRuleId = "";
+                }
+               tem_installSections.push(tem_sec);
+           }
+           newInstallationFee.installSection = tem_installSections;
+           newInstallationFee.feeRule = tem_feeRules;
+       }
+       if(create==true){
+           installationFeeList.push(newInstallationFee);
+       }
+    }
+    console.log(installationFeeList);
+    if(installationFeeList.length > 0){
+        await installationFeeModel.insertMany(installationFeeList);
+    }
+}
+
+function getData(projectID, callback) {
+    installationFeeModel.find({'projectID': projectID}, (err, datas) => {
+        if (err) {
+            callback(1, '', null);
+        } else {
+            callback(0, consts.projectConst.INSTALLATION_FEE, datas);
+        }
+    })
+}
+
+

+ 4 - 3
modules/main/facade/labour_coe_facade.js

@@ -112,9 +112,10 @@ function save (data, callback) {
     projectLabourCoesModel.bulkWrite(updateArr)
         .then(function(){
             logger.info(`Project LabourCoe saved successful : ${data.projectID}`);
-            callback(0, '', null);
+            callback('', null);
         })
         .catch(function (err) {
-            logger.err(err);
-            callback(1, err, null)});
+            logger.err('labourCoe save error: ' + err);
+            callback(err, null)
+        });
 }

+ 83 - 0
modules/main/facade/project_facade.js

@@ -0,0 +1,83 @@
+/**
+ * Created by zhang on 2018/1/26.
+ */
+let  projectsModel = require("../../pm/models/project_schema");
+let async_n = require("async");
+let ration_model = require('../models/ration');
+let bill_model = require('../models/bills');
+let consts = require('../models/project_consts');
+let projectConsts = consts.projectConst;
+
+module.exports = {
+    markUpdateProject:markUpdateProject,
+    removeProjectMark:removeProjectMark,
+    updateNodes:updateNodes
+};
+
+function updateNodes(datas,callback) {
+    let tasks = [];
+    for(let node of datas){
+        tasks.push(updateOne(node))
+    }
+    async_n.parallel(tasks, function(err, results) {
+        if (!err){
+            callback(0, '', results);
+        }
+        else{
+            console.log(err);
+            callback(1, 'save project failed'+err.message, null);
+        }
+    });
+    function updateOne(node) {
+        if(node.type == projectConsts.BILLS){
+            return function (asCallback) {
+                bill_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback);
+            }
+        }else if(node.type ==projectConsts.RATION){
+            return function (asCallback) {
+                ration_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback);
+            }
+        }
+    }
+}
+
+//data = {feeRateID:111111,projectID:1245}; type = feeRate
+async function markUpdateProject(data,type) {
+    let tasks=[];
+    let query = {deleteInfo:null};
+    let result = null;
+    if(type=="feeRate"){//更改了费率
+        query['property.feeFile.id'] = data.feeRateID;
+    }
+    if(type=="unitFile"){//更改了单价文件
+        query['property.unitPriceFile.id'] = data.unitFileID;//unitPriceFile
+    }
+    let projects =await projectsModel.find(query);
+    for(let p of projects){
+        if(p.ID!=data.projectID){//当前项目不用更新
+            tasks.push(generateMarkTask(type,p.ID));
+        }
+    }
+    if(tasks.length>0){
+        result = await projectsModel.bulkWrite(tasks);
+    }
+    return result;
+}
+
+async function removeProjectMark(projectID) {
+    return await projectsModel.findOneAndUpdate({ID:projectID},{"$unset":{"changeMark":1}});
+}
+
+function generateMarkTask(value,projectID) {
+    let task = {
+        updateOne:{
+            filter:{
+                ID:projectID
+            },
+            update:{
+                changeMark:value
+            }
+        }
+    };
+    return task
+}

+ 27 - 20
modules/main/facade/quantity_detail_facade.js

@@ -28,7 +28,8 @@ module.exports={
     updateRegex:updateRegex,
     insertRecode:insertRecode,
     deleteRecode:deleteRecode,
-    swapRow:swapRow
+    swapRow:swapRow,
+    deleteByQuery:deleteByQuery
 };
 
 let operationMap={
@@ -44,8 +45,8 @@ async function saveQuantityDetail(datas) {
     let doc = datas;
     doc.ID = uuidV1();
     if(doc.refreshQuantity==false){ //如果选择了不替换工程量,则清空
-        await cleanQuantityDetail(doc);
-        return
+         await cleanQuantityDetail(doc);
+        return {};
     }
     if(doc.hasOwnProperty('regex')){
        return await insertRecodeWithReg(doc);
@@ -112,9 +113,7 @@ async function updateRegex(datas) {
     await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
     resultObjec.refreshList=updateTasks;
     if(datas.query.refreshQuantity==true){
-        let data = {};
-        data.quantity = await summateResults(datas.query,detailList,decimal);
-        data.isFromDetail = 1;
+        let data = await summateResults(datas.query,detailList,decimal);
         node.data = data;
         resultObjec.node = node;
     }
@@ -132,7 +131,7 @@ async function cleanQuantityDetail(doc) {
     }else {
         query.billID = doc.billID;
     }
-    await quantity_detail_model.deleteMany(query);
+    return await quantity_detail_model.deleteMany(query);
 }
 
 
@@ -259,9 +258,7 @@ async function insertRecodeWithReg (doc) {
         let newRecord = await quantity_detail_model.create(doc) ;
         detailList.push(newRecord);
         if(refreshQuantity==true){
-            let data = {};
-            data.quantity = await summateResults(doc,detailList,decimal);
-            data.isFromDetail = 1;
+            let data = await summateResults(doc,detailList,decimal);
             returnObjec.node.data = data;
         }
         returnObjec.newRecord = newRecord;
@@ -283,9 +280,7 @@ async function doIsSummationUpdate(query,doc) {
     await quantity_detail_model.update(query,doc);
     let detailList = await getDatailList(query,returnObjec);
     if(refreshQuantity==true){
-        let data = {};
-        data.quantity = await summateResults(query,detailList,decimal);
-        data.isFromDetail = 1;
+        let data = await summateResults(query,detailList,decimal);
         returnObjec.node.data = data;
     }
     return returnObjec;
@@ -331,6 +326,7 @@ function updateReferenceRecode(index,detailList,updateTasks) {
 
 async function summateResults (query,detailList,decimal) {
     let quantity = 0;
+    let doc = {};
      decimal = decimal?decimal:await decimal_facade.getProjectDecimal(query.projectID);
     for(let d of detailList){
         if(d.isSummation==1){
@@ -340,17 +336,25 @@ async function summateResults (query,detailList,decimal) {
     }
     if(query.hasOwnProperty('rationID')){
         let ration = await  ration_model.findOne({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null});
+        let bill = await bill_model.findOne({'projectID':query.projectID,deleteInfo: null,"ID":ration.billsItemID});
+        let bill_decimal = await decimal_facade.getBillsQuantityDecimal(query.projectID,bill.unit);
+        let bill_quantity = scMathUtil.roundForObj(bill.quantity,bill_decimal);
+        let contain = bill_quantity==0?0:scMathUtil.roundForObj(quantity/bill_quantity,decimal.process);
+        let r_quantity = quantity;
         quantity = getQuantityByUnit(quantity,ration.unit);
         quantity = scMathUtil.roundTo(quantity, -decimal.ration.quantity);
-        await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
+        doc={quantity:quantity,isFromDetail:1,quantityEXP:'GCLMXHJ',contain:contain};
+        await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},doc);
+        doc.r_quantity = r_quantity;
     }else {
         let bill = await bill_model.findOne({'ID':query.billID,'projectID':query.projectID,deleteInfo: null});
         decimal = await decimal_facade.getBillsQuantityDecimal(query.projectID,bill.unit);
-        quantity = getQuantityByUnit(quantity,bill.unit);
+       // quantity = getQuantityByUnit(quantity,bill.unit);
         quantity = scMathUtil.roundTo(quantity, -decimal);
-        await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
+        doc = {quantity:quantity,isFromDetail:1,quantityEXP:'GCLMXHJ'};
+        await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},doc);
     }
-    return quantity
+    return doc
 }
 
 function getQuantityByUnit(quantity,unit) {
@@ -476,9 +480,8 @@ async function deleteRecode(doc) {
     await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
     _.remove(quantity_detail_List,{ID:doc.ID,projectID:doc.projectID});
 
-    let n_data = {};//更新节点信息
-    n_data.quantity = await summateResults(query,quantity_detail_List,decimal);
-    n_data.isFromDetail = 1;
+    //更新节点信息
+    let n_data = await summateResults(query,quantity_detail_List,decimal);
     resultObject.node.data = n_data;
 
     let r_data ={
@@ -541,6 +544,10 @@ function deleteByBill(data) {
     }
 }
 
+async function deleteByQuery(query) {
+   return await  quantity_detail_model.deleteMany(query);
+}
+
 function quantityEditChecking(doc,type,functions) {
     if(doc.updateType == commonConsts.UT_UPDATE&&doc.updateData.hasOwnProperty('isFromDetail')&&doc.updateData.isFromDetail==0) {
         if(type=='bills'){

+ 331 - 0
modules/main/facade/ration_facade.js

@@ -0,0 +1,331 @@
+/**
+ * Created by zhang on 2018/2/9.
+ */
+let mongoose = require('mongoose');
+import SearchDao from '../../complementary_ration_lib/models/searchModel';
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
+let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade")
+let quantity_detail = require("../facade/quantity_detail_facade");
+let ration_glj = mongoose.model('ration_glj');
+let ration_coe = mongoose.model('ration_coe');
+let ration_model = require('../models/ration');
+let bill_model = require('../models/bills');
+let decimal_facade = require('./decimal_facade');
+import installationFeeModel from "../models/installation_fee";
+import rationInstallationModel from "../models/ration_installation";
+const uuidV1 = require('uuid/v1');
+let std_glj_lib_gljList_model = mongoose.model('std_glj_lib_gljList');
+let coeMolde = mongoose.model('std_ration_lib_coe_list');
+let _= require('lodash');
+
+module.exports = {
+    replaceRations: replaceRations,
+    addNewRation:addNewRation
+};
+async function addNewRation(data) {
+    let query = data.itemQuery;
+    let stdRation = null;
+    if(query){
+        let searchDao = new SearchDao();
+        stdRation = await searchDao.getRationItem(query.userID,query.rationRepId,query.code);
+        data.newData.code = query.code;
+    }
+    if(data.brUpdate.length>0){
+        await updateSerialNo(data.brUpdate);
+    }
+    let newRation =await insertNewRation(data.newData,stdRation,data.calQuantity);
+    if(stdRation){
+        return await addRationSubList(stdRation,newRation);
+    }else {
+        return {ration:newRation};
+    }
+}
+
+async function  updateSerialNo(serialNoUpdate){
+    let tasks=[];
+    for(let data of serialNoUpdate){
+        let task={
+            updateOne:{
+                filter:{
+                    ID:data.ID,
+                    projectID:data.projectID
+                },
+                update :{
+                    serialNo:data.serialNo
+                }
+            }
+        };
+        tasks.push(task);
+    }
+    await ration_model.model.bulkWrite(tasks);
+
+}
+
+async function insertNewRation(newData,std,calQuantity) {//插入新的定额
+    if(std){
+        newData.name = std.name;
+        newData.caption = std.caption;
+        newData.unit = std.unit;
+        newData.libID = std.rationRepId;
+        newData.content = std.jobContent;
+        if (std.chapter) {
+            newData.comments = std.chapter.explanation;
+            newData.ruleText = std.chapter.ruleText;
+        }
+        newData.from = std.type === 'complementary' ? 'cpt' : 'std';
+        newData.programID = std.feeType;
+        newData.rationAssList =  createRationAss(std);
+        // calculate ration Quantity
+    }
+    if(calQuantity){
+        await CalculateQuantity(newData,newData.billsItemID,newData.projectID);
+    }
+
+    let newRation = await ration_model.model.create(newData);
+     return newRation;
+
+}
+
+async function replaceRations(userID,data) {
+    let searchDao = new SearchDao();
+    let recodes = [];
+    for(let recode of data.nodeInfo){
+        let stdRation = await searchDao.getRationItem(userID,data.libID,recode.newCode);
+        let newRecode = await replaceRation(recode,stdRation,data.projectID,data.calQuantity);
+        if(newRecode){
+            recodes.push(newRecode);
+        }else {
+            break;
+        }
+    }
+    return recodes;
+}
+
+async function replaceRation(nodeInfo,stdRation,projectID,calQuantity) {
+    if(stdRation){
+        await deleRationSubRecode(projectID,nodeInfo.ID);
+        let newRation = await updateRation(stdRation,nodeInfo.ID,nodeInfo.billsItemID,projectID,calQuantity);//生成并插入新的定额
+        return await addRationSubList(stdRation,newRation);
+    }else {
+        return null;
+    }
+}
+
+async function addRationSubList(stdRation,newRation) {
+    let ration_gljs = await addRationGLJ(stdRation,newRation);
+    let ration_coes = await addRationCoe(stdRation,newRation);
+    let ration_installs =  await addRationInstallFee(stdRation,newRation);
+    return {ration:newRation,ration_gljs:ration_gljs,ration_coes:ration_coes,ration_installs:ration_installs};
+}
+
+async function addRationInstallFee(std,newRation) {
+    let install_fee_list = [];
+    if(std.hasOwnProperty('rationInstList') && std.rationInstList.length > 0){
+        let installFee = await installationFeeModel.findOne({'projectID': newRation.projectID});
+        for(let ri of std.rationInstList){
+            let feeItem = _.find(installFee.installFeeItem,{'ID':ri.feeItemId});
+            let section = _.find(installFee.installSection,{'ID':ri.sectionId});
+            if(feeItem&&section){
+                let tem_r_i = {
+                    libID:installFee.libID,
+                    projectID:newRation.projectID,
+                    rationID:newRation.ID,
+                    feeItemId:feeItem.ID,
+                    sectionId:section.ID,
+                    itemName:feeItem.feeItem,
+                    feeType:feeItem.feeType,
+                    sectionName:section.name,
+                    unifiedSetting:1,
+                    ruleId:''
+                };
+                if(feeItem.isCal==1&&section.feeRuleId&&section.feeRuleId!=''){//勾选记取时并且有规则ID时才读取
+                    let feeRule = _.find(installFee.feeRule,{'ID':section.feeRuleId});
+                    if(feeRule){
+                        tem_r_i.ruleId = feeRule.ID;
+                        tem_r_i.code = feeRule.code;
+                        tem_r_i.rule = feeRule.rule;
+                        tem_r_i.base = feeRule.base;
+                        tem_r_i.feeRate = feeRule.feeRate;
+                        tem_r_i.labour = feeRule.labour;
+                        tem_r_i.material = feeRule.material;
+                        tem_r_i.machine = feeRule.machine;
+                        tem_r_i.position = feeRule.position;
+                        tem_r_i.billID = feeRule.billID;
+                    }
+                }
+                tem_r_i.ID = uuidV1();
+                install_fee_list.push(tem_r_i);
+            }
+        }
+        if(install_fee_list.length>0){
+            await rationInstallationModel.insertMany(install_fee_list);
+        }
+    }
+
+    return install_fee_list;
+}
+
+async function addRationCoe(std,newRation) {
+    let ration_coe_list = [];
+    let seq = 0;
+    if(std.hasOwnProperty('rationCoeList')&&std.rationCoeList.length>0){//添加标准库的工料机
+        for(let sub of std.rationCoeList){
+            let libCoe = await coeMolde.findOne({'libID':std.rationRepId,'ID':sub.ID,"$or": [{"isDeleted": null}, {"isDeleted": false}]});//std.rationRepId;
+            if(libCoe){
+                let newCoe = {};
+                newCoe.ID = uuidV1();
+                newCoe.coeID = sub.ID;
+                newCoe.seq = seq;
+                newCoe.name = libCoe.name;
+                newCoe.content = libCoe.content;
+                newCoe.isAdjust=0;
+                newCoe.coes = libCoe.coes;
+                newCoe.rationID = newRation.ID;
+                newCoe.projectID = newRation.projectID;
+                seq++;
+                ration_coe_list.push(newCoe);
+            }
+        }
+    }
+    let lastCoe ={
+        coeID:-1,
+        name : '自定义系数',
+        content:'人工×1,材料×1,机械×1,主材×1,设备×1',
+        isAdjust:0,
+        seq:seq,
+        rationID : newRation.ID,
+        projectID : newRation.projectID
+    };
+    lastCoe.ID = uuidV1();
+    lastCoe.coes = getCustomerCoeData();
+    ration_coe_list.push(lastCoe);
+    await ration_coe.insertMany(ration_coe_list);
+    return ration_coe_list;
+
+}
+
+function getCustomerCoeData() {
+    var coeList = [];
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'定额'});
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'人工'});
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'材料'});
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'机械'});
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'主材'});
+    coeList.push({ amount:1, operator:'*', gljCode:null, coeType:'设备'});
+    return coeList;
+};
+
+async function addRationGLJ(std,newRation) {
+    let newRationGLJList = [];
+    let rationGLJShowList = [];
+    if(std.hasOwnProperty('rationGljList') && std.rationGljList.length > 0){
+        for(let sub of std.rationGljList){
+            let newGLJ = {};
+            newGLJ.ID = uuidV1();
+            newGLJ.projectID = newRation.projectID;
+            newGLJ.GLJID = sub.gljId;
+            newGLJ.rationID = newRation.ID;
+            newGLJ.billsItemID = newRation.billsItemID;
+            newGLJ.rationItemQuantity = sub.consumeAmt;
+            newGLJ.quantity = sub.consumeAmt;
+            newGLJ.glj_repository_id = std.rationRepId;
+            let std_glj = await std_glj_lib_gljList_model.findOne({'ID':sub.gljId});
+            if(std_glj){
+                newGLJ.name = std_glj.name;
+                newGLJ.code = std_glj.code;
+                newGLJ.original_code = std_glj.code;
+                newGLJ.unit = std_glj.unit;
+                newGLJ.specs = std_glj.specs;
+                newGLJ.basePrice = std_glj.basePrice;
+                newGLJ.shortName = std_glj.shortName;
+                newGLJ.type = std_glj.gljType;
+                newGLJ.repositoryId = std_glj.repositoryId;
+                newGLJ.adjCoe = std_glj.adjCoe;
+               let info = await  ration_glj_facade.getInfoFromProjectGLJ(newGLJ);
+                newGLJ = ration_glj_facade.createNewRecord(info);
+                newRationGLJList.push(newGLJ);
+                rationGLJShowList.push(info);
+            }
+        }
+    }
+    if(newRationGLJList.length>0){
+        await ration_glj.insertMany(newRationGLJList);
+    }
+    return rationGLJShowList;
+}
+
+async function deleRationSubRecode(projectID,rationID) {//删除挂在定额下的数据,如工程量明细,定额工料机等
+    let delete_query={projectID: projectID, rationID: rationID};
+    //删除工程量明细
+    await quantity_detail.deleteByQuery(delete_query) ;
+    await ration_coe.deleteMany(delete_query);//删除附注条件
+    await ration_glj.deleteMany(delete_query);//删除定额工料机
+    await rationInstallationModel.deleteMany(delete_query);//删除安装增加费
+}
+
+async function  updateRation(std,rationID,billsItemID,projectID,calQuantity) {
+    // insertNewRation
+    let ration ={};
+    ration.code = std.code;
+    ration.name = std.name;
+    ration.caption = std.caption;
+    ration.unit = std.unit;
+    ration.libID = std.rationRepId;
+    ration.content = std.jobContent;
+    ration.adjustState = '';
+    ration.isFromDetail=0;
+    ration.isSubcontract=false;
+    ration.fees=[];
+    if (std.chapter) {
+        ration.comments = std.chapter.explanation;
+        ration.ruleText = std.chapter.ruleText;
+    }
+    ration.from = std.type === 'complementary' ? 'cpt' : 'std';
+    ration.programID = std.feeType;
+    ration.rationAssList = createRationAss(std);//生成辅助定额
+    if(calQuantity){
+       await CalculateQuantity(ration,billsItemID,projectID);
+    }
+
+ let unsetObject = {
+     "marketUnitFee":1,
+     'marketTotalFee':1,
+     "maskName":1
+ }
+    let newRation = await ration_model.model.findOneAndUpdate({ID:rationID,projectID:projectID},{"$set":ration,"$unset":unsetObject},{new: true});//;
+    return newRation;
+}
+
+function createRationAss(std) {
+    let  rationAssList = [];//生成辅助定额
+    if(std.hasOwnProperty('rationAssList')&&std.rationAssList.length>0){
+        for(let i=0;i<std.rationAssList.length;i++){
+            let ass = std.rationAssList[i];
+            ass.actualValue = ass.stdValue;
+            rationAssList.push(ass);
+        }
+    }
+    return rationAssList;
+}
+
+async function CalculateQuantity (ration,billsItemID,projectID) {
+    // calculate ration Quantity
+    let decimalObject =await decimal_facade.getProjectDecimal(projectID);
+    let quantity_decimal = (decimalObject&&decimalObject.ration&&decimalObject.ration.quantity)?decimalObject.ration.quantity:3;
+    let pbill = await bill_model.model.findOne({projectID:projectID,ID:billsItemID});
+    let billsQuantity = pbill.quantity ? pbill.quantity : 0;
+    let bill_decimal = await decimal_facade.getBillsQuantityDecimal(projectID,pbill.unit);
+    billsQuantity=scMathUtil.roundForObj(billsQuantity,bill_decimal);
+    ration.quantityEXP="QDL";
+    ration.quantity = scMathUtil.roundForObj(billsQuantity / FilterNumberFromUnit(ration.unit),quantity_decimal);//不管是否打勾都做转换
+    ration.contain =  scMathUtil.roundForObj(ration.quantity/billsQuantity,6);
+};
+
+function FilterNumberFromUnit (unit) {
+    let reg = new RegExp('^[0-9]+');
+    if (reg.test(unit)) {
+        return parseInt(unit.match(reg)[0]);
+    } else {
+        return 1;
+    }
+};

+ 31 - 0
modules/main/facade/ration_installation_facade.js

@@ -0,0 +1,31 @@
+/**
+ * Created by zhang on 2018/2/24.
+ */
+
+import rationInstallationModel from "../models/ration_installation";
+let consts = require('../models/project_consts');
+let projectConsts = consts.projectConst;
+
+module.exports={
+    updateRationInstallation:updateRationInstallation,
+    getData:getData
+};
+
+async function updateRationInstallation(data){
+    if(data.projectID&&data.ID){
+        let result = await rationInstallationModel.findOneAndUpdate({'projectID':data.projectID,'ID':data.ID},data);
+        return result
+    }else {
+        throw  new Error('项目ID或者ID为空,更新失败!');
+    }
+}
+
+function getData(projectID, callback) {
+    rationInstallationModel.find({'projectID': projectID}, (err, datas) => {
+        if (err) {
+            callback(1, '', null);
+        } else {
+            callback(0, consts.projectConst.RATION_INSTALLATION, datas);
+        }
+    })
+}

+ 45 - 7
modules/main/models/bills.js

@@ -12,20 +12,25 @@ let consts = require('./project_consts');
 let projectConsts = consts.projectConst;
 let commonConsts = consts.commonConst;
 let quantity_detial = require('../facade/quantity_detail_facade');
+let projectModel = require('../../pm/models/project_schema');
 
 
 let billsSchema = new Schema({
-    ID: Number,
-    ParentID: Number,
-    NextSiblingID: Number,
+    ID: String,
+    ParentID: String,
+    NextSiblingID: String,
     projectID: Number,
     serialNo: Number,
     chapterID: Number,
+    billsLibId: Number,
     code: String,
     fullCode: String,
+    type:{type: Number,default:4},//1 :大项费用 2:分部 3分项 4清单
+    isAdd:{type: Number,default:0},//1 true 0 false是否新增
     name: String,
     unit: String,
     quantity: String, // Decimal
+    quantityEXP:String,//工程量表达式
     feeRateID:Number,
     feeRate:String,
     isFromDetail:{type: Number,default:0},//1 true 0 false
@@ -60,7 +65,7 @@ let billsSchema = new Schema({
     fees: [subSchema.feesSchema],
     // 标记字段
     flags: [subSchema.flagsSchema],
-    deleteInfo: deleteSchema
+    deleteInfo: deleteSchema,
 });
 
 let bills = db.model("bills", billsSchema);
@@ -89,7 +94,21 @@ class billsModel extends baseModel {
             return function (cb) {
                 switch (doc.updateType) {
                     case commonConsts.UT_UPDATE:
-                        bills.update({projectID: doc.updateData.projectID, ID: doc.updateData.ID,deleteInfo: null}, doc.updateData, cb);
+                        async.parallel([      // CSL,2018.01.10 如果是总造价清单,要将4个汇总金额写到projects表中
+                            function (asyncCB) {
+                                bills.update({projectID: doc.updateData.projectID, ID: doc.updateData.ID,deleteInfo: null}, doc.updateData, asyncCB);
+                            },
+                            function (asyncCB) {
+                                if (doc.updateData.summaryFees){
+                                    // console.log('%%%%%%%%%%%%%%%%%%%  ' + doc.updateData.projectID + ' | ' + JSON.stringify(doc.updateData.summaryFees));
+                                    projectModel.update({ID: doc.updateData.projectID}, {"summaryFees": doc.updateData.summaryFees}, asyncCB);
+                                }else {
+                                    asyncCB(null, {});
+                                };
+                            }
+                        ], function(err,result){
+                            cb(err, {});
+                        });
                         break;
                     case commonConsts.UT_CREATE:
                         bills.create(doc.updateData, cb);
@@ -109,7 +128,7 @@ class billsModel extends baseModel {
         async.parallel(functions, function(err,result){
             let returnData = {
                 moduleName:'bills',
-                data:{}
+                data:result
             };
             callback(err, returnData);
         });
@@ -143,6 +162,25 @@ class billsModel extends baseModel {
            }
        });
     };
-};
+    async updateBill(findSet, updateData) {
+        let update = {};
+        if (!updateData instanceof Array) {
+            return false;
+        }
+
+        for (const tmp of updateData) {
+            if (tmp === undefined) {
+                continue;
+            }
+            update[tmp.field] = tmp.value;
+        }
+
+        if (Object.keys(update).length <= 0) {
+            return false;
+        }
+
+        return bills.update(findSet, update);
+    };
+}
 
 module.exports = new billsModel();

+ 2 - 2
modules/main/models/glj.js

@@ -16,8 +16,8 @@ var GLJSchema = new Schema({
     ID: Number,
     GLJID: Number,
     rGLJID: Number,
-    rationID: Number,
-    billsItemID: Number,
+    rationID: String,
+    billsItemID: String,
     projectID: Number,
     orgRQuantity: String, //Decimal
     rQuantity: String, //Decimal

+ 54 - 0
modules/main/models/installation_fee.js

@@ -0,0 +1,54 @@
+/**
+ * Created by Zhong on 2017/9/13.
+ */
+import mongoose from "mongoose";
+let Schema = mongoose.Schema;
+
+//安装增加费-费用规则
+let feeRuleSchema = new Schema({
+    ID: String,
+    sectionId: String, //分册章节id
+    feeItemId:String,
+    code: String,
+    rule: String,
+    base: String,
+    feeRate: Number,
+    labour: Number,
+    material: Number,
+    machine: Number,
+    position: String,//记取位置
+    billID:String//记取位置对应的清单ID
+});
+
+//安装增加费-分册章节
+let installSectionSchema = new Schema({
+    ID: String,
+    feeItemId: String,
+    feeRuleId: String,
+    name: String
+});
+
+//安装增加费-费用项
+let installFeeItemSchema = new Schema({
+    ID: String,
+    feeItem: String, //费用项
+    feeType: String, //费用类型
+    position: String,//记取位置
+    billID:String,//记取位置对应的清单ID
+    isCal: {type: Number,default:0}//是否记取0:false 1:true
+});
+
+let installationFeeSchema = new Schema({
+    ID:String,
+    libID:Number,
+    libName:String,
+    projectID:String,
+    installFeeItem:[installFeeItemSchema],
+    installSection:[installSectionSchema],
+    feeRule:[feeRuleSchema]
+},{versionKey:false});
+
+let installationFeeModel = mongoose.model("installation_fee",installationFeeSchema,"installation_fee");
+
+
+export{ installationFeeModel as default}

+ 4 - 0
modules/main/models/project.js

@@ -7,6 +7,7 @@ var GLJData = require('./glj');
 var ration_glj_data = require('../../ration_glj/facade/ration_glj_facade');
 var ration_coe_data = require('../../ration_glj/facade/ration_coe_facade');
 var ration_ass_data = require('../../ration_glj/facade/ration_ass_facade');
+let ration_installation = require('../facade/ration_installation_facade');
 var quantity_detail_data = require('../facade/quantity_detail_facade');
 var fee_rate_data = require('../../fee_rates/facade/fee_rates_facade');
 let projCounter = require('./proj_counter_model');
@@ -15,6 +16,7 @@ let projSetting = require('./proj_setting_model');
 var labour_coe_facade = require('../facade/labour_coe_facade');
 var calc_program_facade = require('../facade/calc_program_facade');
 import GLJController from "../../glj/controllers/glj_controller";
+let installation_facade = require('../facade/installation_facade');
 
 
 const ProjectModel = require('../../pm/models/project_model').project;
@@ -32,6 +34,7 @@ moduleMap[projectConsts.RATION] = rationData;
 moduleMap[projectConsts.RATION_GLJ] = ration_glj_data;
 moduleMap[projectConsts.RATION_COE] = ration_coe_data;
 moduleMap[projectConsts.RATION_ASS] = ration_ass_data;
+moduleMap[projectConsts.RATION_INSTALLATION] = ration_installation;
 moduleMap[projectConsts.QUANTITY_DETAIL] = quantity_detail_data;
 moduleMap[projCounter.collectionName] = projCounter;
 moduleMap[projSetting.collectionName] = projSetting;
@@ -40,6 +43,7 @@ moduleMap[projectConsts.FEERATE] = fee_rate_data;
 moduleMap[projectConsts.LABOUR_COE] = labour_coe_facade;
 moduleMap[projectConsts.CALC_PROGRAM] = calc_program_facade;
 moduleMap[projectConsts.PROJECTGLJ] = new GLJController();
+moduleMap[projectConsts.INSTALLATION_FEE] = installation_facade;
 
 var Project = function (){};
 

+ 3 - 1
modules/main/models/project_consts.js

@@ -8,6 +8,7 @@ let projectConst = {
     RATION_GLJ:'ration_glj',
     RATION_COE:'ration_coe',
     RATION_ASS:'ration_ass',
+    RATION_INSTALLATION:'ration_installation',
     QUANTITY_DETAIL:'quantity_detail',
     PROJECTGLJ: 'projectGLJ',
     GLJLIST: 'GLJList',
@@ -16,7 +17,8 @@ let projectConst = {
     // VOLUMEPRICE: 'volume_price',
     FEERATE:'feeRate',
     LABOUR_COE:'labour_coe',
-    CALC_PROGRAM:'calc_program'
+    CALC_PROGRAM:'calc_program',
+    INSTALLATION_FEE:'installation_fee'
 };
 
 let projectConstList = [

+ 17 - 5
modules/main/models/ration.js

@@ -29,14 +29,16 @@ var rationAssItemSchema = mongoose.Schema({
 // 定额、量价、工料机定额 合并存储
 let rationSchema = new Schema({
     // 公用属性部分
-    ID: Number,
+    ID: String,
     projectID: Number,
-    billsItemID: Number,
+    billsItemID: String,
     serialNo: Number,
     code: String,
     name: String,
     unit: String,
     quantity: String,
+    contain:String,//含量
+    quantityEXP:String,//工程量表达式
     programID: Number,
     marketUnitFee: String,
     marketTotalFee: String,
@@ -44,6 +46,8 @@ let rationSchema = new Schema({
     deleteInfo: deleteSchema,
     type: Number,                               // 1 定额、2 量价、3 工料机定额
     subType: Number,                            // 子类型:1人工、201材料、301机械、4主材、5设备
+    from:{type: String,default:'std'},          //std, cpt  来自标准、补充
+    isSubcontract: Boolean,                     // 是否分包
 
     // 定额特有属性:
     libID: Number,
@@ -51,13 +55,22 @@ let rationSchema = new Schema({
     caption: String,
     isFromDetail:{type: Number,default:0},       // 1 true 2 false
     adjustState: String,
-    content: String,
     rationProjName: String,
     comments: String,                           // 说明
     flags: [subSchema.flagsSchema],             // 标记字段
     rationAssList: [rationAssItemSchema],
     content: String,                            // 工作内容
-    ruleText: String                            // 计算规则
+    ruleText: String,                            // 计算规则
+
+    //工料机特有属性
+    projectGLJID:Number,  //项目工料机ID
+    GLJID:Number,//工料机库ID
+    original_code:String, //原始编码
+    specs:String,//规格型号
+    shortName:String,//缩写
+    customQuantity:String,//自定义消耗
+    adjCoe:Number
+
 });
 
 let ration = db.model("ration", rationSchema, "ration");
@@ -81,7 +94,6 @@ class rationModel extends baseModel {
     save (user_id, datas, callback) {
         let functions = [];
         let data;
-
         function saveOne(doc) {
             return function (cb) {
                 switch (doc.updateType) {

+ 33 - 0
modules/main/models/ration_installation.js

@@ -0,0 +1,33 @@
+/**
+ * Created by zhang on 2018/2/8.
+ */
+
+var mongoose = require('mongoose'),
+    Schema = mongoose.Schema;
+
+var ration_installation = new Schema({
+    ID:String,
+    libID:Number,
+    projectID: Number,
+    rationID:String,
+    sectionId: String, //分册章节id
+    feeItemId:String,
+    ruleId:String,
+    itemName:String,
+    sectionName:String,
+    code: String,
+    rule: String,
+    base: String,
+    feeRate: Number,
+    labour: Number,
+    material: Number,
+    machine: Number,
+    feeType: String, //费用类型
+    position: String,//记取位置
+    billID:String,//记取位置对应的清单ID
+    unifiedSetting:{type: Number,default:1},//0:false 1:true  按统一设置
+},{versionKey:false});
+
+let rationInstallationModel = mongoose.model('ration_installation', ration_installation,"ration_installation");
+
+export{ rationInstallationModel as default}

+ 8 - 1
modules/main/routes/bills_route.js

@@ -9,7 +9,14 @@ module.exports = function (app) {
     billsRouter.post('/getData', billsController.getData);
     billsRouter.post('/getItemTemplate', billsController.getItemTemplate);
     billsRouter.post('/allocIDs', billsController.allocIDs);
-    billsRouter.post('/updateCharacterContent', billsController.updateCharacterContent)//特征及内容更新 zhong 2017-9-1
+    billsRouter.post('/updateCharacterContent', billsController.updateCharacterContent);//特征及内容更新 zhong 2017-9-1
+    // 批量更新bill数据
+    billsRouter.post('/updateBill', billsController.updateBill);
+    //删除单个清单,不删除子项
+    billsRouter.post('/singleDelete',billsController.singleDelete);
+    billsRouter.post('/multiDelete',billsController.multiDelete);
+    billsRouter.post('/getSectionInfo', billsController.getSectionInfo);
+    billsRouter.post('/reorganizeFBFX', billsController.reorganizeFBFX);
     app.use('/bills', billsRouter);
 };
 

+ 15 - 0
modules/main/routes/installation_route.js

@@ -0,0 +1,15 @@
+/**
+ * Created by zhang on 2018/2/5.
+ */
+
+let express = require('express');
+let installationController = require('../controllers/installation_controller');
+
+module.exports = function (app) {
+    let installationRouter = express.Router();
+
+    installationRouter.post('/updateInstallationFee', installationController.updateInstallationFee);
+    installationRouter.post('/updateRationInstallation', installationController.updateRationInstallation);
+
+    app.use('/installation',installationRouter);
+};

+ 3 - 0
modules/main/routes/project_route.js

@@ -9,6 +9,9 @@ module.exports = function (app) {
 
     projectRouter.post('/save', projectController.save);
     projectRouter.post('/getData', projectController.getData);
+    projectRouter.post('/markUpdateProject', projectController.markUpdateProject);
+    projectRouter.post('/removeProjectMark', projectController.removeProjectMark);
+    projectRouter.post('/updateNodes', projectController.updateNodes);
 
     app.use('/project',projectRouter);
 };

+ 3 - 1
modules/main/routes/ration_route.js

@@ -6,10 +6,12 @@ let express = require('express');
 module.exports = function (app) {
     let rationRouter = express.Router();
     let rationController = require('../controllers/ration_controller');
-
     rationRouter.post('/getData', rationController.getData);
     rationRouter.post('/getItemTemplate', rationController.getItemTemplate);
     rationRouter.post('/allocIDs', rationController.allocIDs);
+    rationRouter.post('/insertGLJAsRation', rationController.insertGLJAsRation);
+    rationRouter.post('/replaceRations', rationController.replaceRations);
+    rationRouter.post('/addNewRation', rationController.addNewRation);
 
     app.use('/ration', rationRouter);
 };

+ 59 - 2
modules/options/controllers/optionsController.js

@@ -4,7 +4,7 @@
 
 import BaseController from '../../common/base/base_controller';
 import OptionsDao from '../models/optionsModel';
-import optionsTypes from '../models/optionTypes';
+import optionSetting from '../models/optionTypes';
 
 let optionsDao = new OptionsDao();
 class OptionController extends BaseController {
@@ -13,6 +13,63 @@ class OptionController extends BaseController {
         let resJson = {error: 0, message: '', data: []};
         let user_id = req.session.sessionUser.id,
             compilation_id = req.session.sessionCompilation._id;
+        try{
+            resJson.data = await optionsDao.getOptions(user_id, compilation_id);
+            if(!resJson.data){
+                resJson.data = await optionsDao.saveOptions(user_id, compilation_id, optionSetting);
+            }
+        }
+        catch (err){
+            resJson.error = true;
+            resJson.message = '获取失败';
+            resJson.data = null;
+        }
+        res.json(resJson);
+    }
+    //获得特定选项类型的选项
+    async getOptionsByType(req, res){
+        let resJson = {error: null, message: '', data: null};
+        let user_id = req.session.sessionUser.id,
+            compilation_id = req.session.sessionCompilation._id,
+            optsType = req.body.optsType;
+        try{
+            resJson.data = await optionsDao.getOptionsByType(user_id, compilation_id, optsType);
+        }
+        catch (err){
+            resJson.error = true;
+            resJson.message = '获取失败';
+            resJson.data = null;
+        }
+        res.json(resJson);
+    }
+
+    async saveOptions(req, res){
+        let resJson = {error: 0, message: '', data: null};
+        let data = JSON.parse(req.body.data);
+        let user_id = req.session.sessionUser.id,
+            compilation_id = req.session.sessionCompilation._id,
+            optSetting = data.optSetting;
+        try{
+            resJson.data = await optionsDao.saveOptions(user_id, compilation_id, optSetting);
+        }
+        catch (err){
+            resJson.error = true;
+            resJson.message = '保存失败';
+            resJson.data = null;
+        }
+        res.json(resJson);
+    }
+}
+
+
+
+
+/*class OptionController extends BaseController {
+    //获得所有选项类型的选项
+    async getOptions(req, res){
+        let resJson = {error: 0, message: '', data: []};
+        let user_id = req.session.sessionUser.id,
+            compilation_id = req.session.sessionCompilation._id;
         let defaultOpts = {
             GENERALOPTS: {
                 rationQuanACToBillsQuan: true,//自动根据清单工程量填写定额工程量
@@ -82,6 +139,6 @@ class OptionController extends BaseController {
         }
         res.json(resJson);
     }
-}
+}*/
 
 export default OptionController;

+ 57 - 4
modules/options/models/optionTypes.js

@@ -5,9 +5,62 @@
 /*
 * 用户选项设置的选项类型,目前有常规选项
 * */
-
-const optionsTypes = {
-    GENERALOPTS: 'GENERALOPTS'//常规选项:1.自动根据清单工程量填写定额工程量 2.自动根据定额单位转换定额工程量
+const optionSetting = {
+    GENERALOPTS: {
+        rationQuanACToBillsQuan: true,//自动根据清单工程量填写定额工程量
+        rationQuanACToRationUnit: true//自动根据定额单位转换定额工程量
+    },
+    //色彩相关
+    COLOROPTS: {
+        DEFAULT: {
+            backColor: 'White',
+            foreColor: 'Black',
+            stringFont: '15px Arial',
+            numFont: '13px Arial'
+        },
+        DXFY: {
+            backColor: 'default',
+            foreColor: 'default',
+            stringFont: 'bold 15px Arial',
+            numFont: 'bold 13px Arial'
+        },
+        FB: {
+            backColor: '#C4CAFB',
+            foreColor: 'default',
+            stringFont: 'default',
+            numFont: 'default'
+        },
+        UNLEAFBILL: {
+            backColor: '#C4CAFB',
+            foreColor: 'default',
+            stringFont: 'default',
+            numFont: 'default'
+        },
+        FX: {
+            backColor: '#DFE8F9',
+            foreColor: 'default',
+            stringFont: 'default',
+            numFont: 'default'
+        },
+        UNCBBILL: {
+            backColor: '#DFE8F9',
+            foreColor: 'default',
+            stringFont: 'default',
+            numFont: 'default'
+        },
+        CBBILL: {
+            backColor: '#E5F3F2',
+            foreColor: 'default',
+            stringFont: 'default',
+            numFont: 'default'
+        },
+        ZCSB: {
+            backColor: 'default',
+            foreColor: '#4D7BFF',
+            stringFont: 'default',
+            numFont: 'default'
+        }
+    }
 };
 
-export default optionsTypes;
+export default optionSetting;

+ 29 - 1
modules/options/models/optionsModel.js

@@ -12,6 +12,34 @@ class OptionsDao {
     }
 
     async getOptionsByType(user_id, compilation_id, optsType){
+        let rst = await optionsModel.findOne({user_id: user_id, compilation_id: compilation_id});
+        if(rst){
+            return rst.options[optsType] !== undefined && rst.options[optsType] !== null ? rst.options[optsType] : null;
+        }
+    }
+
+    async saveOptions(user_id, compilation_id, optSetting){
+        let optionsData = await optionsModel.find({user_id: user_id, compilation_id: compilation_id});
+        if(optionsData.length === 0){
+            await optionsModel.create({user_id: user_id, compilation_id: compilation_id, options: optSetting});
+        }
+        await optionsModel.update({user_id: user_id, compilation_id: compilation_id}, optSetting);
+        let rst = await optionsModel.find({user_id: user_id, compilation_id: compilation_id});
+        return rst.length > 0 && typeof rst[0].options !== 'undefined' ? rst[0].options : null;
+    }
+}
+
+
+
+
+/*class OptionsDao {
+    async getOptions(user_id, compilation_id){
+        let rst = await optionsModel.find({user_id: user_id, compilation_id: compilation_id});
+        rst = rst.length > 0 && typeof rst[0].options !== 'undefined' ? rst[0].options : null;
+        return rst;
+    }
+
+    async getOptionsByType(user_id, compilation_id, optsType){
         let rst = await optionsModel.find({user_id: user_id, compilation_id: compilation_id});
         if(rst.length > 0){
             let opts = rst[0].options;
@@ -33,6 +61,6 @@ class OptionsDao {
         let rst = await optionsModel.find({user_id: user_id, compilation_id: compilation_id});
         return rst;
     }
-}
+}*/
 
 export default OptionsDao;

+ 10 - 1
modules/options/models/schemas.js

@@ -10,9 +10,18 @@ let Schema = mongoose.Schema;
 let optionSchema = new Schema({
     user_id: String,
     compilation_id: String,
-    options: Array
+    options: {
+        type: Schema.Types.Mixed,
+        default: {}
+    }
 }, {versionKey: false});
 
+/*let optionSchema = new Schema({
+    user_id: String,
+    compilation_id: String,
+    options: Array
+}, {versionKey: false});*/
+
 let optionsModel = mongoose.model('options', optionSchema);
 
 export default optionsModel;

+ 10 - 0
modules/pm/controllers/new_proj_controller.js

@@ -6,6 +6,7 @@ let billsData = require('../../main/models/bills');
 let projCounter = require('../../main/models/proj_counter_model');
 let projSetting = require('../../main/models/proj_setting_model');
 let async = require('async');
+const uuidV1 = require('uuid/v1');
 
 import BillsTemplateModel from "../models/templates/bills_template_model";
 import EngineeringLibModel from "../../users/models/engineering_lib_model";
@@ -18,8 +19,17 @@ module.exports = {
                 let billsTemplateModel = new BillsTemplateModel();
                 let templateData = JSON.stringify(await billsTemplateModel.getTemplateDataForNewProj(property.valuation, property.engineering));
                 let billsDatas = JSON.parse(templateData);
+                let uuidMaping = Object.create(null);
+                uuidMaping['-1'] = -1;
+                //建立uuid-ID映射
+                for(let bill of billsDatas){
+                    uuidMaping[bill.ID] = uuidV1();
+                }
                 billsDatas.forEach(function (template) {
                     template.projectID = newProjID;
+                    template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
+                    template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
+                    template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
                 });
                 billsData.insertData(billsDatas, callback);
             },

+ 29 - 13
modules/pm/controllers/pm_controller.js

@@ -12,6 +12,7 @@ let fee_rate_facade = require("../../fee_rates/facade/fee_rates_facade");
 let billsModel = require('../../main/models/bills').model;
 let rationsModel = require('../../main/models/ration').model;
 let projectModel = require("../models/project_schema");
+let asyncTool = require('async');
 
 //统一回调函数
 let callback = function(req, res, err, message, data){
@@ -43,8 +44,8 @@ module.exports = {
             callback(false);
         });
     },
-    getProjects: function(req, res){
-        ProjectsData.getUserProjects(req.session.sessionUser.ssoId, req.session.sessionCompilation._id, function(err, message, projects){
+    getProjects: async function(req, res){
+         await ProjectsData.getUserProjects(req.session.sessionUser.ssoId, req.session.sessionCompilation._id, function(err, message, projects){
             if (projects) {
                 callback(req, res, err, message, projects);
             } else {
@@ -64,40 +65,55 @@ module.exports = {
     },
     // CSL, 2017-12-14 该方法用于项目属性:提交保存混合型数据,这些数据来自不同的表,包括projects.property、ration、bills、labour_coes.
     updateMixDatas: function(req, res){
-        let callBackInner = function (err, message, data) {
-            if (err === 0) {
-                res.json({error: err, message: message, data: data});
-            } else {
-                res.json({error: err, message: message, data: null});
+        let datas = JSON.parse(req.body.data).mixDataArr;
+        let functions = [];
+
+        function updateFunc(model, cod, doc) {
+            return function (cb) {
+                model.update(cod, doc, cb);
             }
         };
 
-        let datas = JSON.parse(req.body.data).mixDataArr;
+        function updateLC(){
+            return function (cb) {
+                datas.labourCoes.updateData.projectID = datas.projectID;
+                labourCoe.save(datas.labourCoes.updateData, cb);
+            }
+        };
 
         // 项目属性
         if (Object.keys(datas.properties).length > 0){
-            projectModel.update({ID: datas.projectID}, datas.properties, callBackInner);
+            functions.push(updateFunc(projectModel, {ID: datas.projectID}, datas.properties));
         };
 
         // 人工系数
         if (datas.labourCoes.updateData){
-            datas.labourCoes.updateData.projectID = datas.projectID;
-            labourCoe.save(datas.labourCoes.updateData, callBackInner);
+            functions.push(updateLC());
         };
 
         // 清单:每文档doc只存储一条清单,每条清单都必须定位一次文档,无法合并处理
         if (datas.bills.length > 0){
             for (let bill of datas.bills){
-                billsModel.update({projectID: datas.projectID, ID: bill.ID, deleteInfo: null}, bill, callBackInner);
+                functions.push(updateFunc(billsModel, {projectID: datas.projectID, ID: bill.ID, deleteInfo: null}, bill));
             };
         };
 
         // 定额:每文档doc只存储一条定额,每条定额都必须定位一次文档,无法合并处理
         if (datas.rations.length > 0){
             for (let ration of datas.rations){
-                rationsModel.update({projectID: datas.projectID, ID: ration.ID, deleteInfo: null}, ration, callBackInner);
+                functions.push(updateFunc(rationsModel, {projectID: datas.projectID, ID: ration.ID, deleteInfo: null}, ration));
             };
         };
+
+        asyncTool.parallel(functions, function(err, result){
+            {
+                if (!err) {
+                    res.json({error: 0, message: err, data: result});
+                } else {
+                    res.json({error: 1, message: err, data: null});
+                }
+            }
+        });
     },
     updateFiles: async function(req, res){
         let data = JSON.parse(req.body.data);

+ 230 - 109
modules/pm/models/project_model.js

@@ -5,7 +5,15 @@ import mongoose from 'mongoose';
 import async_c from 'async';
 import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
 import UnitPriceFiles from '../../glj/models/schemas/unit_price_file';
-import {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting} from './project_property_template';
+import {
+    defaultDecimal,
+    billsQuantityDecimal,
+    basicInformation,
+    projectFeature,
+    displaySetting,
+    calcOptions
+} from './project_property_template';
+import fixedFlag from '../../common/const/bills_fixed';
 let FeeRateFiles = mongoose.model('fee_rate_file');
 let counter = require("../../../public/counter/counter.js");
 
@@ -14,7 +22,9 @@ let copyProjController = require('../controllers/copy_proj_controller');
 let feeRateFacade = require('../../fee_rates/facade/fee_rates_facade');
 let labourCoeFacade = require('../../main/facade/labour_coe_facade');
 let calcProgramFacade = require('../../main/facade/calc_program_facade');
+let installationFacade = require('../../main/facade/installation_facade');
 let logger = require("../../../logs/log_helper").logger;
+let BillsModel = require("../../main/models/bills").model;
 
 let Projects = require("./project_schema");
 let projectType = {
@@ -28,20 +38,36 @@ let fileType = {
     feeRateFile: 'FeeRateFile'
 };
 
-let ProjectsDAO = function(){};
+let ProjectsDAO = function () {
+};
 
-ProjectsDAO.prototype.getUserProjects = function(userId, compilation, callback){
-    Projects.find({'$or': [{'userID': userId, 'compilation': compilation, 'deleteInfo': null}, {'userID': userId, 'compilation': compilation, 'deleteInfo.deleted': {'$in': [null, false]}}]}, '-_id', function(err, templates){
-        if (err) {
-            callback(1, 'Error', null);
-        } else {
-            callback(0, '', templates);
+ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, callback) {
+    try {
+        let projects = await Projects.find({
+            '$or': [{
+                'userID': userId,
+                'compilation': compilation,
+                'deleteInfo': null
+            }, {'userID': userId, 'compilation': compilation, 'deleteInfo.deleted': {'$in': [null, false]}}]
+        }, '-_id');
+        for (let i = 0, len = projects.length; i < len; i++) {
+            let proj = projects[i];
+            let engineeringCost = await BillsModel.find({
+                projectID: proj.ID,
+                'flags.flag': fixedFlag.ENGINEERINGCOST,
+                'fees.totalFee': {$exists: true}
+            });
+            proj._doc.engineeringCost = engineeringCost.length > 0 ? engineeringCost[0].fees[0].totalFee : 0;
         }
-    });
+        callback(0, '', projects);
+    }
+    catch (err) {
+        callback(1, 'Error', null);
+    }
 };
 
 ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
-    Projects.findOne({userID: userId, ID: ProjId}, '-_id', function(err, template){
+    Projects.findOne({userID: userId, ID: ProjId}, '-_id', function (err, template) {
         if (err) {
             callback(1, '找不到标段数据', null);
         } else {
@@ -50,22 +76,22 @@ ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
     });
 }
 
-ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId, datas, callback){
+ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, i, newProject;
     let updateAll = function (err) {
-            if (!err){
-                updateLength += 1;
-                if (updateLength === datas.length) {
-                    callback(0, '', datas);
-                }
-            } else {
-                hasError = true;
-                console.log(err);
-                callback(1, '提交数据出错.', null);
+        if (!err) {
+            updateLength += 1;
+            if (updateLength === datas.length) {
+                callback(0, '', datas);
             }
-        };
-    if (datas){
-        for (i = 0; i < datas.length && !hasError; i++){
+        } else {
+            hasError = true;
+            console.log(err);
+            callback(1, '提交数据出错.', null);
+        }
+    };
+    if (datas) {
+        for (i = 0; i < datas.length && !hasError; i++) {
             data = datas[i];
             if (data.updateData.name !== undefined) {
                 data.updateData.name = data.updateData.name.trim();
@@ -96,7 +122,9 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                     }
                     data.updateData.property.unitPriceFile.id = addResult.id;
                 }
-                if(data.updateData.projType === projectType.tender){
+                if (data.updateData.projType === projectType.tender) {
+                    //单价文件
+                    data.updateData.property.unitPriceFile.id=parseInt(data.updateData.property.unitPriceFile.id);
                     //小数位数
                     data.updateData.property.decimal = defaultDecimal;
                     //清单工程量精度
@@ -107,10 +135,18 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                     //工程特征
                     data.updateData.property.projectFeature = projectFeature;
                     //呈现选项
-		    data.updateData.property.displaySetting = displaySetting;
-		    
+                    data.updateData.property.displaySetting = displaySetting;
+
                     data.updateData.property.billsCalcMode = 0;
-		    data.updateData.property.zanguCalcMode = 0;
+                    data.updateData.property.zanguCalcMode = 0;
+                    //计算选项
+                    data.updateData.property.calcOptions = calcOptions
+                    //安装增加费
+                    if(parseInt(data.updateData.property.engineering)==4){
+                        await installationFacade.copyInstallationFeeFromLib(data.updateData.ID,data.updateData.property.engineering_id);
+                    }
+
+
                 }
                 newProject = new Projects(data.updateData);
                 // 查找同级是否存在同名数据
@@ -119,9 +155,9 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                     callback(1, '同级目录已存在相同名称数据.', null);
                     return;
                 }
-                if(data.updateData.projType==='Tender'){
-                    let  feeRateFileID = await feeRateFacade.newFeeRateFile(userId, data.updateData);
-                    newProject.property.feeFile = feeRateFileID?feeRateFileID:-1;
+                if (data.updateData.projType === 'Tender') {
+                    let feeRateFileID = await feeRateFacade.newFeeRateFile(userId, data.updateData);
+                    newProject.property.feeFile = feeRateFileID ? feeRateFileID : -1;
 
                     // 新建人工系数文件 CSL, 2017.10.13
                     let lcFile = await labourCoeFacade.newProjectLabourCoe(data.updateData);
@@ -146,22 +182,28 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                 data.updateData['deleteInfo'] = deleteInfo;
                 //Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
                 //update
-                try{
-                    if(data.updateData.projType === projectType.project){
+                try {
+                    if (data.updateData.projType === projectType.project) {
                         let engineerings = await Projects.find({userID: userId, ParentID: data.updateData.ID});
                         let isExist = false;
-                        if(engineerings.length > 0){
-                            for(let j = 0, jLen = engineerings.length; j < jLen; j++){
+                        if (engineerings.length > 0) {
+                            for (let j = 0, jLen = engineerings.length; j < jLen; j++) {
                                 let e_tenders = await Projects.find({userID: userId, ParentID: engineerings[j].ID});
-                                if(e_tenders.length > 0){
+                                if (e_tenders.length > 0) {
                                     isExist = true;
                                     break;
                                 }
                             }
                         }
-                        if(isExist){//fake
-                            await UnitPriceFiles.update({user_id: userId, root_project_id: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
-                            await FeeRateFiles.update({userID: userId, rootProjectID: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                        if (isExist) {//fake
+                            await UnitPriceFiles.update({
+                                user_id: userId,
+                                root_project_id: data.updateData.ID
+                            }, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                            await FeeRateFiles.update({
+                                userID: userId,
+                                rootProjectID: data.updateData.ID
+                            }, {$set: {deleteInfo: deleteInfo}}, {multi: true});
                             await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
                         }
                         else {//true
@@ -171,9 +213,9 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                             await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
                         }
                     }
-                    else if(data.updateData.projType === projectType.engineering){
+                    else if (data.updateData.projType === projectType.engineering) {
                         let tenders = await Projects.find({userID: userId, ParentID: data.updateData.ID});
-                        if(tenders.length > 0){//fake
+                        if (tenders.length > 0) {//fake
                             await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
                         }
                         else {//true
@@ -181,15 +223,15 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
                             await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
                         }
                     }
-                    else if(data.updateData.projType === projectType.tender){//fake
+                    else if (data.updateData.projType === projectType.tender) {//fake
                         await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
                     }
-                    else if(data.updateData.projType === projectType.folder){//true
+                    else if (data.updateData.projType === projectType.folder) {//true
                         await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
                     }
                     else throw '未知文件类型,删除失败!';
                 }
-                catch (error){
+                catch (error) {
                     callback(1, error, null);
                 }
             }
@@ -201,29 +243,38 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, compilationId,
     }
 };
 
-ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback){
+ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback) {
     let updateType = {update: 'update', delete: 'delete'};
     let deleteInfo = Object.create(null);
     deleteInfo.deleted = true;
     deleteInfo.deleteBy = userId;
     deleteInfo.deleteDateTime = new Date();
-    try{
-        for(let i = 0, len = datas.length; i < len; i++){
+    try {
+        for (let i = 0, len = datas.length; i < len; i++) {
             let data = datas[i];
-            if(data.updateType === updateType.update && data.fileType === fileType.unitPriceFile){
+            if (data.updateType === updateType.update && data.fileType === fileType.unitPriceFile) {
                 await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
-                await Projects.update({userID: userId, 'property.unitPriceFile.id': data.updateData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}});
+                await Projects.update({
+                    userID: userId,
+                    'property.unitPriceFile.id': data.updateData.id
+                }, {$set: {'property.unitPriceFile.name': data.updateData.name}});
             }
-            else if(data.updateType === updateType.update && data.fileType === fileType.feeRateFile){
+            else if (data.updateType === updateType.update && data.fileType === fileType.feeRateFile) {
                 await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
-                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.ID}, {$set: {'property.feeFile.name': data.updateData.name}});
+                await Projects.update({
+                    userID: userId,
+                    'property.feeFile.id': data.updateData.ID
+                }, {$set: {'property.feeFile.name': data.updateData.name}});
             }
-            else if(data.updateType === updateType.delete && data.fileType === fileType.unitPriceFile){
+            else if (data.updateType === updateType.delete && data.fileType === fileType.unitPriceFile) {
                 data.updateData.deleteInfo = deleteInfo;
                 await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
-                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.id}, {$set: {'property.feeFile.name': data.updateData.name}});
+                await Projects.update({
+                    userID: userId,
+                    'property.feeFile.id': data.updateData.id
+                }, {$set: {'property.feeFile.name': data.updateData.name}});
             }
-            else if(data.updateType === updateType.delete && data.fileType === fileType.feeRateFile){
+            else if (data.updateType === updateType.delete && data.fileType === fileType.feeRateFile) {
                 data.updateData.deleteInfo = deleteInfo;
                 await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
             }
@@ -231,7 +282,7 @@ ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback)
         }
         callback(false, '删除成功', null);
     }
-    catch(error){
+    catch (error) {
         callback(true, '删除失败', null);
     }
 };
@@ -239,7 +290,7 @@ ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback)
 ProjectsDAO.prototype.copyUserProjects = function (userId, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, tempType = 1, i;
     let updateAll = function (err) {
-        if (!err){
+        if (!err) {
             updateLength += 1;
             if (updateLength === datas.length) {
                 callback(0, '', datas);
@@ -274,7 +325,7 @@ ProjectsDAO.prototype.copyUserProjects = function (userId, datas, callback) {
     }
 };
 
-ProjectsDAO.prototype.rename = async function (userId, compilationId, data, callback){
+ProjectsDAO.prototype.rename = async function (userId, compilationId, data, callback) {
     try {
         if (data.id === undefined || data.id === '') {
             throw '数据错误!';
@@ -289,8 +340,8 @@ ProjectsDAO.prototype.rename = async function (userId, compilationId, data, call
             throw '同级目录已存在相同名称数据';
         }
 
-        Projects.update({userID: userId, ID: data.id}, {name: data.newName}, function(err){
-            if (err){
+        Projects.update({userID: userId, ID: data.id}, {name: data.newName}, function (err) {
+            if (err) {
                 throw '项目不存在';
             }
         });
@@ -301,10 +352,10 @@ ProjectsDAO.prototype.rename = async function (userId, compilationId, data, call
     callback(0, '');
 };
 
-ProjectsDAO.prototype.beforeOpenProject = function (userId, projectId, updateData, callback){
+ProjectsDAO.prototype.beforeOpenProject = function (userId, projectId, updateData, callback) {
     updateData['recentDateTime'] = new Date();
-    Projects.update({userID: userId, ID: projectId}, updateData, function(err){
-        if (err){
+    Projects.update({userID: userId, ID: projectId}, updateData, function (err) {
+        if (err) {
             callback(1, '项目不存在.');
         } else {
             callback(0, '');
@@ -337,28 +388,28 @@ ProjectsDAO.prototype.getProject = function (key, callback) {
     }
 };
 
-ProjectsDAO.prototype.getProjectsByIds = async function (userId, compilationId, ids){
+ProjectsDAO.prototype.getProjectsByIds = async function (userId, compilationId, ids) {
     return await Projects.find({userID: userId, compilation: compilationId, ID: {$in: ids}});
 };
 
-ProjectsDAO.prototype.getGCFiles = async function (fileType, userID){
+ProjectsDAO.prototype.getGCFiles = async function (fileType, userID) {
     let rst;
-    if(fileType === 'UnitPriceFile'){
+    if (fileType === 'UnitPriceFile') {
         let unitPriceFileModel = new UnitPriceFileModel();
         rst = await unitPriceFileModel.getGCUnitPriceFiles(userID);
     }
-    else if(fileType === 'FeeRateFile'){
+    else if (fileType === 'FeeRateFile') {
         rst = await feeRateFacade.getGCFeeRateFiles(userID);
     }
     else {
         let isExist = false;
-        for(let type in projectType){
-            if(projectType[type] === fileType) {
+        for (let type in projectType) {
+            if (projectType[type] === fileType) {
                 isExist = true;
                 break;
             }
         }
-        if(!isExist) throw '不存在此项目类型!';
+        if (!isExist) throw '不存在此项目类型!';
         rst = await Projects.find({userID: userID, projType: fileType, 'deleteInfo.deleted': true});
     }
     return rst;
@@ -366,55 +417,55 @@ ProjectsDAO.prototype.getGCFiles = async function (fileType, userID){
 
 ProjectsDAO.prototype.getFirstNodeID = async function (userID, pid) {
     let nodes = await Projects.find({userID: userID, ParentID: pid, deleteInfo: null});
-    if(nodes.length === 0){
+    if (nodes.length === 0) {
         return -1;
     }
     else {
         let prefix = 'ID_';
         let chain = Object.create(null);
-        for(let i = 0, len = nodes.length; i < len; i++){
+        for (let i = 0, len = nodes.length; i < len; i++) {
             let nodeDoc = nodes[i]._doc;
             let node = Object.create(null);
             node.ID = nodeDoc.ID;
             node.NextSiblingID = nodeDoc.NextSiblingID;
             chain[prefix + node.ID] = node;
         }
-        for(let i =0, len = nodes.length; i < len; i++){
+        for (let i = 0, len = nodes.length; i < len; i++) {
             let nodeDoc = nodes[i]._doc;
             let next = nodeDoc.NextSiblingID > 0 ? chain[prefix + nodeDoc.NextSiblingID] : null;
             chain[prefix + nodeDoc.ID].next = next;
-            if(next){
+            if (next) {
                 next.pre = chain[prefix + nodeDoc.ID]
             }
         }
-        for(let node in chain){
+        for (let node in chain) {
             let pre = chain[node].pre || null;
-            if(!pre){
+            if (!pre) {
                 return chain[node].ID;
             }
         }
     }
 };
 
-ProjectsDAO.prototype.recGC = async function(userID, datas, callback){
+ProjectsDAO.prototype.recGC = async function (userID, datas, callback) {
     let functions = [];
     let updateDatas = [];
-    for(let i = 0, len = datas.length; i < len; i++){
-        if(datas[i].findData.ParentID !== undefined && datas[i].findData.NextSiblingID === -1 && !datas[i].findData.deleteInfo){//维护项目管理树
+    for (let i = 0, len = datas.length; i < len; i++) {
+        if (datas[i].findData.ParentID !== undefined && datas[i].findData.NextSiblingID === -1 && !datas[i].findData.deleteInfo) {//维护项目管理树
             let findNode = await Projects.find(datas[i].findData);
-            if(findNode.length > 0){
+            if (findNode.length > 0) {
                 datas[i].findData = Object.create(null);
                 datas[i].findData.ID = findNode[0].ID;
                 updateDatas.push(datas[i]);
             }
         }
         else {
-            if(datas[i].updateType === projectType.project && datas[i].updateData.NextSiblingID === undefined){//则为待查询NextSiblingID,前端无法查询
+            if (datas[i].updateType === projectType.project && datas[i].updateData.NextSiblingID === undefined) {//则为待查询NextSiblingID,前端无法查询
                 let projData = await Projects.find({userID: userID, ID: datas[i].findData.ID});//建设项目原本可能属于某文件夹、文件夹的存在需要判断
                 let projPid = projData[0].ParentID;
-                if(projPid !== -1){
+                if (projPid !== -1) {
                     let projFolder = await Projects.find({userID: userID, ID: projPid});
-                    if(projFolder.length === 0){//文件夹已不存在
+                    if (projFolder.length === 0) {//文件夹已不存在
                         projPid = -1;
                         datas[i].updateData.ParentID = -1;
                     }
@@ -425,45 +476,50 @@ ProjectsDAO.prototype.recGC = async function(userID, datas, callback){
             updateDatas.push(datas[i])
         }
     }
-    for(let i = 0, len = updateDatas.length; i < len; i ++){
-        functions.push((function(data){
+    for (let i = 0, len = updateDatas.length; i < len; i++) {
+        functions.push((function (data) {
                 return function (cb) {
-                    if(data.updateType === fileType.unitPriceFile){
+                    if (data.updateType === fileType.unitPriceFile) {
                         UnitPriceFiles.update({id: parseInt(data.findData.id)}, data.updateData, function (err) {
-                            if(err) cb(err);
+                            if (err) cb(err);
                             else {
-                                Projects.update({userID: userID, 'property.unitPriceFile.id': data.findData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}}, function (err) {
-                                    if(err) cb(err);
+                                Projects.update({
+                                    userID: userID,
+                                    'property.unitPriceFile.id': data.findData.id
+                                }, {$set: {'property.unitPriceFile.name': data.updateData.name}}, function (err) {
+                                    if (err) cb(err);
                                     else cb(false);
                                 });
                             }
                         })
                     }
-                    else if(data.updateType === fileType.feeRateFile){
+                    else if (data.updateType === fileType.feeRateFile) {
                         FeeRateFiles.update(data.findData, data.updateData, function (err) {
-                            if(err) cb(err);
+                            if (err) cb(err);
                             else {
-                                Projects.update({userID: userID, 'property.feeFile.id': data.findData.ID}, {$set: {'property.feeFile.name': data.updateData.name}}, function (err) {
-                                    if(err) cb(err);
+                                Projects.update({
+                                    userID: userID,
+                                    'property.feeFile.id': data.findData.ID
+                                }, {$set: {'property.feeFile.name': data.updateData.name}}, function (err) {
+                                    if (err) cb(err);
                                     else cb(false);
                                 });
                             }
                         });
                     }
-                    else{
-                        if(data){
+                    else {
+                        if (data) {
                             Projects.update(data.findData, data.updateData, function (err) {
-                                if(err)cb(err);
+                                if (err) cb(err);
                                 else cb(false);
                             });
                         }
                     }
                 }
-            }
-        )(updateDatas[i]));
+            })(updateDatas[i]));
     }
     async_c.parallel(functions, function (err, results) {
-        if(err) callback(err, 'fail', null);
+        if (err) callback(err, 'fail', null);
         else callback(0, 'success', null);
     });
 };
@@ -474,12 +530,18 @@ ProjectsDAO.prototype.recGC = async function(userID, datas, callback){
  * @param {Object} data
  * @return {Boolean}
  */
-ProjectsDAO.prototype.isExist = async function(userId, compilationId, name, parentID) {
+ProjectsDAO.prototype.isExist = async function (userId, compilationId, name, parentID) {
     parentID = parseInt(parentID);
     if (name === '' || isNaN(parentID)) {
         return true;
     }
-    let condition = {userID: userId, compilation: compilationId, ParentID: parentID, name: name, "$or":[{deleteInfo: null}, {"deleteInfo.deleted": false}]};
+    let condition = {
+        userID: userId,
+        compilation: compilationId,
+        ParentID: parentID,
+        name: name,
+        "$or": [{deleteInfo: null}, {"deleteInfo.deleted": false}]
+    };
     let count = await Projects.count(condition);
     return count > 0;
 };
@@ -490,7 +552,7 @@ ProjectsDAO.prototype.isExist = async function(userId, compilationId, name, pare
  * @param {Number} projectId
  * @return {Promise}
  */
-ProjectsDAO.prototype.getTenderByProjectId = async function(projectId) {
+ProjectsDAO.prototype.getTenderByProjectId = async function (projectId) {
     let result = [];
     // 首先获取对应的单位工程id
     let engineeringData = await Projects.find({ParentID: projectId});
@@ -499,7 +561,7 @@ ProjectsDAO.prototype.getTenderByProjectId = async function(projectId) {
     }
 
     let engineeringIdList = [];
-    for(let tmp of engineeringData) {
+    for (let tmp of engineeringData) {
         engineeringIdList.push(tmp.ID);
     }
 
@@ -509,7 +571,7 @@ ProjectsDAO.prototype.getTenderByProjectId = async function(projectId) {
         return result;
     }
 
-    for(let tmp of tenderData) {
+    for (let tmp of tenderData) {
         result.push(tmp.ID);
     }
 
@@ -522,7 +584,7 @@ ProjectsDAO.prototype.getTenderByProjectId = async function(projectId) {
  * @param {Number} unitPriceFileId
  * @return {Promise}
  */
-ProjectsDAO.prototype.getTenderByUnitPriceFileId = async function(unitPriceFileId) {
+ProjectsDAO.prototype.getTenderByUnitPriceFileId = async function (unitPriceFileId) {
     let result = [];
 
     unitPriceFileId = parseInt(unitPriceFileId);
@@ -542,7 +604,7 @@ ProjectsDAO.prototype.getTenderByUnitPriceFileId = async function(unitPriceFileI
  * @param {Number} projectId
  * @return {Promise}
  */
-ProjectsDAO.prototype.getUnitPriceFileId = async function(projectId) {
+ProjectsDAO.prototype.getUnitPriceFileId = async function (projectId) {
     let result = 0;
     let projectData = await Projects.findOne({ID: projectId});
     if (projectData === null) {
@@ -559,12 +621,12 @@ ProjectsDAO.prototype.getUnitPriceFileId = async function(projectId) {
  * @param {Number} userId
  * @return {Promise}
  */
-ProjectsDAO.prototype.getUserProjectData = async function(userId) {
+ProjectsDAO.prototype.getUserProjectData = async function (userId) {
     let projectList = await Projects.find({
         '$or': [
             {'userID': userId, 'deleteInfo': null, projType: 'Project'},
             {'userID': userId, 'deleteInfo.deleted': {'$in': [null, false]}, projType: 'Project'}
-            ]
+        ]
     }, {_id: 0, name: 1, ID: 1});
 
     return projectList;
@@ -577,7 +639,7 @@ ProjectsDAO.prototype.getUserProjectData = async function(userId) {
  * @param {Object} changeInfo
  * @return {Promise}
  */
-ProjectsDAO.prototype.changeUnitPriceFileInfo = async function(projectId, changeInfo) {
+ProjectsDAO.prototype.changeUnitPriceFileInfo = async function (projectId, changeInfo) {
     projectId = parseInt(projectId);
     if (isNaN(projectId) || projectId <= 0) {
         return false;
@@ -587,8 +649,67 @@ ProjectsDAO.prototype.changeUnitPriceFileInfo = async function(projectId, change
     return result.ok === 1;
 };
 
-module.exports ={
-    project: new ProjectsDAO(),
+
+/**
+ * 更新项目属性
+ *
+ * @param {Number} projectId - 项目id
+ * @param {Object} propertyData - 项目属性数据
+ * @return {Promise}
+ */
+ProjectsDAO.prototype.updateProjectProperty = async function(projectId, propertyData) {
+    projectId = parseInt(projectId);
+    if (isNaN(projectId) || projectId <= 0 || propertyData.property === undefined || propertyData.data === undefined) {
+        return false;
+    }
+    const updateData = {};
+    updateData["property." + propertyData.property] = propertyData.data;
+    let result = await Projects.update({ID: projectId}, updateData);
+
+    return result.ok === 1;
+};
+
+// CSL, 2018-01-11 获取指定ID节点(如单项工程、建设项目)下所有单位工程的各项汇总金额,用于报表计算汇总。
+ProjectsDAO.prototype.getSummaryFees = async function (ID) {
+    async function getProject(ID){
+        return await Projects.findOne({'ID': ID, deleteInfo: null}, need);
+    };
+    async function getChildrenDocs(IDs){
+        return await Projects.find({'ParentID': {"$in":IDs}, deleteInfo: null}, need);
+    };
+    async function getEgrIDs(ID){
+        let _docs = await Projects.find({'ParentID': ID, deleteInfo: null}, ['ID', '-_id']);
+        let arr = [];
+        for (let doc of _docs){
+            arr.push(doc.ID);
+        };
+        return arr;
+    };
+
+    let need = ['ID', 'ParentID', 'NextSiblingID', 'name', 'projType',
+        'fullFolder', 'summaryFees', '-_id'];
+    let _doc = await getProject(ID);
+    if (!_doc) return _doc;
+
+    if (_doc.projType.sameText('Engineering')){
+        return await getChildrenDocs([ID]);
+    }
+    else if (_doc.projType.sameText('Project')){
+        let eIDs = await getEgrIDs(ID);
+        return await getChildrenDocs(eIDs);
+    }
+    else{
+        return _doc;
+    };
+};
+
+ProjectsDAO.prototype.updateUnitFileToProject=async function(projectID,unitFile){
+    return await Projects.findOneAndUpdate({'ID':projectID},{'property.unitPriceFile':unitFile});
+}
+
+
+
+module.exports ={    project: new ProjectsDAO(),
     projType: projectType,
     fileType: fileType
 };

+ 6 - 1
modules/pm/models/project_property_template.js

@@ -16,6 +16,11 @@ const displaySetting = {
     disPlayMainMaterial:true
 }
 
+const calcOptions={
+    calc_main:false,
+    calc_add:true,
+    calc_est:true
+}
 /*
 * 单位工程清单工程量精度模板
 * */
@@ -123,4 +128,4 @@ const projectFeature = [
     {dispName: '门窗材料及装饰', key: 'doorsWindowsMaterial', value: ''}
 ];
 
-export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting};
+export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting,calcOptions};

+ 9 - 1
modules/pm/models/project_schema.js

@@ -7,6 +7,7 @@ let Schema = mongoose.Schema;
 let deleteSchema = require('../../../public/models/delete_schema');
 
 let collectionName = 'projects';
+
 let ProjectSchema = new Schema({
     "ID": Number,
     "ParentID": Number,
@@ -22,7 +23,14 @@ let ProjectSchema = new Schema({
     "property": {
         type: Schema.Types.Mixed,
         default: {}
-    }
+    },
+    "summaryFees":{
+        totalFee: String,
+        estimateFee: String,
+        safetyFee: String,
+        chargeFee: String
+    },
+    "changeMark":String//更新标记  feeRate:费率文件发生了改变,unitFile 单件文件发生了改变
 });
 
 module.exports = mongoose.model(collectionName, ProjectSchema);

+ 2 - 2
modules/pm/models/templates/bills_template_model.js

@@ -25,7 +25,7 @@ class BillsTemplateModel extends BaseModel {
      */
     async getTemplateData (valuationId, engineering) {
         // 筛选字段
-        let field = {_id: 1, valuationId: 1, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let field = {_id: 1, valuationId: 1, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1, calcBase: 1};
         let data = await this.findDataByCondition({valuationId: valuationId, engineering: engineering}, field, false);
 
         return data === null ? [] : data;
@@ -39,7 +39,7 @@ class BillsTemplateModel extends BaseModel {
      */
     async getTemplateDataForNewProj (valuationId, engineering) {
         // 筛选字段
-        let field = {_id: 0, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let field = {_id: 0, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1,type:1, calcBase: 1};
         let data = await this.findDataByCondition({valuationId: valuationId, engineering: engineering}, field, false);
 
         return data === null ? [] : data;

+ 3 - 1
modules/pm/models/templates/schemas/bills_template.js

@@ -30,7 +30,9 @@ let BillsTemplateSchema = {
     // 所属计价ID
     valuationId: String,
     // 工程专业
-    engineering: Number
+    engineering: Number,
+    type:Number,
+    calcBase: String
 };
 
 let model = mongoose.model(collectionName, new Schema(BillsTemplateSchema, {versionKey: false, collection: collectionName}));

+ 11 - 7
modules/ration_glj/facade/glj_calculate_facade.js

@@ -20,7 +20,7 @@ module.exports={
 //辅助定额调整、替换工料机、标准附注条件调整、添加工料机、自定义消耗量(包括删除工料机)、自定义乘系数、市场单价调整
 let stateSeq ={
     ass:1,
-    replase:2,
+    replace:2,
     coe:3,
     add:4,
     cusQuantity:5,
@@ -41,6 +41,9 @@ async function calculateQuantity(query,noNeedCal){
          let assList=[];
          let assRation = null;
          let adjustState=[];
+         if(!impactRation){//如果定额不存在或者已删除,返回空
+             return null;
+         }
          if(impactRation._doc.hasOwnProperty("rationAssList")&&impactRation.rationAssList.length>0){
              for(let i=0;i<impactRation.rationAssList.length;i++){
                  let times = calculateTimes(impactRation.rationAssList[i]);
@@ -48,8 +51,8 @@ async function calculateQuantity(query,noNeedCal){
                      assRation = await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:impactRation.rationAssList[i].assistCode});
                      assList.push({times:times,assRation:assRation})
                      adjustState.push({index:stateSeq.ass,content:impactRation.rationAssList[i].name+" "+impactRation.rationAssList[i].actualValue+" : +"+impactRation.rationAssList[i].assistCode+"x"+times});
-                     }
                  }
+             }
          }
          for(let i =0;i<gljList.length;i++ ){
              let r = await calculateQuantityPerGLJ(gljList[i],i,coeList,assList,adjustState,noNeedCal);
@@ -101,16 +104,17 @@ async function calculateQuantityPerGLJ(glj,index,coeList,assList,adjustState,noN
     };
     try {
         if(noNeedCal==null){
-            if(!glj._doc.hasOwnProperty('customQuantity')||glj.customQuantity==null){
+            if(!glj._doc.hasOwnProperty('customQuantity')||glj.customQuantity==null||glj.customQuantity==""){
                 quantity =scMathUtil.roundTo(parseFloat(glj.rationItemQuantity),-decimal);
-                quantity =calculateAss(quantity,assList,glj);
+                quantity =scMathUtil.roundTo(calculateAss(quantity,assList,glj),-decimal);
                 quantity = calculateQuantityByCoes(quantity,coeList,glj);
             }else {
                 quantity = glj.customQuantity;
                 result.doc.customQuantity = glj.customQuantity;
             }
             let customerCoe = _.last(coeList);
-            if(customerCoe.isAdjust==1){
+            if(customerCoe&&customerCoe.isAdjust==1){
+                quantity = scMathUtil.roundToString(quantity,decimal);
                 quantity = calculateQuantityByCustomerCoes(quantity,customerCoe,glj);
             }
             result.doc.quantity =scMathUtil.roundToString(quantity,decimal);
@@ -139,8 +143,8 @@ function calculateAss(quantity,assList,glj) {
 
 function generateAdjustState(glj,coeList,adjustState,index,quantity) {
    //替换工料机 and 添加工料机
-    if(glj._doc.createType=='replace'){
-        adjustState.push({index:stateSeq.replase,content:glj.rcode+'换'+glj.code});
+    if(glj._doc.createType=='replace'&&glj.rcode!=glj.code){
+        adjustState.push({index:stateSeq.replace,content:glj.rcode+'换'+glj.code});
     }else if(glj._doc.createType=='add'){
         adjustState.push({index:stateSeq.add,content:'添'+glj.code+'量'+parseFloat(quantity)});
     }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 489 - 401
modules/ration_glj/facade/ration_glj_facade.js


+ 2 - 2
modules/ration_glj/models/quantity_detail.js

@@ -8,8 +8,8 @@ var mongoose = require('mongoose'),
 var quantity_detail = new Schema({
     ID:String,
     projectID: Number,
-    rationID:Number,
-    billID:Number,
+    rationID:String,
+    billID:String,
     name:String,
     regex:String,
     result:Number,

+ 1 - 1
modules/ration_glj/models/ration_coe.js

@@ -17,7 +17,7 @@ var coeListSchema = mongoose.Schema({
     ID: String,                         // 系数ID(流水号ID)
     name: String,                       // 名称
     content: String,                    // 说明
-    rationID:Number,
+    rationID:String,
     projectID:Number,
     coeID:Number,
     isAdjust:Number, //0不调整,1调整

+ 2 - 2
modules/ration_glj/models/ration_glj.js

@@ -9,7 +9,7 @@ var ration_glj = new Schema({
     GLJID:Number,
     repositoryId:Number,
     projectID: Number,
-    rationID:Number,
+    rationID:String,
     projectGLJID:Number,
     name:String,
     code:String,
@@ -22,7 +22,7 @@ var ration_glj = new Schema({
     specs:String,
     unit:String,
     shortName:String,
-    billsItemID: Number,
+    billsItemID: String,
     type:Number,
     // 调整系数ID
     adjCoe: Number,

+ 48 - 18
modules/ration_glj/models/ration_glj_temp.js

@@ -40,30 +40,48 @@ var rationAssItemSchema = mongoose.Schema({
 
 
 let rationSchema = new Schema({
-    ID: Number,
+    // 公用属性部分
+    ID: String,
     projectID: Number,
-    billsItemID: Number,
+    billsItemID: String,
     serialNo: Number,
-    libID: Number,
     code: String,
     name: String,
-    maskName: String,
-    caption: String,
     unit: String,
-    quantity: String, // Decimal
-    isFromDetail:{type: Number,default:0},//1 true 2 false
+    quantity: String,
+    contain:String,//含量
+    quantityEXP:String,//工程量表达式
     programID: Number,
-    adjustState: String,
-    content: String,
-    rationProjName: String,
-    comments: String,
-    // 费用字段
+    marketUnitFee: String,
+    marketTotalFee: String,
     fees: [subSchema.feesSchema],
-    // 标记字段
-    flags: [subSchema.flagsSchema],
     deleteInfo: deleteSchema,
-    rationAssList: [rationAssItemSchema]
-});
+    type: Number,                               // 1 定额、2 量价、3 工料机定额
+    subType: Number,                            // 子类型:1人工、201材料、301机械、4主材、5设备
+
+    // 定额特有属性:
+    libID: Number,
+    maskName: String,
+    caption: String,
+    isFromDetail:{type: Number,default:0},       // 1 true 2 false
+    adjustState: String,
+    rationProjName: String,
+    comments: String,                           // 说明
+    flags: [subSchema.flagsSchema],             // 标记字段
+    rationAssList: [rationAssItemSchema],
+    content: String,                            // 工作内容
+    ruleText: String,                            // 计算规则
+
+    //工料机特有属性
+    projectGLJID:Number,  //项目工料机ID
+    GLJID:Number,//工料机库ID
+    original_code:String, //原始编码
+    specs:String,//规格型号
+    shortName:String,//缩写
+    customQuantity:String,//自定义消耗
+    from:{type: String,default:'std'}//std, cpt  来自标准工料机库、补充工料机库
+
+},{versionKey:false});
 
 mongoose.model("ration", rationSchema, "ration");
 
@@ -104,6 +122,12 @@ var rationAssItemSchema = mongoose.Schema({
     maxValue: String
 }, { _id: false });
 
+//定额安装增加费用
+let rationInstSchema = mongoose.Schema({
+    feeItemId: String,
+    sectionId: String
+},{_id: false});
+
 var rationItemSchema = mongoose.Schema({
     ID:Number,
     code: String,
@@ -114,21 +138,27 @@ var rationItemSchema = mongoose.Schema({
     rationRepId: Number,
     caption: String,
     feeType: Number,
+    jobContent: String,
+    annotation: String,
     rationGljList: [rationGljItemSchema],
     rationCoeList: Array,
-    rationAssList: [rationAssItemSchema]
+    rationAssList: [rationAssItemSchema],
+    rationInstList: [rationInstSchema]
 });
 mongoose.model("std_ration_lib_ration_items",rationItemSchema, "std_ration_lib_ration_items");
 
 let billsSchema = new Schema({
-    ID: Number,
+    ID: String,
     ParentID: Number,
     NextSiblingID: Number,
     projectID: Number,
     serialNo: Number,
     chapterID: Number,
+    billsLibId: Number,//清单库ID
     code: String,
     fullCode: String,
+    type:Number,
+    isAdd:{type: Number,default:0},//1 true 0 false是否新增
     name: String,
     unit: String,
     quantity: String, // Decimal

+ 3 - 2
modules/ration_repository/controllers/ration_controller.js

@@ -2,7 +2,7 @@
  * Created by Tony on 2017/5/2.
  */
 
-var rationItem = require('../models/ration_item');
+var rationItem = require('../models/ration_item').Dao;
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
 };
@@ -10,7 +10,8 @@ var callback = function(req, res, err, message, data){
 module.exports = {
     getRationItemsBySection: function(req, res){
         var sectionId = req.body.sectionID;
-        rationItem.getRationItemsBySection(sectionId, function(err, message, rst){
+        var rationRepId = req.body.rationRepId;
+        rationItem.getRationItemsBySection(rationRepId, sectionId, function(err, message, rst){
             if (err) {
                 callback(req, res, err, message, null);
             } else {

+ 1 - 1
modules/ration_repository/controllers/ration_repository_controller.js

@@ -1,7 +1,7 @@
 /**
  * Created by Tony on 2017/4/20.
  */
-var rationRepository = require("../models/repository_map");
+var rationRepository = require("../models/repository_map").Dao;
 
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});

+ 1 - 1
modules/ration_repository/controllers/ration_section_tree_controller.js

@@ -2,7 +2,7 @@
  * Created by Tony on 2017/4/21.
  */
 
-var rationChapterTreeData = require('../models/ration_section_tree');
+var rationChapterTreeData = require('../models/ration_section_tree').Dao;
 var callback = function(req,res,err,message, data){
     res.json({error: err, message: message, data: data});
 }

+ 2 - 2
modules/ration_repository/controllers/search_controller.js

@@ -1,8 +1,8 @@
 /**
  * Created by Mai on 2017/6/5.
  */
-var rationItem = require('../models/ration_item');
-let rationChapter = require('../models/ration_section_tree');
+var rationItem = require('../models/ration_item').Dao;;
+let rationChapter = require('../models/ration_section_tree').Dao;
 let asyncTool = require('async');
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});

+ 14 - 3
modules/ration_repository/models/coe.js

@@ -46,10 +46,21 @@ coeListDAO.prototype.getCoeItemsByIDs = function (data, callback) {
     coeListModel.find({
             "libID": data.libID,
             "ID": {"$in":data.coeIDs}
-        }, ["libID","ID","name","content","-_id"],
+        }, ["libID","ID", "serialNo","name","content","-_id"],
         function (err, doc) {
             if (err) callback("批量获取系数明细错误!", null)
-            else callback(null, doc);
+            else callback(0, doc);
+        })
+};
+
+coeListDAO.prototype.getCoeItemsByNos = function (data, callback) {
+    coeListModel.find({
+            "libID": data.libID,
+            "serialNo": {"$in":data.coeNos}
+        }, ["libID","ID","serialNo","name","content","-_id"],
+        function (err, doc) {
+            if (err) callback("批量获取系数明细错误!", null)
+            else callback(0, doc);
         })
 };
 
@@ -57,7 +68,7 @@ coeListDAO.prototype.getCoesByLibID = function (libID, callback) {
     coeListModel.find({ "libID": libID },
         function (err, doc) {
             if (err) callback("获取定额库系数表错误", null)
-            else callback(null, doc);
+            else callback(0, doc);
         })
 };
 

+ 1 - 1
modules/ration_repository/models/glj_repository.js

@@ -33,7 +33,7 @@ var gljSchema = mongoose.Schema({
 });
 var gljTypeModel = db.model("std_ration_lib_glj_type",gljTypeSchema, "std_ration_lib_glj_type");
 var gljItemModel = mongoose.model("std_glj_lib_gljList");
-var repositoryMap = require('./repository_map');
+var repositoryMap = require('./repository_map').Dao;
 var counter = require('../../../public/counter/counter');
 
 var gljItemDAO = function(){};

+ 15 - 4
modules/ration_repository/models/ration_item.js

@@ -25,6 +25,12 @@ var rationAssItemSchema = mongoose.Schema({
     maxValue: String
 }, { _id: false });
 
+//定额安装增加费用
+let rationInstSchema = mongoose.Schema({
+    feeItemId: String,
+    sectionId: String
+},{_id: false});
+
 var rationItemSchema = mongoose.Schema({
     ID:Number,
     code: String,
@@ -39,7 +45,8 @@ var rationItemSchema = mongoose.Schema({
     annotation: String,
     rationGljList: [rationGljItemSchema],
     rationCoeList: Array,
-    rationAssList: [rationAssItemSchema]
+    rationAssList: [rationAssItemSchema],
+    rationInstList: [rationInstSchema]
 });
 var rationItemModel = db.model("std_ration_lib_ration_items",rationItemSchema, "std_ration_lib_ration_items")
 var counter = require('../../../public/counter/counter');
@@ -47,8 +54,8 @@ import stdGljListModel from '../../common/std/schemas/std_ration_lib_glj_list';
 
 var rationItemDAO = function(){};
 
-rationItemDAO.prototype.getRationItemsBySection = function(sectionId,callback){
-    rationItemModel.find({"sectionId": sectionId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]}, null, {sort: {code: 1}}, function(err,data){
+rationItemDAO.prototype.getRationItemsBySection = function(rationRepId, sectionId,callback){
+    rationItemModel.find({"rationRepId": rationRepId, "sectionId": sectionId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]}, null, {sort: {code: 1}}, function(err,data){
         if(err) callback(true, "Fail to get items", "")
         else callback(false,"Get items successfully", data);
     })
@@ -213,5 +220,9 @@ rationItemDAO.prototype.updateRationItems = function(rationLibId, sectionId, ite
     });
 };
 
-module.exports = new rationItemDAO()
+module.exports = {
+    Dao: new rationItemDAO(),
+    Model: rationItemModel
+};
+//module.exports = new rationItemDAO()
 

+ 5 - 1
modules/ration_repository/models/ration_section_tree.js

@@ -117,4 +117,8 @@ rationChapterTreeDAO.prototype.getRationChapter = function (repId, chapterID, ca
     }
 }
 
-module.exports = new rationChapterTreeDAO()
+module.exports = {
+    Model: rationChapterTreeModel,
+    Dao: new rationChapterTreeDAO()
+};
+//module.exports = new rationChapterTreeDAO()

+ 5 - 1
modules/ration_repository/models/repository_map.js

@@ -117,4 +117,8 @@ rationRepositoryDao.prototype.deleteRationLib = function(rationName,callback){
     });
 }
 
-module.exports = new rationRepositoryDao();
+module.exports = {
+    Dao: new rationRepositoryDao(),
+    Model: rationRepository
+}
+//module.exports = new rationRepositoryDao();

+ 1 - 0
modules/reports/models/tpl_tree_node.js

@@ -23,6 +23,7 @@ let TplNodeSchema = new Schema({
     ID: Number,         //template节点ID,只有在nodeType是模板节点有效
     refId: Number,      //引用报表模板id (引用 collection: rpt_templates)
     name: String,       //显示名称
+    released: Boolean,  //是否已发布
     items: []           //子节点
 });
 

+ 2 - 2
modules/reports/rpt_component/helper/jpc_helper_area.js

@@ -50,12 +50,12 @@ let JpcAreaHelper = {
                 }
             } else {
                 //颗粒度更加细化的控制,可能上下两边的计算坐标方式都不同
-                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_TOP] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                if (areaNode[JV.PROP_V_CALCULATION][JV.PROP_TOP] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
                     innerTop = (1.0 * areaNode[JV.PROP_TOP] * areaHeight / JV.HUNDRED_PERCENT);
                 } else {
                     innerTop = 1.0 * areaNode[JV.PROP_TOP] * unitFactor;
                 }
-                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_BOTTOM] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                if (areaNode[JV.PROP_V_CALCULATION][JV.PROP_BOTTOM] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
                     innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT);
                 } else {
                     innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor;

+ 29 - 25
modules/reports/rpt_component/helper/jpc_helper_common_output.js

@@ -2,7 +2,7 @@ let JV = require('../jpc_value_define');
 let JpcFieldHelper = require('./jpc_helper_field');
 
 let JpcCommonOutputHelper = {
-    createCommonOutputWithoutDecorate: function (node, value, controls) {
+    createCommonOutputWithoutDecorate: function (node, value) {
         let rst = {};
         //1. font/style/control
         rst[JV.PROP_FONT] = node[[JV.PROP_FONT]];
@@ -10,20 +10,11 @@ let JpcCommonOutputHelper = {
         rst[JV.PROP_STYLE] = node[[JV.PROP_STYLE]];
         //2. value
         rst[JV.PROP_VALUE] = value;
-        if (node[JV.PROP_FORMAT]) {
-            if (!(isNaN(parseFloat(rst[JV.PROP_VALUE])))) {
-                let dotIdx = node[JV.PROP_FORMAT].indexOf(".");
-                if (dotIdx >= 0) {
-                    rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(node[JV.PROP_FORMAT].length - dotIdx - 1);
-                } else {
-                    rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(0);
-                }
-            }
-        }
-        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] != null) {
+        innerFormat(node[JV.PROP_FORMAT], rst);
+        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] !== null) {
             rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX] + rst[JV.PROP_VALUE];
         }
-        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] != null) {
+        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] !== null) {
             rst[JV.PROP_VALUE] = rst[JV.PROP_VALUE] + node[JV.PROP_SUFFIX];
         }
         return rst;
@@ -37,24 +28,37 @@ let JpcCommonOutputHelper = {
         //2. value
         rst[JV.PROP_VALUE] = value;
         JpcFieldHelper.decorateValue(rst, controls);
-        if (node[JV.PROP_FORMAT]) {
-            if (!(isNaN(parseFloat(rst[JV.PROP_VALUE])))) {
-                let dotIdx = node[JV.PROP_FORMAT].indexOf(".");
-                if (dotIdx >= 0) {
-                    rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(node[JV.PROP_FORMAT].length - dotIdx - 1);
-                } else {
-                    rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(0);
-                }
-            }
-        }
-        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] != null && rst[JV.PROP_VALUE] != "") {
+        innerFormat(node[JV.PROP_FORMAT], rst);
+        if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] !== null && rst[JV.PROP_VALUE] !== "") {
             rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX] + rst[JV.PROP_VALUE];
         }
-        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] != null && rst[JV.PROP_VALUE] != "") {
+        if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] !== null && rst[JV.PROP_VALUE] !== "") {
             rst[JV.PROP_VALUE] = rst[JV.PROP_VALUE] + node[JV.PROP_SUFFIX];
         }
         return rst;
     }
+};
+
+function innerFormat(formatStr, rst) {
+    if (formatStr) {
+        if (!(isNaN(parseFloat(rst[JV.PROP_VALUE])))) {
+            let dotIdx = formatStr.indexOf(".");
+            if (dotIdx >= 0) {
+                rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(formatStr.length - dotIdx - 1);
+            } else {
+                rst[JV.PROP_VALUE] = parseFloat(rst[JV.PROP_VALUE]).toFixed(0);
+            }
+            let commaIdx = formatStr.indexOf(",");
+            if (commaIdx >= 0) {
+                rst[JV.PROP_VALUE] = comdify(rst[JV.PROP_VALUE].toString());
+            }
+        }
+    }
+}
+
+function comdify(numStr){
+    let re = /\d{1,3}(?=(\d{3})+$)/g;
+    return numStr.replace(/^(\d+)((\.\d+)?)$/,function(s,s1,s2){return s1.replace(re,"$&,")+s2;});
 }
 
 module.exports = JpcCommonOutputHelper;

+ 1 - 0
modules/reports/rpt_component/jpc_bill_tab.js

@@ -1,4 +1,5 @@
 let JV = require('./jpc_value_define');
+let JE = require('./jpc_rte');
 let JpcFieldHelper = require('./helper/jpc_helper_field');
 let JpcBandHelper = require('./helper/jpc_helper_band');
 let JpcCommonHelper = require('./helper/jpc_helper_common');

+ 53 - 3
modules/reports/rpt_component/jpc_flow_tab.js

@@ -85,6 +85,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
         me.seg_sum_fields_idx = [];
         me.seg_sum_tab_fields = [];
         me.page_sum_fields_idx = [];
+        me.page_sum_tab_fields = [];
 
         me.group_fields = [];
         me.group_sum_fields = [];
@@ -95,6 +96,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
         me.pageStatusLst = [];
         me.groupSumValLst = [];
         me.segSumValLst = [];
+        me.pageSumValLst = [];
         me.multiCols = 1;
         me.pagesAmt = 0;
     };
@@ -102,7 +104,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
         let me = this;
         let FLOW_NODE_STR = me.isEx?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_SEG_SUM][JV.PROP_SUM_FIELDS], me.seg_sum_tab_fields, me.seg_sum_fields_idx, me.isEx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM][JV.PROP_SUM_FIELDS], null, me.page_sum_fields_idx, me.isEx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM][JV.PROP_SUM_FIELDS], me.page_sum_tab_fields, me.page_sum_fields_idx, me.isEx);
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_FIELDS], me.group_fields, null, me.isEx);
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_SUM_FIELDS], me.group_sum_fields, null, me.isEx);
         for (let si = 0; si < dataSeq.length; si++) {
@@ -355,15 +357,18 @@ JpcFlowTabSrv.prototype.createNew = function(){
                     } else {
                         if (currentRecAmt + maxRowRec >= ttlSegRecAmt) {
                             pageStatus[JV.STATUS_SEGMENT_END] = true;
+                            pageStatus[JV.STATUS_REPORT_END] = true;
                             private_resetBandArea();
                             let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt, maxRowRec, me.isEx);
                             if (hasAdHocRow) {
                                 //add page info(pre segment end)
                                 pageStatus[JV.STATUS_SEGMENT_END] = false;
+                                pageStatus[JV.STATUS_REPORT_END] = false;
                                 private_addPage(segIdx, grpSeqInfo, false, false, -1);
                             }
                             //add page info
                             pageStatus[JV.STATUS_SEGMENT_END] = true;
+                            pageStatus[JV.STATUS_REPORT_END] = true;
                             private_addPage(segIdx, grpSeqInfo, false, false, -1);
                         } else {
                             private_addPage(segIdx, grpSeqInfo, false, false, -1);
@@ -503,6 +508,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
             // 2.3 Sum Seg
             tabRstLst.push(me.outputSegSum(rptTpl, dataObj, page, segIdx, bands, unitFactor, controls));
             // 2.4 Sum Page
+            tabRstLst.push(me.outputPageSum(rptTpl, dataObj, page, segIdx, bands, unitFactor, controls));
             // 2.5 Group
             // 2.6 Discrete
             tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[FLOW_NODE_STR][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, 1, 0, $CURRENT_RPT));
@@ -536,6 +542,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 // 2.3 Sum Seg
                 tabRstLst.push(me.outputSegSum(rptTpl, dataObj, actualPage, segIdx, bands, unitFactor, controls));
                 // 2.4 Sum Page
+                tabRstLst.push(me.outputPageSum(rptTpl, dataObj, actualPage, segIdx, bands, unitFactor, controls));
                 // 2.5 Group
                 // 2.6 Discrete
                 if (pi === 0) {
@@ -563,6 +570,15 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 let data_details = me.isEx?dataObj[JV.DATA_DETAIL_DATA_EX]:dataObj[JV.DATA_DETAIL_DATA];
                 let data_details_ex = $CURRENT_RPT.isFollowMode?dataObj[JV.DATA_DETAIL_DATA_EX]:null;
                 let contentValuesIdx = me.dispValueIdxLst[page - 1];
+                let page_sum_data_fields = [];
+                for (let i = 0; i < me.page_sum_fields_idx.length; i++) {
+                    let data_field = data_details[me.page_sum_fields_idx[i]];
+                    page_sum_data_fields.push(data_field);
+                }
+                let rowGrandTotal = [];
+                for (let di = 0; di < page_sum_data_fields.length; di++) {
+                    rowGrandTotal.push(0.0);
+                }
                 //normal content
                 for (let rowIdx = 0; rowIdx < contentValuesIdx.length; rowIdx++) {
                     for (let i = 0; i < tab_fields.length; i++) {
@@ -603,7 +619,12 @@ JpcFlowTabSrv.prototype.createNew = function(){
                             }
                         }
                     }
+                    //page sum content
+                    for (let di = 0; di < page_sum_data_fields.length; di++) {
+                        rowGrandTotal[di] = rowGrandTotal[di] + 1.0 * JpcFieldHelper.getValue(page_sum_data_fields[di], contentValuesIdx[rowIdx][2]);
+                    }
                 }
+                me.pageSumValLst.push(rowGrandTotal);
                 //grouping content
                 for (let rowIdx = 0; rowIdx < contentValuesIdx.length; rowIdx++) {
                     if (contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_GROUP) {
@@ -672,6 +693,35 @@ JpcFlowTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
+    JpcFlowTabResult.outputPageSum = function (rptTpl, dataObj, page, segIdx, bands, unitFactor, controls) {
+        let me = this, rst = [];
+        let FLOW_NODE_STR = me.isEx?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
+        let tab = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM];
+        let band = bands[tab[JV.PROP_BAND_NAME]];
+        if (band) {
+            let pageStatus = me.pageStatusLst[page - 1];
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
+                let tab_fields = me.page_sum_tab_fields;
+                for (let i = 0; i < tab_fields.length; i++) {
+                    let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_fields[i], me.pageSumValLst[page - 1], controls);
+                    cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_fields[i][JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0, true, false);
+                    rst.push(cellItem);
+                }
+                if (tab[JV.PROP_TEXT]) {
+                    rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXT], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0));
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, 1, 0, 1, 0, me.multiCols, 0));
+                    }
+                }
+                if (tab[JV.NODE_DISCRETE_INFO]) {
+                    rst = rst.concat(JpcDiscreteHelper.outputDiscreteInfo(tab[JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, me.multiCols, 0));
+                }
+            }
+        }
+        return rst;
+    };
     JpcFlowTabResult.outputTabField = function (band, tab_field, data_field, valueIdx, serialIdx, rows, rowIdx, cols, colIdx, unitFactor, isRow, controls, multiColIdx) {
         let me = this,
             rst = JpcCommonOutputHelper.createCommonOutput(tab_field, JpcFieldHelper.getValue(data_field, valueIdx), controls);
@@ -695,10 +745,10 @@ JpcFlowTabSrv.prototype.createNew = function(){
             }
         }
         if (grp_line[JV.PROP_DISCRETE_FIELDS]) {
-            //
+            //暂时不支持
         }
         if (grp_line[JV.PROP_PARAMS]) {
-            //
+            //暂时不支持
         }
         // console.log(rst);
         return rst;

+ 2 - 1
modules/reports/rpt_component/jpc_rte.js

@@ -1,9 +1,10 @@
 /**
  * Created by Tony on 2016/12/28.
  */
-
+let strUtil = require('../../../public/stringUtil');
 let JV = require('./jpc_value_define');
 let JE = {
+    $STR_UTIL: strUtil,
     F: function(fID, $CURRENT_RPT) {
         let rst = null;
         if ($CURRENT_RPT && ($CURRENT_RPT.fields[JV.NODE_DETAIL_FIELDS][JV.PROP_ID + "_" + fID])) {

+ 153 - 35
modules/reports/util/rpt_construct_data_util.js

@@ -143,6 +143,36 @@ class Rpt_Data_Extractor {
         pri_setup_filter(JV.NODE_DETAIL_FIELDS);
         pri_setup_filter(JV.NODE_MASTER_FIELDS_EX);
         pri_setup_filter(JV.NODE_DETAIL_FIELDS_EX);
+        if (tpl[JV.NODE_MAP_DATA_HANDLE_INFO] && tpl[JV.NODE_MAP_DATA_HANDLE_INFO].length > 0) {
+            for (let preHandle of tpl[JV.NODE_MAP_DATA_HANDLE_INFO]) {
+                if (rst.indexOf(preHandle[JV.PROP_DATA_KEY]) < 0) {
+                    rst.push(preHandle[JV.PROP_DATA_KEY]);
+                }
+                if (preHandle[JV.PROP_HANDLE_TYPE] === JV.PROP_HANDLE_TYPE_FILTER) {
+                    if (preHandle[JV.PROP_FILTER_KEYS]) {
+                        for (let filter of preHandle[JV.PROP_FILTER_KEYS]) {
+                            if (filter[JV.PROP_FILTER_COMPARE_OBJ] && rst.indexOf(filter[JV.PROP_FILTER_COMPARE_OBJ]) < 0) {
+                                rst.push(filter[JV.PROP_FILTER_COMPARE_OBJ]);
+                            }
+                        }
+                    }
+                } else if (preHandle[JV.PROP_HANDLE_TYPE] === JV.PROP_HANDLE_TYPE_SUM) {
+                    if (preHandle[JV.PROP_SUM_GROUP_KEYS]) {
+                        for (let grpKey of preHandle[JV.PROP_SUM_GROUP_KEYS]) {
+                            if (grpKey.seeking_parent && rst.indexOf(grpKey.seeking_parent) < 0) {
+                                rst.push(grpKey.seeking_parent);
+                            }
+                        }
+                    }
+                } else if (preHandle[JV.PROP_PARENT_CHILD_SORT_KEY] && preHandle[JV.PROP_PARENT_CHILD_SORT_KEY].length > 0) {
+                    for (let item of preHandle[JV.PROP_PARENT_CHILD_SORT_KEY]) {
+                        if (rst.indexOf(item[JV.PROP_PARENT_DATA_KEY]) < 0) {
+                            rst.push(item[JV.PROP_PARENT_DATA_KEY]);
+                        }
+                    }
+                }
+            }
+        }
         if (rst.length === 0) {
             rst.push(projectConst.RATION_ASS);
         }
@@ -279,42 +309,70 @@ function filterData(sourceData, handleCfg, prjData) {
             tempRstArr.push(item);
         }
     }
-    let private_chkVal = function (src, dest, compStr) {
+    let private_chkVal = function (src, compVal, compStr) {
         let rst = true;
         switch (compStr) {
             case "==" :
-                rst = (src == dest);
+                rst = (src == compVal);
                 break;
             case "===" :
-                rst = (src === dest);
+                rst = (src === compVal);
                 break;
             case ">" :
-                rst = (src > dest);
+                rst = (src > compVal);
                 break;
             case ">=" :
-                rst = (src >= dest);
+                rst = (src >= compVal);
                 break;
             case "<" :
-                rst = (src < dest);
+                rst = (src < compVal);
                 break;
             case "<=" :
-                rst = (src <= dest);
+                rst = (src <= compVal);
                 break;
             case "!=" :
-                rst = (src != dest);
+                rst = (src != compVal);
                 break;
             case "!==" :
-                rst = (src !== dest);
+                rst = (src !== compVal);
+                break;
+            case "in" :
+                if (compVal instanceof Array) {
+                    rst = compVal.indexOf(src) >= 0;
+                } else {
+                    //string,需要转类型
+                    let newCv = JSON.parse(compVal);
+                    if (newCv instanceof Array) {
+                        rst = newCv.indexOf(src) >= 0;
+                    } else {
+                        rst = false;
+                    }
+                }
+                break;
+            case "not in":
+                if (compVal instanceof Array) {
+                    rst = compVal.indexOf(src) < 0;
+                } else {
+                    //string,需要转类型
+                    let newCv = JSON.parse(compVal);
+                    if (newCv instanceof Array) {
+                        rst = newCv.indexOf(src) < 0;
+                    } else {
+                        rst = true;
+                    }
+                }
                 break;
             default:
                 rst = true;
         }
         return rst;
     };
+    let compareObj = {};
     for (let item of tempRstArr) {
         let compRst = true;
         let curComparePrjData = null;
-        for (let cfg of handleCfg[JV.PROP_FILTER_KEY]) {
+        let startIdx = 0;
+        for (let cfg of handleCfg[JV.PROP_FILTER_KEYS]) {
             if (cfg[JV.PROP_FILTER_COMPARE_VAL]) {
                 //比较key值
                 compRst = private_chkVal(item[cfg.key], cfg[JV.PROP_FILTER_COMPARE_VAL], cfg[JV.PROP_FILTER_CONDITION]);
@@ -323,11 +381,28 @@ function filterData(sourceData, handleCfg, prjData) {
                 if (!curComparePrjData) {
                     curComparePrjData = getModuleDataByKey(prjData, cfg[JV.PROP_FILTER_COMPARE_OBJ]);
                 }
-                for (let data of curComparePrjData.data) {
-                    compRst = private_chkVal(item[cfg.key], data[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY]], cfg[JV.PROP_FILTER_CONDITION]);
-                    if (compRst) break;
+                if (cfg[JV.PROP_FILTER_CONDITION] === "in" || cfg[JV.PROP_FILTER_CONDITION] === "not in") {
+                    let compareArr = null;
+                    if (!compareObj.hasOwnProperty(cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY] + startIdx.toString())) {
+                        compareObj[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY] + startIdx.toString()] = [];
+                        compareArr = compareObj[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY] + startIdx.toString()];
+                        for (let data of curComparePrjData.data) {
+                            if (compareArr.indexOf(data[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY]]) < 0) {
+                                compareArr.push(data[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY]]);
+                            }
+                        }
+                    } else {
+                        compareArr = compareObj[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY] + startIdx.toString()];
+                    }
+                    compRst = private_chkVal(item[cfg.key], compareArr, cfg[JV.PROP_FILTER_CONDITION]);
+                } else {
+                    for (let data of curComparePrjData.data) {
+                        compRst = private_chkVal(item[cfg.key], data[cfg[JV.PROP_FILTER_COMPARE_OBJ_KEY]], cfg[JV.PROP_FILTER_CONDITION]);
+                        if (compRst) break;
+                    }
                 }
             }
+            startIdx++;
         }
         if (compRst) {
             rstArr.push(item);
@@ -601,6 +676,7 @@ function setupFunc(obj, prop, ownRawObj) {
     obj[prop].getFee = ext_getFee;
     obj[prop].getPropertyByForeignId = ext_getPropertyByForeignId;
     obj[prop].getArrayItemByKey = ext_getArrayItemByKey;
+    obj[prop].getPropertyByFlag = ext_getPropertyByFlag;
     if (prop === projectConst.CALC_PROGRAM) obj[prop].getCalcProperty = ext_getCalcProperty;
     if (prop === projectConst.FEERATE) obj[prop].getFeeRate = ext_getFeeRate;
 }
@@ -734,33 +810,39 @@ function ext_getFee(feeKey, dtlFeeKey) {
     let dtObj = parentObj["myOwnRawDataObj"];
     if (feeKey && dtObj) {
         for (let dItem of dtObj.data) {
-            let hasValue = false;
-            if (dItem.hasOwnProperty("fees")) {
-                for (let fee of dItem["fees"]) {
-                    if (fee["fieldName"] === feeKey) {
-                        if (dtlFeeKey) {
-                            rst.push(fee[dtlFeeKey]);
-                        } else {
-                            rst.push(fee["unitFee"]);
-                        }
-                        hasValue = true;
-                        break;
-                    }
+            rst.push(pri_getFee(dItem, feeKey, dtlFeeKey));
+        }
+    }
+    for (let i = 0; i < rst.length; i++) {
+        rst[i] = parseFloat(rst[i]);
+    }
+    return rst;
+}
+
+function pri_getFee(dItem, feeKey, dtlFeeKey) {
+    let rst = 0;
+    let hasValue = false;
+    if (dItem["fees"]) {
+        for (let fee of dItem["fees"]) {
+            if (fee["fieldName"] === feeKey) {
+                if (dtlFeeKey) {
+                    rst = fee[dtlFeeKey];
+                } else {
+                    rst = fee["unitFee"];
                 }
-            } else if (dItem.hasOwnProperty(feeKey)) {
                 hasValue = true;
-                rst.push(dItem[feeKey]);
-            } else {
-                hasValue = true;
-                rst.push(0);
-            }
-            if (!hasValue) {
-                rst.push(0);
+                break;
             }
         }
+    } else if (dItem[feeKey]) {
+        hasValue = true;
+        rst = dItem[feeKey];
+    } else {
+        hasValue = true;
+        rst = 0;
     }
-    for (let i = 0; i < rst.length; i++) {
-        rst[i] = parseFloat(rst[i]);
+    if (!hasValue) {
+        rst = 0;
     }
     return rst;
 }
@@ -859,7 +941,43 @@ function ext_getArrayItemByKey(arrayKey, itemKey, itemKeyValue, itemRstKey){
             private_getItemValue(arr, itemKeyValue);
         }
     }
+}
 
+function ext_getPropertyByFlag(flagVal, rstKey, dftValIfEmpty) {
+    let rst = [], parentObj = this;
+    let dtObj = parentObj["myOwnRawDataObj"];
+    if (flagVal && rstKey && dtObj) {
+        let isArr = (flagVal instanceof Array);
+        for (let dItem of dtObj.data) {
+            let doc = (dItem._doc)?dItem._doc:dItem;
+            if (doc.hasOwnProperty("flags")) {
+                let bFlag = false;
+                for (let flagItem of doc.flags) {
+                    if (isArr) {
+                        bFlag = (flagVal.indexOf(flagItem.flag) >= 0);
+                    } else {
+                        if (flagItem.flag === flagVal) {
+                            bFlag = true;
+                        }
+                    }
+                    if (bFlag) break;
+                }
+                if (bFlag) {
+                    let keys = rstKey.split(".");
+                    if (keys[0] === "fees") {
+                        rst.push(pri_getFee(doc, "common", keys[1]));
+                    } else {
+                        //其他,比如名称什么
+                    }
+                    break;
+                }
+            }
+        }
+    }
+    if (rst.length === 0 && dftValIfEmpty !== null) {
+        rst.push(dftValIfEmpty);
+    }
+    return rst;
 }
 
 function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey, dftValIfNotFound) {

+ 45 - 37
modules/reports/util/rpt_excel_util.js

@@ -23,10 +23,10 @@ function writeContentTypes(sheets, isSinglePage) {
     rst.push('<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>');
     rst.push('<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>');
     if (isSinglePage) {
-        rst.push('<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>')
+        rst.push('<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>');
     } else {
         for (let i = 0; i < sheets.length; i++) {
-            rst.push('<Override PartName="/xl/worksheets/sheet' + (i + 1) + '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>')
+            rst.push('<Override PartName="/xl/worksheets/sheet' + (i + 1) + '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>');
         }
     }
     rst.push('<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>');
@@ -60,11 +60,11 @@ function writeApp(sheets, isSinglePage) {
     rst.push('<TitlesOfParts>');
     if (isSinglePage) {
         rst.push('<vt:vector size="1" baseType="lpstr">');
-        rst.push('<vt:lpstr>' + sheets[0].sheetName + '</vt:lpstr>')
+        rst.push('<vt:lpstr>' + sheets[0].sheetName + '</vt:lpstr>');
     } else {
         rst.push('<vt:vector size="' + sheets.length + '" baseType="lpstr">');
         for (let i = 0; i < sheets.length; i++) {
-            rst.push('<vt:lpstr>' + sheets[i].sheetName + '</vt:lpstr>')
+            rst.push('<vt:lpstr>' + sheets[i].sheetName + '</vt:lpstr>');
         }
     }
     rst.push('</vt:vector>');
@@ -74,7 +74,6 @@ function writeApp(sheets, isSinglePage) {
     rst.push('<SharedDoc>false</SharedDoc>');
     rst.push('<HyperlinksChanged>false</HyperlinksChanged>');
     rst.push('<AppVersion>12.0000</AppVersion>');
-    //rst.push('');
     rst.push('</Properties>');
     return rst;
 }
@@ -91,7 +90,9 @@ function writeCore() {
     rst.push('<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">');
     rst.push('<dc:creator>SmartCost</dc:creator>');
     rst.push('<cp:lastModifiedBy>SmartCost</cp:lastModifiedBy>');
-    let dt = new Date(), dtStr = dt.getFullYear() + '-' + p_fillZero(dt.getMonth()+1) + '-' + p_fillZero(dt.getDate()) + 'T' +
+    let dt = new Date();
+    dt.setDate(dt.getDate() - 8/24); //it's GMT time, so please add the server offset time ( -8 hours )
+    let dtStr = dt.getFullYear() + '-' + p_fillZero(dt.getMonth()+1) + '-' + p_fillZero(dt.getDate()) + 'T' +
         p_fillZero(dt.getHours()) + ':' + p_fillZero(dt.getMinutes()) + ':' + p_fillZero(dt.getSeconds()) + 'Z';
     rst.push('<dcterms:created xsi:type="dcterms:W3CDTF">' + dtStr + '</dcterms:created>');
     rst.push('<dcterms:modified xsi:type="dcterms:W3CDTF">' + dtStr + '</dcterms:modified>');
@@ -116,7 +117,6 @@ function writeXlWorkBook(sheets, isSinglePage){
     }
     rst.push('</sheets>');
     rst.push('<calcPr calcId="124519"/>');
-    //rst.push('');
     rst.push('</workbook>');
     return rst;
 }
@@ -125,11 +125,11 @@ function writeXlRels(sheets, isSinglePage){
     rst.push(dftHeadXml + '\r\n');
     rst.push('<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">');
     if (isSinglePage) {
-        rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>')
+        rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>');
         idx++;
     } else {
         for (let i = 0; i < sheets.length; i++) {
-            rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet' + (i + 1) + '.xml"/>')
+            rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet' + (i + 1) + '.xml"/>');
             idx++;
         }
     }
@@ -138,7 +138,6 @@ function writeXlRels(sheets, isSinglePage){
     rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>');
     idx++;
     rst.push('<Relationship Id="rId' + idx + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>');
-    //rst.push('');
     rst.push('</Relationships>');
     return rst;
 }
@@ -151,7 +150,7 @@ function writeStyles(stylesObj){
     rst.push(dftHeadXml + '\r\n');
     rst.push('<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
     //1. push fonts
-    rst.push('<fonts count="' + stylesObj.fonts.length + '">')
+    rst.push('<fonts count="' + stylesObj.fonts.length + '">');
     for (let i = 0; i < stylesObj.fonts.length; i++) {
         let font = stylesObj.fonts[i];
         rst.push('<font>');
@@ -171,14 +170,14 @@ function writeStyles(stylesObj){
     //2. push default fills
     rst.push('<fills count="2"><fill><patternFill patternType="none" /></fill><fill><patternFill patternType="gray125" /></fill></fills>');
     //3. push borders
-    rst.push('<borders count="' + stylesObj.borders.length + '">')
+    rst.push('<borders count="' + stylesObj.borders.length + '">');
     let private_setBorder = function(border, borderDirection) {
-        if (border[borderDirection][JV.PROP_LINE_WEIGHT] == 0) {
+        if (parseInt(border[borderDirection][JV.PROP_LINE_WEIGHT]) === 0) {
             rst.push('<' + borderDirection.toLowerCase() + '/>');
         } else {
             let bW = 'thin';
-            if (border[borderDirection][JV.PROP_LINE_WEIGHT] == 2) bW = 'medium';
-            if (border[borderDirection][JV.PROP_LINE_WEIGHT] > 2) bW = 'thick';
+            if (parseInt(border[borderDirection][JV.PROP_LINE_WEIGHT]) === 2) bW = 'medium';
+            if (parseInt(border[borderDirection][JV.PROP_LINE_WEIGHT]) > 2) bW = 'thick';
             rst.push('<' + borderDirection.toLowerCase() + ' style="' + bW + '">' + '<color indexed="64"/>' + '</' + borderDirection.toLowerCase() + '>');
         }
     };
@@ -244,7 +243,7 @@ function writeStyles(stylesObj){
             newVertical = tmpV;
         }
         alignStr += ' horizontal="' + newHorizontal + '" vertical="' + newVertical + '"';
-        if (strUtil.convertStrToBoolean(excelStyle[JV.CONTROL_PROPS[1]])) {
+        if (strUtil.convertStrToBoolean(excelStyle[JV.CONTROL_PROPS[0]])) {
             alignStr += ' shrinkToFit="1"';
         }
         if (strUtil.convertStrToBoolean(excelStyle[JV.CONTROL_PROPS[4]])) {
@@ -345,21 +344,21 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
     let private_pre_analyze_pos = function(){
         let cell, pos;
         let self_analyze_sheet_pos = function (theShtData, theXPos, theYPos) {
-            theShtData.cells.sort(function(cell1, cell2) {
-                let rst = 0;
-                if (cell1[JV.PROP_AREA][JV.PROP_TOP] > cell2[JV.PROP_AREA][JV.PROP_TOP]) {
-                    rst = 1;
-                } else if (cell1[JV.PROP_AREA][JV.PROP_TOP] < cell2[JV.PROP_AREA][JV.PROP_TOP]) {
-                    rst = -1;
-                } else {
-                    if (cell1[JV.PROP_AREA][JV.PROP_LEFT] > cell2[JV.PROP_AREA][JV.PROP_LEFT]) {
-                        rst = 1;
-                    } else if (cell1[JV.PROP_AREA][JV.PROP_LEFT] < cell2[JV.PROP_AREA][JV.PROP_LEFT]) {
-                        rst = -1;
-                    }
-                }
-                return rst;
-            });
+            // theShtData.cells.sort(function(cell1, cell2) {
+            //     let rst = 0;
+            //     if (cell1[JV.PROP_AREA][JV.PROP_TOP] > cell2[JV.PROP_AREA][JV.PROP_TOP]) {
+            //         rst = 1;
+            //     } else if (cell1[JV.PROP_AREA][JV.PROP_TOP] < cell2[JV.PROP_AREA][JV.PROP_TOP]) {
+            //         rst = -1;
+            //     } else {
+            //         if (cell1[JV.PROP_AREA][JV.PROP_LEFT] > cell2[JV.PROP_AREA][JV.PROP_LEFT]) {
+            //             rst = 1;
+            //         } else if (cell1[JV.PROP_AREA][JV.PROP_LEFT] < cell2[JV.PROP_AREA][JV.PROP_LEFT]) {
+            //             rst = -1;
+            //         }
+            //     }
+            //     return rst;
+            // });
             for (let i = 0; i < theShtData.cells.length; i++) {
                 cell = theShtData.cells[i];
                 pos = cell[JV.PROP_AREA][JV.PROP_LEFT];
@@ -493,7 +492,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
     };
     let private_chkAndGetMergeLine = function(cell, sheetBorder, borderStr, needFurtherChk) {
         let rst = 0,
-            mergeBorder = (sheetData[JV.PROP_PAGE_MERGE_BORDER])?sheetData[JV.PROP_PAGE_MERGE_BORDER]:pageData[JV.BAND_PROP_MERGE_BAND],
+            mergeBorder = (sheetData)?sheetData[JV.PROP_PAGE_MERGE_BORDER]:pageData[JV.BAND_PROP_MERGE_BAND],
             mergeBand = pageData[JV.BAND_PROP_MERGE_BAND]
         ;
         if (sheetBorder[borderStr] && sheetBorder[borderStr][JV.PROP_LINE_WEIGHT] !== undefined) {
@@ -625,14 +624,23 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         return rst;
     };
     let private_setCols = function(){
-        //remark: 1 excel width = 2.117 mm
+        //remark: 1 excel unit width = 2.117 mm
         rst.push('<cols>');
         let w = 0;
+        /*
         for (let i = 1; i < xPos.length - 1; i++) {
             w = 1.0 * (xPos[i + 1] - xPos[i]) / DPI * 25.4 / 2.117;
             w = Math.round(w * 1000) / 1000;
             rst.push('<col min="' + i +'" max="' + i +'" width="' + w + '" customWidth="1"/>');
         }
+        /*/
+        for (let i = 1; i < xPos.length - 2; i++) {
+            w = 1.0 * (xPos[i + 1] - xPos[i]) / DPI * 25.4 / 2.117;
+            w = Math.round(w * 1000) / 1000;
+            rst.push('<col min="' + i +'" max="' + i +'" width="' + w + '" customWidth="1"/>');
+        }
+        rst.push('<col min="' + (xPos.length - 1) +'" max="' + (xPos.length - 1) +'" width="' + 10 + '" customWidth="1"/>');
+        //*/
         rst.push('</cols>');
     };
     let private_setMergedCells = function() {
@@ -694,7 +702,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
                     if (private_chkIfNeedCacheCell(cell)) {
                         for (let xi = idxL; xi < idxR; xi++) {
                             for (let yj = idxT; yj < idxB; yj++) {
-                                cacheBorderCell[private_getCellIdxStr(xi - 1) + yj + offsetY] = cell;
+                                cacheBorderCell[private_getCellIdxStr(xi - 1) + (yj + offsetY)] = cell;
                             }
                         }
                     }
@@ -714,7 +722,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         }
     };
     let private_setSheetData = function(){
-        //remark: 1 excel height = 0.3612 mm
+        //remark: 1 excel unit height = 0.3612 mm
         rst.push('<sheetData>');
         let spanX = xPos.length - 2, cellIdx = 0, h = 0
             ;
@@ -783,7 +791,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
             //4. maybe need to dispose the memory
             //...
         };
-        private_cacheMergeBandBorderIdxs();
+        //private_cacheMergeBandBorderIdxs();
         if (sheetData) {
             //current sheet data
             currentPageMergePos = sheetData[JV.PAGE_SPECIAL_MERGE_POS];
@@ -1013,7 +1021,7 @@ module.exports = {
             }
             //3. everything is ok, then call me
             me.exportExcel(newPageData, paperSize, fName, 'false', sheetNames, callback);
-            fsUtil.writeObjToFile(newPageData, 'D:/GitHome/ConstructionOperation/tmp/combinedHeader.js');
+            // fsUtil.writeObjToFile(newPageData, 'D:/GitHome/ConstructionOperation/tmp/combinedHeader.js');
         } catch (e) {
             console.log(e);
         }

+ 63 - 27
public/stringUtil.js

@@ -145,36 +145,72 @@ module.exports = {
     rightTrim: function(rst) {
         return str.replace(/(\s*$)/g,"");
     },
-    convertNumToChinese : function(num, isCurrency) {
-        if (!/^\d*(\.\d*)?$/.test(num)) { return "Number is wrong!"; }
-        let AA, BB;
-        if (isCurrency) {
-            AA = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
-            BB = ["", "拾", "佰", "仟", "萬", "億", "点", ""];
-        } else {
-            AA = ['零','一','二','三','四','五','六','七','八','九'];
-            BB = ["", "十", "百", "千", "万", "亿", "点", ""];
-        }
-        //let AA = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖");
-        //let BB = new Array("", "拾", "佰", "仟", "萬", "億", "点", "");
-        let a = ("" + num).replace(/(^0*)/g, "").split("."), k = 0, re = "";
-        for (let i = a[0].length - 1; i >= 0; i--) {
-            switch (k) {
-                case 0: re = BB[7] + re; break;
-                case 4: if (!new RegExp("0{4}\\d{" + (a[0].length - i - 1) + "}$").test(a[0]))
-                    re = BB[4] + re; break;
-                case 8: re = BB[5] + re; BB[7] = BB[5]; k = 0; break;
+    replaceAll: function (targetStr, FindText, RepText) {
+        let regExp = new RegExp(FindText, "gm");
+        return targetStr.replace(regExp, RepText);
+    },
+    comdify: function(numStr){
+        let re = /\d{1,3}(?=(\d{3})+$)/g;
+        return numStr.replace(/^(\d+)((\.\d+)?)$/,function(s,s1,s2){return s1.replace(re,"$&,")+s2;});
+    },
+    convertToCaptionNum: function(num, isCurrency, isTraditionalCap) {
+        let me = this, rst = "";
+        if (/^\d*(\.\d*)?$/.test(num)) {
+            let capChars, unitChars;
+            if (isTraditionalCap) {
+                capChars = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+                unitChars = ["" , "拾", "佰", "仟", "萬", "拾", "佰", "仟", "億", "拾", "佰", "仟", "萬"];
+            } else {
+                capChars = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
+                unitChars = ["" , "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "万"];
             }
-            if (k % 4 === 2 && a[0].charAt(i + 2) !== 0 && a[0].charAt(i + 1) === 0) re = AA[0] + re;
-            if (a[0].charAt(i) !== 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re; k++;
-        }
 
-        if (a.length > 1) //加上小数部分(如果有小数部分)
-        {
-            re += BB[6];
-            for (let i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
+            let numSplitArr = ("" + num).replace(/(^0*)/g, "").split(".");
+            if (numSplitArr[0] === "") numSplitArr[0] = "0";
+            let len = numSplitArr[0].length;
+            let intPartArr = [];
+            if (len <= 13) {
+                for (let idx = 0; idx < len; idx++) {
+                    intPartArr.push(capChars[ parseInt(numSplitArr[0].charAt(idx) )] + unitChars[len - idx - 1]);
+                }
+                rst = intPartArr.join('');
+                rst = me.replaceAll(rst, capChars[0] + unitChars[3], capChars[0]); //零千 -> 零
+                rst = me.replaceAll(rst, capChars[0] + unitChars[2], capChars[0]); //零百 -> 零
+                rst = me.replaceAll(rst, capChars[0] + unitChars[1], capChars[0]); //零十 -> 零
+                //
+                rst = me.replaceAll(me.replaceAll(rst, "零零", "零"), "零零", "零");
+                rst = me.replaceAll(rst, capChars[0] + unitChars[8], unitChars[8]); //零亿 -> 亿
+                rst = me.replaceAll(rst, capChars[0] + unitChars[4], unitChars[4]); //零万 -> 万
+                //
+                rst = me.replaceAll(rst, unitChars[8] + unitChars[4], unitChars[8] + capChars[0]); //亿万 -> 亿零
+                if (num === 0) {
+                    rst = "零";
+                } else if (rst.length > 1 && rst.charAt(rst.length - 1) === '零') {
+                    rst = rst.slice(0, rst.length - 1);
+                }
+                //小数部分处理
+                if (numSplitArr.length > 1) {
+                    len = numSplitArr[1].length;
+                    if (parseInt(numSplitArr[1]) === 0) {
+                        rst = rst + (isCurrency?"元整":"");
+                    } else {
+                        if (isCurrency && len > 2) len = 2;
+                        let fractionStr = [];
+                        for (let idx = 0; idx < len; idx++) {
+                            fractionStr.push(capChars[ parseInt(numSplitArr[1].charAt(idx))]+ (isCurrency?((idx === 0)?"角":"分"):""));
+                        }
+                        rst = rst + (isCurrency?"元":"点") + fractionStr.join("");
+                    }
+                } else {
+                    rst = rst + (isCurrency?"元整":"");
+                }
+            } else {
+                rst = "Number is too big!";
+            }
+        } else {
+            rst = "Number is wrong!";
         }
-        return re;
+        return rst;
     },
     convertStrToBoolean: function(str) {
         let rst = false, me = this;

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 1
public/web/PerfectLoad.js


+ 17 - 0
public/web/common_util.js

@@ -18,3 +18,20 @@ function parseFloatPlus(value){
     let rst = parseFloat(value);
     return  isNaN(rst) ? 0 : rst;
 };
+
+// 将arr2合并到arr1,并去重复。
+function mergeArr(arr1, arr2){
+    if (arr2.length > 0){
+        for (let cNode of arr2){
+            if (!arr1.includes(cNode)) arr1.push(cNode);
+        };
+    }
+};
+
+// 判断 sub 是否是 arr 的子数组。
+function isSubArr(sub, arr){
+    for(var i = 0, len = sub.length; i < len; i++){
+        if(arr.indexOf(sub[i]) == -1) return false;
+    }
+    return true;
+};

+ 67 - 16
public/web/id_tree.js

@@ -88,8 +88,12 @@ var idTree = {
                 var pre, next, i;
                 if (nodes.length === 0) { return; }
                 if (arguments.length === 4) {
-                    pre = (iIndex <= 0 || iIndex > children.length) ? null : children[iIndex - 1];
-                    next = pre ? pre.nextSibling : null;
+                    pre = (iIndex <= 0 || iIndex > children.length) ? null : children[iIndex-1];
+                    if(pre==null){
+                        next = iIndex==0?children[0]:null;
+                    }else {
+                        next = pre.nextSibling;
+                    }
                 } else if (arguments.length === 3) {
                     pre = children.length === 0 ? null : children[children.length - 1];
                     next = null;
@@ -422,7 +426,7 @@ var idTree = {
             }
         })();
         Tree.prototype.newNodeID = function () {
-            if (this.rangeNodeID() === -1) {
+            if (this.rangeNodeID() == -1) {
                 return this.maxNodeID() + 1;
             } else {
                 if (this.maxNodeID() < this.rangeNodeID()) {
@@ -460,7 +464,7 @@ var idTree = {
             // set parent by pid, set nextSibling by nid
             datas.forEach(function (data) {
                 node = that.nodes[prefix + data[that.setting.id]];
-                if (data[that.setting.pid] === that.setting.rootId) {
+                if (data[that.setting.pid] == that.setting.rootId) {
                     that.roots.push(node);
                 } else {
                     parent = that.nodes[prefix + data[that.setting.pid]];
@@ -500,8 +504,8 @@ var idTree = {
 
         Tree.prototype.insert = function (parentID, nextSiblingID) {
             var newID = this.newNodeID(), node = null, data = {};
-            var parent = parentID === -1 ? null : this.nodes[this.prefix + parentID];
-            var nextSibling = nextSiblingID === -1 ? null: this.nodes[this.prefix + nextSiblingID];
+            var parent = parentID == -1 ? null : this.nodes[this.prefix + parentID];
+            var nextSibling = nextSiblingID == -1 ? null: this.nodes[this.prefix + nextSiblingID];
             if (newID !== -1) {
                 data = {};
                 data[this.setting.id] = newID;
@@ -519,12 +523,12 @@ var idTree = {
             }
             return node;
         };
-        Tree.prototype.getInsertData = function (parentID, nextSiblingID) {
+        Tree.prototype.getInsertData = function (parentID, nextSiblingID, uid = null) {
             var data = [];
-            var newID = this.newNodeID();
-            var parent = parentID === -1 ? null : this.nodes[this.prefix + parentID];
-            var nextSibling = nextSiblingID === -1 ? null: this.nodes[this.prefix + nextSiblingID];
-            if (newID !== -1) {
+            var newID = uid ? uuid.v1() : this.newNodeID();
+            var parent = parentID == -1 ? null : this.nodes[this.prefix + parentID];
+            var nextSibling = nextSiblingID == -1 ? null: this.nodes[this.prefix + nextSiblingID];
+            if (newID != -1) {
                 data.push({type: 'new', data: this.getDataTemplate(newID, parent ? parent.getID() : this.setting.rootId, nextSibling ? nextSibling.getID() : this.setting.rootId)});
 
                 if (nextSibling && nextSibling.preSibling) {
@@ -537,9 +541,9 @@ var idTree = {
             }
             return data;
         };
-        Tree.prototype.insertByData = function (data, parentID, nextSiblingID) {
-            var parent = parentID === -1 ? null : this.nodes[this.prefix + parentID];
-            var nextSibling = nextSiblingID === -1 ? null : this.nodes[this.prefix + nextSiblingID];
+        Tree.prototype.insertByData = function (data, parentID, nextSiblingID, uid = null) {
+            var parent = parentID == -1 ? null : this.nodes[this.prefix + parentID];
+            var nextSibling = nextSiblingID == -1 ? null : this.nodes[this.prefix + nextSiblingID];
             var node = this.nodes[this.prefix + data[this.setting.id]];
             if (node) {
                 return node;
@@ -552,12 +556,27 @@ var idTree = {
                 }
                 this.nodes[this.prefix +  data[this.setting.id]] = node;
                 tools.sortTreeItems(this);
-                this.maxNodeID( data[this.setting.id]);
+                if(!uid){
+                    this.maxNodeID( data[this.setting.id]);
+                }
                 return node;
             }
         };
 
         Tree.prototype.delete = function (node) {
+            var success = false;
+            success=this.cascadeRemove(node);
+            tools.sortTreeItems(this);
+            return success;
+        };
+        Tree.prototype.m_delete=function(nodes){
+            for(let node of nodes){
+                this.cascadeRemove(node);
+            }
+            tools.sortTreeItems(this);
+            return true;
+        };
+        Tree.prototype.cascadeRemove = function (node){
             var success = false, that = this;
             var deleteIdIndex = function (nodes) {
                 nodes.forEach(function (node) {
@@ -578,11 +597,40 @@ var idTree = {
                 } else {
                     this.roots.splice(node.siblingIndex(), 1);
                 }
+                success = true;
+            }
+            return success;
+        };
+        Tree.prototype.singleDelete = function (node) {//删除本身不删除子项
+            let that = this;
+            let success = false;
+            delete that.nodes[that.prefix + node.getID()];//删除本身
+            if(node.children.length>0){
+                if(node.preSibling){//子项变成前兄弟的子项
+                    for(let c of node.children){
+                        node.preSibling.addChild(c);
+                    }
+                }else if(node.nextSibling){//没有前兄弟,有后兄弟
+                    let oldChild = node.parent.children;
+                    node.parent.children = [];
+                    for(let c of node.children){
+                        node.parent.addChild(c);
+                    }
+                    for(let oc of oldChild){
+                        node.parent.addChild(oc);
+                    }
+                }else {//都没有的情况
+                    for(let c of node.children){
+                        node.parent.addChild(c);
+                    }
+                }
                 tools.sortTreeItems(this);
                 success = true;
             }
             return success;
         };
+
+
         Tree.prototype.getDeleteData = function (node) {
             var data = [];
             var addUpdateDataForDelete = function (datas, nodes) {
@@ -616,7 +664,10 @@ var idTree = {
         Tree.prototype.bind = function (eventName, eventFun) {
             this.event[eventName] = eventFun;
         };
-
+        Tree.prototype.getNodeByID = function (ID) {
+            let node = this.nodes[this.prefix+ID];
+            return node;
+        };
         return new Tree(setting);
     },
     updateType: {update: 'update', new: 'new', delete: 'delete'}

+ 1 - 1
public/web/rpt_value_define.js

@@ -53,7 +53,7 @@ const JV = {
     PROP_CHILD_SORT_KEYS: "子排序键值集",
     PROP_OTHER_SUB_SORT: "其他子排序",
     PROP_HANDLE_TYPE: "预处理类型",
-    PROP_FILTER_KEY: "过滤键值集",
+    PROP_FILTER_KEYS: "过滤键值集",
     PROP_FILTER_COMPARE_OBJ: "compareObjKey",
     PROP_FILTER_COMPARE_OBJ_KEY: "compareObjIdKey",
     PROP_FILTER_COMPARE_VAL: "compareValue",

+ 11 - 1
public/web/scMathUtil.js

@@ -169,6 +169,16 @@ let scMathUtil = {
     isNumber : function (obj) {
         return obj === +obj;
     },
+    roundForObj:function(obj,decimal){
+        let me = this;
+        let value;
+        if(me.isNumber(obj)){
+            value = me.roundTo(obj,-decimal)
+        }else {
+            value = me.roundTo(Number(obj),-decimal);
+        }
+        return value
+    },
     roundToString:function(obj,decimal){
         let me = this;
         let value;
@@ -183,7 +193,7 @@ let scMathUtil = {
 
 Number.prototype.toDecimal = function (ADigit) {
     //return parseFloat(this.toFixed(ADigit));
-    digit = (ADigit && typeof(ADigit) === 'number' && Number.isInteger(ADigit) && ADigit >= 0) ? -ADigit : -2;
+    digit = ((ADigit!=null||ADigit!=undefined) && typeof(ADigit) === 'number' && Number.isInteger(ADigit) && ADigit >= 0) ? -ADigit : -2;
     // var s = scMathUtil.roundTo(this, digit);
     // console.log('Number: ' + this + '   Digit: ' + digit + '    Result: ' + s);
     // return parseFloat(s);

+ 214 - 36
public/web/sheet/sheet_common.js

@@ -40,7 +40,10 @@ var sheetCommonObj = {
 
         sheet.showRowOutline(false);
         me.buildHeader(sheet, setting);
-        if (rowCount > 0) sheet.setRowCount(rowCount);
+        if (rowCount > 0)
+            sheet.setRowCount(rowCount)
+        else
+            sheet.setRowCount(1);
         sheet.resumeEvent();
         sheet.resumePaint();
     },
@@ -70,6 +73,14 @@ var sheetCommonObj = {
         sheet.resumeEvent();
         sheet.resumePaint();
     },
+    cleanData: function (sheet, setting, rowCount) {
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        sheet.clear(-1, 0, -1, setting.header.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+        if (rowCount > 0) sheet.setRowCount(rowCount);
+        sheet.resumeEvent();
+        sheet.resumePaint();
+    },
     setAreaAlign: function(area, hAlign, vAlign){
         if (!(hAlign) || hAlign === "left") {
             area.hAlign(GC.Spread.Sheets.HorizontalAlign.left);
@@ -124,42 +135,62 @@ var sheetCommonObj = {
                 sheet.setValue(0, col, setting.header[col].headerName, header);
                 sheet.setColumnWidth(col, setting.header[col].headerWidth?setting.header[col].headerWidth:100);
             }
-            for (var row = 0; row < data.length; row++) {
-                //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
-                var val = data[row][setting.header[col].dataCode];
-                if(val&&setting.header[col].dataType === "Number"){
-                    if(setting.header[col].hasOwnProperty('tofix')){
-                        val =scMathUtil.roundToString(val,setting.header[col].tofix);
-                    }
-                    if(setting.header[col].hasOwnProperty('decimalField')){
-                        var decimal = getDecimal(setting.header[col].decimalField);
-                        val =scMathUtil.roundToString(val,decimal);
-                        sheet.setFormatter(-1, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
-                    }else {
-                        val =scMathUtil.roundToString(val,2);
-                    }
-                }
-                if(val!=null&&setting.header[col].cellType === "checkBox"){
-                    this.setCheckBoxCell(row,col,sheet,val)
-                 }
-                 if(setting.owner==='gljTree'){
-                    if(setting.header[col].cellType === "checkBox"){
-                        val==1?val:0;
-                        this.setCheckBoxCell(row,col,sheet,val);
-                    }
-                    if(setting.header[col].dataCode === 'gljType' && data[row].gljType){
-                        let distTypeVal =  distTypeTree.distTypes[distTypeTree.prefix + data[row].gljType].data.fullName;
-                        val=distTypeVal;
-                    }
-                 }
-                sheet.setValue(row, col, val, ch);
-            }
+        }
+        for (var row = 0; row < data.length; row++) {
+            //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
+            this.showRowData(sheet,setting,row,data,distTypeTree);
         }
         this.lockCells(sheet,setting);
         sheet.resumeEvent();
         sheet.resumePaint();
         //me.shieldAllCells(sheet);
     },
+    showRowData:function (sheet,setting,row,data,distTypeTree=null) {
+        let ch = GC.Spread.Sheets.SheetArea.viewport;
+        for (var col = 0; col < setting.header.length; col++) {
+            //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
+            var val = data[row][setting.header[col].dataCode];
+            if(val&&setting.header[col].dataType === "Number"){
+                if(setting.header[col].hasOwnProperty('tofix')){
+                    val =scMathUtil.roundToString(val,setting.header[col].tofix);
+                }
+                else if(setting.header[col].hasOwnProperty('decimalField')){
+                    var decimal = getDecimal(setting.header[col].decimalField);
+                    val =scMathUtil.roundToString(val,decimal);
+                    sheet.setFormatter(-1, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
+                }else {
+                    val =scMathUtil.roundToString(val,2);
+                }
+            }
+            if(val!=null&&setting.header[col].cellType === "checkBox"){
+                this.setCheckBoxCell(row,col,sheet,val)
+            }
+            if(setting.header[col].cellType === "comboBox"){
+                this.setComboBox(row,col,sheet,setting.header[col].options);
+            }
+            if(setting.header[col].cellType === "selectButton"){
+                this.setSelectButton(row,col,sheet,setting.header[col]);
+            }
+            if(setting.owner==='gljTree'){
+                if(setting.header[col].cellType === "checkBox"){
+                    val==1?val:0;
+                    this.setCheckBoxCell(row,col,sheet,val);
+                }
+                if(setting.header[col].dataCode === 'gljType' && data[row].gljType){
+                    let distTypeVal =  distTypeTree.distTypes[distTypeTree.prefix + data[row].gljType].data.fullName;
+                    val=distTypeVal;
+                }
+            }
+            if(setting.header[col].getText){
+                val = setting.getText[setting.header[col].getText](data[row],val)
+            }
+            sheet.setValue(row, col, val, ch);
+        }
+        if(setting.autoFit==true){
+            sheet.getRange(row, -1, 1, -1, GC.Spread.Sheets.SheetArea.viewport).wordWrap(true);
+            sheet.autoFitRow(row);
+        }
+    },
     analyzePasteData: function(setting, pastedInfo) {
         var rst = [], propId = pastedInfo.cellRange.col, preStrIdx = 0, itemObj = {};//propId = 0 to proId = pastedInfo.cellRange.col, update by zhong
         for (var i = 0; i < pastedInfo.pasteData.text.length; i++) {
@@ -216,11 +247,95 @@ var sheetCommonObj = {
         sheet.getCell(row, col).hAlign(GC.Spread.Sheets.HorizontalAlign.center);
 
     },
+    setComboBox(row,col,sheet,options){
+        //let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        let dynamicCombo = sheetCommonObj.getDynamicCombo(true);
+        dynamicCombo.itemHeight(options.length).items(options);
+        sheet.setCellType(row, col,dynamicCombo,GC.Spread.Sheets.SheetArea.viewport);
+    },
+    setSelectButton(row,col,sheet,header){
+        let getSelectButton = function (cellWidth=100) {
+            function moreButton() {
+
+            }
+            moreButton.prototype = new GC.Spread.Sheets.CellTypes.Button();
+            moreButton.prototype.paint = function (ctx, value, x, y, w, h, style, options){
+                GC.Spread.Sheets.CellTypes.Button.prototype.paint.call(this, ctx, value, x, y, w, h, style, options);
+                let buttonW = cellWidth/5;
+                let endX = x+w-2;
+                if(value){
+                    let textWidth = ctx.measureText(value).width;
+                    let spaceWidth = cellWidth - buttonW;
+                    let textEndX = x+2+textWidth;
+                    if(spaceWidth<textWidth){
+                        for(let i = value.length-1;i>1;i--){
+                            let newValue = value.substr(0,i);
+                            let newTestWidth =  ctx.measureText(newValue).width;
+                            if(spaceWidth>newTestWidth){
+                                value = newValue;
+                                textEndX = x+2+newTestWidth;
+                                break;
+                            }
+                        }
+                    }
+                    ctx.fillText(value,textEndX,y+h-5);
+                }
+
+                //画三个点
+                ctx.save();
+                ctx.beginPath();
+                ctx.arc(endX-buttonW/2,y+h/2,1,0,360,false);
+                ctx.arc(endX-buttonW/2-4,y+h/2,1,0,360,false);
+                ctx.arc(endX-buttonW/2+4,y+h/2,1,0,360,false);
+                ctx.fillStyle="black";//填充颜色,默认是黑色
+                ctx.fill();//画实心圆
+                ctx.closePath();
+                ctx.restore();
+            };
+
+            moreButton.prototype.processMouseLeave= function (hitinfo) {
+                let newCell = new selectButton();
+                hitinfo.sheet.setCellType(hitinfo.row, hitinfo.col, newCell, GC.Spread.Sheets.SheetArea.viewport);
+                hitinfo.sheet.getCell(hitinfo.row, hitinfo.col).locked(false);
+            };
+
+            function selectButton() {
+            }
+
+            selectButton.prototype = new GC.Spread.Sheets.CellTypes.Text();
+
+            selectButton.prototype.paint = function (ctx, value, x, y, w, h, style, options){
+                GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this,arguments);
+            };
+            selectButton.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
+                return {
+                    x: x,
+                    y: y,
+                    row: context.row,
+                    col: context.col,
+                    cellStyle: cellStyle,
+                    cellRect: cellRect,
+                    sheetArea: context.sheetArea
+                };
+            };
+            selectButton.prototype.processMouseDown = function (hitinfo){
+                if(hitinfo.sheet.getCell(hitinfo.row,hitinfo.col).locked()!=true){
+                    let b1 = new moreButton();
+                    b1.marginLeft(cellWidth*4/5);
+                    hitinfo.sheet.setCellType(hitinfo.row, hitinfo.col, b1, GC.Spread.Sheets.SheetArea.viewport);
+                    hitinfo.sheet.getCell(hitinfo.row, hitinfo.col).locked(true);
+                }
+            };
+            return new selectButton();
+        };
+        sheet.setCellType(row, col,getSelectButton(header.headerWidth),GC.Spread.Sheets.SheetArea.viewport);
+
+    },
     chkIfEmpty: function(rObj, setting) {
         var rst = true;
         if (rObj) {
             for (var i = 0; i < setting.header.length; i++) {
-                if (rObj[setting.header[i]]) {
+                if (rObj[setting.header[i].dataCode]) {
                     rst = false;
                     break;
                 }
@@ -230,12 +345,13 @@ var sheetCommonObj = {
     },
     //add by zhong 2017-10-10
     //动态下拉框,配合EnterCell, args.sheet.repaint();
-    getDynamicCombo: function () {
+    getDynamicCombo: function (forLocked) {
         let ComboCellForActiveCell = function () { };
         ComboCellForActiveCell.prototype = new GC.Spread.Sheets.CellTypes.ComboBox();
         ComboCellForActiveCell.prototype.paintValue = function (ctx, value, x, y, w, h, style, options) {
             let sheet = options.sheet;
-            if (options.row === sheet.getActiveRowIndex() && options.col === sheet.getActiveColumnIndex()) {
+            if (options.row === sheet.getActiveRowIndex() && options.col === sheet.getActiveColumnIndex() && (!forLocked || forLocked && !sheet.getCell(options.row, options.col).locked())) {
+
                 GC.Spread.Sheets.CellTypes.ComboBox.prototype.paintValue.apply(this, arguments);
 
             } else {
@@ -244,9 +360,8 @@ var sheetCommonObj = {
         };
         ComboCellForActiveCell.prototype.getHitInfo = function (x, y, cellStyle, cellRect, options) {
             let sheet = options.sheet;
-            if (options.row === sheet.getActiveRowIndex() && options.col === sheet.getActiveColumnIndex()) {
+            if (options.row === sheet.getActiveRowIndex() && options.col === sheet.getActiveColumnIndex() && (!forLocked || forLocked && !sheet.getCell(options.row, options.col).locked())) {
                 return GC.Spread.Sheets.CellTypes.ComboBox.prototype.getHitInfo.apply(this, arguments);
-
             } else {
                 return GC.Spread.Sheets.CellTypes.Base.prototype.getHitInfo.apply(this, arguments);
             }
@@ -277,5 +392,68 @@ var sheetCommonObj = {
             sheet.getCell(beginRow + i, col).cellType(combo);
         }
         sheet.resumePaint();
+    },
+    //设置系统粘贴板数据,需要用户触发事件,直接调用会失败
+    copyTextToClipboard: function(text) {
+        var 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 {
+            var successful = document.execCommand('copy');
+            var msg = successful ? 'successful' : 'unsuccessful';
+            console.log('Copying text command was ' + msg);
+        } catch (err) {
+            console.log('Oops, unable to copy');
+        }
+        document.body.removeChild(textArea);
+    },
+    //获取选中区域的表格类型数据(可粘贴到excel)
+    getTableData: function (sheet, colSettings = null) {
+        let rst = '';
+        let sel = sheet.getSelections()[0];
+        let vRows = [];
+        for(let i = sel.row, len = sel.row + sel.rowCount; i < len; i++){
+            if(sheet.getCell(i, -1).visible()){
+                vRows.push(i);
+            }
+        }
+        for(let row of vRows){
+            rst += '\n';
+            for(let j = 0, jLen = sel.colCount; j <jLen; j++){
+                let col = sel.col + j;
+                if(sheet.getCell(-1, col).visible()){
+                    let v = '';
+                    if(colSettings && (colSettings[col]['data']['field'] === 'itemCharacterText' || colSettings[col]['data']['field'] === 'jobContentText')){
+                       // v += sheet.getText(row, col) ? sheet.getText(row, col).replace(new RegExp('\n', 'g'), '\v') : '';
+                        v += sheet.getText(row, col) ? `"${sheet.getText(row, col)}"` : '';
+                        if(j !== jLen - 1){
+                            v += '\t';
+                        }
+                    }
+                    else {
+                        if(j === jLen - 1){
+                            v += sheet.getText(row, col) ? sheet.getText(row, col).replace('\n', '') : '';
+                        }
+                        else {
+                            v += sheet.getText(row, col) ? sheet.getText(row, col).replace('\n', '') + '\t' : '\t';
+                        }
+                    }
+                    rst += v;
+                }
+            }
+        }
+        rst = rst.replace('\n', '');
+        return rst;
     }
 }

+ 38 - 6
public/web/socket/connection.js

@@ -2,25 +2,57 @@
  * Created by zhangweicheng on 2017/8/7.
  */
 socketObject={
+  roomInfo : null,
   connect:function () {
       // 连接socket服务器
       var hostName = window.location.hostname;
+      let me = this;
       socket = io('http://'+hostName+':3300');
       socket.on('connect', function () {
-
-          var roomID = projectObj.project.FeeRate.getActivateFeeRateFileID();
-          socket.emit('join', roomID);
+          me.roomInfo={
+              feeRate:me.getFeeRateRoomID(),
+              unitFile:me.getUnitFileRoomID()
+          }
+          socket.emit('join', {name:'feeRate',value:me.getFeeRateRoomID()});
+          socket.emit('join', {name:'unitFile',value:me.getUnitFileRoomID()});
           console.log('连接成功');
       });
 
       socket.on('feeRateChange', function(data) {
           //data = JSON.parse(data);
 
-          $("#message").html('费率文件已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载</a>');
+          $("#message").html('费率文件已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
           $("#notify").show();
           //alert('费率文件已经修改,请刷新页面');
           //window.location.reload();
-      })
-
+      });
+      socket.on('unitFileChange', function (data) {
+          data = JSON.parse(data);
+          /*console.log(data);
+          if (data.newValue === undefined) {
+              return false;
+          }*/
+          $("#message").html('市场单价已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
+          $("#notify").show();
+      });
+      socket.on('changeFileNotify', function (data) {//收到文件改变的消息
+          if(data.projectID==projectObj.project.ID()){//如果是同个项目,则给出提示,否则忽略
+                let preString ="";
+                if(data.name=='feeRate'){
+                    preString = "费率文件";
+                }
+                if(data.name=='unitFile'){
+                    preString = "单价文件";
+                }
+              $("#message").html(preString+'已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
+              $("#notify").show();
+          }
+      });
+  },
+  getFeeRateRoomID:function (){
+      return  projectObj.project.FeeRate.getActivateFeeRateFileID();
+  },
+  getUnitFileRoomID:function () {
+      return projectObj.project.projectGLJ.datas.constData.roomId?projectObj.project.projectGLJ.datas.constData.roomId:roomId;
   }
 }

+ 10 - 8
public/web/treeDataHelper.js

@@ -7,14 +7,14 @@ let tree_Data_Helper = {
     buildTreeNodeDirectly: function(data) {
         let topArr = [], rst = [], tmpNodes = {}, prefix = "id_";
         let private_getTopNode = function (idArr) {
-            let rst = null;
+            let tmpNodeRst = null;
             for (let i = 0; i < idArr.length; i++) {
-                if (tmpNodes[prefix + idArr[i]][ADHOC_PRE_ID] === EMPTY_ID_VAL) {
-                    rst = tmpNodes[prefix + idArr[i]];
+                if (parseInt(tmpNodes[prefix + idArr[i]][ADHOC_PRE_ID]) === EMPTY_ID_VAL) {
+                    tmpNodeRst = tmpNodes[prefix + idArr[i]];
                     break;
                 }
             }
-            return rst;
+            return tmpNodeRst;
         };
         let private_buildNodeData = function(parentItem, idArr) {
             let iter = [], nextNode = private_getTopNode(idArr);
@@ -37,15 +37,17 @@ let tree_Data_Helper = {
             data[i][ADHOC_PRE_ID] = EMPTY_ID_VAL;
             data[i][SUB_ID] = [];
             data[i][CHILDREN_NODE] = [];
-            if (data[i][P_ID] === EMPTY_ID_VAL) {
+            if (parseInt(data[i][P_ID]) === EMPTY_ID_VAL) {
                 topArr.push(data[i][NODE_ID]);
             }
         }
         for (let i = 0; i < data.length; i++) {
-            if (data[i][NEXT_ID] !== EMPTY_ID_VAL) {
-                tmpNodes[prefix + data[i][NEXT_ID]][ADHOC_PRE_ID] = data[i][NODE_ID];
+            if (parseInt(data[i][NEXT_ID]) !== EMPTY_ID_VAL) {
+                if (tmpNodes[prefix + data[i][NEXT_ID]] !== undefined){
+                    tmpNodes[prefix + data[i][NEXT_ID]][ADHOC_PRE_ID] = data[i][NODE_ID];
+                }
             }
-            if (data[i][P_ID] !== EMPTY_ID_VAL) {
+            if (parseInt(data[i][P_ID]) !== EMPTY_ID_VAL) {
                 tmpNodes[prefix + data[i][P_ID]][SUB_ID].push(data[i][NODE_ID]);
             }
         }

+ 32 - 1
public/web/tree_sheet/tree_sheet_controller.js

@@ -39,6 +39,7 @@ var TREE_SHEET_CONTROLLER = {
                         that.setTreeSelected(newNode);
                         that.sheet.setSelection(newNode.serialNo(), sels[0].col, 1, 1);
                         that.sheet.showRow(newNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
+                        cbTools.refreshFormulaNodes();
                     });
                 }
             }
@@ -51,9 +52,39 @@ var TREE_SHEET_CONTROLLER = {
                         that.sheet.deleteRows(sels[0].row, that.tree.selected.posterityCount() + 1);
                         that.setTreeSelected(that.tree.items[sels[0].row]);
                     });
+                    cbTools.refreshFormulaNodes();
                 }
             }
         };
+        controller.prototype.m_delete = function (nodes) {//删除选中的多行节点
+            var that = this, sels = this.sheet.getSelections();
+            if (this.tree.selected) {
+                if (this.tree.m_delete(nodes)) {
+                    TREE_SHEET_HELPER.massOperationSheet(this.sheet, function () {
+                        let rowCount = 0;
+                        for(let node of nodes){
+                            rowCount = rowCount+node.posterityCount() + 1;
+                        }
+                        that.sheet.deleteRows(sels[0].row, rowCount);
+                        that.setTreeSelected(that.tree.items[sels[0].row]);
+                        that.sheet.setSelection(sels[0].row,sels[0].col,1,sels[0].colCount);
+                    });
+                    cbTools.refreshFormulaNodes();
+                }
+            }
+        };
+        controller.prototype.singleDelete = function () {//只删除当前节点,不删除子节点
+            var that = this, sels = this.sheet.getSelections();
+            if (this.tree.selected) {
+                if (this.tree.singleDelete(this.tree.selected)) {
+                    TREE_SHEET_HELPER.massOperationSheet(this.sheet, function () {
+                        that.sheet.deleteRows(sels[0].row,1);
+                        that.setTreeSelected(that.tree.items[sels[0].row]);
+                    });
+                }
+            }
+        };
+
         controller.prototype.deleteNode = function (node,next) {
             var that = this;
             if (node){
@@ -63,9 +94,9 @@ var TREE_SHEET_CONTROLLER = {
                         that.sheet.deleteRows(row,1);
                         next?that.setTreeSelected(that.tree.items[row]):"";
                     });
+                    cbTools.refreshFormulaNodes();
                 }
             }
-
         };
         controller.prototype.upLevel = function () {
             var that = this;

+ 52 - 31
public/web/tree_sheet/tree_sheet_helper.js

@@ -47,29 +47,31 @@ var TREE_SHEET_HELPER = {
         return style;
     },
     loadSheetHeader: function (setting, sheet) {
-        if (setting.frozenCols) {
-            sheet.frozenColumnCount(setting.frozenCols);
-        }
-        sheet.setColumnCount(setting.cols.length);
-        sheet.setRowCount(setting.headRows, GC.Spread.Sheets.SheetArea.colHeader);
-        setting.headRowHeight.forEach(function (rowHeight, index) {
-            sheet.setRowHeight(index, rowHeight, GC.Spread.Sheets.SheetArea.colHeader);
-        });
-        setting.cols.forEach(function (col, index) {
-            var i, iRow = 0, cell;
-            for (i = 0; i < col.head.spanCols.length; i++) {
-                if (col.head.spanCols[i] !== 0) {
-                    cell = sheet.getCell(iRow, index, GC.Spread.Sheets.SheetArea.colHeader);
-                    cell.value(col.head.titleNames[i]).font(col.head.font).hAlign(col.head.hAlign[i]).vAlign(col.head.vAlign[i]).wordWrap(true);
-                }
-                if (col.head.spanCols[i] > 1 || col.head.spanRows[i] > 1) {
-                    sheet.addSpan(iRow, index, col.head.spanRows[i], col.head.spanCols[i], GC.Spread.Sheets.SheetArea.colHeader);
-                }
-                iRow += col.head.spanRows[i];
-            };
-            sheet.setColumnWidth(index, col.width);
-            sheet.setColumnVisible(index, col.visible && true);
-        });
+        this.massOperationSheet(sheet, function () {
+            if (setting.frozenCols) {
+                sheet.frozenColumnCount(setting.frozenCols);
+            }
+            sheet.setColumnCount(setting.cols.length);
+            sheet.setRowCount(setting.headRows, GC.Spread.Sheets.SheetArea.colHeader);
+            setting.headRowHeight.forEach(function (rowHeight, index) {
+                sheet.setRowHeight(index, rowHeight, GC.Spread.Sheets.SheetArea.colHeader);
+            });
+            setting.cols.forEach(function (col, index) {
+                var i, iRow = 0, cell;
+                for (i = 0; i < col.head.spanCols.length; i++) {
+                    if (col.head.spanCols[i] !== 0) {
+                        cell = sheet.getCell(iRow, index, GC.Spread.Sheets.SheetArea.colHeader);
+                        cell.value(col.head.titleNames[i]).font(col.head.font).hAlign(col.head.hAlign[i]).vAlign(col.head.vAlign[i]).wordWrap(true);
+                    }
+                    if (col.head.spanCols[i] > 1 || col.head.spanRows[i] > 1) {
+                        sheet.addSpan(iRow, index, col.head.spanRows[i], col.head.spanCols[i], GC.Spread.Sheets.SheetArea.colHeader);
+                    }
+                    iRow += col.head.spanRows[i];
+                };
+                sheet.setColumnWidth(index, col.width);
+                sheet.setColumnVisible(index, col.visible && true);
+            });
+        });      
     },
     protectdSheet: function (sheet) {
         var option = {
@@ -101,10 +103,21 @@ var TREE_SHEET_HELPER = {
     },
     refreshTreeNodeData: function (setting, sheet, nodes, recursive) {
         nodes.forEach(function (node) {
+            let iRow = node.serialNo();
+            if(typeof projectObj !== 'undefined'){
+                let nodeStyle = projectObj.getNodeColorStyle(node);
+                if(nodeStyle){
+                    sheet.setStyle(iRow, -1, nodeStyle);
+                }
+            }
             setting.cols.forEach(function (colSetting, iCol) {
-                var iRow = node.serialNo();
                 var cell = sheet.getCell(iRow, iCol, GC.Spread.Sheets.SheetArea.viewport);
-
+                if(typeof projectObj !== 'undefined'){
+                    let boldFontStyle = projectObj.getBoldFontStyle(node, colSetting);
+                    if(boldFontStyle){
+                        sheet.setStyle(iRow, iCol, boldFontStyle);
+                    }
+                }
                 // var getFieldText = function () {
                 //     var fields = colSetting.data.field.split('.');
                 //     var validField = fields.reduce(function (field1, field2) {
@@ -131,6 +144,10 @@ var TREE_SHEET_HELPER = {
                     }
                     return data;
                 };
+                if(colSetting.data.field=="quantity"){
+                    let tag = node.data.quantityEXP?node.data.quantityEXP:'';
+                    sheet.setTag(iRow, iCol,tag);
+                }
                 if (colSetting.data.getText && Object.prototype.toString.apply(colSetting.data.getText) === "[object Function]") {
                     cell.value(colSetting.data.getText(node));
                 } else {
@@ -157,6 +174,7 @@ var TREE_SHEET_HELPER = {
     },
     showTreeData: function (setting, sheet, tree) {
         let indent = 20;
+        let levelIndent = -5;
         let halfBoxLength = 5;
         let halfExpandLength = 3;
 
@@ -225,7 +243,7 @@ var TREE_SHEET_HELPER = {
             let showTreeLine = true;
             if (!node) { return; }
 
-            let centerX = Math.floor(x) + node.depth() * indent + indent / 2;
+            let centerX = Math.floor(x) + node.depth() * indent + node.depth() * levelIndent + indent / 2;
             let x1 = centerX + indent / 2;
             let centerY = Math.floor((y + (y + h)) / 2);
             let y1;
@@ -251,7 +269,7 @@ var TREE_SHEET_HELPER = {
             }
             // Draw Parent Line
             if (showTreeLine) {
-                var parent = node.parent, parentCenterX = centerX - indent;
+                var parent = node.parent, parentCenterX = centerX - indent - levelIndent;
                 while (parent) {
                     if (!parent.isLast()) {
                         if (parentCenterX < x + w) {
@@ -259,11 +277,11 @@ var TREE_SHEET_HELPER = {
                         }
                     }
                     parent = parent.parent;
-                    parentCenterX -= indent;
+                    parentCenterX -= (indent + levelIndent);
                 }
             };
             // Draw Text
-            x = x + (node.depth() + 1) * indent;
+            x = x + (node.depth() + 1) * indent +  node.depth() * levelIndent;
             GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
         };
         TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
@@ -282,7 +300,7 @@ var TREE_SHEET_HELPER = {
             let node = tree.items[hitinfo.row];
             tree.selected = node;
             if (!node || node.children.length === 0) { return; }
-            let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + indent / 2;
+            let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
             let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
 
             if (hitinfo.x > centerX - halfBoxLength && hitinfo.x < centerX + halfBoxLength && hitinfo.y > centerY - halfBoxLength && hitinfo.y < centerY + halfBoxLength) {
@@ -317,7 +335,9 @@ var TREE_SHEET_HELPER = {
         TipCellType.prototype.processMouseEnter = function (hitinfo) {
             let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
             let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
-            if(tag !== undefined && tag) {
+            if(setting.cols[hitinfo.col].data.field=="quantity"){
+                text = tag;
+            }else if(tag !== undefined && tag) {
                 text = tag;
             }
             if (setting.pos && text && text !== '') {
@@ -356,6 +376,7 @@ var TREE_SHEET_HELPER = {
                 sheet.defaults.rowHeight = setting.defaultRowHeight;
             }
             sheet.setRowCount(tree.count() + setting.emptyRows, GC.Spread.Sheets.SheetArea.viewport);
+            sheet.getRange(tree.count(), -1, setting.emptyRows, -1).locked(true);
             setting.cols.forEach(function (colSetting, iCol) {
                 sheet.setStyle(-1, iCol, TREE_SHEET_HELPER.getSheetCellStyle(colSetting));
                 if (colSetting.showHint) {

+ 12 - 7
socket.js

@@ -11,25 +11,30 @@ const socketIO = socket(3300);
 
 // socket.io相关操作
 socketIO.on('connection', function(socket) {
-    let roomId = '';
+    let roomInfo = {};
     console.log("new connection");
     // 加入房间
     socket.on('join', function(data) {
-        roomId = data;
-        socket.join(data);
+        roomInfo[data.name] = data.value;
+        socket.join(data.value);
     });
 
 
     // 数据更改通知
-    socket.on('dataNotify', function(data) {
-        socket.broadcast.to(roomId).emit('dataChange', data);
+    socket.on('unitFileChangeNotify', function(data) {
+        console.log(roomInfo);
+        socket.broadcast.to(roomInfo['unitFile']).emit('unitFileChange', data);
     });
     socket.on('feeRateChangeNotify', function(data) {
-        socket.broadcast.to(data).emit('feeRateChange', data);
+        socket.broadcast.to(roomInfo['feeRate']).emit('feeRateChange', data);
     });
-    socket.on('changeActivateFeeRate', function(data) {
+    socket.on('changeNewRoom', function(data) {
+        if(data.projectID){//如果有项目ID,则通知同一个项目在其它地方被打开的页面
+            socket.broadcast.to(data.oldRoom).emit('changeFileNotify', data);
+        }
         socket.leave(data.oldRoom);
         socket.join(data.newRoom);
+        roomInfo[data.name]=data.newRoom;
     });
 
 });

+ 43 - 5
test/demo/stringTest.js

@@ -55,9 +55,47 @@ var strUtil = require('../../public/stringUtil');
 //     t.end();
 // })
 
-test('test typeof', function(t){
-    t.equal(typeof(true), "boolean");
-    t.equal((typeof("abc")).toUpperCase(), "STRING");
-    t.equal(typeof(1), "number");
+// test('test typeof', function(t){
+//     t.equal(typeof(true), "boolean");
+//     t.equal((typeof("abc")).toUpperCase(), "STRING");
+//     t.equal(typeof(1), "number");
+//     t.end();
+// })
+
+test('test 千分位', function(t){
+    let num = 123.01;
+    t.equal( strUtil.comdify(num.toString()), "123.01");
+    num = 123456.01;
+    t.equal( strUtil.comdify(num.toString()), "123,456.01");
+    num = 1234567.01;
+    t.equal( strUtil.comdify(num.toString()), "1,234,567.01");
+    num = 1234567;
+    t.equal( strUtil.comdify(String(num)), "1,234,567");
+    t.end();
+})
+
+
+test('test number to Chinese', function(t){
+    t.equal(strUtil.convertToCaptionNum(0, false, false), '零');
+    t.equal(strUtil.convertToCaptionNum(0, true, false), '零元整');
+    t.equal(strUtil.convertToCaptionNum(0.68, true, false), '零元六角八分');
+    t.equal(strUtil.convertToCaptionNum(0.68, true, true), '零元陆角捌分');
+    t.equal(strUtil.convertToCaptionNum(10, false, false), '一十');
+    t.equal(strUtil.convertToCaptionNum(10, false, true), '壹拾');
+    t.equal(strUtil.convertToCaptionNum(11, false, false), '一十一');
+    t.equal(strUtil.convertToCaptionNum(11, false, true), '壹拾壹');
+    t.equal(strUtil.convertToCaptionNum(12, false, false), '一十二');
+    t.equal(strUtil.convertToCaptionNum(12, false, true), '壹拾贰');
+    t.equal(strUtil.convertToCaptionNum(21, false, false), '二十一');
+    t.equal(strUtil.convertToCaptionNum(21, false, true), '贰拾壹');
+
+    t.equal(strUtil.convertToCaptionNum(0.123456789, false, false), '零点一二三四五六七八九');
+    t.equal(strUtil.convertToCaptionNum(10.123456789, false, false), '一十点一二三四五六七八九');
+
+    t.equal(strUtil.convertToCaptionNum(123456789.15, false, false), '一亿二千三百四十五万六千七百八十九点一五');
+    t.equal(strUtil.convertToCaptionNum(123456789.15, false, true), '壹億贰仟叁佰肆拾伍萬陆仟柒佰捌拾玖点壹伍');
+    t.equal(strUtil.convertToCaptionNum(123456789.15, true, false), '一亿二千三百四十五万六千七百八十九元一角五分');
+    t.equal(strUtil.convertToCaptionNum(123456789.15, true, true), '壹億贰仟叁佰肆拾伍萬陆仟柒佰捌拾玖元壹角伍分');
+
     t.end();
-})
+})

+ 3 - 0
test/unit/reports/test_cover_01.js

@@ -31,6 +31,9 @@ require('../../../modules/fee_rates/models/fee_rates');
 // 引入人工系数模块
 require('../../../modules/main/models/labour_coe_model');
 require('../../../modules/main/models/calc_program_model');
+//config.setupCache();
+let cfgCacheUtil = require("../../../config/cacheCfg");
+cfgCacheUtil.setupDftCache();
 
 let fsUtil = require("../../../public/fsUtil");
 

+ 118 - 0
test/unit/reports/test_cover_02.js

@@ -0,0 +1,118 @@
+/**
+ * Created by Tony on 2017/12/18.
+ */
+
+let test = require('tape');
+import JpcEx from "../../../modules/reports/rpt_component/jpc_ex";
+import JV from "../../../modules/reports/rpt_component/jpc_value_define";
+let mongoose = require("mongoose");
+let fileUtils = require("../../../modules/common/fileUtils");
+let path = require('path');
+let dbm = require("../../../config/db/db_manager");
+let rpt_cfg = require('./rpt_cfg');
+dbm.connect();
+let consts = require('../../../modules/main/models/project_consts');
+let projectConsts = consts.projectConst;
+fileUtils.getGlobbedFiles('../../../modules/complementary_glj_lib/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+});
+
+fileUtils.getGlobbedFiles('../../../modules/ration_glj/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+});
+
+//引入报表模块
+fileUtils.getGlobbedFiles('../../../modules/reports/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+})
+
+//暂时引入其它模块的model
+require('../../../modules/fee_rates/models/fee_rates');
+// 引入人工系数模块
+require('../../../modules/main/models/labour_coe_model');
+require('../../../modules/main/models/calc_program_model');
+//config.setupCache();
+let cfgCacheUtil = require("../../../config/cacheCfg");
+cfgCacheUtil.setupDftCache();
+
+let fsUtil = require("../../../public/fsUtil");
+
+let prjMdl = require('../../../modules/pm/models/project_model');
+let projectDataMdl = require('../../../modules/main/models/project');
+let demoPrjId = - 1;
+let demoRptId = 229, pagesize = "A4";
+
+let userId_Leng = 1142; //小冷User Id
+demoPrjId = 1296; //QA:
+//*/
+ let userId_Dft = userId_Leng;
+ /*/
+let userId_Dft = 76075;
+//*/
+
+let rptTplFacade = require("../../../modules/reports/facade/rpt_template_facade");
+let rptTplDataFacade = require("../../../modules/reports/facade/rpt_tpl_data_facade");
+
+import rptDataExtractor from "../../../modules/reports/util/rpt_construct_data_util";
+
+let fs = require('fs');
+//设置Date Format函数
+fs.readFile(__dirname.slice(0, __dirname.length - 18) + '/public/web/date_util.js', 'utf8', 'r', function (err, data) {
+    eval(data);
+});
+
+//*
+test('测试 - 打开模板: 封-1 ', function (t) {
+    rptTplFacade.getRptTemplate(demoRptId).then(function(rptTpl) {
+        let rptDataUtil = new rptDataExtractor();
+        rptDataUtil.initialize(rptTpl._doc);
+        let filter = rptDataUtil.getDataRequestFilter();
+        // console.log(filter);
+        //正常应该根据报表模板定义的数据类型来请求数据
+        rptTplDataFacade.prepareProjectData(userId_Dft, demoPrjId, filter, function (err, msg, rawDataObj) {
+            if (!err) {
+                try {
+                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataObject.js");
+                    let tplData = rptDataUtil.assembleData(rawDataObj);
+                    //build the report
+                    let printCom = JpcEx.createNew();
+                    rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pagesize;
+                    let defProperties = rpt_cfg;
+                    let dftOption = JV.PAGING_OPTION_NORMAL;
+                    printCom.initialize(rptTpl);
+                    printCom.analyzeData(rptTpl, tplData, defProperties, dftOption);
+                    let maxPages = printCom.totalPages;
+                    let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
+                    if (pageRst) {
+                        fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
+                    } else {
+                        console.log("oh! no pages were created!");
+                    }
+                } catch (ex) {
+                    console.log(ex);
+                    t.pass('pass with exception!');
+                    t.end();
+                }
+
+                t.pass('pass succeeded!');
+                t.end();
+            } else {
+                console.log(msg);
+                t.pass('pass with error!');
+                t.end();
+            }
+        })
+    });
+});
+//*/
+
+test('close the connection', function (t) {
+    setTimeout(function () {
+        mongoose.disconnect();
+        t.pass('closing db connection');
+        t.end();
+    }, 3000);
+    // mongoose.disconnect();
+    // t.pass('closing db connection');
+    // t.end();
+});

+ 0 - 0
test/unit/reports/test_tpl_09.js


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio