Explore Categories

 

 PDF

Choosing the Right Approach

Practice makes things perfect. The perfect way of writing code benefits both the Customer and Partner. There are hundreds of capabilities in TDL and a solution can be achieved with the different approaches. But, you should choose the right approach that gives a better experience in a longer period and even with the large volume of data. With this guide, you shall be able to understand the TDL capabilities and their elements with a comparison of a similar kind. This shall help you to decide when to use and what capability.

Set as with IF-THEN-ELSE condition Vs Set By condition

Set as with IF-THEN-ELSE

The attribute is utilised to set a value at the field. If the end-user wants to provide a conditional output developer can use the If-Then-Else construct. A nested If-Then-Else construct can be used to provide conditional output. However, the nested conditions can be provided only up to 13 conditions. In case more conditions are required, the last condition should be a formula. This formula can further contain more If-Then-Else constructs. When it is a nested condition, the readability of the condition is difficult.

Set By Condition

The attribute Set By Condition is similar to a conditional Set at Field level. If multiple Set By Condition is mentioned under a Field, then the last satisfied Set By Condition will be executed. There are no limitations on the number of usages of the attribute in the field. This attribute makes the code more readable. ‘Set By Condition’ is similar to Set As with condition in situations wherein both are specified, Set By Condition has precedence over Set As. Only when the condition specified at Set By Condition fails, Set As would be executed.

Option vs Switch

Option

When any definition needs to be executed based on condition sequentially, satisfying conditions, all the provided conditions are evaluated. The most appropriate condition is executed. In case of a higher number of options, all the options in the definition are evaluated and the most appropriate condition is executed. When the number of options is more and the conditions are complexed, the evaluation takes longer. Even though the most appropriate condition is met, the system continues to evaluate the conditions. The benefit of using an option is it allows you to execute multiple conditional definitions.

 

 

Switch

When any definition needs to be executed based on condition sequentially, satisfying conditions, however, the loop would break once the first condition is satisfied. Conditions can be grouped based on their criteria using labels.

Using the Switch is better as it has better readability and gives better performance. It breaks the loop once the satisfied condition is executed. Hence, this speeds up the performance. If multiple conditional definitions require to be executed, we need to provide different labels to each condition.

Scenario

Requirement

Bad Code

Good Code

Ideal Artifact

Different Parts

Depending on voucher Mode parts in voucher change

[Part: EI Info]

   Option:EI InvInvoice : NOT @@AcctsInvoice

   Option:EI ACcInvoice : @@AcctsInvoice

[Part: EI Info]

   Switch : EIInfo: EIInvInvoice : NOT @@AcctsInvoice

   Switch : EIInfo : EIAccInvoice : @@AcctsInvoice

Switch

Child Of vs Filter

Child Of and Filter attributes at Collection definition pulls out a set of data from a Superset. The functionality of both attributes seems similar. However, there is little difference between them.

 

Child Of

Filter

A subset of data is fetched from a particular object. This object should fall under the parent-child hierarchy like ledgers of a group, vouchers of a voucher type.

 

A subset of data is fetched from more than one single object. The filtering parameter may not be a parent object. When multiple filters are required to be applied like, vouchers of a particular date range, Stock items having non zero closing balance and GST applicable.

The collection is gathered only for the parent object. Use cases like Trigger reports, Drilldown reports, where data gathered is for a preselected object.

In the case of a filter, the collection is fully gathered, and then the ‘Filter’ is applied.

‘Child of’ takes lesser memory and thus quicker reports.

‘Filter’ is expensive in terms of memory and speed as it requires multiple iterating.

Some quick examples to provide more details

Scenario

Requirement

Bad Code

Good Code

Ideal Artifact

Ledgers of a group

To filter a set of ledgers of a particular group

[Collection: TSPLColl]

   Type   : Ledger

   Filter : DebtorFilter

[Collection : TSPLColl]

   Type      : Ledger

   Child of  : $$GroupSundryDebtors

 Child Of

