nonprofit-data-architecture

Use this skill when designing or querying the NPSP data model — constituent 360, household accounts, giving history rollups, and program participation. Trigger keywords: NPSP data model, household account, constituent record, giving rollups, CRLP, program engagement, ServiceDelivery, npo02__ fields. NOT for standard data model design, Nonprofit Cloud (NPC) data model, FSC household groups, or platform data modeling outside the NPSP context.

Best use case

nonprofit-data-architecture is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use this skill when designing or querying the NPSP data model — constituent 360, household accounts, giving history rollups, and program participation. Trigger keywords: NPSP data model, household account, constituent record, giving rollups, CRLP, program engagement, ServiceDelivery, npo02__ fields. NOT for standard data model design, Nonprofit Cloud (NPC) data model, FSC household groups, or platform data modeling outside the NPSP context.

Teams using nonprofit-data-architecture should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/nonprofit-data-architecture/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/data/nonprofit-data-architecture/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nonprofit-data-architecture/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nonprofit-data-architecture Compares

Feature / Agentnonprofit-data-architectureStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use this skill when designing or querying the NPSP data model — constituent 360, household accounts, giving history rollups, and program participation. Trigger keywords: NPSP data model, household account, constituent record, giving rollups, CRLP, program engagement, ServiceDelivery, npo02__ fields. NOT for standard data model design, Nonprofit Cloud (NPC) data model, FSC household groups, or platform data modeling outside the NPSP context.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Nonprofit Data Architecture

Use this skill when working with the NPSP (Nonprofit Success Pack) data model, including constituent 360 design, household account configuration, giving history rollup fields, and program participation tracking via the Program Management Module (PMM). This skill does NOT cover the Nonprofit Cloud (NPC) data model, FSC household groups, or generic Salesforce data modeling patterns.

---

## Before Starting

Gather this context before working on anything in this domain:

- **Which constituent model is active?** NPSP supports three models: Household Account (recommended, most common), One-to-One, and Individual (deprecated bucket account). Household Account is almost always what you are working with in modern NPSP orgs.
- **Is PMM installed?** The Program Management Module is a separate managed package with its own `pmdm__` namespace. It is NOT part of NPSP core (namespaces `npo02__`, `npsp__`, `npe01__`, `npe03__`, `npe4__`, `npe5__`). Many orgs have NPSP but not PMM.
- **Is CRLP enabled?** Customizable Rollups (CRLP) replaced legacy rollup behavior. When CRLP is enabled, rollup fields are calculated by Rollup__mdt custom metadata records, not by the legacy batch job. Mixing CRLP and legacy rollups in the same org causes silent duplicate or zeroed rollup values.
- **Is this NPSP or Nonprofit Cloud?** NPSP and NPC are two entirely different products built on different foundational objects. Never apply NPSP patterns to an NPC org. Confirm via installed packages before proceeding.

---

## Core Concepts

### The Three-Layer Constituent 360 Model

NPSP models a constituent as three interconnected layers:

1. **HH_Account (Household Account)** — A standard `Account` record with the `HH_Account` record type (API name `HH_Account`). The household groups one or more related Contacts (family members, partners). The account is the financial unit: `Opportunity` and `npo02__OppPayment__c` records link to the Account, not to individual Contacts. Rollup summary fields on the Household Account aggregate giving totals across all members.

2. **Contact** — The individual constituent. NPSP adds the `npe01__Primary_Address_Type__c`, `npe01__Home_Address__c`, and related address management fields. A Contact's `AccountId` points to their Household Account. The `npe01__Primary_Contact__c` field on the Account identifies the primary contact for the household.

3. **npo02__ rollup fields on Contact and Account** — NPSP drives a constellation of rollup fields under the `npo02__` namespace. Key fields include:
   - `npo02__TotalOppAmount__c` — lifetime total giving
   - `npo02__LastOppAmount__c` — most recent gift amount
   - `npo02__LastCloseDate__c` — most recent gift close date
   - `npo02__OppAmountLastYear__c` — giving in the prior calendar year
   - `npo02__ConsecutiveYearsGiven__c` — streak counter
   
   These fields exist on both `Contact` and `Account`. The values are recalculated by either the CRLP engine or the legacy Household Rollup batch job, not by standard rollup summary fields.

