FormulaCalc.pas 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. unit FormulaCalc;
  2. interface
  3. uses
  4. sdIDTree, ZhAPI, Math;
  5. const
  6. ArithmeticCharSet: set of Char = ['0'..'9', '.', '(','[','{', ')',']','}', '+','-','*','/','^', '%', '='];
  7. ExprsBaseCharSet: set of Char = ['h', 't', 'j', 'b', 'q', 'w', 'c', 'k', 'g', 'y', 'f', 'l'];
  8. ExprsExceptCharSet: set of Char = ['h', 't', 'j', 'k', 'g', 'y', 'f', 'c', 'l'];
  9. type
  10. TOnGetValueEvent = function (const ACardinalNum: string): Double of object;
  11. TFormulaCalc = class
  12. private
  13. FTree: TsdIDTree;
  14. FDisplayText: string;
  15. FRecordText: string;
  16. FIndexRange: Integer;
  17. FOnGetValue: TOnGetValueEvent;
  18. FPhaseIndex: Integer;
  19. function GetIndexID(AKeyID: Integer): Integer;
  20. function GetKeyID(AIndexID: Integer): Integer;
  21. procedure ResetDisplayText;
  22. procedure ResetRecordText;
  23. function GetCalcText: string;
  24. function GetValue: Double;
  25. function DoOnGetValue(const ACardinalNum: string): Double;
  26. procedure SetIndexRange(ANodeID: Integer);
  27. public
  28. constructor Create(ATree: TsdIDTree);
  29. destructor Destroy; override;
  30. property DisplayText: string read FDisplayText;
  31. property RecordText: string read FRecordText;
  32. property Value: Double read GetValue;
  33. procedure SetDisplayText(const AText: string; AIndexRange: Integer);
  34. procedure SetRecordText(const AText: string);
  35. property PhaseIndex: Integer read FPhaseIndex write FPhaseIndex;
  36. property OnGetValue: TOnGetValueEvent read FOnGetValue write FOnGetValue;
  37. end;
  38. TPayFormula = class
  39. private
  40. FProjectData: TObject;
  41. FDisplayText: string;
  42. FStartedPrice: Double;
  43. // 签约合同价
  44. function ContractPrice: Double;
  45. // 开工预付款
  46. function StartedSubsist: Double;
  47. // 材料预付款
  48. function MaterialSubsist: Double;
  49. // 本期本阶段结算价
  50. function StageSettlement: Double;
  51. // 100/200/.../900章本期完成计量
  52. function ChapterStageGather(AChapter: Integer): Double;
  53. function GetCalcText: string;
  54. function GetValue: Double;
  55. public
  56. constructor Create(AProjectData: TObject);
  57. destructor Destroy; override;
  58. function Calculate(const ADisplayText: string; AStartedPrice: Double = 0): Double;
  59. end;
  60. implementation
  61. uses
  62. SysUtils, ProjectData, PhaseData, UtilMethods;
  63. { TFormulaCalc }
  64. constructor TFormulaCalc.Create(ATree: TsdIDTree);
  65. begin
  66. FTree := ATree;
  67. end;
  68. destructor TFormulaCalc.Destroy;
  69. begin
  70. inherited;
  71. end;
  72. function TFormulaCalc.DoOnGetValue(const ACardinalNum: string): Double;
  73. begin
  74. Result := 0;
  75. if Assigned(FOnGetValue) then
  76. Result := FOnGetValue(ACardinalNum);
  77. end;
  78. function TFormulaCalc.GetCalcText: string;
  79. var
  80. iPos, iCharIndex: Integer;
  81. sNode: string;
  82. begin
  83. Result := FRecordText;
  84. iPos := Pos('@', Result);
  85. while iPos > 0 do
  86. begin
  87. sNode:= '';
  88. for iCharIndex := iPos + 1 to Length(Result) do
  89. begin
  90. if Result[iCharIndex] in ['0'..'9', 'a'..'d', 'A'..'D'] then
  91. sNode := sNode + Result[iCharIndex]
  92. else
  93. Break;
  94. end;
  95. Result := StringReplace(Result, '@'+sNode, FloatToStr(DoOnGetValue(sNode)), [rfReplaceAll, rfIgnoreCase]);
  96. iPos := Pos('@', Result);
  97. end;
  98. end;
  99. function TFormulaCalc.GetIndexID(AKeyID: Integer): Integer;
  100. begin
  101. with FTree do
  102. Result := IndexOf(FindNode(AKeyID));
  103. end;
  104. function TFormulaCalc.GetKeyID(AIndexID: Integer): Integer;
  105. begin
  106. if AIndexID > -1 then
  107. Result := FTree.Items[AIndexID].ID
  108. else
  109. Result := -1;
  110. end;
  111. function TFormulaCalc.GetValue: Double;
  112. begin
  113. Result := EvaluateExprs(GetCalcText);
  114. end;
  115. procedure TFormulaCalc.ResetDisplayText;
  116. var
  117. iCharIndex, iLength: Integer;
  118. sChar, sID, sFormula: string;
  119. begin
  120. iCharIndex := 1;
  121. iLength := Length(FRecordText);
  122. sFormula := '';
  123. while iCharIndex <= iLength do
  124. begin
  125. if FRecordText[iCharIndex] = '@' then
  126. begin
  127. Inc(iCharIndex);
  128. if FRecordText[iCharIndex] in ['A'..'D', 'a'..'d'] then
  129. begin
  130. sChar := FRecordText[iCharIndex];
  131. Inc(iCharIndex);
  132. sID := '';
  133. while (FRecordText[iCharIndex] in ['0'..'9']) and (iCharIndex <= iLength) do
  134. begin
  135. sID := sID + FRecordText[iCharIndex];
  136. Inc(iCharIndex);
  137. end;
  138. if GetIndexID(StrToIntDef(sID, -1)) > -1 then
  139. sFormula := sFormula + sChar + IntToStr(GetIndexID(StrToIntDef(sID, -1)) + 1);
  140. end;
  141. end
  142. else if FRecordText[iCharIndex] in ['0'..'9', '.', '(','[','{', ')',']','}', '+','-','*','/','^', '%', '='] then
  143. begin
  144. sFormula := sFormula + FRecordText[iCharIndex];
  145. Inc(iCharIndex);
  146. end;
  147. end;
  148. FDisplayText := sFormula;
  149. end;
  150. procedure TFormulaCalc.ResetRecordText;
  151. var
  152. iCharIndex, iLength: Integer;
  153. sChar, sID, sFormula: string;
  154. begin
  155. iCharIndex := 1;
  156. iLength := Length(FDisplayText);
  157. sFormula := '';
  158. while iCharIndex <= iLength do
  159. begin
  160. if FDisplayText[iCharIndex] in ['A'..'D', 'a'..'d'] then
  161. begin
  162. sChar := FDisplayText[iCharIndex];
  163. Inc(iCharIndex);
  164. sID := '';
  165. while FDisplayText[iCharIndex] in ['0'..'9'] do
  166. begin
  167. sID := sID + FDisplayText[iCharIndex];
  168. Inc(iCharIndex);
  169. end;
  170. if FDisplayText[iCharIndex] in ['.', '%'] then
  171. Raise Exception.Create('表达式非法' + FDisplayText + '!')
  172. else if StrToIntDef(sID, -1) > FIndexRange then
  173. Raise Exception.Create('表达式仅可以引用前项清单!')
  174. else if GetKeyID(StrToIntDef(sID, -1) - 1) = -1 then
  175. Raise Exception.Create('表达式非法' + FDisplayText + '!')
  176. else
  177. sFormula := sFormula + '@' + sChar + IntToStr(GetKeyID(StrToIntDef(sID, -1) - 1));
  178. end
  179. else if FDisplayText[iCharIndex] in ['0'..'9', '.', '(','[','{', ')',']','}', '+','-','*','/','^', '%', '='] then
  180. begin
  181. sFormula := sFormula + FDisplayText[iCharIndex];
  182. Inc(iCharIndex);
  183. end;
  184. end;
  185. FRecordText := sFormula
  186. end;
  187. procedure TFormulaCalc.SetDisplayText(const AText: string; AIndexRange: Integer);
  188. begin
  189. SetIndexRange(AIndexRange);
  190. FDisplayText := AText;
  191. FRecordText := '';
  192. ResetRecordText;
  193. end;
  194. procedure TFormulaCalc.SetIndexRange(ANodeID: Integer);
  195. var
  196. stnNode: TsdIDTreeNode;
  197. begin
  198. stnNode := FTree.FindNode(ANodeID);
  199. while Assigned(stnNode.Parent) do
  200. stnNode := stnNode.Parent;
  201. FIndexRange := FTree.IndexOf(stnNode);
  202. end;
  203. procedure TFormulaCalc.SetRecordText(const AText: string);
  204. begin
  205. FRecordText := AText;
  206. FDisplayText := '';
  207. ResetDisplayText;
  208. end;
  209. { TPayFormula }
  210. function TPayFormula.Calculate(const ADisplayText: string; AStartedPrice: Double = 0): Double;
  211. begin
  212. FDisplayText := ADisplayText;
  213. FStartedPrice := AStartedPrice;
  214. Result := GetValue;
  215. end;
  216. function TPayFormula.ChapterStageGather(AChapter: Integer): Double;
  217. begin
  218. with TProjectData(FProjectData).PhaseData do
  219. if Active then
  220. Result := StageData.ChapterStageGather[AChapter]
  221. else
  222. Result := 0;
  223. end;
  224. function TPayFormula.ContractPrice: Double;
  225. begin
  226. Result := TProjectData(FProjectData).ProjProperties.ContractPrice;
  227. end;
  228. constructor TPayFormula.Create(AProjectData: TObject);
  229. begin
  230. FProjectData := AProjectData;
  231. end;
  232. destructor TPayFormula.Destroy;
  233. begin
  234. inherited;
  235. end;
  236. function TPayFormula.GetCalcText: string;
  237. begin
  238. Result := StringReplace(FDisplayText, 'htj', FloatToStr(ContractPrice), [rfReplaceAll, rfIgnoreCase]);
  239. Result := StringReplace(Result, 'kgyfk', FloatToStr(StartedSubsist), [rfReplaceAll, rfIgnoreCase]);
  240. Result := StringReplace(Result, 'clyfk', FloatToStr(MaterialSubsist), [rfReplaceAll, rfIgnoreCase]);
  241. Result := StringReplace(Result, 'ybbqwc', FloatToStr(ChapterStageGather(1)), [rfReplaceAll, rfIgnoreCase]);
  242. Result := StringReplace(Result, 'bqwc', FloatToStr(StageSettlement), [rfReplaceAll, rfIgnoreCase]);
  243. end;
  244. function TPayFormula.GetValue: Double;
  245. begin
  246. Result := TotalPriceRoundTo(EvaluateExprs(GetCalcText));
  247. end;
  248. function TPayFormula.MaterialSubsist: Double;
  249. begin
  250. Result := TProjectData(FProjectData).ProjProperties.MaterialSubsist;
  251. end;
  252. function TPayFormula.StageSettlement: Double;
  253. begin
  254. with TProjectData(FProjectData).PhaseData do
  255. if Active then
  256. begin
  257. // 初次达到起扣金额时,bqwc计数值,取累计完成计量-起扣金额
  258. if FStartedPrice = 0 then
  259. Result := StageData.StageSettlement[1, 4]
  260. else
  261. Result := StageData.StageSettlement[2, 4] - FStartedPrice;
  262. end
  263. else
  264. Result := 0;
  265. end;
  266. function TPayFormula.StartedSubsist: Double;
  267. begin
  268. Result := TProjectData(FProjectData).ProjProperties.StartedSubsisit;
  269. end;
  270. end.