Vouchers of a Voucher Type

To filter a set of transactions of a particular Type

[Collection: TSPLColl]

   Type   : Voucher

   Filter : SalesFilter

[Collection: TSPLColl]

    Type     : Vouchers : VoucherType

    Child Of : $$VchTypeSales

Vouchers of a Ledger

To filter a set of transactions of a particular ledger

[Collection: TSPLColl]

    Type   : Voucher

    Filter : LedgerFilter

[System: Formulae]

   LedgerFilter: NOT $$IsEmpty:$LedgerEntries[1,@@LedEqual].LedgerName

   LedEqual: $LedgerName = “Sunny Enterprises”

 

[Collection: TSPLColl]

    Type     : Vouchers : Ledger

    Child Of : “Sunny Enterprises”

Vouchers of a VoucherType and a Ledger

To filter a set of transactions of a particular Voucher type for a Ledger

[Collection: TSPLColl]

   Type   : Voucher

   Filter : LedgerFilter, VchTypeFilter

[System: Formulae]

   LedgerFilter : NOT $$IsEmpty:$LedgerEntries[1,@@LedEqual].LedgerName

   LedEqual     : $LedgerName = “Sunny Enterprises”

   VchTypeFilter: $$IsEqual:$VoucherTypeName:$$VchTypeSales

[Collection: TSPLColl]

    Type     : Vouchers : Ledger

    Child Of : “Sunny Enterprises”

    Filter   : VchTypeFilter

[System: Formulae]

    VchTypeFilter: $$IsEqual:$VoucherTypeName:$$VchTypeSales

Combination of ‘Child of’ and ‘Filter’

Ledgers having zero Opening Balance

To filter ledgers containing no opening balance

————————————

[Collection: TSPLColl]

   Type     : Ledger

   Filter   : LedgerZero

[System: Formulae]

   LedgerZero :  $$IsEmpty:$OpeningBalance Or $OpeningBalance =< 0

Only Filters

Stock items having non zero closing balance and GST applicable

To filter stock items that has zero opening balance and GST is applicable

———————————–

[Collection: TSPLColl]

   Type   : Stock Item

   Filter : NotZeroClBal, IsGSTAppl

[System: Formulae]

   NotZeroClBal  : NOT $$IsEmpty:$ClosingBalance

   IsGSTAppl     : $GstApplicable

Optimal Fetches 

Each object in the Tally database has a huge set of hierarchies. Hence it is essential to fetch only the required set of data. When a collection is gathered, certain methods are fetched by default. Simultaneously, if required, the rest needs to be fetched explicitly using the ‘fetch’ attribute. The more the number of fetches, more the time consumed to gather the data. So it is a good practice to fetch only the required methods and refrain from using *. * <subcollection>. *, as this wild card; fetches all the methods. Using this method impacts the performance of data gathering and make the processing slow.

When to use .*?

Fetching all the methods with .* can be done only when

  • The methods required is not known well ahead.
  • The collection is used as a source collection for multiple summary collections where the methods required are not preset.

When not to use .*?

When the required methods are already known

Scenario

Bad Code

Good Code

Fetching only required methods – in the main collection

Fetching Narration and PartyName from voucher

[Collection: TSPLVchColl]

    Type  : Voucher

    Fetch : *.*

[Collection: TSPLVchColl]

    Type  : Voucher

    Fetch : Narration, PartyName

Fetching required methods from a subcollection, in the main collection

Fetching Amount and Rate from AllInventoryEntries, BatchName and Amount from BatchAllocations

[Collection: TSPLVchColl]

   Type  : Voucher

   Fetch : AllInventoryEntries.*, AllInventoryEntries.Batchallocations.*

[Collection: TSPLVchColl]

    Type  : Voucher

    Fetch : AllInventoryEntries.Amount, AllInventoryEntries.Rate

    Fetch  :  AllInventoryEntries.Batchallocations.BatchName, AllInventoryEntries.Batchallocations.Amount

