BillsCompileDm.pas 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. unit BillsCompileDm;
  2. interface
  3. uses
  4. BillsDm, StandardBillsFme,
  5. SysUtils, Classes, sdDB, BillsTree, sdIDTree;
  6. type
  7. TBillsCompileData = class(TDataModule)
  8. sdvBillsCompile: TsdDataView;
  9. procedure sdvBillsCompileGetText(var Text: String;
  10. ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  11. DisplayText: Boolean);
  12. procedure sdvBillsCompileAfterValueChanged(AValue: TsdValue);
  13. procedure sdvBillsCompileBeforeValueChange(AValue: TsdValue;
  14. const NewValue: Variant; var Allow: Boolean);
  15. procedure sdvBillsCompileSetText(var Text: String;
  16. ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  17. var Allow: Boolean);
  18. procedure sdvBillsCompileAfterOpen(Sender: TObject);
  19. procedure sdvBillsCompileAfterClose(Sender: TObject);
  20. procedure sdvBillsCompileAfterAddRecord(ARecord: TsdDataRecord);
  21. procedure sdvBillsCompileCurrentChanged(ARecord: TsdDataRecord);
  22. private
  23. FProjectData: TObject;
  24. FBillsData: TBillsData;
  25. FBillsCompileTree: TBillsIDTree;
  26. FOnRecChange: TRecChangeEvent;
  27. function GatherChildrenOrg(ANode: TsdIDTreeNode): Double;
  28. procedure UpdateRecordOrg(ABillsID: Integer; ATotalPrice: Double);
  29. function FindChild(AParentNode, ANode: TsdIDTreeNode): TsdIDTreeNode;
  30. function InsertChild(AParentNode, ANode: TsdIDTreeNode): TsdIDTreeNode;
  31. function CompareNodeCode(ANode, ACompareNode: TsdIDTreeNode): Integer;
  32. function GetNextSiblingID(AParent, ANode: TsdIDTreeNode): Integer;
  33. function IsSameNode(ANode, ACompareNode: TsdIDTreeNode): Boolean;
  34. function GetTopParentNode(ANode: TsdIDTreeNode; ALevel: Integer): TsdIDTreeNode;
  35. procedure AddXmjBillsFromLib(AStdBillsNode: TsdIDTreeNode);
  36. function CanAddGclBills: Boolean;
  37. function GetGclBillsParent(AChildNode: TsdIDTreeNode): TsdIDTreeNode;
  38. procedure AddGclBillsFromLib(AStdBillsNode: TsdIDTreeNode);
  39. procedure DoOnAfterDeleteNode(AParent: TsdIDTreeNode);
  40. function GatherChildren(ANode: TsdIDTreeNode; const AFieldName: string): Double;
  41. procedure UpdateParent(ABillsID: Integer; ADifferTotalPrice: Double; const AFieldName: string);
  42. // 经济指标[与其他节点无关]
  43. procedure CalculateDesignPrice(ANode: TBillsIDTreeNode);
  44. // 施工图原设计[增量]
  45. procedure CalculateOrg(ABillsID: Integer);
  46. // 设计错漏增减[增量]
  47. procedure CalculateMis(ABillsID: Integer);
  48. // 其他错漏增减[增量]
  49. procedure CalculateOth(ABillsID: Integer);
  50. procedure CalculateTotal(ABillsID: Integer);
  51. procedure CalculateLeaf(ANode: TBillsIDTreeNode);
  52. procedure GatherNode(ANode: TBillsIDTreeNode);
  53. procedure CalculateBills(ANode: TsdIDTreeNode);
  54. function GetActive: Boolean;
  55. procedure SetOnRecChange(const Value: TRecChangeEvent);
  56. public
  57. constructor Create(AProjectData: TObject);
  58. destructor Destroy; override;
  59. procedure Open;
  60. procedure Close;
  61. procedure ReConnectTree;
  62. procedure AddBillsFromLib(ANode: TsdIDTreeNode; ABillsType: TBillsType);
  63. procedure AddBillsFromDealBills(ARec: TsdDataRecord);
  64. procedure Calculate(ABillsID: Integer);
  65. procedure CalculateAll;
  66. function GetLeafXmjParentID(ABillsID: Integer): Integer;
  67. procedure ExpandNodeTo(ALevel: Integer);
  68. procedure ExpandXmjNode;
  69. procedure ExpandPegXmjNode;
  70. procedure ReorderChildrenCode(ANode: TsdIDTreeNode);
  71. // 所有解锁的节点全部重新锁定
  72. procedure ReLockBaseData;
  73. property ProjectData: TObject read FProjectData;
  74. property BillsData: TBillsData read FBillsData;
  75. property BillsCompileTree: TBillsIDTree read FBillsCompileTree;
  76. property Active: Boolean read GetActive;
  77. property OnRecChange: TRecChangeEvent read FOnRecChange write SetOnRecChange;
  78. end;
  79. implementation
  80. uses
  81. ProjectData, Math, ZhAPI, UtilMethods, ConstUnit, mDataRecord;
  82. {$R *.dfm}
  83. { TBillsCompileData }
  84. constructor TBillsCompileData.Create(AProjectData: TObject);
  85. begin
  86. inherited Create(nil);
  87. FProjectData := AProjectData;
  88. FBillsData := TProjectData(FProjectData).BillsData;
  89. FBillsCompileTree := TBillsIDTree.Create;
  90. FBillsCompileTree.KeyFieldName := 'ID';
  91. FBillsCompileTree.ParentFieldName := 'ParentID';
  92. FBillsCompileTree.NextSiblingFieldName := 'NextSiblingID';
  93. FBillsCompileTree.AutoCreateKeyID := True;
  94. FBillsCompileTree.AutoExpand := True;
  95. FBillsCompileTree.DataView := sdvBillsCompile;
  96. FBillsCompileTree.SeedID := Max(FBillsCompileTree.SeedID, 100);
  97. FBillsCompileTree.DoOnAfterDeleteNode := DoOnAfterDeleteNode;
  98. FBillsCompileTree.OnReCalcParent := Calculate;
  99. end;
  100. destructor TBillsCompileData.Destroy;
  101. begin
  102. FBillsCompileTree.Free;
  103. inherited;
  104. end;
  105. procedure TBillsCompileData.Open;
  106. begin
  107. sdvBillsCompile.DataSet := TProjectData(FProjectData).BillsData.sddBills;
  108. sdvBillsCompile.Open;
  109. FBillsCompileTree.SeedID := Max(FBillsCompileTree.SeedID, 100);
  110. end;
  111. procedure TBillsCompileData.ReConnectTree;
  112. begin
  113. FBillsCompileTree.DataView := nil;
  114. FBillsCompileTree.DataView := sdvBillsCompile;
  115. end;
  116. procedure TBillsCompileData.sdvBillsCompileGetText(var Text: String;
  117. ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  118. DisplayText: Boolean);
  119. procedure GetEditText;
  120. var
  121. sFormula: string;
  122. begin
  123. if SameText('OrgQuantity', AColumn.FieldName) then
  124. sFormula := ARecord.ValueByName('OrgFormula').AsString
  125. else if SameText('MisQuantity', AColumn.FieldName) then
  126. sFormula := ARecord.ValueByName('MisFormula').AsString
  127. else if SameText('OthQuantity', AColumn.FieldName) then
  128. sFormula := ARecord.ValueByName('OthFormula').AsString
  129. else
  130. sFormula := '';
  131. if sFormula <> '' then
  132. Text := sFormula;
  133. end;
  134. procedure GetDisplayText;
  135. begin
  136. if (Pos('Price', AColumn.FieldName) > 0) or
  137. (Pos('Quantity', AColumn.FieldName) > 0) then
  138. begin
  139. if Assigned(AValue) and (AValue.AsFloat = 0) then
  140. Text := '';
  141. end;
  142. end;
  143. begin
  144. if DisplayText then
  145. GetDisplayText
  146. else
  147. GetEditText;
  148. end;
  149. procedure TBillsCompileData.ExpandNodeTo(ALevel: Integer);
  150. begin
  151. BillsCompileTree.ExpandLevel := ALevel;
  152. end;
  153. procedure TBillsCompileData.ExpandXmjNode;
  154. var
  155. iIndex: Integer;
  156. stnNode: TsdIDTreeNode;
  157. begin
  158. for iIndex := 0 to BillsCompileTree.Count - 1 do
  159. begin
  160. stnNode := BillsCompileTree.Items[iIndex];
  161. if (stnNode.ParentID <> -1) then
  162. stnNode.Parent.Expanded := stnNode.Rec.ValueByName('B_Code').AsString = '';
  163. end;
  164. end;
  165. procedure TBillsCompileData.sdvBillsCompileAfterValueChanged(
  166. AValue: TsdValue);
  167. procedure ResetChildrenLockedInfo(ANode: TsdIDTreeNode; ALockedInfo: Boolean);
  168. var
  169. iChild: Integer;
  170. begin
  171. if not Assigned(ANode) then Exit;
  172. if ANode.Rec.ValueByName('LockedLevel').AsBoolean then
  173. ANode.Rec.ValueByName('LockedInfo').AsBoolean := ALockedInfo;
  174. if ANode.HasChildren then
  175. for iChild := 0 to ANode.ChildCount - 1 do
  176. ResetChildrenLockedInfo(ANode.ChildNodes[iChild], ALockedInfo);
  177. end;
  178. var
  179. stnNode: TsdIDTreeNode;
  180. begin
  181. if SameText(AValue.FieldName, 'OrgQuantity') or
  182. SameText(AValue.FieldName, 'OrgTotalPrice') then
  183. CalculateOrg(AValue.Owner.ValueByName('ID').AsInteger)
  184. else if SameText(AValue.FieldName, 'MisQuantity') or
  185. SameText(AValue.FieldName, 'MisTotalPrice') then
  186. CalculateMis(AValue.Owner.ValueByName('ID').AsInteger)
  187. else if SameText(AValue.FieldName, 'OthQuantity') or
  188. SameText(AValue.FieldName, 'OthTotalPrice') then
  189. CalculateOth(AValue.Owner.ValueByName('ID').AsInteger)
  190. else if SameText(AValue.FieldName, 'Price') or
  191. SameText(AValue.FieldName, 'DgnQuantity1') then
  192. CalculateTotal(AValue.Owner.ValueByName('ID').AsInteger);
  193. if (AValue.FieldName = 'LockedInfo') then
  194. begin
  195. stnNode := BillsCompileTree.FindNode(AValue.Owner.ValueByName('ID').AsInteger);
  196. ResetChildrenLockedInfo(stnNode, AValue.AsBoolean);
  197. end;
  198. end;
  199. function TBillsCompileData.GatherChildrenOrg(ANode: TsdIDTreeNode): Double;
  200. var
  201. iChild: Integer;
  202. begin
  203. if ANode = nil then Exit;
  204. if ANode.HasChildren and Assigned(ANode.FirstChild) then
  205. begin
  206. Result := 0;
  207. for iChild := 0 to ANode.ChildCount - 1 do
  208. Result := Result + GatherChildrenOrg(ANode.ChildNodes[iChild]);
  209. Result := TotalPriceRoundTo(Result);
  210. end
  211. else
  212. if Assigned(ANode.Rec) and Assigned(ANode.Rec.ValueByName('TotalPrice')) then
  213. Result := ANode.Rec.ValueByName('TotalPrice').AsFloat
  214. else
  215. Result := 0;
  216. end;
  217. procedure TBillsCompileData.UpdateRecordOrg(ABillsID: Integer;
  218. ATotalPrice: Double);
  219. var
  220. stnNode: TsdIDTreeNode;
  221. begin
  222. stnNode := BillsCompileTree.FindNode(ABillsID);
  223. if not Assigned(stnNode) then Exit;
  224. with stnNode.Rec do
  225. begin
  226. ValueByName('TotalPrice').AsFloat := TotalPriceRoundTo(
  227. ValueByName('TotalPrice').AsFloat + ATotalPrice);
  228. if ValueByName('DgnQuantity1').AsFloat <> 0 then
  229. ValueByName('DgnPrice').AsFloat := PriceRoundTo(
  230. ValueByName('TotalPrice').AsFloat/ValueByName('DgnQuantity1').AsFloat);
  231. end;
  232. UpdateRecordOrg(stnNode.ParentID, ATotalPrice);
  233. end;
  234. procedure TBillsCompileData.sdvBillsCompileBeforeValueChange(
  235. AValue: TsdValue; const NewValue: Variant; var Allow: Boolean);
  236. begin
  237. // 清单编号和项目节编号不可同时存在
  238. if SameText(AValue.FieldName, 'Code') then
  239. begin
  240. if AValue.Owner.ValueByName('B_Code').AsString <> '' then
  241. DataSetErrorMessage(Allow, '已存在清单编号,不可输入项目节编号!');
  242. end
  243. else if SameText(AValue.FieldName, 'B_Code') then
  244. begin
  245. if AValue.Owner.ValueByName('Code').AsString <> '' then
  246. DataSetErrorMessage(Allow, '已存在项目节编号,不可输入清单编号!');
  247. end
  248. //
  249. else if SameText(AValue.FieldName, 'Price') then
  250. begin
  251. if AValue.Owner.ValueByName('AddGatherTotalPrice').AsFloat <> 0 then
  252. DataSetErrorMessage(Allow, '该清单已经开始计量,不可修改单价!');
  253. end
  254. // 变更清单不可修改0号台账数据
  255. else if SameText(AValue.FieldName, 'OrgQuantity') or
  256. SameText(AValue.FieldName, 'MisQuantity') or
  257. SameText(AValue.FieldName, 'OthQuantity') then
  258. begin
  259. if AValue.Owner.ValueByName('IsMeasureAdd').AsBoolean then
  260. DataSetErrorMessage(Allow, '变更清单不可填写0号台账数量与金额');
  261. end;
  262. if not Allow then Exit;
  263. if SameText(AValue.FieldName, 'OrgQuantity') or
  264. SameText(AValue.FieldName, 'MisQuantity') or
  265. SameText(AValue.FieldName, 'OthQuantity') or
  266. SameText(AValue.FieldName, 'OrgTotalPrice') or
  267. SameText(AValue.FieldName, 'MisTotalPrice') or
  268. SameText(AValue.FieldName, 'OthTotalPrice') or
  269. SameText(AValue.FieldName, 'Price') then
  270. begin
  271. TBillsRecord(AValue.Owner).CacheOrgTP := AValue.Owner.ValueByName('OrgTotalPrice').AsFloat;
  272. TBillsRecord(AValue.Owner).CacheMisTP := AValue.Owner.ValueByName('MisTotalPrice').AsFloat;
  273. TBillsRecord(AValue.Owner).CacheOthTP := AValue.Owner.ValueByName('OthTotalPrice').AsFloat;
  274. end;
  275. end;
  276. procedure TBillsCompileData.CalculateAll;
  277. procedure RecursiveCalc(ANode: TsdIDTreeNode);
  278. begin
  279. if not Assigned(ANode) then Exit;
  280. if ANode.HasChildren then
  281. begin
  282. RecursiveCalc(ANode.FirstChild);
  283. GatherNode(TBillsIDTreeNode(ANode));
  284. end
  285. else
  286. CalculateLeaf(TBillsIDTreeNode(ANode));
  287. RecursiveCalc(ANode.NextSibling);
  288. end;
  289. procedure BeginCalc;
  290. begin
  291. sdvBillsCompile.BeforeValueChange := nil;
  292. sdvBillsCompile.AfterValueChanged := nil;
  293. end;
  294. procedure EndCalc;
  295. begin
  296. sdvBillsCompile.BeforeValueChange := sdvBillsCompileBeforeValueChange;
  297. sdvBillsCompile.AfterValueChanged := sdvBillsCompileAfterValueChanged;
  298. end;
  299. begin
  300. BeginCalc;
  301. try
  302. RecursiveCalc(BillsCompileTree.FirstNode);
  303. finally
  304. EndCalc;
  305. end;
  306. end;
  307. procedure TBillsCompileData.AddBillsFromLib(ANode: TsdIDTreeNode;
  308. ABillsType: TBillsType);
  309. begin
  310. if not Assigned(ANode) then Exit;
  311. if ABillsType = btXm then
  312. AddXmjBillsFromLib(ANode)
  313. else if ABillsType = btGcl then
  314. AddGclBillsFromLib(ANode);
  315. end;
  316. procedure TBillsCompileData.AddGclBillsFromLib(
  317. AStdBillsNode: TsdIDTreeNode);
  318. var
  319. stnParent, stnStdNode: TsdIDTreeNode;
  320. iLevel: Integer;
  321. begin
  322. if not CanAddGclBills then
  323. raise Exception.Create('当前节点下不可添加工程量清单!');
  324. stnParent := GetGclBillsParent(BillsCompileTree.Selected);
  325. if TBillsIDTreeNode(stnParent).HasLedger or
  326. (not stnParent.HasChildren and TBillsIDTreeNode(stnParent).HasMeasure) then
  327. raise Exception.Create('当前节点不可添加工程量清单!');
  328. stnStdNode := GetTopParentNode(AStdBillsNode, AStdBillsNode.Level);
  329. for iLevel := 1 to AStdBillsNode.Level + 1 do
  330. begin
  331. if stnStdNode.Rec.ValueByName('B_Code').AsString <> '' then
  332. if FindChild(stnParent, stnStdNode) <> nil then
  333. stnParent := FindChild(stnParent, stnStdNode)
  334. else
  335. stnParent := InsertChild(stnParent, stnStdNode);
  336. stnStdNode := GetTopParentNode(AStdBillsNode, AStdBillsNode.Level - iLevel);
  337. end;
  338. end;
  339. procedure TBillsCompileData.AddXmjBillsFromLib(
  340. AStdBillsNode: TsdIDTreeNode);
  341. var
  342. stnStdNode, stnCurNode: TsdIDTreeNode;
  343. iLevel: Integer;
  344. begin
  345. stnStdNode := GetTopParentNode(AStdBillsNode, AStdBillsNode.Level);
  346. stnCurNode := nil;
  347. for iLevel := 1 to AStdBillsNode.Level + 1 do
  348. begin
  349. if FindChild(stnCurNode, stnStdNode) <> nil then
  350. stnCurNode := FindChild(stnCurNode, stnStdNode)
  351. else
  352. begin
  353. if TBillsIDTreeNode(stnCurNode).HasLedger or
  354. (not stnCurNode.HasChildren and TBillsIDTreeNode(stnCurNode).HasMeasure) then
  355. raise Exception.Create('不可添加该项目节数据!')
  356. else
  357. stnCurNode := InsertChild(stnCurNode, stnStdNode);
  358. end;
  359. stnStdNode := GetTopParentNode(AStdBillsNode, AStdBillsNode.Level - iLevel);
  360. end;
  361. end;
  362. function TBillsCompileData.CanAddGclBills: Boolean;
  363. function CheckChildrenHasXmj(ANode: TsdIDTreeNode): Boolean;
  364. var
  365. stnCurNode: TsdIDTreeNode;
  366. begin
  367. Result := False;
  368. if not ANode.HasChildren then Exit;
  369. stnCurNode := ANode.FirstChild;
  370. while not Result and Assigned(stnCurNode) do
  371. begin
  372. Result := Result or (stnCurNode.Rec.ValueByName('Code').AsString <> '');
  373. if stnCurNode.HasChildren then
  374. Result := Result or CheckChildrenHasXmj(stnCurNode);
  375. stnCurNode := stnCurNode.NextSibling;
  376. end;
  377. end;
  378. function CheckParentIsXmj(ANode: TsdIDTreeNode): Boolean;
  379. begin
  380. Result := False;
  381. if not Assigned(ANode) then Exit;
  382. Result := ANode.Rec.ValueByName('Code').AsString <> '';
  383. if not Result then
  384. Result := Result or CheckParentIsXmj(ANode.Parent);
  385. end;
  386. begin
  387. Result := False;
  388. if not Assigned(BillsCompileTree.Selected) then Exit;
  389. Result := CheckParentIsXmj(BillsCompileTree.Selected)
  390. and not CheckChildrenHasXmj(BillsCompileTree.Selected);
  391. end;
  392. function TBillsCompileData.CompareNodeCode(ANode,
  393. ACompareNode: TsdIDTreeNode): Integer;
  394. begin
  395. if ANode.Rec.ValueByName('Code').AsString <> '' then
  396. Result := CompareCode(ANode.Rec.ValueByName('Code').AsString,
  397. ACompareNode.Rec.ValueByName('Code').AsString)
  398. else if ANode.Rec.ValueByName('B_Code').AsString <> '' then
  399. Result := CompareCode(ANode.Rec.ValueByName('B_Code').AsString,
  400. ACompareNode.Rec.ValueByName('B_Code').AsString);
  401. end;
  402. function TBillsCompileData.GetGclBillsParent(
  403. AChildNode: TsdIDTreeNode): TsdIDTreeNode;
  404. begin
  405. if AChildNode.Rec.ValueByName('B_Code').AsString <> '' then
  406. Result := GetGclBillsParent(AChildNode.Parent)
  407. else
  408. Result := AChildNode;
  409. end;
  410. function TBillsCompileData.GetNextSiblingID(AParent,
  411. ANode: TsdIDTreeNode): Integer;
  412. var
  413. stnCurNode: TsdIDTreeNode;
  414. begin
  415. Result := -1;
  416. if Assigned(AParent) then
  417. stnCurNode := AParent.FirstChild
  418. else
  419. stnCurNode := BillsCompileTree.FirstNode;
  420. if not Assigned(stnCurNode) then Exit;
  421. while Assigned(stnCurNode) do
  422. begin
  423. if CompareNodeCode(stnCurNode, ANode) >= 0 then
  424. begin
  425. Result := stnCurNode.ID;
  426. Exit;
  427. end;
  428. stnCurNode := stnCurNode.NextSibling;
  429. end;
  430. end;
  431. function TBillsCompileData.GetTopParentNode(ANode: TsdIDTreeNode;
  432. ALevel: Integer): TsdIDTreeNode;
  433. begin
  434. Result := ANode;
  435. while Assigned(Result.Parent) and (Result.Level + ALevel > ANode.Level) do
  436. Result := Result.Parent;
  437. end;
  438. function TBillsCompileData.IsSameNode(ANode,
  439. ACompareNode: TsdIDTreeNode): Boolean;
  440. begin
  441. Result := (ANode.Rec.ValueByName('Code').AsString = ACompareNode.Rec.ValueByName('Code').AsString)
  442. and (ANode.Rec.ValueByName('B_Code').AsString = ACompareNode.Rec.ValueByName('B_Code').AsString)
  443. and (ANode.Rec.ValueByName('Name').AsString = ACompareNode.Rec.ValueByName('Name').AsString);
  444. end;
  445. function TBillsCompileData.FindChild(AParentNode,
  446. ANode: TsdIDTreeNode): TsdIDTreeNode;
  447. function FindSibling(AFirstNode, ANode: TsdIDTreeNode): TsdIDTreeNode;
  448. var
  449. stnCurNode: TsdIDTreeNode;
  450. begin
  451. Result := nil;
  452. stnCurNode := AFirstNode;
  453. while Assigned(stnCurNode) and not Assigned(Result) do
  454. begin
  455. if IsSameNode(ANode, stnCurNode) then
  456. Result := stnCurNode;
  457. stnCurNode := stnCurNode.NextSibling;
  458. end;
  459. end;
  460. begin
  461. if not Assigned(AParentNode) then
  462. Result := FindSibling(BillsCompileTree.FirstNode, ANode)
  463. else
  464. Result := FindSibling(AParentNode.FirstChild, ANode);
  465. end;
  466. function TBillsCompileData.InsertChild(AParentNode,
  467. ANode: TsdIDTreeNode): TsdIDTreeNode;
  468. var
  469. iNextSiblingID: Integer;
  470. begin
  471. iNextSiblingID := GetNextSiblingID(AParentNode, ANode);
  472. if Assigned(AParentNode) then
  473. Result := BillsCompileTree.Add(AParentNode.ID, iNextSiblingID)
  474. else
  475. Result := BillsCompileTree.Add(-1, iNextSiblingID);
  476. Result.Rec.ValueByName('Code').AsString := ANode.Rec.ValueByName('Code').AsString;
  477. Result.Rec.ValueByName('B_Code').AsString := ANode.Rec.ValueByName('B_Code').AsString;
  478. Result.Rec.ValueByName('Name').AsString := ANode.Rec.ValueByName('Name').AsString;
  479. Result.Rec.ValueByName('Units').AsString := ANode.Rec.ValueByName('Unit').AsString;
  480. end;
  481. procedure TBillsCompileData.sdvBillsCompileSetText(var Text: String;
  482. ARecord: TsdDataRecord; AValue: TsdValue; AColumn: TsdViewColumn;
  483. var Allow: Boolean);
  484. procedure SetTextErrorHint(const AHint: string);
  485. begin
  486. ErrorMessage(AHint);
  487. Allow := False;
  488. end;
  489. procedure SetQuantity(const AFieldName: string);
  490. var
  491. sPre: string;
  492. begin
  493. sPre := StringReplace(AFieldName, 'Quantity', '', [rfIgnoreCase, rfReplaceAll]);
  494. if CheckStringNull(Text) or CheckNumeric(Text) then
  495. Text := FloatToStr(QuantityRoundTo(StrToFloatDef(Text, 0)))
  496. else
  497. begin
  498. ARecord.ValueByName(sPre + 'Formula').AsString := Text;
  499. Text := FloatToStr(QuantityRoundTo(EvaluateExprs(Text)));
  500. end;
  501. ARecord.ValueByName('CalcType').AsInteger := 0;
  502. end;
  503. procedure SetTotalPrice(const AFieldName: string);
  504. var
  505. sPre: string;
  506. begin
  507. sPre := StringReplace(AFieldName, 'TotalPrice', '', [rfIgnoreCase, rfReplaceAll]);
  508. if CheckStringNull(Text) or CheckNumeric(Text) then
  509. Text := FloatToStr(TotalPriceRoundTo(StrToFloatDef(Text, 0)))
  510. else
  511. begin
  512. ARecord.ValueByName(sPre + 'Formula').AsString := Text;
  513. Text := FloatToStr(TotalPriceRoundTo(EvaluateExprs(Text)));
  514. end;
  515. ARecord.ValueByName('CalcType').AsInteger := 1;
  516. end;
  517. procedure SetDgnQuantity;
  518. begin
  519. Text := FloatToStr(QuantityRoundTo(StrToFloatDef(Text, 0)));
  520. end;
  521. procedure SetPrice;
  522. begin
  523. Text := FloatToStr(PriceRoundTo(StrToFloatDef(Text, 0)));
  524. end;
  525. procedure DoCurChanged;
  526. begin
  527. if SameText(AColumn.FieldName, 'OrgQuantity') or
  528. SameText(AColumn.FieldName, 'MisQuantity') or
  529. SameText(AColumn.FieldName, 'OthQuantity')then
  530. SetQuantity(AColumn.FieldName)
  531. else if SameText(AColumn.FieldName, 'OrgTotalPrice') or
  532. SameText(AColumn.FieldName, 'MisTotalPrice') or
  533. SameText(AColumn.FieldName, 'OthTotalPrice') then
  534. SetTotalPrice(AColumn.FieldName)
  535. else if Pos('DgnQuantity', AColumn.FieldName) = 1 then
  536. SetDgnQuantity
  537. else if SameText(AColumn.FieldName, 'Price') then
  538. SetPrice;
  539. end;
  540. procedure CheckLockedData;
  541. begin
  542. if SameText(AColumn.FieldName, 'Code') or
  543. SameText(AColumn.FieldName, 'B_Code') or
  544. SameText(AColumn.FieldName, 'Name') or
  545. SameText(AColumn.FieldName, 'Units') or
  546. SameText(AColumn.FieldName, 'Price') or
  547. SameText(AColumn.FieldName, 'OrgQuantity') or
  548. SameText(AColumn.FieldName, 'OrgTotalPrice') or
  549. SameText(AColumn.FieldName, 'MisQuantity') or
  550. SameText(AColumn.FieldName, 'MisTotalPrice') or
  551. SameText(AColumn.FieldName, 'OthQuantity') or
  552. SameText(AColumn.FieldName, 'OthTotalPrice') or
  553. SameText(AColumn.FieldName, 'DrawingCode')then
  554. if ARecord.ValueByName('LockedInfo').AsBoolean then
  555. SetTextErrorHint('清单信息已被锁定,不允许修改编号、名称、单位、清单单价、0号台账数量与金额、图号!');
  556. end;
  557. procedure CheckNodeWritable;
  558. var
  559. vNode: TBillsIDTreeNode;
  560. iCreatePhase: Integer;
  561. begin
  562. if not Allow then Exit;
  563. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ARecord.ValueByName('ID').AsInteger));
  564. iCreatePhase := vNode.Rec.ValueByName('CreatePhaseID').AsInteger;
  565. if vNode.HasChildren then
  566. begin
  567. if Text = '' then
  568. Exit
  569. else if ((Pos('Quantity', AColumn.FieldName) > 0) and (Pos('Dgn', AColumn.FieldName) <=0)) or
  570. (Pos('TotalPrice', AColumn.FieldName) > 0) then
  571. SetTextErrorHint('该清单有子计算项,不能直接修改!')
  572. else if (Pos('Price', AColumn.FieldName) > 0) then
  573. SetTextErrorHint('仅最底层清单可输入单价!');
  574. if not Allow then Exit;
  575. end
  576. else
  577. begin
  578. if SameText('OrgTotalPrice', AColumn.FieldName) or
  579. SameText('MisTotalPrice', AColumn.FieldName) or
  580. SameText('OthTotalPrice', AColumn.FieldName) then
  581. begin
  582. if not vNode.TotalPriceEnable then
  583. SetTextErrorHint('该清单不可直接输入金额,如需直接输入金额,请先清空所有数量、单价!');
  584. end;
  585. if not Allow then Exit;
  586. if SameText('Price', AColumn.FieldName) or
  587. SameText('OrgQuantity', AColumn.FieldName) or
  588. SameText('MisQuantity', AColumn.FieldName) or
  589. SameText('OthQuantity', AColumn.FieldName) then
  590. begin
  591. if not vNode.CountPriceEnable then
  592. SetTextErrorHint('该清单不可输入数量单价,如需使用数量×单价计算,请先清空所有直接输入的金额!');
  593. end;
  594. if not Allow then Exit;
  595. end;
  596. if SameText('Code', AColumn.FieldName) or
  597. SameText('B_Code', AColumn.FieldName) or
  598. SameText('Name', AColumn.FieldName) or
  599. SameText('Units', AColumn.FieldName) or
  600. SameText('Price', AColumn.FieldName) then
  601. if TBillsIDTreeNode(vNode).HasMeasure then
  602. SetTextErrorHint('该清单已经计量,不可修改清单编号');
  603. end;
  604. begin
  605. if not Assigned(AValue) then Exit;
  606. // 修改后数据与原数据相同则不提交
  607. if (AValue.AsString = Text) or ((AValue.AsFloat = 0) and (Text = '')) then Exit;
  608. CheckLockedData;
  609. if not Allow then Exit;
  610. CheckNodeWritable;
  611. if not Allow then Exit;
  612. Text := Trim(Text);
  613. if Pos('=', Text) = 1 then
  614. Text := Copy(Text, 2, Length(Text) - 1);
  615. DoCurChanged;
  616. end;
  617. function TBillsCompileData.GetActive: Boolean;
  618. begin
  619. Result := sdvBillsCompile.Active;
  620. end;
  621. function TBillsCompileData.GetLeafXmjParentID(ABillsID: Integer): Integer;
  622. var
  623. stnNode: TsdIDTreeNode;
  624. begin
  625. stnNode := BillsCompileTree.FindNode(ABillsID);
  626. Result := GetGclBillsParent(stnNode).ID;
  627. end;
  628. procedure TBillsCompileData.sdvBillsCompileAfterOpen(Sender: TObject);
  629. begin
  630. BillsCompileTree.Active := True;
  631. end;
  632. procedure TBillsCompileData.sdvBillsCompileAfterClose(Sender: TObject);
  633. begin
  634. BillsCompileTree.Active := False;
  635. end;
  636. procedure TBillsCompileData.ReorderChildrenCode(ANode: TsdIDTreeNode);
  637. var
  638. iChild: Integer;
  639. sParentCode: string;
  640. stnChild: TsdIDTreeNode;
  641. begin
  642. if not Assigned(ANode) then Exit;
  643. sParentCode := ANode.Rec.ValueByName('Code').AsString;
  644. for iChild := 0 to ANode.ChildCount - 1 do
  645. begin
  646. stnChild := ANode.ChildNodes[iChild];
  647. if stnChild.Rec.ValueByName('Code').AsString <> '' then
  648. stnChild.Rec.ValueByName('Code').AsString := sParentCode + '-' + IntToStr(iChild + 1);
  649. ReorderChildrenCode(stnChild);
  650. end;
  651. end;
  652. procedure TBillsCompileData.sdvBillsCompileAfterAddRecord(
  653. ARecord: TsdDataRecord);
  654. begin
  655. // 解锁前,新增清单为变更清单,解锁后,新增清单为0号台账清单
  656. if TProjectData(FProjectData).ProjProperties.PhaseCount > 0 then
  657. ARecord.ValueByName('IsMeasureAdd').AsBoolean := not TProjectData(FProjectData).CanUnlockInfo;
  658. end;
  659. procedure TBillsCompileData.DoOnAfterDeleteNode(AParent: TsdIDTreeNode);
  660. begin
  661. if Assigned(AParent) and (AParent.ID > 0) then
  662. Calculate(AParent.ID);
  663. end;
  664. procedure TBillsCompileData.Close;
  665. begin
  666. sdvBillsCompile.Close;
  667. end;
  668. procedure TBillsCompileData.SetOnRecChange(const Value: TRecChangeEvent);
  669. begin
  670. FOnRecChange := Value;
  671. end;
  672. procedure TBillsCompileData.sdvBillsCompileCurrentChanged(
  673. ARecord: TsdDataRecord);
  674. begin
  675. if Assigned(FOnRecChange) then
  676. FOnRecChange(ARecord);
  677. end;
  678. procedure TBillsCompileData.ReLockBaseData;
  679. procedure LockNodeBaseData(ANode: TsdIDTreeNode);
  680. begin
  681. if not Assigned(ANode) then Exit;
  682. if ANode.Rec.ValueByName('LockedLevel').AsBoolean then
  683. if not ANode.Rec.ValueByName('LockedInfo').AsBoolean then
  684. ANode.Rec.ValueByName('LockedInfo').AsBoolean := True;
  685. LockNodeBaseData(ANode.FirstChild);
  686. LockNodeBaseData(ANode.NextSibling);
  687. end;
  688. begin
  689. sdvBillsCompile.AfterValueChanged := nil;
  690. try
  691. LockNodeBaseData(FBillsCompileTree.FirstNode);
  692. finally
  693. sdvBillsCompile.AfterValueChanged := sdvBillsCompileAfterValueChanged;
  694. end;
  695. end;
  696. procedure TBillsCompileData.AddBillsFromDealBills(ARec: TsdDataRecord);
  697. var
  698. stnParent, stnNode: TsdIDTreeNode;
  699. begin
  700. if not CanAddGclBills then
  701. raise Exception.Create('当前节点下不可添加工程量清单!');
  702. stnParent := GetGclBillsParent(BillsCompileTree.Selected);
  703. if TBillsIDTreeNode(stnParent).HasLedger or
  704. (not stnParent.HasChildren and TBillsIDTreeNode(stnParent).HasMeasure) then
  705. raise Exception.Create('当前节点不可添加工程量清单!');
  706. stnNode := BillsCompileTree.Add(stnParent.ID, -1);
  707. stnNode.Rec.ValueByName('B_Code').AsString := ARec.ValueByName('B_Code').AsString;
  708. stnNode.Rec.ValueByName('Name').AsString := ARec.ValueByName('Name').AsString;
  709. stnNode.Rec.ValueByName('Units').AsString := ARec.ValueByName('Units').AsString;
  710. stnNode.Rec.ValueByName('Price').AsString := ARec.ValueByName('Price').AsString;
  711. end;
  712. procedure TBillsCompileData.CalculateMis(ABillsID: Integer);
  713. var
  714. vNode: TBillsIDTreeNode;
  715. iChild: Integer;
  716. begin
  717. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ABillsID));
  718. if not Assigned(vNode) then Exit;
  719. if vNode.HasChildren then
  720. begin
  721. for iChild := 0 to vNode.ChildCount - 1 do
  722. CalculateMis(vNode.ChildNodes[iChild].ID);
  723. end
  724. else
  725. begin
  726. with vNode.Rec do
  727. begin
  728. // 数量单价模式则计算金额
  729. if CalcType.AsInteger = 0 then
  730. MisTotalPrice.AsFloat := TotalPriceRoundTo(MisQuantity.AsFloat * Price.AsFloat);
  731. // 金额与修改前不一样,则向父项增量
  732. if MisTotalPrice.AsFloat <> CacheMisTP then
  733. begin
  734. UpdateParent(vNode.ParentID, MisTotalPrice.AsFloat - CacheMisTP, 'MisTotalPrice');
  735. Quantity.AsFloat := QuantityRoundTo(
  736. OrgQuantity.AsFloat + MisQuantity.AsFloat + OthQuantity.AsFloat);
  737. TotalPrice.AsFloat := TotalPriceRoundTo(
  738. OrgTotalPrice.AsFloat + MisTotalPrice.AsFloat + OthTotalPrice.AsFloat);
  739. end;
  740. end;
  741. end;
  742. CalculateDesignPrice(vNode);
  743. end;
  744. procedure TBillsCompileData.CalculateOrg(ABillsID: Integer);
  745. var
  746. vNode: TBillsIDTreeNode;
  747. iChild: Integer;
  748. begin
  749. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ABillsID));
  750. if not Assigned(vNode) then Exit;
  751. if vNode.HasChildren then
  752. begin
  753. for iChild := 0 to vNode.ChildCount - 1 do
  754. CalculateOrg(vNode.ChildNodes[iChild].ID);
  755. end
  756. else
  757. begin
  758. with vNode.Rec do
  759. begin
  760. // 数量单价模式则计算金额
  761. if CalcType.AsInteger = 0 then
  762. OrgTotalPrice.AsFloat := TotalPriceRoundTo(OrgQuantity.AsFloat * Price.AsFloat);
  763. // 金额与修改前不一样,则向父项增量
  764. if CacheOrgTP <> OrgTotalPrice.AsFloat then
  765. begin
  766. UpdateParent(vNode.ParentID, OrgTotalPrice.AsFloat - CacheOrgTP, 'OrgTotalPrice');
  767. Quantity.AsFloat := QuantityRoundTo(
  768. OrgQuantity.AsFloat + MisQuantity.AsFloat + OthQuantity.AsFloat);
  769. TotalPrice.AsFloat := TotalPriceRoundTo(
  770. OrgTotalPrice.AsFloat + MisTotalPrice.AsFloat + OthTotalPrice.AsFloat);
  771. end;
  772. end;
  773. end;
  774. CalculateDesignPrice(vNode);
  775. end;
  776. procedure TBillsCompileData.CalculateOth(ABillsID: Integer);
  777. var
  778. vNode: TBillsIDTreeNode;
  779. iChild: Integer;
  780. begin
  781. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ABillsID));
  782. if not Assigned(vNode) then Exit;
  783. if vNode.HasChildren then
  784. begin
  785. for iChild := 0 to vNode.ChildCount - 1 do
  786. CalculateOth(vNode.ChildNodes[iChild].ID);
  787. end
  788. else
  789. begin
  790. with vNode.Rec do
  791. begin
  792. // 数量单价模式则计算金额
  793. if CalcType.AsInteger = 0 then
  794. OthTotalPrice.AsFloat := TotalPriceRoundTo(OthQuantity.AsFloat * Price.AsFloat);
  795. // 金额与修改前不一样,则向父项增量
  796. if OthTotalPrice.AsFloat <> CacheOthTP then
  797. begin
  798. UpdateParent(vNode.ParentID, OthTotalPrice.AsFloat - CacheOthTP, 'OthTotalPrice');
  799. Quantity.AsFloat := QuantityRoundTo(
  800. OrgQuantity.AsFloat + MisQuantity.AsFloat + OthQuantity.AsFloat);
  801. TotalPrice.AsFloat := TotalPriceRoundTo(
  802. OrgTotalPrice.AsFloat + MisTotalPrice.AsFloat+ OthTotalPrice.AsFloat);
  803. end;
  804. end;
  805. end;
  806. CalculateDesignPrice(vNode);
  807. end;
  808. function TBillsCompileData.GatherChildren(ANode: TsdIDTreeNode;
  809. const AFieldName: string): Double;
  810. var
  811. iChild: Integer;
  812. begin
  813. Result := 0;
  814. if not Assigned(ANode) then Exit;
  815. if ANode.HasChildren and Assigned(ANode.FirstChild) then
  816. begin
  817. Result := 0;
  818. for iChild := 0 to ANode.ChildCount - 1 do
  819. Result := Result + ANode.Rec.ValueByName(AFieldName).AsFloat;
  820. Result := TotalPriceRoundTo(Result);
  821. end
  822. else
  823. if Assigned(ANode.Rec) and Assigned(ANode.Rec.ValueByName(AFieldName)) then
  824. Result := ANode.Rec.ValueByName(AFieldName).AsFloat;
  825. end;
  826. procedure TBillsCompileData.UpdateParent(ABillsID: Integer;
  827. ADifferTotalPrice: Double; const AFieldName: string);
  828. var
  829. vNode: TBillsIDTreeNode;
  830. begin
  831. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ABillsID));
  832. if not Assigned(vNode) then Exit;
  833. with vNode.Rec do
  834. begin
  835. ValueByName(AFieldName).AsFloat := TotalPriceRoundTo(
  836. ValueByName(AFieldName).AsFloat + ADifferTotalPrice);
  837. TotalPrice.AsFloat := TotalPriceRoundTo(TotalPrice.AsFloat + ADifferTotalPrice);
  838. end;
  839. CalculateDesignPrice(vNode);
  840. UpdateParent(vNode.ParentID, ADifferTotalPrice, AFieldName);
  841. end;
  842. procedure TBillsCompileData.CalculateTotal(ABillsID: Integer);
  843. begin
  844. CalculateOrg(ABillsID);
  845. CalculateMis(ABillsID);
  846. CalculateOth(ABillsID);
  847. end;
  848. procedure TBillsCompileData.CalculateBills(ANode: TsdIDTreeNode);
  849. var
  850. iChild: Integer;
  851. begin
  852. if not Assigned(ANode) then Exit;
  853. if ANode.HasChildren then
  854. begin
  855. for iChild := 0 to ANode.ChildCount - 1 do
  856. CalculateBills(ANode.ChildNodes[iChild]);
  857. GatherNode(TBillsIDTreeNode(ANode));
  858. end
  859. else
  860. CalculateLeaf(TBillsIDTreeNode(ANode));
  861. end;
  862. procedure TBillsCompileData.CalculateLeaf(ANode: TBillsIDTreeNode);
  863. begin
  864. if not Assigned(ANode) or ANode.HasChildren then Exit;
  865. with ANode.Rec do
  866. begin
  867. // 分项
  868. if CalcType.AsFloat = 0 then
  869. begin
  870. OrgTotalPrice.AsFloat := TotalPriceRoundTo(Price.AsFloat * OrgQuantity.AsFloat);
  871. MisTotalPrice.AsFloat := TotalPriceRoundTo(Price.AsFloat * MisQuantity.AsFloat);
  872. OthTotalPrice.AsFloat := TotalPriceRoundTo(Price.AsFloat * OthQuantity.AsFloat);
  873. end;
  874. // 汇总
  875. Quantity.AsFloat := QuantityRoundTo(
  876. OrgQuantity.AsFloat + MisQuantity.AsFloat + OthQuantity.AsFloat);
  877. TotalPrice.AsFloat := TotalPriceRoundTo(
  878. OrgTotalPrice.AsFloat + MisTotalPrice.AsFloat + OthTotalPrice.AsFloat);
  879. end;
  880. CalculateDesignPrice(ANode);
  881. end;
  882. procedure TBillsCompileData.GatherNode(ANode: TBillsIDTreeNode);
  883. var
  884. iChild: Integer;
  885. fOrg, fMis, fOth: Double;
  886. vChild: TBillsIDTreeNode;
  887. begin
  888. fOrg := 0;
  889. fMis := 0;
  890. fOth := 0;
  891. for iChild := 0 to ANode.ChildCount - 1 do
  892. begin
  893. vChild := TBillsIDTreeNode(ANode.ChildNodes[iChild]);
  894. fOrg := fOrg + vChild.Rec.OrgTotalPrice.AsFloat;
  895. fMis := fMis + vChild.Rec.MisTotalPrice.AsFloat;
  896. fOth := fOth + vChild.Rec.OthTotalPrice.AsFloat;
  897. end;
  898. ANode.Rec.OrgTotalPrice.AsFloat := TotalPriceRoundTo(fOrg);
  899. ANode.Rec.MisTotalPrice.AsFloat := TotalPriceRoundTo(fMis);
  900. ANode.Rec.OthTotalPrice.AsFloat := TotalPriceRoundTo(fOth);
  901. ANode.Rec.TotalPrice.AsFloat := TotalPriceRoundTo(fOrg + fMis + fOth);
  902. CalculateDesignPrice(ANode);
  903. end;
  904. procedure TBillsCompileData.Calculate(ABillsID: Integer);
  905. procedure UpdateParent(ANode: TBillsIDTreeNode; ADifferOrg, ADifferMis, ADifferOth: Double);
  906. begin
  907. if not Assigned(ANode) then Exit;
  908. with ANode.Rec do
  909. begin
  910. OrgTotalPrice.AsFloat := TotalPriceRoundTo(OrgTotalPrice.AsFloat + ADifferOrg);
  911. MisTotalPrice.AsFloat := TotalPriceRoundTo(MisTotalPrice.AsFloat + ADifferMis);
  912. OthTotalPrice.AsFloat := TotalPriceRoundTo(OthTotalPrice.AsFloat + ADifferOth);
  913. TotalPrice.AsFloat := TotalPriceRoundTo(
  914. TotalPrice.AsFloat + ADifferOrg + ADifferMis + ADifferOth);
  915. if DgnQuantity1.AsFloat <> 0 then
  916. DgnPrice.AsFloat := PriceRoundTo(TotalPrice.AsFloat/DgnQuantity1.AsFloat);
  917. end;
  918. UpdateParent(TBillsIDTreeNode(ANode.Parent), ADifferOrg, ADifferMis, ADifferOth);
  919. end;
  920. var
  921. vNode: TBillsIDTreeNode;
  922. iChild: Integer;
  923. fOrg, fMis, fOth: Double;
  924. begin
  925. vNode := TBillsIDTreeNode(BillsCompileTree.FindNode(ABillsID));
  926. if not Assigned(vNode) then Exit;
  927. fOrg := vNode.Rec.OrgTotalPrice.AsFloat;
  928. fMis := vNode.Rec.MisTotalPrice.AsFloat;
  929. fOth := vNode.Rec.OthTotalPrice.AsFloat;
  930. CalculateBills(vNode);
  931. fOrg := vNode.Rec.OrgTotalPrice.AsFloat - fOrg;
  932. fMis := vNode.Rec.MisTotalPrice.AsFloat - fMis;
  933. fOth := vNode.Rec.OthTotalPrice.AsFloat - fOth;
  934. UpdateParent(TBillsIDTreeNode(vNode.Parent), fOrg, fMis, fOth);
  935. end;
  936. procedure TBillsCompileData.CalculateDesignPrice(ANode: TBillsIDTreeNode);
  937. begin
  938. if ANode.Rec.DgnQuantity1.AsFloat <> 0 then
  939. ANode.Rec.DgnPrice.AsFloat := PriceRoundTo(
  940. ANode.Rec.TotalPrice.AsFloat/ANode.Rec.DgnQuantity1.AsFloat)
  941. else
  942. ANode.Rec.DgnPrice.Clear;
  943. end;
  944. procedure TBillsCompileData.ExpandPegXmjNode;
  945. function HasPegChild(ANode: TBillsIDTreeNode): Boolean;
  946. var
  947. NextNode: TBillsIDTreeNode;
  948. begin
  949. Result := False;
  950. NextNode := TBillsIDTreeNode(ANode.NextNode);
  951. while ((NextNode.MajorIndex - ANode.MajorIndex) <= ANode.PosterityCount) do
  952. begin
  953. if CheckPeg(NextNode.Rec.Name.AsString) then
  954. begin
  955. Result := True;
  956. Break;
  957. end;
  958. NextNode := TBillsIDTreeNode(NextNode.NextNode);
  959. end;
  960. end;
  961. function HasGclChild(ANode: TBillsIDTreeNode): Boolean;
  962. var
  963. vChild: TBillsIDTreeNode;
  964. begin
  965. Result := True;
  966. vChild := TBillsIDTreeNode(ANode.FirstChild);
  967. while Assigned(vChild) and not Result do
  968. begin
  969. if vChild.Rec.B_Code.AsString <> '' then
  970. Result := False;
  971. vChild := TBillsIDTreeNode(vChild.NextSibling);
  972. end;
  973. end;
  974. var
  975. iIndex: Integer;
  976. vNode: TBillsIDTreeNode;
  977. begin
  978. for iIndex := 0 to BillsCompileTree.Count - 1 do
  979. begin
  980. vNode := TBillsIDTreeNode(BillsCompileTree.Items[iIndex]);
  981. if vNode.HasChildren then
  982. vNode.Expanded := HasPegChild(vNode) or not HasGclChild(vNode);
  983. end;
  984. end;
  985. end.