Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .forceignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Exclude the manifest from source tracking operations
package.xml

# LWC/Aura config files (none in this project, kept for convention)
**/jsconfig.json
**/.eslintrc.json
76 changes: 76 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Validate metadata

on:
push:
branches: [main]
pull_request:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check XML well-formedness
run: |
python3 - <<'EOF'
import glob, sys, xml.dom.minidom
failures = []
files = glob.glob('force-app/**/*.xml', recursive=True) + glob.glob('manifest/*.xml')
for path in sorted(files):
try:
xml.dom.minidom.parse(path)
print(f'OK {path}')
except Exception as exc:
failures.append((path, exc))
print(f'FAIL {path}: {exc}')
sys.exit(1 if failures else 0)
EOF

- uses: actions/setup-node@v4
with:
node-version: 22

- name: Install Salesforce CLI
run: npm install --global @salesforce/cli

- name: Convert source (validates SFDX project structure)
run: sf project convert source --output-dir mdapi-out

- name: Verify manifest members exist in source
run: |
python3 - <<'EOF'
import sys, xml.etree.ElementTree as ET
from pathlib import Path

NS = {'m': 'http://soap.sforce.com/2006/04/metadata'}
SUFFIX = {
'ReportType': ('force-app/main/default/reportTypes', '.reportType-meta.xml'),
'Report': ('force-app/main/default/reports', '.report-meta.xml'),
'Dashboard': ('force-app/main/default/dashboards', '.dashboard-meta.xml'),
}
FOLDER_SUFFIX = {
'Report': '.reportFolder-meta.xml',
'Dashboard': '.dashboardFolder-meta.xml',
}

tree = ET.parse('manifest/package.xml')
missing = []
for t in tree.getroot().findall('m:types', NS):
name = t.find('m:name', NS).text
if name not in SUFFIX:
continue
base, suffix = SUFFIX[name]
for member in t.findall('m:members', NS):
value = member.text
candidates = [Path(base) / (value + suffix)]
if '/' not in value and name in FOLDER_SUFFIX:
candidates.append(Path(base) / (value + FOLDER_SUFFIX[name]))
if not any(c.is_file() for c in candidates):
missing.append(f'{name}: {value}')
if missing:
print('Manifest members with no matching source file:')
print('\n'.join(missing))
sys.exit(1)
print('All manifest members have matching source files.')
EOF
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.sf/
.sfdx/
.localdevserver/
deploy-options.json
node_modules/
.DS_Store
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 nzc-customreports contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,86 @@
# nzc-customreports
This package will provide custom reports types to build salesforce lightning reports for Net Zero Cloud

A free, unmanaged, open-source package of basic **Lightning Report Types, Reports, and Dashboards** for **Salesforce Net Zero Cloud** (core Lightning analytics only — no CRM Analytics / Einstein components). Metadata API version **60.0**.

## What's included

| Component | API Name | Details |
|---|---|---|
| Custom Report Type | `Stationary_Assets_with_without_Carbon_Footprints` | `StnryAsstEnvrSrc` with/without `StnryAsstCrbnFprint` (outer join) |
| Custom Report Type | `Footprints_with_Energy_Uses` | `StnryAsstCrbnFprint` with `StnryAsstFossilFuelEnrgyUse` (inner join) |
| Custom Report Type | `Accounts_with_or_without_Supplier_Metrics` | `Account` with/without `SpplrMtrc` (outer join) |
| Custom Report Type | `Standalone_Carbon_Footprints` | Single object: `StnryAsstCrbnFprint` |
| Custom Report Type | `Standalone_Vehicle_Assets` | Single object: `VehicleAsset` |
| Report | `NZC_Reports/Stationary_Assets_Missing_Footprints` | Summary report grouped by asset type (record type), filtered to rows where the related Carbon Footprint Id is blank |
| Report | `NZC_Reports/Fuel_Consumption_by_Fuel_Type` | Summary report grouped by fuel type, with summed fuel consumption |
| Report | `NZC_Reports/Suppliers_Missing_Metrics` | Accounts with no Supplier Metric record, grouped by industry |
| Report | `NZC_Reports/Emissions_by_Reporting_Year` | Footprints grouped by reporting year with summed Scope 1 / Scope 2 / total emissions |
| Report | `NZC_Reports/Fleet_by_Fuel_Type` | Vehicle assets grouped by fuel type |
| Dashboard | `NZC_Dashboards/NZC_Emissions_Coverage` | Bar charts for asset/supplier data gaps, column chart of emissions by year, donut of fleet by fuel type |