### Customizable Rollups (CRLP) Engine

CRLP is the modern NPSP rollup calculation engine introduced in NPSP 3.x. It replaces the nightly `RLLP_OppRollup_BATCH` with a near-real-time engine driven by `Rollup__mdt` custom metadata type records. Each `Rollup__mdt` record defines:

- The source object (Opportunity or npe01__OppPayment__c)
- The filter group (which Opportunities count)
- The operation (SUM, COUNT, AVERAGE, FIRST, LAST, LARGEST, SMALLEST)
- The target object and field

Adding custom rollup fields requires creating new `Rollup__mdt` and `Filter_Group__mdt` records through the CRLP UI (Setup > Nonprofit Success Pack > Customizable Rollups). Direct API manipulation of these metadata records without understanding the required lookup relationships will break rollup calculations silently.

### Program Management Module (PMM) Object Model

PMM is a separate managed package (`pmdm__` namespace) that tracks program enrollment and service delivery. Its core objects are:

- **`pmdm__Program__c`** — The program itself (e.g., "Youth Tutoring"). Has `pmdm__Status__c`, `pmdm__StartDate__c`, `pmdm__EndDate__c`.
- **`pmdm__ProgramEngagement__c`** — The enrollment junction between a Contact and a Program. Has `pmdm__Contact__c`, `pmdm__Program__c`, `pmdm__Stage__c` (Active, Waitlisted, Completed, Withdrawn), and `pmdm__StartDate__c`.
- **`pmdm__ServiceDelivery__c`** — An individual unit of service delivered to a Contact within an Engagement. Has `pmdm__Contact__c`, `pmdm__ProgramEngagement__c`, `pmdm__DeliveryDate__c`, `pmdm__Quantity__c`, and `pmdm__Service__c`.

PMM objects are related to NPSP Contacts but PMM does not share namespace or package dependencies with NPSP core. An org can run PMM without NPSP, though this is rare.

---

## Common Patterns

### Constituent 360 Query: Full Giving and Program History for a Contact

**When to use:** Building a constituent summary view, reporting dashboard, or data migration validation that requires giving history, rollup totals, and program participation.

**How it works:**

```soql
-- Get constituent giving summary (rollup fields live on both Contact and HH Account)
SELECT
    Id,
    Name,
    AccountId,
    Account.Name,
    npo02__TotalOppAmount__c,
    npo02__LastOppAmount__c,
    npo02__LastCloseDate__c,
    npo02__ConsecutiveYearsGiven__c,
    npo02__OppAmountLastYear__c
FROM Contact
WHERE Id = :contactId

-- Get individual gift transactions for this constituent's household
SELECT Id, Name, Amount, CloseDate, StageName, npsp__Primary_Contact__c
FROM Opportunity
WHERE AccountId = :householdAccountId
  AND IsWon = true
ORDER BY CloseDate DESC

-- Get program participation
SELECT
    Id,
    pmdm__Program__r.Name,
    pmdm__Stage__c,
    pmdm__StartDate__c
FROM pmdm__ProgramEngagement__c
WHERE pmdm__Contact__c = :contactId

-- Get service deliveries
SELECT
    Id,
    pmdm__DeliveryDate__c,
    pmdm__Quantity__c,
    pmdm__Service__r.Name,
    pmdm__ProgramEngagement__r.pmdm__Program__r.Name
FROM pmdm__ServiceDelivery__c
WHERE pmdm__Contact__c = :contactId
ORDER BY pmdm__DeliveryDate__c DESC
```

**Why not the alternative:** Querying only the Contact record misses the gift transactions on the Household Account. Rollup fields may lag real-time transactions by the CRLP recalculation window; always query raw Opportunities for authoritative transaction data.

### Custom Rollup via CRLP Configuration

**When to use:** An org needs a rollup value not covered by NPSP's default rollup fields (e.g., total giving to a specific campaign, major gift count above a threshold).

**How it works:**

