-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCostTool_V091_GenericBXXLayer_UIStateCleanup.bas
More file actions
1519 lines (1331 loc) · 87 KB
/
Copy pathCostTool_V091_GenericBXXLayer_UIStateCleanup.bas
File metadata and controls
1519 lines (1331 loc) · 87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Option Compare Database
Option Explicit
' ============================================================
' Decommissioning Cost Tool - Access Builder v0.9.1 Add-On
' Generic BXX Template Layer + UI Input Polish
' ============================================================
'
' Purpose:
' Adds a universal high-level estimating layer on top of the stable v0.6.2
' bottom-up Access cost tool.
'
' Core idea:
' New building inputs -> generic BXX-style WBS estimate -> generated lines
' -> existing tblJobLines fine-tuning screen.
'
' Design principles:
' - Requires stable v0.6.2 baseline objects.
' - Does NOT overwrite tblCostLibrary master records.
' - Uses table-driven templates instead of hard-coded B56/B18 logic.
' - Supports a generic Building / Structure D&D basis and a generic
' Soil / Outdoor Remediation basis.
' - Exposes key universal inputs: area, duration, crew size, class, scope basis,
' removal adjustment, percent clean/contaminated, removal depth, backfill depth,
' and the judgement levers already present in the Excel template.
'
' v0.9.1 UI / input polish:
' - No calculation changes to the validated v0.8.9 engine.
' - New input rows are created blank instead of pre-populated with B56-like values.
' - Management staff number inputs are exposed alongside use-factor inputs.
' - Site preparation can be selected by individual Excel task checkbox while
' preserving the original 16 hr x 4 specialists + 16 hr x 1 PM calculation.
'
' Entry point:
' BuildCostTool_V091_GenericBXXLayer
'
' ============================================================
Public Sub BuildCostTool_V091_GenericBXXLayer()
On Error GoTo ErrHandler
DoCmd.Hourglass True
Application.Echo False
V091_CheckBaseObjects
V091_CreateTables
V091_UpdateBaseSchema
V091_EnsureModelAllowanceLibraryItems
V091_SeedSettings
V091_SeedEquipmentTemplates
V091_SeedConsumableTemplates
V091_SeedAreaActivityTemplates
V091_SeedDefaultInputs
V091_CreateQueries
V091_DeleteFormIfExists "frmV091GeneratedLinesSubform"
V091_DeleteFormIfExists "frmV091GenericEstimate"
V091_DeleteFormIfExists "frmV091PortfolioOverview"
V091_CreateGeneratedLinesSubform
V091_CreateEstimateForm
V091_CreatePortfolioForm
Application.Echo True
DoCmd.Hourglass False
DoCmd.OpenForm "frmV091GenericEstimate"
MsgBox "Cost Tool v0.9.1 UI-state-polished generic BXX layer built successfully." & vbCrLf & vbCrLf & _
"Use frmV091GenericEstimate as the universal new-building entry point.", _
vbInformation, "v0.9.1 Build Complete"
Exit Sub
ErrHandler:
Application.Echo True
DoCmd.Hourglass False
MsgBox "v0.9.1 build failed: " & Err.Number & vbCrLf & Err.Description, _
vbCritical, "v0.9.1 Build Error"
End Sub
Private Sub V091_CheckBaseObjects()
If Not V091_TableExists("tblJobs") Then Err.Raise vbObjectError + 8201, , "tblJobs not found. Run BuildCostTool_V062_AllInOne first."
If Not V091_TableExists("tblJobLines") Then Err.Raise vbObjectError + 8202, , "tblJobLines not found. Run BuildCostTool_V062_AllInOne first."
If Not V091_TableExists("tblCostLibrary") Then Err.Raise vbObjectError + 8203, , "tblCostLibrary not found. Run BuildCostTool_V062_AllInOne first."
If Not V091_TableExists("tblCategories") Then Err.Raise vbObjectError + 8204, , "tblCategories not found. Run BuildCostTool_V062_AllInOne first."
If Not V091_QueryExists("qryGrandTotals") Then Err.Raise vbObjectError + 8205, , "qryGrandTotals not found. Run BuildCostTool_V062_AllInOne first."
End Sub
' ============================================================
' TABLES
' ============================================================
Private Sub V091_CreateTables()
Dim sql As String
sql = "CREATE TABLE tblV091BuildingInputs ("
sql = sql & "JobID TEXT(50) CONSTRAINT pk_tblV091BuildingInputs PRIMARY KEY, "
sql = sql & "BuildingCode TEXT(50), "
sql = sql & "BuildingName TEXT(255), "
sql = sql & "FacilityClass TEXT(10), "
sql = sql & "FacilityType TEXT(100), "
sql = sql & "EstimateBasis TEXT(100), "
sql = sql & "TotalAreaM2 DOUBLE, "
sql = sql & "FootprintAreaM2 DOUBLE, "
sql = sql & "ProjectDurationDays DOUBLE, "
sql = sql & "WorkDays DOUBLE, "
sql = sql & "CrewSize DOUBLE, "
sql = sql & "ScaleRemovalByCrew YESNO, "
sql = sql & "RemovalAdjustmentPct DOUBLE, "
sql = sql & "PortfolioManagerUseFactor DOUBLE, PortfolioManagerNumber DOUBLE, "
sql = sql & "SeniorPMUseFactor DOUBLE, SeniorPMNumber DOUBLE, "
sql = sql & "ProjectManagerUseFactor DOUBLE, ProjectManagerNumber DOUBLE, "
sql = sql & "ProcedureHours DOUBLE, "
sql = sql & "QASafetyHours DOUBLE, "
sql = sql & "IncludeTraining YESNO, "
sql = sql & "IncludeSitePrep YESNO, "
sql = sql & "SitePrepTaskCount DOUBLE, SitePrepHoursPerTask DOUBLE, SitePrepInitialSurvey YESNO, SitePrepBoundariesHepa YESNO, SitePrepStagingArea YESNO, SitePrepRadSegregation YESNO, SitePrepElectricalIsolation YESNO, SitePrepPipingIsolation YESNO, "
sql = sql & "IncludeDetailedCharacterization YESNO, "
sql = sql & "CharacterizationSpecialistCount DOUBLE, "
sql = sql & "CharacterizationPMCount DOUBLE, "
sql = sql & "CharacterizationHoursPerPerson DOUBLE, "
sql = sql & "IncludeConsumables YESNO, "
sql = sql & "PercentClean DOUBLE, "
sql = sql & "PercentContaminated DOUBLE, "
sql = sql & "RemovalDepthM DOUBLE, "
sql = sql & "BackfillDepthM DOUBLE, "
sql = sql & "AsbestosPipeLengthM DOUBLE, "
sql = sql & "AsbestosTileAreaM2 DOUBLE, "
sql = sql & "ConsumableMonths DOUBLE, "
sql = sql & "DosimeterYears DOUBLE, "
sql = sql & "BioassayYears DOUBLE, "
sql = sql & "IsRadiological YESNO, "
sql = sql & "IncludeHotCell YESNO, "
sql = sql & "HotCellAreaM2 DOUBLE, "
sql = sql & "Notes MEMO, "
sql = sql & "LastGeneratedAt DATETIME, "
sql = sql & "UpdatedAt DATETIME"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091BuildingInputs", sql
sql = "CREATE TABLE tblV091Settings ("
sql = sql & "SettingName TEXT(100) CONSTRAINT pk_tblV091Settings PRIMARY KEY, "
sql = sql & "SettingValueNumber DOUBLE, "
sql = sql & "SettingValueText TEXT(255), "
sql = sql & "Notes MEMO, "
sql = sql & "UpdatedAt DATETIME"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091Settings", sql
sql = "CREATE TABLE tblV091EquipmentTemplate ("
sql = sql & "TemplateID AUTOINCREMENT CONSTRAINT pk_tblV091EquipmentTemplate PRIMARY KEY, "
sql = sql & "FacilityClass TEXT(10), "
sql = sql & "CategoryName TEXT(100), "
sql = sql & "ItemID LONG, "
sql = sql & "ItemName TEXT(255), "
sql = sql & "Quantity DOUBLE, "
sql = sql & "DefaultEnabled YESNO, "
sql = sql & "Notes MEMO"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091EquipmentTemplate", sql
sql = "CREATE TABLE tblV091ConsumableTemplate ("
sql = sql & "TemplateID AUTOINCREMENT CONSTRAINT pk_tblV091ConsumableTemplate PRIMARY KEY, "
sql = sql & "FacilityClass TEXT(10), "
sql = sql & "CategoryName TEXT(100), "
sql = sql & "ItemID LONG, "
sql = sql & "ItemName TEXT(255), "
sql = sql & "UseRate DOUBLE, "
sql = sql & "DurationBasis TEXT(50), "
sql = sql & "DefaultEnabled YESNO, "
sql = sql & "Notes MEMO"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091ConsumableTemplate", sql
sql = "CREATE TABLE tblV091StaffUseTemplate ("
sql = sql & "TemplateID AUTOINCREMENT CONSTRAINT pk_tblV091StaffUseTemplate PRIMARY KEY, "
sql = sql & "FacilityClass TEXT(10), "
sql = sql & "RoleName TEXT(255), "
sql = sql & "LabourItemID LONG, "
sql = sql & "HoursPerDay DOUBLE, "
sql = sql & "UseFactor DOUBLE, "
sql = sql & "DefaultEnabled YESNO, "
sql = sql & "Notes MEMO"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091StaffUseTemplate", sql
sql = "CREATE TABLE tblV091AreaActivityTemplate ("
sql = sql & "TemplateID AUTOINCREMENT CONSTRAINT pk_tblV091AreaActivityTemplate PRIMARY KEY, "
sql = sql & "EstimateBasis TEXT(100), "
sql = sql & "WBSCode TEXT(50), "
sql = sql & "WBSSubCode TEXT(50), "
sql = sql & "Description TEXT(255), "
sql = sql & "CategoryName TEXT(100), "
sql = sql & "ItemID LONG, "
sql = sql & "QuantitySource TEXT(100), "
sql = sql & "UnitName TEXT(50), "
sql = sql & "UnitRateAUD DOUBLE, "
sql = sql & "ApplyRemovalAdjustment YESNO, "
sql = sql & "RequiresRadiological YESNO, "
sql = sql & "CleanVolRateM3 DOUBLE, "
sql = sql & "LsaVolRateM3 DOUBLE, "
sql = sql & "HazVolRateM3 DOUBLE, "
sql = sql & "MixedVolRateM3 DOUBLE, "
sql = sql & "DefaultEnabled YESNO, "
sql = sql & "Notes MEMO"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091AreaActivityTemplate", sql
sql = "CREATE TABLE tblV091GeneratedLines ("
sql = sql & "GeneratedLineID AUTOINCREMENT CONSTRAINT pk_tblV091GeneratedLines PRIMARY KEY, "
sql = sql & "JobID TEXT(50) NOT NULL, "
sql = sql & "WBSCode TEXT(50), "
sql = sql & "WBSSubCode TEXT(50), "
sql = sql & "Description TEXT(255), "
sql = sql & "LineType TEXT(50), "
sql = sql & "TargetCategoryName TEXT(100), "
sql = sql & "TargetItemID LONG, "
sql = sql & "QuantityPhysical DOUBLE, "
sql = sql & "UnitName TEXT(50), "
sql = sql & "UnitRateAUD CURRENCY, "
sql = sql & "AdjustmentFactor DOUBLE, "
sql = sql & "GeneratedCostAUD CURRENCY, "
sql = sql & "CleanVolM3 DOUBLE, "
sql = sql & "LsaVolM3 DOUBLE, "
sql = sql & "HazVolM3 DOUBLE, "
sql = sql & "MixedVolM3 DOUBLE, "
sql = sql & "SourceNote MEMO, "
sql = sql & "AppliedToJobLine YESNO, "
sql = sql & "CreatedAt DATETIME"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091GeneratedLines", sql
sql = "CREATE TABLE tblV091JobLineBackup ("
sql = sql & "BackupID AUTOINCREMENT CONSTRAINT pk_tblV091JobLineBackup PRIMARY KEY, "
sql = sql & "BackupAt DATETIME, "
sql = sql & "JobID TEXT(50), "
sql = sql & "JobLineID LONG, "
sql = sql & "CategoryName TEXT(100), "
sql = sql & "ItemID LONG, "
sql = sql & "IncludeItem YESNO, "
sql = sql & "Quantity DOUBLE, "
sql = sql & "BaseUnitRateUSD2009 CURRENCY"
sql = sql & ");"
V091_CreateTableIfMissing "tblV091JobLineBackup", sql
End Sub
Private Sub V091_UpdateBaseSchema()
' v0.9.1 can be re-run over an earlier v0.9.1 build without losing data.
If V091_TableExists("tblV091BuildingInputs") Then
V091_AddFieldIfMissing "tblV091BuildingInputs", "PortfolioManagerNumber", "DOUBLE"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SeniorPMNumber", "DOUBLE"
V091_AddFieldIfMissing "tblV091BuildingInputs", "ProjectManagerNumber", "DOUBLE"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepHoursPerTask", "DOUBLE"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepInitialSurvey", "YESNO"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepBoundariesHepa", "YESNO"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepStagingArea", "YESNO"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepRadSegregation", "YESNO"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepElectricalIsolation", "YESNO"
V091_AddFieldIfMissing "tblV091BuildingInputs", "SitePrepPipingIsolation", "YESNO"
End If
V091_AddFieldIfMissing "tblJobLines", "V091GeneratedQuantity", "DOUBLE"
V091_AddFieldIfMissing "tblJobLines", "V091GeneratedCostAUD", "CURRENCY"
V091_AddFieldIfMissing "tblJobLines", "V091IsGenerated", "YESNO"
V091_AddFieldIfMissing "tblJobLines", "V091OriginalBaseRateUSD2009", "CURRENCY"
V091_AddFieldIfMissing "tblJobLines", "V091LastGeneratedAt", "DATETIME"
End Sub
' ============================================================
' SEED DATA
' ============================================================
Private Sub V091_SeedSettings()
If DCount("*", "tblV091Settings") > 0 Then Exit Sub
V091_AddSettingNum "EquipmentEscalation2009To2026", 1.58014, "Generic template: 2009 to 2026 escalation factor for equipment/materials before AUD conversion."
V091_AddSettingNum "UsdToAudExchangeRate", 1.54, "Generic template: Inputs!C37 exchange factor applied to the whole 1.1.2 equipment/materials/consumables block when the workbook basis is US."
V091_AddSettingNum "SmallToolsPctOfActivityLabor", 0.02, "Generic template: Small Tools = 2% of activity labour/activity cost base."
V091_AddSettingNum "HpEquipmentReplacementPctOfActivityLabor", 0.05, "Generic template: HP Equipment Replacement = 5%."
V091_AddSettingNum "EquipmentOverheadPct", 0.08, "Generic template: DGC OH&P on equipment/materials = 8%."
V091_AddSettingNum "WasteDensityLbPerCuft", 100, "Generic template waste density."
V091_AddSettingNum "M3ToCuft", 35.3147, "Generic template conversion."
V091_AddSettingNum "LbPerWasteTon", 2000, "Short ton conversion used by template."
V091_AddSettingNum "IndustrialWasteRate", 300, "Clean / industrial waste disposal rate."
V091_AddSettingNum "HazardousWasteRate", 500, "Hazardous and mixed waste disposal rate."
End Sub
Private Sub V091_AddSettingNum(ByVal settingName As String, ByVal settingValue As Double, ByVal notes As String)
CurrentDb.Execute "INSERT INTO tblV091Settings (SettingName, SettingValueNumber, Notes, UpdatedAt) VALUES (" & _
V091_Q(settingName) & ", " & V091_SqlNum(settingValue) & ", " & V091_Q(notes) & ", Now());", dbFailOnError
End Sub
Private Sub V091_SeedEquipmentTemplates()
If DCount("*", "tblV091EquipmentTemplate") > 0 Then Exit Sub
' B1 generic BXX class equipment template.
V091_AddEquip "B1", 1, "HEPA filter systems", 2
V091_AddEquip "B1", 2, "Replacement filters", 24
V091_AddEquip "B1", 3, "Respirator", 12
V091_AddEquip "B1", 4, "Rad/Vac wet-dry high eff. vacuum", 1
V091_AddEquip "B1", 5, "Rad/Vac wet-dry high eff. vacuum filters", 5
V091_AddEquip "B1", 6, "Reciprocating Saws", 5
V091_AddEquip "B1", 7, "Pneumatic chipping hammers", 2
V091_AddEquip "B1", 8, "Chipping hammer blades", 20
V091_AddEquip "B1", 9, "Purchase an air compressor", 2
V091_AddEquip "B1", 10, "Jackhammer", 4
V091_AddEquip "B1", 11, "Jackhammer Chisels", 10
V091_AddEquip "B1", 12, "Safety glasses", 15
V091_AddEquip "B1", 13, "Fall protection - harness", 6
V091_AddEquip "B1", 14, "Fall protection - lanyard", 6
V091_AddEquip "B1", 15, "Hardhats", 15
V091_AddEquip "B1", 16, "Hard hat hearing protection", 15
V091_AddEquip "B1", 17, "Trailer rental", 1
V091_AddEquip "B1", 18, "Phone and computer hook-up", 1
V091_AddEquip "B1", 19, "Industrial hygiene instrumentation", 0
V091_AddEquip "B1", 20, "Tractor loader, wheeled", 1
V091_AddEquip "B1", 21, "Wheeled skid steer with concrete hammer", 1
V091_AddEquip "B1", 22, "Excavator", 1
V091_AddEquip "B1", 23, "Floor shaver", 1
V091_AddEquip "B1", 24, "Wall shaver", 1
V091_AddEquip "B1", 25, "Man lift", 2
V091_AddEquip "B1", 26, "Crane rental for building removal", 1
' C1 and D1 use the same editable template as generic radiological classes.
V091_CopyEquipmentClass "B1", "C1"
V091_CopyEquipmentClass "B1", "D1"
CurrentDb.Execute "UPDATE tblV091EquipmentTemplate SET Quantity=2 WHERE FacilityClass='C1' AND ItemID=4;", dbFailOnError
' D2 non-radiological generic equipment template.
V091_AddEquip "D2", 1, "HEPA filter systems", 0
V091_AddEquip "D2", 2, "Replacement filters", 0
V091_AddEquip "D2", 3, "Respirator", 0
V091_AddEquip "D2", 4, "Rad/Vac wet-dry high eff. vacuum", 0
V091_AddEquip "D2", 5, "Rad/Vac wet-dry high eff. vacuum filters", 0
V091_AddEquip "D2", 6, "Reciprocating Saws", 0
V091_AddEquip "D2", 7, "Pneumatic chipping hammers", 2
V091_AddEquip "D2", 8, "Chipping hammer blades", 20
V091_AddEquip "D2", 9, "Purchase an air compressor", 2
V091_AddEquip "D2", 10, "Jackhammer", 4
V091_AddEquip "D2", 11, "Jackhammer Chisels", 10
V091_AddEquip "D2", 12, "Safety glasses", 15
V091_AddEquip "D2", 13, "Fall protection - harness", 0
V091_AddEquip "D2", 14, "Fall protection - lanyard", 0
V091_AddEquip "D2", 15, "Hardhats", 15
V091_AddEquip "D2", 16, "Hard hat hearing protection", 15
V091_AddEquip "D2", 17, "Trailer rental", 1
V091_AddEquip "D2", 18, "Phone and computer hook-up", 1
V091_AddEquip "D2", 19, "Industrial hygiene instrumentation", 0
V091_AddEquip "D2", 20, "Tractor loader, wheeled", 1
V091_AddEquip "D2", 21, "Wheeled skid steer with concrete hammer", 1
V091_AddEquip "D2", 22, "Excavator", 1
V091_AddEquip "D2", 23, "Floor shaver", 0
V091_AddEquip "D2", 24, "Wall shaver", 0
V091_AddEquip "D2", 25, "Man lift", 0
V091_AddEquip "D2", 26, "Crane rental for building removal", 0
End Sub
Private Sub V091_AddEquip(ByVal facilityClass As String, ByVal itemID As Long, ByVal itemName As String, ByVal qty As Double)
CurrentDb.Execute "INSERT INTO tblV091EquipmentTemplate (FacilityClass, CategoryName, ItemID, ItemName, Quantity, DefaultEnabled, Notes) VALUES (" & _
V091_Q(facilityClass) & ", 'Equipment', " & itemID & ", " & V091_Q(itemName) & ", " & V091_SqlNum(qty) & ", True, 'Seeded from generic BXX class equipment template.');", dbFailOnError
End Sub
Private Sub V091_CopyEquipmentClass(ByVal sourceClass As String, ByVal targetClass As String)
CurrentDb.Execute "INSERT INTO tblV091EquipmentTemplate (FacilityClass, CategoryName, ItemID, ItemName, Quantity, DefaultEnabled, Notes) " & _
"SELECT " & V091_Q(targetClass) & ", CategoryName, ItemID, ItemName, Quantity, DefaultEnabled, 'Copied generic class template from ' & FacilityClass & '; edit table if required.' " & _
"FROM tblV091EquipmentTemplate WHERE FacilityClass=" & V091_Q(sourceClass) & ";", dbFailOnError
End Sub
Private Sub V091_SeedConsumableTemplates()
If DCount("*", "tblV091ConsumableTemplate") > 0 Then Exit Sub
V091_AddConsumableAllRad 27, "Coveralls", 2, "man-day"
V091_AddConsumableAllRad 28, "Hoods", 0.1, "man-day"
V091_AddConsumableAllRad 29, "Shoe covers", 4, "man-day"
V091_AddConsumableAllRad 30, "Latex gloves", 4, "man-day"
V091_AddConsumableAllRad 31, "Rubber overshoes", 0.01, "man-day"
V091_AddConsumableAllRad 32, "Gloves", 0.01, "man-day"
V091_AddConsumableAllRad 33, "Dosimeters", 1, "man-year"
V091_AddConsumableAllRad 34, "TLDs", 1, "man-month"
V091_AddConsumableAllRad 35, "Bioassays", 2, "man-year-bioassay"
V091_AddConsumableClass "D2", 27, "Coveralls", 0, "man-day"
V091_AddConsumableClass "D2", 28, "Hoods", 0, "man-day"
V091_AddConsumableClass "D2", 29, "Shoe covers", 0, "man-day"
V091_AddConsumableClass "D2", 30, "Latex gloves", 0, "man-day"
V091_AddConsumableClass "D2", 31, "Rubber overshoes", 0, "man-day"
V091_AddConsumableClass "D2", 32, "Gloves", 0, "man-day"
V091_AddConsumableClass "D2", 33, "Dosimeters", 0, "man-year"
V091_AddConsumableClass "D2", 34, "TLDs", 0, "man-month"
V091_AddConsumableClass "D2", 35, "Bioassays", 0, "man-year-bioassay"
End Sub
Private Sub V091_AddConsumableAllRad(ByVal itemID As Long, ByVal itemName As String, ByVal useRate As Double, ByVal basis As String)
V091_AddConsumableClass "B1", itemID, itemName, useRate, basis
V091_AddConsumableClass "C1", itemID, itemName, useRate, basis
V091_AddConsumableClass "D1", itemID, itemName, useRate, basis
End Sub
Private Sub V091_AddConsumableClass(ByVal facilityClass As String, ByVal itemID As Long, ByVal itemName As String, ByVal useRate As Double, ByVal basis As String)
CurrentDb.Execute "INSERT INTO tblV091ConsumableTemplate (FacilityClass, CategoryName, ItemID, ItemName, UseRate, DurationBasis, DefaultEnabled, Notes) VALUES (" & _
V091_Q(facilityClass) & ", 'Consumables', " & itemID & ", " & V091_Q(itemName) & ", " & V091_SqlNum(useRate) & ", " & V091_Q(basis) & ", True, 'Seeded from generic BXX consumable template.');", dbFailOnError
End Sub
Private Sub V091_SeedStaffUseTemplates()
If DCount("*", "tblV091StaffUseTemplate") > 0 Then Exit Sub
' Not called directly in build; kept available for future table editing.
End Sub
Private Sub V091_SeedAreaActivityTemplates()
If DCount("*", "tblV091AreaActivityTemplate") > 0 Then Exit Sub
' Generic Building / Structure D&D route. UnitRateAUD values are Summary 2026 base rates before crew-factor scaling.
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.1", "Remove contaminated asbestos pipe insulation", "Area Costs", 1, "AsbestosPipeLengthM", "m", 721.8449512, False, False, 0, 0, 0, 0.04615349, "Generic BXX asbestos pipe activity. Excel parity v0.9.1: contaminated pipe insulation waste routes to mixed waste, not clean waste."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.1", "Remove asbestos tile", "Area Costs", 2, "AsbestosTileAreaM2", "m2", 58.25179202, False, False, 0.03061509, 0, 0, 0, "Generic BXX asbestos tile activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.2", "System Clean", "Area Costs", 3, "TotalAreaM2", "m2", 603.9881484, True, False, 0.210971851, 0, 0, 0, "Generic BXX system clean activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.2", "System LLW", "Area Costs", 4, "TotalAreaM2", "m2", 414.5889597, True, True, 0, 0.021826187, 0, 0, "Generic BXX radiological system LLW activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.2", "System Hazardous", "Area Costs", 5, "TotalAreaM2", "m2", 11.19679335, True, False, 0, 0, 0.000347973, 0, "Generic BXX system hazardous activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.2", "System Mixed", "Area Costs", 6, "TotalAreaM2", "m2", 8.8206536, True, True, 0, 0, 0, 0.002881651, "Generic BXX radiological mixed-system activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.3", "Decon Cleaning", "Area Costs", 7, "TotalAreaM2", "m2", 18.15004985, True, False, 0.009905621, 0, 0, 0, "Generic BXX decon clean activity."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.3", "Decontaminate Hot Cells", "Area Costs", 31, "HotCellAreaM2", "m2", 120.8368922, True, True, 0, 0.002542516119, 0, 0, "Generic hot-cell decontamination activity. Quantity defaults to total area when hot-cell scope is included and no hot-cell area is entered."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.3", "Remove hazardous material", "Model Allowances", 7, "TotalAreaM2", "m2", 21.15184624, True, False, 0, 0, 0.2726278792, 0, "Waste-volume carrier for hazardous material. Cost is activated only when hot-cell scope is included; otherwise rate is set to zero to match non-hot-cell Excel tabs."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.3", "Decon Contaminated", "Area Costs", 9, "TotalAreaM2", "m2", 427.7725506, True, True, 0, 0.000844498, 0, 0, "Generic BXX radiological contaminated decon."
V091_AddAreaActivity "Building / Structure D&D", "1.5", "1.5.4", "Final Survey", "Area Costs", 34, "TotalAreaM2", "m2", 242.3439213, False, False, 0, 0, 0, 0, "Generic BXX final survey."
V091_AddAreaActivity "Building / Structure D&D", "1.6", "1.6.1", "Remove Building", "Area Costs", 35, "TotalAreaM2", "m2", 455.2381391, True, False, 0.770874078, 0, 0, 0, "Generic BXX remove building."
V091_AddAreaActivity "Building / Structure D&D", "1.6", "1.6.2", "Remove Hot Cell", "Area Costs", 31, "HotCellAreaM2", "m2", 150.7114894, True, True, 0.1222098525, 0, 0, 0, "Generic hot-cell removal activity. Quantity defaults to total area when hot-cell scope is included and no hot-cell area is entered."
V091_AddAreaActivity "Building / Structure D&D", "1.6", "1.6.3", "Grade and Seed", "Area Costs", 36, "GradeAreaM2", "m2", 15.82447093, False, False, 0, 0, 0, 0, "Generic BXX grade and seed at 2x footprint."
V091_AddAreaActivity "Building / Structure D&D", "1.6", "1.6.4", "Backfill", "Area Costs", 37, "BackfillM3", "m3", 23.78663385, False, False, 0, 0, 0, 0, "Generic BXX backfill using SI depth."
' Generic Soil / Outdoor Remediation route. UnitRateAUD values are Summary 2026 base rates before crew-factor scaling.
' v0.9.1 Excel parity cleanup:
' B18-style soil/outdoor remediation is generated as one direct WBS 1.5.1
' Remove Soil and Asphalt activity, rather than approximating it through
' legacy System Hazardous + System Mixed + Soil rows.
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.5", "1.5.1", "Remove Soil and Asphalt", "Area Costs", 28, "TotalAreaM2", "m2", 85.25913043, True, False, 0, 0, 0, 0, "Excel parity v0.9.1 soil/outdoor route: direct WBS 1.5.1 Remove Soil and Asphalt line."
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.5", "1.5.1", "Clean soil/asphalt waste volume carrier", "Model Allowances", 7, "CleanSoilVolumeM3", "m3", 0, False, False, 1, 0, 0, 0, "Excel parity v0.9.1 soil/outdoor route: zero-cost clean soil/asphalt volume carrier for waste disposal."
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.5", "1.5.1", "Contaminated soil/asphalt tracked volume", "Model Allowances", 7, "ContaminatedSoilVolumeM3", "m3", 0, False, False, 0, 1, 0, 0, "Excel parity v0.9.1 soil/outdoor route: zero-cost contaminated soil/asphalt volume tracker; cost rate remains zero in template."
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.5", "1.5.4", "Final Survey", "Area Costs", 34, "TotalAreaM2", "m2", 242.3439213, False, False, 0, 0, 0, 0, "Generic soil/outdoor final survey rate."
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.6", "1.6.3", "Grade and Seed", "Area Costs", 36, "TotalAreaM2", "m2", 15.82447093, False, False, 0, 0, 0, 0, "Generic soil/outdoor grade and seed."
V091_AddAreaActivity "Soil / Outdoor Remediation", "1.6", "1.6.4", "Backfill", "Area Costs", 37, "BackfillM3", "m3", 23.78663385, False, False, 0, 0, 0, 0, "Generic soil/outdoor backfill using SI depth."
End Sub
Private Sub V091_AddAreaActivity(ByVal basis As String, ByVal wbsCode As String, ByVal wbsSub As String, ByVal descText As String, ByVal cat As String, ByVal itemID As Long, ByVal source As String, ByVal unitName As String, ByVal rate As Double, ByVal applyAdj As Boolean, ByVal reqRad As Boolean, ByVal cleanRate As Double, ByVal lsaRate As Double, ByVal hazRate As Double, ByVal mixedRate As Double, ByVal notes As String)
CurrentDb.Execute "INSERT INTO tblV091AreaActivityTemplate (EstimateBasis, WBSCode, WBSSubCode, Description, CategoryName, ItemID, QuantitySource, UnitName, UnitRateAUD, ApplyRemovalAdjustment, RequiresRadiological, CleanVolRateM3, LsaVolRateM3, HazVolRateM3, MixedVolRateM3, DefaultEnabled, Notes) VALUES (" & _
V091_Q(basis) & ", " & V091_Q(wbsCode) & ", " & V091_Q(wbsSub) & ", " & V091_Q(descText) & ", " & V091_Q(cat) & ", " & itemID & ", " & V091_Q(source) & ", " & V091_Q(unitName) & ", " & V091_SqlNum(rate) & ", " & IIf(applyAdj, "True", "False") & ", " & IIf(reqRad, "True", "False") & ", " & V091_SqlNum(cleanRate) & ", " & V091_SqlNum(lsaRate) & ", " & V091_SqlNum(hazRate) & ", " & V091_SqlNum(mixedRate) & ", True, " & V091_Q(notes) & ");", dbFailOnError
End Sub
Private Sub V091_SeedDefaultInputs()
' v0.9.1 UI/state cleanup:
' Do NOT create input shells for existing tblJobs during build.
' This prevents the form from opening on JOB-001 or any other old job.
' Input rows are created only when the user clicks New / Attach Job or
' explicitly selects an existing job from the Load Job combo.
End Sub
' ============================================================
' MODEL ALLOWANCE CATEGORY / LIBRARY ITEMS
' ============================================================
Private Sub V091_EnsureModelAllowanceLibraryItems()
If DCount("*", "tblCategories", "CategoryName='Model Allowances'") = 0 Then
CurrentDb.Execute "INSERT INTO tblCategories (CategoryName, DisplayOrder) VALUES ('Model Allowances', 6);", dbFailOnError
End If
V091_EnsureLibraryItem "Model Allowances", 1, "1.1", "1.1.2", "Small Tools Allowance", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 2, "1.1", "1.1.2", "HP Equipment Replacement Allowance", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 3, "1.1", "1.1.2", "DGC OH&P on Equipment and Materials", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 4, "1.2", "1.2.3", "General Employee Training Labour", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 5, "1.7", "1.7", "Mixed Waste Disposal", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 6, "1.7", "1.7", "Clean Waste Disposal", "AUD", 1
V091_EnsureLibraryItem "Model Allowances", 7, "1.5", "1.5.3", "Zero-Cost Waste Volume Carrier", "AUD", 1
End Sub
Private Sub V091_EnsureLibraryItem(ByVal cat As String, ByVal itemID As Long, ByVal wbs As String, ByVal wbsSub As String, ByVal itemName As String, ByVal unitName As String, ByVal baseRate As Double)
If DCount("*", "tblCostLibrary", "CategoryName=" & V091_Q(cat) & " AND ItemID=" & itemID) > 0 Then Exit Sub
CurrentDb.Execute "INSERT INTO tblCostLibrary (CategoryName, ItemID, IsActive, WBSCode, WBSSubCode, ItemName, UnitName, BaseUnitRateUSD2009, CreatedAt, UpdatedAt) VALUES (" & _
V091_Q(cat) & ", " & itemID & ", True, " & V091_Q(wbs) & ", " & V091_Q(wbsSub) & ", " & V091_Q(itemName) & ", " & V091_Q(unitName) & ", " & V091_SqlNum(baseRate) & ", Now(), Now());", dbFailOnError
End Sub
' ============================================================
' QUERIES
' ============================================================
Private Sub V091_CreateQueries()
V091_SaveQuery "qryV091GeneratedLineEdit", _
"SELECT GeneratedLineID, JobID, WBSCode, WBSSubCode, Description, LineType, TargetCategoryName, TargetItemID, QuantityPhysical, UnitName, UnitRateAUD, AdjustmentFactor, GeneratedCostAUD, CleanVolM3, LsaVolM3, HazVolM3, MixedVolM3, SourceNote, AppliedToJobLine " & _
"FROM tblV091GeneratedLines ORDER BY JobID, WBSCode, WBSSubCode, GeneratedLineID;"
V091_SaveQuery "qryV091Totals", _
"SELECT JobID, Sum(Nz(GeneratedCostAUD,0)) AS V091GeneratedSubtotalAUD, Sum(Nz(CleanVolM3,0)) AS TotalCleanVolM3, Sum(Nz(LsaVolM3,0)) AS TotalLsaVolM3, Sum(Nz(HazVolM3,0)) AS TotalHazVolM3, Sum(Nz(MixedVolM3,0)) AS TotalMixedVolM3 " & _
"FROM tblV091GeneratedLines GROUP BY JobID;"
V091_SaveQuery "qryV091WbsSummary", _
"SELECT JobID, WBSCode, WBSSubCode, Min(Description) AS ExampleDescription, Sum(Nz(GeneratedCostAUD,0)) AS WbsCostAUD, Sum(Nz(CleanVolM3,0)) AS WbsCleanVolM3, Sum(Nz(LsaVolM3,0)) AS WbsLsaVolM3, Sum(Nz(HazVolM3,0)) AS WbsHazVolM3, Sum(Nz(MixedVolM3,0)) AS WbsMixedVolM3 " & _
"FROM tblV091GeneratedLines GROUP BY JobID, WBSCode, WBSSubCode ORDER BY JobID, WBSCode, WBSSubCode;"
V091_SaveQuery "qryV091PortfolioOverview", _
"SELECT j.JobID, j.JobName, b.BuildingCode, b.FacilityClass, b.FacilityType, b.EstimateBasis, b.TotalAreaM2, b.ProjectDurationDays, b.CrewSize, b.RemovalAdjustmentPct, Nz(v.V091GeneratedSubtotalAUD,0) AS V091GeneratedSubtotalAUD, Nz(g.SubtotalAUD,0) AS FineTuneSubtotalAUD, Nz(g.GrandTotalAUD,0) AS FineTuneGrandTotalAUD, Nz(g.SubtotalAUD,0)-Nz(v.V091GeneratedSubtotalAUD,0) AS DeltaAUD, b.LastGeneratedAt " & _
"FROM ((tblJobs AS j LEFT JOIN tblV091BuildingInputs AS b ON j.JobID=b.JobID) LEFT JOIN qryV091Totals AS v ON j.JobID=v.JobID) LEFT JOIN qryGrandTotals AS g ON j.JobID=g.JobID ORDER BY j.JobID;"
End Sub
Private Sub V091_SaveQuery(ByVal queryName As String, ByVal sqlText As String)
On Error Resume Next
CurrentDb.QueryDefs.Delete queryName
On Error GoTo 0
CurrentDb.CreateQueryDef queryName, sqlText
End Sub
' ============================================================
' GENERATION ENGINE
' ============================================================
Public Sub V091_GenerateGenericEstimate(ByVal jobID As String)
On Error GoTo ErrHandler
If DCount("*", "tblJobs", "JobID=" & V091_Q(jobID)) = 0 Then Err.Raise vbObjectError + 8210, , "JobID not found: " & jobID
V091_CreateInputIfMissing jobID
CurrentDb.Execute "DELETE FROM tblV091GeneratedLines WHERE JobID=" & V091_Q(jobID) & ";", dbFailOnError
V091_GenerateManagementStaff jobID
V091_GeneratePlanningAndSitePrep jobID
V091_GenerateDetailedCharacterization jobID
V091_GenerateAreaActivities jobID
V091_GenerateWasteDisposal jobID
V091_GenerateEquipmentConsumablesAndAllowances jobID
CurrentDb.Execute "UPDATE tblV091BuildingInputs SET LastGeneratedAt=Now(), UpdatedAt=Now() WHERE JobID=" & V091_Q(jobID) & ";", dbFailOnError
Exit Sub
ErrHandler:
Err.Raise Err.Number, , "V091_GenerateGenericEstimate failed for " & jobID & ": " & Err.Description
End Sub
Private Sub V091_GenerateManagementStaff(ByVal jobID As String)
Dim d As Double
Dim portfolioUse As Double
Dim seniorUse As Double
Dim wasteUse As Double
Dim portfolioNumber As Double
Dim seniorNumber As Double
Dim wasteNumber As Double
d = V091_InputDbl(jobID, "ProjectDurationDays", 105)
' These are explicit Excel judgement levers. Number and use factor both
' exist in the workbook staff table. Defaults preserve the validated v0.8.9
' outputs when fields are left blank.
portfolioUse = V091_InputDbl(jobID, "PortfolioManagerUseFactor", 1)
seniorUse = V091_InputDbl(jobID, "SeniorPMUseFactor", 0.5)
wasteUse = V091_InputDbl(jobID, "ProjectManagerUseFactor", 0.5)
portfolioNumber = V091_InputDbl(jobID, "PortfolioManagerNumber", 1)
seniorNumber = V091_InputDbl(jobID, "SeniorPMNumber", 1)
wasteNumber = V091_InputDbl(jobID, "ProjectManagerNumber", 1)
V091_AddLine jobID, "1.1", "1.1.1", "Portfolio Manager", "Labour", "Labour", 48, d * 8 * portfolioNumber * portfolioUse, "hr", V091_LabourRate(48), 1, 0, 0, 0, 0, "Portfolio manager number and use factor from estimate controls."
V091_AddLine jobID, "1.1", "1.1.1", "Senior Project Manager / Characterization SME", "Labour", "Labour", 47, d * 10 * seniorNumber * seniorUse, "hr", V091_LabourRate(47), 1, 0, 0, 0, 0, "Senior PM / characterization SME number and use factor from estimate controls."
V091_AddLine jobID, "1.1", "1.1.1", "Project Manager / Waste Management", "Labour", "Labour", 46, d * 10 * wasteNumber * wasteUse, "hr", V091_LabourRate(46), 1, 0, 0, 0, 0, "Project manager / waste manager number and use factor from estimate controls."
End Sub
Private Sub V091_GeneratePlanningAndSitePrep(ByVal jobID As String)
Dim crew As Double
Dim peopleTraining As Double
Dim costPerHourTraining As Double
Dim procHours As Double
Dim qaHours As Double
Dim siteTasks As Double
Dim siteHoursPerTask As Double
Dim portfolioNumber As Double
Dim seniorNumber As Double
Dim wasteNumber As Double
crew = V091_InputDbl(jobID, "CrewSize", 24)
procHours = V091_InputDbl(jobID, "ProcedureHours", 80)
qaHours = V091_InputDbl(jobID, "QASafetyHours", 80)
siteTasks = V091_SitePrepSelectedTaskCount(jobID)
siteHoursPerTask = V091_InputDbl(jobID, "SitePrepHoursPerTask", 16)
If siteHoursPerTask <= 0 Then siteHoursPerTask = 16
portfolioNumber = V091_InputDbl(jobID, "PortfolioManagerNumber", 1)
seniorNumber = V091_InputDbl(jobID, "SeniorPMNumber", 1)
wasteNumber = V091_InputDbl(jobID, "ProjectManagerNumber", 1)
' Procedure development and QA are explicit duration levers from the Excel template.
V091_AddLine jobID, "1.2", "1.2.1", "Procedure Development - Project Specialist", "Labour", "Labour", 45, 2 * procHours, "hr", V091_LabourRate(45), 1, 0, 0, 0, 0, "2 Project Specialists x procedure hours."
V091_AddLine jobID, "1.2", "1.2.1", "Procedure Development - Project Manager", "Labour", "Labour", 46, procHours, "hr", V091_LabourRate(46), 1, 0, 0, 0, 0, "1 Project Manager x procedure hours."
V091_AddLine jobID, "1.2", "1.2.2", "QA/Safety Documents - Project Specialist", "Labour", "Labour", 45, 2 * qaHours, "hr", V091_LabourRate(45), 1, 0, 0, 0, 0, "2 Project Specialists x QA/safety hours."
V091_AddLine jobID, "1.2", "1.2.2", "QA/Safety Documents - Project Manager", "Labour", "Labour", 46, qaHours, "hr", V091_LabourRate(46), 1, 0, 0, 0, 0, "1 Project Manager x QA/safety hours."
V091_AddLine jobID, "1.2", "1.2.3", "Site Mobilization", "Labour", "Labour", 45, crew * 40, "hr", V091_LabourRate(45), 1, 0, 0, 0, 0, "40 hr x decom crew size."
If V091_InputBool(jobID, "IncludeTraining", True) Then
peopleTraining = crew + portfolioNumber + seniorNumber + wasteNumber
costPerHourTraining = portfolioNumber * V091_LabourRate(48) + seniorNumber * V091_LabourRate(47) + wasteNumber * V091_LabourRate(46) + crew * V091_LabourRate(45)
V091_AddAllowance jobID, "1.2", "1.2.3", "General Employee Training Labour", 4, V091_TrainingLabourCost(peopleTraining, costPerHourTraining), "Excel parity v0.9.1: WBS 1.2.3 includes GET labour only; direct medical/test costs are shown in the Excel subtable but are not rolled into the WBS total."
End If
If V091_InputBool(jobID, "IncludeSitePrep", True) And siteTasks > 0 Then
V091_AddLine jobID, "1.3", "1.3.2", "Site Prep - Project Specialists", "Labour", "Labour", 45, 4 * siteHoursPerTask * siteTasks, "hr", V091_LabourRate(45), 1, 0, 0, 0, 0, "Selected site prep tasks x hours/task x 4 specialists."
V091_AddLine jobID, "1.3", "1.3.2", "Site Prep - Project Manager", "Labour", "Labour", 46, siteHoursPerTask * siteTasks, "hr", V091_LabourRate(46), 1, 0, 0, 0, 0, "Selected site prep tasks x hours/task x 1 PM."
End If
End Sub
Private Function V091_SitePrepSelectedTaskCount(ByVal jobID As String) As Double
Dim selectedCount As Double
Dim legacyCount As Double
selectedCount = 0
If V091_InputBool(jobID, "SitePrepInitialSurvey", False) Then selectedCount = selectedCount + 1
If V091_InputBool(jobID, "SitePrepBoundariesHepa", False) Then selectedCount = selectedCount + 1
If V091_InputBool(jobID, "SitePrepStagingArea", False) Then selectedCount = selectedCount + 1
If V091_InputBool(jobID, "SitePrepRadSegregation", False) Then selectedCount = selectedCount + 1
If V091_InputBool(jobID, "SitePrepElectricalIsolation", False) Then selectedCount = selectedCount + 1
If V091_InputBool(jobID, "SitePrepPipingIsolation", False) Then selectedCount = selectedCount + 1
If selectedCount > 0 Then
V091_SitePrepSelectedTaskCount = selectedCount
Else
legacyCount = V091_InputDbl(jobID, "SitePrepTaskCount", 0)
V091_SitePrepSelectedTaskCount = legacyCount
End If
End Function
Private Function V091_TrainingLabourCost(ByVal people As Double, ByVal costPerHour As Double) As Double
' Excel parity v0.9.1:
' The Excel training subtable shows direct medical/test costs plus labour costs,
' but the WBS 1.2.3 roll-up carries the General Employee Training labour component.
' Therefore this returns only the 55-hour labour component, not the direct test-cost component.
Dim labourCost As Double
labourCost = (4 * costPerHour) + (1 * costPerHour) + (1 * costPerHour) + (1 * costPerHour) + (8 * costPerHour) + (40 * costPerHour)
V091_TrainingLabourCost = labourCost
End Function
Private Sub V091_GenerateDetailedCharacterization(ByVal jobID As String)
Dim specCount As Double
Dim pmCount As Double
Dim hoursEach As Double
If Not V091_InputBool(jobID, "IncludeDetailedCharacterization", True) Then Exit Sub
specCount = V091_InputDbl(jobID, "CharacterizationSpecialistCount", 8)
pmCount = V091_InputDbl(jobID, "CharacterizationPMCount", 2)
hoursEach = V091_InputDbl(jobID, "CharacterizationHoursPerPerson", 320)
V091_AddLine jobID, "1.4", "1.4", "Detailed Characterization - Project Specialists", "Labour", "Labour", 45, specCount * hoursEach, "hr", V091_LabourRate(45), 1, 0, 0, 0, 0, "Characterization specialist count and hours from estimate controls."
V091_AddLine jobID, "1.4", "1.4", "Detailed Characterization - Project Managers", "Labour", "Labour", 46, pmCount * hoursEach, "hr", V091_LabourRate(46), 1, 0, 0, 0, 0, "Characterization PM count and hours from estimate controls."
End Sub
Private Sub V091_GenerateAreaActivities(ByVal jobID As String)
Dim rs As DAO.Recordset
Dim basis As String, isRad As Boolean
Dim area As Double, footprint As Double, remMult As Double, crewFactor As Double
Dim qty As Double, cleanV As Double, lsaV As Double, hazV As Double, mixedV As Double
Dim appliedFactor As Double, rateAUD As Double
basis = V091_InputText(jobID, "EstimateBasis", "Building / Structure D&D")
isRad = V091_InputBool(jobID, "IsRadiological", True)
area = V091_InputDbl(jobID, "TotalAreaM2", 0)
footprint = V091_InputDbl(jobID, "FootprintAreaM2", area)
If footprint <= 0 Then footprint = area
remMult = 1 + V091_InputDbl(jobID, "RemovalAdjustmentPct", 0) / 100
If remMult < 0 Then remMult = 0
' v0.9.1 formula parity cleanup:
' The Excel building sheets calculate the activity rate as:
' Summary 2026 base rate x (crew size / 12)
' The class/type adjustment is then represented by RemovalAdjustmentPct,
' applied as (1 + RemovalAdjustmentPct) only to rows that use the removal adjustment.
crewFactor = V091_InputDbl(jobID, "CrewSize", 12) / 12
If crewFactor <= 0 Then crewFactor = 1
Set rs = CurrentDb.OpenRecordset("SELECT * FROM tblV091AreaActivityTemplate WHERE DefaultEnabled=True AND EstimateBasis=" & V091_Q(basis) & " ORDER BY WBSCode, WBSSubCode, TemplateID;", dbOpenSnapshot)
Do While Not rs.EOF
If (Not Nz(rs!RequiresRadiological, False)) Or isRad Then
qty = V091_ActivityQty(jobID, Nz(rs!quantitySource, ""), area, footprint)
cleanV = qty * CDbl(Nz(rs!CleanVolRateM3, 0))
lsaV = qty * CDbl(Nz(rs!LsaVolRateM3, 0))
hazV = qty * CDbl(Nz(rs!HazVolRateM3, 0))
mixedV = qty * CDbl(Nz(rs!MixedVolRateM3, 0))
If qty > 0 Or cleanV <> 0 Or lsaV <> 0 Or hazV <> 0 Or mixedV <> 0 Then
rateAUD = CDbl(Nz(rs!unitRateAUD, 0)) * crewFactor
If basis = "Building / Structure D&D" And Nz(rs!Description, "") = "Remove hazardous material" Then
If Not V091_InputBool(jobID, "IncludeHotCell", False) Then rateAUD = 0
End If
appliedFactor = 1
If Nz(rs!ApplyRemovalAdjustment, False) Then appliedFactor = remMult
V091_AddLine jobID, Nz(rs!wbsCode, ""), Nz(rs!WBSSubCode, ""), Nz(rs!Description, ""), "AreaActivity", Nz(rs!categoryName, ""), CLng(Nz(rs!itemID, 0)), qty, Nz(rs!unitName, ""), rateAUD, appliedFactor, cleanV, lsaV, hazV, mixedV, Nz(rs!notes, "") & " Rate = Summary 2026 base rate x crew size/12."
End If
End If
rs.MoveNext
Loop
rs.Close
End Sub
Private Function V091_ActivityQty(ByVal jobID As String, ByVal quantitySource As String, ByVal area As Double, ByVal footprint As Double) As Double
Dim removalDepth As Double, backfillDepth As Double, pctClean As Double, pctCont As Double
removalDepth = V091_InputDbl(jobID, "RemovalDepthM", 1)
backfillDepth = V091_InputDbl(jobID, "BackfillDepthM", 1)
pctClean = V091_InputDbl(jobID, "PercentClean", 50) / 100
pctCont = V091_InputDbl(jobID, "PercentContaminated", 50) / 100
Select Case quantitySource
Case "TotalAreaM2"
V091_ActivityQty = area
Case "AsbestosPipeLengthM"
V091_ActivityQty = V091_InputDbl(jobID, "AsbestosPipeLengthM", 0)
Case "AsbestosTileAreaM2"
V091_ActivityQty = V091_InputDbl(jobID, "AsbestosTileAreaM2", 0)
Case "GradeAreaM2"
V091_ActivityQty = footprint * 2
Case "BackfillM3"
V091_ActivityQty = footprint * backfillDepth
Case "CleanSoilVolumeM3"
V091_ActivityQty = area * removalDepth * pctClean
Case "ContaminatedSoilVolumeM3"
V091_ActivityQty = area * removalDepth * pctCont
Case "HotCellAreaM2"
If V091_InputBool(jobID, "IncludeHotCell", False) Then
V091_ActivityQty = V091_InputDbl(jobID, "HotCellAreaM2", 0)
If V091_ActivityQty <= 0 Then V091_ActivityQty = area
Else
V091_ActivityQty = 0
End If
Case "Zero"
V091_ActivityQty = 0
Case Else
V091_ActivityQty = 0
End Select
End Function
Private Sub V091_GenerateWasteDisposal(ByVal jobID As String)
Dim cleanM3 As Double, lsaM3 As Double, hazM3 As Double, mixedM3 As Double
Dim density As Double, m3ToCuft As Double, lbPerTon As Double
Dim cleanTons As Double, hazTons As Double, mixedTons As Double
cleanM3 = Nz(DSum("CleanVolM3", "tblV091GeneratedLines", "JobID=" & V091_Q(jobID)), 0)
lsaM3 = Nz(DSum("LsaVolM3", "tblV091GeneratedLines", "JobID=" & V091_Q(jobID)), 0)
hazM3 = Nz(DSum("HazVolM3", "tblV091GeneratedLines", "JobID=" & V091_Q(jobID)), 0)
mixedM3 = Nz(DSum("MixedVolM3", "tblV091GeneratedLines", "JobID=" & V091_Q(jobID)), 0)
density = V091_SettingDbl("WasteDensityLbPerCuft", 100)
m3ToCuft = V091_SettingDbl("M3ToCuft", 35.3147)
lbPerTon = V091_SettingDbl("LbPerWasteTon", 2000)
cleanTons = cleanM3 * m3ToCuft * density / lbPerTon
hazTons = hazM3 * m3ToCuft * density / lbPerTon
mixedTons = mixedM3 * m3ToCuft * density / lbPerTon
V091_AddLine jobID, "1.7", "1.7", "Clean Waste Disposal", "Waste", "Model Allowances", 6, cleanTons, "ton", V091_SettingDbl("IndustrialWasteRate", 300), 1, 0, 0, 0, 0, "Waste derived from generated clean volume."
V091_AddLine jobID, "1.7", "1.7", "Hazardous Waste Disposal", "Waste", "Waste Disposal", 37, hazTons, "ton", V091_SettingDbl("HazardousWasteRate", 500), 1, 0, 0, 0, 0, "Waste derived from generated hazardous volume."
V091_AddLine jobID, "1.7", "1.7", "Mixed Waste Disposal", "Waste", "Model Allowances", 5, mixedTons, "ton", V091_SettingDbl("HazardousWasteRate", 500), 1, 0, 0, 0, 0, "Waste derived from generated mixed volume."
V091_AddLine jobID, "1.7", "1.7", "LSA/LLW Stored Waste", "Waste", "Waste Disposal", 38, lsaM3, "m3", 0, 1, 0, 0, 0, 0, "LSA/LLW volume tracked; cost rate is zero in template."
End Sub
Private Sub V091_GenerateEquipmentConsumablesAndAllowances(ByVal jobID As String)
' Excel formula parity for 1.1.2 Equipment, Materials & Consumables:
' S8 = (H46 + M58 + K60 + K62 + K64) * Inputs!C37
' where:
' H46 = escalated equipment subtotal before AUD conversion
' M58 = escalated consumables subtotal before AUD conversion
' K60 = Small Tools = 2% of (T6 - T8)
' K62 = HP Equipment Replacement = 5% of (T6 - T8)
' K64 = DGC OH&P = 8% of (H46 + M58 + K60 + K62 + any extra basis, usually zero)
'
' To keep generated lines in AUD, equipment/consumable rates and allowance costs are multiplied
' by the workbook exchange factor here, but the allowance base is still calculated pre-FX.
Dim facilityClass As String
Dim rs As DAO.Recordset
Dim qty As Double, rateUSD As Double, rateAUD As Double, durationDays As Double, crew As Double
Dim equipCostPreFx As Double, consCostPreFx As Double, activityCostBase As Double
Dim smallToolsPreFx As Double, hpReplacePreFx As Double, ohpPreFx As Double
Dim fx As Double
facilityClass = V091_InputText(jobID, "FacilityClass", "B1")
durationDays = V091_InputDbl(jobID, "WorkDays", V091_InputDbl(jobID, "ProjectDurationDays", 105))
crew = V091_InputDbl(jobID, "CrewSize", 24)
fx = V091_SettingDbl("UsdToAudExchangeRate", 1.54)
Set rs = CurrentDb.OpenRecordset("SELECT * FROM tblV091EquipmentTemplate WHERE FacilityClass=" & V091_Q(facilityClass) & " AND DefaultEnabled=True ORDER BY ItemID;", dbOpenSnapshot)
Do While Not rs.EOF
qty = CDbl(Nz(rs!quantity, 0))
If qty > 0 Then
rateUSD = V091_BaseRate("Equipment", CLng(rs!itemID)) * V091_SettingDbl("EquipmentEscalation2009To2026", 1.58014)
rateAUD = rateUSD * fx
equipCostPreFx = equipCostPreFx + (qty * rateUSD)
V091_AddLine jobID, "1.1", "1.1.2", Nz(rs!itemName, "Equipment"), "Equipment", "Equipment", CLng(rs!itemID), qty, "item", rateAUD, 1, 0, 0, 0, 0, "Class equipment template quantity. Excel parity: escalated equipment cost converted by Inputs!C37."
End If
rs.MoveNext
Loop
rs.Close
If V091_InputBool(jobID, "IncludeConsumables", True) Then
Set rs = CurrentDb.OpenRecordset("SELECT * FROM tblV091ConsumableTemplate WHERE FacilityClass=" & V091_Q(facilityClass) & " AND DefaultEnabled=True ORDER BY ItemID;", dbOpenSnapshot)
Do While Not rs.EOF
qty = V091_ConsumableQty(jobID, CDbl(Nz(rs!useRate, 0)), Nz(rs!DurationBasis, "man-day"), durationDays, crew)
If qty > 0 Then
rateUSD = V091_BaseRate("Consumables", CLng(rs!itemID)) * V091_SettingDbl("EquipmentEscalation2009To2026", 1.58014)
rateAUD = rateUSD * fx
consCostPreFx = consCostPreFx + (qty * rateUSD)
V091_AddLine jobID, "1.1", "1.1.2", Nz(rs!itemName, "Consumable"), "Consumable", "Consumables", CLng(rs!itemID), qty, Nz(rs!DurationBasis, "unit"), rateAUD, 1, 0, 0, 0, 0, "Class consumable template quantity. Excel parity: escalated consumable cost converted by Inputs!C37."
End If
rs.MoveNext
Loop
rs.Close
End If
activityCostBase = V091_ActivityCostBase(jobID)
smallToolsPreFx = activityCostBase * V091_SettingDbl("SmallToolsPctOfActivityLabor", 0.02)
hpReplacePreFx = activityCostBase * V091_SettingDbl("HpEquipmentReplacementPctOfActivityLabor", 0.05)
ohpPreFx = (equipCostPreFx + consCostPreFx + smallToolsPreFx + hpReplacePreFx) * V091_SettingDbl("EquipmentOverheadPct", 0.08)
V091_AddAllowance jobID, "1.1", "1.1.2", "Small Tools - 2% of activity cost base", 1, smallToolsPreFx * fx, "Generic BXX allowance. Excel parity: K60 included in S8 and converted by Inputs!C37."
V091_AddAllowance jobID, "1.1", "1.1.2", "HP Equipment Replacement - 5% of activity cost base", 2, hpReplacePreFx * fx, "Generic BXX allowance. Excel parity: K62 included in S8 and converted by Inputs!C37."
V091_AddAllowance jobID, "1.1", "1.1.2", "DGC OH&P on equipment and materials", 3, ohpPreFx * fx, "Generic BXX allowance. Excel parity: K64 included in S8 and converted by Inputs!C37."
End Sub
Private Function V091_ConsumableQty(ByVal jobID As String, ByVal useRate As Double, ByVal basis As String, ByVal days As Double, ByVal crew As Double) As Double
Select Case basis
Case "man-day"
V091_ConsumableQty = useRate * days * crew
Case "man-month"
V091_ConsumableQty = useRate * V091_InputDbl(jobID, "ConsumableMonths", 3) * crew
Case "man-year"
V091_ConsumableQty = useRate * V091_InputDbl(jobID, "DosimeterYears", 0.25) * crew
Case "man-year-bioassay"
V091_ConsumableQty = useRate * V091_InputDbl(jobID, "BioassayYears", 0.5) * crew
Case Else
V091_ConsumableQty = 0
End Select
End Function
Private Function V091_ActivityCostBase(ByVal jobID As String) As Double
Dim whereText As String
whereText = "JobID=" & V091_Q(jobID) & " AND WBSSubCode<>'1.1.1' AND WBSSubCode<>'1.1.2' AND WBSSubCode<>'1.7'"
V091_ActivityCostBase = Nz(DSum("GeneratedCostAUD", "tblV091GeneratedLines", whereText), 0)
End Function
Private Sub V091_AddAllowance(ByVal jobID As String, ByVal wbsCode As String, ByVal wbsSub As String, ByVal descText As String, ByVal allowanceItemID As Long, ByVal costAUD As Double, ByVal note As String)
If costAUD <= 0 Then Exit Sub
V091_AddLine jobID, wbsCode, wbsSub, descText, "Allowance", "Model Allowances", allowanceItemID, costAUD, "AUD", 1, 1, 0, 0, 0, 0, note
End Sub
Private Sub V091_AddLine(ByVal jobID As String, ByVal wbsCode As String, ByVal wbsSub As String, ByVal descText As String, ByVal lineType As String, ByVal targetCat As String, ByVal targetItemID As Long, ByVal qty As Double, ByVal unitName As String, ByVal unitRateAUD As Double, ByVal adjustmentFactor As Double, ByVal cleanV As Double, ByVal lsaV As Double, ByVal hazV As Double, ByVal mixedV As Double, ByVal sourceNote As String)
Dim amount As Double
If adjustmentFactor < 0 Then adjustmentFactor = 0
amount = qty * unitRateAUD * adjustmentFactor
If amount = 0 And cleanV = 0 And lsaV = 0 And hazV = 0 And mixedV = 0 Then Exit Sub
CurrentDb.Execute "INSERT INTO tblV091GeneratedLines " & _
"(JobID, WBSCode, WBSSubCode, Description, LineType, TargetCategoryName, TargetItemID, QuantityPhysical, UnitName, UnitRateAUD, AdjustmentFactor, GeneratedCostAUD, CleanVolM3, LsaVolM3, HazVolM3, MixedVolM3, SourceNote, AppliedToJobLine, CreatedAt) VALUES (" & _
V091_Q(jobID) & ", " & V091_Q(wbsCode) & ", " & V091_Q(wbsSub) & ", " & V091_Q(descText) & ", " & V091_Q(lineType) & ", " & V091_Q(targetCat) & ", " & targetItemID & ", " & V091_SqlNum(qty) & ", " & V091_Q(unitName) & ", " & V091_SqlCurrency(unitRateAUD) & ", " & V091_SqlNum(adjustmentFactor) & ", " & V091_SqlCurrency(amount) & ", " & V091_SqlNum(cleanV) & ", " & V091_SqlNum(lsaV) & ", " & V091_SqlNum(hazV) & ", " & V091_SqlNum(mixedV) & ", " & V091_Q(sourceNote) & ", False, Now());", dbFailOnError
End Sub
' ============================================================
' APPLY GENERATED LINES TO EXISTING JOB LINES
' ============================================================
Public Sub V091_ApplyGeneratedToJobLines(ByVal jobID As String)
On Error GoTo ErrHandler
Dim rs As DAO.Recordset
Dim cat As String, itemID As Long, qty As Double, cost As Double, desiredRateAUD As Double, baseForV062 As Double
If DCount("*", "tblV091GeneratedLines", "JobID=" & V091_Q(jobID)) = 0 Then V091_GenerateGenericEstimate jobID
V091_BackupJobLines jobID
V091_EnsureJobHasAllActiveLibraryLines jobID
CurrentDb.Execute "UPDATE tblJobLines SET IncludeItem=False, Quantity=0, V091GeneratedQuantity=Null, V091GeneratedCostAUD=Null, V091IsGenerated=False WHERE JobID=" & V091_Q(jobID) & ";", dbFailOnError
Set rs = CurrentDb.OpenRecordset("SELECT TargetCategoryName, TargetItemID, Sum(Nz(QuantityPhysical,0)) AS TotalQty, Sum(Nz(GeneratedCostAUD,0)) AS TotalCostAUD FROM tblV091GeneratedLines WHERE JobID=" & V091_Q(jobID) & " AND TargetCategoryName Is Not Null GROUP BY TargetCategoryName, TargetItemID;", dbOpenSnapshot)
Do While Not rs.EOF
cat = Nz(rs!TargetCategoryName, "")
itemID = CLng(Nz(rs!TargetItemID, 0))
qty = CDbl(Nz(rs!TotalQty, 0))
cost = CDbl(Nz(rs!TotalCostAUD, 0))
If cat <> "" And itemID > 0 And cost <> 0 Then
If qty <= 0 Then qty = cost
desiredRateAUD = cost / qty
baseForV062 = desiredRateAUD / V091_V062Multiplier()
CurrentDb.Execute "UPDATE tblJobLines SET IncludeItem=True, Quantity=" & V091_SqlNum(qty) & _
", V091GeneratedQuantity=" & V091_SqlNum(qty) & _
", V091GeneratedCostAUD=" & V091_SqlCurrency(cost) & _
", V091IsGenerated=True, V091LastGeneratedAt=Now(), " & _
"V091OriginalBaseRateUSD2009=IIf(V091OriginalBaseRateUSD2009 Is Null, BaseUnitRateUSD2009, V091OriginalBaseRateUSD2009), " & _
"BaseUnitRateUSD2009=" & V091_SqlCurrency(baseForV062) & _
" WHERE JobID=" & V091_Q(jobID) & " AND CategoryName=" & V091_Q(cat) & " AND ItemID=" & itemID & ";", dbFailOnError
End If
rs.MoveNext
Loop
rs.Close
CurrentDb.Execute "UPDATE tblV091GeneratedLines SET AppliedToJobLine=True WHERE JobID=" & V091_Q(jobID) & ";", dbFailOnError
Exit Sub
ErrHandler:
On Error Resume Next
If Not rs Is Nothing Then rs.Close
Err.Raise Err.Number, , "V091_ApplyGeneratedToJobLines failed: " & Err.Description
End Sub
Private Sub V091_BackupJobLines(ByVal jobID As String)
CurrentDb.Execute "INSERT INTO tblV091JobLineBackup (BackupAt, JobID, JobLineID, CategoryName, ItemID, IncludeItem, Quantity, BaseUnitRateUSD2009) " & _
"SELECT Now(), JobID, JobLineID, CategoryName, ItemID, IncludeItem, Quantity, BaseUnitRateUSD2009 FROM tblJobLines WHERE JobID=" & V091_Q(jobID) & ";", dbFailOnError
End Sub
Private Sub V091_EnsureJobHasAllActiveLibraryLines(ByVal jobID As String)
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT LibraryID, CategoryName, ItemID, WBSCode, WBSSubCode, ItemName, UnitName, BaseUnitRateUSD2009 FROM tblCostLibrary WHERE IsActive=True ORDER BY CategoryName, ItemID;", dbOpenSnapshot)
Do While Not rs.EOF
If DCount("*", "tblJobLines", "JobID=" & V091_Q(jobID) & " AND CategoryName=" & V091_Q(Nz(rs!CategoryName, "")) & " AND ItemID=" & CLng(Nz(rs!itemID, 0))) = 0 Then
CurrentDb.Execute "INSERT INTO tblJobLines (JobID, LibraryID, CategoryName, ItemID, IncludeItem, WBSCode, WBSSubCode, ItemName, Quantity, UnitName, BaseUnitRateUSD2009) VALUES (" & _
V091_Q(jobID) & ", " & CLng(Nz(rs!LibraryID, 0)) & ", " & V091_Q(Nz(rs!CategoryName, "")) & ", " & CLng(Nz(rs!itemID, 0)) & ", False, " & V091_Q(Nz(rs!wbsCode, "")) & ", " & V091_Q(Nz(rs!WBSSubCode, "")) & ", " & V091_Q(Nz(rs!itemName, "")) & ", 0, " & V091_Q(Nz(rs!UnitName, "")) & ", " & V091_SqlCurrency(CDbl(Nz(rs!BaseUnitRateUSD2009, 0))) & ");", dbFailOnError
End If
rs.MoveNext
Loop
rs.Close
End Sub
' ============================================================
' UI FUNCTIONS
' ============================================================
Public Function V091_UI_NewJob() As Boolean
On Error GoTo ErrHandler
Dim jobID As String, jobName As String, preparedBy As String
jobID = Trim(InputBox("Enter Job ID / Building ID.", "New v0.9.1 Estimate", ""))
If jobID = "" Then Exit Function
jobName = Trim(InputBox("Enter job/building name.", "New v0.9.1 Estimate", ""))
If jobName = "" Then jobName = "Building " & jobID
preparedBy = Trim(InputBox("Prepared by:", "New v0.9.1 Estimate", ""))
If DCount("*", "tblJobs", "JobID=" & V091_Q(jobID)) = 0 Then
V062_CreateNewJobFromLibrary jobID, jobName, preparedBy
End If
V091_CreateInputIfMissing jobID
Forms("frmV091GenericEstimate").Requery
V091_MoveFormToJob "frmV091GenericEstimate", jobID
V091_UI_NewJob = True
Exit Function
ErrHandler:
MsgBox "Could not create v0.9.1 job:" & vbCrLf & Err.Description, vbExclamation, "v0.9.1 New Job Error"
V091_UI_NewJob = False
End Function
Public Function V091_UI_LoadSelectedJob() As Boolean
On Error GoTo ErrHandler
Dim jobID As String
jobID = Nz(Forms("frmV091GenericEstimate").Controls("cboJobSelector").value, "")
If jobID = "" Then Exit Function
V091_CreateInputIfMissing jobID
Forms("frmV091GenericEstimate").Requery
V091_MoveFormToJob "frmV091GenericEstimate", jobID
V091_UI_Refresh
V091_UI_LoadSelectedJob = True
Exit Function
ErrHandler:
MsgBox "Could not load job:" & vbCrLf & Err.Description, vbExclamation, "v0.9.1 Load Error"
V091_UI_LoadSelectedJob = False
End Function
Public Function V091_UI_Generate() As Boolean
On Error GoTo ErrHandler
Dim jobID As String
jobID = V091_CurrentFormJobID()
If jobID = "" Then Exit Function
If Forms("frmV091GenericEstimate").Dirty Then Forms("frmV091GenericEstimate").Dirty = False
V091_GenerateGenericEstimate jobID
V091_UI_Refresh
MsgBox "v0.9.1 generic estimate generated for " & jobID & ".", vbInformation, "Generated"
V091_UI_Generate = True
Exit Function
ErrHandler:
MsgBox "Could not generate v0.9.1 estimate:" & vbCrLf & Err.Description, vbExclamation, "v0.9.1 Generate Error"
V091_UI_Generate = False
End Function
Public Function V091_UI_Apply() As Boolean
On Error GoTo ErrHandler
Dim jobID As String
jobID = V091_CurrentFormJobID()
If jobID = "" Then Exit Function
If MsgBox("Apply v0.9.1 generated lines to the detailed job line copy for " & jobID & "?" & vbCrLf & vbCrLf & _
"This changes only tblJobLines for this job. tblCostLibrary is not overwritten.", vbQuestion + vbYesNo, "Apply v0.9.1") <> vbYes Then Exit Function
V091_ApplyGeneratedToJobLines jobID
V091_UI_Refresh
MsgBox "v0.9.1 generated scope applied to detailed job lines.", vbInformation, "Applied"
V091_UI_Apply = True
Exit Function
ErrHandler:
MsgBox "Could not apply v0.9.1 estimate:" & vbCrLf & Err.Description, vbExclamation, "v0.9.1 Apply Error"
V091_UI_Apply = False
End Function
Public Function V091_UI_OpenFineTune() As Boolean
On Error GoTo ErrHandler
Dim jobID As String
jobID = V091_CurrentFormJobID()