FormulaCalc.pas 8.5 KB

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