1. In Setup, navigate to Nonprofit Success Pack > Customizable Rollups.
2. Create a new `Filter_Group__mdt` record defining the criteria (e.g., `Type = 'Major Gift'`).
3. Create a new `Rollup__mdt` record pointing to the filter group, source field (`Amount`), operation (`SUM`), target object (`Contact`), and target field (a custom currency field you pre-create).
4. Run the Recalculate All Rollups batch job to populate historical data.
5. After enabling, CRLP will update the field in near-real-time on new and changed Opportunities.

**Why not the alternative:** Standard Salesforce rollup summary fields on Account do not understand the Contact-to-Household Account relationship for per-Contact aggregation. Formula fields cannot aggregate across multiple records. Custom Apex triggers on Opportunity are fragile and conflict with NPSP's own Opportunity trigger framework.

---

## Decision Guidance

| Situation | Recommended Approach | Reason |
|---|---|---|
| Need lifetime giving total for a Contact | Read `npo02__TotalOppAmount__c` on Contact | Maintained by CRLP; faster than aggregate SOQL for display purposes |
| Need authoritative real-time total | Aggregate SOQL on Opportunity WHERE AccountId = HH Account | Rollup fields may lag by CRLP recalculation window |
| Need program enrollment history | Query `pmdm__ProgramEngagement__c` WHERE pmdm__Contact__c | PMM junction object; requires pmdm__ package installed |
| Need to add a custom donation rollup | Create Rollup__mdt + Filter_Group__mdt via CRLP UI | Safest extension path; avoids conflicts with NPSP trigger framework |
| Org is on Nonprofit Cloud (NPC), not NPSP | Stop — use NPC data model skill instead | NPC and NPSP use incompatible foundational objects; NPSP patterns do not apply |
| Org has FSC Household Groups alongside NPSP | Confirm packages; treat as separate architectures | FSC uses AccountContactRelationship-based Household Group object, not HH_Account record type |

---

## Recommended Workflow

Step-by-step instructions for an AI agent or practitioner working on this task:

1. **Verify the installed package set.** Confirm `npo02__` namespace is queryable (NPSP core), whether `pmdm__` is installed (PMM), and whether `npsp__` settings records exist. Run `SELECT Id, Name FROM npo02__Household_Settings__c LIMIT 1` — if it returns data, NPSP is installed and active.
2. **Identify the constituent model.** Query `SELECT Id, Name FROM RecordType WHERE SObjectType = 'Account' AND Name = 'Household Account'` to confirm the HH_Account record type is present. If absent, the org may be on One-to-One or Individual model — do not assume HH_Account model.
3. **Check CRLP status.** Navigate to NPSP Settings > Rollup Settings to confirm CRLP is enabled or disabled. In CRLP-enabled orgs, do not modify `npo02__Settings__c.npo02__Rollup_N_Day_Value__c` or related legacy fields — they are ignored by the CRLP engine and changes create confusion.
4. **Design queries using the three-layer model.** For giving data, join Contact → Account (HH_Account) → Opportunity. For program data, join Contact → pmdm__ProgramEngagement__c → pmdm__Program__c and pmdm__ServiceDelivery__c. Never query Opportunity.ContactId for NPSP gift records — NPSP stores the primary contact via `npsp__Primary_Contact__c` on Opportunity, not via a standard contact role in all configurations.
5. **Validate rollup field values against raw transactions.** Before trusting any `npo02__` rollup field in a report or migration, verify it matches an aggregate SOQL query on the underlying Opportunities. Stale rollups from legacy batch mode or a failed CRLP job are a common source of data quality defects.
6. **Document namespace dependencies in any solution.** Any SOQL, Flow, or Apex that references `npo02__`, `npsp__`, `npe01__`, or `pmdm__` fields will fail in orgs without those packages. Add an org-detection guard at the start of any script that accesses these fields.
7. **Run the NPSP Health Check before any schema change.** Available under Nonprofit Success Pack > System Tools > NPSP Health Check. Identifies configuration problems that would cause your changes to behave unexpectedly.

---

## Review Checklist

Run through these before marking work in this area complete:

