From 0872e8d3bf7fca87f24786ee10f8c41ed5284328 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 03:29:21 +0000 Subject: [PATCH 1/2] Close the fleet gap on the Data Quality dashboard, and record why the reports are unbounded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Orphans column watched the stationary side only: Orphaned Carbon Footprints and Orphaned Stationary Energy Uses were on the dashboard, while Orphaned Fleet Footprints and Orphaned Vehicle Energy Uses existed as reports and appeared nowhere. Both are now components, so the column covers what the left and middle columns already do for gaps. Fleet data quality was going unwatched. The rest of this is a decision written down rather than a change. Fifteen of the 21 reports have no filter and all 21 leave the standard date filter unbounded, so each reads its whole object on every run. That looks like an oversight and is not one. The only date column these objects offer is CreatedDate, and the business axis is ReportingYear: a footprint *for* 2024 is routinely entered in 2026 during the disclosure cycle. Bounding on CreatedDate would drop records that belong in the answer — faster and wrong, which is the worse failure for anything feeding a disclosure. Bounding on ReportingYear is the right fix and the choice of years is a disclosure question, so the README now says so and leaves it to whoever deploys this. Two related shapes are deliberate for the same reason, and are documented rather than "corrected": the six tabular reports have no row limit because they are worklists meant to be worked through, and the summary reports keep details on because the same report is read as a dashboard chart and as the list of assets to go and fix. Considered and rejected: three report types are referenced by no report (Vehicle_Assets_with_Carbon_Footprints, Footprints_with_Energy_Uses, Vehicle_Footprints_with_Energy_Uses). In a repository people fork to build their own reports, a report type with no report is a starting point, not an orphan. XML well-formedness (53 files) and `sf project convert source` both pass. --- README.md | 32 ++++++++++++++++ .../NZC_Data_Quality.dashboard-meta.xml | 38 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/README.md b/README.md index 55d10e6..b8524c8 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,38 @@ produces a report that runs cleanly and reports the wrong number. **Fleet fuel comes from the energy use record.** `VehicleAssetEmssnSrc` has no fuel field — fuel, consumption and distance all live on `VehicleAssetEnrgyUse`. +## Scope: the reports read the whole object, deliberately + +Fifteen of the 21 reports carry no filter, and all 21 leave the standard date filter +unbounded. That is a decision, not an oversight, and it is worth understanding before you +deploy into an org with years of history. + +**Why unbounded.** The date column available on these objects is `CreatedDate` — when +somebody typed the record in. The business axis is `ReportingYear`, and the two do not +agree: a footprint *for* 2024 is routinely entered in 2026, during the disclosure cycle. +Bounding on `CreatedDate` would therefore drop records that belong in the answer. It would +make the reports faster and wrong, which is the worse of the two failures for anything +feeding a disclosure. + +**What it costs.** Each of these reports reads every record in scope on every run. On a +dev org or a first year of data that is invisible. On an org with several years of +footprint and energy-use records it is the first thing that will get slow, and the +dashboard-fed reports pay it on every refresh. + +**What to do about it, when you get there.** Add a `ReportingYear` filter — it is on every +report type here, and it is the axis the reports already group by. Which years to keep is +a question about your disclosure obligations rather than about Salesforce, which is why +this repository does not choose for you. + +Two related shapes, also deliberate: + +- **The six tabular reports have no row limit.** They are worklists and lookup lists — + *Footprint Data Entry*, *Asset Names and Locations* — meant to be worked all the way + through. A row limit on a worklist hides the rows somebody still has to fill in. +- **The summary reports show details.** The same reports are read two ways: as a chart on a + dashboard, and as a list of the specific assets to go and fix. Turning details off would + make the chart marginally cheaper and the worklist useless. + ## Also in this repo - [`sample-data/`](sample-data/) — six UTF-8 CSVs with deliberate gaps and orphans, so the diff --git a/force-app/main/default/dashboards/NZC_Dashboards/NZC_Data_Quality.dashboard-meta.xml b/force-app/main/default/dashboards/NZC_Dashboards/NZC_Data_Quality.dashboard-meta.xml index 0e7c202..11812ac 100644 --- a/force-app/main/default/dashboards/NZC_Dashboards/NZC_Data_Quality.dashboard-meta.xml +++ b/force-app/main/default/dashboards/NZC_Dashboards/NZC_Data_Quality.dashboard-meta.xml @@ -128,6 +128,44 @@ Orphaned Energy Uses by Fuel false + + true + Auto + Bar + Auto + false + false + true + true +
Orphans
+ Bottom + NZC_Reports/Orphaned_Fleet_Footprints + false + false + true + RowValueDescending + Orphaned Fleet Footprints + false +
+ + true + Auto + Bar + Auto + false + false + true + true +
Orphans
+ Bottom + NZC_Reports/Orphaned_Vehicle_Energy_Uses + false + false + true + RowValueDescending + Orphaned Vehicle Energy Uses + false +
#000000 NZC Data Quality From 728e21f9c3cc464b784244b45ce67e3be7c5142b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 03:44:13 +0000 Subject: [PATCH 2/2] Docs: retrieving overwrites the omissions that make the dashboards portable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by doing it. A retrieve stamps every dashboard with and carrying the retrieving user's address, whatever the source said, and even for a LoggedInUser dashboard where the running user decides nothing. Committing that breaks the package for everyone else — the username does not exist in their org, so the deploy fails on a user it cannot resolve — and puts a personal email address into a public repository, twice per dashboard. The committed dashboards carry dashboardType and neither of those elements; that omission is the portable form and it is load-bearing. The note gives the grep to run before committing any dashboard retrieve, and says plainly that throwing the retrieve away is usually the right move. Also recorded, since the same round trip produced them: the reports come back with co=1, and added, which change nothing; and the dashboards come back with flipped to false and dropped. Those two do nothing on a Bar component, which is worth knowing when writing a new one — but not worth a two-hundred-line reformat to discover. --- docs/building-your-own.md | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/building-your-own.md b/docs/building-your-own.md index 5fba29f..1cbc449 100644 --- a/docs/building-your-own.md +++ b/docs/building-your-own.md @@ -174,3 +174,55 @@ that pattern. The lesson is the same one as the report type joins: the platform' objects use plain API names in places where its older objects use legacy keys, and only a deploy tells you which you've got. + +## Retrieving overwrites what the source deliberately leaves out + +`sf project retrieve start` does not give you back what you deployed. It gives you the +org's rendering of it, and for dashboards that includes two elements this package +deliberately does not carry: + +```xml +someone@example.com +someone@example.com +``` + +Salesforce stamps both on every dashboard, whatever the source said, and it does so even +for a `LoggedInUser` dashboard where the running user is not used to decide what anyone +sees. Commit them and you have broken the package for everybody else: that username does +not exist in their org, so the deploy fails on a user it cannot resolve. You have also put +your own email address into a public repository, twice per dashboard. + +The committed dashboards here carry `LoggedInUser` and no +owner or running user. That omission is the portable form, and it is load-bearing. + +**So: never commit a dashboard retrieve without checking.** + +```bash +sf project retrieve start --manifest manifest/package.xml --target-org +grep -rn "owner\|runningUser" force-app/main/default/dashboards/ +``` + +Anything printed has to come out before you commit. If the rest of the retrieve is +cosmetic — and it usually is — the honest move is to throw the whole thing away and keep +the hand-authored source: + +```bash +git checkout -- force-app +``` + +### What else comes back, and why none of it is worth keeping + +The same round trip adds a few dozen lines of defaults across the reports: +`co1`, ``, ``. +They change nothing — a deploy from the source without them succeeds — and they make a +report definition longer without making its intent clearer. + +The dashboards also come back with two properties *rewritten* rather than added: +`` flips to `false` and `` is dropped altogether. Neither applies +to a Bar component; Salesforce is normalising settings that were never doing anything. +That is worth knowing when you write a new component — those two do nothing on a bar chart +— but it is not worth taking a two-hundred-line reformat to find out. + +The general rule, and it is the same one as everywhere else in this document: a deploy +tells you whether the source is *sufficient*. A retrieve tells you what the org chose to +store, which is a different question, and a noisier answer.