October 26, 2026

Writing Your First YSR //Columns and //Filter Script for Yardi

Every Voyager custom report starts life as a plain-text script — a flat sequence of //Section ... //End Section blocks. For the columnar grid style (the default when //Database is empty, and the encouraged style for on-screen grids and exports), two sections do almost all the work: //Columns, which defines what the grid looks like, and //Filter, which defines what the user gets to prompt for. Get comfortable with those two and you can write most rent-roll-shaped reports from scratch.

This walks through both, field by field, using a minimal skeleton — a property/unit list with three columns and a property filter.

The shape of the file

Section markers are case-insensitive and whitespace-tolerant (//End Select, //end select, //END SELECT all parse the same way), and the conventional order is //Notes → //Database///Options → //Title → //Version → //Select → //ColHeading → //Columns → //Filter — though order isn't strictly enforced, just conventional. The //Select section holds your actual T-SQL query. //Columns and //Filter, by contrast, aren't SQL at all — they're comma-delimited spec tables, one row per column or per prompt.

Here's a minimal //Select to build the rest of the example against:

//Select
select
   p.scode              PropertyCode
  ,u.scode              UnitCode
  ,u.dsqft              Area
from property p
inner join unit u on u.hproperty = p.hmy
where 1=1
  and u.exclude <> -1
#conditions#
order by p.scode, u.scode
//End Select

Three output columns — property code, unit code, area — and a #conditions# token sitting in the WHERE clause. That token is the bridge to //Filter, and we'll come back to it.

//Columns: one row per output column

Each row in //Columns binds, positionally, to one column coming out of the //Select. The commonly-used fields, in order, are: Type, Name, Head1, Head2, Head3, Head4, Show, Color, Formula, Drill, Key, Width, Total (the full grammar has a few more rarely-used slots after Total — Control, Mandatory, List, Validate, Default, Keep, Suppress — that exist but are rarely populated; do computation in the SQL instead of leaning on them).

For the property/unit example:

//Columns
//Type, Name, Head1, Head2, Head3, Head4,    Show, Color, Formula, Drill, Key, Width, Total
T,      ,     ,      ,      ,      Property, Y,    ,      ,        3,     ,    700,   N
T,      ,     ,      ,      ,      Unit,     Y,    ,      ,        4,     ,    700,   N
D,      ,     ,      ,      ,      Area,     Y,    ,      ,        ,      ,    900,   Y

A few things worth understanding about each field:

Rows parse by comma position, so pad trailing commas even when a field is empty, and comment rows can start with //.

//Filter: one row per runtime prompt

//Filter defines what shows up as a prompt when the report runs. Each row is: Type, DataTyp, Name, Caption, Key, List, Val1, Val2, Mandatory, Multi, Title.

//Filter
//Type, DataTyp, Name,  Caption,   Key, List, Val1,            Val2, Mand, Multi, Title
C,      T,       p.hMy, *Property, ,    61,   p.hMy = #p.hMy#, ,     Y,    Y,     Y,

Breaking that row down:

How the two sections connect back to //Select

The #conditions# token in the //Select block is the catch-all slot Voyager splices every active filter's Val1 fragment into at runtime; inactive filters simply contribute nothing, so the query stays valid no matter which combination of filters the user actually fills in. Numbered variants — #condition1#, #condition2#, and so on — exist for reports with multiple datasets that need to route different filters to different queries. Every filter's own Name also becomes a standalone token (#p.hMy# here) available anywhere in the SQL, not just inside the condition slot — which is exactly what made p.hMy = #p.hMy# in the Val1 fragment above resolve to something the database can run.

Putting the whole thing together

A complete minimal report is just these pieces plus //Notes (documentation, ignored by the engine, but worth keeping current) and a //Title:

//Notes
    NAME:        rs_<pack>_<ReportName>.txt
    DESCRIPTION: <one line>
    MODIFIED
      <date> <ticket> <author> Created.
//End Notes

//Title
<Report Title>
//End Title

//Select
select
   p.scode              PropertyCode
  ,u.scode              UnitCode
  ,u.dsqft              Area
from property p
inner join unit u on u.hproperty = p.hmy
where 1=1
  and u.exclude <> -1
#conditions#
order by p.scode, u.scode
//End Select

//Columns
//Type, Name, Head1, Head2, Head3, Head4,    Show, Color, Formula, Drill, Key, Width, Total
T,      ,     ,      ,      ,      Property, Y,    ,      ,        3,     ,    700,   N
T,      ,     ,      ,      ,      Unit,     Y,    ,      ,        4,     ,    700,   N
D,      ,     ,      ,      ,      Area,     Y,    ,      ,        ,      ,    900,   Y
//End Columns

//Filter
//Type, DataTyp, Name,  Caption,   Key, List, Val1,            Val2, Mand, Multi, Title
C,      T,       p.hMy, *Property, ,    61,   p.hMy = #p.hMy#, ,     Y,    Y,     Y,
//End Filter

Six sections, one property filter, three columns — and every other columnar report on a Voyager install is a variant of this same shape with more columns, more filter rows, and richer SQL in //Select.

Where to go from here

Once the basic shape is comfortable, the places people actually get stuck are knowing which real tables and columns belong in //Select for a given business question, and which of the less-common //Columns///Filter fields (drill codes, picklist ids, multi-select token expansion) apply. That's what the PropETL SQL Query Assistant is built to help with — it carries the full //Columns and //Filter grammars alongside a queryable map of the Voyager schema (9,793 tables, 137,000+ columns, 12,900+ relationships), plus sanitized script skeletons by category, queryable in plain English inside Claude.

It's included in every tier, including the free 7-day trial. Details on the SQL Assistant page.

Featured on FoundrListFeatured & Verified on TrustMRRPropETL on PeerPushLaunchpadly Startup DirectoryPropETL — Featured on LaunchBuck