SSRS Reports: Precision Design
Build, design, and deploy SQL Server Reporting Services reports in F&O β RDP classes, precision vs auto design, report projects in Visual Studio, and extending standard reports.
Why SSRS is still the backbone of F&O reporting
Think of a printing press.
Power BI is great for exploring data on screen, but when you need to print something β an invoice, a packing slip, a cheque β you need a tool that controls exactly where every line, logo, and number appears on the page. Thatβs SSRS.
SSRS is the printing press of F&O. You design the exact layout in Visual Studio, feed it data from an X++ class, and it produces pixel-perfect PDF, Word, or Excel documents every time.
Precision design vs auto design
Every SSRS report in F&O uses one of two design approaches:
| Aspect | Auto Design | Precision Design |
|---|---|---|
| Layout creation | Generated automatically from dataset fields | Manually designed in RDLC designer (Visual Studio) |
| Control over placement | Minimal β columns render in order | Complete β every textbox, image, line is placed precisely |
| Use case | Simple tabular lists, internal reports | Invoices, cheques, packing slips, any externally-facing document |
| Grouping / subtotals | Basic grouping supported | Full control: nested groups, conditional visibility, sub-reports |
| Effort to build | Low β drag dataset, done | Higher β manual layout, expressions, formatting |
| Modification approach | Change dataset or query | Edit .rdl XML or use VS designer |
Exam tip: When to pick which design
The exam tests this distinction. Rule of thumb:
- βThe report must match an existing printed form layout exactlyβ β Precision design
- βThe report shows a simple list of records for internal reviewβ β Auto design
- βThe customer invoice must include a company logo at specific coordinatesβ β Precision design
- βA developer needs a quick data dump to verify business logicβ β Auto design
Building an SSRS report: the full pipeline
Hereβs the end-to-end flow for creating a custom SSRS report:
βββββββββββββββ βββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β 1. Contract βββββΆβ 2. RDP βββββΆβ 3. Report βββββΆβ 4. Menu Item β
β Class β β Class β β Design β β + Privilege β
β (parameters) β β (data logic)β β (VS .rdl) β β (security) β
βββββββββββββββ βββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β
βββββββββββ΄ββββββββββ
β 5. Deploy to β
β Report Server β
ββββββββββββββββββββββ
Step 1: Contract class (parameters)
The contract class defines what the user enters before running the report.
[DataContractAttribute]
class PFProductionReportContract
{
TransDate fromDate;
TransDate toDate;
ProdPoolId prodPool;
[DataMemberAttribute('FromDate'),
SysOperationLabelAttribute(literalStr("From date"))]
public TransDate parmFromDate(TransDate _fromDate = fromDate)
{
fromDate = _fromDate;
return fromDate;
}
[DataMemberAttribute('ToDate'),
SysOperationLabelAttribute(literalStr("To date"))]
public TransDate parmToDate(TransDate _toDate = toDate)
{
toDate = _toDate;
return toDate;
}
[DataMemberAttribute('ProdPool'),
SysOperationLabelAttribute(literalStr("Production pool"))]
public ProdPoolId parmProdPool(ProdPoolId _prodPool = prodPool)
{
prodPool = _prodPool;
return prodPool;
}
}
Key decorators explained
| Decorator | Purpose |
|---|---|
[DataContractAttribute] | Marks the class as a serialisable data contract |
[DataMemberAttribute('Name')] | Exposes the property as a parameter with a serialisation name |
[SysOperationLabelAttribute] | Sets the label shown to users in the parameter dialog |
[SysOperationGroupAttribute] | Groups parameters visually in the dialog |
[SysOperationDisplayOrderAttribute] | Controls the order parameters appear |
Step 2: RDP class
The RDP class runs the actual data retrieval and business logic.
[SRSReportParameterAttribute(classStr(PFProductionReportContract))]
class PFProductionReportDP extends SRSReportDataProviderBase
{
PFProductionReportTmp tmpTable;
[SRSReportDataSetAttribute(tableStr(PFProductionReportTmp))]
public PFProductionReportTmp getTmpTable()
{
select tmpTable;
return tmpTable;
}
public void processReport()
{
PFProductionReportContract contract = this.parmDataContract()
as PFProductionReportContract;
TransDate fromDate = contract.parmFromDate();
TransDate toDate = contract.parmToDate();
ProdTable prodTable;
while select prodTable
where prodTable.SchedDate >= fromDate
&& prodTable.SchedDate <= toDate
{
tmpTable.ProdId = prodTable.ProdId;
tmpTable.ItemId = prodTable.ItemId;
tmpTable.QtyGood = prodTable.QtyGood;
tmpTable.SchedDate = prodTable.SchedDate;
tmpTable.insert();
}
}
}
Sophie asks: Why a temp table and not a direct query?
Sophie Chen asks Elena: βWhy canβt the report just read from ProdTable directly?β
Elena explains:
- Business logic β you often need calculated fields, joined data from multiple tables, or conditional logic that a simple query canβt express
- Performance β the temp table is populated once, then the report reads from it. The live table isnβt locked during rendering
- Isolation β the reportβs data snapshot is frozen at execution time. No dirty reads mid-render
- Reusability β the same RDP class can feed multiple report designs
βFor simple reports with no logic, use an AOT query instead,β Elena adds. βBut most real-world reports need the RDP class.β
Step 3: Report design in Visual Studio
| Task | How |
|---|---|
| Add report to project | Right-click project β Add β New Item β Report |
| Add dataset | In report, add dataset β select RDP class or query |
| Auto design | Right-click Designs β Add Auto Design β fields render automatically |
| Precision design | Right-click Designs β Add Precision Design β opens RDLC designer |
| Expressions | Use =Fields!FieldName.Value for data, =Parameters!Param.Value for parameters |
| Grouping | In precision design, add row groups for subtotals and page breaks |
Step 4: Menu item and security
Every report needs a menu item output to be accessible:
| Element | Purpose |
|---|---|
| Menu item output | The entry point β Object = ReportName, ObjectType = SSRSReport |
| Privilege | Grants access to the menu item |
| Duty | Contains the privilege |
| Role | Contains the duty β users with this role can run the report |
Step 5: Deploying reports
| Method | When |
|---|---|
| Build + Deploy from VS | Development environment β right-click report β Deploy |
| Incremental sync | After code deployment β new or changed reports deploy automatically |
| PowerShell | Force-refresh on report server for stubborn deployments |
| Admin page | System Administration β Setup β Deploy reports |
Exam tip: Report deployment troubleshooting
If a report doesnβt appear after deployment:
- Check the deploy log in Visual Studio output window for errors
- Verify the reportβs menu item exists and is correctly linked
- Confirm the userβs security role includes the privilege
- Run Deploy reports from System Administration if incremental sync missed it
- Clear browser cache β sometimes old report parameter dialogs are cached
Extending standard reports
You should never modify a standard report directly (thatβs overlayering). Instead, use extensions.
| Extension approach | What it does | When to use |
|---|---|---|
| Delegate handler | Subscribe to report events to modify data or parameters | Add calculated fields, filter data, change default parameters |
| Custom RDP extending standard | Create a new RDP class that extends the standard one | Need to add new data sources or heavy custom logic |
| Report format customisation | Duplicate the .rdl design, modify layout, register as custom | Change visual layout (logo placement, column order, totals) |
| Print management override | Configure Print Management to use your custom report instead of standard | Replace the standard invoice/packing slip with your version |
Scenario: Elena extends the sales invoice
PacificForge needs the standard sales invoice to include a βProduction Batch Numberβ column. Elenaβs approach:
- Creates a new report design β duplicates the standard SalesInvoice precision design
- Extends the RDP class β adds a delegate handler to the standard
SalesInvoiceDPthat populates the batch number field - Registers the custom design β creates a new report with the modified layout
- Configures Print Management β sets the custom report as the default for sales invoices
βWe never touch the standard report,β Elena tells compliance officer Kenji. βIf Microsoft updates it, our extension still works because weβre subscribing to events, not modifying source.β
Print Management
Print Management decides which report design to use when printing documents like invoices, purchase orders, and packing slips.
| Concept | Detail |
|---|---|
| Report format | Maps a document type to a specific report design |
| Conditions | Print different designs based on customer, vendor, or other criteria |
| Destinations | Screen, printer, email, file β configurable per document type |
| Override hierarchy | Module defaults β Account-level β Transaction-level |
Elena needs to create a production report that calculates weighted average costs across multiple warehouses with conditional discount logic. Which data source approach should she use?
PacificForge's customer invoices must include a company logo at exact coordinates and match a government-mandated layout. Which report design type should Elena use?
Vik needs to add a custom field to the standard purchase order report without overlayering. What is the correct approach?
Next up: Power BI & Analytical Workspaces β embedding interactive dashboards and KPI tiles directly in F&O.