- [ ] Confirmed org is NPSP (not Nonprofit Cloud / NPC) before applying any guidance from this skill
- [ ] Verified constituent model is HH_Account and not One-to-One or Individual
- [ ] CRLP enabled/disabled status documented and respected in any rollup-related work
- [ ] PMM package presence confirmed before referencing any `pmdm__` objects or fields
- [ ] All SOQL queries use `AccountId` on Opportunity (pointing to HH Account) rather than ContactId for gift queries
- [ ] Rollup field values validated against aggregate SOQL on raw Opportunity records
- [ ] Namespace dependencies documented for any solution that will be deployed to other orgs

---

## Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

1. **Opportunities link to the Household Account, not to the Contact** — In the NPSP Household Account model, `Opportunity.AccountId` points to the HH_Account, not to the individual Contact. The relationship to the primary constituent is stored in `npsp__Primary_Contact__c` (a lookup to Contact on the Opportunity object). Code or reports that filter `Opportunity.ContactId IS NOT NULL` to find constituent gifts will return no results or incorrect results.

2. **CRLP and legacy rollup mode cannot safely coexist** — Orgs that partially migrate to CRLP without disabling the legacy `RLLP_OppRollup_BATCH` scheduled job will run both engines simultaneously. The result is unpredictable: rollup fields may be doubled, zeroed, or overwritten in alternating directions. Always confirm one engine is fully disabled before enabling the other.

3. **PMM is a separate managed package from NPSP core** — The `pmdm__` namespace is the Program Management Module package, installed separately from NPSP. It is common to encounter NPSP orgs where PMM is not installed. Any code, Flow, or SOQL referencing `pmdm__ServiceDelivery__c`, `pmdm__ProgramEngagement__c`, or `pmdm__Program__c` will throw errors in NPSP orgs without PMM. Always check installed packages before assuming PMM objects are available.

4. **HH Account record type name is configurable** — The default record type for household accounts is named "Household Account" but NPSP allows it to be renamed. Queries using `RecordType.Name = 'Household Account'` will break in orgs where the record type was renamed. Use `RecordType.DeveloperName = 'HH_Account'` (the developer name is fixed) for reliable filtering.

5. **npo02__ rollup fields exist on Contact AND on Account — with different scopes** — Rollup fields on the Contact aggregate gifts where `npsp__Primary_Contact__c` equals that Contact. Rollup fields on the Household Account aggregate all gifts linked to that Account, regardless of primary contact. Both sets have the same field names (e.g., `npo02__TotalOppAmount__c`). Using the wrong object in a report produces systematically wrong totals without any error.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Constituent 360 data model diagram | Three-layer NPSP model: HH_Account → Contact → npo02__ rollup fields, with Opportunity and PMM objects mapped |
| SOQL query library | Namespace-aware queries for giving history, rollup validation, and program participation |
| CRLP configuration checklist | Steps to add or validate a custom rollup via Rollup__mdt and Filter_Group__mdt |
| PMM availability guard | Python/Apex pattern to detect PMM installation before accessing pmdm__ objects |

---

## Related Skills

- `data/npsp-data-model` — Overlapping skill covering NPSP object relationships; this skill focuses specifically on constituent 360 and rollup architecture
- `admin/npsp-household-accounts` — Household Account configuration, naming formats, and primary contact designation
- `architect/npsp-vs-nonprofit-cloud-decision` — Decision framework for orgs choosing between NPSP and NPC; must be resolved before applying this skill

Related Skills

sandbox-data-masking

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when configuring or reviewing Salesforce Data Mask to protect PII/PHI in partial or full copy sandboxes after a refresh. Trigger keywords: data mask, sandbox masking, PII in sandbox, GDPR sandbox, HIPAA non-production, mask contacts, obfuscate fields non-production. NOT for sandbox refresh mechanics (use sandbox-refresh-and-templates), NOT for production data anonymization, NOT for Shield Platform Encryption at rest.

