Skip to main content
Technical ManualLooking for step-by-step instructions? In the User Manual: Estimating ReportsYour Own Reports

Estimator Reports Module

The CrestApps.OrchardCore.Estimator.Reports module adds the estimating reports to the CrestApps Reports area, and the Estimator data source to the Report Builder and to Orchard Core data pipelines.

Feature IDNameCategoryDependencies
CrestApps.OrchardCore.Estimator.ReportsEstimator ReportsEstimatorCrestApps.OrchardCore.Estimator.Estimates, CrestApps.OrchardCore.Reports

The Estimator setup recipe enables the feature with CrestApps.OrchardCore.Reports.OpenXml (Excel export) and CrestApps.OrchardCore.Reports.Builder. The design is recorded in docs/engineering/adr/0014-estimating-reports.md in the repository.

Permission​

EstimatorReportPermissions.ViewReports (ViewEstimatorReports, View the estimating reports) lets a user run and export the reports. The Administrator role gets it. A report reads only the projects the user may see (EstimatorAccess.CanViewAsync), so an estimator who manages only their own projects sees only those.

Coded reports​

Each report is an IReport in the Estimating category, registered with AddReport<TReport>():

NameClassFilterContent
estimator-bid-recapBidRecapReportEstimate, revisionSummary metrics, the recap rows (prime cost as a subtotal, the selling price as the grand total), the cost by type, the alternates, and the material and labor of each breakdown node
estimator-laborLaborReportEstimate, revisionHours and cost by labor category (from the components of each line), a horizontal bar chart, and hours and cost by breakdown node
estimator-material-listMaterialListReportEstimate, revisionPurchase quantities and material by item code, with the items of assemblies; quoted items apart; manual material lines; items not in the catalog
estimator-quote-summaryQuoteSummaryReportEstimate, revisionEach package with its quote count, the leveled low quote, what is carried and from where, and the scope gaps without a plug
estimator-bid-resultsBidResultsReportThe date rangeThe bids due in the period: counts by status, hit rate, value won, a doughnut chart and each bid with its due time in the site's time zone

The four estimate reports read the base bid: the lines with no AlternateId. A breakdown node includes the nodes under it (EstimateReportWriter.RollUp).

ServiceLifetimeWhat it does
EstimateReportDataLoaderScopedLoadAsync(user, estimateId, revisionNumber): the project, estimate, revision, lines, recap (EstimateRecap.Calculate), quote packages and quotes, or null when the user may not see the project; ListEstimatesAsync(user) for the filter; ListBidsAsync(user, fromUtc, toUtc) through IBidStore.ListDueBetweenAsync
EstimateReportWriterScopedWrites each ReportDocument. Amounts are texts in the current culture; totals are Subtotal and GrandTotal rows; warnings use the color #B45309 so they reach the Excel export

The labor report and the material list expand every line with EstimateLineCalculator.ExplainAllAsync, which loads the catalog once for all lines. Each EstimateLineComponent has its LaborCategory (of its item, of the assembly for the labor of an assembly, or of a manual line) and IsAssemblyLabor for the labor unit of an assembly, which the material list leaves out.

The filter​

EstimateReportFilterDisplayDriver adds Estimate and Revision to the filter of the four estimate reports (EstimateReportFilters.EstimateReports). It writes the EstimateId and RevisionNumber keys of the ReportFilter. The Reports page binds the filter on every request under the ReportFilter prefix, so a link can choose the estimate:

/Admin/Reports/view/estimator-bid-recap?ReportFilter.EstimateId={estimateId}&ReportFilter.RevisionNumber=2

The Reports button of the workspace (ReportsEstimateDisplayDriver, EstimateDisplayTypes.WorkspaceActions) opens the bid recap this way, for users with the permission.

The Report Builder data source​

With OrchardCore.DataPipelines.Core enabled, DataSourcesStartup registers EstimatorDataSource (Estimator), a RecordDataSource with four data sets:

Data setRecordMain dateKey fieldsReferences
ProjectsProjectNoneItemIdOwnerUserId to Users.Users.UserId
BidsBidDueUtcItemId, ProjectIdProjectId to Estimator.Projects.ItemId
EstimatesEstimateReportRow (an estimate and its current revision)NoneItemId, ProjectIdProjectId, BidId to Estimator.Bids.ItemId
EstimateLinesEstimateLineReportRow (a line of the current revision, with its breakdown path and alternate code)NoneEstimateId, ProjectIdEstimateId to Estimator.Estimates.ItemId, ProjectId
  • Every data set is read only by a principal for which EstimatorAccess.CanViewAllAsync is true. The data sets cannot filter rows per project, so users with only Manage own projects and bids get none.
  • Projects and bids are read with ReportIndexPages (newest first, keyset paging, detached from the session). Bids apply the date range on DueUtc and the join keys in the database.
  • Estimates and lines are read by EstimateReportPages, 100 estimates at a time by index row, detached from the session. Lines load per revision through IEstimateLineStore, so the Report Builder row limit (MaxRowsPerDataSet, 50,000 by default) bounds a read.

Tests​

tests/CrestApps.OrchardCore.Estimator.Tests/Reports holds the tests of the writer (every report document), and on real SQLite the loader with project access, the reports with their filters, and the four data sets with keys, dates and access.