Explorar o código

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

Conflicts:
	modules/reports/routes/report_router.js
TonyKang %!s(int64=8) %!d(string=hai) anos
pai
achega
e81afc35e2
Modificáronse 100 ficheiros con 7689 adicións e 763 borrados
  1. 337 0
      lib/JSExpressionEval_src/Date.js
  2. 1050 0
      lib/JSExpressionEval_src/Evaluator.js
  3. 129 0
      lib/JSExpressionEval_src/ExpressionTester.htm
  4. 141 0
      lib/JSExpressionEval_src/JsHashMap.js
  5. 133 0
      lib/JSExpressionEval_src/License.txt
  6. 105 0
      lib/JSExpressionEval_src/Stack.js
  7. 108 0
      lib/JSExpressionEval_src/StackImpl.htm
  8. 119 0
      lib/JSExpressionEval_src/StackTester.htm
  9. 54 0
      lib/JSExpressionEval_src/TokanTester.htm
  10. 493 0
      lib/JSExpressionEval_src/Tokanizer.js
  11. 47 0
      lib/JSExpressionEval_src/VBEval.htm
  12. 123 0
      lib/JSExpressionEval_src/postfix.txt
  13. 657 0
      lib/bootstrap/bootstrap-paginator.js
  14. 1 1
      lib/ztree/jquery.ztree.exedit.js
  15. 33 28
      modules/bills_lib/routes/bills_lib_routes.js
  16. 28 0
      modules/bills_lib/routes/maintain_bills_routes.js
  17. 9 2
      modules/common/base/base_controller.js
  18. 14 4
      modules/common/base/base_model.js
  19. 38 0
      modules/common/const/glj_type_const.js
  20. 14 0
      modules/common/const/log_type_const.js
  21. 7 1
      modules/common/helper/mongoose_helper.js
  22. 30 0
      modules/common/std/schemas/std_mix_ratio.js
  23. 13 0
      modules/common/std/schemas/std_ration_lib_glj_list.js
  24. 48 0
      modules/common/std/std_mix_ratio_model.js
  25. 51 0
      modules/common/std/std_ration_lib_glj_list_model.js
  26. 16 11
      modules/fee_rates/routes/fee_rates_route.js
  27. 132 19
      modules/glj/controllers/glj_controller.js
  28. 361 25
      modules/glj/models/glj_list_model.js
  29. 0 70
      modules/glj/models/glj_repository_model.js
  30. 83 0
      modules/glj/models/mix_ratio_model.js
  31. 20 5
      modules/glj/models/schemas/glj.js
  32. 0 29
      modules/glj/models/schemas/glj_repository.js
  33. 35 0
      modules/glj/models/schemas/mix_ratio.js
  34. 10 4
      modules/glj/models/schemas/unit_price.js
  35. 4 1
      modules/glj/models/schemas/unit_price_file.js
  36. 43 30
      modules/glj/models/unit_price_model.js
  37. 6 2
      modules/glj/routes/glj_router.js
  38. 1 0
      modules/main/models/bills.js
  39. 2 1
      modules/main/models/proj_counter.js
  40. 5 0
      modules/main/models/project.js
  41. 3 2
      modules/main/models/project_consts.js
  42. 2 0
      modules/main/models/ration.js
  43. 10 7
      modules/main/routes/GLJ_route.js
  44. 11 7
      modules/main/routes/bills_route.js
  45. 22 0
      modules/main/routes/main_route.js
  46. 10 6
      modules/main/routes/project_route.js
  47. 10 7
      modules/main/routes/ration_route.js
  48. 2 0
      modules/pm/controllers/copy_proj_controller.js
  49. 1 1
      modules/pm/models/project_schema.js
  50. 39 27
      modules/pm/routes/pm_route.js
  51. 31 11
      modules/ration_glj/facade/glj_calculate_facade.js
  52. 633 0
      modules/ration_glj/facade/quantity_detail_facade.js
  53. 3 3
      modules/ration_glj/facade/ration_ass_facade.js
  54. 0 2
      modules/ration_glj/facade/ration_coe_facade.js
  55. 23 6
      modules/ration_glj/facade/ration_glj_facade.js
  56. 21 0
      modules/ration_glj/models/quantity_detail.js
  57. 2 2
      modules/ration_glj/models/ration_glj.js
  58. 43 2
      modules/ration_glj/models/ration_glj_temp.js
  59. 2 1
      modules/ration_repository/models/glj_repository.js
  60. 27 21
      modules/ration_repository/routes/ration_front_end_routes.js
  61. 44 41
      modules/ration_repository/routes/ration_rep_routes.js
  62. 14 3
      modules/reports/routes/report_router.js
  63. 16 13
      modules/reports/routes/rpt_tpl_router.js
  64. 6 5
      modules/reports/routes/rpt_tpl_router_fe.js
  65. 41 5
      modules/templates/routes/bills_template_router.js
  66. 4 3
      modules/users/controllers/login_controller.js
  67. 31 5
      modules/users/controllers/user_controller.js
  68. 104 0
      modules/users/models/log_model.js
  69. 29 0
      modules/users/models/schema/log.js
  70. 24 7
      modules/users/models/schema/user.js
  71. 8 16
      modules/users/models/user_model.js
  72. 3 3
      modules/users/routes/user_route.js
  73. 66 0
      modules/volume_price/models/volume_price_model.js
  74. 33 0
      modules/volume_price/models/volume_price_schema.js
  75. 3 0
      package.json
  76. 11 10
      public/calc_util.js
  77. 15 17
      public/web/id_tree.js
  78. 8 2
      public/web/sheet/sheet_common.js
  79. 10 1
      public/web/tree_sheet/tree_sheet_helper.js
  80. 5 104
      server.js
  81. 27 0
      socket.js
  82. 441 0
      test/calculation/test_ration_calc.js
  83. 9 67
      test/tmp_data/bills_grid_setting.js
  84. 209 0
      test/tmp_data/test_bills_calc/bills_calc_base.js
  85. 86 0
      test/tmp_data/test_ration_calc/ration_calc_base.js
  86. 3 0
      web/building_saas/fee_rates/fee_rate.html
  87. 20 2
      web/building_saas/main/html/main.html
  88. 45 77
      web/building_saas/main/js/models/bills_calc.js
  89. 61 0
      web/building_saas/main/js/calc/calc_fees.js
  90. 330 0
      web/building_saas/main/js/calc/ration_calc.js
  91. 29 1
      web/building_saas/main/js/controllers/project_controller.js
  92. 1 1
      web/building_saas/main/js/main_ajax.js
  93. 21 1
      web/building_saas/main/js/models/bills.js
  94. 38 26
      web/building_saas/main/js/models/cache_tree.js
  95. 3 1
      web/building_saas/main/js/models/main_consts.js
  96. 13 1
      web/building_saas/main/js/models/project.js
  97. 453 0
      web/building_saas/main/js/models/quantity_detail.js
  98. 24 11
      web/building_saas/main/js/models/ration.js
  99. 6 2
      web/building_saas/main/js/models/ration_ass.js
  100. 0 0
      web/building_saas/main/js/models/ration_calc.js

+ 337 - 0
lib/JSExpressionEval_src/Date.js

@@ -0,0 +1,337 @@
+// ===================================================================
+// Author: Matt Kruse <matt@mattkruse.com>
+// WWW: http://www.mattkruse.com/
+//
+// NOTICE: You may use this code for any purpose, commercial or
+// private, without any further permission from the author. You may
+// remove this notice from your final code if you wish, however it is
+// appreciated by the author if at least my web site address is kept.
+//
+// You may *NOT* re-distribute this code in any way except through its
+// use. That means, you can include it in your product, or your web
+// site, or any other form where the code is actually being used. You
+// may not put the plain javascript up on your site for download or
+// include it in your javascript libraries for download. 
+// If you wish to share this code with others, please just point them
+// to the URL instead.
+// Please DO NOT link directly to my .js files from your site. Copy
+// the files to your server and use them there. Thank you.
+// ===================================================================
+
+// HISTORY
+// ------------------------------------------------------------------
+// May 17, 2003: Fixed bug in parseDate() for dates <1970
+// March 11, 2003: Added parseDate() function
+// March 11, 2003: Added "NNN" formatting option. Doesn't match up
+//                 perfectly with SimpleDateFormat formats, but 
+//                 backwards-compatability was required.
+
+// ------------------------------------------------------------------
+// These functions use the same 'format' strings as the 
+// java.text.SimpleDateFormat class, with minor exceptions.
+// The format string consists of the following abbreviations:
+// 
+// Field        | Full Form          | Short Form
+// -------------+--------------------+-----------------------
+// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
+// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
+//              | NNN (abbr.)        |
+// Day of Month | dd (2 digits)      | d (1 or 2 digits)
+// Day of Week  | EE (name)          | E (abbr)
+// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
+// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
+// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
+// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
+// Minute       | mm (2 digits)      | m (1 or 2 digits)
+// Second       | ss (2 digits)      | s (1 or 2 digits)
+// AM/PM        | a                  |
+//
+// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
+// Examples:
+//  "MMM d, y" matches: January 01, 2000
+//                      Dec 1, 1900
+//                      Nov 20, 00
+//  "M/d/yy"   matches: 01/20/00
+//                      9/2/00
+//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
+// ------------------------------------------------------------------
+
+var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+function LZ(x) {return(x<0||x>9?"":"0")+x}
+
+// ------------------------------------------------------------------
+// isDate ( date_string, format_string )
+// Returns true if date string matches format of format string and
+// is a valid date. Else returns false.
+// It is recommended that you trim whitespace around the value before
+// passing it to this function, as whitespace is NOT ignored!
+// ------------------------------------------------------------------
+function isDate(val,format) {
+	var date=getDateFromFormat(val,format);
+	if (date==0) { return false; }
+	return true;
+	}
+
+// -------------------------------------------------------------------
+// compareDates(date1,date1format,date2,date2format)
+//   Compare two date strings to see which is greater.
+//   Returns:
+//   1 if date1 is greater than date2
+//   0 if date2 is greater than date1 of if they are the same
+//  -1 if either of the dates is in an invalid format
+// -------------------------------------------------------------------
+function compareDates(date1,dateformat1,date2,dateformat2) {
+	var d1=getDateFromFormat(date1,dateformat1);
+	var d2=getDateFromFormat(date2,dateformat2);
+	if (d1==0 || d2==0) {
+		return -1;
+		}
+	else if (d1 > d2) {
+		return 1;
+		}
+	return 0;
+	}
+
+// ------------------------------------------------------------------
+// formatDate (date_object, format)
+// Returns a date in the output format specified.
+// The format string uses the same abbreviations as in getDateFromFormat()
+// ------------------------------------------------------------------
+function formatDate(date,format) {
+	format=format+"";
+	var result="";
+	var i_format=0;
+	var c="";
+	var token="";
+	var y=date.getYear()+"";
+	var M=date.getMonth()+1;
+	var d=date.getDate();
+	var E=date.getDay();
+	var H=date.getHours();
+	var m=date.getMinutes();
+	var s=date.getSeconds();
+	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
+	// Convert real date parts into formatted versions
+	var value=new Object();
+	if (y.length < 4) {y=""+(y-0+1900);}
+	value["y"]=""+y;
+	value["yyyy"]=y;
+	value["yy"]=y.substring(2,4);
+	value["M"]=M;
+	value["MM"]=LZ(M);
+	value["MMM"]=MONTH_NAMES[M-1];
+	value["NNN"]=MONTH_NAMES[M+11];
+	value["d"]=d;
+	value["dd"]=LZ(d);
+	value["E"]=DAY_NAMES[E+7];
+	value["EE"]=DAY_NAMES[E];
+	value["H"]=H;
+	value["HH"]=LZ(H);
+	if (H==0){value["h"]=12;}
+	else if (H>12){value["h"]=H-12;}
+	else {value["h"]=H;}
+	value["hh"]=LZ(value["h"]);
+	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
+	value["k"]=H+1;
+	value["KK"]=LZ(value["K"]);
+	value["kk"]=LZ(value["k"]);
+	if (H > 11) { value["a"]="PM"; }
+	else { value["a"]="AM"; }
+	value["m"]=m;
+	value["mm"]=LZ(m);
+	value["s"]=s;
+	value["ss"]=LZ(s);
+	while (i_format < format.length) {
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+			}
+		if (value[token] != null) { result=result + value[token]; }
+		else { result=result + token; }
+		}
+	return result;
+	}
+	
+// ------------------------------------------------------------------
+// Utility functions for parsing in getDateFromFormat()
+// ------------------------------------------------------------------
+function _isInteger(val) {
+	var digits="1234567890";
+	for (var i=0; i < val.length; i++) {
+		if (digits.indexOf(val.charAt(i))==-1) { return false; }
+		}
+	return true;
+	}
+function _getInt(str,i,minlength,maxlength) {
+	for (var x=maxlength; x>=minlength; x--) {
+		var token=str.substring(i,i+x);
+		if (token.length < minlength) { return null; }
+		if (_isInteger(token)) { return token; }
+		}
+	return null;
+	}
+	
+// ------------------------------------------------------------------
+// getDateFromFormat( date_string , format_string )
+//
+// This function takes a date string and a format string. It matches
+// If the date string matches the format string, it returns the 
+// getTime() of the date. If it does not match, it returns 0.
+// ------------------------------------------------------------------
+function getDateFromFormat(val,format) {
+	val=val+"";
+	format=format+"";
+	var i_val=0;
+	var i_format=0;
+	var c="";
+	var token="";
+	var token2="";
+	var x,y;
+	var now=new Date();
+	var year=now.getYear();
+	var month=now.getMonth()+1;
+	var date=1;
+	var hh=now.getHours();
+	var mm=now.getMinutes();
+	var ss=now.getSeconds();
+	var ampm="";
+	
+	while (i_format < format.length) {
+		// Get next token from format string
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+			}
+
+		// Extract contents of value based on format token
+		if (token=="yyyy" || token=="yy" || token=="y") {
+			if (token=="yyyy") { x=4;y=4; }
+			if (token=="yy")   { x=2;y=2; }
+			if (token=="y")    { x=2;y=4; }
+			year=_getInt(val,i_val,x,y);
+
+			if (year==null) { return 0; }
+			i_val += year.length;
+			if (year.length==2) {
+				if (year > 70) { year=1900+(year-0); }
+				else { year=2000+(year-0); }
+				}
+			}
+		else if (token=="MMM"||token=="NNN"){
+			month=0;
+			for (var i=0; i<MONTH_NAMES.length; i++) {
+				var month_name=MONTH_NAMES[i];
+				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
+					if (token=="MMM"||(token=="NNN"&&i>11)) {
+						month=i+1;
+						if (month>12) { month -= 12; }
+						i_val += month_name.length;
+						break;
+						}
+					}
+				}
+			if ((month < 1)||(month>12)){return 0;}
+			}
+		else if (token=="EE"||token=="E"){
+			for (var i=0; i<DAY_NAMES.length; i++) {
+				var day_name=DAY_NAMES[i];
+				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
+					i_val += day_name.length;
+					break;
+					}
+				}
+			}
+		else if (token=="MM"||token=="M") {
+			month=_getInt(val,i_val,token.length,2);
+			if(month==null||(month<1)||(month>12)){return 0;}
+			i_val+=month.length;}
+		else if (token=="dd"||token=="d") {
+			date=_getInt(val,i_val,token.length,2);
+			if(date==null||(date<1)||(date>31)){return 0;}
+			i_val+=date.length;}
+		else if (token=="hh"||token=="h") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<1)||(hh>12)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="HH"||token=="H") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<0)||(hh>23)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="KK"||token=="K") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<0)||(hh>11)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="kk"||token=="k") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<1)||(hh>24)){return 0;}
+			i_val+=hh.length;hh--;}
+		else if (token=="mm"||token=="m") {
+			mm=_getInt(val,i_val,token.length,2);
+			if(mm==null||(mm<0)||(mm>59)){return 0;}
+			i_val+=mm.length;}
+		else if (token=="ss"||token=="s") {
+			ss=_getInt(val,i_val,token.length,2);
+			if(ss==null||(ss<0)||(ss>59)){return 0;}
+			i_val+=ss.length;}
+		else if (token=="a") {
+			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
+			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
+			else {return 0;}
+			i_val+=2;}
+		else {
+			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
+			else {i_val+=token.length;}
+			}
+		}
+	// If there are any trailing characters left in the value, it doesn't match
+	if (i_val != val.length) { return 0; }
+	// Is date valid for month?
+	if (month==2) {
+		// Check for leap year
+		if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
+			if (date > 29){ return 0; }
+			}
+		else { if (date > 28) { return 0; } }
+		}
+	if ((month==4)||(month==6)||(month==9)||(month==11)) {
+		if (date > 30) { return 0; }
+		}
+	// Correct hours value
+	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
+	else if (hh>11 && ampm=="AM") { hh-=12; }
+	var newdate=new Date(year,month-1,date,hh,mm,ss);
+	return newdate.getTime();
+	}
+
+// ------------------------------------------------------------------
+// parseDate( date_string [, prefer_euro_format] )
+//
+// This function takes a date string and tries to match it to a
+// number of possible date formats to get the value. It will try to
+// match against the following international formats, in this order:
+// y-M-d   MMM d, y   MMM d,y   y-MMM-d   d-MMM-y  MMM d
+// M/d/y   M-d-y      M.d.y     MMM-d     M/d      M-d
+// d/M/y   d-M-y      d.M.y     d-MMM     d/M      d-M
+// A second argument may be passed to instruct the method to search
+// for formats like d/M/y (european format) before M/d/y (American).
+// Returns a Date object or null if no patterns match.
+// ------------------------------------------------------------------
+function parseDate(val) {
+	var preferEuro=(arguments.length==2)?arguments[1]:false;
+	generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
+	monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
+	dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
+	var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
+	var d=null;
+	for (var i=0; i<checkList.length; i++) {
+		var l=window[checkList[i]];
+		for (var j=0; j<l.length; j++) {
+			d=getDateFromFormat(val,l[j]);
+			if (d!=0) { return new Date(d); }
+			}
+		}
+	return null;
+	}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1050 - 0
lib/JSExpressionEval_src/Evaluator.js


+ 129 - 0
lib/JSExpressionEval_src/ExpressionTester.htm

@@ -0,0 +1,129 @@
+   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1250">
+<meta name="generator" content="PSPad editor, www.pspad.com">
+<title>Expression Converter</title>
+<style>
+body     {font-family: Verdana,Tahoma; font-size: 8px; font-weight: normal;}
+input    {font-family: Verdana,Tahoma; font-size: 8px; font-weight: normal;}
+textarea {font-family: Verdana,Tahoma; font-size: 8px; font-weight: normal;}
+button   {font-family: Verdana,Tahoma; font-size: 9px; font-weight: normal; height: 24px;}
+table, td {font-family: Verdana,Tahoma; font-size: 8px; font-weight: normal;}
+
+</style>
+<script src="Date.js" type="text/javascript"></script>
+<script src="Stack.js" type="text/javascript"></script>
+<script src="Tokanizer.js" type="text/javascript"></script>
+<script src="Evaluator.js" type="text/javascript"></script>
+<script language="vbscript">
+'5*log(1000)*-2/3+MAX(4,6 MOD 7) 
+'5*log(1000)*-2/3+6 
+'5*6.90775527898214*-2/3+6 
+'34.5387763949107*-2/3+6 
+'-69.0775527898214/3+6 
+'-23.0258509299405+6 
+'-17.0258509299405
+'-17.025850929940457
+'InputBox "6 Mod 7", "Result", (6 Mod 7)
+</script>
+<script language="javascript">
+//alert (6 % 7);
+
+var exp = new Expression("");
+
+function Convert()
+{
+    var frm = document.frmMain;
+    var arrToks;
+    var arrPFix;
+    var strExp;
+    var intCntr;
+
+    strExp = frm.txtExp.value;
+    if (strExp == null || strExp == undefined)
+    {
+        alert ("No expression is specified!");
+        return false;
+    }
+
+    exp.Expression(strExp);
+    frm.txtResult.value = exp.Parse();
+    return false;
+}
+
+function Compute()
+{
+    var frm;
+    var strExp;
+
+    frm = document.frmMain;
+    strExp = frm.txtExp.value;
+    exp.Expression(strExp);
+    frm.txtResult.value = exp.Evaluate();
+}
+
+function AddVar()
+{
+    var frm;
+
+    frm = document.frmMain;
+    exp.AddVariable(frm.txtVarName.value, frm.txtVarValue.value);
+    return true;
+}
+
+function Reset()
+{
+	exp.Reset();
+}
+</script>
+</head>
+<body>
+<form id="frmMain" name="frmMain">
+	<table>
+    	<tr>
+        	<td>
+        		<table>
+            		<tr>
+                		<td>Expression : </td>
+                		<td><input type="text" size="50" name="txtExp" id="txtExp" /></td>
+            		</tr>
+            		<tr>
+                		<td>&nbsp;</td>
+                		<td><textarea cols="58" rows="5" name="txtResult" id="txtResult"></textarea></td>
+            		</tr>
+            		<tr>
+                		<td colspan="2" align="right">
+                    		<a href="#" name="btnConvert" id="btnConvert" onClick="javascript:Convert();">PostFix</a>
+                    		&nbsp;
+                    		<a href="#" name="btnCompute" id="btnCompute" onClick="javascript:Compute();">Evaluate</a>
+                    		&nbsp;
+                    		<a href="#" name="btnReset" id="btnReset" onClick="javascript:Reset();">Reset</a>
+                		</td>
+            		</tr>
+        		</table>
+			</td>
+		</tr>
+		<tr>
+			<td>
+        		<table>
+            		<tr>
+                		<td>Variable Name : </td>
+                		<td><input type="text" size="50" name="txtVarName" id="txtVarName" /></td>
+            		</tr>
+            		<tr>
+                		<td>Variable Value : </td>
+                		<td><input type="text" size="50" name="txtVarValue" id="txtVarVal" /></td>
+            		</tr>
+            		<tr>
+                		<td colspan="2" align="right">
+                    		<a href="#" name="btnAdd" id="btnAdd" onClick="AddVar();">Add Variable</a>
+                		</td>
+            		</tr>
+        		</table>
+			</td>
+		</tr>
+	</table>
+</form>
+</body>
+</html>

+ 141 - 0
lib/JSExpressionEval_src/JsHashMap.js

@@ -0,0 +1,141 @@
+/////////////////////////////////////////////
+/////       Js Object                 //////
+///////////////////////////////////////////
+// Author: NHM TAnveer Hossain Khan (Hasan)
+// http://hasan.we4tech.com
+// mail:admin at we4tech.com
+
+// hashmap internal data object
+JsObject=function(key, value) {
+  this._key=key;
+  this._value=value;
+}
+
+// set some methods for JsObject
+JsObject.prototype.getKey=function() {
+  return this._key;
+}
+
+// get value
+JsObject.prototype.getValue=function() {
+  return this._value;
+}
+
+
+/////////////////////////////////////////////
+////        Iterator                 ///////
+///////////////////////////////////////////
+JsIterator=function(array) {
+  // set internal array
+  this._array=array;
+
+  // create inernal index counter
+  this._counter=0;
+  
+  // set _hasNext value
+  if(array.length>0)
+    this._hasNext=true;
+  else
+    this._hasNext=false;
+}
+
+// return boolean value
+JsIterator.prototype.hasNext=function() {
+  return this._hasNext;
+}
+
+// return object in next method
+JsIterator.prototype.next=function() {
+    if(this._array.length>this._counter)
+    {
+        // get object
+        var rtnObj=this._array[this._counter];
+        // increment counter value;
+        this._counter++;
+        // check is has next true of flase
+        if(this._array.length>this._counter)
+            this._hasNext=true;
+        else
+            this._hasNext=false;
+
+        // return data
+        return rtnObj;
+    }
+    else
+    {
+        this._hasNext=false;
+    }
+}
+
+// remove object
+JsIterator.prototype.remove=function() {
+    this._array.splice(this._counter,1);
+    if(this._array.length > this._counter)
+        this._hasNext=false;
+}
+
+
+/////////////////////////////////////////////
+////        HashMap Object           ///////
+///////////////////////////////////////////
+
+// create JsHashMap class object
+JsHashMap=function() {
+    // init. internal array
+    this._array=new Array();
+    // set internal counter value as 0
+    // this counter will keep track the current index
+    // of array
+    this._counter=0;
+}
+
+// create add method
+// put key and value
+JsHashMap.prototype.put=function(key, value) {
+    // add new value
+    var newJsObj=new JsObject(key, value);
+    // add in internal array
+    this._array[this._counter]=newJsObj;
+    // increment the internal index counter
+    this._counter++;
+}
+
+// retrive data based on iterator
+JsHashMap.prototype.iterator=function() {
+    // create iterator
+    var it=new JsIterator(this._array);
+    // return iterator
+    return it;
+}
+
+// retrive data based on keyword
+JsHashMap.prototype.get=function(key) {
+    // create iterator object
+    var it=this.iterator();
+  
+    // iterate untile get success
+    while(it.hasNext())
+    {
+        // fetch object
+        var getObj=it.next();
+        // check is found or not
+        if(getObj.getKey()==key)
+            return getObj.getValue();
+    }
+}
+
+// remove key and object
+JsHashMap.prototype.remove=function(key) {
+    // create iterator object
+    var it=this.iterator();
+  
+    // iterate untile get success
+    while(it.hasNext())
+    {
+        // fetch object
+        var getObj=it.next();
+        // check is found or not
+        if(getObj.getKey()==key)
+            it.remove();
+    }
+}

+ 133 - 0
lib/JSExpressionEval_src/License.txt

@@ -0,0 +1,133 @@
+KIS Public License 1.0
+Terms & Conditions Of Use
+February, 2005
+Copyright (c) 2005. Khan Information Systems. All Rights Reserved
+
+
+PLEASE READ THIS DOCUMENT CAREFULLY. BY DOWNLOADING OR USING THE CODE BASE AND
+DOCUMENTATION ACCOMPANYING THIS LICENSE (THE "License"), YOU AGREE TO THE
+FOLLOWING TERMS AND CONDITIONS OF THIS LICENSE.
+
+Section One: Your Rights
+Subject to these terms and conditions of this License, Khan Information
+Systems (KIS) (the "Original Contributor") and each subsequent contributor
+(collectively with the Original Contributor, the "Contributors") hereby grants
+you a non-exclusive, worldwide, no-charge, transferable, copyright license to
+execute, prepare derivative works of, and distribute (internally and externally),
+for commercial and noncommercial purposes, the original code contributed by
+Original Contributor and all Modifications (collectively called the "Program").
+
+This non-exclusive license (with respect to the grant from a particular
+Contributor) automatically terminates for any entity that initiates legal action
+for intellectual property infringement against such Contributor as of the
+initiation of such action.
+
+Section Two: Your License Grant
+If you make a Modification and distribute it externally you are a Contributor,
+and you must provide such Modification in source code form to the Original
+Contributor within thirty (30) days of such distribution under the terms of the
+license in Section 1 above in accordance with the instructions below.
+A "Modification" to the Program is any addition to or deletion from the contents
+of any file of the Program or of any Modifications and any new file that contains
+any part of the Program or of any Modifications.
+
+As a Contributor you represent that your contributions are your original
+creation(s) and, to the best of your knowledge, no third party has any claim
+(including but not limited to intellectual property claims) relating to your
+Modification. You represent that your contribution submission includes complete
+details of any license or other restriction associated with any part of your
+Modification (including a copy of any applicable license agreement).
+
+If, after submitting your contribution, you learn of a third party claim or other
+restriction relating to your contribution, you shall promptly modify your
+contribution submission and take all reasonable steps to inform those who may
+have received the Program containing such Modification.
+
+Section Three: Redistribution
+If you distribute the Program or any derivative work of the Program in a form to
+which the recipient can make Modifications, you must ensure that the recipient
+of the Program or derivative work of the Program accepts the terms of this
+License with respect to the Program and any Modifications included your
+distribution. In addition, in each source and data file of the Program and any
+Modification you distribute must contain the following:
+
+	"The contents of this file, as updated from time to time by Khan Information
+	Systems (KIS), are subject to the KIS Public License Version 1.0
+	(the "License"); you may not use this file except in compliance with the
+	License.
+
+	Software distributed under the License is distributed on an "AS IS" basis,
+	WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+	the specific language governing rights and limitations under the License.
+
+	This software consists of voluntary contributions made by many individuals
+	on behalf of the Khan Information Systems.
+
+	The Original Code is ______________________________________________________.
+	The Initial Developer of the Original Code is _____________________________.
+	Portions created by _______________________________________________________
+	are Copyright (C) ______ _______________________. All Rights Reserved.
+	Contributor(s): __________________________________________________________."
+
+If you redistribute the Program or any derivative work of the Program in a form
+to which the recipient can not make Modifications, you must include the
+following in appropriate and conspicuous locations:
+
+	"Includes software, which is Copyright (c) 2005 - (insert then current year)
+	Khan Information Systems (KIS) and others. All rights reserved."
+
+Any redistribution must be made without any further restriction on the
+recipient's exercise of the rights granted herein.
+
+Section Four: Termination
+If you fail to comply with this License, your rights (but not your obligations)
+under this License shall terminate automatically unless you cure such breach
+within thirty days of becoming aware of the noncompliance. All sublicenses
+granted by you which preexist such termination and are properly granted shall
+survive such termination.
+
+Section Five: Other Terms
+Except for the copyright notices required above or as otherwise agreed in writing,
+you may not use any trademark of any of the Contributors.
+
+All transfers of the Program or any part thereof shall be made in compliance
+with U.S. Trade regulations or other restrictions of the U.S. Department of
+Commerce, as well as other similar trade or commerce restrictions which might
+apply.
+
+Any patent obtained by any party covering the Program or any part thereof must
+include a provision providing for the free, perpetual and unrestricted commercial
+and noncommercial use by any third party.
+
+If, as a consequence of a court judgment or allegation of patent infringement or
+for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent license would not
+permit royalty-free redistribution of the Program by all those who receive copies
+directly or indirectly through you, then the only way you could satisfy both it
+and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and the
+section as a whole is intended to apply in other circumstances.
+
+YOU AGREE THAT THE PROGRAM IS PROVIDED AS-IS, WITHOUT WARRANTY OF ANY KIND
+(EITHER EXPRESS OR IMPLIED) INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY 
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND ANY WARRANTY OF NON
+INFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE PROGRAM, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The Original Contributor from time to time may change this License, and the
+amended license will apply to all copies of the Program downloaded after the new
+license is posted. This License provides you no implied rights or licenses to the
+intellectual property of any Contributor.
+
+								Created 01/01/2005

+ 105 - 0
lib/JSExpressionEval_src/Stack.js

@@ -0,0 +1,105 @@
+/*------------------------------------------------------------------------------
+ * NAME    : Stack.js
+ * PURPOSE : Stack dta structure using java script
+ * AUTHOR  : Prasad P. Khandekar
+ * CREATED : August 21, 2005 
+ *------------------------------------------------------------------------------
+ * Copyright (c) 2005. Khan Information Systems. All Rights Reserved
+ * The contents of this file are subject to the KIS Public License 1.0
+ * (the "License"); you may not use this file except in compliance with the 
+ * License. You should have received a copy of the KIS Public License along with 
+ * this library; if not, please ask your software vendor to provide one.
+ * 
+ * YOU AGREE THAT THE PROGRAM IS PROVIDED AS-IS, WITHOUT WARRANTY OF ANY KIND
+ * (EITHER EXPRESS OR IMPLIED) INCLUDING, WITHOUT LIMITATION, ANY IMPLIED 
+ * WARRANTY OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND ANY 
+ * WARRANTY OF NON INFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE 
+ * PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * See the License for the specific language governing rights and limitations 
+ * under the License.
+ *-----------------------------------------------------------------------------*/
+// Stack object constructor
+function Stack()
+{
+    this.arrStack = new Array();
+    this.intIndex = 0;
+
+    this.Size     = getSize;
+    this.IsEmpty  = isStackEmpty;
+    this.Push     = pushElement;
+    this.Pop      = popElement;
+    this.Get      = getElement;
+    this.toString = dumpStack;
+}
+
+// Converts stack contents into a comma seperated string
+function dumpStack()
+{
+    var intCntr = 0;
+    var strRet  =  "";
+    if (this.intIndex == 0) return null;
+    for (intCntr = 0; intCntr < this.intIndex; intCntr++)
+    {
+        if (strRet.length == 0)
+            strRet += this.arrStack[intCntr];
+        else
+            strRet += "," + this.arrStack[intCntr];
+    }
+    return strRet;
+}
+
+// Returns size of stack
+function getSize()
+{
+    return this.intIndex;
+}
+
+// This method tells us if this Stack object is empty
+function isStackEmpty()
+{
+	if (this.intIndex == 0)
+		return true;
+	else
+		return false;
+}
+
+// This method pushes a new element onto the top of the stack
+function pushElement(newData)
+{
+	// Assign our new element to the top
+	debugAssert ("Pushing " + newData);
+	this.arrStack[this.intIndex] = newData;
+	this.intIndex++;
+}
+
+// This method pops the top element off of the stack
+function popElement()
+{
+    var retVal;
+
+    retVal = null;
+    if (this.intIndex > 0)
+    {
+	   // Assign our new element to the top
+	   this.intIndex--;
+	   retVal = this.arrStack[this.intIndex];
+	}
+	return retVal;
+}
+
+// Gets an element at a particular offset from top of the stack
+function getElement(intPos)
+{
+    var retVal;
+
+    //alert ("Size : " + this.intIndex + ", Index " + intPos);
+    if (intPos >= 0 && intPos < this.intIndex)
+        retVal = this.arrStack[this.intIndex - intPos - 1];
+    return retVal;
+}

+ 108 - 0
lib/JSExpressionEval_src/StackImpl.htm

@@ -0,0 +1,108 @@
+<HTML>
+<HEAD>
+<TITLE>Simple Stack Object Example</TITLE>
+<SCRIPT LANGUAGE="JavaScript">
+<!-
+// This is the constructor for our stack object
+// If the "length" instance variable is 0, the stack is empty
+function oStack() {
+	this.top = null;
+	this.length = 0;
+	this.isEmpty = oStackIsEmpty;
+	this.push = oStackPush;
+	this.pop = oStackPop;
+	}
+
+// This method tells us if this oStack object is empty
+function oStackIsEmpty() {
+	if (this.length == 0)
+		return true;
+	else
+		return false;
+	}
+
+// This method pushes a new element onto the top of the stack
+function oStackPush( newData) {
+	// Create the new element
+	var newElement = new oStackElement();
+	// Add the data to it
+	newElement.setData( newData);
+	// Put the current top in our next field
+	newElement.setNext( this.top);
+	// Assign our new element to the top
+	this.top = newElement;
+	this.length++;
+	}
+	
+// This method pops the top element off of the stack
+function oStackPop() {
+	// Put old top away for safe keeping
+	var oldTop = this.top;
+	// Get the new top
+	this.top = this.top.getNext();
+	this.length-;
+	return oldTop;
+	}
+
+// This is the constructor for an element in our stack object
+// The "data" variable holds our actual data (whatever it may be)
+// and the "next" variable holds the oStackElement object that is
+// 'underneath' this oStackElement object.
+function oStackElement() {
+	this.data = 0;
+	this.next = null;
+	this.setData = oStackElementSetData;
+	this.getData = oStackElementGetData;
+	this.setNext = oStackElementSetNext;
+	this.getNext = oStackElementGetNext;
+	}
+
+// This method sets the data field of this oStackElement
+function oStackElementSetData( newData) {
+	this.data = newData;
+	}
+
+// This method returns the data field of this oStackElement
+function oStackElementGetData() {
+	return this.data;
+	}
+
+// This method sets the next field of this oStackElement
+function oStackElementSetNext( newNext) {
+	this.next = newNext;
+	}
+
+// This method returns the next field of this oStackElement
+function oStackElementGetNext() {
+	return this.next;
+	}
+
+// create a new oStack object
+var stack1 = new oStack();
+
+// Fill it with something interesting
+// In this case, the names of my cats
+stack1.push( "Puddin' Head")
+stack1.push( "Sunshine")
+stack1.push( "Shadow")
+stack1.push( "Sassy")
+stack1.push( "Toby")
+stack1.push( "Sandy")
+stack1.push( "Pixie")
+stack1.push( "Edi")
+stack1.push( "Gizmo")
+
+// Get the number of items in the stack
+var numElements = stack1.length;
+
+// Print out the contents of the stack
+for (var i = 0; i < numElements; i++)
+	document.write( stack1.length + " - " + stack1.pop().getData() 
++ "<br>");
+//->
+</SCRIPT>
+</HEAD>
+<BODY>
+</BODY>
+</HTML>
+