gdpr-data-privacy

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when implementing GDPR or CCPA data privacy controls in Salesforce: Individual sObject linkage, consent tracking, Right to Be Forgotten (RTBF) requests, data subject request handling, and Privacy Center configuration. Trigger keywords: GDPR, data privacy, consent management, right to erasure, Individual object, ContactPointConsent, ShouldForget, data subject request, Privacy Center, data portability. NOT for general data quality cleanup, duplicate management, field-level encryption (see platform-encryption skill), or sandbox data masking (see sandbox-data-masking skill).

data-classification-labels

8
from PranavNagrecha/AwesomeSalesforceSkills

Classify Salesforce fields by data sensitivity and compliance category using the four built-in classification attributes (SecurityClassification, ComplianceGroup, BusinessOwnerId, BusinessStatus). Covers Metadata API deployment, Tooling API querying, and Einstein Data Detect recommendations. NOT for data masking, Shield Platform Encryption, or runtime access control enforcement.

customer-data-request-workflow

8
from PranavNagrecha/AwesomeSalesforceSkills

Implement GDPR/CCPA data subject rights (access, deletion, rectification) using Salesforce Privacy Center and/or custom workflow. NOT for general backup or org-level data retention policy.

omnistudio-deployment-datapacks

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when exporting, importing, or version-controlling OmniStudio components using DataPacks via the OmniStudio DataPacks tool or vlocity CLI. Covers DataPack export/import, Git version control integration, CI/CD for OmniStudio. NOT for SFDX-based metadata deployment of non-OmniStudio components.

omnistudio-asynchronous-data-operations

8
from PranavNagrecha/AwesomeSalesforceSkills

Use Integration Procedures queues, DataRaptor Chain, and Remote Actions with async patterns for long-running OmniStudio flows. NOT for simple DataRaptor reads.

dataraptor-transform-optimization

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when DataRaptor Transform operations are slow, hit governor limits, or use Apex where formula fields would suffice. Covers formula vs Apex expressions, bulk transform sizing, and chained transform composition. Triggers: 'dataraptor transform slow', 'dataraptor formula vs apex', 'dataraptor bulk transform', 'dr governor limit'. NOT for DataRaptor Extract or Load performance.

dataraptor-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or reviewing OmniStudio DataRaptors, especially Extract versus Turbo Extract versus Transform versus Load, field mapping strategy, performance tradeoffs, and when to move work into Integration Procedures or Apex. Triggers: 'DataRaptor Extract', 'Turbo Extract', 'DataRaptor Load', 'DataRaptor Transform', 'OmniStudio data mapping'. NOT for overall OmniScript journey design or Integration Procedure sequencing when the main question is not the DataRaptor shape itself.

lwc-datatable-advanced

8
from PranavNagrecha/AwesomeSalesforceSkills

Advanced lightning-datatable patterns — inline edit + draftValues, custom cell types via extending LightningDatatable, sortable columns, infinite scroll with onloadmore, row-level errors, and the cost of large data sets. NOT for read-only display of small lists (plain lightning-datatable suffices) or fully custom grids (use a third-party library).

lwc-data-table

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or reviewing `lightning-datatable` usage in Lightning Web Components, including column configuration, stable `key-field` values, inline editing, row actions, infinite loading, and custom cell types. Triggers: 'lightning datatable inline edit', 'row actions in lwc datatable', 'key field missing', 'infinite loading in datatable'. NOT for highly custom virtualized grids or broad page-performance work outside the datatable boundary.

lwc-custom-datatable-types

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when you need to extend `lightning-datatable` with custom cell renderings: status pills, progress bars, image thumbnails, action cells, editable pickliststo, rich-text, or any column that `lightning-datatable` does not ship out of the box. Triggers: 'custom cell type lightning datatable', 'progress bar column', 'image column', 'inline edit picklist in datatable', 'rich text column'. NOT for basic datatable usage (see `lwc-data-table`) and NOT for tree-grid or large-dataset virtualization (see `virtualized-lists`).

salesforce-data-pipeline-etl

8
from PranavNagrecha/AwesomeSalesforceSkills

Export large Salesforce datasets to a lakehouse via Bulk API 2.0, CDC streams, or Salesforce Data Pipelines. NOT for ad-hoc exports.