TDL Development: The Language That Extends TallyPrime
From objects and collections to reports, forms, parts, lines, fields, functions, actions, events, and user-defined fields. Build custom reports, custom invoices, and complete TallyPrime extensions — hands-on, step-by-step, with production-ready code examples.
What Is TDL?
TDL stands for Tally Definition Language. It is the proprietary, domain-specific programming language designed specifically for extending TallyPrime. Everything beyond Tally's built-in features — custom reports, custom invoices, user-defined fields, automated workflows, external integrations, and third-party add-ons — is built using TDL.
Why TDL Exists
No software can anticipate every business requirement. A dairy company needs a milk-collection report. A pharma distributor needs batch-wise expiry tracking. A textile exporter needs multi-currency invoices with specific layouts. Tally cannot ship every possible feature.
Instead, Tally provides a complete definition language that lets developers build anything the business needs — while keeping the underlying engine stable, tested, and compliant.
What TDL Can Do
- Create entirely new reports — define report structure, columns, filters, calculations, and layouts from scratch
- Modify existing reports — add columns, change sorting, add filters, or hide fields in Tally's built-in reports
- Build custom invoice and voucher formats — full control over print layouts, logos, QR codes, barcodes, and dynamic content
- Add user-defined fields (UDF) — extend masters and vouchers with custom data fields specific to your business
- Automate workflows — trigger actions on voucher save, validation, delete, print, or user login
- Integrate with external systems — send data to web services, fetch data from databases, respond to HTTP requests
- Customise the UI — add buttons, menus, keyboard shortcuts, and custom forms
- Extend TallyPrime Server capabilities — build enterprise-grade extensions that scale across users
What TDL Cannot Do
- Not a general-purpose language like C#, Java, or Python
- Not a way to bypass Tally's accounting logic
- Not a way to modify Tally's core engine
- Not a replacement for Tally's built-in features
- Not suitable for building standalone applications
- A domain-specific extension language
- A way to define reports, forms, and behaviours
- A way to extend without breaking
- A complement to Tally's built-in features
- Suitable for extending Tally's ecosystem
TDL vs Other Extension Approaches
| Approach | What It Can Do | Limitations |
|---|---|---|
| XML/JSON Integration | Push/pull vouchers, ledgers, masters | Cannot create new reports or modify UI |
| ODBC | Read-only analytics access | No writes, no UI modification |
| TDL | Everything — reports, invoices, UDF, events, integrations | Requires TDL knowledge, specific tooling |
Who Should Learn TDL
- Software developers building Tally add-ons, integrations, or enterprise extensions
- ERP consultants who need to deliver custom requirements beyond Tally's built-in features
- Chartered accountants who want to build custom MIS reports for their clients
- CFOs and finance leaders who want to understand what is possible with Tally before commissioning projects
- Business analysts who need to bridge the gap between business needs and technical implementation
TDL Architecture & Execution Model
Before writing a single line of TDL, you must understand where TDL sits in TallyPrime's architecture and how it executes. This mental model is critical — it separates developers who merely copy-paste TDL from those who can design and debug custom extensions.
Where TDL Sits in the Stack
How TDL Executes — The Lifecycle
- Load: When TallyPrime starts, it loads its own TDL definitions first, then loads any external TDL files you have added (add-ons, custom reports, etc.)
- Parse: The TDL interpreter reads each definition and builds an internal model of Reports, Forms, Parts, Lines, Fields, Functions, and Actions
- Resolve: When a user opens a report, Tally resolves the report definition, evaluates any attributes or formulas, and constructs the display
- Fetch: Data is retrieved via Collections from the object model. Filters are applied. Sorting is applied. Aggregations are computed
- Render: The report is drawn on screen using Tally's built-in rendering engine
- Interact: User actions (clicks, key presses) trigger Events, which fire Actions, which may modify data, navigate, or run functions
Where TDL Files Live
- Built-in TDL: Bundled inside TallyPrime itself — you never see these files directly
- Add-on TDL: Placed in the TallyPrime installation folder or a custom folder configured in Tally
- User TDL: Loaded via the
Load TDLmenu in TallyPrime Developer or via F1 → Settings → TDLs & Add-Ons - Compiled TDL: TDL files can be compiled into
.tcp(Tally Compiled Program) format for distribution and protection
TallyPrime Developer — The TDL IDE
TallyPrime Developer is the official IDE for writing, testing, and debugging TDL code. It provides:
- Syntax highlighting for TDL keywords, definitions, attributes, and values
- Code completion for definitions and attributes
- Built-in debugger to step through TDL execution
- Object browser to explore Tally's built-in objects and methods
- Definition search to find where Tally defines its own reports, functions, and behaviours
- Compilation to
.tcpformat for distribution
Balance Sheet) and see exactly how Tally defines it. This is like having the source code to Tally's built-in reports.
TDL Syntax Basics
TDL is a declarative language. You do not write procedural code that executes line by line. Instead, you declare definitions that describe what you want Tally to do. Tally's interpreter reads your declarations and figures out the execution.
The Three Building Blocks
| Building Block | Purpose | Example |
|---|---|---|
| Definition | Declares a component — a report, form, part, line, field, function, etc. | [Report: MyReport] |
| Attribute | Specifies a property of a definition | Title : "My Report" |
| Value | The actual data assigned to an attribute | "My Report", 123, Yes |
The Simplest TDL Definition
;; The simplest TDL definition [Report: MyFirstReport] Title : "My First Custom Report" Form : MyFirstForm
This definition declares a report named MyFirstReport. The report has a title and uses a form named MyFirstForm. That is the entire report definition. Tally will now know this report exists — though it will not yet appear in the menu until we add it to a menu structure.
Comments in TDL
;; This is a single-line comment in TDL /* This is a multi-line comment */
Naming Conventions
- Definitions use PascalCase:
MyCustomReport,SalesRegisterExtended - Attributes use PascalCase:
Title,Form,Variable - Values can be strings, numbers, expressions, or keywords
- User-Defined Functions typically start with
UDF_or a prefix - User-Defined Fields use the
UDFprefix - Never use reserved keywords as definition names — check TallyPrime Developer's definition browser
Value Types in TDL
| Type | Examples | Notes |
|---|---|---|
| String | "Hello", "Sales Report" |
Always in double quotes |
| Number | 100, 3.14, -500 |
Can be integer or decimal |
| Keyword | Yes, No, True, False |
Boolean-like values |
| Method | $Name, $ClosingBalance |
Prefixed with $, retrieves data from objects |
| Formula | #MyFormula, $$Value:Field |
Prefixed with # or $$ |
| Variable | SVFromDate, SVToDate |
System variables (SV prefix) or custom variables |
| Expression | 1 + 2, #Amount * 0.05 |
Mathematical or logical expressions |
A Slightly Richer Example
;; A report with multiple attributes [Report: MyLedgerReport] Form : MyLedgerForm Title : "Custom Ledger Report" Variable : SVFromDate, SVToDate Variable : MyFilter Set : MyFilter : "All Ledgers" [Form: MyLedgerForm] Part : MyLedgerPart Width : 100 % Screen Height : 100 % Screen [Part: MyLedgerPart] Lines : MyLedgerLines Repeat : MyLedgerLines : MyLedgerColl [Line: MyLedgerLines] Fields : Name, ClosingBalance Local : Field : Name : Set as : $Name Local : Field : ClosingBalance : Set as : $ClosingBalance [Collection: MyLedgerColl] Type : Ledger Fetch : Name, ClosingBalance Sort : -$ClosingBalance
Objects — The Building Blocks of Data
In Tally's architecture, every business entity is an object. A Ledger is an object. A Voucher is an object. A Stock Item is an object. A Group is an object. Even a Company is an object. Understanding this object model is fundamental to TDL.
What Is an Object?
- Identity: Every object has a name or identifier (e.g., "Rahim Traders", "SI-2026-0001")
- Methods: Functions to retrieve data from the object (e.g.,
$Name,$ClosingBalance) - Collections: Groups of related objects (e.g., a Ledger's Bill Allocations)
- Attributes: Properties that define the object's behaviour
- Parent: The object higher in the hierarchy (e.g., a Ledger's parent Group)
The Object Hierarchy
Accessing Object Data — Methods
In TDL, you retrieve data from an object using methods. Methods always start with the $ symbol.
;; Common methods for accessing object data $Name ;; The name of the current object $ClosingBalance ;; Ledger's closing balance $OpeningBalance ;; Ledger's opening balance $Parent ;; The parent of the current object $Date ;; Voucher date $VoucherNumber ;; Voucher number $Amount ;; Amount in ledger entry $IsDeemedPositive ;; Is the entry a debit? $Group ;; The group a ledger belongs to
$Parent.$Name returns the name of the current object's parent. This is how you walk up and down the object tree.
How to Explore Available Methods
- Open TallyPrime Developer and use the Object Browser
- Search for built-in TDL definitions that reference the object type you are working with
- Read the official TDL reference documentation
- Use Debug mode to inspect the current object's available methods
- Study Tally's own TDL source for any built-in report that uses that object type
Practical Example — Walking the Hierarchy
;; Example: Get the Group name of a Ledger [Function: GetLedgerGroup] Returns : String Variable : LedgerObj : Object : Ledger Returns : $LedgerObj.Parent.$Name ;; Example: Check if a Ledger is a Sundry Debtor [Function: IsSundryDebtor] Returns : Logical Variable : LedgerObj : Object : Ledger Returns : $LedgerObj.Parent.$Name = "Sundry Debtors"
Collections — Fetching Sets of Objects
A Collection is a set of objects retrieved from the object model. Collections are how TDL queries data. Every report you build in TDL is powered by one or more collections.
The Anatomy of a Collection
[Collection: MyLedgerCollection] Type : Ledger Fetch : Name, ClosingBalance, Parent Filter : MyLedgerFilter Sort : -$ClosingBalance [System: Formulae] MyLedgerFilter : $$IsLedger:$Name
| Attribute | Purpose | Example |
|---|---|---|
| Type | What kind of objects to fetch | Ledger, Voucher, StockItem, Group |
| Fetch | Which methods to compute and cache | Name, ClosingBalance |
| Filter | Which objects to include | A formula returning Yes/No |
| Sort | Order of objects | -$ClosingBalance (descending) |
| Walk | Traverse parent-child relationships | MyLedgerColl (walk down) |
Collection Types
- Simple Collection: Fetches a flat list of objects of one type (e.g., all Ledgers)
- Nested Collection: A collection inside a collection (e.g., a Ledger's vouchers)
- Walking Collection: Traverses parent-child relationships (e.g., a Group's sub-groups and ledgers)
- Method Collection: Uses a Method to determine which objects to fetch
Filters — The Power of Selective Data
Filters determine which objects end up in a collection. They are formulas that evaluate to Yes or No for each object. If the formula returns Yes, the object is included. If No, it is excluded.
;; Filter: Only Sundry Debtors with balance over 10000 [System: Formulae] HighValueDebtors : $$IsSundryDebtor:$Parent AND $ClosingBalance > 10000 ;; Filter: Only Sales vouchers in a date range SalesInRange : $VoucherTypeName = "Sales" AND $Date >= $$SVFromDate AND $Date <= $$SVToDate
Sorting — Order Matters
;; Sort ascending by name Sort : $Name ;; Sort descending by balance Sort : -$ClosingBalance ;; Multi-level sort: group ascending, then balance descending Sort : $Parent, -$ClosingBalance
Aggregation — Computing Totals
TDL provides built-in aggregate functions that operate on collections:
$Total— Sum of a method across all objects in a collection$Count— Number of objects in a collection$Average— Average of a method across all objects$Maximum— Highest value of a method$Minimum— Lowest value of a method
Type : Ledger with no filters. Get it working. Then add a filter. Then add sorting. Then add aggregation. Build up complexity one step at a time.
Reports — The Top-Level Definition
A Report is the top-level definition in TDL. It represents an entire screen that the user can open, view, and interact with. Every report in Tally — from the Day Book to the Balance Sheet — is defined as a Report.
The Minimal Report Definition
[Report: MyReport] Form : MyForm Title : "My Custom Report" Variable : SVFromDate, SVToDate
| Attribute | Purpose |
|---|---|
| Form | The form that defines the report's layout |
| Title | The title displayed at the top of the report |
| Variable | Variables this report uses (system or custom) |
| Set | Initial value for a variable |
| Menu | Context menu shown when the report is open |
| On Enter | Action to run when the user enters the report |
| On Exit | Action to run when the user exits the report |
| Repeat | Report-level repetition (for hierarchical reports) |
System Variables You Will Use Everywhere
SVFromDate— The report's "from" date (user can change with F2)SVToDate— The report's "to" date (user can change with F2)SVCurrentCompany— The active companySVCurrentDate— Today's dateSVPeriodicity— Monthly, quarterly, yearly reporting periodSVCostCentre— The active cost centreSVShowLedgerDetails— A flag for showing drill-down detail
Adding a Report to the Menu
A report definition alone does not make it accessible from the Tally menu. You must add it to a Menu definition.
;; Add report to a custom menu [Menu: MyCustomMenu] Add : Item : MyReport : MyReport Key : Alt+F10 ;; Alternative: Add to existing menu [Menu: Display More Reports] Add : Item : MyReport : MyReport
Drill-Down — Reports Inside Reports
One of Tally's signature UX patterns is drill-down. From a Balance Sheet, you can press Enter on a Group to see its Ledgers. Press Enter on a Ledger to see its Vouchers. Press Enter on a Voucher to see its entries.
;; Enable drill-down on a field [Field: LedgerName] Use : Name Field Set as : $Name On : Enter : DrillDown [Report: DrillDownReport] Form : LedgerDetailForm Title : $$String:$Name + " - Details" Variable : SVCurrentLedger Set : SVCurrentLedger : $$Value:$Name
Forms, Parts, Lines, Fields — The UI Hierarchy
A report's visual layout in TDL is built through a strict hierarchy: Form → Part → Line → Field. Every TDL UI element lives somewhere in this hierarchy. Understanding this is essential for building any custom report or invoice.
The Four-Level Hierarchy
Form Definition
[Form: MyReportForm] Part : MyHeaderPart, MyBodyPart, MyFooterPart Width : 100 % Screen Height : 100 % Screen Button : MyPrintButton, MyExportButton Key : F12 : MyConfigMenu
A form defines:
- Which parts it contains and in what order they appear
- The overall dimensions (width and height)
- What buttons appear at the bottom
- What keys are bound to actions (like F12 for configuration)
Part Definition
[Part: MyBodyPart] Lines : MyHeaderLine, MyDataLine, MyTotalLine Repeat : MyDataLine : MyLedgerColl Scroll : Vertical Border : Thin Left Common Border : Yes
| Part Attribute | Purpose |
|---|---|
| Lines | Which lines this part contains |
| Repeat | Which line repeats for each object in a collection |
| Scroll | Enable horizontal or vertical scrolling |
| Border | Add border lines |
| Common Border | Share border with adjacent parts |
| Vertical | Yes for vertical parts (split screen) |
Line Definition
[Line: MyDataLine] Fields : LedgerName, OpeningBal, Debit, Credit, ClosingBal Local : Field : LedgerName : Set as : $Name Local : Field : OpeningBal : Set as : $OpeningBalance Local : Field : Debit : Set as : $$AsAmount:$$Debit Local : Field : Credit : Set as : $$AsAmount:$$Credit Local : Field : ClosingBal : Set as : $ClosingBalance Border : Thin Bottom
Field Definition
[Field: LedgerName] Use : Name Field Width : 30 % Screen Style : Bold Align : Left [Field: ClosingBal] Use : Amount Field Width : 20 % Screen Align : Right Style : Normal
| Field Attribute | Purpose | Common Values |
|---|---|---|
| Use | Pre-defined field type | Name Field, Amount Field, Date Field |
| Width | Field width | Percentage or fixed character count |
| Align | Text alignment | Left, Right, Center |
| Style | Visual styling | Bold, Normal, Italic, Underline |
| Set as | The value to display | A method or formula |
| Info | Tooltip text | String |
Variables & System Formulas
Variables are named storage for values. System Formulas are named expressions that TDL evaluates to compute values. Together, they are how TDL handles dynamic data — anything that depends on user input, current context, or calculated logic.
Types of Variables
| Type | Prefix | Purpose |
|---|---|---|
| System Variable | SV |
Built-in — from date, to date, current company, active user |
| System Formula | $$ |
Built-in computed formula — $$AsAmount, $$String, $$Value |
| User Variable | Any name | Custom variable you declare in a report or form |
| Method Variable | Any name | Variable that holds a method reference |
Declaring Custom Variables
[Report: MyReport] Form : MyForm Variable : MyStartDate Variable : MyEndDate Variable : MyShowZero Set : MyStartDate : $$SVFromDate Set : MyEndDate : $$SVToDate Set : MyShowZero : No
System Formulas — The Workhorses
System formulas always start with $$. They are called "system" because they are built into Tally. Some of the most used:
$$String:...— Convert a value to a string$$Value:...— Get the current value of a field or variable$$AsAmount:...— Format a number as an amount (with commas and decimals)$$AsQty:...— Format a number as a quantity$$IsLedger:...— Check if an object is a Ledger$$IsSundryDebtor:...— Check if an object is a Sundry Debtor$$IsSundryCreditor:...— Check if an object is a Sundry Creditor$$Total:...— Sum a method across a collection$$Count:...— Count objects in a collection$$CollectionFieldByKey:...— Look up a field value in a collection
Naming Your Own Formulas
;; Define formulas in the System: Formulae section [System: Formulae] IsHighValue : $ClosingBalance > 100000 IsCurrentMonth : $Date >= $$SVFromDate AND $Date <= $$SVToDate GSTAmount : $Amount * 0.15 IsSalesVoucher : $VoucherTypeName = "Sales"
Using Variables in a Report
[Report: HighValueReport] Form : HighValueForm Variable : SVFromDate, SVToDate Variable : MinAmount Set : MinAmount : 100000 [Collection: HighValueColl] Type : Ledger Fetch : Name, ClosingBalance Filter : $ClosingBalance > $$Value:MinAmount
Functions — Reusable Logic in TDL
Functions in TDL let you package reusable logic. Tally provides hundreds of built-in functions, and you can define your own User-Defined Functions to encapsulate business logic.
Built-in Function Categories
| Category | Examples |
|---|---|
| String Functions | $$StringLength, $$StringFind, $$StringReplace, $$Upper, $$Lower, $$Trim |
| Number Functions | $$Round, $$Ceiling, $$Floor, $$Abs, $$Power, $$Sqrt |
| Date Functions | $$Year, $$Month, $$Day, $$DateDiff, $$AddDays |
| Format Functions | $$AsAmount, $$AsQty, $$AsDate |
| Aggregate Functions | $$Total, $$Count, $$Maximum, $$Minimum |
| Type Check Functions | $$IsLedger, $$IsVoucher, $$IsEmpty |
| Object Functions | $$Object, $$CollectionFieldByKey, $$Fetch |
Defining a User Function
;; User-defined function that calculates GST [Function: UDF_CalculateGST] Parameter : Amount : Number Parameter : Rate : Number Returns : Number Returns : $$Value:Amount * $$Value:Rate / 100 ;; User-defined function that determines debit or credit [Function: UDF_IsDebit] Parameter : EntryObj : Object : LedgerEntry Returns : Logical Returns : $EntryObj.IsDeemedPositive
Using User Functions
;; Call a user function from a formula [System: Formulae] GSTOnAmount : #UDF_CalculateGST(10000, 15) ;; Call a user function from a field [Field: TaxAmount] Set as : #UDF_CalculateGST($Amount, 15)
UDF_ or your company's short code (e.g., ABC_CalculateGST). This avoids collisions with Tally's built-in functions and with functions from other add-ons.
Recursion in User Functions
TDL supports recursive functions, which are especially useful for traversing hierarchical structures like Group → Ledger trees.
;; Recursive function to sum all balances in a Group [Function: UDF_GroupBalance] Parameter : GroupObj : Object : Group Returns : Number Returns : $GroupObj.ClosingBalance ;; Note: This is a simplification. Real recursive group walking ;; requires walking child groups and ledgers separately.
Actions & Events — The Behaviour Layer
Actions are blocks of behaviour that execute in response to Events. An event is something that happens — a user pressing a key, a voucher being saved, a report being entered. Actions respond to those events.
Common Events in TDL
| Event | When It Fires | Typical Use |
|---|---|---|
| On Enter | User enters a report or form | Initialise variables, load defaults |
| On Exit | User exits a report or form | Clean up, save state |
| On Save | Voucher is being saved | Validate, compute fields, send notifications |
| On Delete | Voucher is being deleted | Prevent deletion, log event |
| On Alter | Voucher is being altered | Track changes, validate modifications |
| On Print | Voucher is being printed | Custom print layout, additional data |
| On Key | User presses a key | Keyboard shortcuts |
| On Button | User clicks a button | Custom button actions |
| On Focus | Field gains focus | Auto-populate, validation |
Defining an Action
[Action: MyOnSaveAction] Action : Set : MyVariable : 1 Action : New Object : Voucher Action : Display : "Voucher Saved Successfully" [VoucherType: Sales] On : Save : MyOnSaveAction
Common Actions
Set— Assign a value to a variableDisplay— Show a message dialogNew Object— Create a new object (Voucher, Ledger, etc.)Alter Object— Modify an existing objectDelete Object— Delete an objectOpen Report— Navigate to a reportCall— Execute a user-defined functionExport— Export data to fileImport— Import data from fileHTTP Post— Send an HTTP request to an external serviceLog— Write to a log fileBreak— Stop execution (for debugging)
Example — Voucher Save Validation
;; Prevent saving Sales vouchers above a threshold without approval [Function: UDF_ValidateSalesAmount] Returns : Logical Returns : $$Value:$$Total:SalesAmount < 500000 [Action: ValidateSalesAmount] Action : Call : UDF_ValidateSalesAmount Action : Display : "Sales above 5 lakh requires manager approval" Action : Break [VoucherType: Sales] On : Save : ValidateSalesAmount
• Send WhatsApp/SMS alerts when a high-value voucher is saved
• Auto-calculate commission on Sales vouchers
• Validate customer credit limit before saving a Sales invoice
• Push voucher data to an external ERP via HTTP POST
• Log user activity for audit purposes
• Auto-print invoices with custom layouts
User-Defined Fields (UDF)
UDF (User-Defined Fields) are custom fields you can add to Tally's masters and vouchers. This is how you extend Tally's data model to capture business-specific information — without modifying the underlying database.
Why UDF Matters
- No schema migration: Add fields without touching Tally's core
- Reportable: UDFs can be used in filters, sorts, and report columns
- Integrable: UDFs can be pushed and pulled via XML/JSON
- Safe: UDFs never break Tally's accounting logic
- Customisable: Define per company or globally
Common UDF Scenarios
| Object Type | Example UDF | Business Use |
|---|---|---|
| Customer Ledger | UDF_Region | Region-wise sales reporting |
| Customer Ledger | UDF_CreditScore | Risk-based credit limits |
| Supplier Ledger | UDF_PaymentPriority | Payment scheduling |
| Stock Item | UDF_ShelfLife | Expiry tracking |
| Stock Item | UDF_Manufacturer | Brand-wise analysis |
| Sales Voucher | UDF_SalesPerson | Commission calculation |
| Sales Voucher | UDF_DeliveryDate | Logistics tracking |
| Purchase Voucher | UDF_ApprovedBy | Approval workflow |
Defining a UDF
;; Define a UDF for Customer Ledger [UDF: UDF_Region] Type : String Length : 20 Report : Ledger Tabular : Yes Display : Region [UDF: UDF_CreditScore] Type : Number Report : Ledger Tabular : Yes Display : Credit Score
UDF Attributes Explained
| Attribute | Purpose | Values |
|---|---|---|
| Type | Data type | String, Number, Date, Logical, Amount, Quantity |
| Length | Maximum length (for String) | A number |
| Report | What master the UDF belongs to | Ledger, StockItem, Voucher, etc. |
| Tabular | Show in list view | Yes / No |
| Display | Label shown to user | A string |
| Set as | Default value | Any value |
Using UDF in Reports
[Collection: CustomerColl] Type : Ledger Fetch : Name, ClosingBalance, UDF_Region Filter : $UDF_Region = "Dhaka" [Field: RegionField] Set as : $UDF_Region
UDF via XML Integration
<LEDGER NAME="Rahim Traders" ACTION="Create"> <PARENT>Sundry Debtors</PARENT> <UDF:REGION>Dhaka</UDF:REGION> <UDF:CREDITSCORE>750</UDF:CREDITSCORE> </LEDGER>
<UDF:FIELDNAME>. The field name is the UDF name you defined, with the UDF_ prefix included. So UDF_Region becomes <UDF:REGION> in XML.
Building a Custom Report — Complete Walkthrough
Let us build a real custom report from scratch. Requirement: A report showing all Sundry Debtors with their region (from UDF), outstanding balance, and a high-value flag for balances over ৳1,00,000.
Step 1 — Design the Visual Layout
- Header: Report title and date range
- Column Headers: Customer Name, Region, Outstanding, Flag
- Data Rows: One row per Sundry Debtor
- Total Row: Sum of all outstanding balances
- Footer: Count of high-value customers
Step 2 — Define the UDF (if not already existing)
[UDF: UDF_Region] Type : String Length : 20 Report : Ledger Tabular : Yes Display : Region
Step 3 — Define the Collection
[Collection: DebtorColl] Type : Ledger Fetch : Name, ClosingBalance, UDF_Region, Parent Filter : IsSundryDebtor Sort : -$ClosingBalance [System: Formulae] IsSundryDebtor : $Parent = "Sundry Debtors"
Step 4 — Define the Report
[Report: DebtorRegionReport] Form : DebtorRegionForm Title : "Debtor Region Report" Variable : SVFromDate, SVToDate Variable : SVCurrentCompany
Step 5 — Define the Form
[Form: DebtorRegionForm] Part : DebtorRegionHeader, DebtorRegionBody Width : 100 % Screen Height : 100 % Screen Key : F2 : ChangeDate Key : F4 : ChangePeriod
Step 6 — Define the Parts
[Part: DebtorRegionHeader] Lines : DebtorHeaderLine Border : Thin Bottom [Part: DebtorRegionBody] Lines : DebtorDataLine, DebtorTotalLine Repeat : DebtorDataLine : DebtorColl Scroll : Vertical
Step 7 — Define the Lines
[Line: DebtorHeaderLine] Fields : HeaderName, HeaderRegion, HeaderBalance, HeaderFlag Local : Field : HeaderName : Set as : "Customer Name" Local : Field : HeaderRegion : Set as : "Region" Local : Field : HeaderBalance : Set as : "Outstanding" Local : Field : HeaderFlag : Set as : "High Value" Style : Bold [Line: DebtorDataLine] Fields : DataName, DataRegion, DataBalance, DataFlag Local : Field : DataName : Set as : $Name Local : Field : DataRegion : Set as : $UDF_Region Local : Field : DataBalance : Set as : $ClosingBalance Local : Field : DataFlag : Set as : $$String:$$IsHighValue Border : Thin Bottom [System: Formulae] IsHighValue : $ClosingBalance > 100000 [Line: DebtorTotalLine] Fields : TotalLabel, TotalValue Local : Field : TotalLabel : Set as : "Total:" Local : Field : TotalValue : Set as : $$AsAmount:$$Total:DebtorColl:$ClosingBalance Style : Bold
Step 8 — Define the Fields
[Field: DataName] Use : Name Field Width : 40 % Screen Align : Left [Field: DataRegion] Width : 20 % Screen Align : Center [Field: DataBalance] Use : Amount Field Width : 25 % Screen Align : Right [Field: DataFlag] Width : 15 % Screen Align : Center
Step 9 — Add to Menu
[Menu: Display More Reports] Add : Item : DebtorRegionReport : DebtorRegionReport
1. Save the TDL file
2. In TallyPrime: F1 → Settings → TDLs & Add-Ons → Load TDL
3. Select your file
4. Navigate to Gateway of Tally → Display More Reports
5. Your custom report should appear
• Test with real data before deploying
• Add error handling for missing UDF values
• Use
$$IsEmpty to safely handle null values• Version your TDL files (v1.0, v1.1, etc.)
• Document every report with comments
Building a Custom Invoice — Full Print Layout
Custom invoice layouts are one of the most valuable uses of TDL. Tally's built-in invoice formats cover standard cases, but businesses often need custom layouts — with logos, QR codes, specific tax summaries, terms and conditions, and industry-specific fields.
What You Can Customise
- Header: Company logo, name, address, GST/VAT numbers
- Customer Block: Name, address, contact, BIN, shipping details
- Invoice Details: Number, date, PO reference, delivery date
- Item Table: Custom columns — HSN, batch, expiry, MRP
- Tax Summary: Rate-wise tax breakup, CGST/SGST or VAT
- Totals: Subtotal, discount, tax, round-off, grand total
- Amount in Words: Auto-conversion using system formula
- QR Code: UPI payment QR or e-invoice QR
- Footer: Terms, bank details, signature, declaration
- Barcode: Product-wise barcode for scanner support
The Invoice Form Structure
[Form: MyCustomInvoiceForm] Part : CompanyHeader, CustomerBlock, InvoiceDetails, ItemTable, TaxSummary, GrandTotal, AmountInWords, FooterTerms, SignatureBlock Width : 100 % Screen Height : 100 % Screen Print : Yes Page : A4 Margin : Left : 10 mm, Right : 10 mm, Top : 10 mm, Bottom : 10 mm
Adding a Logo to the Invoice
[Part: CompanyHeader] Lines : LogoLine, CompanyNameLine Border : Thin Bottom [Line: LogoLine] Fields : LogoImage Local : Field : LogoImage : Set as : "C:\\TallyAddons\\logo.png" Local : Field : LogoImage : Width : 40 mm Local : Field : LogoImage : Height : 20 mm [Line: CompanyNameLine] Fields : CompanyName, CompanyAddress Local : Field : CompanyName : Set as : "ABC Electronics Ltd." Local : Field : CompanyName : Style : Bold Local : Field : CompanyName : Align : Center
The Item Table with Custom Columns
[Part: ItemTable] Lines : ItemHeaderLine, ItemDataLine Repeat : ItemDataLine : ItemColl [Line: ItemHeaderLine] Fields : HSNHeader, ItemHeader, QtyHeader, RateHeader, AmountHeader Local : Field : HSNHeader : Set as : "HSN" Local : Field : ItemHeader : Set as : "Item Description" Local : Field : QtyHeader : Set as : "Qty" Local : Field : RateHeader : Set as : "Rate" Local : Field : AmountHeader : Set as : "Amount" Style : Bold Border : Thin Bottom [Line: ItemDataLine] Fields : HSNData, ItemData, QtyData, RateData, AmountData Local : Field : HSNData : Set as : $StockItemHSNCode Local : Field : ItemData : Set as : $StockItemName Local : Field : QtyData : Set as : $$AsQty:$ActualQty Local : Field : RateData : Set as : $$AsAmount:$Rate Local : Field : AmountData : Set as : $$AsAmount:$Amount [Collection: ItemColl] Type : StockItem Fetch : StockItemName, StockItemHSNCode, ActualQty, Rate, Amount
Adding a QR Code
[Line: QRCodeLine] Fields : PaymentQR Local : Field : PaymentQR : Set as : #UDF_GenerateUPIQR("ABC Electronics", "abc@upi", $$Value:GrandTotal) [Function: UDF_GenerateUPIQR] Parameter : Payee : String Parameter : UPIID : String Parameter : Amount : Number Returns : String Returns : "upi://pay?pa=" + $$Value:UPIID + "&pn=" + $$Value:Payee + "&am=" + $$String:$$Value:Amount
Amount in Words
[Line: AmountInWordsLine] Fields : AmountInWords Local : Field : AmountInWords : Set as : $$NumberToWords:$GrandTotal Local : Field : AmountInWords : Style : Italic
Tax Summary
[Line: TaxRateHeader] Fields : TaxRateLabel, TaxableLabel, CGSTLabel, SGSTLabel, TotalLabel [Line: TaxRateData] Fields : TaxRate, TaxableAmt, CGSTAmt, SGSTAmt, TotalTaxAmt Local : Field : CGSTAmt : Set as : $$AsAmount:$$Total:TaxColl:$CGST Local : Field : SGSTAmt : Set as : $$AsAmount:$$Total:TaxColl:$SGST
• Test with long customer names and addresses
• Test with many items (page breaks)
• Test with zero-quantity or zero-rate items
• Test with negative amounts (returns)
• Test on A4, A5, and thermal printer sizes
• Verify QR code scans correctly
• Verify amount in words is correct
Deployment, Distribution & FAQ
Compiling TDL to .tcp
For distribution, TDL files should be compiled into .tcp (Tally Compiled Program) format. This protects your source code and simplifies deployment.
- Open TallyPrime Developer
- Load your
.tdlfile - Go to File → Compile or press Ctrl+F9
- Choose the output folder for the
.tcpfile - Test the compiled file in a fresh TallyPrime instance
Distribution Options
| Method | Best For | Pros | Cons |
|---|---|---|---|
| Direct .tdl file | In-house customisation | Easy to modify, source visible | Source not protected |
| Compiled .tcp file | Commercial add-ons | Source protected, compact | Cannot be modified |
| Installer package | Wide distribution | Professional, easy install | Requires installer tooling |
| Cloud-loaded TDL | SaaS-style add-ons | Automatic updates | Requires server infrastructure |
Best Practices & Pitfalls
- Version-control every TDL file
- Test in a separate company before production
- Comment every function and report
- Use prefixes (UDF_, ABC_) to avoid collisions
- Keep TDL files small and modular
- Handle empty/null values with $$IsEmpty
- Provide clear error messages
- Document installation steps
- Test on target TallyPrime version
- Backup original before deploying
- Modifying Tally's built-in reports directly
- Using reserved keywords as names
- Hardcoding paths to files (use config)
- Ignoring version compatibility
- Forgetting to test across users/companies
- Skipping null value handling
- Not testing print layouts
- Deploying without backup
- No error logging in production
- Abandoning TDL without documentation
TDL Learning Resources
- TallyPrime Developer: The built-in IDE with definition search — your best learning tool
- Definition Search: Look up any built-in report to see how Tally defines it
- Tally Developer Reference: Official documentation from Tally Solutions
- TDL Forums: Community forums and Stack Overflow
- GitHub: Search for open-source TDL projects
- Practice: The only way to truly learn TDL is to build real reports
Frequently Asked Questions
Basic programming understanding helps, but TDL is a declarative language with a gentle learning curve. If you understand accounting concepts and can think logically about data relationships, you can learn TDL. The syntax is declaration-based rather than procedural.
No. TDL extends Tally without modifying its core. You can add reports, fields, and behaviours, but you cannot change how Tally calculates debits and credits, or how it stores data. This is by design — it protects data integrity.
TDL is the language. TallyPrime Developer is the IDE (Integrated Development Environment) used to write, test, and debug TDL code. TallyPrime Developer was formerly known as Tally Developer.
Common causes:
- Collection filter is returning no objects
- Field
Set asis not retrieving the right method - Date range excludes all data
- Report is not loaded (check TDLs & Add-Ons)
- Menu is not properly linked
Use TallyPrime Developer's debugger to set breakpoints and inspect variables.
Yes, more relevant than ever. TallyPrime's entire customisation framework runs on TDL. All third-party Tally add-ons, custom reports, and advanced integrations are built using TDL. As long as TallyPrime is used by businesses, TDL will be the way to extend it.
Yes. TDL can act as an ODBC client, fetching data from external databases via SQL queries. This enables importing customer, stock, or other data from external systems into Tally. TDL also supports HTTP POST for calling external web services.
Thousands. TallyPrime contains 500+ built-in reports, each defined with numerous sub-definitions (forms, parts, lines, fields, functions). The total count of TDL definitions in TallyPrime exceeds 10,000 — all accessible via Definition Search in TallyPrime Developer.
The Complete TDL Learning Path
- Week 1 — Foundation: Understand TDL syntax, definitions, attributes, values
- Week 2 — Objects & Collections: Master the object model, methods, and collection mechanics
- Week 3 — Reports & UI: Build simple reports, forms, parts, lines, fields
- Week 4 — Functions & Actions: Write user functions, handle events, trigger actions
- Week 5 — UDF & Data Extension: Add user-defined fields to masters and vouchers
- Week 6 — Custom Invoice: Build a complete customised invoice layout
- Week 7 — Integration: Connect TDL to XML, JSON, ODBC, and HTTP
- Week 8 — Production: Compile, deploy, test, document, distribute
Series Roadmap — 45 Parts to CFO-Level Mastery
You have completed 22 parts of the TallyPrime Deep-Dive Masterclass. Here is the complete roadmap.
0 Comments
thanks for your comments!