Using dotted notations

Every database object is connected to the interface object. An interface would be associated with a data object. When an Interface is in the context of a data object, any object method can be fetched. However, to fetch methods from sub-collection or another primary object’s subcollection, we would have to use platform functions to fetch values. The more functions involved in fetching the data, the higher the time consumption.

Scenario

Bad code

Good code

Require Batch Name from Batch Allocations of the voucher

;; This is a repeated line

[Line : Expl InventoryLines]

        Field      : Expl InventoryLines

[Field : Expl InventoryLines]

       Set as :   $$CollectionField:$StockItemName:$$Line:InventoryEntries

[Line : Expl Batch Part]

   Field : Expl Batch Part

   [Field : Expl Batch Part]

     Set as : $$CollectionField:$MyBatchName:$$Line:InventoryEntries

;;Modifying Inventory Object

[#Object : Inventory Entry]

   MyBatchName : $$FilterValue:$BatchName:BatchAllocations:First:IsStockName

[System : Formula]

   IsStockName : #ExplInventoryLines = $StockItemName

[Line : Expl InventoryLines]

        Field      : Expl InventoryLines

[Field : Expl InventoryLines]

       Set as : $InventoryEntries[$$Line].StockItemName

[Line : Expl Batch Part]

            Field : Expl Batch Part

[Field : Expl Batch Part]

      Set as : $InventoryEntries[1,#ExplInventoryLines = $StockItemName].BatchAllocations[$$Line].BatchName

Batch posting

The database is locked for the time of updating data from external sources and unlocked on completion. Batch Posting action enables updating the database in a batch. In the absence of this action, each object will update the database, thus consuming high memory.

The bigger the batch size, the lesser the total number of batches, and the better the performance. The operation will be accomplished faster if the Batch Size specified is optimal. A very high Batch Size, beyond a particular point, may also deteriorate the performance. Hence, striking the right balance and specifying the optimal batch size is essential to achieve the best performance.

Scenario

Bad code

Good code

Creating objects from an external collection

Creating 180 ledgers from excel

[Function: TSPLCreateLedgers]

   001 : Walk Collection: TSPLExcelColl

   002 :  New Object: Ledger

…..

…..

   033 :  Save Target

   034 : End Walk

[Function: TSPLCreateLedgers]

   000 : Start Batch Post : 50

   001 :   Walk Collection : TSPLExcelColl

   002 :     New Object: Ledger

…..

…..

   033 :     Save Target

   034 :  End Walk

   035 : End Batch Post

Data aggregation vs Totaling Functions

In every business report at the end, we need aggregated data and values. The aggregation can be achieved through functions using FilterAmountTotal, CollAmtTotal, CollNumTotal, FilterNumTotal, CollNumTotalEx, and an exhaustive list. Each time the functions are triggered/evaluated, the collection is constructed and scanned more than once if more than one aggregated value is required from the same collection. Thus, leading to a performance hit.

With the grouping and aggregation capabilities of the collection- Walk, By, and Aggr Compute, aggregated values can be obtained at a very high performance.

Benefits

  • With the aggregation capabilities of collection, the required aggregated values are readily available in the collection, and collection needs to be constructed only once with required values.
  • These capabilities also allow obtaining maximum and minimum values in a collection.
  • With the help of CollectionFieldByKey or repeating the collection, these aggregated values can be accessed without reconstructing the collection.

Scenario

Bad Code

Good Code

Stock Itemwise Total quantity and Amount for a period

[Collection: VchColl]

   Type     : Vouchers : VoucherType

   Child Of : $$VchTypeSales

[Field: StkBilled]

   Set as        : $$CollQtyTotal:VchColl:@StkTot

   Local Formula : StkTot :  $$FilterQtyTotal:Allinventoryentries:StkEqual:$BilledQty

[Field: StkActual]

   Set as        : $$CollQtyTotal:VchColl:@StkTot

   Local Formula : StkTot: $$FilterQtyTotal:Allinventoryentries:StkEqual:$ActualQty

[Field: StkAmount]

   Set as        : $$CollAmtTotal:VchColl:@StkTot

   Local Formula : StkTot: $$FilterAmtTotal:AllInventoryEntries:StkEqual:$Amount

[System : Formula]

        StkEqual: #StkName = $StockItemName

[Collection: VchSalesColl]

    Type     : Vouchers : VoucherType

    Child Of : $$VchTypeSales

[Collection: VchCollInv]

   Source Collection  : VchSalesColl

   Walk               : All Inventory Entries

   By                 : StkName   : $StockItemName

   AggrCompute        : StkBilled : Sum : $BilledQty

   AggrCompute        : StkActual : Sum : $ActualQty

   AggrCompute        : StkAmt    : Sum : $Amount

Get maximum and minimum stock item purchase value

[Collection: VchPurchaseColl]

   Type     : Vouchers : VoucherType

   Child Of : $$VchTypePurchase

[Function: GetMaxmInValue]

   00: Walk : VchPurchaseColl

   01:   Walk : All Inventory Entries

   02:    …….Code to get maximum & minimum values

   03:   End Walk

   04: End Walk

[Collection : VchPurchaseColl]

   Type     : Vouchers : VoucherType

   Child Of : $$VchTypePurchase

[Collection : VchCollInv]

   Source Collection : VchPurchaseColl

   Walk              : All Inventory Entries

   By                : StkName : $StockItemName

   Aggr Compute      : StkMax  : Max : $Amount

   Aggr Compute      : StkMin  : Min : $Amount

Use Keep Source

When a report is triggered, a collection is constructed in memory. This process takes both memory and time. When summary collections are referred, the source collection is gathered first, and then the summary collection is constructed. Suppose multiple summary collections are used having the same source collection. In that case, the same source collection is still gathered again and again, which becomes redundant. ‘Keep Source’ helps in storing source collection in cache memory and the source can be used more than once. Once the source collection is cached, the same cached data will be used when the collection is used as a source collection in the report. This attribute improves performance by eliminating the time taken in constructing the same source collection repeatedly. The Source collection can be cached at the current owner, owner’s parent and so on (with dots) or at the report level. The decision of where to cache the data depends on the elements in the report that uses the source data.

When to use

  1. When the same source collection is required for more than one summary collection used in the report.
  2. When the source collection data is used in multiple collections.
  3. Use Keep Source : (). (Cached at report level) when source collection is required to be used across parts. or when recursive explodes, use the same collection. (Group explodes to ledgers and groups, where groups again explode to ledgers and groups and so on)

When not to use

  1. When the source collection is used only once in the entire report
  2. When the source collection data is enormous, the system goes out of memory since holding those objects in memory in one shot is not possible.
  3. When source collection data is always low, and even if referred in multiple summary collections, there is minimal to no performance impact.

Scenario

Bad Code

Good Code

Ledger entry and inventory entry details shown in 2 parts

[Collection: TSPLVoucherSrc]

   Type : Voucher

[Collection: TSPLLedgerEntry]

   Source Collection : TSPLVoucherSrc

   Walk              : All Ledger Entries

   By                : Ledger : $LedgerName

     …

[Collection: TSPLLedgerEntry]

   Source Collection : TSPLVoucherSrc

   Walk              : All Inventory Entries

   By                : Stock : $StockItem

    …

[Collection: TSPLVoucherSrc]

   Type : Voucher

[Collection: TSPLLedgerEntry]

   Source Collection : TSPLVoucherSrc

   Walk              : All Ledger Entries

   By                : Ledger : $LedgerName

   Keep Source       : ().

      …

[Collection: TSPLLedgerEntry]

   Source Collection : TSPLVoucherSrc

   Walk              : All Inventory Entries

   By                : Stock : $StockItem

   Keep Source       : ().

      …

Use of Search Key

In a multi-dimension report, it is required to compare values of objects which may belong to the different or same set of the object of collections. Values can be obtained with the help of various Filter functions and attribute Filter at collections. However, this pulls down the performance of the report output. A better way to optimise the report is indexing. These objects can be indexed with the help of ‘Search Key’, which will associate the key specified with each object when the collection is gathered. With the help of $$CollectionFieldByKey, the methods of any specific object can be retrieved via the ‘Key’ specified. This ‘Key’ acts as a foreign key with the attribute ‘Search Key’ at Collection. 

Scenario

Bad Code

Good Code

Retrieve voucher amount for a Ledger for a cost center.

Ledgers are rows and Cost centres are columns

[Collection : LedCC]

    Use          : Voucher Collection

    Walk         : LedgerEntries, CategoryAllocations, CostCentreAllocations

    By           : PartyLedgerName   : $PartyLedgerName

    By           : Cost Centre Name  : $Name

    Aggr Compute : LedCCAmount       : Sum : $Amount

[Field : My Rep Field]

    Set as : $$Filtervalue:$LedCCAmount:LedCC:1:MyFilter

[System: Formula]

     MyFilter : (#LedName = $PartyLedgerName) And (#CCName = $Name)

 

[Collection : LedCC]

   Use         : Voucher Collection

   Walk        : LedgerEntries, CategoryAllocations, CostCentreAllocations

   By           : PartyLedgerName : $PartyLedgerName

   By           : Cost Centre Name: $Name

   Aggr Compute : LedCCAmount     : Sum : $Amount

   Search Key   : $PartyLedgerName + $CostCentreName


[Field : My Rep Field]

   Set as        : $$CollectionFieldByKey:$LedCCAmount:@MySearchKey:LedCC

   Local Formula : MySearchKey : #LedName + #CCName

Use of WalkEx

In scenarios where we require a union of collections wherein source collection is the same, the gathering of source collection might happen once. However, still, the objects of source collection will be walked more than once. With walk ex, each object of source collection is walked only once, along the different walk paths. The attribute ‘Walk Ex’ results in performance improvements drastically.

Scenario

Bad Code

Good Code

Union of Inventory and ledger entries of vouchers

[Collection: TSPLResultColl]

   Collection : TSPlVchLed, TSPLVchInv

[Collection : TSPlVchLed]

   Source Collection : VoucherColl

   Walk         : AllLedgerEntries

   By           : Particulars : $LedgerName

   Aggr Compute : Tot Amount : Sum: $Amount

[Collection : TSPLVchInv]

   Source Collection : VoucherColl

   Walk              : AllInventoryEntries

   By                : Particulars : $StockItemName

   Aggr Compute      : Tot Amount : Sum : $Amount

[Collection: TSPLResultColl]

   Source Collection : VoucherColl

   WalkEx            : TSPLVchInv, TSPlVchLed

[Collection : TSPlVchLed]

   Walk         : AllLedgerEntries

   By           : Particulars : $LedgerName

   Aggr Compute : Tot Amount : Sum: $Amount

[Collection : TSPLVchInv]

   Walk         : AllInventoryEntries

   By           : Particulars : $StockItemName

   Aggr Compute : Tot Amount : Sum: $Amount

TDL Procedural Utilization

User-defined functions or TDL Procedural is a compelling capability of TDL. This artefact empowers TDL developers to execute actions in a defined process. In contrast, the actual flow of events that happens for that action was entirely platform controlled.

When and how to use TDL Procedural?

  1. When a specific interaction flow is required to be attained. E.g., based on particular user input action is performed. So the report is opened and based on inputs, further action is executed.
  2. When certain export and import operations are required
  3. Calling the TDL procedural should be done with CALL action when interaction flow is designed and not with $$.
  4. When certain computations are required, where it is complex to use system formulas. 
  5. As part of interaction flow, specific evaluations can be done like setting a variable, which doesn’t lead to any unstable system state.

When not to use TDL procedural?

  1. When specific values are required from collection or object. Object referencing and Collection capabilities like aggregations, search key, extracting are powerful enough to obtain such value. DO NOT use functions in such cases, as function creates an overhead and slows down the system and impacts the performance.
  2. When computations are possible through system formulas, use system formulas and not functions.

Scenario

Bad Code

Good Code

Getting the closing balance of a StockItem

[Field: TSPLstkCls]

   Set as: $$GetStkClosingBal:#StockItem

[Function:GetStkClosingBal]

   Parameter : pStK: String

   00 : Walk : StkColl

   02 :  DO IF : ##pStK = $Name : Return : $ClosingBalance

   03 : End Walk

[Field: TSPLStkCls]

   Set As: $(StockItem,#StockItem).ClosingBalance

Getting sales voucher values for a ledger for a stockitem

[Field: LedStkAmount]

   Set as :$$GetStkLedAmount:#LedName:#StkName

[Function : GetStkLedAmount]

   ….<Parameters pLed and pStk> <Variable vAmount>

   00: Walk : SalesVchExtract

   02:  Walk : All Ledger Entries

   03:   If : ##pLed = $LedgerName

   04:    Walk : Inventory Allocations

   05:     If : ##pStk = $StockItemName

   06:       Set : vAmount : vAmount + $Amount

   07:     End If

   08:    End Walk

    …..

   100: Return : ##vAmount

[Collction: SalesvchExtract]

   Source Collection : Salesvch

   Walk              : AllLedgerEntries, InventoryAllocations

   By                : Ledger : $LedgerName

   By                : Stock: $StockItem

   Aggr Compute      : LedStkAmount : Sum : $Amount

   Search Key        : $Ledger + $Stock

[Field: LedStkAmount]

   Set as        : $$CollectionFieldByKey:$LedStkAmount:@MySearchKey:SalesVchExtract

   Local Formula : MySearchKey : #LedName + #StkName

Getting Nett assesable value for a ledger based on tax type

[Function: GetAssesablevalue]

   <parameter pledged>

   001 : If : $$IsSysNameEqual:Vat:($TaxType:Ledger:##pledger)

   002:  Return : @ExciseMfgrItemRateVal + @ApportionValueForVat

   003 : Else If : $$IsSysNameEqual:Excise:($TaxType:Ledger:##pledger) OR $$IsSysNameEqual:CENVAT:($TaxType:Ledger:##pledger) OR +
$$IsSysNameEqual:CVD:($TaxType:Ledger:##pledger)

   004 : Return : @ExciseMfgrItemRateVal + @ApportionValueForExcise

   006 : Else

   007 : Return : $$AsAmount:0

   008: ……

[System: Formula]

NettAssVal:If $$IsSysNameEqual:Vat:($TaxType:Ledger:#SVLedger) +

Then (@ExciseMfgrItemRateVal + @ApportionValueForVat) +

ElseIf ($$IsSysNameEqual:Excise:($TaxType:Ledger:#SVLedger) OR $$IsSysNameEqual:CENVAT:($TaxType:Ledger:#SVLedger) OR +
$$IsSysNameEqual:CVD:($TaxType:Ledger:#SVLedger)) Then (@ExciseMfgrItemRateVal + @ApportionValueForExcise)+

Else $$AsAmount:0

Events Utilisation

Events can be system events or report level events. Report events are primarily used to automate certain operations, like creating or updating objects. This capability will not hinder the normal execution of the default product. As per guidelines, any report customisations should be done by writing a new report.

System events like ‘On System Start’ and ‘On Company Load’ are more likely to hinder the product’s default interaction flow. Hence, it is better to refrain from using these events. For more details on Event Framework click here.

Action at Object definition

The action attribute at Object definition has simplified the association of a data object with UI element. Rather than creating various objects a single object with various actions can be defined and associated at the field. This enhances the way the code is written and make the life of a developer simpler. For more details on this topic click here.

TallyHelpwhatsAppbanner
Is this information useful?
YesNo
Helpful?
/* */