All report type layouts are curated to the fields sustainability teams use most (asset name, reporting year, stage, Scope 1 / Scope 2 emissions (tCO2e), fuel type, record type), and every curated column is flagged `checkedByDefault` so it is pre-selected when users build a new report.

> **Note:** The first report type's label is "Stationary Assets with/without Carbon Footprints" (rather than "…with or without…") to stay within the 50-character report type label limit.

## Project structure

```
manifest/package.xml
force-app/main/default/
├── reportTypes/
│ ├── Stationary_Assets_with_without_Carbon_Footprints.reportType-meta.xml
│ ├── Footprints_with_Energy_Uses.reportType-meta.xml
│ ├── Accounts_with_or_without_Supplier_Metrics.reportType-meta.xml
│ ├── Standalone_Carbon_Footprints.reportType-meta.xml
│ └── Standalone_Vehicle_Assets.reportType-meta.xml
├── reports/
│ ├── NZC_Reports.reportFolder-meta.xml
│ └── NZC_Reports/
│ ├── Stationary_Assets_Missing_Footprints.report-meta.xml
│ ├── Fuel_Consumption_by_Fuel_Type.report-meta.xml
│ ├── Suppliers_Missing_Metrics.report-meta.xml
│ ├── Emissions_by_Reporting_Year.report-meta.xml
│ └── Fleet_by_Fuel_Type.report-meta.xml
└── dashboards/
├── NZC_Dashboards.dashboardFolder-meta.xml
└── NZC_Dashboards/NZC_Emissions_Coverage.dashboard-meta.xml
```

## Continuous integration

Every push and pull request runs `.github/workflows/validate.yml`, which:

1. Checks that every XML file is well-formed.
2. Runs `sf project convert source` to validate the SFDX project structure and metadata file suffixes (no org required).
3. Cross-checks that every member listed in `manifest/package.xml` has a matching source file.

Deploy-time validation against a real Net Zero Cloud org (field and relationship API names) still needs to be done with `sf project deploy start --dry-run` against your own org, since those objects require the NZC license.

## Prerequisites

- A Salesforce org with the **Net Zero Cloud** license and managed objects enabled (`StnryAsstEnvrSrc`, `StnryAsstCrbnFprint`, `StnryAsstFossilFuelEnrgyUse`, `SpplrMtrc`, `VehicleAsset`).
- Salesforce CLI (`sf`).

## Deploy

```bash
sf project deploy start --manifest manifest/package.xml --target-org <your-org-alias>
```

## Validating API names against your org

Net Zero Cloud field and child-relationship API names can vary slightly by release and license edition. If a deploy fails on a field or relationship name, verify the exact names in your org and adjust the XML:

```bash
sf sobject describe --sobject StnryAsstCrbnFprint --target-org <alias> | jq '.fields[].name'
sf sobject describe --sobject StnryAsstEnvrSrc --target-org <alias> | jq '.childRelationships[] | {relationshipName, childSObject}'
```

The most likely candidates to need adjustment:

- Child relationship names used in the report type joins: `StnryAsstCrbnFprints`, `StnryAsstFossilFuelEnrgyUses`, `SpplrMtrcs`.
- Emissions field spellings on `StnryAsstCrbnFprint` (e.g. `TotalScope2LocationBasedEmissions` / `TotalScope2MarketBasedEmissions`) and metric fields on `SpplrMtrc`.

## License

