Epicor BAQ Calculated Field Examples Reference
Most BAQ problems are small expression problems: a null that wipes out a label, a divide by zero, a date difference that counts the wrong way. This reference collects calculated field patterns you can paste into the Query Builder and adapt.
| Group | Calculated field | Expression | What it does | Watch out for |
|---|---|---|---|---|
Dates | DaysLate | datediff(day, JobHead.DueDate, getdate()) | Days between the job due date and today | Positive means past due. Closed jobs still return a value, filter on JobClosed. |
Dates | OrderAgeDays | datediff(day, OrderHed.OrderDate, getdate()) | Age of an order in days | getdate() is server time, not the user's time zone. |
Dates | OrderMonth | month(OrderHed.OrderDate) | Month number for grouping | Pair with year(), otherwise two years collapse into one month. |
Dates | OrderYear | year(OrderHed.OrderDate) | Year number for grouping | Cast to varchar when concatenating with text. |
Dates | OrderDateText | convert(varchar(10), OrderHed.OrderDate, 101) | mm/dd/yyyy text for exports | Text sorts alphabetically, keep the real date column for sorting. |
Dates | WeekStart | dateadd(day, 1 - datepart(weekday, OrderHed.OrderDate), OrderHed.OrderDate) | Date of the first day of the week | Depends on the SQL Server DATEFIRST setting. |
Quantities | QtyRemaining | JobHead.ProdQty - JobHead.QtyCompleted | Open quantity on a job | Can go negative on overruns. Wrap in case when if not wanted. |
Quantities | PctComplete | case when JobHead.ProdQty = 0 then 0 else (JobHead.QtyCompleted / JobHead.ProdQty) * 100 end | Percent complete with divide by zero guard | Decimal types matter. Multiply by 100.0 if you see integer results. |
Quantities | IsShort | case when OrderDtl.OrderQty > 0 and JobHead.QtyCompleted < OrderDtl.OrderQty then 1 else 0 end | Flag lines where output is below ordered quantity | Only valid if the tables are joined on the correct job to line link. |
Pricing | ExtPrice | OrderDtl.OrderQty * OrderDtl.DocUnitPrice | Extended line amount in document currency | Ignores discounts and price per code. Compare with DocExtPriceDtl. |
Pricing | MarginPct | case when OrderDtl.DocExtPriceDtl = 0 then 0 else ((OrderDtl.DocExtPriceDtl - (OrderDtl.OrderQty * Part.UnitPrice)) / OrderDtl.DocExtPriceDtl) * 100 end | Rough margin versus a reference price | Part.UnitPrice is a list price, not cost. Use a cost table for real margin. |
Pricing | LineBand | case when OrderDtl.DocExtPriceDtl >= 10000 then 'A' when OrderDtl.DocExtPriceDtl >= 1000 then 'B' else 'C' end | Bucket lines into value bands | Order of WHEN branches matters, the first match wins. |
Text | PartAndDesc | Part.PartNum + ' - ' + Part.PartDescription | Combined label for dashboards | A null on either side makes the whole result null. Use coalesce. |
Text | SafeDesc | coalesce(Part.PartDescription, '') | Replace null with an empty string | Use isnull only if you accept the first argument's data type. |
Text | PartPrefix | left(Part.PartNum, 3) | First three characters of a part number | Part numbers with leading spaces break this. Add ltrim(). |
Text | TrimmedPart | ltrim(rtrim(Part.PartNum)) | Remove stray spaces from keys | Fix the data instead if joins fail because of trailing spaces. |
Logic | StatusText | case when OrderHed.VoidOrder = 1 then 'Void' when OrderHed.OpenOrder = 1 then 'Open' else 'Closed' end | Readable order status | Check the flag semantics in your release with the Data Dictionary Viewer. |
Logic | HeldFlag | case when OrderHed.OrderHeld = 1 then 'HELD' else '' end | Show a marker for held orders | Booleans are bit fields, compare with 1, not the word true. |
Logic | IsPastDue | case when JobHead.DueDate < getdate() and JobHead.JobClosed = 0 then 1 else 0 end | Flag open jobs past their due date | Use cast(getdate() as date) to ignore the time portion. |
Aggregates | TotalQty | sum(OrderDtl.OrderQty) | Aggregate used in a Group By query | Every non-aggregate column must be in the Group By list. |
Aggregates | LineCount | count(OrderDtl.OrderLine) | Number of lines per order | Use count(distinct ...) if joins multiply rows. |
Parameters | FilterByCustomer | OrderHed.CustNum = @CustNum | Criteria pattern using a BAQ parameter | Parameters must be defined on the Phrase Build or Criteria tab with a matching data type. |
Expressions use T-SQL functions, which the BAQ calculated field editor passes to SQL Server. Table and column names shown follow common Epicor naming (OrderHed, OrderDtl, JobHead, Part). Confirm every column in your release with the Data Dictionary Viewer before relying on it.
Want your BAQs documented automatically?
Optional: we can walk through how a read-only assistant could explain and test your BAQs against your own Epicor data.
Optional. The tool and every export work without this. No spam.
How to use this reference
Search for the situation you are in, such as date, case or coalesce, then copy the expression into Query Builder on the Calculated Fields tab. Replace the table and column names with the ones in your own query, because a calculated field can only reference tables already added to the query. Set the data type of the calculated field to match the result: decimal for amounts, int for flags, nvarchar for text, datetime for dates. Test with a small criteria filter first, then remove it once the output looks right.
- Add the table to the query before referencing it in an expression
- Match the field data type to the expression result
- Test on a handful of rows before running a full extract
- Name fields in PascalCase so they read well in dashboards
Why calculated fields behave the way they do
A BAQ calculated field is evaluated by SQL Server as part of the generated query, so the rules of T-SQL apply: integer division truncates, any arithmetic with a null returns null, string concatenation with a null returns null, and CASE branches are checked in order. Most surprises come from one of those four rules. When a result looks wrong, test the expression in a SQL client against the same tables, or simplify it in the editor until the faulty part is isolated.
Reading the examples
Each row lists the expression, the purpose and the main thing to watch for. The pricing examples use document currency fields, so they are correct for single currency sites and need review when the base currency differs. The margin example compares against a list price only to show the pattern. For real margin use a cost source such as part cost or job costs. Treat every row as a starting point, since field availability changes between releases.
Common mistakes
The most frequent errors are using a calculated field inside a join condition, building very large nested CASE blocks that nobody can read, and forgetting that a Group By query needs aggregates over columns, not over other calculated fields without care. Another trap is formatting dates as text early and then sorting on the text. Keep raw values in the query and format at the dashboard or report layer.
- Do not nest more than three CASE levels, split into two fields instead
- Guard every division with a zero check
- Wrap nullable text in coalesce before concatenating
- Keep date columns as dates and format in the report
Frequently Asked Questions
Where do I add a calculated field in an Epicor BAQ?
Open the BAQ in Query Builder, go to the Display Fields or Calculated Fields area, add a new calculated field, name it, choose its data type and enter the expression. Then select it on the Display Fields tab so it appears in the output of the query.
Why does my BAQ calculated field return null?
In SQL Server any arithmetic or string concatenation involving a null returns null. Wrap nullable columns in coalesce with a safe default such as zero or an empty string, and check that the joined table actually has a matching row for each result.
Can I use SQL functions in a BAQ expression?
Yes, T-SQL functions like datediff, dateadd, coalesce, convert, left and case expressions are commonly used. Functions that are not allowed by the BAQ designer or by your security settings will raise an error when you test the query.
Should I use a BAQ calculated field or a function library?
A calculated field is fine for short expressions that stay in one query. If the same logic is needed in several BAQs, reports and customizations, a function in a library is easier to test and maintain, though it costs more setup.
If your team maintains dozens of BAQs and wants help documenting or testing them against live ERP data, Netray can talk through an approach that stays read-only and runs on your own infrastructure.
Related Tools
Epicor Table Reference: Erp.Part, Erp.JobHead, Erp.OrderHed
Searchable list of frequently used columns in three core Epicor tables, with type and usage notes for BAQs, reports and REST queries.
EpicorEpicor Function Library Design Template
A design document for an Epicor function: signature, inputs, outputs, logic notes, security, test cases and sign off.
EpicorEpicor REST API PowerShell Script Generator
Fill in your server, company and service name to generate a PowerShell script that calls Epicor REST v2 and saves the result to CSV.
EpicorEpicor SSRS Report Template Checklist
A pre-release checklist for custom Epicor SSRS reports covering report style, data definition, layout, parameters, security and testing.
More Epicor tools
See all- Epicor Kinetic Customization Change Request Form Template
- Epicor Kinetic Go-Live Cutover Plan Template
- Epicor Kinetic User Access Request Form Template
- Epicor Kinetic BPM Directive Inventory Worksheet
- Epicor Kinetic Scheduled Task Register Worksheet
- Epicor Kinetic Custom Report Inventory Worksheet
- Epicor Kinetic Data Migration Mapping Worksheet
- Epicor Kinetic Data Load Effort Calculator
- Epicor Business Object Services Reference