DetailExcelImport.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. unit DetailExcelImport;
  2. interface
  3. uses
  4. Classes, ProjectData, ScXlsOutput, MCacheTree, XLSAdapter, sdDB,
  5. Variants, Forms, Controls;
  6. type
  7. TDetailExcelImport = class
  8. private
  9. FProjectData: TProjectData;
  10. FTempFile: string;
  11. FExcel: TXlsOutPut;
  12. function GetCellValue(AXlsFile: TXLSFile; ARow, ACol: Integer): string;
  13. function GetCellValueFormat(AXlsFile: TXLSFile; ARow, ACol: Integer): string;
  14. procedure BeginImport; virtual; abstract;
  15. procedure EndImport; virtual; abstract;
  16. procedure Import; virtual; abstract;
  17. public
  18. constructor Create(AProjectData: TProjectData);
  19. destructor Destroy; override;
  20. procedure ImportFile(const AFileName: string);
  21. end;
  22. // 平面分项清单格式导入,导入至某项目节节点之下
  23. TPlaneFxBillsExcelImport = class(TDetailExcelImport)
  24. private
  25. FParentID: Integer;
  26. FCacheTree: TBillsCacheTree;
  27. FCurRow: Integer;
  28. FXmjLevel1Col: Integer;
  29. FXmjLevel2Col: Integer;
  30. FXmjLevel3Col: Integer;
  31. FXmjLevel4Col: Integer;
  32. FXmjLevel5Col: Integer;
  33. FXmjLevel6Col: Integer;
  34. FXmjLevel7Col: Integer;
  35. FB_CodeCol: Integer;
  36. FNameCol: Integer;
  37. FUnitCol: Integer;
  38. FQuantityCol: Integer;
  39. FPriceCol: Integer;
  40. FDrawingCol: Integer;
  41. FMemoCol: Integer;
  42. procedure LoadXmjLevel1(AXlsFile: TXLSFile);
  43. procedure LoadXmjLevel2(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  44. procedure LoadXmjLevel3(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  45. procedure LoadXmjLevel4(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  46. procedure LoadXmjLevel5(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  47. procedure LoadXmjLevel6(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  48. procedure LoadXmjLevel7(AXlsFile: TXLSFile; AParent: TBillsCacheNode);
  49. procedure LoadBillsNode(AXlsFile: TXLSFile; AXmj: TBillsCacheNode);
  50. function LoadColumnsFromHead(AXlsFile: TXlsFile): Boolean;
  51. procedure LoadFxBills(AXlsFile: TXLSFile);
  52. procedure WriteNode(ADataSet: TsdDataSet; ANode: TBillsCacheNode);
  53. procedure WriteNodes(ADataSet: TsdDataSet);
  54. procedure BeginImport; override;
  55. procedure EndImport; override;
  56. procedure Import; override;
  57. public
  58. property ParentID: Integer read FParentID write FParentID;
  59. end;
  60. implementation
  61. uses
  62. UtilMethods, SysUtils, ZhAPI, SheetSelectFrm, UExcelAdapter, UFlxMessages,
  63. UFlxFormats, ProgressHintFrm;
  64. { TDetailExcelImport }
  65. constructor TDetailExcelImport.Create(AProjectData: TProjectData);
  66. begin
  67. FProjectData := AProjectData;
  68. FTempFile := GetTempFileName;
  69. end;
  70. destructor TDetailExcelImport.Destroy;
  71. begin
  72. if FileExists(FTempFile) then
  73. DeleteFile(FTempFile);
  74. inherited;
  75. end;
  76. function TDetailExcelImport.GetCellValue(AXlsFile: TXLSFile; ARow,
  77. ACol: Integer): string;
  78. var
  79. xlsCell: TXlsCellValue;
  80. begin
  81. Result := '';
  82. if not Assigned(AXlsFile) or (ARow = -1) or (ACol = -1) then Exit;
  83. xlsCell := AXlsFile.CellValueX[ARow, ACol];
  84. Result := xlsCell.Value;
  85. end;
  86. function TDetailExcelImport.GetCellValueFormat(AXlsFile: TXLSFile; ARow,
  87. ACol: Integer): string;
  88. function GetDigit(AFormat: WideString): Integer;
  89. var
  90. I: Integer;
  91. bDigit: Boolean;
  92. begin
  93. Result := 0;
  94. bDigit := False;
  95. for I := 1 to Length(AFormat) do
  96. begin
  97. if AFormat[I] = '.' then
  98. begin
  99. if bDigit then Break
  100. else bDigit := True;
  101. end
  102. else if AFormat[I] = ';' then Break
  103. else if bDigit and (AFormat[I] = '0') then
  104. Dec(Result);
  105. end;
  106. end;
  107. function FormatNum(AValue: Variant; AFormat: WideString): string;
  108. begin
  109. Result := AValue;
  110. if not VarIsNull(AValue) then
  111. begin
  112. if CheckNumeric(Result) then
  113. begin
  114. if Pos('%', AFormat) <> 0 then AValue := AValue * 100;
  115. if AFormat <> '' then
  116. Result := FloatToStr(AdvRoundTo(AValue, GetDigit(AFormat)))
  117. else
  118. Result := FloatToStr(AdvRoundTo(AValue, -2));
  119. if Pos('%', AFormat) <> 0 then Result := Result + '%';
  120. if AValue = '0' then Result := '';
  121. end;
  122. end;
  123. end;
  124. var
  125. xlsCell: TXlsCellValue;
  126. FlxFormat: TFlxFormat;
  127. begin
  128. Result := '';
  129. if not Assigned(AXlsFile) or (ARow = -1) or (ACol = -1) then Exit;
  130. xlsCell := AXlsFile.GetCellDataX(ARow, ACol);
  131. Result := xlsCell.Value;
  132. if xlsCell.XF <> -1 then
  133. begin
  134. FlxFormat := AXlsFile.FormatList[xlsCell.XF];
  135. Result := FormatNum(xlsCell.Value, FlxFormat.Format);
  136. end;
  137. end;
  138. procedure TDetailExcelImport.ImportFile(const AFileName: string);
  139. begin
  140. CopyFileOrFolder(AFileName, FTempFile);
  141. FExcel := TXlsOutPut.Create(FTempFile);
  142. BeginImport;
  143. try
  144. Import;
  145. finally
  146. EndImport;
  147. FExcel.Free;
  148. end;
  149. end;
  150. { TPlaneFxBillsExcelImport }
  151. procedure TPlaneFxBillsExcelImport.Import;
  152. begin
  153. FCurRow := 1;
  154. if LoadColumnsFromHead(FExcel.XlsFile) then
  155. begin
  156. LoadFxBills(FExcel.XlsFile);
  157. WriteNodes(FProjectData.BillsData.sddBills);
  158. end
  159. else
  160. ErrorMessage('导入的Excel格式有误!');
  161. end;
  162. procedure TPlaneFxBillsExcelImport.LoadBillsNode(AXlsFile: TXLSFile;
  163. AXmj: TBillsCacheNode);
  164. var
  165. sB_Code, sName, sUnits: string;
  166. vGclNode: TBillsCacheNode;
  167. fPrice: Double;
  168. begin
  169. sB_Code := Trim(GetCellValue(AXlsFile, FCurRow, FB_CodeCol));
  170. sName := Trim(GetCellValue(AXlsFile, FCurRow, FNameCol));
  171. sUnits := Trim(GetCellValue(AXlsFile, FCurRow, FUnitCol));
  172. fPrice := StrToFloatDef(GetCellValue(AXlsFile, FCurRow, FPriceCol), 0);
  173. if sB_Code <> '' then
  174. begin
  175. vGclNode := FCacheTree.FindGclChild(AXmj, sB_Code, sName, sUnits, fPrice);
  176. if not Assigned(vGclNode) then
  177. begin
  178. vGclNode := FCacheTree.AddNode(AXmj, nil);
  179. vGclNode.B_Code := sB_Code;
  180. vGclNode.Name := sName;
  181. vGclNode.Units := sUnits;
  182. vGclNode.Quantity := StrToFloatDef(GetCellValue(AXlsFile, FCurRow, FQuantityCol), 0);
  183. vGclNode.Price := fPrice;
  184. vGclNode.DrawingCode := GetCellValue(AXlsFile, FCurRow, FDrawingCol);
  185. vGclNode.MemoStr := GetCellValue(AXlsFile, FCurRow, FMemoCol);
  186. end
  187. else
  188. vGclNode.Quantity := vGclNode.Quantity + StrToFloatDef(GetCellValue(AXlsFile, FCurRow, FQuantityCol), 0);
  189. end;
  190. Inc(FCurRow);
  191. end;
  192. function TPlaneFxBillsExcelImport.LoadColumnsFromHead(AXlsFile: TXlsFile): Boolean;
  193. var
  194. iCol: Integer;
  195. sColName: string;
  196. begin
  197. Result := False;
  198. FXmjLevel1Col := -1;
  199. FXmjLevel2Col := -1;
  200. FXmjLevel3Col := -1;
  201. FXmjLevel4Col := -1;
  202. FXmjLevel5Col := -1;
  203. FXmjLevel6Col := -1;
  204. FXmjLevel7Col := -1;
  205. FB_CodeCol := -1;
  206. FNameCol := -1;
  207. FUnitCol := -1;
  208. FQuantityCol := -1;
  209. FPriceCol := -1;
  210. FDrawingCol := -1;
  211. FMemoCol := -1;
  212. UpdateProgressHint('正在识别Excel数据格式');
  213. UpdateProgressPosition(0);
  214. while not Result and (FCurRow <= AXlsFile.MaxRow) do
  215. begin
  216. for iCol := 1 to AXlsFile.MaxCol do
  217. begin
  218. sColName := GetCellValue(AXlsFile, FCurRow, iCol);
  219. if sColName = '第1层' then
  220. FXmjLevel1Col := iCol
  221. else if sColName = '第2层' then
  222. FXmjLevel2Col := iCol
  223. else if sColName = '第3层' then
  224. FXmjLevel3Col := iCol
  225. else if sColName = '第4层' then
  226. FXmjLevel4Col := iCol
  227. else if sColName = '第5层' then
  228. FXmjLevel5Col := iCol
  229. else if sColName = '第6层' then
  230. FXmjLevel6Col := iCol
  231. else if sColName = '第7层' then
  232. FXmjLevel7Col := iCol
  233. else if sColName = '清单号' then
  234. FB_CodeCol := iCol
  235. else if sColName = '清单名称' then
  236. FNameCol := iCol
  237. else if sColName = '单位' then
  238. FUnitCol := iCol
  239. else if sColName = '数量' then
  240. FQuantityCol := iCol
  241. else if sColName = '单价' then
  242. FPriceCol := iCol
  243. else if sColName = '图号' then
  244. FDrawingCol := iCol
  245. else if sColName = '备注' then
  246. FMemoCol := iCol
  247. end;
  248. Result := FXmjLevel1Col <> -1;
  249. Inc(FCurRow);
  250. end;
  251. end;
  252. procedure TPlaneFxBillsExcelImport.LoadFxBills(AXlsFile: TXLSFile);
  253. var
  254. iPos: Integer;
  255. begin
  256. UpdateProgressHint('正在解析平面台账数据');
  257. while FCurRow <= AXlsFile.MaxRow do
  258. begin
  259. iPos := FCurRow*100 div AXlsFile.MaxRow;
  260. UpdateProgressPosition(iPos);
  261. LoadXmjLevel1(AXlsFile);
  262. end;
  263. end;
  264. procedure TPlaneFxBillsExcelImport.LoadXmjLevel1(AXlsFile: TXLSFile);
  265. var
  266. sName: string;
  267. vXmj: TBillsCacheNode;
  268. iEndRow: Integer;
  269. begin
  270. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel1Col));
  271. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel1Col] do
  272. iEndRow := FCurRow + Bottom - Top;
  273. if sName <> '' then
  274. begin
  275. vXmj := FCacheTree.FindXmjChild(nil, '', sName);
  276. if not Assigned(vXmj) then
  277. begin
  278. vXmj := FCacheTree.AddNode(nil);
  279. vXmj.Name := sName;
  280. end;
  281. if FXmjLevel2Col <> -1 then
  282. begin
  283. while FCurRow <= iEndRow do
  284. LoadXmjLevel2(AXlsFile, vXmj);
  285. end
  286. else
  287. begin
  288. while FCurRow <= iEndRow do
  289. LoadBillsNode(AXlsFile, vXmj);
  290. end;
  291. end
  292. else
  293. Inc(FCurRow);
  294. end;
  295. procedure TPlaneFxBillsExcelImport.LoadXmjLevel2(AXlsFile: TXLSFile;
  296. AParent: TBillsCacheNode);
  297. var
  298. sName: string;
  299. vXmj: TBillsCacheNode;
  300. iEndRow: Integer;
  301. begin
  302. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel2Col));
  303. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel2Col] do
  304. iEndRow := FCurRow + Bottom - Top;
  305. if sName <> '' then
  306. begin
  307. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  308. if not Assigned(vXmj) then
  309. begin
  310. vXmj := FCacheTree.AddNode(AParent);
  311. vXmj.Name := sName;
  312. end;
  313. if FXmjLevel3Col <> -1 then
  314. begin
  315. while FCurRow <= iEndRow do
  316. LoadXmjLevel3(AXlsFile, vXmj);
  317. end
  318. else
  319. begin
  320. while FCurRow <= iEndRow do
  321. LoadBillsNode(AXlsFile, vXmj);
  322. end;
  323. end
  324. else
  325. begin
  326. while FCurRow <= iEndRow do
  327. LoadBillsNode(AXlsFile, vXmj);
  328. end;
  329. end;
  330. procedure TPlaneFxBillsExcelImport.LoadXmjLevel3(AXlsFile: TXLSFile;
  331. AParent: TBillsCacheNode);
  332. var
  333. sName: string;
  334. vXmj: TBillsCacheNode;
  335. iEndRow: Integer;
  336. begin
  337. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel3Col));
  338. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel3Col] do
  339. iEndRow := FCurRow + Bottom - Top;
  340. if sName <> '' then
  341. begin
  342. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  343. if not Assigned(vXmj) then
  344. begin
  345. vXmj := FCacheTree.AddNode(AParent);
  346. vXmj.Name := sName;
  347. end;
  348. if FXmjLevel4Col <> -1 then
  349. begin
  350. while FCurRow <= iEndRow do
  351. LoadXmjLevel4(AXlsFile, vXmj);
  352. end
  353. else
  354. begin
  355. while FCurRow <= iEndRow do
  356. LoadBillsNode(AXlsFile, vXmj);
  357. end;
  358. end
  359. else
  360. begin
  361. while FCurRow <= iEndRow do
  362. LoadBillsNode(AXlsFile, AParent);
  363. end;
  364. end;
  365. procedure TPlaneFxBillsExcelImport.LoadXmjLevel4(AXlsFile: TXLSFile;
  366. AParent: TBillsCacheNode);
  367. var
  368. sName: string;
  369. vXmj: TBillsCacheNode;
  370. iEndRow: Integer;
  371. begin
  372. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel4Col));
  373. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel4Col] do
  374. iEndRow := FCurRow + Bottom - Top;
  375. if sName <> '' then
  376. begin
  377. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  378. if not Assigned(vXmj) then
  379. begin
  380. vXmj := FCacheTree.AddNode(AParent);
  381. vXmj.Name := sName;
  382. end;
  383. if FXmjLevel5Col <> -1 then
  384. begin
  385. while FCurRow <= iEndRow do
  386. LoadXmjLevel5(AXlsFile, vXmj);
  387. end
  388. else
  389. begin
  390. while FCurRow <= iEndRow do
  391. LoadBillsNode(AXlsFile, vXmj);
  392. end;
  393. end
  394. else
  395. begin
  396. while FCurRow <= iEndRow do
  397. LoadBillsNode(AXlsFile, AParent);
  398. end;
  399. end;
  400. procedure TPlaneFxBillsExcelImport.LoadXmjLevel5(AXlsFile: TXLSFile;
  401. AParent: TBillsCacheNode);
  402. var
  403. sName: string;
  404. vXmj: TBillsCacheNode;
  405. iEndRow: Integer;
  406. begin
  407. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel5Col));
  408. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel5Col] do
  409. iEndRow := FCurRow + Bottom - Top;
  410. if sName <> '' then
  411. begin
  412. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  413. if not Assigned(vXmj) then
  414. begin
  415. vXmj := FCacheTree.AddNode(AParent);
  416. vXmj.Name := sName;
  417. end;
  418. if FXmjLevel6Col <> -1 then
  419. begin
  420. while FCurRow <= iEndRow do
  421. LoadXmjLevel6(AXlsFile, vXmj);
  422. end
  423. else
  424. begin
  425. while FCurRow <= iEndRow do
  426. LoadBillsNode(AXlsFile, vXmj);
  427. end;
  428. end
  429. else
  430. begin
  431. while FCurRow <= iEndRow do
  432. LoadBillsNode(AXlsFile, AParent);
  433. end;
  434. end;
  435. procedure TPlaneFxBillsExcelImport.LoadXmjLevel6(AXlsFile: TXLSFile;
  436. AParent: TBillsCacheNode);
  437. var
  438. sName: string;
  439. vXmj: TBillsCacheNode;
  440. iEndRow: Integer;
  441. begin
  442. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel6Col));
  443. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel6Col] do
  444. iEndRow := FCurRow + Bottom - Top;
  445. if sName <> '' then
  446. begin
  447. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  448. if not Assigned(vXmj) then
  449. begin
  450. vXmj := FCacheTree.AddNode(AParent);
  451. vXmj.Name := sName;
  452. end;
  453. if FXmjLevel7Col <> -1 then
  454. begin
  455. while FCurRow <= iEndRow do
  456. LoadXmjLevel7(AXlsFile, vXmj);
  457. end
  458. else
  459. begin
  460. while FCurRow <= iEndRow do
  461. LoadBillsNode(AXlsFile, vXmj);
  462. end;
  463. end
  464. else
  465. begin
  466. while FCurRow <= iEndRow do
  467. LoadBillsNode(AXlsFile, AParent);
  468. end;
  469. end;
  470. procedure TPlaneFxBillsExcelImport.LoadXmjLevel7(AXlsFile: TXLSFile;
  471. AParent: TBillsCacheNode);
  472. var
  473. sName: string;
  474. vXmj: TBillsCacheNode;
  475. iEndRow: Integer;
  476. begin
  477. sName := Trim(GetCellValue(AXlsFile, FCurRow, FXmjLevel7Col));
  478. if sName = '' then Exit;
  479. vXmj := FCacheTree.FindXmjChild(AParent, '', sName);
  480. if not Assigned(vXmj) then
  481. begin
  482. vXmj := FCacheTree.AddNode(AParent);
  483. vXmj.Name := sName;
  484. end;
  485. with AXlsFile.CellMergedBounds[FCurRow, FXmjLevel7Col] do
  486. iEndRow := FCurRow + Bottom - Top;
  487. while FCurRow <= iEndRow do
  488. LoadBillsNode(AXlsFile, vXmj);
  489. end;
  490. procedure TPlaneFxBillsExcelImport.WriteNodes(ADataSet: TsdDataSet);
  491. var
  492. i, iPos: Integer;
  493. begin
  494. UpdateProgressHint('写入读取的Excel数据');
  495. UpdateProgressPosition(0);
  496. for i := 0 to FCacheTree.CacheNodes.Count - 1 do
  497. begin
  498. WriteNode(ADataSet, TBillsCacheNode(FCacheTree.CacheNodes[i]));
  499. iPos := i*100 div FCacheTree.CacheNodes.Count;
  500. UpdateProgressPosition(iPos);
  501. end;
  502. UpdateProgressPosition(100);
  503. end;
  504. procedure TPlaneFxBillsExcelImport.WriteNode(ADataSet: TsdDataSet;
  505. ANode: TBillsCacheNode);
  506. var
  507. Rec: TsdDataRecord;
  508. begin
  509. if ANode.B_Code <> '' then
  510. UpdateProgressHint('写入读取的Excel数据 ' + ANode.B_Code)
  511. else
  512. UpdateProgressHint('写入读取的Excel数据 ' + ANode.Name);
  513. Rec := ADataSet.Add;
  514. Rec.ValueByName('ID').AsInteger := ANode.ID;
  515. if ANode.ParentID = -1 then
  516. Rec.ValueByName('ParentID').AsInteger := ParentID
  517. else
  518. Rec.ValueByName('ParentID').AsInteger := ANode.ParentID;
  519. Rec.ValueByName('NextSiblingID').AsInteger := ANode.NextSiblingID;
  520. Rec.ValueByName('B_Code').AsString := ANode.B_Code;
  521. Rec.ValueByName('Name').AsString := ANode.Name;
  522. Rec.ValueByName('Units').AsString := ANode.Units;
  523. Rec.ValueByName('Price').AsFloat := PriceRoundTo(ANode.Price);
  524. Rec.ValueByName('OrgQuantity').AsFloat := QuantityRoundTo(ANode.Quantity);
  525. Rec.ValueByName('DrawingCode').AsString := ANode.DrawingCode;
  526. Rec.ValueByName('MemoStr').AsString := ANode.MemoStr;
  527. // 解锁前,新增清单为变更清单,解锁后,新增清单为0号台账清单
  528. if FProjectData.ProjProperties.PhaseCount > 0 then
  529. Rec.ValueByName('IsMeasureAdd').AsBoolean := not FProjectData.CanUnlockInfo;
  530. end;
  531. procedure TPlaneFxBillsExcelImport.BeginImport;
  532. begin
  533. Screen.Cursor := crHourGlass;
  534. ShowProgressHint('导入Excel数据', 100);
  535. FCacheTree := TBillsCacheTree.Create;
  536. FCacheTree.NewNodeID := FProjectData.BillsData.GetMaxBillsID + 1;
  537. FProjectData.DisConnectTree;
  538. FProjectData.BillsData.DisableEvents;
  539. end;
  540. procedure TPlaneFxBillsExcelImport.EndImport;
  541. begin
  542. FCacheTree.Free;
  543. FProjectData.BillsData.EnableEvents;
  544. FProjectData.ReConnectTree;
  545. FProjectData.BillsCompileData.CalculateAll;
  546. CloseProgressHint;
  547. Screen.Cursor := crDefault;
  548. end;
  549. end.