+ 119 - 0
lib/JSExpressionEval_src/StackTester.htm

@@ -0,0 +1,119 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1250">
+<meta name="generator" content="PSPad editor, www.pspad.com">
+<title>Stack Tester</title>
+<script language="javascript" type="text/javascript" src="Stack.js"></script>
+<style>
+body {font-family: Verdana, Tahoma; font-size: 8pt; font-weight: normal}
+button {font-family: Verdana, Tahoma; font-size: 8pt; font-weight: bold; width: 100px; height: 24px;}
+textarea {font-family: Verdana, Tahoma; font-size: 8pt; font-weight: normal; border: 1px solid;}
+input {font-family: Verdana, Tahoma; font-size: 8pt; font-weight: normal; border: 1px solid;}
+</style>
+<script language="javascript">
+var myStack = new Stack();
+
+function pushValue()
+{
+    var objFrm;
+    var strVal;
+
+    objFrm = document.frmMain;
+    if (objFrm) strVal = objFrm.txtValue.value;
+    if (strVal) myStack.Push(strVal);
+    objFrm.txtValue.value = "";
+}
+
+function popValue()
+{
+    var objFrm;
+    var strVal;
+
+    objFrm = document.frmMain;
+    if (!myStack.IsEmpty())
+    {
+        strVal = myStack.Pop();
+        objFrm.txtValue.value = strVal;
+    }
+}
+
+function getValue()
+{
+    var objFrm;
+    var strVal;
+    var intIndex = NaN;
+
+    objFrm = document.frmMain;
+    if (objFrm) intIndex = parseInt(objFrm.txtIndex.value);
+    if (!isNaN(intIndex))
+    {
+        strVal = myStack.Get(intIndex);
+        objFrm.txtValue.value = strVal;
+    }
+}
+
+function showStack()
+{
+    var objFrm;
+    var strVal, strTemp;
+    var intCntr = 0;
+
+    if (myStack.IsEmpty())
+    {
+        alert ("No values stored on stack!");
+        return;
+    }
+
+    strTemp = "";
+    objFrm = document.frmMain;
+    for (intCntr = 0; intCntr < myStack.Size(); intCntr++)
+    {
+        strVal = myStack.Get(intCntr);
+        if (intCntr == 0)
+            strTemp += "Stack[Top] = " + strVal + "\r\n";
+        else
+            strTemp += "Stack[Top + " + intCntr + "] = " + strVal + "\r\n";
+        strVal = "";
+    }
+    objFrm.txtStack.value = strTemp;
+}
+</script>
+</head>
+<body>
+    <form name="frmMain" id="frmMain" method="POST">
+        <table>
+            <tr>
+                <td valign="top">
+                    <table>
+                        <tr>
+                            <td>Value : </td>
+                            <td><input type="text" name="txtValue" id="txtValue" size="20" /></td>
+                        </tr>
+                        <tr>
+                            <td colspan="2">
+                                <button id="btnPush" name="btnPush" onClick="javascript:pushValue()">Push</button>
+                                <button id="btnPop" name="btnPop" onClick="javascript:popValue()">Pop</button>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td colspan="2">
+                                <button id="btnGet" name="btnGet" onClick="getValue()">Get</button>
+                                @&nbsp;<input type="text" size="2" maxlength="2" id="txtIndex" name="txtIndex" />
+                            </td>
+                        </tr>
+                        <tr>
+                            <td colspan="2">
+                                <button name="btnWalk" id="btnWalk" onClick="javascript:showStack()">Walk</button>
+                            </td>
+                        </tr>
+                    </table>
+                </td>
+                <td>
+                    <textarea id="txtStack" name="txtStack" cols="30" rows="10"></textarea>
+                </td>
+            </tr>
+        </table>
+    </form>
+</body>
+</html>

+ 54 - 0
lib/JSExpressionEval_src/TokanTester.htm

@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1250">
+<meta name="generator" content="PSPad editor, www.pspad.com">
+<title>Tokanizer Tester</title>
+<script language="javascript" type="text/javascript" src="Tokanizer.js"></script>
+<script language="javascript">
+<!--//
+function getTokens()
+{
+    var intCntr;
+    var form = document.frmMain;
+    var arrTokens = Tokanize(form.txtExp.value);
+    var strTemp = "";
+    var strTmp = "";
+    if (arrTokens.length > 0)
+    {
+        for (intCntr = 0; intCntr < arrTokens.length; intCntr++)
+        {
+            strTemp += arrTokens[intCntr];
+            strTmp += "Token " + intCntr + " : " + arrTokens[intCntr] + "\r\n";
+        }
+        form.txtResult.value = strTemp + "\r\n" + strTmp     ;
+    }
+    else
+        alert ("No Tokens");
+}
+//-->
+</script>
+</head>
+<body>
+    <form name="frmMain" id="frmMain" method="POST">
+        <table>
+        <tbody>
+            <tr>
+                <td>Enter an Expression : </td>
+                <td><input type="text" size="60" name="txtExp" id="txtExp"></input></td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <textarea name="txtResult" id="txtResult" cols="60" rows="10"></textarea>
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <button name="btnTokanize" id="btnTokanize" onClick="javascript:getTokens();">Tokanize</button>
+                </td>
+            </tr>
+        </tbody>
+        </table>
+    </form>
+</body>
+</html>

+ 493 - 0
lib/JSExpressionEval_src/Tokanizer.js