Free to use, modify, and redistribute.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<DashboardFolder xmlns="http://soap.sforce.com/2006/04/metadata">
<name>NZC Dashboards</name>
</DashboardFolder>
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Dashboard xmlns="http://soap.sforce.com/2006/04/metadata">
<backgroundEndColor>#FFFFFF</backgroundEndColor>
<backgroundFadeDirection>Diagonal</backgroundFadeDirection>
<backgroundStartColor>#FFFFFF</backgroundStartColor>
<dashboardType>LoggedInUser</dashboardType>
<description>Net Zero Cloud emissions overview: data coverage gaps for stationary assets and suppliers, emissions trend by reporting year, and fleet composition.</description>
<isGridLayout>false</isGridLayout>
<leftSection>
<columnSize>Medium</columnSize>
<components>
<autoselectColumnsFromReport>true</autoselectColumnsFromReport>
<chartAxisRange>Auto</chartAxisRange>
<componentType>Bar</componentType>
<displayUnits>Auto</displayUnits>
<drillEnabled>false</drillEnabled>
<drillToDetailEnabled>false</drillToDetailEnabled>
<enableHover>true</enableHover>
<expandOthers>true</expandOthers>
<header>Data Coverage</header>
<legendPosition>Bottom</legendPosition>
<report>NZC_Reports/Stationary_Assets_Missing_Footprints</report>
<showPercentage>false</showPercentage>
<showTotal>false</showTotal>
<showValues>true</showValues>
<sortBy>RowValueDescending</sortBy>
<title>Assets Missing Carbon Footprints by Asset Type</title>
<useReportChart>false</useReportChart>
</components>
<components>
<autoselectColumnsFromReport>true</autoselectColumnsFromReport>
<chartAxisRange>Auto</chartAxisRange>
<componentType>Bar</componentType>
<displayUnits>Auto</displayUnits>
<drillEnabled>false</drillEnabled>
<drillToDetailEnabled>false</drillToDetailEnabled>
<enableHover>true</enableHover>
<expandOthers>true</expandOthers>
<header>Data Coverage</header>
<legendPosition>Bottom</legendPosition>
<report>NZC_Reports/Suppliers_Missing_Metrics</report>
<showPercentage>false</showPercentage>
<showTotal>false</showTotal>
<showValues>true</showValues>
<sortBy>RowValueDescending</sortBy>
<title>Suppliers Missing Metrics by Industry</title>
<useReportChart>false</useReportChart>
</components>
</leftSection>
<middleSection>
<columnSize>Medium</columnSize>
<components>
<autoselectColumnsFromReport>true</autoselectColumnsFromReport>
<chartAxisRange>Auto</chartAxisRange>
<componentType>Column</componentType>
<displayUnits>Auto</displayUnits>
<drillEnabled>false</drillEnabled>
<drillToDetailEnabled>false</drillToDetailEnabled>
<enableHover>true</enableHover>
<expandOthers>true</expandOthers>
<header>Emissions Trend</header>
<legendPosition>Bottom</legendPosition>
<report>NZC_Reports/Emissions_by_Reporting_Year</report>
<showPercentage>false</showPercentage>
<showTotal>false</showTotal>
<showValues>true</showValues>
<sortBy>RowLabelAscending</sortBy>
<title>Total Emissions by Reporting Year (tCO2e)</title>
<useReportChart>false</useReportChart>
</components>
</middleSection>
<rightSection>
<columnSize>Medium</columnSize>
<components>
<autoselectColumnsFromReport>true</autoselectColumnsFromReport>
<chartAxisRange>Auto</chartAxisRange>
<componentType>Donut</componentType>
<displayUnits>Auto</displayUnits>
<drillEnabled>false</drillEnabled>
<drillToDetailEnabled>false</drillToDetailEnabled>
<enableHover>true</enableHover>
<expandOthers>true</expandOthers>
<header>Fleet</header>
<legendPosition>Bottom</legendPosition>
<report>NZC_Reports/Fleet_by_Fuel_Type</report>
<showPercentage>true</showPercentage>
<showTotal>true</showTotal>
<showValues>true</showValues>
<sortBy>RowValueDescending</sortBy>
<title>Vehicle Assets by Fuel Type</title>
<useReportChart>false</useReportChart>
</components>
</rightSection>
<textColor>#000000</textColor>
<title>NZC Emissions Coverage</title>
</Dashboard>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<ReportType xmlns="http://soap.sforce.com/2006/04/metadata">
<baseObject>Account</baseObject>
<category>accounts</category>
<deployed>true</deployed>
<description>Accounts with or without related Supplier Metric records (outer join). Use this report type to find suppliers that have not yet disclosed emissions data.</description>
<join>
<outerJoin>true</outerJoin>
<relationship>SpplrMtrcs</relationship>
</join>
<label>Accounts with or without Supplier Metrics</label>
<sections>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Name</field>
<table>Account</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Industry</field>
<table>Account</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>BillingCountry</field>
<table>Account</table>
</columns>
<masterLabel>Accounts</masterLabel>
</sections>
<sections>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Name</field>
<table>Account.SpplrMtrcs</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>ReportingYear</field>
<table>Account.SpplrMtrcs</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Scope1Emissions</field>
<table>Account.SpplrMtrcs</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Scope2Emissions</field>
<table>Account.SpplrMtrcs</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Scope3Emissions</field>
<table>Account.SpplrMtrcs</table>
</columns>
<masterLabel>Supplier Metrics</masterLabel>
</sections>
</ReportType>
Loading
Loading