Example: ASP.NET Core Razor Pages screen document¶
Doc gen produces one screen document per non-utility screen in an ASP.NET Core
Razor Pages front end, written to docs/screens/<slug>.md from the @page
file and its page model. This is a different document from the
BOF WebForms screen: a screen is an @page file, and its
interface is page handlers and routes rather than .aspx controls.
What reviewers should check¶
- Screens are
@pagefiles. Every non-utility@pagefile underPages\has a screen document, anddocs/screens/index.mdis reconciled against the@pagefiles, not the page models. Scaffold pages (Error,Privacy,Indexleft from the template) are not screens. - The Route table uses real URLs. The URL comes from the file path under
Pages\, the@pageroute template, and anyAddPageRouteconvention — not an invented path. A named handler is its own Route row only when it returns a full page. - Operations use the Core request form. The
Requestcolumn isVERB /<Page>?handler=<Name>; the default handler isVERB /<Page>. Rows named after a postback,ViewState,Controller/Action, or an.aspxfile mean the WebForms or MVC vocabulary leaked in — a defect. - Access is the effective requirement.
[SecurityApiAuthorize]values are resolved to their literal role names, and anything the base page model does that source does not show is marked†or**. - Anti-forgery is stated. Pages built on BolMiddleware frequently have
validation disabled (
[IgnoreAntiforgeryToken], often inherited from a package base class and so marked†); that is a standing tech-debt finding, not a detail to omit. This example shows an application that keeps validation on. Either way, the document must say whether POST handlers are covered by anti-forgery validation, and from what (an attribute, a global filter, or a package default). - Delegation is sourced. Every
Delegates tovalue is a resolvable link, a†-marked dotted library name, or—for screen-local handlers. - AJAX rows link to
hosted-endpoints.md. Each row in## AJAX and grid actionslinks its entry ondocs/application/hosted-endpoints.md; there is nodocs/apis/set. - Rule IDs are links to their anchor on
docs/application/business-rules.md. - A
## Screen flow diagramappears only when the screen has three or more outbound navigations.
Example¶
The document below is a complete, correct policy-summary.md for a fictional
application.
View rendered example — the same document as a real page, so you can compare rendering as well as source.
---
title: Policy Summary
summary: Shows an employer's policy at a glance and lets staff open payroll reports, start an endorsement, or print the declaration page.
status: draft
owner: Policy Services Team
last_reviewed: 2026-09-09
---
# Policy Summary
Policy Summary is the landing page of the Policy Services Portal once a policy
is in context. Underwriting and customer-service staff reach it from the menu or
from Policy Search, and use it to confirm coverage dates, the premium balance,
and the current payroll report status before acting on the policy. The page
loads the policy named by the `WebContext` policy number, renders the summary
panels, and exposes handlers to refresh the premium balance, print the
declaration page, and open the payroll report history in a dialog. From here the
user moves to payroll reports, a new endorsement, or the premium ledger.
## Route
| Page | Handler | Verb | URL | Route parameters | Page model | Layout |
| --- | --- | --- | --- | --- | --- | --- |
| `/Policy/Summary` | `OnGetAsync` | GET | `/Policy/Summary/{policyNumber?}` | `policyNumber` | `SummaryModel` (`Pages/Policy/Summary.cshtml.cs`) | `_PolicyLayout` |
| `/Policy/Summary` | `OnPostRefreshBalanceAsync` | POST | `/Policy/Summary?handler=RefreshBalance` | — | `SummaryModel` | `_PolicyLayout` |
## Access
| Requirement | Declared on | Effect |
| --- | --- | --- |
| `[SecurityApiAuthorize("PolicyServices.Read,PolicyServices.Underwriting")]` | `BolPolicyModel`† | Only users holding one of the two roles can open any page that derives from the policy page model |
| `[SecurityApiAuthorize("PolicyServices.Underwriting")]` | `SummaryModel.OnPostRefreshBalanceAsync` | Only underwriting staff can refresh the balance; others receive a redirect to `/AccessDenied` |
| `IsAuthorizedAsync(Roles.Billing)` | `SummaryModel.OnGetAsync` | Billing roles see the **Premium ledger** link; other users do not |
| Anti-forgery validation | Razor Pages default for unsafe verbs | Covers `OnPostRefreshBalanceAsync`; the page form emits the token through the form tag helper |
## Entry conditions
- The user selects **Policy Summary** from the policy menu; the entry is
`BasicAppMap.PageIds.PolicySummary`.
- The user follows an **Open** link from Policy Search, which passes
`policyNumber` as a route parameter.
- The page requires a policy number in `WebContext`. When none is present the
base page model redirects to `/Policy/Search`†.
## Fields
| Field | Binds to | Control | Purpose | Validation |
| --- | --- | --- | --- | --- |
| Policy number | `Summary.PolicyNumber` | Read-only `<span>` | Identifies the policy in context | None; display only |
| Employer | `Summary.EmployerName` | Read-only `<span>` | Insured employer of record | None; display only |
| Coverage period | `Summary.EffectiveDate`, `Summary.ExpirationDate` | Read-only `<span>` | Current term of the policy | None; display only |
| Premium balance | `Summary.PremiumBalance` | Read-only `<span>` | Outstanding premium balance, formatted as currency | None; display only |
| Report year | `ReportYear` | Kendo DropDownList | Year used when the payroll report dialog opens | `[BindProperty(SupportsGet = true)]`; must be a year listed in the policy history; client-side yes |
| Refresh balance | — | Button | Re-reads the premium balance from the billing service | Subject to anti-forgery validation |
## Operations
| Operation | Trigger | Request | Description | Delegates to |
| --- | --- | --- | --- | --- |
| load-summary | Page load | `GET /Policy/Summary` | Reads the policy and premium balance for the policy number in context and populates the summary panels | `GetPolicySummary` in `SAIF.Policy.Services`† |
| refresh-balance | `btnRefreshBalance` click | `POST /Policy/Summary?handler=RefreshBalance` | Re-reads the premium balance from the billing service and redisplays the page | [RefreshPremiumBalance](../libraries/saif-policy-billing/operations/refresh-premium-balance.md) |
| print-declaration | **Print declaration** link | `GET /Policy/Summary?handler=Declaration` | Streams the declaration page for the policy as a PDF file result | `RenderDeclaration` in `SAIF.Policy.Documents`† |
| open-payroll-history | `lnkPayrollHistory` click | `GET /Policy/Summary?handler=PayrollHistory` | Returns the payroll report history partial for the selected report year | — |
## Dialogs and partials
| Name | Opens as | Returned by | Purpose |
| --- | --- | --- | --- |
| `_PayrollHistory` | Bootstrap modal, fetched into `#dlgPayroll` | `GET /Policy/Summary?handler=PayrollHistory` | Lists payroll reports filed for the selected year with their status |
## AJAX and grid actions
| Request | Kind | Called by | Returns |
| --- | --- | --- | --- |
| [`GET /Policy/Summary?handler=PayrollHistory`](../application/hosted-endpoints.md#get-policysummaryhandlerpayrollhistory) | Partial fetch | `policySummary.openPayrollHistory()` | `_PayrollHistory` partial view |
| [`GET /Policy/Summary?handler=Declaration`](../application/hosted-endpoints.md#get-policysummaryhandlerdeclaration) | File handler | **Print declaration** link | `FileStreamResult`, `application/pdf` |
## Client-side behavior
- **Script module:** `wwwroot/js/policy/summary.js`
- The `ReportYear` dropdown is a Kendo DropDownList; changing it reloads the
payroll history partial when the dialog is open.
- `policySummary.openPayrollHistory()` fetches the partial with `fetch`, injects
it into `#dlgPayroll`, and shows the Bootstrap modal.
- Failed fetches raise a toast through the BolMiddleware notification helper†.
## States
| State | Condition |
| --- | --- |
| Redirect to `/AccessDenied` | The user lacks the role required by the handler |
| `ErrorMessage` banner | The billing service call fails during **Refresh balance**; the page redisplays with the previous balance |
| Premium ledger link hidden | The user does not hold a billing role |
## Business rules
| Rule ID | Rule | Enforcement | Source |
| --- | --- | --- | --- |
| [BR-SCR-001](../application/business-rules.md#br-scr-001-balance-refresh-restricted-to-underwriting) | Only underwriting staff can refresh a premium balance | `[SecurityApiAuthorize]` on `OnPostRefreshBalanceAsync` | `src/SAIF.Policy.Portal/Pages/Policy/Summary.cshtml.cs:64` |
| [BR-SCR-002](../application/business-rules.md#br-scr-002-payroll-history-limited-to-policy-years) | Payroll history can only be requested for a year in the policy history | `ReportYear` validated against `Summary.PolicyYears` in `OnGetPayrollHistoryAsync` | `src/SAIF.Policy.Portal/Pages/Policy/Summary.cshtml.cs:92` |
| [BR-APP-001](../application/business-rules.md#br-app-001-policy-context-required) | A policy number must be in context before a policy page renders | Base page model redirect† | `BolPolicyModel`† |
## Navigation
| Target | Trigger | Parameters |
| --- | --- | --- |
| [Payroll Reports](payroll-reports.md) | **Payroll reports** menu entry or panel link | `policyNumber` route parameter |
| [New Endorsement](endorsement-new.md) | **Start endorsement** button | `policyNumber` route parameter |
| [Premium Ledger](premium-ledger.md) | **Premium ledger** link, shown to billing roles | `policyNumber` route parameter |
## Screen flow diagram
```mermaid
flowchart TD
A[Policy Summary] -->|Payroll reports| B[Payroll Reports]
A -->|Start endorsement| C[New Endorsement]
A -->|Premium ledger| D[Premium Ledger]
B -->|Back to summary| A
```
---
> [!NOTE]
> Statements marked with `*` are inferred from code patterns. Statements marked with `**` could not be verified from available source. Statements marked with `†` are sourced from package documentation retrieved with the Legends CLI.
## Sources
- src/SAIF.Policy.Portal/Pages/Policy/Summary.cshtml
- src/SAIF.Policy.Portal/Pages/Policy/Summary.cshtml.cs
- src/SAIF.Policy.Portal/Pages/Policy/_PayrollHistory.cshtml
- src/SAIF.Policy.Portal/Models/PolicySummaryViewModel.cs
- src/SAIF.Policy.Portal/wwwroot/js/policy/summary.js