@@ -0,0 +1,493 @@
+/*------------------------------------------------------------------------------
+ * NAME    : Tokanizer.js
+ * PURPOSE : Parse a string a make an array of tokens. The following tokens are
+ *           reconized.
+ *           () 
+ *           ^ * / % + -  
+ *           ! & | TRUE FALSE
+ *           < <= > >= <> =
+ *           AVG ABS ACOS ASC ASIN ATAN CDATE CHR COS DATE FIX HEX IIF 
+ *           LCASE LEFT LOG MAX MID MIN RIGHT ROUND SIN SQRT TAN UCASE 
+ *           , ' "
+ * AUTHOR  : Prasad P. Khandekar
+ * CREATED : August 19, 2005
+ *------------------------------------------------------------------------------
+ * -3              // Negative 3 - is the first token
+ * 3+-2            // Negative 2 - previous token is an operator and next is a digit
+ * 3*-(2)          // Negative 2 - previous token is an operator and next is an opening brace
+ * 3*ABS(-2)       // Negative 2 - previous token is an opening brace and next is a digit
+ * 3+-SQR(4)       // Negative SQR - previous token is an operator and next is a alpha
+ *
+ * 3-2             // Positive 2 - previous token is a digit and next is a digit
+ * 3 - 2           // Positive 2 - previous token is a digit or space and next is a space
+ * ABS(3.4)-2      // Positive 2 - previous token is a closing brace and next is a digit
+ * ABS(3.4)- 2     // Positive 2 - previous token is a digit and next is a space
+ * ABS(3.4) - 2    // Positive 2 - previous token is a closing brace or space and next is a space
+ *------------------------------------------------------------------------------
+ * Copyright (c) 2005. Khan Information Systems. All Rights Reserved
+ * The contents of this file are subject to the KIS Public License 1.0
+ * (the "License"); you may not use this file except in compliance with the 
+ * License. You should have received a copy of the KIS Public License along with 
+ * this library; if not, please ask your software vendor to provide one.
+ * 
+ * YOU AGREE THAT THE PROGRAM IS PROVIDED AS-IS, WITHOUT WARRANTY OF ANY KIND
+ * (EITHER EXPRESS OR IMPLIED) INCLUDING, WITHOUT LIMITATION, ANY IMPLIED 
+ * WARRANTY OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND ANY 
+ * WARRANTY OF NON INFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE 
+ * PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * See the License for the specific language governing rights and limitations 
+ * under the License.
+ *-----------------------------------------------------------------------------*/
+ var lstAlpha    = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,uv,w,x,y,z";
+ var lstDigits   = "0,1,2,3,4,5,6,7,8,9";
+ var lstArithOps = "^,*,/,%,+,-";
+ var lstLogicOps = "!,&,|";
+ var lstCompaOps = "<,<=,>,>=,<>,=";
+ var lstFuncOps  = ["AVG","ABS","ACOS","ASC","ASIN","ATAN","CDATE","CHR","COS","DATE","FIX","HEX","IIF","LCASE","LEFT","LOG","MAX","MID","MIN","RIGHT","ROUND","SIN","SQRT","TAN","UCASE"];
+
+/*------------------------------------------------------------------------------
+ * NAME       : Tokanize
+ * PURPOSE    : Breaks the string into a token array. It also checks whether the
+ *              parenthesis, single quotes and double quotes are balanced or not.
+ * PARAMETERS : pstrExpression - The string from which token array is to be 
+ *              constructed.
+ * RETURNS    : An array of tokens.
+ * THROWS     : Unterminated string constant - Single/Double quotes are not 
+ *                                             properly terminated
+ *              Unbalanced parenthesis - Opening/closing braces are not balanced
+ *----------------------------------------------------------------------------*/
+ function Tokanize(pstrExpression)
+ {
+    var intCntr, intBraces;
+    var arrTokens;
+    var intIndex, intPos;
+    var chrChar, chrNext;
+    var strToken, prevToken;
+
+    intCntr   = 0;
+    intBraces = 0;
+    intIndex  = 0;
+    strToken  = "";
+    arrTokens = new Array();
+    pstrExpression = Trim(pstrExpression);
+    while (intCntr < pstrExpression.length)
+    {
+        prevToken = "";
+        chrChar = pstrExpression.substr(intCntr, 1);
+        if (window)
+            if (window.status)
+                window.status = "Processing " + chrChar;
+        switch (chrChar)
+        {
+            case " " :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                break;
+            case "(":
+                intBraces++;
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case ")" :
+                intBraces--;
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "^" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "*" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "/" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "%" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "&" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "|" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "," :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "-" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                chrNext = pstrExpression.substr(intCntr + 1, 1);
+                if (arrTokens.length > 0)
+                    prevToken = arrTokens[intIndex - 1];
+                if (intCntr == 0 || ((IsOperator(prevToken) ||
+                    prevToken == "(" || prevToken == ",") && 
+                    (IsDigit(chrNext) || chrNext == "(")))
+                {
+                    // Negative Number
+                    strToken += chrChar;
+                }
+                else
+                {
+                    arrTokens[intIndex] = chrChar;
+                    intIndex++;
+                    strToken = "";
+                }
+                break;
+            case "+" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                chrNext = pstrExpression.substr(intCntr + 1, 1);
+                if (arrTokens.length > 0)
+                    prevToken = arrTokens[intIndex - 1];
+                if (intCntr == 0 || ((IsOperator(prevToken) ||
+                    prevToken == "(" || prevToken == ",") && 
+                    (IsDigit(chrNext) || chrNext == "(")))
+                {
+                    // positive Number
+                    strToken += chrChar;
+                }
+                else
+                {
+                    arrTokens[intIndex] = chrChar;
+                    intIndex++;
+                    strToken = "";
+                }
+                break;
+            case "<" :
+                chrNext = pstrExpression.substr(intCntr + 1, 1);
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                if (chrNext == "=")
+                {
+                    arrTokens[intIndex] = chrChar + "=";
+                    intIndex++;
+                    intCntr++;
+                }
+                else if (chrNext == ">")
+                {
+                    arrTokens[intIndex] = chrChar + ">";
+                    intIndex++;
+                    intCntr++;
+                }
+                else
+                {
+                    arrTokens[intIndex] = chrChar;
+                    intIndex++;
+                }
+                break;
+            case ">" :
+                chrNext = pstrExpression.substr(intCntr + 1, 1);
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                if (chrNext == "=")
+                {
+                    arrTokens[intIndex] = chrChar + "=";
+                    intIndex++;
+                    intCntr++;
+                }
+                else
+                {
+                    arrTokens[intIndex] = chrChar;
+                    intIndex++;
+                }
+                break;
+            case "=" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+                arrTokens[intIndex] = chrChar;
+                intIndex++;
+                break;
+            case "'" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+
+                intPos = pstrExpression.indexOf(chrChar, intCntr + 1);
+                if (intPos < 0) 
+                    throw "Unterminated string constant";
+                else
+                {
+                    strToken += pstrExpression.substring(intCntr + 1, intPos);
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                    intCntr = intPos;
+                }
+                break;
+            case "\"" :
+                if (strToken.length > 0)
+                {
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                }
+
+                intPos = pstrExpression.indexOf(chrChar, intCntr + 1);
+                if (intPos < 0)
+                {
+                    throw "Unterminated string constant";
+                }
+                else
+                {
+                    strToken += pstrExpression.substring(intCntr + 1, intPos);
+                    arrTokens[intIndex] = strToken;
+                    intIndex++;
+                    strToken = "";
+                    intCntr = intPos;
+                }
+                break;
+            default :
+                strToken += chrChar;
+                break;
+        }
+        intCntr++;
+    }
+    if (intBraces > 0)
+        throw "Unbalanced parenthesis!";
+
+    if (strToken.length > 0)
+        arrTokens[intIndex] = strToken;
+    return arrTokens;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : IsDigit
+ * PURPOSE    : Checks whether the character specified by chrArg is a numeric 
+ *              character.
+ * PARAMETERS : chrArg - The character to be checked
+ * RETURNS    : False - If chrArg is not a numeric character
+ *              True - Otherwise 
+ *----------------------------------------------------------------------------*/
+function IsDigit(chrArg)
+{
+    if (lstDigits.indexOf(chrArg) >= 0)
+        return true;
+    return false;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : IsAlpha
+ * PURPOSE    : Checks whether the character specified by chrArg is a alphabet 
+ * PARAMETERS : chrArg - The character to be checked
+ * RETURNS    : False - If chrArg is not a alphabet
+ *              True - Otherwise 
+ *----------------------------------------------------------------------------*/
+function IsAlpha(chrArg)
+{
+    if (lstAlpha.indexOf(chrArg) >= 0 || 
+        lstAlpha.toUpperCase().indexOf(chrArg) >= 0)
+        return true;
+    return false;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : IsOperator
+ * PURPOSE    : Checks whether the string specified by strArg is an operator
+ * PARAMETERS : strArg - The string to be checked
+ * RETURNS    : False - If strArg is not an operator symbol
+ *              True - Otherwise 
+ *----------------------------------------------------------------------------*/
+function IsOperator(strArg)
+{
+    if (lstArithOps.indexOf(strArg) >= 0 || lstCompaOps.indexOf(strArg) >= 0)
+        return true;
+    return false;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : IsFunction
+ * PURPOSE    : Checks whether the string specified by strArg is a function name
+ * PARAMETERS : strArg - The string to be checked
+ * RETURNS    : False - If strArg is not a valid built-in function name.
+ *              True - Otherwise 
+ *----------------------------------------------------------------------------*/
+function IsFunction(strArg)
+{
+	var idx = 0;
+
+	strArg = strArg.toUpperCase();
+	for (idx = 0; idx < lstFuncOps.length; idx++)
+	{
+	    if (strArg == lstFuncOps[idx])
+	        return true;
+	}
+	return false;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : Trim
+ * PURPOSE    : Removes trailing and leading spaces from a string.
+ * PARAMETERS : pstrVal - The string from which leading and trailing spaces are 
+ *              to be removed.
+ * RETURNS    : A string with leading and trailing spaces removed.
+ *----------------------------------------------------------------------------*/
+function Trim(pstrVal)
+{
+    if (pstrVal.length < 1) return "";
+
+    pstrVal = RTrim(pstrVal);
+    pstrVal = LTrim(pstrVal);
+    if (pstrVal == "")
+		return "";
+    else
+        return pstrVal;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : RTrim
+ * PURPOSE    : Removes trailing spaces from a string.
+ * PARAMETERS : pstrValue - The string from which trailing spaces are to be removed.
+ * RETURNS    : A string with trailing spaces removed.
+ *----------------------------------------------------------------------------*/
+function RTrim(pstrValue)
+{
+    var w_space = String.fromCharCode(32);
+    var v_length = pstrValue.length;
+    var strTemp = "";
+    if(v_length < 0)
+    {
+        return"";
+    }
+    var iTemp = v_length - 1;
+
+    while(iTemp > -1)
+    {
+        if(pstrValue.charAt(iTemp) == w_space)
+        {
+        }
+        else
+        {
+            strTemp = pstrValue.substring(0, iTemp + 1);
+            break;
+        }
+        iTemp = iTemp - 1;
+    }
+    return strTemp;
+}
+
+/*------------------------------------------------------------------------------
+ * NAME       : LTrim
+ * PURPOSE    : Removes leading spaces from a string.
+ * PARAMETERS : pstrValue - The string from which leading spaces are to be removed.
+ * RETURNS    : A string with leading spaces removed.
+ *----------------------------------------------------------------------------*/
+function LTrim(pstrValue)
+{
+    var w_space = String.fromCharCode(32);
+    if(v_length < 1)
+    {
+        return "";
+    }
+    var v_length = pstrValue.length;
+    var strTemp = "";
+    var iTemp = 0;
+
+    while(iTemp < v_length)
+    {
+        if(pstrValue.charAt(iTemp) == w_space)
+        {
+        }
+        else
+        {
+            strTemp = pstrValue.substring(iTemp, v_length);
+            break;
+        }
+        iTemp = iTemp + 1;
+    }
+    return strTemp;
+}

+ 47 - 0
lib/JSExpressionEval_src/VBEval.htm

@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1250">
+<meta name="generator" content="PSPad editor, www.pspad.com">
+<style>
+body {font-family: Verdana,Tahoma; font-size: 9pt; font-weight: normal;}
+table {font-family: Verdana,Tahoma; font-size: 9pt; font-weight: normal;}
+input {font-family: Verdana,Tahoma; font-size: 10pt; font-weight: normal; border: 1px solid;}
+textarea {font-family: Verdana,Tahoma; font-size: 10pt; font-weight: normal; border: 1px solid;}
+button {font-family: Verdana,Tahoma; font-size: 8pt; font-weight: bold; width: 125px; height: 24px;}
+</style>
+<title>VB Eval</title>
+<script language="vbscript">
+Sub Calculate()
+    Dim frm
+    Dim strExp
+    Dim strResult
+
+    Set frm = document.frmMain
+    strExp = frm.txtExp.Value
+    strResult = Eval(strExp)
+    frm.txtResult.Value = strResult
+End Sub
+</script>
+</head>
+<body>
+    <form name="frmMain" id="frmMain" method="POST">
+        <table>
+            <tr>
+                <td>Expression : </td>
+                <td><input type="text" size="50" name="txtExp" id="txtExp" /></td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <textarea name="txtResult" id="txtResult" cols="61" rows="5"></textarea>
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align ="center">
+                    <button name="btnCalc" id="btnCalc" onClick="vbscript:Calculate()">Calculate</button>
+                </td>
+            </tr>
+        </table>
+    </form>
+</body>
+</html>

+ 123 - 0
lib/JSExpressionEval_src/postfix.txt

@@ -0,0 +1,123 @@
+(           99
+-!          10
+Functions   9
+^           7
+*/%         6
++-          5
+&|          3
+><=<><=>=   4
+
+
+10/Abs(3) + 2 > 2 & 3 >= 2
+--------------------------------------
+10      (       10
+/       (/      10
+Abs     (/Abs   10
+(       (/Abs(  10
+3       (/Abs(  10 3
+)       (/Abs   10 3
++       (+      10 3 Abs /
+2       (+      10 3 Abs / 2
+>       (>      10 3 Abs / 2 +
+2       (>      10 3 Abs / 2 + 2
+&       (&      10 3 Abs / 2 + 2 >
+3       (&      10 3 Abs / 2 + 2 > 3
+>=      (&>=    10 3 Abs / 2 + 2 > 3
+2       (&>=    10 3 Abs / 2 + 2 > 3 2
+--------------------------------------
+10 3 Abs / 2 + 2 > 3 2 >= & 
+10 3 Abs / 2 + 2 > 3 2 >= &
+
+10/Abs(3) + 2
+----------------------------------
+10      (       10
+/       (/      10
+Abs     (/Abs   10
+(       (/Abs(  10
+3       (/Abs(  10 3
+)       (/Abs   10 3
++       (+      10 3 Abs /
+2       (+      10 3 Abs / 2
+-----------------------------------
+10 3 Abs / 2 +
+10 3 Abs / 2 +
+
+
+2+3.4*56/MAX(3,4,5)
+---------------------------------
+2	(	2
++	(+	2
+3.4	(+	2 3.4
+*	(+*	2 3.4
+56	(+*	2 3.4 56
+/	(+/	2 3.4 56 *
+MAX	(+/MAX	2 3.4 56 *
+(	(+/MAX(	2 3.4 56 *
+3	(+/MAX(	2 3.4 56 * 3
+,	(+/MAX(	2 3.4 56 * 3
+4	(+/MAX(	2 3.4 56 * 3 4
+,	(+/MAX(	2 3.4 56 * 3 4
+5	(+/MAX(	2 3.4 56 * 3 4 5
+)	(+/MAX	2 3.4 56 * 3 4 5
+--------------------------------
+2 3.4 56 * 3 4 5 MAX / +
+2 3.4 56 * 3 4 5 MAX / +
+
+
+2+3.4*56^AVG(3,4,5)
+--------------------------------------
+2	(		2
++	(+		2
+3.4	(+		2 3.4
+*	(+*		2 3.4
+56	(+*		2 3.4 56
+^	(+*^		2 3.4 56
+AVG	(+*^AVG		2 3.4 56
+(	(+*^AVG(	2 3.4 56
+3	(+*^AVG(	2 3.4 56 3
+,	(+*^AVG(	2 3.4 56 3
+4	(+*^AVG(	2 3.4 56 3 4
+,	(+*^AVG(	2 3.4 56 3 4
+5	(+*^AVG(	2 3.4 56 3 4 5
+)	(+*^AVG		2 3.4 56 3 4 5
+---------------------------------------
+2 3.4 56 3 4 5 AVG ^ * +
+
+
+3+MAX(3*4,6,7)
+--------------------------------------------
+3	(		3
++	(+		3
+MAX	(+MAX		3
+(	(+MAX(		3
+3	(+MAX(		3 3
+*	(+MAX(*		3 3
+4	(+MAX(*		3 3 4
+,	(+MAX(		3 3 4 *
+6	(+MAX(		3 3 4 * 6
+,	(+MAX(		3 3 4 * 6
+7	(+MAX(		3 3 4 * 6 7
+)	(+MAX		3 3 4 * 6 7
+--------------------------------------------
+3 3 4 * 6 7 MAX
+
+
+3+MAX(3+2*4,6,7)
+------------------------------------------
+3	(		3
++	(+		3
+MAX	(+MAX		3
+(	(+MAX(		3
+3	(+MAX(		3 3
++	(+MAX(+		3 3
+2	(+MAX(+		3 3 2
+*	(+MAX(+*	3 3 2
+4	(+MAX(+*	3 3 2 4
+,	(+MAX(		3 3 2 4 * +
+6	(+MAX(		3 3 2 4 * + 6
+,	(+MAX(		3 3 2 4 * + 6
+7	(+MAX(		3 3 2 4 * + 6 7
+)	(+MAX		3 3 2 4 * + 6 7
+--------------------------------------------
+3 3 2 4 * + 6 7 MAX +
+3 3 2 4 * + 6 7 MAX +

+ 657 - 0
lib/bootstrap/bootstrap-paginator.js

@@ -0,0 +1,657 @@
+/**
+ * bootstrap-paginator.js v0.5
+ * --
+ * Copyright 2013 Yun Lai <lyonlai1984@gmail.com>
+ * --
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function ($) {
+
+    "use strict"; // jshint ;_;
+
+
+    /* Paginator PUBLIC CLASS DEFINITION
+     * ================================= */
+
+    /**
+     * Boostrap Paginator Constructor
+     *
+     * @param element element of the paginator
+     * @param options the options to config the paginator
+     *
+     * */
+    var BootstrapPaginator = function (element, options) {
+        this.init(element, options);
+    },
+        old = null;
+
+    BootstrapPaginator.prototype = {
+
+        /**
+         * Initialization function of the paginator, accepting an element and the options as parameters
+         *
+         * @param element element of the paginator
+         * @param options the options to config the paginator
+         *
+         * */
+        init: function (element, options) {
+
+            this.$element = $(element);
+
+            var version = (options && options.bootstrapMajorVersion) ? options.bootstrapMajorVersion : $.fn.bootstrapPaginator.defaults.bootstrapMajorVersion,
+                id = this.$element.attr("id");
+
+            if (version === 2 && !this.$element.is("div")) {
+
+                throw "in Bootstrap version 2 the pagination must be a div element. Or if you are using Bootstrap pagination 3. Please specify it in bootstrapMajorVersion in the option";
+            } else if (version > 2 && !this.$element.is("ul")) {
+                throw "in Bootstrap version 3 the pagination root item must be an ul element."
+            }
+
+
+
+            this.currentPage = 1;
+
+            this.lastPage = 1;
+
+            this.setOptions(options);
+
+            this.initialized = true;
+        },
+
+        /**
+         * Update the properties of the paginator element
+         *
+         * @param options options to config the paginator
+         * */
+        setOptions: function (options) {
+
+            this.options = $.extend({}, (this.options || $.fn.bootstrapPaginator.defaults), options);
+
+            this.totalPages = parseInt(this.options.totalPages, 10);  //setup the total pages property.
+            this.numberOfPages = parseInt(this.options.numberOfPages, 10); //setup the numberOfPages to be shown
+
+            //move the set current page after the setting of total pages. otherwise it will cause out of page exception.
+            if (options && typeof (options.currentPage)  !== 'undefined') {
+
+                this.setCurrentPage(options.currentPage);
+            }
+
+            this.listen();
+
+            //render the paginator
+            this.render();
+
+            if (!this.initialized && this.lastPage !== this.currentPage) {
+                this.$element.trigger("page-changed", [this.lastPage, this.currentPage]);
+            }
+
+        },
+
+        /**
+         * Sets up the events listeners. Currently the pageclicked and pagechanged events are linked if available.
+         *
+         * */
+        listen: function () {
+
+            this.$element.off("page-clicked");
+
+            this.$element.off("page-changed");// unload the events for the element
+
+            if (typeof (this.options.onPageClicked) === "function") {
+                this.$element.bind("page-clicked", this.options.onPageClicked);
+            }
+
+            if (typeof (this.options.onPageChanged) === "function") {
+                this.$element.on("page-changed", this.options.onPageChanged);
+            }
+
+            this.$element.bind("page-clicked", this.onPageClicked);
+        },
+
+
+        /**
+         *
+         *  Destroys the paginator element, it unload the event first, then empty the content inside.
+         *
+         * */
+        destroy: function () {
+
+            this.$element.off("page-clicked");
+
+            this.$element.off("page-changed");
+
+            this.$element.removeData('bootstrapPaginator');
+
+            this.$element.empty();
+
+        },
+
+        /**
+         * Shows the page
+         *
+         * */
+        show: function (page) {
+
+            this.setCurrentPage(page);
+
+            this.render();
+
+            if (this.lastPage !== this.currentPage) {
+                this.$element.trigger("page-changed", [this.lastPage, this.currentPage]);
+            }
+        },
+
+        /**
+         * Shows the next page
+         *
+         * */
+        showNext: function () {
+            var pages = this.getPages();
+
+            if (pages.next) {
+                this.show(pages.next);
+            }
+
+        },
+
+        /**
+         * Shows the previous page
+         *
+         * */
+        showPrevious: function () {
+            var pages = this.getPages();
+
+            if (pages.prev) {
+                this.show(pages.prev);
+            }
+
+        },
+
+        /**
+         * Shows the first page
+         *
+         * */
+        showFirst: function () {
+            var pages = this.getPages();
+
+            if (pages.first) {
+                this.show(pages.first);
+            }
+
+        },
+
+        /**
+         * Shows the last page
+         *
+         * */
+        showLast: function () {
+            var pages = this.getPages();
+
+            if (pages.last) {
+                this.show(pages.last);
+            }
+
+        },
+
+        /**
+         * Internal on page item click handler, when the page item is clicked, change the current page to the corresponding page and
+         * trigger the pageclick event for the listeners.
+         *
+         *
+         * */
+        onPageItemClicked: function (event) {
+
+            var type = event.data.type,
+                page = event.data.page;
+
+            this.$element.trigger("page-clicked", [event, type, page]);
+
+        },
+
+        onPageClicked: function (event, originalEvent, type, page) {
+
+            //show the corresponding page and retrieve the newly built item related to the page clicked before for the event return
+
+            var currentTarget = $(event.currentTarget);
+
+            switch (type) {
+            case "first":
+                currentTarget.bootstrapPaginator("showFirst");
+                break;
+            case "prev":
+                currentTarget.bootstrapPaginator("showPrevious");
+                break;
+            case "next":
+                currentTarget.bootstrapPaginator("showNext");
+                break;
+            case "last":
+                currentTarget.bootstrapPaginator("showLast");
+                break;
+            case "page":
+                currentTarget.bootstrapPaginator("show", page);
+                break;
+            }
+
+        },
+
+        /**
+         * Renders the paginator according to the internal properties and the settings.
+         *
+         *
+         * */
+        render: function () {
+
+            //fetch the container class and add them to the container
+            var containerClass = this.getValueFromOption(this.options.containerClass, this.$element),
+                size = this.options.size || "normal",
+                alignment = this.options.alignment || "left",
+                pages = this.getPages(),
+                listContainer = this.options.bootstrapMajorVersion === 2 ? $("<ul></ul>") : this.$element,
+                listContainerClass = this.options.bootstrapMajorVersion === 2 ? this.getValueFromOption(this.options.listContainerClass, listContainer) : null,
+                first = null,
+                prev = null,
+                next = null,
+                last = null,
+                p = null,
+                i = 0;
+
+
+            this.$element.prop("class", "");
+
+            this.$element.addClass("pagination");
+
+            switch (size.toLowerCase()) {
+            case "large":
+            case "small":
+            case "mini":
+                this.$element.addClass($.fn.bootstrapPaginator.sizeArray[this.options.bootstrapMajorVersion][size.toLowerCase()]);
+                break;
+            default:
+                break;
+            }
+
+            if (this.options.bootstrapMajorVersion === 2) {
+                switch (alignment.toLowerCase()) {
+                case "center":
+                    this.$element.addClass("pagination-centered");
+                    break;
+                case "right":
+                    this.$element.addClass("pagination-right");
+                    break;
+                default:
+                    break;
+                }
+            }
+
+
+            this.$element.addClass(containerClass);
+
+            //empty the outter most container then add the listContainer inside.
+            this.$element.empty();
+
+            if (this.options.bootstrapMajorVersion === 2) {
+                this.$element.append(listContainer);
+
+                listContainer.addClass(listContainerClass);
+            }
+
+            //update the page element reference
+            this.pageRef = [];
+
+            if (pages.first) {//if the there is first page element
+                first = this.buildPageItem("first", pages.first);
+
+                if (first) {
+                    listContainer.append(first);
+                }
+
+            }
+
+            if (pages.prev) {//if the there is previous page element
+
+                prev = this.buildPageItem("prev", pages.prev);
+
+                if (prev) {
+                    listContainer.append(prev);
+                }
+
+            }
+
+
+            for (i = 0; i < pages.length; i = i + 1) {//fill the numeric pages.
+
+                p = this.buildPageItem("page", pages[i]);
+
+                if (p) {
+                    listContainer.append(p);
+                }
+            }
+
+            if (pages.next) {//if there is next page
+
+                next = this.buildPageItem("next", pages.next);
+
+                if (next) {
+                    listContainer.append(next);
+                }
+            }
+
+            if (pages.last) {//if there is last page
+
+                last = this.buildPageItem("last", pages.last);
+
+                if (last) {
+                    listContainer.append(last);
+                }
+            }
+        },
+
+        /**
+         *
+         * Creates a page item base on the type and page number given.
+         *
+         * @param page page number
+         * @param type type of the page, whether it is the first, prev, page, next, last
+         *
+         * @return Object the constructed page element
+         * */
+        buildPageItem: function (type, page) {
+
+            var itemContainer = $("<li></li>"),//creates the item container
+                itemContent = $("<a></a>"),//creates the item content
+                text = "",
+                title = "",
+                itemContainerClass = this.options.itemContainerClass(type, page, this.currentPage),
+                itemContentClass = this.getValueFromOption(this.options.itemContentClass, type, page, this.currentPage),
+                tooltipOpts = null;
+
+
+            switch (type) {
+
+            case "first":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "last":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "prev":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "next":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "page":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            }
+
+            itemContainer.addClass(itemContainerClass).append(itemContent);
+
+            itemContent.addClass(itemContentClass).html(text).on("click", null, {type: type, page: page}, $.proxy(this.onPageItemClicked, this));
+
+            if (this.options.pageUrl) {
+                itemContent.attr("href", this.getValueFromOption(this.options.pageUrl, type, page, this.currentPage));
+            }
+
+            if (this.options.useBootstrapTooltip) {
+                tooltipOpts = $.extend({}, this.options.bootstrapTooltipOptions, {title: title});
+
+                itemContent.tooltip(tooltipOpts);
+            } else {
+                itemContent.attr("title", title);
+            }
+
+            return itemContainer;
+
+        },
+
+        setCurrentPage: function (page) {
+            if (page > this.totalPages || page < 1) {// if the current page is out of range, throw exception.
+
+                throw "Page out of range";
+
+            }
+
+            this.lastPage = this.currentPage;
+
+            this.currentPage = parseInt(page, 10);
+
+        },
+
+        /**
+         * Gets an array that represents the current status of the page object. Numeric pages can be access via array mode. length attributes describes how many numeric pages are there. First, previous, next and last page can be accessed via attributes first, prev, next and last. Current attribute marks the current page within the pages.
+         *
+         * @return object output objects that has first, prev, next, last and also the number of pages in between.
+         * */
+        getPages: function () {
+
+            var totalPages = this.totalPages,// get or calculate the total pages via the total records
+                pageStart = (this.currentPage % this.numberOfPages === 0) ? (parseInt(this.currentPage / this.numberOfPages, 10) - 1) * this.numberOfPages + 1 : parseInt(this.currentPage / this.numberOfPages, 10) * this.numberOfPages + 1,//calculates the start page.
+                output = [],
+                i = 0,
+                counter = 0;
+
+            pageStart = pageStart < 1 ? 1 : pageStart;//check the range of the page start to see if its less than 1.
+
+            for (i = pageStart, counter = 0; counter < this.numberOfPages && i <= totalPages; i = i + 1, counter = counter + 1) {//fill the pages
+                output.push(i);
+            }
+
+            output.first = 1;//add the first when the current page leaves the 1st page.
+
+            if (this.currentPage > 1) {// add the previous when the current page leaves the 1st page
+                output.prev = this.currentPage - 1;
+            } else {
+                output.prev = 1;
+            }
+
+            if (this.currentPage < totalPages) {// add the next page when the current page doesn't reach the last page
+                output.next = this.currentPage + 1;
+            } else {
+                output.next = totalPages;
+            }
+
+            output.last = totalPages;// add the last page when the current page doesn't reach the last page
+
+            output.current = this.currentPage;//mark the current page.
+
+            output.total = totalPages;
+
+            output.numberOfPages = this.options.numberOfPages;
+
+            return output;
+
+        },
+
+        /**
+         * Gets the value from the options, this is made to handle the situation where value is the return value of a function.
+         *
+         * @return mixed value that depends on the type of parameters, if the given parameter is a function, then the evaluated result is returned. Otherwise the parameter itself will get returned.
+         * */
+        getValueFromOption: function (value) {
+
+            var output = null,
+                args = Array.prototype.slice.call(arguments, 1);
+
+            if (typeof value === 'function') {
+                output = value.apply(this, args);
+            } else {
+                output = value;
+            }
+
+            return output;
+
+        }
+
+    };
+
+
+    /* TYPEAHEAD PLUGIN DEFINITION
+     * =========================== */
+
+    old = $.fn.bootstrapPaginator;
+
+    $.fn.bootstrapPaginator = function (option) {
+
+        var args = arguments,
+            result = null;
+
+        $(this).each(function (index, item) {
+            var $this = $(item),
+                data = $this.data('bootstrapPaginator'),
+                options = (typeof option !== 'object') ? null : option;
+
+            if (!data) {
+                data = new BootstrapPaginator(this, options);
+
+                $this = $(data.$element);
+
+                $this.data('bootstrapPaginator', data);
+
+                return;
+            }
+
+            if (typeof option === 'string') {
+
+                if (data[option]) {
+                    result = data[option].apply(data, Array.prototype.slice.call(args, 1));
+                } else {
+                    throw "Method " + option + " does not exist";
+                }
+
+            } else {
+                result = data.setOptions(option);
+            }
+        });
+
+        return result;
+
+    };
+
+    $.fn.bootstrapPaginator.sizeArray = {
+
+        "2": {
+            "large": "pagination-large",
+            "small": "pagination-small",
+            "mini": "pagination-mini"
+        },
+        "3": {
+            "large": "pagination-lg",
+            "small": "pagination-sm",
+            "mini": ""
+        }
+
+    };
+
+    $.fn.bootstrapPaginator.defaults = {
+        containerClass: "",
+        size: "normal",
+        alignment: "left",
+        bootstrapMajorVersion: 2,
+        listContainerClass: "",
+        itemContainerClass: function (type, page, current) {
+            return (page === current) ? "active" : "";
+        },
+        itemContentClass: function (type, page, current) {
+            return "";
+        },
+        currentPage: 1,
+        numberOfPages: 5,
+        totalPages: 1,
+        pageUrl: function (type, page, current) {
+            return null;
+        },
+        onPageClicked: null,
+        onPageChanged: null,
+        useBootstrapTooltip: false,
+        shouldShowPage: function (type, page, current) {
+
+            var result = true;
+
+            switch (type) {
+            case "first":
+                result = (current !== 1);
+                break;
+            case "prev":
+                result = (current !== 1);
+                break;
+            case "next":
+                result = (current !== this.totalPages);
+                break;
+            case "last":
+                result = (current !== this.totalPages);
+                break;
+            case "page":
+                result = true;
+                break;
+            }
+
+            return result;
+
+        },
+        itemTexts: function (type, page, current) {
+            switch (type) {
+            case "first":
+                return "&lt;&lt;";
+            case "prev":
+                return "&lt;";
+            case "next":
+                return "&gt;";
+            case "last":
+                return "&gt;&gt;";
+            case "page":
+                return page;
+            }
+        },
+        tooltipTitles: function (type, page, current) {
+
+            switch (type) {
+            case "first":
+                return "Go to first page";
+            case "prev":
+                return "Go to previous page";
+            case "next":
+                return "Go to next page";
+            case "last":
+                return "Go to last page";
+            case "page":
+                return (page === current) ? "Current page is " + page : "Go to page " + page;
+            }
+        },
+        bootstrapTooltipOptions: {
+            animation: true,
+            html: true,
+            placement: 'top',
+            selector: false,
+            title: "",
+            container: false
+        }
+    };
+
+    $.fn.bootstrapPaginator.Constructor = BootstrapPaginator;
+
+
+
+}(window.jQuery));

+ 1 - 1
lib/ztree/jquery.ztree.exedit.js

@@ -241,7 +241,7 @@
 		setSonNodeLevel: function(setting, parentNode, node) {
 			if (!node) return;
 			var childKey = setting.data.key.children;
-			node.level = (parentNode)? parentNode.level + 1 : 0;
+			node.level = (parentNode) ? parentNode.level + 1 : 0;
 			if (!node[childKey]) return;
 			for (var i = 0, l = node[childKey].length; i < l; i++) {
 				if (node[childKey][i]) data.setSonNodeLevel(setting, node, node[childKey][i]);

+ 33 - 28
modules/bills_lib/routes/bills_lib_routes.js

@@ -1,33 +1,38 @@
 /**
  * Created by vian on 2017/3/17.
  */
-var express = require("express");
-var billsController = require("./../controllers/bills_lib_controllers");
-var billsRouter =express.Router();
+let express = require("express");
+module.exports = function (app) {
+    let billsController = require("./../controllers/bills_lib_controllers");
+    let billsRouter =express.Router();
+
+    billsRouter.post('/getMaxNumber', billsController.getMaxNumber);
+    billsRouter.post('/getABillsLib', billsController.getABillsLib);
+    billsRouter.post("/getStdBillsLib", billsController.getStdBillsLib);
+    billsRouter.post("/createStdBillsLib", billsController.createStdBillsLib);
+    billsRouter.post("/deleteStdBillsLib", billsController.deleteStdBillsLib);
+    billsRouter.post("/renameStdBillsLib", billsController.renameStdBillsLib);
+    billsRouter.post("/getStdBillsLibName", billsController.getStdBillsLibName);
+    billsRouter.post("/getBills", billsController.getBills);
+    billsRouter.post("/createBills", billsController.createBills);
+    billsRouter.post("/updatePNId", billsController.updatePNId);
+    billsRouter.post("/updateBills", billsController.updateBills);
+    billsRouter.post("/updateBillsArr", billsController.updateBillsArr);
+    billsRouter.post("/pasteBills", billsController.pasteBills);
+    billsRouter.post('/updateRecharge', billsController.updateRecharge);
+    billsRouter.post("/deleteBills", billsController.deleteBills);
+    billsRouter.post("/getJobContent", billsController.getJobContent);
+    billsRouter.post("/createJobContent", billsController.createJobContent);
+    billsRouter.post("/updateJobContent", billsController.updateJobContent);
+    billsRouter.post("/deleteJobContent", billsController.deleteJobContent);
+    billsRouter.post("/getItemCharacter", billsController.getItemCharacter);
+    billsRouter.post("/createItemCharacter", billsController.createItemCharacter);
+    billsRouter.post("/updateItemCharacter", billsController.updateItemCharacter);
+    billsRouter.post("/updateValue", billsController.updateValue);
+    billsRouter.post("/deleteItemCharacter", billsController.deleteItemCharacter);
+
+    app.use("/stdBillsEditor", billsRouter);
+};
+
 
-billsRouter.post('/getMaxNumber', billsController.getMaxNumber);
-billsRouter.post('/getABillsLib', billsController.getABillsLib);
-billsRouter.post("/getStdBillsLib", billsController.getStdBillsLib);
-billsRouter.post("/createStdBillsLib", billsController.createStdBillsLib);
-billsRouter.post("/deleteStdBillsLib", billsController.deleteStdBillsLib);
-billsRouter.post("/renameStdBillsLib", billsController.renameStdBillsLib);
-billsRouter.post("/getStdBillsLibName", billsController.getStdBillsLibName);
-billsRouter.post("/getBills", billsController.getBills);
-billsRouter.post("/createBills", billsController.createBills);
-billsRouter.post("/updatePNId", billsController.updatePNId);
-billsRouter.post("/updateBills", billsController.updateBills);
-billsRouter.post("/updateBillsArr", billsController.updateBillsArr);
-billsRouter.post("/pasteBills", billsController.pasteBills);
-billsRouter.post('/updateRecharge', billsController.updateRecharge);
-billsRouter.post("/deleteBills", billsController.deleteBills);
-billsRouter.post("/getJobContent", billsController.getJobContent);
-billsRouter.post("/createJobContent", billsController.createJobContent);
-billsRouter.post("/updateJobContent", billsController.updateJobContent);
-billsRouter.post("/deleteJobContent", billsController.deleteJobContent);
-billsRouter.post("/getItemCharacter", billsController.getItemCharacter);
-billsRouter.post("/createItemCharacter", billsController.createItemCharacter);
-billsRouter.post("/updateItemCharacter", billsController.updateItemCharacter);
-billsRouter.post("/updateValue", billsController.updateValue);
-billsRouter.post("/deleteItemCharacter", billsController.deleteItemCharacter);
 
-module.exports = billsRouter;

+ 28 - 0
modules/bills_lib/routes/maintain_bills_routes.js

@@ -0,0 +1,28 @@
+/**
+ * Created by chen on 2017/7/17.
+ */
+
+let express = require("express");
+
+
+module.exports = function (app) {
+    app.get("/stdBillsmain", function(req, res){
+        if(!req.session.sessionUser){
+            res.redirect('/login');
+        }
+        else {
+            res.render("maintain/bills_lib/html/main.html",
+                {userAccount: req.session.userAccount,
+                    userID: req.session.sessionUser.ssoId});
+        }
+    });
+    app.get("/stdBills", function(req, res){
+        res.render("maintain/bills_lib/html/qingdan.html");
+    });
+    app.get('/stdJobs', function(req, res){
+        res.render('maintain/bills_lib/html/neirong.html');
+    });
+    app.get('/stdItems', function(req, res){
+        res.render('maintain/bills_lib/html/tezheng.html');
+    });
+};

+ 9 - 2
modules/common/base/base_controller.js

@@ -5,6 +5,9 @@
  * @date 2017/6/29
  * @version
  */
+import Moment from "moment";
+import Url from "url";
+
 class BaseController {
 
     /**
@@ -34,8 +37,12 @@ class BaseController {
      * @return {void}
      */
     init(request, response, next) {
-        // 页面标题
-        response.locals.title = 'test';
+        // moment工具
+        response.locals.moment = Moment;
+
+        // url相关数据
+        let urlInfo = Url.parse(request.originalUrl, true);
+        response.locals.urlQuery = JSON.stringify(urlInfo.query);
 
         next();
     }

+ 14 - 4
modules/common/base/base_model.js

@@ -47,15 +47,25 @@ class BaseModel {
      * @param {Object} condition
      * @param {Object} fields
      * @param {boolean} singleData
+     * @param {String} indexBy
      * @return {Promise}
      */
-    async findDataByCondition(condition, fields = null, singleData = true) {
+    async findDataByCondition(condition, fields = null, singleData = true, indexBy = null) {
+        let result = [];
         if (Object.keys(condition).length <= 0) {
-            return null;
+            return result;
         }
 
-        let data = await singleData ? this.db.findOne(condition, fields) : this.db.find(condition, fields);
-        return data;
+        result = singleData ? await this.db.findOne(condition, fields) : await this.db.find(condition, fields);
+
+        if (indexBy !== null && !singleData && result.length > 0) {
+            let tmpResult = {};
+            for(let tmp of result) {
+                tmpResult[tmp[indexBy]] = tmp;
+            }
+            result = tmpResult;
+        }
+        return result;
     }
 
     /**

+ 38 - 0
modules/common/const/glj_type_const.js

@@ -0,0 +1,38 @@
+/**
+ * 工料机类型常量
+ *
+ * @author CaiAoLin
+ * @date 2017/7/7
+ * @version
+ */
+
+const gljType = {
+    // 人工
+    LABOUR: 1,
+    // ==============材料类型=================
+    // 普通材料
+    GENERAL_MATERIAL: 201,
+    // 混凝土
+    CONCRETE: 202,
+    // 砂浆
+    MORTAR: 203,
+    // 配合比
+    MIX_RATIO: 204,
+    // 商品混凝土
+    COMMERCIAL_CONCRETE: 205,
+    // 商品砂浆
+    COMMERCIAL_MORTAR: 206,
+    // ==============材料类型=================
+    // ==============机械类型=================
+    // 普通机械
+    GENERAL_MACHINE: 301,
+    // 机械组成物
+    MACHINE_COMPOSITION: 302,
+    // ==============机械类型=================
+    // 主材
+    MAIN_MATERIAL: 4,
+    // 设备
+    EQUIPMENT: 5
+};
+
+export default gljType;

+ 14 - 0
modules/common/const/log_type_const.js

@@ -0,0 +1,14 @@
+/**
+ * 日志类型常量
+ *
+ * @author CaiAoLin
+ * @date 2017/7/27
+ * @version
+ */
+
+const logType = {
+    // 登录日志
+    LOGIN_LOG: 1
+};
+
+export default logType;

+ 7 - 1
modules/common/helper/mongoose_helper.js

@@ -51,6 +51,12 @@ class MongooseHelper {
      */
     find(conditions, fields = null, option = null) {
         let self = this;
+        let limit = 0;
+        let skip = 0;
+        if (Object.keys(option).length > 0) {
+            limit = option.pageSize !== undefined ? option.pageSize : limit;
+            skip = option.offset !== undefined ? option.offset : skip;
+        }
         return new Promise(function (resolve, reject) {
             self.model.find(conditions, fields, option, function (error, data) {
                 if (error) {
@@ -58,7 +64,7 @@ class MongooseHelper {
                 } else {
                     resolve(data);
                 }
-            });
+            }).skip(skip).limit(limit);
         });
     }
 

+ 30 - 0
modules/common/std/schemas/std_mix_ratio.js

@@ -0,0 +1,30 @@
+/**
+ * 配合比标准库数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/7/7
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'std_mix_ratio';
+let modelSchema = {
+    // 编码
+    code: String,
+    // 名称
+    name: String,
+    // 消耗量
+    consumption: Number,
+    // 关联工料机的编码
+    connect_code: {
+        type: String,
+        index: true
+    },
+    // 对应的工料机总库id
+    glj_id: Number,
+    // 对应工料机类型
+    glj_type: Number
+};
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 13 - 0
modules/common/std/schemas/std_ration_lib_glj_list.js

@@ -0,0 +1,13 @@
+/**
+ * 定额工料机标准库数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/7/7
+ * @version
+ */
+import mongoose from "mongoose";
+
+let collectionName = 'std_ration_lib_glj_list';
+// 因为别处已初始化过,所以直接使用
+let model = mongoose.model(collectionName);
+export {model as default, collectionName as collectionName};

+ 48 - 0
modules/common/std/std_mix_ratio_model.js

@@ -0,0 +1,48 @@
+/**
+ * 配合比标准库业务模型
+ *
+ * @author CaiAoLin
+ * @date 2017/7/10
+ * @version
+ */
+import BaseModel from "../base/base_model";
+import STDMixRatioSchema from "./schemas/std_mix_ratio";
+
+class STDMixRatioModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = STDMixRatioSchema;
+        parent.init();
+    }
+
+    /**
+     * 根据编码获取对应的组成物信息
+     *
+     * @param {String} code
+     * @return {Promise}
+     */
+    async getDataByCode(code) {
+        let result = [];
+        code = code.trim();
+        try {
+            if (code === '') {
+                throw '编码为空';
+            }
+            result = await this.findDataByCondition({connect_code: code}, null, false);
+        } catch (error) {
+            console.log('std_mix_ratio_model:' + error);
+            result = [];
+        }
+
+        return result;
+    }
+
+}
+
+export default STDMixRatioModel;

+ 51 - 0
modules/common/std/std_ration_lib_glj_list_model.js

@@ -0,0 +1,51 @@
+/**
+ * 工料机总库相关业务逻辑
+ *
+ * @author CaiAoLin
+ * @date 2017/7/11
+ * @version
+ */
+import BaseModel from "../base/base_model";
+import STDRationLibGLJListSchemas from "./schemas/std_ration_lib_glj_list";
+
+class STDRationLibGLJListModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = STDRationLibGLJListSchemas;
+        parent.init();
+    }
+
+    /**
+     * 根据id获取数据
+     *
+     * @param {Number|Array} id
+     * @return {Promise}
+     */
+    async getDataById(id) {
+        let result = null;
+        try {
+            if (!id instanceof Array) {
+                id = parseInt(id);
+                if (isNaN(id)) {
+                    throw 'id有误';
+                }
+            }
+            let condition = {ID: {"$in": id}};
+            result = await this.findDataByCondition(condition, null, false);
+        } catch (error) {
+            console.log(error);
+            result = null;
+        }
+
+        return result;
+    }
+
+}
+
+export default STDRationLibGLJListModel;

+ 16 - 11
modules/fee_rates/routes/fee_rates_route.js

@@ -2,18 +2,23 @@
  * Created by CSL on 2017-03-22.
  */
 
-var express = require('express');
-var frRouter = express.Router();
-var frController = require('../controllers/fee_rates_controller');
+let express = require('express');
+let frController = require('../controllers/fee_rates_controller');
+
+module.exports = function (app) {
+
+    var frRouter = express.Router();
+    frRouter.get('/', function(req, res) {
+        res.render('building_saas/fee_rates/fee_rate', {});
+    });
+    frRouter.post('/getLibNames', frController.libNames);
+    frRouter.post('/getLibFeeRates', frController.libFeeRates);
+    frRouter.post('/getProjectFeeRates', frController.projectFeeRates);
+    frRouter.post('/updateProjectFeeRate', frController.projectFeeUpdateRate);
+
+    app.use('/feeRates',frRouter);
+}
 
-frRouter.get('/', function(req, res) {
-    res.render('building_saas/fee_rates/fee_rate', {});
-});
 
-frRouter.post('/getLibNames', frController.libNames);
-frRouter.post('/getLibFeeRates', frController.libFeeRates);
-frRouter.post('/getProjectFeeRates', frController.projectFeeRates);
-frRouter.post('/updateProjectFeeRate', frController.projectFeeUpdateRate);
 
 
-module.exports = frRouter;

+ 132 - 19
modules/glj/controllers/glj_controller.js

@@ -6,9 +6,11 @@
  * @version
  */
 import BaseController from "../../common/base/base_controller";
+import GLJTypeConst from "../../common/const/glj_type_const";
 import GLJListModel from "../models/glj_list_model";
 import UnitPriceModel from "../models/unit_price_model";
 import UnitPriceFileModel from "../models/unit_price_file_model";
+import MixRatioModel from "../models/mix_ratio_model";
 
 class GLJController extends BaseController {
 
@@ -37,18 +39,30 @@ class GLJController extends BaseController {
             if (isNaN(projectId) || projectId <= 0) {
                 throw '标段id有误';
             }
+
+            // 获取标段对应的单价文件id
+            let unitPriceFileModel = new UnitPriceFileModel();
+            let unitPriceFile = await unitPriceFileModel.getDataByProject(projectId);
+            if (!unitPriceFile) {
+                throw '没有对应的单价文件';
+            }
+            // 单价文件id作为同步的roomId
+            let unitPriceFileId = unitPriceFile.id;
+
             // 先获取对应标段的项目工料机数据
             let gljListModel = new GLJListModel();
-            let gljList = await gljListModel.getListByProjectId(projectId);
+            let [gljList, mixRatioConnectData] = await gljListModel.getListByProjectId(projectId, unitPriceFileId);
 
             let renderData = {
                 gljList: JSON.stringify(gljList),
+                mixRatioConnectData: JSON.stringify(mixRatioConnectData),
                 materialIdList: gljListModel.materialIdList,
-                autoChangeGLJType: UnitPriceModel.autoChangeGLJType,
-                triggerCalculateGLJType: UnitPriceModel.triggerCalculateGLJType
+                ownCompositionTypes: gljListModel.ownCompositionTypes,
+                hostname: request.hostname,
+                roomId: unitPriceFileId,
+                GLJTypeConst: JSON.stringify(GLJTypeConst)
             };
             response.render('glj/html/glj_index', renderData);
-
         } catch (error) {
             response.status(404).send('404 Error');
         }
@@ -75,7 +89,7 @@ class GLJController extends BaseController {
         };
         try {
             // 可编辑的字段
-            let editableField = ['is_evaluate', 'unit_price.market_price', 'is_adjust_price'];
+            let editableField = ['is_evaluate', 'unit_price.market_price', 'is_adjust_price', 'mix_ratio.consumption'];
             if (editableField.indexOf(field) < 0) {
                 throw '对应字段不能编辑';
             }
@@ -97,11 +111,41 @@ class GLJController extends BaseController {
                     model = new GLJListModel();
                     // 更新数据
                     result = await model.updateById(id, updateData);
+
                     break;
                 case 'unit_price':
+                    let extend = request.body.extend;
                     model = new UnitPriceModel();
                     // 更新数据
-                    result = await model.updatePriceById(id, updateData);
+                    result = await model.updatePrice({id: id}, updateData, extend);
+
+                    break;
+                case 'mix_ratio':
+                    let marketPrice = request.body.market_price;
+                    let basePrice = request.body.base_price;
+
+                    model = new MixRatioModel();
+                    result = await model.updateById(id, updateData);
+
+                    if (result && marketPrice && basePrice) {
+                        // 修改后更新父节点的项目工料机市场单价
+                        let mixRatioData = await model.findDataByCondition({id: id});
+                        if (mixRatioData.length <= 0) {
+                            break;
+                        }
+                        // 更新市场单价和基价单价
+                        let condition = {code: mixRatioData.connect_code, unit_price_file_id: mixRatioData.unit_price_file_id};
+                        let unitPriceUpdate = {
+                            base_price: basePrice,
+                            market_price: marketPrice
+                        };
+                        let unitPriceModel = new UnitPriceModel();
+                        let unitPriceResult = await unitPriceModel.updatePrice(condition, unitPriceUpdate);
+                        if (!unitPriceResult) {
+                            throw '更新单价数据失败';
+                        }
+                    }
+
                     break;
             }
 
@@ -117,6 +161,76 @@ class GLJController extends BaseController {
     }
 
     /**
+     * 加载配合比相关数据
+     *
+     * @return {void}
+     */
+    async getRatio(request, response) {
+        let projectGLJId = request.body.id;
+        let projectId = request.body.project_id;
+        let responseData = {
+            err: 0,
+            data: null
+        };
+        try {
+            // 获取标段对应的单价文件id
+            let unitPriceFileModel = new UnitPriceFileModel();
+            let unitPriceFile = await unitPriceFileModel.getDataByProject(projectId);
+            if (!unitPriceFile) {
+                throw '没有对应的单价文件';
+            }
+
+            // 单价文件id作为同步的roomId
+            let unitPriceFileId = unitPriceFile.id;
+
+            let gljListModel = new GLJListModel();
+            let result = await gljListModel.getCompositionList(projectGLJId, unitPriceFileId);
+            if (result.length <= 0) {
+                throw '没有找到数据';
+            }
+
+            responseData.data = JSON.stringify(result);
+        } catch (error) {
+            console.log(error);
+            responseData.err = 1;
+        }
+
+        response.json(responseData);
+    }
+
+    /**
+     * 删除配合比数据
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async deleteMixRatio(request, response) {
+        let id = request.body.id;
+        id = parseInt(id);
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            if (isNaN(id) || id <= 0) {
+                throw '参数错误';
+            }
+
+            let mixRatioModel = new MixRatioModel();
+            let result = await mixRatioModel.deleteById(id);
+            if (!result) {
+                throw '删除失败';
+            }
+        } catch (error) {
+            responseData.err = 1;
+            responseData.msg = error;
+        }
+
+        response.json(responseData);
+    }
+
+    /**
      * 模拟定额插入
      *
      * @param {object} request
@@ -126,21 +240,20 @@ class GLJController extends BaseController {
     async test(request, response) {
         // 从定额库获取的数据
         let data = {
-            glj_repository_id: 1,
+            glj_id: 803,
             project_id: 1,
-            code: '00010201',
-            name: '土石方综合工日',
-            specs: '',
-            unit: '工日',
-            type: 2,
-            base_price: '44.76',
-            market_price: '44.76'
+            code: '85011701',
+            name: '电动夯实机 夯能20~62N·m',
+            specs: '夯能20~62N·m',
+            unit: '台班',
+            type: GLJTypeConst.GENERAL_MACHINE,
+            base_price: 120.45,
+            market_price: 120.45
         };
         try {
             let gljListModel = new GLJListModel();
             let result = await gljListModel.addList(data);
 
-            console.log(result);
         } catch (error) {
             console.log(error);
         }
@@ -157,11 +270,11 @@ class GLJController extends BaseController {
      */
     async testModify(request, response) {
         let projectId = request.query.project;
-        // // 修改数据
+        // 修改数据
         let updateData = {
-            code: '00010201',
-            market_price: '49',
-            name: '土石方综合工日',
+            code: '36290101',
+            market_price: '13',
+            name: '水(10)',
             project_id: projectId
         };
 

+ 361 - 25
modules/glj/models/glj_list_model.js

@@ -9,7 +9,13 @@ import BaseModel from "../../common/base/base_model";
 import {default as GLJSchemas, collectionName as gljCollectionName} from "./schemas/glj";
 import CounterModel from "./counter_model";
 import UnitPriceModel from "./unit_price_model";
+import STDMixRatioModel from "../../common/std/std_mix_ratio_model";
 import UnitPriceFileModel from "./unit_price_file_model";
+import GLJTypeConst from "../../common/const/glj_type_const";
+import RationGLJFacade from "../../ration_glj/facade/ration_glj_facade";
+import STDRationLibGLJListModel from "../../common/std/std_ration_lib_glj_list_model";
+import STDGLJType from "../../../public/cache/std_glj_type_util";
+import MixRatioModel from "./mix_ratio_model";
 
 class GLJListModel extends BaseModel {
 
@@ -18,7 +24,16 @@ class GLJListModel extends BaseModel {
      *
      * @var {Array}
      */
-    materialIdList = [5, 6, 7];
+    materialIdList = [GLJTypeConst.GENERAL_MATERIAL, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO,
+        GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR];
+
+    /**
+     * 拥有组成物的工料机类型id
+     *
+     * @var {Array}
+     */
+    ownCompositionTypes = [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO,
+        GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR, GLJTypeConst.GENERAL_MACHINE];
 
     /**
      * 构造函数
@@ -41,7 +56,7 @@ class GLJListModel extends BaseModel {
         switch (scene) {
             // 新增数据的验证规则
             case 'add':
-                this.model.schema.path('glj_repository_id').required(true);
+                this.model.schema.path('glj_id').required(true);
                 this.model.schema.path('project_id').required(true);
                 this.model.schema.path('code').required(true);
                 this.model.schema.path('name').required(true);
@@ -50,13 +65,15 @@ class GLJListModel extends BaseModel {
     }
 
     /**
-     * 根据标段对应工料机数据
+     * 根据标段对应项目工料机列表
      *
      * @param {Number} projectId
+     * @param {Number} unitPriceFileId
      * @return {Promise}
      */
-    async getListByProjectId(projectId) {
+    async getListByProjectId(projectId, unitPriceFileId) {
         let gljData = null;
+        let mixRatioConnectData = {};
         try {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
@@ -68,20 +85,62 @@ class GLJListModel extends BaseModel {
                 throw '无数据';
             }
 
-            // 获取标段对应的单价文件id
-            let unitPriceFileModel = new UnitPriceFileModel();
-            let unitPriceFile = await unitPriceFileModel.getDataByProject(projectId);
-            if (!unitPriceFile) {
-                throw '没有对应的单价文件';
-            }
-            let unitPriceFileId = unitPriceFile.id;
-
             // 获取标段设置的单价文件数据
             let unitPriceModel = new UnitPriceModel();
             let unitPriceList = await unitPriceModel.getDataByFileId(unitPriceFileId);
 
-            // 组合数据
-            this.combineUnitPrice(gljData, unitPriceList);
+            // 整理获取工料机ID list
+            let gljIdList = [];
+            // 整理获取有组成物的项目工料机编码
+            let projectGLJCode = [];
+            for(let tmp of gljData) {
+                gljIdList.push(tmp.id);
+                // 有组成物的类型才入数组
+                if (this.ownCompositionTypes.indexOf(tmp.type)) {
+                    projectGLJCode.push(tmp.code);
+                }
+            }
+
+            // 从定额工料机库中获取消耗量
+            condition = {
+                projectID: projectId,
+                projectGLJIDList: gljIdList
+            };
+            let quantityData = await RationGLJFacade.getQuantityByProjectGLJ(condition);
+            let quantityList = {};
+            // 整理数据
+            for (let tmp of quantityData) {
+                quantityList[tmp.projectGLJID] = tmp.quantity;
+            }
+
+            // 查找组成物的消耗量
+            let totalComposition = {};
+            let mixRatioData = {};
+            if (projectGLJCode.length > 0) {
+                let mixRatioModel = new MixRatioModel();
+                condition = {connect_code: {"$in": projectGLJCode}, unit_price_file_id: unitPriceFileId};
+                let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
+                for (let tmp of mixRatioList) {
+                    totalComposition[tmp.connect_code] = totalComposition[tmp.connect_code] === undefined ? tmp.consumption :
+                        totalComposition[tmp.connect_code] + tmp.consumption;
+                    totalComposition[tmp.connect_code] = Number(totalComposition[tmp.connect_code].toFixed(2));
+
+                    if (mixRatioData[tmp.glj_id] !== undefined) {
+                        mixRatioData[tmp.glj_id].push(tmp);
+                    } else {
+                        mixRatioData[tmp.glj_id] = [tmp];
+                    }
+
+                    if (mixRatioConnectData[tmp.glj_id] !== undefined) {
+                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_code);
+                    } else {
+                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_code];
+                    }
+                }
+            }
+
+            // 组合单价数据
+            this.combineData(gljData, unitPriceList, quantityList, mixRatioData, totalComposition);
 
             // 排序
             gljData.sort(function (a, b) {
@@ -92,7 +151,7 @@ class GLJListModel extends BaseModel {
             gljData = [];
         }
 
-        return gljData;
+        return [gljData, mixRatioConnectData];
     }
 
     /**
@@ -100,27 +159,51 @@ class GLJListModel extends BaseModel {
      *
      * @param {object} gljList
      * @param {object} unitPriceList
+     * @param {object} quantityList
+     * @param {object} mixRatioData 组合物明细数据
+     * @param {object} totalComposition 组合物父工料机统计数据
      * @return {void}
      */
-    combineUnitPrice(gljList, unitPriceList) {
-        // @todo 以后从js获取?
-        let labour = 2;
-        let machine = 64;
+    combineData(gljList, unitPriceList, quantityList = {}, mixRatioData = {}, totalComposition = {}) {
+        // 整理组成物消耗量(只有在总列表显示的时候才需用到,获取单项项目工料机内容则忽略)
+        let compositionConsumption = {};
+        if (Object.keys(mixRatioData).length > 0 && Object.keys(totalComposition).length > 0) {
+            for(let index in mixRatioData) {
+                for(let tmp of mixRatioData[index]) {
+                    compositionConsumption[tmp.glj_id] = compositionConsumption[tmp.glj_id] === undefined ? tmp.consumption :
+                        compositionConsumption[tmp.glj_id] + tmp.consumption;
+                }
 
+            }
+        }
         // 循环组合数据
         for(let glj of gljList) {
             if (glj.code === undefined) {
                 continue;
             }
-            glj.unit_price = unitPriceList[glj.code + glj.name] !== undefined ? unitPriceList[glj.code + glj.name] : null;
+            glj.unit_price = unitPriceList !== null && unitPriceList[glj.code + glj.name] !== undefined ? unitPriceList[glj.code + glj.name] : null;
+
+            if (glj.unit_price === null) {
+                continue;
+            }
+
+            let gljId = glj.glj_id + '';
+            // 消耗量赋值
+            glj.quantity = quantityList[glj.id] !== undefined ? quantityList[glj.id] : 0;
+            glj.quantity = totalComposition[glj.code] !== undefined ? totalComposition[glj.code] : glj.quantity;
+            glj.quantity = compositionConsumption[gljId] !== undefined ?  glj.quantity + compositionConsumption[gljId] : glj.quantity;
+
+            // 组成物数据
+            glj.ratio_data = mixRatioData[gljId] !== undefined ? mixRatioData[gljId] : [];
+
             // 计算调整基价
-            switch (glj.type + '') {
+            switch (glj.unit_price.type + '') {
                 // 人工: 调整基价=基价单价*调整系数
-                case labour:
+                case GLJTypeConst.LABOUR:
                     glj.adjust_price = glj.adjustment * glj.unit_price.base_price;
                     break;
                 // 机械类型的算法
-                case machine:
+                case GLJTypeConst.MACHINE:
                     console.log('机械');
                     break;
                 // 材料、主材、设备
@@ -145,6 +228,7 @@ class GLJListModel extends BaseModel {
             }
             // 首先查找是否有同编码同名称的工料机数据
             let projectGljData = await this.findDataByCondition({code: data.code, project_id: data.project_id});
+            let isAddProjectGLJ = false;
             // 如果找不到数据则新增
             if (!projectGljData) {
                 // 新增单条记录 (两个操作本来应该是事务操作,然而mongodb事务支持比较弱,就当作是都可以顺利执行)
@@ -152,6 +236,7 @@ class GLJListModel extends BaseModel {
                 if (!gljInsertData) {
                     throw '新增项目工料机失败!';
                 }
+                isAddProjectGLJ = true;
                 projectGljData = gljInsertData;
             }
 
@@ -163,6 +248,12 @@ class GLJListModel extends BaseModel {
             }
             let unitPriceFileId = unitPriceFile.id;
 
+            // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
+            if (isAddProjectGLJ && (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
+                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE)) {
+                this.compositionInit(data.code, data.project_id, unitPriceFileId);
+            }
+
             // 新增单价文件
             let unitPriceModel = new UnitPriceModel();
             let [unitPriceInsertData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId);
@@ -193,7 +284,14 @@ class GLJListModel extends BaseModel {
         }
 
         let counterModel = new CounterModel();
-        data.id = await counterModel.getId(gljCollectionName);
+        if (data instanceof Array) {
+            // 如果是批量新增
+            for(let tmp in data) {
+                data[tmp].id = await counterModel.getId(gljCollectionName);
+            }
+        } else {
+            data.id = await counterModel.getId(gljCollectionName);
+        }
 
         this.setScene('add');
         let result = await this.db.create(data);
@@ -243,7 +341,7 @@ class GLJListModel extends BaseModel {
             // 如果单价数据新增则工料机也需要新增
             if (isAdd) {
                 // 如果没有对应的记录则新增一条工料机数据,并更改name
-                let regular = /\(\d\)/;
+                let regular = /\(\d+\)/;
                 let changeString = '(' + gljCount + ')';
                 updateData.name = regular.test(updateData.name) ? updateData.name.replace(regular, changeString) :
                     updateData.name + changeString;
@@ -298,6 +396,244 @@ class GLJListModel extends BaseModel {
 
         return index;
     }
+
+    /**
+     * 工料机中组成物操作
+     * 该方法只在确保没有对应项目工料机的时候才会调用
+     *
+     * @param {String} code
+     * @param {Number} projectId
+     * @param {Number} unitPriceFileId
+     * @return {void}
+     */
+    async compositionInit(code, projectId, unitPriceFileId) {
+        // 查找对应组成物的项目工料机数据
+        let [projectGljList, compositionList] = await this.getCompositionGLJList(code, projectId, 'name');
+
+
+        // 整理配合比待插入数据
+        let mixRatioInsertData = [];
+        for (let tmp of compositionList) {
+            // 配合比数据插入
+            let mixRatioData = {
+                consumption: tmp.consumption,
+                glj_id: tmp.glj_id,
+                unit_price_file_id: unitPriceFileId,
+                connect_code: tmp.connect_code,
+                glj_type: tmp.glj_type
+            };
+            mixRatioInsertData.push(mixRatioData);
+        }
+
+        // 插入配合比表
+        // 因为有可能项目工料机与单价数据已存在,但配合比数据不存在,所以先插入配合比,后续判断如果存在项目工料机则可以省下数据库操作
+        let mixRatioModel = new MixRatioModel();
+        let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData);
+        if (!addMixRatioResult) {
+            throw '组成物插入单价数据失败!';
+        }
+
+        // 如果已经存在则后续操作停止
+        if(projectGljList.length === compositionList.length) {
+            return;
+        }
+
+        // 插入不存在的项目工料机数据
+        let notInGLJId = [];
+        let compositionData = {};
+        for(let tmp of compositionList) {
+            compositionData[tmp.glj_id] = tmp;
+            if (projectGljList[tmp.name] !== undefined) {
+                continue;
+            }
+            // 把不存在的工料机总库id加入数组
+            notInGLJId.push(tmp.glj_id);
+        }
+
+        // 如果没有对应的数据则忽略后面的操作
+        if (notInGLJId.length <= 0) {
+            return;
+        }
+
+        // 查找对应工料机总库数据
+        let stdRationLibGLJListModel = new STDRationLibGLJListModel();
+        let stdGLJData = await stdRationLibGLJListModel.getDataById(notInGLJId);
+        if (stdGLJData === null) {
+            throw '没有找到对应的工料机总库数据';
+        }
+
+        // 获取工料机类型以及整理数据
+        let gljTypeList = STDGLJType.getStdGljTypeCacheObj().toArray();
+        let gljType = {};
+        for (let tmp of gljTypeList) {
+            gljType[tmp.fullName] = tmp.ID;
+        }
+
+        // 整理插入的数据
+        let gljInsertData = [];
+        let unitPriceInsertData = [];
+        for (let tmp of stdGLJData) {
+            // 项目工料机插入的数据
+            let gljData = {
+                glj_id: tmp.ID,
+                project_id: projectId,
+                code: tmp.code,
+                name: tmp.name
+            };
+            gljInsertData.push(gljData);
+
+            // 单价文件插入的数据
+            let unitPriceData = {
+                base_price: tmp.basePrice,
+                // 初始市场价=基价
+                market_price: tmp.basePrice,
+                code: tmp.code,
+                name: tmp.name,
+                specs: tmp.specs,
+                unit: tmp.unit,
+                unit_price_file_id: unitPriceFileId,
+                // 如果没有对应的工料机类型则默认设置为普通材料
+                type: gljType[tmp.gljDistType] !== undefined ? gljType[tmp.gljDistType] : GLJTypeConst.GENERAL_MATERIAL
+            };
+            unitPriceInsertData.push(unitPriceData);
+        }
+
+        // 整理完后开始插入数据
+        let addResult = await this.add(gljInsertData);
+        if (!addResult) {
+            throw '组成物插入项目工料机失败!';
+        }
+        // 插入单价数据表
+        let unitPriceModel = new UnitPriceModel();
+        let addUnitPriceResult = await unitPriceModel.add(unitPriceInsertData);
+        if (!addUnitPriceResult) {
+            throw '组成物插入单价数据失败!';
+        }
+
+    }
+
+    /**
+     * 获取组成物具体数据
+     *
+     * @param {Number} projectGLJId
+     * @param {Number} unitPriceFileId
+     * @return {Promise}
+     */
+    async getCompositionList(projectGLJId, unitPriceFileId) {
+        let result = [];
+        try {
+            // 查找对应的项目工料机数据
+            let projectGLJData = await this.getDataById(projectGLJId);
+
+            let allowType = [GLJTypeConst.MIX_RATIO, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR,
+                GLJTypeConst.GENERAL_MACHINE];
+
+            if (projectGLJData.unit_price === null || allowType.indexOf(projectGLJData.unit_price.type) < 0) {
+                throw '找不到相关项目工料机';
+            }
+
+            // 查找对应的项目工料机数据
+            let [gljData, compositionList] = await this.getCompositionGLJList(projectGLJData.code, projectGLJData.project_id);
+
+            if (gljData.length <= 0) {
+                throw '没有对应的组成物项目工料机';
+            }
+
+            // 整理出code和name查找相关单价数据
+            let codeList = [];
+            let nameList = [];
+            let gljIdList = [];
+            for(let tmp of gljData) {
+                codeList.push(tmp.code);
+                nameList.push(tmp.name);
+                gljIdList.push(tmp.glj_id);
+            }
+
+            // 查找对应的单价数据
+            let unitPriceModel = new UnitPriceModel();
+            let condition = {code: {"$in": codeList}, name: {"$in": nameList}, unit_price_file_id: unitPriceFileId};
+            let unitPriceList = await unitPriceModel.findDataByCondition(condition, {_id: 0}, false);
+
+            // 查找对应的配合比数据
+            let mixRatioModel = new MixRatioModel();
+            condition = {glj_id: {"$in": gljIdList}, connect_code: projectGLJData.code, unit_price_file_id: unitPriceFileId};
+            let mixRatioData = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false, 'glj_id');
+
+            // 整理数据
+            let unitPriceData = {};
+            for(let tmp of unitPriceList) {
+                unitPriceData[tmp.code + tmp.name] = tmp;
+            }
+
+            this.combineData(gljData, unitPriceData, [], mixRatioData);
+
+            // 排序
+            gljData.sort(function (a, b) {
+                return parseInt(a.code) - parseInt(b.code);
+            });
+            result = gljData;
+        } catch (error) {
+            console.log(error);
+            result = [];
+        }
+
+        return result;
+    }
+
+    /**
+     * 获取混凝土等有组成物相关工料机对应的组成物项目工料机数据
+     *
+     * @param {String} code
+     * @param {Number} projectId
+     * @param {String} indexBy
+     * @return {Promise} 返回组成物工料机数据和组成物列表数据
+     */
+    async getCompositionGLJList(code, projectId, indexBy = null) {
+        // 首先获取对应组成物列表
+        let stdMixRatioModel = new STDMixRatioModel();
+        let compositionList = await stdMixRatioModel.getDataByCode(code);
+
+        if (compositionList.length <= 0) {
+            throw '不存在对应的组成物';
+        }
+
+        let codeList = [];
+        let nameList = [];
+        for(let tmp of compositionList) {
+            codeList.push(tmp.code);
+            nameList.push(tmp.name);
+        }
+
+        // 查找对应的项目工料机数据
+        let condition = {code: {"$in": codeList}, name: {"$in": nameList}, project_id: projectId};
+        let gljData = await this.findDataByCondition(condition, {_id: 0}, false, indexBy);
+
+        return [gljData, compositionList];
+    }
+
+    /**
+     * 根据条件获取对应项目工料机数据
+     *
+     * @param {Number} id
+     * @return {Promise}
+     */
+    async getDataById(id) {
+
+        // 查找对应的项目工料机数据
+        let projectGLJData = await this.findDataByCondition({id: id});
+        if (projectGLJData === null) {
+            throw '没有找到对应数据';
+        }
+
+        // 查找对应的单价数据
+        let unitPriceModel = new UnitPriceModel();
+        let unitPrice = await unitPriceModel.findDataByCondition({code: projectGLJData.code, name: projectGLJData.name});
+
+        projectGLJData.unit_price = unitPrice;
+
+        return projectGLJData;
+    }
+
 }
 
 export default GLJListModel;

+ 0 - 70
modules/glj/models/glj_repository_model.js

@@ -1,70 +0,0 @@
-/**
- * 工料机数据源模块
- *
- * @author CaiAoLin
- * @date 2017/6/23
- * @version
- */
-import BaseModel from "../../common/base/base_model";
-import GLJRepositorySchema from "./schemas/glj_repository";
-
-class GLJRepositoryModel extends BaseModel {
-
-    /**
-     * 构造函数
-     *
-     * @return {void}
-     */
-    constructor() {
-        let parent = super();
-        parent.model = GLJRepositorySchema;
-        parent.init();
-    }
-
-    /**
-     * 新增数据
-     *
-     * @param {object} data
-     * @return {Promise} boolean
-     */
-    async add(data) {
-        let result = false;
-        try {
-            result = await this.db.create(data);
-        } catch (error) {
-            if (error.name !== null && error.name === 'ValidationError') {
-                // 这里是数据验证失败
-                console.log('数据验证失败!');
-            }
-            result = false;
-        }
-
-        return result;
-    }
-
-    /**
-     * 根据条件获取对应工料机数据
-     *
-     * @param {Object} condition
-     * @return {Promise} Array
-     */
-    async getDataByCondition(condition) {
-        let gljData = [];
-        try {
-            if (Object.keys(condition).length <= 0) {
-                throw '筛选条件有误';
-            }
-
-            gljData = await this.db.find(condition);
-
-        } catch (error) {
-            console.log(error);
-            gljData = [];
-        }
-
-        return gljData;
-    }
-
-}
-
-export default GLJRepositoryModel;

+ 83 - 0
modules/glj/models/mix_ratio_model.js

@@ -0,0 +1,83 @@
+/**
+ * 配合比业务模型
+ *
+ * @author CaiAoLin
+ * @date 2017/7/12
+ * @version
+ */
+import BaseModel from "../../common/base/base_model";
+import {default as MixRatioSchema, collectionName as collectionName} from "./schemas/mix_ratio";
+import CounterModel from "./counter_model"
+
+class MixRatioModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = MixRatioSchema;
+        parent.init();
+    }
+
+    /**
+     * 设置场景
+     *
+     * @param {string} scene
+     * @return {void}
+     */
+    setScene(scene = '') {
+        switch (scene) {
+            // 新增数据的验证规则
+            case 'add':
+                this.model.schema.path('glj_id').required(true);
+                this.model.schema.path('consumption').required(true);
+                this.model.schema.path('unit_price_file_id').required(true);
+                this.model.schema.path('connect_code').required(true);
+                break;
+        }
+    }
+
+    /**
+     * 新增配合比数据(自动判断是否存在)
+     *
+     * @param {object} data
+     * @return {Promise}
+     */
+    async add(data) {
+        let counterModel = new CounterModel();
+        if (data instanceof Array) {
+            for(let tmp in data) {
+                data[tmp].id = await counterModel.getId(collectionName);
+            }
+        } else {
+            data.id = await counterModel.getId(collectionName);
+        }
+
+        this.setScene('add');
+        return this.db.model.create(data);
+    }
+
+    /**
+     * 更新数据
+     *
+     * @param {Number} id
+     * @param {Object} updateData
+     * @return {Promise}
+     */
+    async updateById(id, updateData) {
+        id = parseInt(id);
+        if (isNaN(id) || id <= 0 || Object.keys(updateData).length <= 0) {
+            return false;
+        }
+
+        let result = await this.db.update({id: id}, updateData);
+
+        return result.ok !== undefined && result.ok === 1;
+    }
+
+}
+
+export default MixRatioModel;

+ 20 - 5
modules/glj/models/schemas/glj.js

@@ -13,13 +13,19 @@ let modelSchema = {
     // 自增id
     id: Number,
     // 工料机总库ID
-    glj_repository_id: Number,
+    glj_id: Number,
     // 标段ID
     project_id: Number,
     // 编码
-    code: String,
+    code: {
+        type: String,
+        index: true
+    },
     // 名称
-    name: String,
+    name: {
+        type: String,
+        index: true
+    },
     // 是否暂估 (0为否 1为是)
     is_evaluate: {
         type: Number,
@@ -56,9 +62,18 @@ let modelSchema = {
         default: 1
     },
     // 显示调整基价
-    adjust_price: String,
+    adjust_price: Number,
     // 显示关联单价文件的字段
-    unit_price: Schema.Types.Mixed
+    unit_price: Schema.Types.Mixed,
+    // 显示关联的消耗量
+    quantity: Number,
+    // 显示组成物的消耗量
+    consumption: Number,
+    // 显示关联配合比的id
+    mix_ratio_id: Number,
+    // 显示关联父级工料机code(组合物用)
+    connect_code: String,
+    ratio_data: Schema.Types.Mixed
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false}));
 

+ 0 - 29
modules/glj/models/schemas/glj_repository.js

@@ -1,29 +0,0 @@
-/**
- * 数据结构
- *
- * @author CaiAoLin
- * @date 2017/6/28
- * @version
- */
-import mongoose from "mongoose";
-
-let Schema = mongoose.Schema;
-let collectionName = 'glj_repository';
-let modelSchema = {
-    // 自增id
-    id: Number,
-    // 定额id
-    ration_id: Number,
-    // 名称
-    name: String,
-    // 编号
-    code: String,
-    // 单位
-    unit: String,
-    // 定额单价
-    ration_price: String,
-    // 规格
-    standard: String
-};
-
-export default mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false}));

+ 35 - 0
modules/glj/models/schemas/mix_ratio.js

@@ -0,0 +1,35 @@
+/**
+ * 配合比数据结构
+ *
+ * @author CaiAoLin
+ * @date 2017/7/12
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'mix_ratio';
+let modelSchema = {
+    // 自增id
+    id: Number,
+    // 消耗量(有别于总消耗,此字段记录配合比的消耗量)
+    consumption: {
+        type: Number,
+        default: 0
+    },
+    // 工料机总库对应id (关联用)
+    glj_id: {
+        type: Number,
+        index: true
+    },
+    // 单价文件表id (因为选择单价文件后配合比数据也需要同步,所以记录单价文件id)
+    unit_price_file_id: Number,
+    // 关联项目工料机的code 不能关联id,因为单价文件导入别的项目后项目工料机id不同
+    connect_code: {
+        type: String,
+        index: true
+    },
+    glj_type: Number
+};
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 10 - 4
modules/glj/models/schemas/unit_price.js

@@ -13,13 +13,19 @@ let modelSchema = {
     // 自增ID
     id: Number,
     // 基价单价
-    base_price: String,
+    base_price: Number,
     // 市场单价
-    market_price: String,
+    market_price: Number,
     // 编码
-    code: String,
+    code: {
+        type: String,
+        index: true
+    },
     // 名称
-    name: String,
+    name: {
+        type: String,
+        index: true
+    },
     // 规格型号
     specs: {
         type: String,

+ 4 - 1
modules/glj/models/schemas/unit_price_file.js

@@ -13,7 +13,10 @@ let modelSchema = {
     // 自增id
     id: Number,
     // 标段id
-    project_id: Number,
+    project_id: {
+        type: Number,
+        index: true
+    },
     // 显示名称
     name: String,
 };

+ 43 - 30
modules/glj/models/unit_price_model.js

@@ -6,29 +6,13 @@
  * @version
  */
 import BaseModel from "../../common/base/base_model"
+import GLJTypeConst from "../../common/const/glj_type_const"
 import CounterModel from "./counter_model"
-import GLJListModel from "./glj_list_model";
 import {default as UnitPriceSchema, collectionName as collectionName} from "./schemas/unit_price";
 
 class UnitPriceModel extends BaseModel {
 
     /**
-     * 自动赋值的工料机类型集
-     * (主材、设备)
-     *
-     * @var {Array}
-     */
-    static autoChangeGLJType = [10, 11];
-
-    /**
-     * 触发计算混凝土、砂浆、配合比、机械的市场单价
-     * (人工、材料(普通材料))
-     *
-     * @var {Array}
-     */
-    static triggerCalculateGLJType = [2, 5];
-
-    /**
      * 构造函数
      *
      * @return {void}
@@ -103,6 +87,7 @@ class UnitPriceModel extends BaseModel {
         let unitPriceData = await this.findDataByCondition({code: data.code, unit_price_file_id: unitPriceFileId}, null, false);
 
         // 如果有记录,判断是否存在一样的市场单价,有则直接返回数据
+        data.market_price = parseFloat(data.market_price);
         let unitPriceIndex = this.isPriceIncluded(unitPriceData, data.market_price);
         if (unitPriceData && unitPriceIndex >= 0) {
             return [unitPriceData[unitPriceIndex], false];
@@ -118,7 +103,7 @@ class UnitPriceModel extends BaseModel {
 
         // 更改名称
         if (gljCount > 0) {
-            let regular = /\(\d\)/;
+            let regular = /\(\d+\)/;
             let changeString = '(' + gljCount + ')';
             data.name = regular.test(data.name) ? data.name.replace(regular, changeString) :
                 data.name + changeString;
@@ -146,8 +131,14 @@ class UnitPriceModel extends BaseModel {
      */
     async add(data) {
         let counterModel = new CounterModel();
-        let unitPriceId = await counterModel.getId(collectionName);
-        data.id = unitPriceId;
+        if (data instanceof Array) {
+            // 如果是批量新增
+            for(let tmp in data) {
+                data[tmp].id = await counterModel.getId(collectionName);
+            }
+        } else {
+            data.id = await counterModel.getId(collectionName);
+        }
 
         this.setScene('add');
         return this.db.model.create(data);
@@ -178,28 +169,50 @@ class UnitPriceModel extends BaseModel {
     /**
      * 更新市场单价
      *
-     * @param {Number} id
+     * @param {Object} condition
      * @param {Object} updateData
+     * @param {String} extend
      * @return {Promise}
      */
-    async updatePriceById(id, updateData) {
-        id = parseInt(id);
-        if (isNaN(id) || id <= 0 || Object.keys(updateData).length <= 0) {
+    async updatePrice(condition, updateData, extend = '') {
+        if (Object.keys(condition).length <= 0 || Object.keys(updateData).length <= 0) {
             return false;
         }
         // 首先查找相应的数据判断工料机类型
-        let unitPriceData = this.findDataByCondition({id: id});
+        let unitPriceData = await this.findDataByCondition(condition);
         if (!unitPriceData) {
             throw '找不到对应的单价数据';
         }
 
-        // 如果是主材、设备自动赋值基价单价=市场单价、调整基价=市场单价
-        if (UnitPriceModel.autoChangeGLJType.indexOf(unitPriceData.type) >= 0) {
-            updateData.base_price = updateData.market_price;
+        // 基价单价的计算
+        switch (unitPriceData.type) {
+            // 主材、设备自动赋值基价单价=市场单价
+            case GLJTypeConst.MAIN_MATERIAL:
+            case GLJTypeConst.EQUIPMENT:
+                updateData.base_price = updateData.market_price;
+                break;
         }
 
-        let result = await this.updateById(id, updateData);
-        return result;
+        // 额外更新数据
+        if (extend !== '') {
+            extend = JSON.parse(extend);
+            for (let code in extend) {
+                let extendUpdateData = {
+                    market_price: extend[code].market_price,
+                };
+                let tmpCondition = {
+                    unit_price_file_id: unitPriceData.unit_price_file_id,
+                    code: code
+                };
+                let extendResult = await this.db.update(tmpCondition, extendUpdateData);
+                if (!extendResult) {
+                    throw '更新额外数据,编码为' + code + '的数据失败!';
+                }
+            }
+        }
+
+        let result = await this.db.update(condition, updateData);
+        return result.ok !== undefined && result.ok === 1;
     }
 
 }

+ 6 - 2
modules/glj/routes/glj_router.js

@@ -1,5 +1,5 @@
 /**
- * 登录相关路由
+ * 项目工料机相关路由
  *
  * @author CaiAoLin
  * @date 2017/6/22
@@ -8,15 +8,19 @@
 import Express from "express";
 import GLJController from "../controllers/glj_controller";
 
+module.exports = function (app) {
 const router = Express.Router();
 let gljController = new GLJController();
 
 // action定义区域
 router.get('/', gljController.init, gljController.index);
 router.post('/update', gljController.init, gljController.updateData);
+router.post('/get-ratio', gljController.init, gljController.getRatio);
+router.post('/delete-ratio', gljController.init, gljController.deleteMixRatio);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);
 router.get('/testDelete', gljController.init, gljController.delete);
 
-module.exports = router;
+app.use('/glj',router);
+};

+ 1 - 0
modules/main/models/bills.js

@@ -24,6 +24,7 @@ let billsSchema = new Schema({
     name: String,
     unit: String,
     quantity: String, // Decimal
+    isFromDetail:{type: Number,default:0},//1 true 2 false
     programID: Number,
     comments: String,
     // 调价

+ 2 - 1
modules/main/models/proj_counter.js

@@ -11,7 +11,8 @@ class projCounter extends baseModel {
         let projCounterSchema = new Schema({
             projectID: Number,
             bills: Number,
-            ration: Number
+            ration: Number,
+            volume_price: Number
         });
         let projCounterModel = db.model(name, projCounterSchema);
         super(projCounterModel);

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

@@ -7,7 +7,9 @@ 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');
+var quantity_detail_data = require('../../ration_glj/facade/quantity_detail_facade');
 let projCounter = require('./proj_counter');
+let volumePriceData = require('../../volume_price/models/volume_price_model');
 var consts = require('./project_consts');
 var projectConsts = consts.projectConst;
 var async = require("async");
@@ -20,7 +22,9 @@ 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.QUANTITY_DETAIL] = quantity_detail_data;
 moduleMap[projCounter.collectionName] = projCounter;
+moduleMap[volumePriceData.collectionName] = volumePriceData;
 
 
 var Project = function (){};
@@ -56,6 +60,7 @@ Project.prototype.save = function(datas, callback){
 
         }
     }
+
     for (var i = 0; i < me.datas.length; i++){
         item = me.datas[i];
         functions.push(saveModule(item));

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

@@ -8,11 +8,12 @@ var projectConst = {
     RATION_GLJ:'ration_glj',
     RATION_COE:'ration_coe',
     RATION_ASS:'ration_ass',
+    QUANTITY_DETAIL:'quantity_detail',
     PROJECTGLJ: 'projectGLJ',
     GLJLIST: 'GLJList',
     UNITPRICEFILE: 'unitPriceFile',
-    PROPERTIES: 'properties'
-
+    PROPERTIES: 'properties',
+    VOLUMEPRICE: 'volume_price'
 };
 
 var commonConst = {

+ 2 - 0
modules/main/models/ration.js

@@ -34,8 +34,10 @@ let rationSchema = new Schema({
     code: String,
     name: String,
     maskName: String,
+    caption: String,
     unit: String,
     quantity: String, // Decimal
+    isFromDetail:{type: Number,default:0},  //1 true 2 false
     programID: Number,
     adjustState: String,
     content: String,

+ 10 - 7
modules/main/routes/GLJ_route.js

@@ -1,12 +1,15 @@
 /**
  * Created by jimiz on 2017/4/17.
  */
-var express = require('express');
-var GLJRouter = express.Router();
-var GLJController = require('../controllers/GLJ_controller');
+let express = require('express');
 
-GLJRouter.post('/getData', GLJController.getData);
-GLJRouter.post('/getItemTemplate', GLJController.getItemTemplate);
-GLJRouter.post('/allocIDs', GLJController.allocIDs);
+module.exports = function (app) {
+    let GLJRouter = express.Router();
+    let GLJController = require('../controllers/GLJ_controller');
 
-module.exports = GLJRouter;
+    GLJRouter.post('/getData', GLJController.getData);
+    GLJRouter.post('/getItemTemplate', GLJController.getItemTemplate);
+    GLJRouter.post('/allocIDs', GLJController.allocIDs);
+
+    app.use('/glj_bak', GLJRouter);
+};

+ 11 - 7
modules/main/routes/bills_route.js

@@ -1,12 +1,16 @@
 /**
  * Created by jimiz on 2017/4/7.
  */
-var express = require('express');
-var billsRouter = express.Router();
-var billsController = require('../controllers/bills_controller');
+let express = require('express');
+let billsController = require('../controllers/bills_controller');
+
+module.exports = function (app) {
+    let billsRouter = express.Router();
+    billsRouter.post('/getData', billsController.getData);
+    billsRouter.post('/getItemTemplate', billsController.getItemTemplate);
+    billsRouter.post('/allocIDs', billsController.allocIDs);
+    app.use('/bills', billsRouter);
+};
+
 
-billsRouter.post('/getData', billsController.getData);
-billsRouter.post('/getItemTemplate', billsController.getItemTemplate);
-billsRouter.post('/allocIDs', billsController.allocIDs);
 
-module.exports = billsRouter;

+ 22 - 0
modules/main/routes/main_route.js

@@ -0,0 +1,22 @@
+/**
+ * Created by chen on 2017/7/17.
+ */
+
+
+
+module.exports =function (app) {
+    app.get('/main',  function(req, res) {
+        let pm = require('../../pm/controllers/pm_controller');
+        pm.checkProjectRight(req.session.sessionUser.ssoId, req.query.project, function (hasRight) {
+            if (hasRight) {
+                res.render('building_saas/main/html/main.html',
+                    {
+                        userAccount: req.session.userAccount,
+                        userID: req.session.sessionUser.ssoId
+                    });
+            } else {
+                res.redirect('/pm');
+            }
+        });
+    });
+}

+ 10 - 6
modules/main/routes/project_route.js

@@ -1,11 +1,15 @@
 /**
  * Created by jimiz on 2017/4/16.
  */
-var express = require('express');
-var projectRouter = express.Router();
-var projectController = require('../controllers/project_controller');
+let express = require('express');
+let projectController = require('../controllers/project_controller');
 
-projectRouter.post('/save', projectController.save);
-projectRouter.post('/getData', projectController.getData);
+module.exports = function (app) {
+    let projectRouter = express.Router();
+
+    projectRouter.post('/save', projectController.save);
+    projectRouter.post('/getData', projectController.getData);
+
+    app.use('/project',projectRouter);
+};
 
-module.exports = projectRouter;

+ 10 - 7
modules/main/routes/ration_route.js

@@ -1,12 +1,15 @@
 /**
  * Created by jimiz on 2017/4/7.
  */
-var express = require('express');
-var rationRouter = express.Router();
-var rationController = require('../controllers/ration_controller');
+let express = require('express');
 
-rationRouter.post('/getData', rationController.getData);
-rationRouter.post('/getItemTemplate', rationController.getItemTemplate);
-rationRouter.post('/allocIDs', rationController.allocIDs);
+module.exports = function (app) {
+    let rationRouter = express.Router();
+    let rationController = require('../controllers/ration_controller');
 
-module.exports = rationRouter;
+    rationRouter.post('/getData', rationController.getData);
+    rationRouter.post('/getItemTemplate', rationController.getItemTemplate);
+    rationRouter.post('/allocIDs', rationController.allocIDs);
+
+    app.use('/ration', rationRouter);
+};

+ 2 - 0
modules/pm/controllers/copy_proj_controller.js

@@ -5,6 +5,7 @@
 let billsData = require('../../main/models/bills');
 let rationData = require('../../main/models/ration');
 let projCounter = require('../../main/models/proj_counter');
+let volumePriceData = require('../../volume_price/models/volume_price_model');
 let async = require('async');
 
 module.exports = {
@@ -29,6 +30,7 @@ module.exports = {
         fun.push(copyData(billsData));
         fun.push(copyData(rationData));
         fun.push(copyData(projCounter));
+        fun.push(copyData(volumePriceData));
         async.parallel(fun, (err) => callback(err));
     }
 };

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

@@ -20,7 +20,7 @@ let ProjectSchema = new Schema({
     'fullFolder': Array
 });
 
-module.exports = mongoose.model(collectionName, ProjectSchema)
+module.exports = mongoose.model(collectionName, ProjectSchema);
 
 
 

+ 39 - 27
modules/pm/routes/pm_route.js

@@ -4,36 +4,48 @@
  */
 
 let express = require('express');
-let pmRouter = express.Router();
 let pmController = require('./../controllers/pm_controller');
 
-pmRouter.use(function (req, res, next) {
-    if (/\/getNewProjectID/.test(req.originalUrl) || pmController.checkRight(req, res)) {
-        next();
-    } else {
-        res.json({error: 1, message: '对不起,您无权限操作。', data: null});
-    }
-});
+module.exports = function (app) {
 
-/*
-  req.body = {data: '{user_id}'}
- */
-pmRouter.post('/getProjects', pmController.getProjects);
+    app.get('/pm', function(req, res){
+        res.render('building_saas/pm/html/project-management.html',
+            {userAccount: req.session.userAccount,
+                userID: req.session.sessionUser.ssoId});
+    });
 
-/*
- req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
- data.updateData.updateType: 1 of ['new', 'update', 'delete']
- */
-pmRouter.post('/updateProjects', pmController.updateProjects);
+    let pmRouter = express.Router();
+
+    pmRouter.use(function (req, res, next) {
+        if (/\/getNewProjectID/.test(req.originalUrl) || pmController.checkRight(req, res)) {
+            next();
+        } else {
+            res.json({error: 1, message: '对不起,您无权限操作。', data: null});
+        }
+    });
+
+    /*
+     req.body = {data: '{user_id}'}
+     */
+    pmRouter.post('/getProjects', pmController.getProjects);
+
+    /*
+     req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
+     data.updateData.updateType: 1 of ['new', 'update', 'delete']
+     */
+    pmRouter.post('/updateProjects', pmController.updateProjects);
+
+    /*
+     req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
+     data.updateData.updateType: 1 of ['update', 'copy']
+     */
+    pmRouter.post('/copyProjects', pmController.copyProjects);
+    pmRouter.post('/renameProject', pmController.rename);
+    pmRouter.post('/beforeOpenProject', pmController.beforeOpenProject);
+    pmRouter.post('/getProject', pmController.getProject);
+    pmRouter.post('/getNewProjectID', pmController.getNewProjectID);
+
+    app.use('/pm/api', pmRouter);
+};
 
-/*
- req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
- data.updateData.updateType: 1 of ['update', 'copy']
- */
-pmRouter.post('/copyProjects', pmController.copyProjects);
-pmRouter.post('/renameProject', pmController.rename);
-pmRouter.post('/beforeOpenProject', pmController.beforeOpenProject);
-pmRouter.post('/getProject', pmController.getProject);
-pmRouter.post('/getNewProjectID', pmController.getNewProjectID);
 
-module.exports = pmRouter;

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

@@ -9,9 +9,12 @@ let ration_glj = mongoose.model('ration_glj');
 let ration = mongoose.model('ration');
 let ration_coe = mongoose.model('ration_coe');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
+let glj_type_util = require('../../../public/cache/std_glj_type_util');
+
 
 module.exports={
-    calculateQuantity:calculateQuantity
+    calculateQuantity:calculateQuantity,
+    getGLJTypeByID:getGLJTypeByID
 }
 //辅助定额调整、替换工料机、标准附注条件调整、添加工料机、自定义消耗量(包括删除工料机)、自定义乘系数、市场单价调整
 let stateSeq ={
@@ -31,7 +34,7 @@ async function calculateQuantity(query,isMarkPriceAjust){
              glj_result:[],
              rationID:query.rationID
          };
-         let impactRation = await ration.findOne({projectID:query.projectID,ID:query.rationID});
+         let impactRation = await ration.findOne({projectID:query.projectID,ID:query.rationID,deleteInfo:null});
          let gljList = await ration_glj.find(query)//{projectID:query.projectID,rationID:query.rationID}
          let coeList = await ration_coe.find({projectID:query.projectID,rationID:query.rationID}).sort('seq').exec();
          let assList=[];
@@ -51,14 +54,14 @@ async function calculateQuantity(query,isMarkPriceAjust){
              let r = await calculateQuantityPerGLJ(gljList[i],impactRation,coeList,assList,adjustState,isMarkPriceAjust);
              result.glj_result.push(r);
          }
-         console.log(result.glj_result);
+
         if(isMarkPriceAjust==null){
             await ration_glj.bulkWrite(generateUpdateTasks(result.glj_result));
         }
          adjustState= _.sortByOrder(adjustState, ['index'], ['asc']);
          adjustState=_.map(adjustState, _.property('content'));
          let adjustStateString = adjustState.join(';');
-         await ration.update({projectID:query.projectID,ID:query.rationID},{adjustState:adjustStateString});
+         await ration.update({projectID:query.projectID,ID:query.rationID,deleteInfo: null},{adjustState:adjustStateString});
          result.adjustState=adjustStateString;
          return result;
     }catch (err){
@@ -90,8 +93,7 @@ async function calculateQuantityPerGLJ(glj,ration,coeList,assList,adjustState,is
             projectID:glj.projectID
         },
         doc:{
-            quantity: quantity,
-            customQuantity:glj.customQuantity
+            quantity: quantity
         }
     };
     try {
@@ -102,12 +104,13 @@ async function calculateQuantityPerGLJ(glj,ration,coeList,assList,adjustState,is
                 quantity = calculateQuantityByCoes(quantity,coeList,glj);
             }else {
                 quantity = glj.customQuantity;
+                result.doc.customQuantity = glj.customQuantity;
             }
             let customerCoe = _.last(coeList);
             if(customerCoe.isAdjust==1){
                 quantity = calculateQuantityByCustomerCoes(quantity,customerCoe,glj);
             }
-            result.doc.quantity =quantity;
+            result.doc.quantity =_.round(quantity,3);
         }
         generateAdjustState(glj,coeList,adjustState);
         return result;
@@ -152,7 +155,7 @@ function generateAdjustState(glj,coeList,adjustState) {
         }
     }
     //市场单价调整
-    if(glj._doc.hasOwnProperty('marketPriceAdjust')&&glj.marketPriceAdjust!=0){
+    if(glj._doc.hasOwnProperty('marketPriceAdjust')&&glj.marketPriceAdjust&&glj.marketPriceAdjust!=0){
         //0101005价66.00
         adjustState.push({index:stateSeq.adjMak,content:glj.code+'价'+glj.marketPriceAdjust});
     }
@@ -163,12 +166,19 @@ function generateAdjustState(glj,coeList,adjustState) {
 
 function calculateTimes(ass){
     let times =(ass.actualValue-ass.stdValue)/ass.stepValue;
-
+    let r = false;
+    if(times<0){
+        r=true;
+        times=times*-1;
+    }
     if(ass.carryBit=='四舍五入'){
         times = _.round(times,ass.decimal);
     }else if (ass.carryBit=='进一'){
         times =_.ceil(times,ass.decimal);
     }
+    if(r){
+        times=times*-1;
+    }
     return times;
 }
 
@@ -190,7 +200,7 @@ function everyCoe(quantity,coe,glj) {
                 coeQuantity = getCalculateResult(coeQuantity,coe.coes[i]);
             } else if(coe.coes[i].coeType=='定额'){
                 coeQuantity = getCalculateResult(coeQuantity,coe.coes[i]);
-            }else if(coe.coes[i].coeType==glj.gljDistType){
+            }else if(coe.coes[i].coeType==getGLJTypeByID(glj.type)){
                 coeQuantity = getCalculateResult(coeQuantity,coe.coes[i]);
             }
         }
@@ -205,7 +215,7 @@ function calculateQuantityByCustomerCoes(quantify,coe,glj) {
         return getCalculateResult(quantify, coe.coes[0])
     }else {
         for(let i=1;i<coe.coes.length;i++){
-            if(coe.coes[i].coeType.search(glj.gljDistType)!=-1){
+            if(coe.coes[i].coeType.search(getGLJTypeByID(glj.type))!=-1){
                 return getCalculateResult(quantify,coe.coes[i])
             }
         }
@@ -233,5 +243,15 @@ function getCalculateResult(quantify,c) {
             break;
     }
     return q;
+}
 
+function getGLJTypeByID(id) {
+    let glj_type_object = glj_type_util.getStdGljTypeCacheObj();
+    let topTypeId = glj_type_object.getTopParentIdByItemId(id);
+    let type = glj_type_object.getItemById(topTypeId);
+    if(type!=undefined){
+        return type.fullName;
+    }else {
+        return '';
+    }
 }

+ 633 - 0
modules/ration_glj/facade/quantity_detail_facade.js

@@ -0,0 +1,633 @@
+/**
+ * Created by chen on 2017/7/20.
+ */
+
+/**
+ * Created by chen on 2017/7/10.
+ */
+let mongoose = require('mongoose');
+let consts = require('../../main/models/project_consts');
+let commonConsts = consts.commonConst;
+let _=require("lodash");
+let async_n = require("async");
+let quantity_detail_model = mongoose.model('quantity_detail');
+const uuidV1 = require('uuid/v1');
+let ration_model = mongoose.model('ration');
+let bill_model=mongoose.model("bills");
+
+
+module.exports={
+    save:save,
+    getData:getData,
+    deleteByRation:deleteByRation,
+    deleteByBill:deleteByBill
+};
+
+let operationMap={
+    'ut_create':create_quantity_detail,
+    'ut_update':update_quantity_detail,
+    'ut_delete':delete_quantity_detail
+};
+
+let updateFunctionMap = {
+    'normalUpdate':normalUpdate,
+    'updateQuantityRegex':updateQuantityRegex,
+    'insertRecode':insertRecode
+}
+
+function create_quantity_detail(user_id,datas) {
+    return function (callback) {
+        let doc = datas.doc;
+        doc.ID = uuidV1();
+        if(doc.hasOwnProperty('regex')){
+            insertRecodeWithReg(doc).then(function (resultObject) {
+                if(resultObject.err){
+                    callback(null,{
+                        moduleName:consts.projectConst.QUANTITY_DETAIL,
+                        err:{
+                            message:result.err.message
+                        }
+                    });
+                } else {
+                    callback(null,resultObject.return_list);
+                }
+            })
+        }else {
+            createNormalRecode(doc,callback)
+        }
+
+    }
+}
+
+function insertRecode(user_id,datas) {
+    return function (callback) {
+        let doc = datas.doc;
+        doc.ID = uuidV1();
+        doInsertRecode(doc).then(function (result) {
+            //console.log(result);
+            if(result.err){
+                callback(result.err,'')
+            }else {
+                callback(null,result.returndata)
+            }
+
+        })
+    }
+}
+
+async function doInsertRecode(doc) {
+    let result={
+        err:null
+    }
+    try{
+        let query = {
+            projectID:doc.projectID,
+            seq : { $gte: doc.seq }
+        }
+        if(doc.hasOwnProperty('rationID')){
+            query.rationID = doc.rationID;
+        }else {
+            query.billID = doc.billID;
+        }
+        let quantity_detail_List = await getDatailList(doc,{data:{}});
+        let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,1);
+        await quantity_detail_model.update(query,{$inc:{seq:1}},{multi: true});
+        if(update_task.length>0){
+            await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
+        }
+        let newrecode = await quantity_detail_model.create(doc);
+        let returndata ={
+            updateTpye:commonConsts.UT_CREATE,
+            moduleName:consts.projectConst.QUANTITY_DETAIL,
+            data:{
+                doc:newrecode,
+                resort:true,
+                update_task:update_task
+            }
+        };
+        result.returndata =returndata
+        return result;
+    }catch (error){
+        console.log(error)
+        result.err;
+        return result
+    }
+}
+
+function getUpdateReferenceTask(quantity_detail_List,seq,re) {
+    let update_task=[];
+    for(let q of quantity_detail_List){
+        let need_update =false;
+        let newReg = q.regex;
+        let newReferenceIndex;
+        if(q.referenceIndexs.length>0){
+            for (let i =0;i< q.referenceIndexs.length;i++){
+                if(q.referenceIndexs[i]>seq){
+                    newReg = replaceAll('C'+q.referenceIndexs[i],'C'+(q.referenceIndexs[i]+re),newReg);
+                    newReg = replaceAll('c'+q.referenceIndexs[i],'c'+(q.referenceIndexs[i]+re),newReg);
+                    q.referenceIndexs[i] +=re;
+                    need_update = true;
+                }
+            }
+        }
+        if(need_update){
+            newReferenceIndex = q.referenceIndexs;
+            let task ={
+                query:{
+                    ID:q.ID,
+                    projectID:q.projectID
+                },
+                doc:{
+                    regex:newReg,
+                    referenceIndexs:newReferenceIndex
+                }
+            }
+            update_task.push(task);
+        }
+    }
+    return update_task;
+}
+
+
+function createNormalRecode(doc,callback) {
+    quantity_detail_model.create(doc,(err,result)=>{
+        if(err){
+            callback(err,null);
+        }else {
+            console.log(result);
+            let returndata ={
+                updateTpye:commonConsts.UT_CREATE,
+                moduleName:consts.projectConst.QUANTITY_DETAIL,
+                data:result
+            }
+            callback(null,returndata)
+        }
+    });
+}
+
+
+async function insertRecodeWithReg (doc) {
+    let returnObjec={
+        err:null,
+        return_list:[]
+    }
+    try {
+        let returnData={
+            moduleName:'',
+            data:{
+                updateTpye:commonConsts.UT_UPDATE,
+            }
+        }
+        let regex = doc.regex.toUpperCase();
+        let referenceIndexs = doc.referenceIndexs;
+        let detailList = await getDatailList(doc,returnData);
+        doc.result =getEvalResult(referenceIndexs,detailList,regex);
+        let refreshQuantity =false;
+        if(doc.refreshQuantity=true){
+            refreshQuantity = true;
+        }
+        delete doc.refreshQuantity;
+        let newRecode = await quantity_detail_model.create(doc) ;
+        detailList.push(newRecode);
+        if(refreshQuantity==true){
+            returnData.data.quantity = await summateResuts(doc,detailList);
+            returnData.data.quantityRefresh = true;
+            returnObjec.return_list.push(returnData);
+        }
+        returnObjec.return_list.push({
+            updateTpye:commonConsts.UT_CREATE,
+            moduleName:consts.projectConst.QUANTITY_DETAIL,
+            data:newRecode
+        });
+        return returnObjec;
+    }catch (error){
+        returnObjec.err = new Error('输入的表达式有误,请重新输入!');
+        console.log(error)
+        return returnObjec;
+    }
+
+
+}
+
+
+
+function normalUpdate(user_id,datas) {
+    return function(callback) {
+        if(datas.doc.hasOwnProperty('isSummation')){
+            doIsSummationUpdate(datas.query,datas.doc).then(function (sresult) {
+                let returndata ={
+                    moduleName:consts.projectConst.QUANTITY_DETAIL,
+                    data:{
+                        updateTpye:commonConsts.UT_UPDATE,
+                        query:datas.query,
+                        doc:datas.doc
+                    }
+                }
+                let retrunArr = [];
+                retrunArr.push(returndata);
+                if(sresult){
+                    retrunArr.push(sresult);
+                }
+                callback(null,retrunArr);
+            })
+        }else {
+            updateRecored(datas.query,datas.doc,callback);
+        }
+    }
+}
+function updateQuantityRegex(user_id,datas) {
+    return function(callback){
+        console.log(datas);
+        doRegexUpdate(datas).then(function (result) {
+            if(result.err){
+                callback(null,{
+                    moduleName:consts.projectConst.QUANTITY_DETAIL,
+                    err:{
+                        message:result.err.message
+                    }
+                });
+            }else {
+                callback(null,result.rList);
+            }
+
+        })
+    }
+}
+
+async function doIsSummationUpdate(query,doc) {
+    try {
+        let returnData={
+            moduleName:'',
+            data:{
+                updateTpye:commonConsts.UT_UPDATE,
+            }
+        }
+        let  refreshQuantity=false;
+        if(query.refreshQuantity==true){
+            refreshQuantity=true;
+        }
+        delete query.refreshQuantity;
+        let updateDoc = await quantity_detail_model.update(query,doc);
+        let detailList = await getDatailList(query,returnData);
+        if(refreshQuantity==true){
+            let quantity = await summateResuts(query,detailList);
+            returnData.data.quantity = quantity;
+            returnData.data.quantityRefresh = true;
+            return returnData;
+        }else {
+            return null;
+        }
+    }catch (error){
+        console.log(error)
+    }
+}
+
+async function getDatailList(query,resultObject) {
+    let detailList = [];
+    if(query.hasOwnProperty('rationID')){
+        detailList = await quantity_detail_model.find({'projectID':query.projectID,'rationID':query.rationID}).sort('seq').exec();
+        resultObject.moduleName = consts.projectConst.RATION;
+        resultObject.data.rationID=query.rationID;
+
+    }else {
+        detailList = await quantity_detail_model.find({'projectID':query.projectID,'billID':query.billID}).sort('seq').exec();
+        resultObject.moduleName = consts.projectConst.BILLS;
+        resultObject.data.billID=query.billID;
+    }
+    return detailList;
+}
+
+
+async function doRegexUpdate(datas) {
+    let resultObjec ={
+        err:null,
+        rList:[]
+    }
+    try {
+        let detailList = [];
+        let quantityResult ={
+            moduleName:'',
+            data:{
+                updateTpye:commonConsts.UT_UPDATE,
+                quantityRefresh:true
+            }
+        }
+        if(datas.query.hasOwnProperty('rationID')){
+            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'rationID':datas.query.rationID}).sort('seq').exec();
+            quantityResult.moduleName = consts.projectConst.RATION;
+            quantityResult.data.rationID = datas.query.rationID;
+        }else {
+            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'billID':datas.query.billID}).sort('seq').exec();
+            quantityResult.data.billID = datas.query.billID;
+            quantityResult.moduleName = consts.projectConst.BILLS;
+        }
+        let regex;
+        let result;
+        if(datas.doc.regex==null){
+            result=0
+            datas.doc.referenceIndexs=[];
+        }else {
+            regex = datas.doc.regex.toUpperCase();
+            let referenceIndexs = datas.doc.referenceIndexs;
+            result =getEvalResult(referenceIndexs,detailList,regex);
+        }
+        detailList[datas.query.index].result =result;
+        detailList[datas.query.index].regex=datas.doc.regex;
+        detailList[datas.query.index].referenceIndexs =datas.doc.referenceIndexs;
+        let updateTasks =[];
+        datas.doc.result=result;
+        updateTasks.push({
+            query:{
+                ID:datas.query.ID,
+                projectID:datas.query.projectID
+            },
+            doc:datas.doc
+        })
+        updateReferenceRecode(datas.query.index+1,detailList,updateTasks);
+        let updateEdit = await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
+        resultObjec.rList.push(gernerateResultList(updateTasks));
+        if(datas.query.refreshQuantity==true){
+            quantityResult.data.quantity = await summateResuts(datas.query,detailList);
+            resultObjec.rList.push(quantityResult);
+        }
+        return resultObjec;
+    }catch (error){
+        console.log(error);
+        resultObjec.err=error;
+        return resultObjec
+
+    }
+}
+
+function gernerateResultList(updateTasks) {
+    let returndata ={
+        moduleName:consts.projectConst.QUANTITY_DETAIL,
+        data:{
+            updateTpye:commonConsts.UT_UPDATE,
+            refreshList:updateTasks
+        }
+    }
+    return returndata;
+
+}
+
+
+
+function updateReferenceRecode(index,detailList,updateTasks) {
+    for(let d of detailList){
+        if(_.includes(d.referenceIndexs,index)){
+            let tResult = getEvalResult(d.referenceIndexs,detailList,d.regex);
+            let t = {
+                query:{
+                    ID:d.ID,
+                    projectID:d.projectID
+                },
+                doc:{
+                    result:tResult
+                }
+            };
+            d.result = tResult;
+            updateTasks.push(t);
+            updateReferenceRecode(d.seq+1,detailList,updateTasks);
+        }
+    }
+}
+
+
+async function summateResuts (query,detailList) {
+    let quantity = 0;
+    for(let d of detailList){
+        if(d.isSummation==1){
+            let result = d.result==null||d.result==undefined?0:d.result
+            quantity+=result;
+        }
+    }
+    quantity = quantity.toFixed(4);
+    if(query.hasOwnProperty('rationID')){
+        await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
+    }else {
+        await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
+    }
+    return quantity
+}
+
+
+
+function  getEvalResult(referenceIndexs,detailList,regex) {
+    try {
+        for(let i of referenceIndexs){
+            regex = replaceReference(i,detailList,regex)
+        }
+        console.log('replace all C reference -----'+regex);
+        regex =replaceSqr(regex);
+        console.log('replace all sqar reference -----'+regex);
+        return _.round(eval(regex), 4);
+    }catch (error){
+        throw new Error('输入的表达式有误,请重新输入!');
+    }
+
+}
+
+function  generateUpdateTaks(updateTasks) {
+    var tasks=[];
+    for(let u of updateTasks){
+        let t ={
+            updateOne:{
+                filter:u.query,
+                update: u.doc
+            }
+        }
+        tasks.push(t);
+    }
+    return tasks;
+}
+
+function replaceReference(index,detailList,str) {
+    str=str.toUpperCase();
+    let rstr= detailList[index-1].regex==null?'0':'('+detailList[index-1].regex+')';
+    str=replaceAll('C'+index,rstr,str);
+    if(detailList[index-1].referenceIndexs.length>0){
+        for (let i of detailList[index-1].referenceIndexs){
+            str =replaceReference(i,detailList,str);
+        }
+    }
+    return str;
+}
+
+
+function replaceAll (FindText, RepText,str) {
+    let regExp = new RegExp(FindText, "g");
+    return str.replace(regExp, RepText);
+}
+
+
+function replaceSqr(text) {
+    var squarRegex = /\([^\^]+\)\^\d+/g;
+    var sqararr = text.match(squarRegex);
+
+    var squarRegex2 = /C[0-9]+\^\d+|[0-9]+([.]{1}[0-9]+){0,1}\^\d+/g; //匹配没有括号的
+    var sqararr2=text.match(squarRegex2);
+    if(sqararr){
+        text=converSqrByArr(sqararr,text);
+    }
+    if(sqararr2){
+        text=converSqrByArr(sqararr2,text);
+    }
+    return text;
+}
+
+function converSqrByArr (sqararr,text) {
+    var temp = text;
+    sqararr.forEach(function (item) {
+        var arr = item.split('\^');
+        var y = parseInt(arr[1]);
+        var x_arr = [];
+        for (var i = 0; i < y; i++) {
+            x_arr.push(arr[0]);
+        }
+        var temStr = x_arr.join('*');
+        temp = temp.replace(item, temStr);
+    });
+    return temp;
+};
+
+
+function updateRecored(query,doc,callback) {
+    quantity_detail_model.update(query,doc,(err,result)=>{
+        if(err){
+            callback(err,'');
+        }else {
+            let returndata ={
+                moduleName:consts.projectConst.QUANTITY_DETAIL,
+                data:{
+                    updateTpye:commonConsts.UT_UPDATE,
+                    query:query,
+                    doc:doc
+                }
+            }
+            callback(null,returndata);
+        }
+    })
+}
+
+
+function update_quantity_detail(user_id,datas) {
+    if(datas.updateFunction){
+        return updateFunctionMap[datas.updateFunction](user_id,datas);
+    }else {
+        console.log(datas);
+        return normalUpdate(user_id,datas);
+    }
+}
+
+function delete_quantity_detail(user_id,datas) {
+    return function (callback) {
+        doQuantityDelete(datas.doc).then(function (result) {
+            console.log(result);
+            if(result.err){
+                callback(result.err,'')
+            }else {
+                callback(null,result.returndata)
+            }
+        });
+    }
+}
+
+async function doQuantityDelete(doc) {
+   console.log(doc) ;
+    let result={
+        err:null
+    }
+    try{
+        let query = {
+            projectID:doc.projectID,
+            seq : { $gt: doc.seq }
+        }
+        if(doc.hasOwnProperty('rationID')){
+            query.rationID = doc.rationID;
+        }else {
+            query.billID = doc.billID;
+        }
+        let quantity_detail_List = await getDatailList(doc,{data:{}});
+        let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,-1);
+        await quantity_detail_model.update(query,{$inc:{seq:-1}},{multi: true});
+        if(update_task.length>0){
+            await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
+        }
+        await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
+        let returndata ={
+            moduleName:consts.projectConst.QUANTITY_DETAIL,
+            data:{
+                doc:doc,
+                resort:true,
+                update_task:update_task,
+                updateTpye:commonConsts.UT_DELETE
+            }
+        };
+        result.returndata =returndata
+        return result;
+    }catch (error){
+        console.log(error)
+        result.err;
+        return result
+    }
+
+
+}
+
+
+function getData(projectID, callback) {
+    quantity_detail_model.find({'projectID':projectID}).sort('seq').exec((err,datas)=>{
+        if(err){
+            callback(1, '', null);
+        }else {
+            callback(0, consts.projectConst.QUANTITY_DETAIL, datas);
+        }
+    })
+}
+
+function save (user_id, datas, callback) {
+    let operations=[];
+    if(_.isArray(datas)){
+        for(let i=0;i<datas.length;i++){
+            operations.push(operationMap[datas[i].updateType](user_id,datas[i]));
+        }
+    }else {
+        operations.push(operationMap[datas.updateType](user_id,datas));
+    }
+    async_n.parallel(operations,function (err,results) {
+        if(err){
+            callback(err,'');
+        }else {
+            if(results.length==1){
+                callback(null,results[0])
+            }else {
+                callback(null,results)
+            }
+        }
+    })
+}
+
+function deleteByRation(data) {
+    return function (callback) {
+        quantity_detail_model.deleteMany({projectID: data.projectID, rationID: data.ID},(err,result)=>{
+            commonCallback(callback,result,err);
+        });
+    }
+}
+function deleteByBill(data) {
+    return function (callback) {
+        console.log({projectID: data.projectID, billID: data.ID});
+        quantity_detail_model.deleteMany({projectID: data.projectID, billID: data.ID},(err,result)=>{
+            commonCallback(callback,result,err);
+        });
+    }
+}
+
+function commonCallback(callback,result,err) {
+    if(err){
+        callback(err,'');
+    }else {
+        callback(null,result);
+    }
+}

+ 3 - 3
modules/ration_glj/facade/ration_ass_facade.js

@@ -30,7 +30,6 @@ function update_ration_ass(user_id,datas) {
     return function (callback) {
         let editAss = datas.query.editAss;
         doRationAssAdjust(datas.query.ration,datas.doc).then(function (result) {
-            console.log(result);
             if(result.err){
                 callback(result.err,'')
             }else {
@@ -58,7 +57,8 @@ function update_ration_ass(user_id,datas) {
                         updateTpye:commonConsts.UT_UPDATE,
                         stateRefresh:true,
                         rationID:result.cal_result.rationID,
-                        adjustState:result.cal_result.adjustState
+                        adjustState:result.cal_result.adjustState,
+                        name:datas.doc.name
                     }
                 };
                 callback(null,[newObject,ration_glj_data,ration_data]);
@@ -75,7 +75,7 @@ async function doRationAssAdjust(ration,doc) {
         cal_result:[]
     }
     try{
-        await rationModel.update({projectID:ration.projectID,ID:ration.ID},doc);
+        await rationModel.update({projectID:ration.projectID,ID:ration.ID,deleteInfo: null},doc);
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:ration.projectID,rationID:ration.ID});
         result.cal_result=cal_result;
         return result;

+ 0 - 2
modules/ration_glj/facade/ration_coe_facade.js

@@ -172,7 +172,6 @@ async function doAdjustUpdate (datas){
         await ration_coe.update(datas.query,datas.doc);
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:datas.query.projectID,rationID:datas.query.rationID});
         result.cal_result =cal_result;
-        console.log(result);
         return result;
     }catch (err){
         result.err = err;
@@ -200,7 +199,6 @@ async function doCustomerCoeUpdateTasks(datas) {
         };
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:datas.query.projectID,rationID:datas.query.rationID});
         result.cal_result =cal_result;
-        console.log(result);
         return result;
     }catch (err){
         result.err = err;

+ 23 - 6
modules/ration_glj/facade/ration_glj_facade.js

@@ -3,7 +3,7 @@
  */
 
 let mongoose = require('mongoose');
-const uuidV1 = require('uuid/v1')
+const uuidV1 = require('uuid/v1');
 let consts = require('../../main/models/project_consts')
 let commonConsts = consts.commonConst;
 let _=require("lodash");
@@ -17,6 +17,8 @@ let ration_coe_facade = require('./ration_coe_facade');
 let ration_coe = mongoose.model('ration_coe');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
 let glj_calculate_facade = require('./glj_calculate_facade');
+let quantity_detail_facade = require('./quantity_detail_facade');
+
 
 
 module.exports={
@@ -92,7 +94,7 @@ function get_lib_glj_info(ration_glj) {
                 ration_glj.unit = glj.unit;
                 ration_glj.specs = glj.specs;
                 ration_glj.basePrice = glj.basePrice;
-                ration_glj.gljDistType = glj.gljDistType;
+                ration_glj.shortName = glj.shortName;
                 ration_glj.type = glj.gljType;
                 getInfoFromProjectGLJ(ration_glj).then(function (result) {
                     if(result){
@@ -110,7 +112,7 @@ function get_lib_glj_info(ration_glj) {
 
 async function getInfoFromProjectGLJ(ration_glj) {
      let data = {
-         glj_repository_id: ration_glj.glj_repository_id,
+         glj_id: ration_glj.glj_repository_id,
          project_id: ration_glj.projectID,
          code: ration_glj.code,
          name: ration_glj.name,
@@ -121,6 +123,7 @@ async function getInfoFromProjectGLJ(ration_glj) {
          base_price: ration_glj.basePrice,
          market_price: ration_glj.basePrice
      };
+
      try {
          let projectGljModel = new GLJListModel();
          let result = await projectGljModel.addList(data);
@@ -230,6 +233,12 @@ async function doCustomQuantityUpdate(datas){
     try{
         await ration_glj.update(datas.query,datas.doc);
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:datas.query.projectID,rationID:datas.query.rationID});
+
+        cal_result.glj_result.forEach(function (item) {
+           if(!item.doc.hasOwnProperty('customQuantity')){
+               item.doc.customQuantity=null;
+           }
+        });
         result.cal_result =cal_result;
         console.log(result);
         return result;
@@ -275,10 +284,15 @@ async function updateprojectGljAndRationGLJ(query,doc) {
     let returnresult={};
     try {
         let gljListModel = new GLJListModel();
+        let temp = doc.market_price;
+        if(doc.market_price==null){
+            doc.market_price = doc.base_price;
+        }
+        delete doc.base_price;
         let result = await gljListModel.modifyMarketPrice(doc);
         let updateDoc ={
             marketPrice:result.unit_price.market_price,
-            marketPriceAdjust:doc.market_price,
+            marketPriceAdjust:temp,
             projectGLJID:result.id,
             isEstimate:result.is_evaluate,
             name : result.name,
@@ -318,7 +332,8 @@ function deleteByRation(datas,callback) {
     let data = datas.updateData;
     let tasks=[];
     tasks.push(deleteGLJList(data));
-    tasks.push(ration_coe_facade.delete_ration_coe(data))
+    tasks.push(ration_coe_facade.delete_ration_coe(data));
+    tasks.push(quantity_detail_facade.deleteByRation(data));
     async_n.parallel(tasks,function (err,result) {
         commonCallback(callback,result,err)
     })
@@ -377,7 +392,7 @@ function startingTask(processName){
 
 function  getRationsByBill(datas) {
     return function (results,callback) {
-        ration.find({projectID:datas.updateData.projectID,billsItemID:datas.updateData.ID},function (err,rations) {
+        ration.find({projectID:datas.updateData.projectID,billsItemID:datas.updateData.ID,deleteInfo: null},function (err,rations) {
             if(err){
                 callback(err,'')
             }else {
@@ -418,7 +433,9 @@ function deleteByMultiRations(datas) {
         for(let i=0;i<rations.length;i++){
             delete_tasks.push(deleteOne(rations[i]._doc));
             delete_tasks.push(ration_coe_facade.delete_ration_coe(rations[i]._doc));
+            delete_tasks.push(quantity_detail_facade.deleteByRation(rations[i]._doc));
         }
+        delete_tasks.push(quantity_detail_facade.deleteByBill(datas.updateData));
         async_n.parallel(delete_tasks,(err,results)=>{
             if (err){
                 deleteCallBack(err,'')

+ 21 - 0
modules/ration_glj/models/quantity_detail.js

@@ -0,0 +1,21 @@
+/**
+ * Created by chen on 2017/7/20.
+ */
+
+var mongoose = require('mongoose'),
+    Schema = mongoose.Schema;
+
+var quantity_detail = new Schema({
+    ID:String,
+    projectID: Number,
+    rationID:Number,
+    billID:Number,
+    name:String,
+    regex:String,
+    result:Number,
+    isSummation: {type: Number,default:1},//0:false 1:true
+    referenceIndexs:[Number],
+    seq:Number
+},{versionKey:false});
+
+mongoose.model('quantity_detail', quantity_detail);

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

@@ -5,7 +5,7 @@ var mongoose = require('mongoose'),
     Schema = mongoose.Schema;
 
 var ration_glj = new Schema({
-    ID:{ type:String,unique:true},
+    ID:String,
     GLJID:Number,
     projectID: Number,
     rationID:Number,
@@ -15,7 +15,7 @@ var ration_glj = new Schema({
     specs:String,
     unit:String,
     basePrice:Number,
-    gljDistType:String,
+    shortName:String,
     type:Number,
     quantity:Number,
     customQuantity:Number,

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

@@ -32,7 +32,8 @@ var gljSchema =new Schema({
     unit: String,
     basePrice: Number,
     gljType: Number, //这个是UI显示上的详细分类,对应gljTypeSchema
-    gljDistType: String  //人工,材料,机械
+    shortName: String,  //人工,材料,机械
+    gljClass:Number
 },{versionKey:false});
 
 mongoose.model("std_ration_lib_glj_list",gljSchema,"std_ration_lib_glj_list");
@@ -46,8 +47,10 @@ let rationSchema = new Schema({
     code: String,
     name: String,
     maskName: String,
+    caption: String,
     unit: String,
     quantity: String, // Decimal
+    isFromDetail:{type: Number,default:0},//1 true 2 false
     programID: Number,
     adjustState: String,
     content: String,
@@ -114,4 +117,42 @@ var rationItemSchema = mongoose.Schema({
     rationCoeList: Array,
     rationAssList: [rationAssItemSchema]
 });
-mongoose.model("std_ration_lib_ration_items",rationItemSchema, "std_ration_lib_ration_items")
+mongoose.model("std_ration_lib_ration_items",rationItemSchema, "std_ration_lib_ration_items");
+
+let billsSchema = new Schema({
+    ID: Number,
+    ParentID: Number,
+    NextSiblingID: Number,
+    projectID: Number,
+    serialNo: Number,
+    chapterID: Number,
+    code: String,
+    fullCode: String,
+    name: String,
+    unit: String,
+    quantity: String, // Decimal
+    isFromDetail:{type: Number,default:0},//1 true 2 false
+    programID: Number,
+    comments: String,
+    // 调价
+    xs_Labour: String, // Decimal
+    xs_Material: String, // Decimal
+    xs_Machine: String, // Decimal
+    xs_FeeRate: String, // Decimal
+    xs_LabourPrice: String, // Decimal
+    xs_MaterialPrice: String, // Decimal
+    xs_MachinePrice: String, // Decimal
+    isTender_Labour: Boolean,
+    isTender_Material: Boolean,
+    isTender_Machine: Boolean,
+    tenderTargetPrice: String, // Decimal
+    tenderTargetUnitPrice: String, // Decimal
+    tenderTargetUnitPrice: String, // Decimal
+    // 费用字段
+    fees: [subSchema.feesSchema],
+    // 标记字段
+    flags: [subSchema.flagsSchema],
+    deleteInfo: deleteSchema
+});
+
+mongoose.model("bills", billsSchema);

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

@@ -28,7 +28,8 @@ var gljSchema = mongoose.Schema({
     unit: String,
     basePrice: Number,
     gljType: Number, //这个是UI显示上的详细分类,对应gljTypeSchema
-    gljDistType: String  //人工,材料,机械
+    shortName: String,  //人工,材料,机械
+    gljClass:Number
 });
 var gljTypeModel = db.model("std_ration_lib_glj_type",gljTypeSchema, "std_ration_lib_glj_type");
 var gljItemModel = mongoose.model("std_ration_lib_glj_list");

+ 27 - 21
modules/ration_repository/routes/ration_front_end_routes.js

@@ -1,33 +1,39 @@
 /**
  * Created by Tony on 2017/6/15.
  */
-var express = require("express");
-var apiRouter =express.Router();
+let express = require("express");
+
+module.exports = function (app) {
+    let apiRouter =express.Router();
 //var _rootDir = __dirname;
 
-var rationRepositoryController = require("../controllers/ration_repository_controller");
-var rationChapterTreeController = require("../controllers/ration_section_tree_controller");
-var rationController = require("../controllers/ration_controller");
-var repositoryGljController = require("../controllers/repository_glj_controller");
-var coeListController = require("../controllers/coe_controller");
-var searchController = require('../controllers/search_controller');
+    var rationRepositoryController = require("../controllers/ration_repository_controller");
+    var rationChapterTreeController = require("../controllers/ration_section_tree_controller");
+    var rationController = require("../controllers/ration_controller");
+    var repositoryGljController = require("../controllers/repository_glj_controller");
+    var coeListController = require("../controllers/coe_controller");
+    var searchController = require('../controllers/search_controller');
+
+    apiRouter.post("/getRationDisplayNames",rationRepositoryController.getDisPlayRationLibs);
+    apiRouter.post("/getRealLibName",rationRepositoryController.getRealLibName);
+    apiRouter.post("/getLibIDByName",rationRepositoryController.getLibIDByName);
+
+    apiRouter.post("/getRationTree",rationChapterTreeController.getRationChapterTree);
+
+    apiRouter.post("/getRationItems",rationController.getRationItemsBySection);
 
-apiRouter.post("/getRationDisplayNames",rationRepositoryController.getDisPlayRationLibs);
-apiRouter.post("/getRealLibName",rationRepositoryController.getRealLibName);
-apiRouter.post("/getLibIDByName",rationRepositoryController.getLibIDByName);
+    apiRouter.post("/getGljTree",repositoryGljController.getGljTree);
+    apiRouter.post("/getGljItems",repositoryGljController.getGljItems);
+    apiRouter.post("/getGljItemsByIds",repositoryGljController.getGljItemsByIds);
+    apiRouter.post("/getGljItemsByCodes",repositoryGljController.getGljItemsByCodes);
 
-apiRouter.post("/getRationTree",rationChapterTreeController.getRationChapterTree);
+    apiRouter.post("/getCoeList",coeListController.getCoeList);
 
-apiRouter.post("/getRationItems",rationController.getRationItemsBySection);
+    apiRouter.post('/getRationItem', searchController.getRationItem);
+    apiRouter.post('/findRation', searchController.findRation);
 
-apiRouter.post("/getGljTree",repositoryGljController.getGljTree);
-apiRouter.post("/getGljItems",repositoryGljController.getGljItems);
-apiRouter.post("/getGljItemsByIds",repositoryGljController.getGljItemsByIds);
-apiRouter.post("/getGljItemsByCodes",repositoryGljController.getGljItemsByCodes);
+    app.use("/rationRepository/api", apiRouter);
+};
 
-apiRouter.post("/getCoeList",coeListController.getCoeList);
 
-apiRouter.post('/getRationItem', searchController.getRationItem);
-apiRouter.post('/findRation', searchController.findRation);
 
-module.exports = apiRouter;

+ 44 - 41
modules/ration_repository/routes/ration_rep_routes.js

@@ -1,46 +1,49 @@
 /**
  * Created by Tony on 2017/4/20.
  */
-var express = require("express");
-var apiRouter =express.Router();
+let express = require("express");
+let apiRouter =express.Router();
 //var _rootDir = __dirname;
+module.exports = function (app) {
+    let rationRepositoryController = require("../controllers/ration_repository_controller");
+    let rationChapterTreeController = require("../controllers/ration_section_tree_controller");
+    let rationController = require("../controllers/ration_controller");
+    let repositoryGljController = require("../controllers/repository_glj_controller");
+    let coeListController = require("../controllers/coe_controller");
+    let searchController = require('../controllers/search_controller');
+
+    apiRouter.post("/getRationDisplayNames",rationRepositoryController.getDisPlayRationLibs);
+    apiRouter.post("/editRationLibs",rationRepositoryController.updateRationRepositoryName);
+    apiRouter.post("/addRationRepository",rationRepositoryController.addRationRepository);
+    apiRouter.post("/deleteRationLibs",rationRepositoryController.deleteRationLib);
+    apiRouter.post("/getRealLibName",rationRepositoryController.getRealLibName);
+    apiRouter.post("/getLibIDByName",rationRepositoryController.getLibIDByName);
+
+    apiRouter.post("/getRationTree",rationChapterTreeController.getRationChapterTree);
+    apiRouter.post("/createNewNode",rationChapterTreeController.createNewNode);
+    apiRouter.post("/updateNodes",rationChapterTreeController.updateNodes);
+    apiRouter.post("/deleteNodes",rationChapterTreeController.deleteNodes);
+
+    apiRouter.post("/getRationItems",rationController.getRationItemsBySection);
+    apiRouter.post("/mixUpdateRationItems",rationController.mixUpdateRationItems);
+
+    apiRouter.post("/createNewGljTypeNode",repositoryGljController.createNewGljTypeNode);
+    apiRouter.post("/updateGljNodes",repositoryGljController.updateGljNodes);
+    apiRouter.post("/deleteGljNodes",repositoryGljController.deleteGljNodes);
+    apiRouter.post("/getGljTree",repositoryGljController.getGljTree);
+    apiRouter.post("/getGljItems",repositoryGljController.getGljItems);
+    apiRouter.post("/mixUpdateGljItems",repositoryGljController.mixUpdateGljItems);
+    apiRouter.post("/getGljItemsByIds",repositoryGljController.getGljItemsByIds);
+    apiRouter.post("/getGljItemsByCodes",repositoryGljController.getGljItemsByCodes);
+
+    apiRouter.post("/getCoeList",coeListController.getCoeList);
+    apiRouter.post("/saveCoeList",coeListController.saveCoeList);
+    apiRouter.post("/getCoeItemsByIDs",coeListController.getCoeItemsByIDs);
+
+    apiRouter.post('/getRationItem', searchController.getRationItem);
+    apiRouter.post('/findRation', searchController.findRation);
+
+
+};
+
 
-var rationRepositoryController = require("../controllers/ration_repository_controller");
-var rationChapterTreeController = require("../controllers/ration_section_tree_controller");
-var rationController = require("../controllers/ration_controller");
-var repositoryGljController = require("../controllers/repository_glj_controller");
-var coeListController = require("../controllers/coe_controller");
-var searchController = require('../controllers/search_controller');
-
-apiRouter.post("/getRationDisplayNames",rationRepositoryController.getDisPlayRationLibs);
-apiRouter.post("/editRationLibs",rationRepositoryController.updateRationRepositoryName);
-apiRouter.post("/addRationRepository",rationRepositoryController.addRationRepository);
-apiRouter.post("/deleteRationLibs",rationRepositoryController.deleteRationLib);
-apiRouter.post("/getRealLibName",rationRepositoryController.getRealLibName);
-apiRouter.post("/getLibIDByName",rationRepositoryController.getLibIDByName);
-
-apiRouter.post("/getRationTree",rationChapterTreeController.getRationChapterTree);
-apiRouter.post("/createNewNode",rationChapterTreeController.createNewNode);
-apiRouter.post("/updateNodes",rationChapterTreeController.updateNodes);
-apiRouter.post("/deleteNodes",rationChapterTreeController.deleteNodes);
-
-apiRouter.post("/getRationItems",rationController.getRationItemsBySection);
-apiRouter.post("/mixUpdateRationItems",rationController.mixUpdateRationItems);
-
-apiRouter.post("/createNewGljTypeNode",repositoryGljController.createNewGljTypeNode);
-apiRouter.post("/updateGljNodes",repositoryGljController.updateGljNodes);
-apiRouter.post("/deleteGljNodes",repositoryGljController.deleteGljNodes);
-apiRouter.post("/getGljTree",repositoryGljController.getGljTree);
-apiRouter.post("/getGljItems",repositoryGljController.getGljItems);
-apiRouter.post("/mixUpdateGljItems",repositoryGljController.mixUpdateGljItems);
-apiRouter.post("/getGljItemsByIds",repositoryGljController.getGljItemsByIds);
-apiRouter.post("/getGljItemsByCodes",repositoryGljController.getGljItemsByCodes);
-
-apiRouter.post("/getCoeList",coeListController.getCoeList);
-apiRouter.post("/saveCoeList",coeListController.saveCoeList);
-apiRouter.post("/getCoeItemsByIDs",coeListController.getCoeItemsByIDs);
-
-apiRouter.post('/getRationItem', searchController.getRationItem);
-apiRouter.post('/findRation', searchController.findRation);
-
-module.exports = apiRouter;

+ 14 - 3
modules/reports/routes/report_router.js

@@ -3,10 +3,21 @@
  */
 
 let express = require('express');
-let rptRouter = express.Router();
-let reportController = require('./../controllers/rpt_controller');
 
+<<<<<<< HEAD
 rptRouter.post('/getReport', reportController.getReportAllPages);
 rptRouter.get('/getExcel/:id/:size/:rptName/:isOneSheet', reportController.getExcel);
+=======
+module.exports = function (app) {
+    let rptRouter = express.Router();
+    let reportController = require('./../controllers/rpt_controller');
+
+    rptRouter.post('/getReport', reportController.getReportAllPages);
+    rptRouter.get('/getExcel/:id/:size/:rptName', reportController.getExcel);
+
+    app.use("/report_api", rptRouter);
+};
+
+
+>>>>>>> f8110b6237ef0dad71efc308cbf93a13b72b62d3
 
-module.exports = rptRouter;

+ 16 - 13
modules/reports/routes/rpt_tpl_router.js

@@ -1,17 +1,20 @@
 let express = require("express");
-let rptTplRouter = express.Router();
-let reportTplController = require('./../controllers/rpt_tpl_controller');
-let reportCfgController = require('./../controllers/rpt_cfg_controller');
+module.exports = function (app) {
+    let rptTplRouter = express.Router();
+    let reportTplController = require('./../controllers/rpt_tpl_controller');
 
-rptTplRouter.post('/createTplTreeNode', reportTplController.createTplTreeNode);
-rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
-rptTplRouter.post('/updateRptTplNodes', reportTplController.updateTreeNodes);
-rptTplRouter.post('/deleteRptTplNodes', reportTplController.deleteRptTplNodes);
-rptTplRouter.post('/createDftRptTpl', reportTplController.createDftRptTpl);
-rptTplRouter.post('/getRefRptTpl', reportTplController.getRefRptTpl);
-rptTplRouter.post('/updateRptTpl', reportTplController.updateRptTpl);
+//let reportCfgController = require('./../controllers/rpt_controller');
+
+    rptTplRouter.post('/createTplTreeNode', reportTplController.createTplTreeNode);
+    rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
+    rptTplRouter.post('/updateRptTplNodes', reportTplController.updateTreeNodes);
+    rptTplRouter.post('/deleteRptTplNodes', reportTplController.deleteRptTplNodes);
+    rptTplRouter.post('/createDftRptTpl', reportTplController.createDftRptTpl);
+    rptTplRouter.post('/getRefRptTpl', reportTplController.getRefRptTpl);
+    rptTplRouter.post('/updateRptTpl', reportTplController.updateRptTpl);
+
+//rptTplRouter.post('/getUserRptCfg', reportCfgController.getReportUserCfg);
+//rptTplRouter.post('/getMappingFields', reportCfgController.getAllMappingFields);
+};
 
-rptTplRouter.post('/getUserRptCfg', reportCfgController.getReportUserCfg);
-rptTplRouter.post('/getMappingFields', reportCfgController.getAllMappingFields);
 
-module.exports = rptTplRouter;

+ 6 - 5
modules/reports/routes/rpt_tpl_router_fe.js

@@ -3,9 +3,10 @@
  * 报表模板路由 - 前台专用(无任何update)
  */
 let express = require("express");
-let rptTplRouter = express.Router();
-let reportTplController = require('./../controllers/rpt_tpl_controller');
+module.exports = function (app) {
+    let rptTplRouter = express.Router();
+    let reportTplController = require('./../controllers/rpt_tpl_controller');
+    rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
 
-rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
-
-module.exports = rptTplRouter;
+    app.use("/report_tpl_api", rptTplRouter);
+};

+ 41 - 5
modules/templates/routes/bills_template_router.js

@@ -3,11 +3,47 @@
  */
 
 var express = require('express');
-var billsTemplateRouter = express.Router();
+
 var billsTemplateController = require('./../controllers/bills_template_controller');
 
-billsTemplateRouter.post('/getBillsTemplate', billsTemplateController.getBillsTemplate);
-billsTemplateRouter.post('/updateBillsTemplate', billsTemplateController.updateBillsTemplate);
-billsTemplateRouter.post('/getNewBillsTemplateID', billsTemplateController.getNewBillsTemplateID);
 
-module.exports = billsTemplateRouter;
+module.exports = function (app) {
+    app.get('/template/bills', function (req, res) {
+        let checkAdmin = function (userAccount) {
+            return true;
+        }
+        if (checkAdmin(req.session.userAccount)) {
+            res.render('maintain/templates/html/bills.html',
+                {userAccount: req.session.userAccount,
+                    userID: req.session.userID});
+        } else {
+            res.redirect('/pm');
+        }
+    });
+
+    app.use('/template/bills/api', function (req, res, next) {
+        let checkAdmin = function (userAccount) {
+            return true;
+        }
+        if (checkAdmin(req.session.sessionUser)) {
+            next();
+        } else {
+            res.json({error: 1, message: '对不起,您无权限操作清单模板。', data: null});
+        }
+    });
+
+    var billsTemplateRouter = express.Router();
+
+    billsTemplateRouter.post('/getBillsTemplate', billsTemplateController.getBillsTemplate);
+    billsTemplateRouter.post('/updateBillsTemplate', billsTemplateController.updateBillsTemplate);
+    billsTemplateRouter.post('/getNewBillsTemplateID', billsTemplateController.getNewBillsTemplateID);
+    app.use('/template/bills/api', billsTemplateRouter);
+}
+
+
+
+
+
+
+
+

+ 4 - 3
modules/users/controllers/login_controller.js

@@ -7,6 +7,7 @@
  */
 import UserModel from "../models/user_model";
 
+
 class LoginController {
 
     /**
@@ -55,11 +56,11 @@ class LoginController {
                 ssoId: userData.id,
                 username: userData.username,
                 email: userData.useremail,
-                mobile: userData.mobile
+                mobile: userData.mobile,
             };
             request.session.sessionUser = sessionUser;
             // 记录用户数据到数据库
-            let [result, exist] = await userModel.markUser(sessionUser);
+            let [result, exist] = await userModel.markUser(sessionUser, request);
             userExist = exist;
 
             if (!result) {
@@ -67,9 +68,9 @@ class LoginController {
             }
 
         } catch (error) {
+            console.log(error);
             return response.json({error: 1, msg: error});
         }
-
         response.json({error: 0, msg: '', exist: userExist ? 1 : 0});
     }
 

+ 31 - 5
modules/users/controllers/user_controller.js

@@ -7,6 +7,8 @@
  */
 import UserModel from "../models/user_model";
 import BaseController from "../../common/base/base_controller";
+import LogType from "../../common/const/log_type_const";
+import LogModel from "../models/log_model";
 
 class UserController extends BaseController {
 
@@ -75,14 +77,38 @@ class UserController extends BaseController {
      * @return {void}
      */
     async safe(request, response) {
-        // 获取当前用户信息
-        let sessionUser = request.session.sessionUser;
-        let userModel = new UserModel();
-        let userData = await userModel.findDataByName(sessionUser.username);
-        userData = userData.length <= 0 ? [] : userData[0];
+        let userData = [];
+        let logList = [];
+        let pageData = {};
+        try {
+            // 获取当前用户信息
+            let sessionUser = request.session.sessionUser;
+            let userModel = new UserModel();
+            userData = await userModel.findDataByName(sessionUser.username);
+            userData = userData.length <= 0 ? [] : userData[0];
+
+            let page = request.query.page === undefined ? 1 : request.query.page;
+
+            // 获取登录信息
+            let logModel = new LogModel();
+            let logCount = await logModel.count();
+            let pageSize = 3;
+            logList = await logModel.getLog(sessionUser.id, LogType.LOGIN_LOG, page, pageSize);
+
+            // 分页数据
+            pageData = {
+                current: page,
+                total: parseInt(logCount / pageSize),
+                queryData: response.locals.urlQuery
+            };
+        } catch (error) {
+            console.log(error);
+        }
 
         let renderData = {
             userData: userData,
+            logList: logList,
+            pages: pageData
         };
         response.render('users/html/user-safe', renderData);
     }

+ 104 - 0
modules/users/models/log_model.js

@@ -0,0 +1,104 @@
+/**
+ * 日志业务逻辑模型
+ *
+ * @author CaiAoLin
+ * @date 2017/7/27
+ * @version
+ */
+import BaseModel from "../../common/base/base_model";
+import LogType from "../../common/const/log_type_const";
+import LogSchema from "./schema/log";
+import UAParser from "ua-parser-js";
+
+class LogModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = LogSchema;
+        parent.init();
+    }
+
+    /**
+     * 新增日志
+     *
+     * @param {String} userId
+     * @param {Number} type
+     * @param {String|Object} message
+     * @return {Promise}
+     */
+    addLog(userId, type, message) {
+        let result = null;
+        if (typeof message === 'string' && message === '') {
+            return result;
+        }
+        if (typeof message === 'object' && Object.keys(message).length <= 0) {
+            return result;
+        }
+
+        let addData = {
+            user_id: userId,
+            type: type,
+            message: message,
+            create_time: new Date().getTime()
+        };
+        result = this.db.create(addData);
+        return result;
+    }
+
+    /**
+     * 新增登录日志
+     *
+     * @param {String} userId
+     * @param {Object} request
+     * @return {Promise}
+     */
+    addLoginLog(userId, request) {
+        let ip = request.connection.remoteAddress;
+        ip = ip.split(':');
+        ip = ip[3] === undefined ? '' : ip[3];
+
+        let userAgentObject = new UAParser(request.headers['user-agent']);
+        let osInfo = userAgentObject.getOS();
+        let cpuInfo = userAgentObject.getCPU();
+        let browserInfo = userAgentObject.getBrowser();
+        let message = {
+            os: osInfo.name + ' ' + osInfo.version + ' ' + cpuInfo.architecture,
+            browser: browserInfo.name + ' ' + browserInfo.version,
+            ip: ip
+        };
+
+        return this.addLog(userId, LogType.LOGIN_LOG, message);
+    }
+
+    /**
+     * 获取log列表
+     *
+     * @param {String} userId
+     * @param {Number} type
+     * @param {Number} page
+     * @param {Number} pageSize
+     * @return {Promise}
+     */
+    async getLog(userId, type, page = 1, pageSize = 3) {
+        let condition = {
+            user_id: userId,
+            type: type
+        };
+        page = parseInt(page);
+        page = page <= 1 ? 1 : page;
+        let option = {pageSize: pageSize, offset: parseInt((page - 1) * pageSize)};
+
+        let logList = await this.db.find(condition, null, option);
+        logList = logList.length > 0 ? logList : [];
+
+        return logList
+    }
+
+}
+
+export default LogModel;

+ 29 - 0
modules/users/models/schema/log.js

@@ -0,0 +1,29 @@
+/**
+ * 日志数据结构
+ *
+ * @author CaiAoLin
+ * @date 2017/7/27
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'log';
+let modelSchema = {
+    // 日志类型
+    type: {
+        type: Number,
+        index: true
+    },
+    // 日志内容
+    message: Schema.Types.Mixed,
+    // 关联用户id
+    user_id: {
+        type: String,
+        index: true
+    },
+    // 创建时间
+    create_time: Number
+};
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 24 - 7
modules/users/models/schema/user.js

@@ -16,13 +16,30 @@ let schema = {
     username: String,
     email: String,
     mobile: String,
-    real_name: String,
-    company: String,
-    province: String,
-    area: Number,
-    company_type: Number,
-    company_scale: Number,
-    last_login: Number,
+    real_name: {
+        type: String,
+        default: ''
+    },
+    company: {
+        type: String,
+        default: ''
+    },
+    province: {
+        type: Number,
+        default: -1
+    },
+    area: {
+        type: Number,
+        default: 0
+    },
+    company_type: {
+        type: Number,
+        default: -1,
+    },
+    company_scale: {
+        type: Number,
+        default: -1
+    },
     create_time: Number
 };
 

+ 8 - 16
modules/users/models/user_model.js

@@ -8,6 +8,7 @@
 import userSchema from "./schema/user";
 import Request from "request";
 import BaseModel from "../../common/base/base_model"
+import LogModel from "./log_model";
 
 class UserModel extends BaseModel {
 
@@ -79,9 +80,10 @@ class UserModel extends BaseModel {
      * 标记用户
      *
      * @param {object} userData
+     * @param {Object} request
      * @return {Promise}
      */
-    async markUser(userData) {
+    async markUser(userData, request = null) {
         let userDataFromDb = await this.findDataByName(userData.username);
         let result = false;
 
@@ -91,15 +93,12 @@ class UserModel extends BaseModel {
             // 不存在用户则入库
             result = await this.addUser(userData);
         } else {
-            // 存在则更新用户信息
-            let updateData = {last_login: new Date().getTime()};
-            let condition = {email: userData.email};
-            result = await this.updateUser(condition, updateData);
-
-            userDataFromDb = userDataFromDb[0];
-            info = userDataFromDb.real_name !== undefined && userDataFromDb.real_name !== '';
-            result = result.ok === 1;
+            info = true;
+            // 存在则新增登录信息
+            let logModel = new LogModel();
+            result = await logModel.addLoginLog(userDataFromDb._id, request);
         }
+        request.session.sessionUser.id = userDataFromDb._id;
 
         return [result, info];
     }
@@ -141,14 +140,7 @@ class UserModel extends BaseModel {
             username: userData.username,
             email: userData.email,
             mobile: userData.mobile,
-            real_name: '',
-            company: '',
-            province: -1,
-            company_type: -1,
-            company_scale: -1,
-            last_login: 0,
             create_time: new Date().getTime(),
-            area: 0
         };
         return this.db.create(insertData);
     }

+ 3 - 3
modules/users/routes/user_route.js

@@ -14,8 +14,8 @@ module.exports = function (app) {
     const userController = new UserController();
 
 // action定义区域
-    router.get('/info', userController.info);
-    router.get('/safe', userController.safe);
-    router.post('/info', userController.saveData);
+    router.get('/info', userController.init, userController.info);
+    router.get('/safe', userController.init, userController.safe);
+    router.post('/info', userController.init, userController.saveData);
     app.use('/user',router);
 };

+ 66 - 0
modules/volume_price/models/volume_price_model.js

@@ -0,0 +1,66 @@
+/**
+ * Created by Mai on 2017/7/25.
+ */
+
+let consts = require('../../main/models/project_consts');
+let commonConsts = consts.commonConst;
+
+let mongoose = require('mongoose');
+let volumePrice = require("./volume_price_schema");
+let async = require("async");
+
+let baseModel = require('../../main/models/base_model');
+
+class volumePriceModel extends baseModel {
+    constructor (name) {
+        super(volumePrice);
+        this.collectionName = name;
+    };
+
+    getData (projectID, callback) {
+        volumePrice.find({'$or': [
+            {
+                projectID: projectID,
+                deleteInfo: null
+            }, {
+                projectID: projectID,
+                'deleteInfo.deleted': {$in: [null, false]}
+            }
+        ]},(err,datas)=>{
+            if(err){
+                callback(1, '', null);
+            }else {
+                callback(0, consts.projectConst.VOLUMEPRICE, datas);
+            }
+        })
+    };
+
+    save (user_id, datas, callback) {
+        let funs = [];
+
+        function saveOne(doc) {
+            return function (cb) {
+                switch (doc.updateType) {
+                    case commonConsts.UT_UPDATE:
+                        volumePrice.update({projectID: doc.updateData.projectID, ID: doc.updateData.ID}, doc.updateData, cb);
+                        break;
+                    case commonConsts.UT_CREATE:
+                        volumePrice.create(doc.updateData, cb);
+                        break;
+                    case commonConsts.UT_DELETE:
+                        doc.updateData.deleteInfo = {deleted: true, deleteDateTime: new Date(), deleteBy: user_id};
+                        volumePrice.update({projectID: doc.updateData.projectID, ID: doc.updateData.ID}, doc.updateData, cb);
+                        break;
+                }
+            }
+        }
+        for (let data of datas){
+            funs.push(saveOne(data));
+        }
+
+        async.parallel(funs, callback);
+    };
+
+};
+
+module.exports = new volumePriceModel(consts.projectConst.VOLUMEPRICE);

+ 33 - 0
modules/volume_price/models/volume_price_schema.js

@@ -0,0 +1,33 @@
+/**
+ * Created by Mai on 2017/7/25.
+ * 量价
+ */
+
+let mongoose = require("mongoose");
+let Schema = mongoose.Schema;
+let deleteSchema = require('../../../public/models/delete_schema');
+let subSchema = require('../../main/models/bills_sub_schemas');
+
+let collectionName = 'volume_price';
+let volumePriceSchema = new Schema({
+    // id
+    ID: Number,
+    // 关联pm
+    projectID: Number,
+    // 关联 Bills
+    billsItemID: Number,
+    // 排序
+    serialNo: Number,
+    // 名称
+    name: String,
+    // 单位
+    unit: String,
+    // 数量
+    quantity: Number,
+    // 费用字段
+    fees: [subSchema.feesSchema],
+    // 是否删除
+    deleteInfo: deleteSchema
+});
+
+module.exports = mongoose.model(collectionName, volumePriceSchema);

+ 3 - 0
package.json

@@ -24,6 +24,9 @@
   "dependencies": {
     "bluebird": "^3.5.0",
     "jszip": "^3.1.3",
+    "moment": "^2.18.1",
+    "socket.io": "^2.0.3",
+    "ua-parser-js": "^0.7.14",
     "uuid": "^3.1.0"
   },
   "scripts": {

+ 11 - 10
public/calc_util.js

@@ -19,7 +19,7 @@ let executeObj = {
     at: function(code) {
         let me = executeObj,
             rst = 0;
-        rst = me.currentTpl.compileAssistantObj[code].execRst;
+        rst = me.currentTpl.compileAssistantObj[code].unitFee;
         return rst;
     },
     base: function(calcBaseCode) {
@@ -28,9 +28,9 @@ let executeObj = {
         if (idx >= 0) {
             if (dummyCalcBaseCodeTypeCollection[idx].length > 0) {
                 let tmpSum = 0;
-                for (let glj of me.currentRationItem.rationGljList) {
-                    if (dummyCalcBaseCodeTypeCollection[idx].indexOf(glj["glj"]["gljType"]) >= 0) {
-                        tmpSum += glj["glj"]["basePrice"] * glj["consumeAmt"];
+                for (let glj of me.currentRationItem.data.gljList) {
+                    if (dummyCalcBaseCodeTypeCollection[idx].indexOf(glj["type"]) >= 0) {
+                        tmpSum += glj["basePrice"] * glj["quantity"];
                     }
                 }
                 rst = tmpSum;
@@ -99,10 +99,10 @@ class calculation {
             return rst;
         };
         let private_parse_ref = function(item, itemIdx){
-            let expr = item.expression.split('at(').join('@(');
+            let expr = item.expression.split('at(').join('A(');
             item.expression = expr;
             //console.log('expression: ' + expr);
-            let idx = expr.indexOf('@(', 0);
+            let idx = expr.indexOf('A(', 0);
             while (idx >= 0) {
                 let code = private_extract_code(expr, idx);
                 //console.log('提取code: ' + code);
@@ -119,7 +119,7 @@ class calculation {
                         console.log('invalid code: ' + code);
                     }
                 }
-                idx = expr.indexOf('@(', idx + code.length + 3);
+                idx = expr.indexOf('A(', idx + code.length + 3);
             }
             if (me.calcTpl.compiledSeq.indexOf(itemIdx) < 0) {
                 //console.log('the code ready to push: ' + item.code);
@@ -134,7 +134,7 @@ class calculation {
         let private_compile_items = function() {
             for (let idx of me.calcTpl.compiledSeq) {
                 let item = me.calcTpl.calcItems[idx];
-                item.compiledExpr = item.expression.split('@(').join('$CE.at(');
+                item.compiledExpr = item.expression.split('A(').join('$CE.at(');
                 item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
                 item.compiledExpr = item.compiledExpr.split('fee(').join('$CE.fee(');
                 item.compiledExpr = item.compiledExpr.split('factor(').join('$CE.factor(');
@@ -184,10 +184,11 @@ class calculation {
             $CE.compiledFeeRateFile = me.compiledFee;
             for (let idx of me.calcTpl.compiledSeq) {
                 let item = me.calcTpl.calcItems[idx];
-                item.execRst = eval(item.compiledExpr);
+                item.unitFee = eval(item.compiledExpr);
+                item.totalFee = eval(item.unitFee * 5);     // AAAAA 5为测试值 $RATION.data.Quantity
             }
         }
     }
 }
 
-module.exports = new calculation();
+//module.exports = new calculation();

+ 15 - 17
public/web/id_tree.js

@@ -73,10 +73,8 @@ var idTree = {
                 var next = (pre && iIndex + count - 1 < children.length) ? children[iIndex + count] : null;
                 if (pre) {
                     pre.setNextSibling(next);
-                    //pre.nextSibling = next;
-                }
-                if (next) {
-                    next.preSibling = pre;
+                } else if (next) {
+                    next.preSibling = null;
                 }
                 if (arguments.length === 4) {
                     children.splice(iIndex, count);
@@ -98,10 +96,8 @@ var idTree = {
                 }
                 if (pre) {
                     pre.setNextSibling(nodes[0]);
-                }
-                nodes[0].preSibling = pre;
-                if (next) {
-                    next.preSibling = nodes[nodes.length - 1];
+                } else {
+                    nodes[0].preSibling = null;
                 }
                 nodes[nodes.length - 1].setNextSibling(next);
                 for (i = 0; i < nodes.length; i++) {
@@ -170,6 +166,9 @@ var idTree = {
         };
         Node.prototype.setNextSibling = function (nextSibling) {
             this.nextSibling = nextSibling;
+            if (nextSibling) {
+                nextSibling.preSibling = this;
+            }
             if (this.tree.setting.autoUpdate) {
                 this.data[this.tree.setting.nid] = this.getNextSiblingID();
             }
@@ -250,7 +249,7 @@ var idTree = {
             if (nextSibling) {
                 nextSibling.preSibling = preSibling;
             }
-            this.children.splice(this.children.re)
+            this.children.splice(node.siblingIndex, 1);
         };
 
         Node.prototype.canUpLevel = function () {
@@ -336,10 +335,10 @@ var idTree = {
             if (this.canUpMove()) {
                 if (orgPre.preSibling) {
                     orgPre.preSibling.setNextSibling(this);
+                } else {
+                    this.preSibling = null;
                 }
                 orgPre.setNextSibling(this.nextSibling);
-                this.preSibling = orgPre.preSibling;
-                orgPre.preSibling = this;
                 this.setNextSibling(orgPre);
                 belongArray.splice(iIndex, 1);
                 belongArray.splice(iIndex - 1, 0, this);
@@ -369,11 +368,11 @@ var idTree = {
             if (this.canDownMove()) {
                 if (this.preSibling) {
                     this.preSibling.setNextSibling(orgNext);
+                } else if (orgNext) {
+                    orgNext.preSibling = null;
                 }
-                orgNext.preSibling = this.preSibling;
                 this.setNextSibling(orgNext.nextSibling);
                 orgNext.setNextSibling(this);
-                this.preSibling = orgNext;
                 belongArray.splice(iIndex, 1);
                 belongArray.splice(iIndex + 1, 0, this);
                 tools.sortTreeItems(this.tree);
@@ -571,9 +570,8 @@ var idTree = {
                 //delete this.nodes[this.prefix + node.getID()];
                 if (node.preSibling) {
                     node.preSibling.setNextSibling(node.nextSibling);
-                }
-                if (node.nextSibling) {
-                    node.nextSibling.preSibling = node.preSibling;
+                } else if (node.nextSibling) {
+                    node.nextSibling.preSibling = null;
                 }
                 if (node.parent) {
                     node.parent.children.splice(node.siblingIndex(), 1);
@@ -622,4 +620,4 @@ var idTree = {
         return new Tree(setting);
     },
     updateType: {update: 'update', new: 'new', delete: 'delete'}
-};
+};

+ 8 - 2
public/web/sheet/sheet_common.js

@@ -10,6 +10,8 @@ var sheetCommonObj = {
         var spreadBook = new GC.Spread.Sheets.Workbook(container, { sheetCount: SheetCount });
         spreadBook.options.tabStripVisible = false;
         spreadBook.options.showHorizontalScrollbar = false;
+        spreadBook.options.allowCopyPasteExcelStyle = false;
+        spreadBook.options.allowUserDragDrop = true;
         return spreadBook;
     },
 
@@ -110,7 +112,7 @@ var sheetCommonObj = {
                 //sheet.setStyle(row,col,style,GC.Spread.Sheets.SheetArea.viewport);
                 sheet.setFormatter(-1, col, setting.header[col].formatter, GC.Spread.Sheets.SheetArea.viewport);
             }
-            if(setting.header[col].cellType === "checkBox"){//clear and reset
+            if(setting.header[col].cellType === "checkBox"||setting.header[col].cellType === "button"){//clear and reset
                 var me = this, header = GC.Spread.Sheets.SheetArea.colHeader;
                 sheet.deleteColumns(col,1);
                 sheet.addColumns(col, 1);
@@ -121,7 +123,11 @@ var sheetCommonObj = {
                 //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"){
-                    val =parseFloat(val).toFixed(2);
+                    if(setting.header[col].hasOwnProperty('tofix')){
+                        val =parseFloat(val).toFixed(setting.header[col].tofix);
+                    }else {
+                        val =parseFloat(val).toFixed(2);
+                    }
                 }
                 if(val!=null&&setting.header[col].cellType === "checkBox"){
                     this.setCheckBoxCell(row,col,sheet,val)

+ 10 - 1
public/web/tree_sheet/tree_sheet_helper.js

@@ -5,7 +5,7 @@
 var TREE_SHEET_HELPER = {
     getSheetCellStyle: function (setting) {
         var style = new GC.Spread.Sheets.Style();
-        style.locked = setting.readOnly ? true : false;
+        //style.locked = setting.readOnly ? true : false;
         style.name = setting.id;
         style.font = setting.data.font;
         style.hAlign = setting.data.hAlign;
@@ -99,6 +99,15 @@ var TREE_SHEET_HELPER = {
                 } else {
                     cell.value(getFieldText2());
                 }
+                if (colSetting.readOnly) {
+                    if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
+                        cell.locked(colSetting.readOnly(node));
+                    } else {
+                        cell.locked(true);
+                    }
+                } else {
+                    cell.locked(false);
+                }
             });
             sheet.autoFitRow(node.serialNo());
             if (recursive) {

+ 5 - 104
server.js

@@ -39,6 +39,7 @@ app.use(session({
     secret: 'session users secret',
     cookie: {maxAge: 1000*60*30},
     resave: false,
+    rolling: true,
     saveUninitialized: true
     //*
     ,store: new DBStore({
@@ -72,116 +73,16 @@ app.use(function (req, res, next) {
     }
 });
 
-// 用户相关路由区域
-//app.use('/', require("./modules/users/routes/login_route"));
-
-require("./modules/users/routes/login_route")(app);
-require("./modules/users/routes/boot_route")(app);
-require("./modules/users/routes/user_route")(app);
-
-
-
-
-
-
-// 项目工料机相关
-app.use('/glj', require("./modules/glj/routes/glj_router"));
-
-app.use('/feeRates', require('./modules/fee_rates/routes/fee_rates_route'));
-
-app.get('/template/bills', function (req, res) {
-    let checkAdmin = function (userAccount) {
-        return true;
-    }
-    if (checkAdmin(req.session.userAccount)) {
-        res.render('maintain/templates/html/bills.html',
-            {userAccount: req.session.userAccount,
-                userID: req.session.userID});
-    } else {
-        res.redirect('/pm');
-    }
-});
-
-app.use('/template/bills/api', function (req, res, next) {
-    let checkAdmin = function (userAccount) {
-        return true;
-    }
-    if (checkAdmin(req.session.sessionUser)) {
-        next();
-    } else {
-        res.json({error: 1, message: '对不起,您无权限操作清单模板。', data: null});
-    }
-});
-let billsTemplateRouter = require('./modules/templates/routes/bills_template_router');
-app.use('/template/bills/api', billsTemplateRouter);
-
-app.get('/pm', function(req, res){
-    res.render('building_saas/pm/html/project-management.html',
-        {userAccount: req.session.userAccount,
-            userID: req.session.sessionUser.ssoId});
-});
-let pmRouter = require('./modules/pm/routes/pm_route');
-app.use('/pm/api', pmRouter);
-
-app.get('/main',  function(req, res) {
-    let pm = require('./modules/pm/controllers/pm_controller');
-    pm.checkProjectRight(req.session.sessionUser.ssoId, req.query.project, function (hasRight) {
-        if (hasRight) {
-            res.render('building_saas/main/html/main.html',
-                {
-                    userAccount: req.session.userAccount,
-                    userID: req.session.sessionUser.ssoId
-                });
-        } else {
-            res.redirect('/pm');
-        }
-    });
-});
-
-let project_Router = require('./modules/main/routes/project_route');
-let bills_Router = require('./modules/main/routes/bills_route');
-let ration_Router = require('./modules/main/routes/ration_route');
-let GLJ_Router = require('./modules/main/routes/GLJ_route');
-app.use('/project', project_Router);
-app.use('/bills', bills_Router);
-app.use('/ration', ration_Router);
-app.use('/glj_bak', GLJ_Router);
+//加载路由文件
+fileUtils.getGlobbedFiles('./modules/**/routes/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath))(app);
+})
 
 //app.use(express.static(_rootDir+"/web"));
 //app.use(express.static(_rootDir+"/lib"));
 //let rations_Router = require("./modules/rationLibEditor/routes/rationLibEditor_route");
 //app.use("/rationLibEditor",rations_Router);
 
-app.get("/stdBillsmain", function(req, res){
-    if(!req.session.sessionUser){
-        res.redirect('/login');
-    }
-    else {
-        res.render("maintain/bills_lib/html/main.html",
-            {userAccount: req.session.userAccount,
-                userID: req.session.sessionUser.ssoId});
-    }
-});
-app.get("/stdBills", function(req, res){
-    res.render("maintain/bills_lib/html/qingdan.html");
-});
-app.get('/stdJobs', function(req, res){
-    res.render('maintain/bills_lib/html/neirong.html');
-});
-app.get('/stdItems', function(req, res){
-    res.render('maintain/bills_lib/html/tezheng.html');
-});
-let billsLibRouter = require("./modules/bills_lib/routes/bills_lib_routes");
-app.use("/stdBillsEditor", billsLibRouter);
-
-let rationRouter = require("./modules/ration_repository/routes/ration_front_end_routes");
-app.use("/rationRepository/api", rationRouter);
-
-let rpt_Router = require("./modules/reports/routes/report_router");
-app.use("/report_api", rpt_Router);
-let rptTpl_Router = require("./modules/reports/routes/rpt_tpl_router_fe");
-app.use("/report_tpl_api", rptTpl_Router);
-
 //-----------------
 
 app.use(function(req, res, next) {

+ 27 - 0
socket.js

@@ -0,0 +1,27 @@
+/**
+ * socket入口脚本文件
+ *
+ * @author caiaolin
+ * @date 2017-05-12
+ * @version
+ */
+import socket from "socket.io";
+
+const socketIO = socket(3300);
+
+// socket.io相关操作
+socketIO.on('connection', function(socket) {
+    let roomId = '';
+
+    // 加入房间
+    socket.on('join', function(data) {
+        roomId = data;
+        socket.join(data);
+    });
+
+    // 数据更改通知
+    socket.on('dataNotify', function(data) {
+        socket.broadcast.to(roomId).emit('dataChange', data);
+    });
+
+});

+ 441 - 0
test/calculation/test_ration_calc.js

@@ -0,0 +1,441 @@
+/**
+ * Created by CSL on 2017/7/14.
+ */
+var test = require('tape');
+var calcUtil = require('../../public/calc_util');
+
+let dummyFee = [
+    {
+        "ID" : 1,
+        "ParentID" : null,
+        "name" : "企业管理费",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 2,
+        "ParentID" : 1,
+        "name" : "建筑工程",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 3,
+        "ParentID" : 2,
+        "name" : "一类工程",
+        "rate" : 16.03,
+        "memo" : null
+    },
+    {
+        "ID" : 4,
+        "ParentID" : 2,
+        "name" : "二类工程",
+        "rate" : 14.95,
+        "memo" : null
+    },
+    {
+        "ID" : 5,
+        "ParentID" : 2,
+        "name" : "三类工程",
+        "rate" : 12.47,
+        "memo" : null
+    },
+    {
+        "ID" : 6,
+        "ParentID" : 2,
+        "name" : "四类工程",
+        "rate" : 9.3,
+        "memo" : null
+    },
+    {
+        "ID" : 7,
+        "ParentID" : 1,
+        "name" : "市政工程",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 8,
+        "ParentID" : 7,
+        "name" : "一类工程",
+        "rate" : 16.33,
+        "memo" : null
+    },
+    {
+        "ID" : 9,
+        "ParentID" : 7,
+        "name" : "二类工程",
+        "rate" : 15,
+        "memo" : null
+    },
+    {
+        "ID" : 10,
+        "ParentID" : 7,
+        "name" : "三类工程",
+        "rate" : 12.5,
+        "memo" : null
+    },
+    {
+        "ID" : 11,
+        "ParentID" : 7,
+        "name" : "四类工程",
+        "rate" : 9.5,
+        "memo" : null
+    },
+    {
+        "ID" : 12,
+        "ParentID" : 1,
+        "name" : "机械土石方",
+        "rate" : 15.5,
+        "memo" : null
+    },
+    {
+        "ID" : 13,
+        "ParentID" : 1,
+        "name" : "仿古建筑工程",
+        "rate" : 12,
+        "memo" : null
+    },
+    {
+        "ID" : 14,
+        "ParentID" : 1,
+        "name" : "建筑修缮工程",
+        "rate" : 12.47,
+        "memo" : null
+    },
+    {
+        "ID" : 15,
+        "ParentID" : 1,
+        "name" : "炉窑砌筑工程",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 16,
+        "ParentID" : 15,
+        "name" : "一类工程",
+        "rate" : 14.25,
+        "memo" : null
+    },
+    {
+        "ID" : 17,
+        "ParentID" : 15,
+        "name" : "二类工程",
+        "rate" : 12.47,
+        "memo" : null
+    },
+    {
+        "ID" : 18,
+        "ParentID" : 15,
+        "name" : "三类工程",
+        "rate" : 10.8,
+        "memo" : null
+    },
+    {
+        "ID" : 19,
+        "ParentID" : 15,
+        "name" : "四类工程",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 20,
+        "ParentID" : null,
+        "name" : "规费",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 21,
+        "ParentID" : 20,
+        "name" : "建筑工程",
+        "rate" : 4.87,
+        "memo" : null
+    },
+    {
+        "ID" : 22,
+        "ParentID" : 20,
+        "name" : "市政工程",
+        "rate" : 3.61,
+        "memo" : null
+    },
+    {
+        "ID" : 23,
+        "ParentID" : 20,
+        "name" : "机械土石方",
+        "rate" : 2.15,
+        "memo" : null
+    },
+    {
+        "ID" : 24,
+        "ParentID" : 20,
+        "name" : "仿古建筑工程",
+        "rate" : 2.84,
+        "memo" : null
+    },
+    {
+        "ID" : 25,
+        "ParentID" : 20,
+        "name" : "建筑修缮工程",
+        "rate" : 2.84,
+        "memo" : null
+    },
+    {
+        "ID" : 26,
+        "ParentID" : 20,
+        "name" : "炉窑砌筑工程",
+        "rate" : 3.61,
+        "memo" : null
+    },
+    {
+        "ID" : 27,
+        "ParentID" : null,
+        "name" : "利润",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 28,
+        "ParentID" : 27,
+        "name" : "建筑工程",
+        "rate" : null,
+        "memo" : null
+    },
+    {
+        "ID" : 29,
+        "ParentID" : 28,
+        "name" : "一类工程",
+        "rate" : 8.73,
+        "memo" : null
+    },
+    {
+        "ID" : 30,
+        "ParentID" : 28,
+        "name" : "二类工程",
+        "rate" : 6.94,
+        "memo" : null
+    }
+]
+
+let dummyCalcTpl = {
+    calType: 3,
+    calTypeName: "测试用_重庆",
+    compiledSeq: [],
+    calcItems: [
+        {
+        code: "1",
+        name: "基价直接工程费",
+        dispExpr: "A2+A5+A6+A10",
+        expression: "A('2') + A('5') + A('6') + A('10')",
+        compiledExpr: "",
+        statement: "基价人工费+基价材料费+基价机械费+未计价材料费"
+    },
+        {
+        code: "2",
+        name: "基价人工费",
+        dispExpr: "A3+A4",
+        expression: "A('3') + A('4')",
+        compiledExpr: "",
+        statement: "定额基价人工费+定额人工单价(基价)调整"
+    },
+        {
+        code: "3",
+        name: "定额基价人工费",
+        dispExpr: "定额基价人工费",
+        expression: "base('定额基价人工费').toFixed(2)",
+        compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+        {
+        code: "4",
+        name: "定额人工单价(基价)调整",
+        dispExpr: "A3*[1.89-1]",
+        expression: "A('3') * (1.89-1)",
+        compiledExpr: "",
+        statement: "定额基价人工费*[定额人工单价(基价)调整系数-1]"
+    },
+        {
+        code: "5",
+        name: "基价材料费",
+        dispExpr: "定额基价材料费",
+        expression: "base('定额基价材料费')",
+        compiledExpr: "",
+        statement: "定额基价材料费"
+    },
+        {
+        code: "6",
+        name: "基价机械费",
+        dispExpr: "A7+A9",
+        expression: "A('7') + A('9')",
+        compiledExpr: "",
+        statement: "定额基价机械费+定额机上人工单价(基价)调整"
+    },
+        {
+        code: "7",
+        name: "定额基价机械费",
+        dispExpr: "定额基价机械费",
+        expression: "base('定额基价机械费')",
+        compiledExpr: "",
+        statement: "定额基价机械费"
+    },
+        {
+        code: "8",
+        name: "其中:定额基价机上人工费",
+        dispExpr: "定额基价机上人工费",
+        expression: "base('定额基价机上人工费')",
+        compiledExpr: "",
+        statement: "定额基价机上人工费"
+    },
+        {
+        code: "9",
+        name: "定额机上人工单价(基价)调整",
+        dispExpr: "A8*[1.89-1]",
+        expression: "A('8') * (1.89-1)",
+        compiledExpr: "",
+        statement: "定额基价机上人工费*[定额机上人工单价(基价)调整系数-1]"
+    },
+        {
+        code: "10",
+        name: "未计价材料费",
+        dispExpr: "主材费+设备费",
+        expression: "base('主材费') + base('设备费')",
+        compiledExpr: "",
+        statement: "主材费+设备费"
+    },
+        {
+        code: "11",
+        name: "企业管理费",
+        dispExpr: "A3",
+        expression: "A('3')",
+        compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+        {
+        code: "12",
+        name: "利润",
+        dispExpr: "A3",
+        expression: "A('3')",
+        compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+        {
+        code: "13",
+        name: "风险因素",
+        dispExpr: "",
+        expression: "A('3')",
+        compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+        {
+        code: "14",
+        name: "人材机价差",
+        dispExpr: "A15+A16+A17",
+        expression: "A('15') + A('16') + A('17')",
+        compiledExpr: "",
+        statement: "人工费价差+材料费价差+机械费价差"
+    },
+        {
+        code: "15",
+        name: "人工费价差",
+        dispExpr: "信息价或市场价格-调整后的定额人工费(基价)",
+        expression: "base('市场价格人工费') - base('定额基价人工费(调整后)')",
+        compiledExpr: "",
+        statement: "市场价格人工费-调整后的定额人工费(基价)"
+    },
+        {
+        code: "16",
+        name: "材料费价差",
+        dispExpr: "信息价或市场价格-定额基价材料费",
+        expression: "base('市场价格材料费') - base('定额基价材料费(调整后)')",
+        compiledExpr: "",
+        statement: "市场价格材料费-定额基价材料费"
+    },
+        {
+        code: "17",
+        name: "机械费价差",
+        dispExpr: "信息价或市场价格-调整后的定额基价机械费(基价)",
+        expression: "base('市场价格机械费') - base('定额基价机械费(调整后)')",
+        compiledExpr: "",
+        statement: "市场价格机械费-调整后的定额基价机械费(基价)"
+    },
+        {
+        code: "18",
+        name: "综合单价",
+        dispExpr: "A1+A11+A12+A13+A14",
+        expression: "A('1') + A('11') + A('12') + A('13') + A('14')",
+        compiledExpr: "",
+        statement: "基价直接工程费+企业管理费+利润+风险因素+人材机价差"
+    }
+    ]
+};
+
+let dummyRation = {
+    "sectionId" : 76,
+    "ID" : 15,
+    "code" : "AA0001",
+    "name" : "人工挖土方",
+    "unit" : "100m3",
+    "basePrice" : 840.84,
+    "caption" : "人工挖土方",
+    "feeType" : 2,
+    "rationGljList" : [
+        {
+            "glj" : {
+                "repositoryId" : 3,
+                "ID" : 17,
+                "code" : "00010201",
+                "name" : "土石方综合工日",
+                "specs" : null,
+                "unit" : "工日",
+                "basePrice" : 22,
+                "gljDistType" : "人工",
+                "gljType" : 2
+            },
+            "consumeAmt" : 38.22,
+            "proportion" : 0
+        },{
+            "glj": {
+                "repositoryId" : 3,
+                "ID" : 68,
+                "code" : "85030207",
+                "name" : "履带式起重机",
+                "specs" : "50t",
+                "unit" : "台班",
+                "basePrice" : 1194.05,
+                "gljDistType" : "机械",
+                "gljType" : 64
+            },
+            "consumeAmt" : 1.22,
+            "proportion" : 0
+        },{
+            "glj": {
+                "repositoryId" : 3,
+                "ID" : 200,
+                "code" : "36290101",
+                "name" : "水",
+                "specs" : "",
+                "unit" : "m3",
+                "basePrice" : 2,
+                "gljDistType" : "材料",
+                "gljType" : 6
+            },
+            "consumeAmt" : 9.2,
+            "proportion" : 0
+        }
+    ],
+    "rationRepId" : 3
+}
+
+//*
+test('计算式测试', function(t){
+    calcUtil.init(dummyCalcTpl, dummyFee);
+    calcUtil.compile();
+    calcUtil.calculate(dummyRation);
+    for (let idx of dummyCalcTpl.compiledSeq) {
+        let item = dummyCalcTpl.calcItems[idx];
+        console.log('code: ' + item.code + ' | expression: ' + item.compiledExpr +  ' | result: ' + item.execRst);
+    }
+    t.pass('just pass for calculation initialization!');
+    t.end();
+})
+

+ 9 - 67
test/tmp_data/bills_grid_setting.js

@@ -4,72 +4,12 @@
 var BillsGridSetting ={
     "emptyRows":3,
     "headRows":1,
-    "treeCol": 2,
+    "treeCol": 0,
     "headRowHeight":[
         47
     ],
     "cols":[
         {
-            "width":50,
-            "readOnly":false,
-            "head":{
-                "titleNames":[
-                    "ID"
-                ],
-                "spanCols":[
-                    1
-                ],
-                "spanRows":[
-                    1
-                ],
-                "vAlign":[
-                    1
-                ],
-                "hAlign":[
-                    1
-                ],
-                "font":[
-                    "Comic Sans MS"
-                ]
-            },
-            "data":{
-                "field":"ID",
-                "vAlign":1,
-                "hAlign":0,
-                "font":"Arial"
-            }
-        },
-        {
-            "width":50,
-            "readOnly":false,
-            "head":{
-                "titleNames":[
-                    "serialNo"
-                ],
-                "spanCols":[
-                    1
-                ],
-                "spanRows":[
-                    1
-                ],
-                "vAlign":[
-                    1
-                ],
-                "hAlign":[
-                    1
-                ],
-                "font":[
-                    "Comic Sans MS"
-                ]
-            },
-            "data":{
-                "field":"serialNo",
-                "vAlign":1,
-                "hAlign":0,
-                "font":"Arial"
-            }
-        },
-        {
             "width":150,
             "readOnly":false,
             "head":{
@@ -101,7 +41,7 @@ var BillsGridSetting ={
         },
         {
             "width":50,
-            "readOnly":false,
+            "readOnly":true,
             "head":{
                 "titleNames":[
                     "类别"
@@ -125,8 +65,9 @@ var BillsGridSetting ={
             "data":{
                 "field":"type",
                 "vAlign":1,
-                "hAlign":0,
-                "font":"Arial"
+                "hAlign":1,
+                "font":"Arial",
+                "getText": 'getText.type'
             }
         },
         {
@@ -156,7 +97,8 @@ var BillsGridSetting ={
                 "field":"name",
                 "vAlign":1,
                 "hAlign":0,
-                "font":"Arial"
+                "font":"Arial",
+                "wordWrap": true
             }
         },
         {
@@ -314,7 +256,7 @@ var BillsGridSetting ={
         },
         {
             "width":120,
-            "readOnly":false,
+            "readOnly": 'readOnly.volumePrice',
             "head":{
                 "titleNames":[
                     "工程量计算规则"
@@ -643,4 +585,4 @@ var BillsGridSetting ={
             }
         }
     ]
-};
+};

+ 209 - 0
test/tmp_data/test_bills_calc/bills_calc_base.js

@@ -0,0 +1,209 @@
+let BillsCalcBase = [
+    {
+        'classKey': 'FBFX',
+        'className': '分部分项',
+        'type': 'bills',
+        'subBase': [
+            {
+                'subKey': 'GCF',
+                'subName': '工程费',
+                'dispName': '分部分项--工程费'
+            },{
+                'subKey': 'DEJJRGF',
+                'subName': '定额基价人工费',
+                'dispName': '分部分项--定额基价人工费'
+            },{
+                'subKey': 'DEJJCLF',
+                'subName': '定额基价材料费',
+                'dispName': '分部分项--定额基价材料费'
+            },{
+                'subKey': 'DEJJJXF',
+                'dispName': '分部分项--定额基价机械费'
+            },{
+                'subKey': 'TZRGF',
+                'dispName': '分部分项--调整人工费'
+            },{
+                'subKey': 'TZJSRGF',
+                'dispName': '分部分项--调整机上人工费'
+            },{
+                'subKey': 'ZCF',
+                'dispName': '分部分项--主材费'
+            },{
+                'subKey': 'SBF',
+                'dispName': '分部分项--定额基价设备费'
+            },{
+                'subKey': 'WJJCLF',
+                'dispName': '分部分项--未计价材料费'
+            },{
+                'subKey': 'RGGR',
+                'dispName': '分部分项--人工工日'
+            },{
+                'subKey': 'GCLQDZDJJZJGCF',
+                'dispName': '分部分项--工程量清单中的基价直接工程费'
+            },{
+                'subKey': 'GCLQDZDJJRGF',
+                'dispName': '分部分项--工程量清单中的基价人工费'
+            }
+        ]
+    },{
+        'className': 'CSXM',
+        'type': 'bills',
+        'className': '措施项目',
+        'subBase': [
+            {
+                'subKey': 'CSXMF',
+                'dispName': '措施项目费'
+            },{
+                'subKey': 'ZZCSXMF',
+                'dispName': '组织措施项目费'
+            },{
+                'subKey': 'ZZCSXMDEJJZJGCF',
+                'dispName': '组织措施项目定额基价直接工程费'
+            },{
+                'subKey': 'ZZCSXMDEJJRGF',
+                'dispName': '组织措施项目定额基价人工费'
+            },{
+                'subKey': 'ZZCSXMDEJJCLF',
+                'dispName': '组织措施项目定额基价材料费'
+            },{
+                'subKey': 'ZZCSXMDEJJJXF',
+                'dispName': '组织措施项目定额基价机械费'
+            },{
+                'subKey': 'JSCSXMF',
+                'dispName': '技术措施项目费'
+            },{
+                'subKey': 'JSCSXMDEJJZJGCF',
+                'dispName': '技术措施项目定额基价直接工程费'
+            },{
+                'subKey': 'JSCSXMDEJJRGF',
+                'dispName': '技术措施项目定额基价人工费'
+            },{
+                'subKey': 'JSCSXMDEJJCLF',
+                'dispName': '技术措施项目定额基价材料费'
+            },{
+                'subKey': 'JSCSXMDEJJJXF',
+                'dispName': '技术措施项目定额基价机械费'
+            },{
+                'subKey': 'JSCSXMTZRGF',
+                'dispName': '技术措施项目调整人工费'
+            },{
+                'subKey': 'JSCSXMTZJSRGF',
+                'dispName': '技术措施项目调整机上人工费'
+            },{
+                'subKey': 'JSCSXMZCF',
+                'dispName': '技术措施项目主材费'
+            },{
+                'subKey': 'JSCSXMSBF',
+                'dispName': '技术措施项目设备费'
+            },{
+                'subKey': 'JSCSXMWJJCLF',
+                'dispName': '技术措施项目未计价材料费'
+            },{
+                'subKey': 'JSCSXMQDZDDEJJZJGCF',
+                'dispName': '技术措施项目清单中的定额基价直接工程费'
+            },{
+                'subKey': 'JSCSXMQDZDDEJJRGF',
+                'dispName': '技术措施项目清单中的定额基价人工费'
+            }
+        ]
+    },{
+        'classKey': 'QTXM',
+        'className': '其他项目',
+        'subBase': [
+            {
+                'subKey': 'QTXMF',
+                'dispName': '其他项目费'
+            },{
+                'subKey': 'GF',
+                'dispName': '规费'
+            },{
+                'subKey': 'SJ',
+                'dispName': '税金'
+            },{
+                'subKey': 'SQGCZJ',
+                'dispName': '税前工程造价'
+            }
+        ]
+    },{
+        'classKey': 'FBF',
+        'className': '分包费',
+        'subBase': [
+            {
+                'subKey': 'FBF',
+                'dispName': '分包费'
+            },{
+                'subKey': 'FBJJZJGCF',
+                'dispName': '分包基价直接工程费'
+            },{
+                'subKey': 'FBJJRGF',
+                'dispName': '分包基价人工费'
+            },{
+                'subKey': 'FBJJCLF',
+                'dispName': '分包基价材料费'
+            },{
+                'subKey': 'FBJJJXF',
+                'dispName': '分包基价机械费'
+            },{
+                'subKey': 'FBJJZCF',
+                'dispName': '分包基价主材费'
+            },{
+                'subKey': 'FBJJSBF',
+                'dispName': '分包基价设备费'
+            },{
+                'subKey': 'FBJJRGGR',
+                'dispName': '分包基价人工工日'
+            }
+        ]
+    },{
+
+        'classKey': 'RCJ',
+        'type': 'projectGLJ',
+        'className': '人材机',
+        'subBase': [
+            {
+                'subKey': 'RGJC',
+                'dispName': '人工价差'
+            },{
+                'subKey': 'CLJC',
+                'dispName': '材料价差'
+            },{
+                'subKey': 'JXJC',
+                'dispName': '机械价差'
+            },{
+                'subKey': 'JGRGF',
+                'dispName': '甲供人工费'
+            },{
+                'subKey': 'JGCLF',
+                'dispName': '甲供材料费'
+            },{
+                'ubKey': 'JGJXF',
+                'dispName': '甲供机械费'
+            },{
+                'ubKey': 'JGZCF',
+                'dispName': '甲供主材费'
+            },{
+                'ubKey': 'JGSBF',
+                'dispName': '甲供设备费'
+            },{
+                'subKey': 'JDRGF',
+                'dispName': '甲定人工费'
+            },{
+                'subKey': 'JDCLF',
+                'dispName': '甲供材料费'
+            },{
+                'ubKey': 'JDJXF',
+                'dispName': '甲供机械费'
+            },{
+                'ubKey': 'JDZCF',
+                'dispName': '甲供主材费'
+            },{
+                'ubKey': 'JDSBF',
+                'dispName': '甲供设备费'
+            }
+        ]
+    },{
+        'className': '结算价'
+    },{
+        'className': '变量表'
+    }
+];

+ 86 - 0
test/tmp_data/test_ration_calc/ration_calc_base.js

@@ -0,0 +1,86 @@
+/**
+ * Created by Mai on 2017/7/21.
+ */
+"use strict";
+
+const baseCalc = 0, adjustCalc = 1, budgetCalc = 2, diffCalc = 3,  offerCalc = 4;
+
+const gljType = {
+    // 人工
+    LABOUR: 1,
+    // ==============材料类型=================
+    // 普通材料
+    GENERAL_MATERIAL: 201,
+    // 混凝土
+    CONCRETE: 202,
+    // 砂浆
+    MORTAR: 203,
+    // 配合比
+    MIX_RATIO: 204,
+    // 商品混凝土
+    COMMERCIAL_CONCRETE: 205,
+    // 商品砂浆
+    COMMERCIAL_MORTAR: 206,
+    // ==============材料类型=================
+    // ==============机械类型=================
+    // 普通机械
+    GENERAL_MACHINE: 301,
+    // 机械组成物
+    MACHINE_COMPOSITION: 302,
+    // 机上人工
+    MACHINE_LABOUR: 303,
+    // ==============机械类型=================
+    // 主材
+    MAIN_MATERIAL: 4,
+    // 设备
+    EQUIPMENT: 5
+};
+
+let rationCalcBase = [
+    {
+        'dispName': '定额基价人工费',
+        'calcFun': 'base',
+        'calcType': baseCalc,
+        'gljTypes': [gljType.LABOUR]
+    },{
+        'dispName': '定额基价材料费',
+        'calcFun': 'base',
+        'calcType': baseCalc,
+        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
+    },{
+        'dispName': '定额基价机械费',
+        'calcFun': 'base',
+        'calcType': baseCalc,
+        'gljTypes': [gljType.GENERAL_MACHINE]
+    },{
+        'dispName': '定额基价机上人工费',
+        'calcFun': 'base',
+        'calcType': baseCalc,
+        'gljTypes': [gljType.MACHINE_LABOUR]
+    },{
+        'dispName': '人工费价差',
+        'calcFun': 'diff',
+        'calcType': budgetCalc,
+        'gljTypes': [gljType.LABOUR]
+    },{
+        'dispName': '材料费价差',
+        'calcFun': 'diff',
+        'calcType': diffCalc,
+        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
+    },{
+        'dispName': '机械费价差',
+        'calcFun': 'diff',
+        'calcType': diffCalc,
+        'gljTypes': [gljType.GENERAL_MACHINE]
+    },{
+        'dispName': '主材费',
+        'calcFun': 'budget',
+        'calcType': diffCalc,
+        'gljTypes': [gljType.MAIN_MATERIAL]
+    },{
+        'dispName': '设备费',
+        'calcFun': 'budget',
+        'calcType': budgetCalc,
+        'gljTypes': [gljType.EQUIPMENT]
+    }
+];

+ 3 - 0
web/building_saas/fee_rates/fee_rate.html

@@ -9,11 +9,14 @@
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
     <link rel="stylesheet" href="/web/building_saas/css/main.css">
     <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
+
     <link rel="stylesheet" href="/lib/spreadjs/views/gc.spread.views.dataview.10.0.0.css">
     <script src="/lib/spreadjs/views/common/gc.spread.common.10.0.0.min.js" type="text/javascript"></script>
     <script src="/lib/spreadjs/views/gc.spread.views.dataview.10.0.0.min.js" type="text/javascript"></script>
     <script src="/lib/spreadjs/views/plugins/gc.spread.views.gridlayout.10.0.0.min.js" type="text/javascript"></script>
     <script src="/lib/spreadjs/views/locale/gc.spread.views.dataview.locale.zh-CN.10.0.0.min.js" type="text/javascript"></script>
+    <script>GC.Spread.Views.LicenseKey = "559432293813965#A0y3iTOzEDOzkjMyMDN9UTNiojIklkI1pjIEJCLi4TPB9mM5AFNTd4cvZ7SaJUVy3CWKtWYXx4VVhjMpp7dYNGdx2ia9sEVlZGOTh7NRlTUwkWR9wEV4gmbjBDZ4ElR8N7cGdHVvEWVBtCOwIGW0ZmeYVWVr3mI0IyUiwCMzETN8kzNzYTM0IicfJye&Qf35VfiEzRwEkI0IyQiwiIwEjL6ByUKBCZhVmcwNlI0IiTis7W0ICZyBlIsIyNyMzM5ADI5ADNwcTMwIjI0ICdyNkIsIibj9SbvNmL4N7bjRnch56ciojIz5GRiwiI8+Y9sWY9QmZ0Jyp96uL9v6L0wap9biY9qiq95q197Wr9g+89iojIh94Wiqi";</script>
+
 
     <style>
         .grid { height: 500px; margin:0 10px 10px 40px;}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 20 - 2
web/building_saas/main/html/main.html


+ 45 - 77
web/building_saas/main/js/models/bills_calc.js

@@ -32,71 +32,15 @@ let billsPriceCalcFields = [
     {'type': 'machine', 'unitFeeFlag': billsPriceUnitFeeFlag, 'totalFeeFlag': sumTotalFeeFlag},
 ];
 
-Number.prototype.toDecimal = function (ADigit) {
-    return parseFloat(this.toFixed(ADigit));
-};
-
 let nodeCalcObj = {
     node: null,
     digit: 2,
     field: null,
-    findFee: function (fieldName) {
-        if (!this.node.data.fees) {
-            this.node.data.fees = [];
-        }
-        for (let fee of this.node.data.fees) {
-            if (fee.fieldName === fieldName) {
-                return fee;
-            }
-        }
-        return null;
-    },
-    AddFee: function (fieldName) {
-        let fee = {
-            'fieldName': fieldName,
-            'unitFee': 0,
-            'totalFee': 0,
-            'tenderUnitFee': 0,
-            'tenderTotalFee': 0
-        };
-        this.node.data.fees.push(fee);
-        this.node.data.feesIndex[fieldName] = fee;
-    },
-    checkFields: function (fields) {
-        for (let field of fields) {
-            if (!this.findFee(field.type)) {
-                this.AddFee(field.type);
-            }
-        }
-    },
-    getFee: function (data, fullField) {
-        let fields = fullField.split('.'), value = data;
-        for (let field of fields) {
-            if (value[field]) {
-                value = value[field];
-            } else {
-                return 0;
-            }
-        }
-        return value;
-    },
-    getFeeSplit: function (data, fullFields) {
-        let value = data;
-        for (let field of fullFields) {
-            if (value[field]) {
-                value = value[field];
-            } else {
-                return 0;
-            }
-        }
-        return value;
-    },
+    getFee: calcFees.getFee,
     sumTotalFee: function() {
         let result = 0, child;
         for (child of this.node.children) {
             result += this.getFee(child.data, this.field.totalFee);
-            //result += child.data.feesIndex[this.field.type].totalFee;
-            //result += this.getFeeSplit(child.data, this.field.totalFeeSplit);
         }
         return result;
     },
@@ -111,8 +55,6 @@ let nodeCalcObj = {
     },
     totalFee: function () {
         return this.getFee(this.node.data, this.field.unitFee) * this.getFee(this.node.data, 'quantity');
-        //return this.node.data.feesIndex[this.field.type].unitFee * this.node.data.quantity;
-        //return this.getFeeSplit(this.node.data, this.field.unitFeeSplit) * this.getFee(this.node.data, 'quantity');
     },
     rationContentUnitFee: function () {
         let result = 0, child, qty = this.getFee(this.node.data, 'quantity');
@@ -121,42 +63,57 @@ let nodeCalcObj = {
         }
         for (child of this.node.children) {
             result += (this.getFee(child.data, this.field.unitFee) * this.getFee(child.data, 'quantity') / qty).toDecimal(this.digit);
-            //result += (child.data.feesIndex[this.field.type].unitFee * child.data.quantity / qty).toDecimal(this.digit);
-            //result += (this.getFeeSplit(child.data, this.field.unitFeeSplit) * this.getFee(child.data, 'quantity') / qty).toDecimal(this.digit);
         }
         return result;
     }
 };
 
-class BillsCalc {
+class BillsCalcHelper {
     constructor (project, CalcFlag) {
         this.project = project;
         this.CalcFlag = CalcFlag;
         this.digit = 2;
         switch (this.CalcFlag) {
             case rationContent:
-                this.calcFieldName = rationContentCalcFields;
+                this.calcField = rationContentCalcFields;
                 break;
             case rationPrice:
-                this.calcFieldName = rationPriceCalcFields;
+                this.calcField = rationPriceCalcFields;
                 break;
             case rationPriceConverse:
-                this.calcFieldName = rationPriceConverseCalcFields;
+                this.calcField = rationPriceConverseCalcFields;
                 break;
             case billsPrice:
-                this.calcFieldName = billsPriceCalcFields;
+                this.calcField = billsPriceCalcFields;
                 break;
             default:
-                this.calcFieldName = [];
+                this.calcField = [];
         }
-        this.InitFields(this.calcFieldName);
+        this.InitFields(this.calcField);
     };
 
-    calcLeaf (node, fields) {
+    getBillsGLjs (node) {
+        let rations = this.project.Ration.getBillsSortRation(node.source.getID());
+        let gljs = this.project.ration_glj.getGatherGljArrByRations(rations);
+        for (let glj of gljs) {
+            glj.quantity = (glj.quantity / calcFees.getFee(node.data, 'quantity')).toDecimal(4);
+        }
+        return gljs;
+    };
+    calcRationLeaf (node, fields) {
         nodeCalcObj.node = node;
         nodeCalcObj.digit = this.digit;
-        nodeCalcObj.checkFields(fields);
-        let nodeCalc = nodeCalcObj;
+        calcFees.checkFields(node.data, fields);
+        let nodeCalc = nodeCalcObj, virData= null;
+
+        // 清单单价:套用定额计算程序
+        if (this.CalcFlag === billsPrice) {
+            rationCalcObj.calcGljs = this.getBillsGLjs(node);
+            console.log(rationCalcObj.calcGljs);
+            rationCalcObj.calcFields = rationCalcFields;
+            virData = rationCalcObj.calculate();
+        }
+
         for (let field of fields) {
             nodeCalcObj.field = field;
             switch (field.unitFeeFlag) {
@@ -166,9 +123,9 @@ class BillsCalc {
                 case averageQtyUnitFeeFlag:
                     node.data.feesIndex[field.type].unitFee = nodeCalcObj.averageQty().toDecimal(this.digit);
                     break;
-                // to do billsPriceUnitFeeFlag(套用定额计算程序)
-                // case billsPriceUnitFeeFlag:
-                //     break;
+                 case billsPriceUnitFeeFlag:
+                     node.data.feesIndex[field.type].unitFee = virData[field.type];
+                     break;
                 default:
                     node.data.feesIndex[field.type].unitFee = 0;
             }
@@ -184,9 +141,12 @@ class BillsCalc {
             }
         }
     };
+    calcVolumePriceLeaf (node, fields) {
+
+    };
     calcParent (node, fields) {
         nodeCalcObj.node = node;
-        nodeCalcObj.checkFields(fields);
+        calcFees.checkFields(node.data, fields);
         for (let field of fields) {
             nodeCalcObj.field = field;
             node.data.feesIndex[field.type].totalFee = nodeCalcObj.sumTotalFee().toDecimal(this.digit);
@@ -200,9 +160,17 @@ class BillsCalc {
 
             if (node.source.children.length > 0) {
                 this.calcNodes(node.children);
-                this.calcParent(node, this.calcFieldName);
+                this.calcParent(node, this.calcField);
             } else {
-                this.calcLeaf(node, this.calcFieldName);
+                if (node.children.length > 0) {
+                    if (node.firstChild().sourceType === this.project.Ration.getSourceType()) {
+                        this.calcRationLeaf(node, this.calcField);
+                    } else {
+                        this.calcVolumePriceLeaf(node, this.calcField);
+                    }
+                } else {
+
+                }
             }
         }
     };

+ 61 - 0
web/building_saas/main/js/calc/calc_fees.js

@@ -0,0 +1,61 @@
+/**
+ * Created by Mai on 2017/7/21.
+ */
+
+let calcFees = {
+    findFee: function (data, fieldName) {
+        if (!data.fees) {
+            data.fees = [];
+        }
+        for (let fee of data.fees) {
+            if (fee.fieldName === fieldName) {
+                return fee;
+            }
+        }
+        return null;
+    },
+    AddFee: function (data, fieldName) {
+        let fee = {
+            'fieldName': fieldName,
+            'unitFee': 0,
+            'totalFee': 0,
+            'tenderUnitFee': 0,
+            'tenderTotalFee': 0
+        };
+        data.fees.push(fee);
+        data.feesIndex[fieldName] = fee;
+    },
+    checkFields: function (data, fields) {
+        if (!data.fees) {
+            data.fees = [];
+            data.feesIndex = {};
+        }
+        for (let field of fields) {
+            if (!this.findFee(data, field.type)) {
+                this.AddFee(data, field.type);
+            }
+        }
+    },
+    getFee: function (data, fullField) {
+        let fields = fullField.split('.'), value = data;
+        for (let field of fields) {
+            if (value[field]) {
+                value = value[field];
+            } else {
+                return 0;
+            }
+        }
+        return value;
+    },
+    getFeeSplit: function (data, fullFields) {
+        let value = data;
+        for (let field of fullFields) {
+            if (value[field]) {
+                value = value[field];
+            } else {
+                return 0;
+            }
+        }
+        return value;
+    }
+}

+ 330 - 0
web/building_saas/main/js/calc/ration_calc.js

@@ -0,0 +1,330 @@
+/**
+ * Created by Mai on 2017/7/13.
+ */
+
+/*
+let rationCalcFields = [
+    {
+        type: 'baseDirect', code: "1", name: "基价直接工程费",
+        dispExpr: "1.1+1.2+1.3+1.4", expression: "at('1.1') + at('1.2') + at('1.3') + at('1.4')", compiledExpr: "",
+        statement: "基价人工费+基价材料费+基价机械费+未计价材料费"
+    },
+    {
+        type: 'baseLabour', code: "1.1", name: "基价人工费",
+        dispExpr: "1.1.1+1.1.2", expression: "at('1.1.1') + at('1.1.2')", compiledExpr: "",
+        statement: "定额基价人工费+定额人工单价(基价)调整"
+    },
+    {
+        type: 'rationBaseLabour', code: "1.1.1", name: "定额基价人工费",
+        dispExpr: "定额基价人工费", expression: "定额基价人工费", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'rationAdjustLabour', code: "1.1.2", name: "定额基价人工费(调整后)",
+        dispExpr: "定额基价人工费(调整后)", expression: "定额基价人工费(调整后)", compiledExpr: "",
+        statement: "定额基价人工费(调整后)"
+    },
+    {
+        type: 'baseMaterial', code: "1.2", name: "基价材料费",
+        dispExpr: "定额基价材料费", expression: "定额基价材料费", compiledExpr: "",
+        statement: "定额基价材料费"
+    },
+    {
+        type: 'baseMachine', code: "1.3", name: "基价机械费",
+        dispExpr: "1.3.1+1.3.2", expression: "at('1.3.1') + at('1.3.2')", compiledExpr: "",
+        statement: "定额基价机械费+定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'rationBaseMachine', code: "1.3.1", name: "定额基价机械费",
+        dispExpr: "定额基价机械费", expression: "定额基价机械费", compiledExpr: "",
+        statement: "定额基价机械费"
+    },
+    {
+        type: 'rationBaseMachineLabour', code: "1.3.1.1", name: "其中:定额基价机上人工费",
+        dispExpr: "定额基价机上人工费", expression: "定额基价机上人工费", compiledExpr: "",
+        statement: "定额基价机上人工费"
+    },
+    {
+        type: 'rationBaseMachineLabourFixed', code: "1.3.2", name: "定额基价机上人工费(调整后)",
+        dispExpr: "定额基价机上人工费(调整后)", expression: "定额基价机上人工费(调整后)", compiledExpr: "",
+        statement: "定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'unPriceMaterial', code: "1.4", name: "未计价材料费",
+        dispExpr: "主材费+设备费", expression: "主材费+设备费", compiledExpr: "",
+        statement: "主材费+设备费"
+    },
+    {
+        type: 'management', code: "2", name: "企业管理费",
+        dispExpr: "1.1.1", expression: "at('1.1.1')", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'profit', code: "3", name: "利润",
+        dispExpr: "1.1.1", expression: "at('1.1.1')", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'risk', code: "4", name: "风险因素",
+        dispExpr: "", expression: "0", compiledExpr: "",
+        statement: ""
+    },
+    {
+        type: 'gljDiff', code: "5", name: "人材机价差",
+        dispExpr: "5.1+5.2+5.3", expression: "at('5.1') + at('5.2') + at('5.3')", compiledExpr: "",
+        statement: "人工费价差+材料费价差+机械费价差"
+    },
+    {
+        type: 'labourDiff', code: "5.1", name: "人工费价差",
+        dispExpr: "市场价格人工费-定额基价人工费(调整后)", expression: "市场价格人工费-定额基价人工费(调整后)", compiledExpr: "",
+        statement: "市场价格人工费-定额基价人工费(调整后)"
+    },
+    {
+        type: 'materialDiff', code: "5.2", name: "材料费价差",
+        dispExpr: "市场价格材料费-定额基价材料费", expression: "budget('材料') - base('材料')", compiledExpr: "",
+        statement: "市场价格材料费-定额基价材料费"
+    },
+    {
+        type: 'machineDiff', code: "5.3", name: "机械费价差",
+        dispExpr: "市场价格机械费-定额基价机械费-定额基价机上人工费(调整后)", expression: "市场价格机械费-定额基价机械费-定额基价机上人工费(调整后)", compiledExpr: "",
+        statement: "市场价格机械费-定额基价机械费-定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'common', code: "6", name: "综合单价",
+        dispExpr: "1+2+3+4+5", expression: "at('1') + at('2') + at('3') + at('4') + at('5')", compiledExpr: "",
+        statement: "基价直接工程费+企业管理费+利润+风险因素+人材机价差"
+    }
+];
+*/
+"use strict";
+
+let calcEvaluate = function (expr) {
+    let exp = new Expression('');
+    exp.Expression(expr);
+    return exp.Evaluate();
+}
+
+let rationCalcFields = [
+    {
+        type: 'rationBaseLabour', code: "1.1.1", name: "定额基价人工费",
+        dispExpr: "定额基价人工费", expression: "定额基价人工费", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'rationAdjustLabour', code: "1.1.2", name: "定额基价人工费(调整后)",
+        dispExpr: "定额基价人工费(调整后)", expression: "at('1.1.1')*(1.89-1)", compiledExpr: "",
+        statement: "定额基价人工费(调整后)"
+    },
+    {
+        type: 'baseLabour', code: "1.1", name: "基价人工费",
+        dispExpr: "1.1.1+1.1.2", expression: "at('1.1.1') + at('1.1.2')", compiledExpr: "",
+        statement: "定额基价人工费+定额人工单价(基价)调整"
+    },
+    {
+        type: 'baseMaterial', code: "1.2", name: "基价材料费",
+        dispExpr: "定额基价材料费", expression: "定额基价材料费", compiledExpr: "",
+        statement: "定额基价材料费"
+    },
+    {
+        type: 'rationBaseMachine', code: "1.3.1", name: "定额基价机械费",
+        dispExpr: "定额基价机械费", expression: "定额基价机械费", compiledExpr: "",
+        statement: "定额基价机械费"
+    },
+    {
+        type: 'rationBaseMachineLabour', code: "1.3.1.1", name: "其中:定额基价机上人工费",
+        dispExpr: "定额基价机上人工费", expression: "定额基价机上人工费", compiledExpr: "",
+        statement: "定额基价机上人工费"
+    },
+    {
+        type: 'rationBaseMachineLabourFixed', code: "1.3.2", name: "定额基价机上人工费(调整后)",
+        dispExpr: "定额基价机上人工费(调整后)", expression: "定额基价机上人工费*(1.89-1)", compiledExpr: "",
+        statement: "定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'baseMachine', code: "1.3", name: "基价机械费",
+        dispExpr: "1.3.1+1.3.2", expression: "at('1.3.1') + at('1.3.2')", compiledExpr: "",
+        statement: "定额基价机械费+定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'unPriceMaterial', code: "1.4", name: "未计价材料费",
+        dispExpr: "主材费+设备费", expression: "主材费+设备费", compiledExpr: "",
+        statement: "主材费+设备费"
+    },
+    {
+        type: 'baseDirect', code: "1", name: "基价直接工程费",
+        dispExpr: "1.1+1.2+1.3+1.4", expression: "at('1.1') + at('1.2') + at('1.3') + at('1.4')", compiledExpr: "",
+        statement: "基价人工费+基价材料费+基价机械费+未计价材料费"
+    },
+    {
+        type: 'management', code: "2", name: "企业管理费", feeRate: 7,
+        dispExpr: "1.1.1", expression: "at('1.1.1')", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'profit', code: "3", name: "利润", feeRate: 13,
+        dispExpr: "1.1.1", expression: "at('1.1.1')", compiledExpr: "",
+        statement: "定额基价人工费"
+    },
+    {
+        type: 'risk', code: "4", name: "风险因素",
+        dispExpr: "", expression: "0", compiledExpr: "",
+        statement: ""
+    },
+    {
+        type: 'labourDiff', code: "5.1", name: "人工费价差",
+        dispExpr: "市场价格人工费-定额基价人工费(调整后)", expression: "人工费价差", compiledExpr: "",
+        statement: "市场价格人工费-定额基价人工费(调整后)"
+    },
+    {
+        type: 'materialDiff', code: "5.2", name: "材料费价差",
+        dispExpr: "市场价格材料费-定额基价材料费", expression: "材料费价差", compiledExpr: "",
+        statement: "市场价格材料费-定额基价材料费"
+    },
+    {
+        type: 'machineDiff', code: "5.3", name: "机械费价差",
+        dispExpr: "市场价格机械费-定额基价机械费-定额基价机上人工费(调整后)", expression: "机械费价差", compiledExpr: "",
+        statement: "市场价格机械费-定额基价机械费-定额基价机上人工费(调整后)"
+    },
+    {
+        type: 'gljDiff', code: "5", name: "人材机价差",
+        dispExpr: "5.1+5.2+5.3", expression: "at('5.1') + at('5.2') + at('5.3')", compiledExpr: "",
+        statement: "人工费价差+材料费价差+机械费价差"
+    },
+    {
+        type: 'common', code: "6", name: "综合单价",
+        dispExpr: "1+2+3+4+5", expression: "at('1') + at('2') + at('3') + at('4') + at('5')", compiledExpr: "",
+        statement: "基价直接工程费+企业管理费+利润+风险因素+人材机价差"
+    }
+];
+
+let rationCalcObj = {
+    calcGljs: [],
+    calcFields: null,
+    baseFields: rationCalcBase,
+    getFee: calcFees.getFee,
+    base: function (type) {
+        let value = 0;
+        for (let glj of this.calcGljs) {
+            if (type.indexOf(glj.type) >= 0) {
+                value = value + this.getFee(glj, 'quantity') * this.getFee(glj, 'basePrice');
+            }
+            glj = null;
+        }
+        return value;
+    },
+    adjust: function (type) {
+        let value = 0;
+        for (let glj of this.calcGljs) {
+            if (type.indexOf(glj.type) >= 0) {
+                value = value + this.getFee(glj, 'quantity') * this.getFee(glj, 'adjustPrice');
+            }
+            glj = null;
+        }
+        return value;
+    },
+    budget: function (type) {
+        let value = 0;
+        for (let glj of this.calcGljs) {
+            if (type.indexOf(glj.type) >= 0) {
+                value = value + this.getFee(glj, 'quantity') * this.getFee(glj, 'marketPrice');
+            }
+            glj = null;
+        }
+        return value;
+    },
+    diff: function (type) {
+        let value = 0;
+        for (let glj of this.calcGljs) {
+            if (type.indexOf(glj.type) >= 0) {
+                value = value + (this.getFee(glj, 'quantity') * this.getFee(glj, 'marketPrice')).toDecimal(2) - (this.getFee(glj, 'quantity') * this.getFee(glj, 'adjustPrice')).toDecimal(2);
+            }
+            glj = null;
+        }
+        return value;
+    },
+    at: function (calcCode) {
+        for (let field of this.calcFields) {
+            if (field.code === calcCode) {
+                return field.fee;
+            }
+        }
+        return 0;
+    },
+    initFields: function () {
+        for (let field of this.calcFields) {
+            field.fee = 0;
+        }
+    },
+    getCalcExpr: function (expression) {
+        let calcExpr = expression, reg;
+        for (let base of this.baseFields) {
+            reg = new RegExp(base.dispName);
+            if (reg.test(calcExpr)) {
+                let value = 0;
+                switch (base.calcType) {
+                    case baseCalc:
+                        value = this.base(base.gljTypes);
+                        break;
+                    case adjustCalc:
+                        value = this.adjust(base.gljTypes);
+                        break;
+                    case budgetCalc:
+                        value = this.budget(base.gljTypes);
+                        break;
+                    case diffCalc:
+                        value = this.diff(base.gljTypes);
+                        break;
+                    case offerCalc:
+                        value = this.offer(base.gljTypes);
+                        break;
+                }
+                calcExpr = calcExpr.replace(reg, value);
+            }
+            base = null;
+        }
+        reg = null;
+        let atReg = /at\('([0-9\.]+)'\)/i, strs;
+        while (strs = calcExpr.match(atReg)) {
+            calcExpr = calcExpr.replace(strs[0], this.at(strs[1]));
+        }
+        atReg = null;
+        strs = null;
+        return calcExpr;
+    },
+    calculate () {
+        let result = {};
+        for (let field of this.calcFields) {
+            let calcExpr = this.getCalcExpr(field.expression);
+            let feeRate = (field.feeRate && field.feeRate !== 0) ? field.feeRate : 1;
+            field.fee = (calcEvaluate(calcExpr) * feeRate).toDecimal(2);
+            result[field.type] = field.fee;
+        }
+        return result;
+    }
+};
+
+class RationCalcHelper {
+    constructor (project) {
+        this.project = project;
+    }
+
+    calculate (ration) {
+        rationCalcObj.calcGljs = this.project.ration_glj.getGljArrByRation(ration.ID);
+        rationCalcObj.calcFields = rationCalcFields;
+        calcFees.checkFields(ration, rationCalcFields);
+        for (let field of rationCalcFields) {
+            let calcExpr = rationCalcObj.getCalcExpr(field.expression);
+            let feeRate = (field.feeRate && field.feeRate !== 0) ? field.feeRate : 1;
+            field.fee = (calcEvaluate(calcExpr) * feeRate).toDecimal(2);
+            ration.feesIndex[field.type].unitFee = field.fee;
+            ration.feesIndex[field.type].totalFee = (field.fee * calcFees.getFee(ration, 'quantity')).toDecimal(2);
+            calcExpr = null;
+        }
+    }
+
+    calculateAll () {
+        for (let rationData of this.project.Ration.datas) {
+            this.calculate(rationData);
+        }
+    };
+}

+ 29 - 1
web/building_saas/main/js/controllers/project_controller.js

@@ -68,9 +68,37 @@ ProjectController = {
             this.syncDisplayNewNode(sheetController, newNode);
         }
     },
+    addVolumePrice: function (project, sheetController) {
+        if (!project || !sheetController) { return null; }
+
+        var selected = project.mainTree.selected;
+        var newSource = null, newNode = null;
+        if(selected === null) {
+            return;
+        }
+
+        if (selected.sourceType === project.Bills.getSourceType() && selected.source.children.length === 0) {
+            newSource = project.VolumePrice.insertVolumePrice(selected.source.getID());
+            newNode = project.mainTree.insert(selected.getID(), selected.tree.rootID());
+        } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
+            newSource = project.VolumePrice.insertVolumePrice(selected.source[project.masterField.volumePrice], selected.source);
+            newNode = project.mainTree.insert(selected.getParentID(), selected.getNextSiblingID());
+        }
+        if (newNode) {
+            newNode.source = newSource;
+            newNode.sourceType = project.VolumePrice.getSourceType();
+            newNode.data = newSource;
+
+            this.syncDisplayNewNode(sheetController, newNode);
+        }
+    },
     calculateAll: function (project, sheetController, CalcType) {
+        let date0 = new Date();
+        let ration_calc = new RationCalcHelper(project);
+        ration_calc.calculateAll();
         let date1 = new Date();
-        let calc = new BillsCalc(project, CalcType);
+        console.log(date1 - date0);
+        let calc = new BillsCalcHelper(project, CalcType);
         calc.calcAll();
         calc = null;
         let date2 = new Date();

+ 1 - 1
web/building_saas/main/js/main_ajax.js

@@ -86,4 +86,4 @@ var GetRations = function (proj_id, callback) {
             alert('error ' + textStatus + " " + errorThrown);
         }
     });
-};
+};

+ 21 - 1
web/building_saas/main/js/models/bills.js

@@ -94,7 +94,26 @@ var Bills = {
 
         // 提交数据后的错误处理方法
         bills.prototype.doAfterUpdate = function(err, data){
-            // to do
+            console.log(data)
+            if(data.quantityRefresh){
+                this.refreshDatas(data,'quantity');
+            }
+        };
+
+        bills.prototype.refreshDatas = function(data,fieldName){
+            var dataIndex = _.findIndex(this.datas,function(item) {
+                return item.ID ==data.billID;
+            });
+            this.datas[dataIndex][fieldName] = data[fieldName];
+            if(fieldName=='quantity'){
+                this.datas[dataIndex]['isFromDetail']=1
+            }
+            var controller = projectObj.mainController;
+            var selected = controller.sheet.getSelections();
+            var col =   _.findIndex(BillsGridSetting.cols,function (col) {
+                return col.data.field ==fieldName;
+            });
+            controller.sheet.getCell(selected[0].row,col).value(data[fieldName]);
         };
 
         bills.prototype.getCounterData = function (count) {
@@ -148,6 +167,7 @@ var Bills = {
             var moudles =[ModuleNames.bills,ModuleNames.ration_glj];
             var deleteDatas=[tools.coverseTreeUpdateData(deleteData, this.project.ID()),ration_glj.getDeleteDataByBills(deleteData)];
             project.ration_glj.deleteByBills(deleteData);
+            project.quantity_detail.deleteByBills(deleteData);
             project.pushNow('deleteBILL', moudles, deleteDatas);
             this.delete(deleteData);
 

+ 38 - 26
web/building_saas/main/js/models/cache_tree.js

@@ -54,16 +54,15 @@ var cacheTree = {
                     nodes[i].nextSibling = (i === nodes.length - 1) ? null : nodes[i + 1];
                 }
             },
-            // 在nodes中,从iIndex(包括)开始全部移除
+            // 锟斤拷nodes锟叫o拷锟斤拷iIndex锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷始全锟斤拷锟狡筹拷
             removeNodes: function (tree, parent, iIndex, count) {
                 var children = parent ? parent.children : tree.roots;
                 var pre = (iIndex < 0 || iIndex >= children.length) ? null : children[iIndex].preSibling;
                 var next = (pre && iIndex + count - 1 < children.length) ? children[iIndex + count] : null;
                 if (pre) {
-                    pre.nextSibling = next;
-                }
-                if (next) {
-                    next.preSibling = pre;
+                    pre.setNextSibling(next);
+                } else if (next) {
+                    next.preSibling = null;
                 }
                 if (arguments.length === 4) {
                     children.splice(iIndex, count);
@@ -71,7 +70,7 @@ var cacheTree = {
                     children.splice(iIndex, children.length - iIndex);
                 }
             },
-            // 在nodes中增加addNodes, 位置从index开
+            // 锟斤拷nodes锟斤拷锟斤拷锟斤拷addNodes, 位锟矫达拷index锟斤拷
             addNodes: function (tree, parent, nodes, iIndex) {
                 var children = parent ? parent.children : tree.roots;
                 var pre, next, i;
@@ -84,13 +83,11 @@ var cacheTree = {
                     next = null;
                 }
                 if (pre) {
-                    pre.nextSibling = nodes[0];
-                }
-                nodes[0].preSibling = pre;
-                if (next) {
-                    next.preSibling = nodes[nodes.length - 1];
+                    pre.setNextSibling(nodes[0]);
+                } else {
+                    nodes[0].preSibling = null;
                 }
-                nodes[nodes.length - 1].nextSibling = next;
+                nodes[nodes.length - 1].setNextSibling(next);
                 for (i = 0; i < nodes.length; i++) {
                     if (arguments.length === 4) {
                         children.splice(iIndex + i, 0, nodes[i]);
@@ -104,7 +101,7 @@ var cacheTree = {
 
         var Node = function (tree, id) {
             var ID = id;
-            // 以下的属性,本单元外均不可直接修改
+            // 锟斤拷锟铰碉拷锟斤拷锟皆o拷锟斤拷锟斤拷元锟斤拷锟斤拷锟斤拷锟斤拷直锟斤拷锟睫革拷
             this.tree = tree;
             this.children = [];
 
@@ -129,6 +126,16 @@ var cacheTree = {
             return this.nextSibling ? this.nextSibling.getID() : -1;
         };
 
+        Node.prototype.setParent = function (parent) {
+            this.parent = parent;
+        };
+        Node.prototype.setNextSibling = function (nextSibling) {
+            this.nextSibling = nextSibling;
+            if (nextSibling) {
+                nextSibling.preSibling = this;
+            }
+        }
+
         Node.prototype.firstChild = function () {
             return this.children.length === 0 ? null : this.children[0];
         };
@@ -248,10 +255,13 @@ var cacheTree = {
             var success = false;
             var iIndex = this.siblingIndex(), belongArray = this.parent ? this.parent.children : this.tree.roots, orgPre = this.preSibling;
             if (this.canUpMove()) {
-                orgPre.nextSibling = this.nextSibling;
-                this.preSibling = orgPre.preSibling;
-                orgPre.preSibling = this;
-                this.nextSibling = orgPre;
+                if (orgPre.preSibling) {
+                    orgPre.preSibling.setNextSibling(this);
+                } else {
+                    this.preSibling = null;
+                }
+                orgPre.setNextSibling(this.nextSibling);
+                this.setNextSibling(orgPre);
                 belongArray.splice(iIndex, 1);
                 belongArray.splice(iIndex - 1, 0, this);
                 this.tree.sortTreeItems();
@@ -263,10 +273,13 @@ var cacheTree = {
             var success = false;
             var iIndex = this.siblingIndex(), belongArray = this.parent ? this.parent.children : this.tree.roots, orgNext = this.nextSibling;
             if (this.canDownMove()) {
-                orgNext.preSibling = this.preSibling;
-                this.nextSibling = orgNext.nextSibling;
-                orgNext.nextSibling = this;
-                this.preSibling = orgNext;
+                if (this.preSibling) {
+                    this.preSibling.setNextSibling(orgNext);
+                } else if (orgNext) {
+                    orgNext.preSibling = null;
+                }
+                this.setNextSibling(orgNext.nextSibling);
+                orgNext.setNextSibling(this);
                 belongArray.splice(iIndex, 1);
                 belongArray.splice(iIndex + 1, 0, this);
                 this.tree.sortTreeItems();
@@ -353,10 +366,9 @@ var cacheTree = {
             if (node) {
                 delete this.nodes[this.prefix + node.getID()];
                 if (node.preSibling) {
-                    node.preSibling.nextSibling = node.nextSibling;
-                }
-                if (node.nextSibling) {
-                    node.nextSibling.preSibling = node.preSibling;
+                    node.preSibling.setNextSibling(node.nextSibling);
+                } else if (node.nextSibling) {
+                    node.nextSibling.preSibling = null;
                 }
                 if (node.parent) {
                     node.parent.children.splice(node.siblingIndex(), 1);
@@ -371,4 +383,4 @@ var cacheTree = {
 
         return new Tree(owner);
     }
-};
+};

+ 3 - 1
web/building_saas/main/js/models/main_consts.js

@@ -9,5 +9,7 @@ const ModuleNames = {
     projectGLJ: 'projectGLJ',
     ration_glj:'ration_glj',
     ration_coe:'ration_coe',
-    ration_ass:'ration_ass'
+    ration_ass:'ration_ass',
+    quantity_detail:'quantity_detail',
+    volume_price: 'volume_price'
 };

+ 13 - 1
web/building_saas/main/js/models/project.js

@@ -71,9 +71,11 @@ var PROJECT = {
             this.ration_glj = ration_glj.createNew(this);
             this.ration_coe = ration_coe.createNew(this);
             this.ration_ass = ration_ass.createNew(this);
+            this.quantity_detail = quantity_detail.createNew(this);
             this.FeeRate = FeeRate.createNew(this);
+            this.VolumePrice = VolumePrice.createNew(this);
 
-            this.masterField = {ration: 'billsItemID'};
+            this.masterField = {ration: 'billsItemID', volumePrice: 'billsItemID'};
         };
 
         // prototype用于定义public方法
@@ -101,6 +103,15 @@ var PROJECT = {
                     newNode.data = br[i];
                 }
             };
+            let loadVolumePriceNode = function (cacheNode) {
+                let newNode = null, bv = that.VolumePrice.getBillsSortVolumePrice(cacheNode.source.getID());
+                for (let v of bv) {
+                    newNode = that.mainTree.addNode(cacheNode);
+                    newNode.source = v;
+                    newNode.sourceType = that.VolumePrice.getSourceType();
+                    newNode.data = v;
+                }
+            };
             var loadIdTreeNode = function (nodes, parent) {
                 var newNode, i;
                 for (i = 0; i < nodes.length; i++) {
@@ -111,6 +122,7 @@ var PROJECT = {
 
                     if (nodes[i].children.length === 0) {
                         loadRationNode(that.Ration.datas, newNode);
+                        loadVolumePriceNode(newNode);
                     } else {
                         loadIdTreeNode(nodes[i].children, newNode);
                     }

+ 453 - 0
web/building_saas/main/js/models/quantity_detail.js

@@ -0,0 +1,453 @@
+/**
+ * Created by Mai on 2017/4/1.
+ */
+var quantity_detail = {
+    createNew: function (project) {
+        // 用户定义private方法
+        var tools = {};
+
+        // 所有通过this访问的属性,都不应在此单元外部进行写入操作
+        var quantity_detail = function (proj) {
+            this.gljTree = cacheTree.createNew(this);
+           // this.project = proj;
+            this.datas = [];
+
+            var sourceType = ModuleNames.quantity_detail;
+            this.getSourceType = function () {
+                return sourceType;
+            }
+            proj.registerModule(ModuleNames.quantity_detail, this);
+            this.temList=[];
+        };
+
+        // prototype用于定义public方法
+        quantity_detail.prototype.loadData = function (datas) {
+            this.datas = datas;
+        };
+
+        // 提交数据后返回数据处理
+        quantity_detail.prototype.doAfterUpdate = function(err, data){
+            if(!err){
+                if(data.updateTpye=='ut_update'){
+                    this.refreshAfterUpdate(data);
+                }else if(data.updateTpye=='ut_delete'){
+                    this.refreshAfterDelete(data);
+                } else {
+                    this.refreshAfterSave(data);
+                }
+            }else {
+                alert(err.message);
+                this.refreshSheetData();
+            }
+        };
+        quantity_detail.prototype.refreshAfterSave=function(data){
+            console.log(data);
+            var me = this;
+            if(data.hasOwnProperty('resort')){
+                this.resortData(data.doc,1);
+                _.forEach(data.update_task,function (item) {
+                    me.refreshEachItme(item.query,item.doc);
+                })
+                gljOprObj.detailData.push(data.doc);
+                this.datas.push(data.doc);
+            }else {
+                this.datas.push(data);
+                gljOprObj.detailData.push(data);
+            }
+            gljOprObj.detailData=_.sortBy(gljOprObj.detailData,'seq');
+            this.refreshSheetData();
+        };
+        quantity_detail.prototype.resortData=function(data,req){
+
+            for(var i =0;i<gljOprObj.detailData.length;i++){
+                var item = gljOprObj.detailData[i];
+                if(item.seq>=data.seq){
+                    item.seq=item.seq+req;
+                }
+            }
+        };
+        quantity_detail.prototype.refreshAfterUpdate=function(data){
+            var me = this;
+            var filter_object;
+            if(data.hasOwnProperty('refreshList')){
+                _.forEach(data.refreshList,function (item) {
+                    filter_object= me.refreshEachItme(item.query,item.doc);
+                })
+            }else {
+                filter_object = me.refreshEachItme(data.query,data.doc);
+            }
+            var showList = _.filter(this.datas,filter_object);
+            gljOprObj.detailData=showList;
+            gljOprObj.detailData=_.sortBy(gljOprObj.detailData,'seq');
+            this.refreshSheetData();
+        };
+        quantity_detail.prototype.refreshEachItme = function(query,doc){
+            var detail_list = this.datas;
+            var detail_index= _.findIndex(detail_list,function(detail){
+                return detail.ID==query.ID;
+            })
+            _.forEach(doc, function(n, key) {
+                detail_list[detail_index][key] = n;
+            });
+            var filter_object;
+            if(detail_list[detail_index].hasOwnProperty('rationID')){
+                filter_object={'rationID':detail_list[detail_index].rationID};
+            }else {
+                filter_object={'billID':detail_list[detail_index].billID};
+            }
+            return filter_object;
+        };
+        quantity_detail.prototype.refreshAfterDelete=function(data){
+            var me = this;
+            if(data.doc.seq != gljOprObj.detailData.length - 1){
+                this.resortData(data.doc,-1);
+            }
+            _.forEach(data.update_task,function (item) {
+                me.refreshEachItme(item.query,item.doc);
+            });
+            _.remove(this.datas,{ID:data.doc.ID});
+            _.remove(gljOprObj.detailData,{ID:data.doc.ID});
+            this.refreshSheetData();
+        };
+        quantity_detail.prototype.refreshSheetData=function () {
+            sheetCommonObj.showData(gljOprObj.detailSheet,gljOprObj.detailSetting,gljOprObj.detailData);
+        };
+        quantity_detail.prototype.getUpdateData=function(type,query,doc,callfunction){
+            var updateData = [];
+            var newobj = {
+                'updateType': type,
+                'query': query,
+            }
+            if(doc){
+                newobj['doc']=doc;
+            }
+            if(callfunction){
+                newobj['updateFunction']=callfunction;
+            }
+            updateData.push(newobj);
+            return updateData;
+        };
+        quantity_detail.prototype.saveQuantityDetail=function (args,dataCode) {
+            var doc={};
+            var selected = projectObj.project.mainTree.selected;
+            if(selected.sourceType==ModuleNames.ration){
+                doc.rationID=selected.data.ID;
+            }
+            if(selected.sourceType==ModuleNames.bills){
+                doc.billID=selected.data.ID;
+            }
+            doc.projectID = selected.data.projectID;
+            doc[dataCode]=args.editingText;
+            doc.seq=args.row;
+            if(dataCode=='regex'){
+                if(!this.regexChecking(args.editingText)||!this.referenceChecking(args.editingText,args.row,doc)){
+                    return;
+                }
+                doc.refreshQuantity=true;
+                if(!selected.data.hasOwnProperty('isFromDetail')||selected.data.isFromDetail==0){
+                    var c = confirm("确定要使用工程量明细替换原工程量吗?");
+                    if(!c){
+                        doc.refreshQuantity=false;
+                    }
+                }
+
+            }
+            var updateData
+            if(args.hasOwnProperty("insertRecode")){
+                updateData = this.getUpdateData('ut_update',null,doc,'insertRecode');
+            }else{
+                updateData = this.getUpdateData('ut_create',null,doc);
+            }
+            project.pushNow('saveQuantityDetail',[this.getSourceType()],updateData);
+        };
+        quantity_detail.prototype.insertQuantityDetail = function (row) {
+            var args = {
+                row:row,
+                editingText:1
+            }
+            if(row < gljOprObj.detailData.length){
+                args.insertRecode = true;
+            }
+            this.saveQuantityDetail(args,'isSummation');
+
+        };
+
+        quantity_detail.prototype.deleteQuantityDetail = function (row) {
+           var deleteable = this.checkReference(row);
+           if(deleteable){
+               var recode = gljOprObj.detailData[row];
+               var updateData = this.getUpdateData('ut_delete',null,recode);
+               project.pushNow('deleteQuantityDetail',[this.getSourceType()],updateData);
+           }else {
+               alert("当前行已被引用,不可删除。");
+           }
+
+        };
+        quantity_detail.prototype.checkReference = function (row) {
+            var deleteable = true;
+           for(var i =0;i<gljOprObj.detailData.length;i++){
+                var item = gljOprObj.detailData[i];
+                if(_.includes(item.referenceIndexs,row+1)){
+                    deleteable = false;
+                    break;
+                }
+            }
+            return deleteable;
+        };
+
+        quantity_detail.prototype.moveDown = function (row) {
+            this.swapRow(row);
+        };
+        quantity_detail.prototype.moveUp = function (row) {
+            this.swapRow(row-1);
+        };
+        quantity_detail.prototype.swapRow = function (preRow) {
+            var me = this;
+            var update_task = [];
+            var a_row = gljOprObj.detailData[preRow];//
+            var b_row = gljOprObj.detailData[preRow +1];//
+            var temA = a_row.seq;
+            var temB = b_row.seq;
+            a_row.seq = temB;
+            update_task.push({query:{ID:a_row.ID,projectID:a_row.projectID},doc:{seq:a_row.seq}});
+            b_row.seq = temA;
+            update_task.push({query:{ID:b_row.ID,projectID:b_row.projectID},doc:{seq:b_row.seq}});
+            gljOprObj.detailData.forEach(function (item) {
+                if(_.includes(item.referenceIndexs,temA+1)||_.includes(item.referenceIndexs,temB+1)){
+                    var regex = item.regex;
+                    for (var i=0;i<item.referenceIndexs.length;i++){
+                        if(item.referenceIndexs[i]==temA+1){
+                            regex = me.replaceAll('C'+item.referenceIndexs[i],'B'+(temB+1),regex);
+                            regex = me.replaceAll('c'+item.referenceIndexs[i],'b'+(temB+1),regex);
+                            item.referenceIndexs[i]=temB+1;
+                        }else if(item.referenceIndexs[i]==temB+1){
+                            regex = me.replaceAll('C'+item.referenceIndexs[i],'B'+(temA+1),regex);
+                            regex = me.replaceAll('c'+item.referenceIndexs[i],'b'+(temA+1),regex);
+                            item.referenceIndexs[i]=temA+1;
+                        }
+                    }
+                    regex =  me.replaceAll('B','C',regex);
+                    regex =  me.replaceAll('b','c',regex);
+                    update_task.push({query:{ID:item.ID,projectID:item.projectID},doc:{regex:regex,referenceIndexs:item.referenceIndexs}});
+                }
+            })
+
+            var updateData=[];
+            update_task.forEach(function (task) {
+                updateData.push({'updateType': 'ut_update', 'query': task.query,'doc':task.doc});
+            })
+            project.pushNow('updateQuantityDetail',[this.getSourceType()],[updateData]);
+
+        };
+        quantity_detail.prototype.replaceAll=function(FindText, RepText,str) {
+            let regExp = new RegExp(FindText, "g");
+            return str.replace(regExp, RepText);
+        };
+        quantity_detail.prototype.updateQuantityDetail=function (args,dataCode,recode) {
+            var doc ={};
+            var query={
+                ID:recode.ID,
+                projectID:recode.projectID
+            };
+            var selected = projectObj.project.mainTree.selected;
+            doc[dataCode]=args.editingText;
+            if (dataCode == 'regex') {
+                if(recode.hasOwnProperty('rationID')){
+                    query.rationID=recode.rationID;
+                }else {
+                    query.billID = recode.billID
+                }
+                query.refreshQuantity=true;
+                if(!selected.data.hasOwnProperty('isFromDetail')||selected.data.isFromDetail==0){
+                    var c = confirm("确定要使用工程量明细替换原工程量吗?");
+                    if(!c){
+                        query.refreshQuantity=false;
+                    }
+                }
+                query.index = args.row;
+                this.updateQuantityRegex(query,doc,args)
+            }else {
+                this.normalUpdate(query,doc);
+            }
+        };
+        quantity_detail.prototype.updateQuantityRegex=function(query,doc,args){
+            var needupdate = false;
+            if(args.editingText==null){
+                needupdate =true;
+            }else {
+                args.editingText = _.trim(args.editingText,/\r\n/);
+                if(this.regexChecking(args.editingText)&&this.referenceChecking(args.editingText,args.row,doc)){
+                    needupdate = true;
+                }
+            }
+            if(needupdate){
+                var updateData = this.getUpdateData('ut_update',query,doc,'updateQuantityRegex');
+                project.pushNow('updateQuantityDetail',[this.getSourceType()],updateData);
+            }else {
+                var sheet = subSpread.getActiveSheet();
+                sheet.getCell(args.row,args.col).value(gljOprObj.detailData[args.row].regex);
+            }
+        };
+
+        quantity_detail.prototype.isSummationUpdate=function (args,detailList,newval) {
+            var query={
+                ID:detailList[args.row].ID,
+                projectID:detailList[args.row].projectID
+            };
+            var selected = projectObj.project.mainTree.selected;
+            query.refreshQuantity=true;
+            if(!selected.data.hasOwnProperty('isFromDetail')||selected.data.isFromDetail==0){
+                var c = confirm("确定要使用工程量明细替换原工程量吗?");
+                if(!c){
+                    query.refreshQuantity=false;
+                }
+            }
+            if(detailList[args.row].hasOwnProperty('rationID')){
+                query.rationID=detailList[args.row].rationID;
+            }else {
+                query.billID = detailList[args.row].billID
+            }
+            var doc={
+                isSummation:newval
+            };
+            this.normalUpdate(query,doc);
+        };
+        quantity_detail.prototype.normalUpdate=function(query,doc){
+            var updateData = this.getUpdateData('ut_update',query,doc);
+            project.pushNow('updateQuantityDetail',[this.getSourceType()],updateData);
+        };
+        quantity_detail.prototype.regexChecking=function(text){
+            var regex=/^[0-9Cc\+\-\*\^/\(\)\.]*$/g;
+            if(!regex.test(text)){
+                alert("输入了非法字符,请重新输入!")
+                return false;
+            }else {
+                return true;
+            }
+        };
+        quantity_detail.prototype.referenceChecking=function (text,row,doc) {
+            text = text.toUpperCase();
+            //text= this.replaceSqr(text);
+            var me = this;
+            var refReg = /C\d+/g;
+            var self ='C'+(row+1);
+
+            var refList = text.match(refReg);
+            var invalidate = _.includes(refList,self);
+            var referenceIndexs = [];
+            var indexOut = false;
+            _.forEach(refList,function (item) {
+                var ref_index = parseInt(item.substring(1));
+                if(ref_index>gljOprObj.detailData.length){
+                    indexOut=true;
+                    return;
+                }else {
+                    referenceIndexs.push(ref_index);
+                }
+            });
+            if(indexOut){
+                alert("引用有误,请重新输入!");
+                return false;
+            }
+            referenceIndexs=_.uniq(referenceIndexs);
+            doc.referenceIndexs = referenceIndexs;
+
+            this.temList = referenceIndexs;
+
+            invalidate=this.getAllReferenceList((row+1),referenceIndexs);
+
+            if(invalidate){
+                alert("计算式中产生了循环引用,请重新输入!");
+                return false;
+            }
+            return true;
+        };
+
+        quantity_detail.prototype.getAllReferenceList=function(original,refList){
+            var me =this;
+            var invalidate=false;
+            _.forEach(refList,function (item) {
+                if(me.getReferenceList(item,original)){
+                    invalidate=true;
+                }
+            })
+            return invalidate;
+
+        };
+
+        quantity_detail.prototype.getReferenceList=function(item,original) {
+            var invalidate =false;
+            if(gljOprObj.detailData.length>=item){
+                var recode = gljOprObj.detailData[item - 1];
+                if (recode.referenceIndexs.length > 0) {
+                    if(_.includes(recode.referenceIndexs,original)){
+                        invalidate = true;
+                        return invalidate;
+                    }
+                    this.temList = this.temList.concat(recode.referenceIndexs);
+                    _.forEach(recode.referenceIndex, function (item) {
+                        if(this.getReferenceList(item,original)){
+                            invalidate = true;
+                        }
+                    })
+                }
+            }
+            return invalidate;
+        }
+
+        quantity_detail.prototype.replaceSqr = function(text) {
+            var squarRegex = /\([^\^]+\)\^\d+/g;
+            var sqararr = text.match(squarRegex);
+
+            var squarRegex2 = /C[0-9]+\^\d+|[0-9]+([.]{1}[0-9]+){0,1}\^\d+/g; //匹配没有括号的
+            var sqararr2=text.match(squarRegex2);
+            if(sqararr){
+                text=converSqrByArr(sqararr,text);
+            }
+            if(sqararr2){
+                text=converSqrByArr(sqararr2,text);
+            }
+            return text;
+        };
+        quantity_detail.prototype.converSqrByArr = function (sqararr,text) {
+            var temp = text;
+            sqararr.forEach(function (item) {
+                var arr = item.split('\^');
+                var y = parseInt(arr[1]);
+                var x_arr = [];
+                for (var i = 0; i < y; i++) {
+                    x_arr.push(arr[0]);
+                }
+                var temStr = x_arr.join('*');
+                temp = temp.replace(item, temStr);
+            });
+            return temp;
+        };
+        quantity_detail.prototype.deleteByRation = function(ration){
+            var detail_list = this.datas;
+            var newList =_.filter(detail_list,(d)=>{
+                return d.rationID!=ration.ID;
+            });
+            if(newList!=undefined){
+                this.datas = newList;
+            }
+        };
+        quantity_detail.prototype.deleteByBills=function(deleteData){
+            var detail_list = this.datas;
+            var billIDList = [];
+            for(var i=0;i<deleteData.length;i++){
+                if(deleteData[i].type=='delete'){
+                    billIDList.push(deleteData[i].data.ID);
+                }
+            }
+            var newList =_.filter(detail_list,(d)=>{
+                return !_.includes(billIDList,d.billID);
+            });
+            if(newList!=undefined){
+                this.datas = newList;
+            }
+        };
+        return new quantity_detail(project);
+    }
+
+};

+ 24 - 11
web/building_saas/main/js/models/ration.js

@@ -59,22 +59,34 @@ var Ration = {
             if(data.stateRefresh){
                 this.refreshAdjustState(data);
             }
+            if(data.quantityRefresh){
+                this.refreshQuantity(data);
+            }
         };
         ration.prototype.refreshAdjustState = function(data){
+            this.refreshDatas(data,'adjustState');
+            if(data.hasOwnProperty('name')){
+              this.refreshDatas(data,'name')
+            }
+        };
+        ration.prototype.refreshQuantity = function(data){
+            this.refreshDatas(data,'quantity');
+        };
+        ration.prototype.refreshDatas = function(data,fieldName){
+            var dataIndex = _.findIndex(this.datas,function(item) {
+                return item.ID ==data.rationID;
+            });
+            this.datas[dataIndex][fieldName] = data[fieldName];
+            if(fieldName=='quantity'){
+                this.datas[dataIndex]['isFromDetail']=1
+            }
             var controller = projectObj.mainController;
-             var dataIndex = _.findIndex(this.datas,function(item) {
-                 return item.ID ==data.rationID;
-             });
-            this.datas[dataIndex].adjustState = data.adjustState;
             var selected = controller.sheet.getSelections();
             var col =   _.findIndex(BillsGridSetting.cols,function (col) {
-                return col.data.field =='adjustState';
-            })
-            controller.sheet.getCell(selected[0].row,col).value(data.adjustState);
-            console.log(data.adjustState);
-        }
-
-
+                return col.data.field ==fieldName;
+            });
+            controller.sheet.getCell(selected[0].row,col).value(data[fieldName]);
+        };
         ration.prototype.getTempRationData = function (id, billsID, serialNo) {
             var newData = {'ID': id, 'serialNo': serialNo, projectID: this.project.ID()};
             newData[project.masterField.ration] = billsID;
@@ -139,6 +151,7 @@ var Ration = {
                 if (data.updateType === 'ut_create') {
                     data.updateData.code = std.code;
                     data.updateData.name = std.name;
+                    data.updateData.caption=std.caption;
                     data.updateData.unit = std.unit;
                     data.updateData.libID = std.rationRepId;
                     data.updateData.rationAssList =  projectObj.project.ration_ass.CreateNewAss(std);

+ 6 - 2
web/building_saas/main/js/models/ration_ass.js

@@ -40,7 +40,6 @@ var ration_ass = {
 
         };
         ration_ass.prototype.refreshAfterUpdate=function(data){
-            console.log(data)
             this.updateRation(data.rationID,data.doc);
             //this.updateRationGLJ(data.ration_glj_list);
             gljOprObj.assSheetData[data.editIndex].actualValue = data.actualValue;
@@ -51,7 +50,7 @@ var ration_ass = {
             var index =  _.findIndex(projectObj.project.Ration.datas,(ration)=>{
                 return ration.ID==rationID;
             })
-            projectObj.project.Ration.datas[index].rationAssList = doc.rationAssList
+            projectObj.project.Ration.datas[index].rationAssList = doc.rationAssList;
         };
 
         ration_ass.prototype.updateRationGLJ = function (updateList) {
@@ -104,6 +103,10 @@ var ration_ass = {
         ration_ass.prototype.updateActualValue=function(assList,index,newVal){
             var selected = projectObj.project.mainTree.selected;
             assList[index].actualValue=newVal;
+            var rationname = selected.data.name;
+            if(selected.data.hasOwnProperty('caption')){
+                 rationname = selected.data.caption.replace('%s',newVal);
+            }
             var query = {
                 'ration':selected.data,
                 'editAss':assList[index],
@@ -111,6 +114,7 @@ var ration_ass = {
             };
             var doc ={
                 rationAssList:assList,
+                name:rationname
             };
 
             var updateData = this.getUpdateData('ut_update',query,doc);

+ 0 - 0
web/building_saas/main/js/models/ration_calc.js


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