Introduction


SuiteTax, which will soon become NetSuite’s default tax framework, presents challenges for SuiteApp developers in ensuring compatibility for both Legacy Tax and SuiteTax customers. For example, in Legacy Tax account, tax rates are retrievable from the item sublist in a Sales Order record, whereas they are not retrievable in SuiteTax account. This article aims to address these challenges by focusing on two critical aspects: triggering tax calculation and obtaining tax details in transactions. 

What is the difference?

To understand these tax frameworks, I have listed a couple of significant differences between both of them.

SuiteTax:

•    SuiteTax is a new framework in NetSuite designed to offer greater flexibility to accommodate specific country tax requirements.
•    It utilizes tax engines provided by various providers, including NetSuite itself, so the tax calculation logic may vary for each SuiteTax customer.

Legacy Tax:

•    Legacy Tax serves as the current default tax framework in NetSuite, featuring a simpler tax calculation mechanism compared to SuiteTax.
•    Some Legacy tax fields and records are no longer available in a SuiteTax account. (Refer to the Migration of Legacy Records and Transactions to SuiteTax in the help center.)

How are both frameworks supported?

It is strongly advised to incorporate support for both frameworks within your single SuiteApp. Anticipating existing customers to migrate from Legacy Tax to SuiteTax, a unified approach ensures seamless continuity. Furthermore, dividing your SuiteApp for each framework would inevitably introduce significant maintenance complexities, potentially leading to a nightmare when managing multiple SuiteApps.

To seamlessly support both frameworks into your single SuiteApp, it is essential to implement distinct logic for each framework. The following SuiteScript snippet demonstrates how to programmatically determine whether the runtime environment is SuiteTax.

var isSuiteTax = runtime.isFeatureInEffect({
    feature: "SUITETAXENGINE"
});
log.debug({title: 'SUITETAXENGINE', details: isSuiteTax});

Case Study: Triggering Tax Calculation


Some partners who support only Legacy Tax customers may rely on their own tax calculation logic. However, it is difficult to calculate taxes the same way as the SuiteTax engine.

SuiteTax

The only way to retrieve calculated tax on a transaction in a SuiteTax account is by using a Restlet that invokes the calculateTax macro. There’s no necessity to invoke the record.save method, as it’s specifically for retrieving the calculated taxes. After invoking the macro, you can retrieve each tax detail by reading the “taxdetails“ sublist. Please note that the “taxdetails“ sublist is exposed only in a SuiteTax account.

// Use SuiteScript 2.0 (ES5.1) to invoke the calculateTax macro, as the macro is not supported in SuiteScript 2.1 as of version 2024.1.
var salesOrder = record.create({
    type: record.Type.SALES_ORDER,
    isDynamic: true,
    defaultValues: {
        entity: <entity-id>
    }
});

salesOrder.selectNewLine({
    sublistId: 'item'
});

salesOrder.setCurrentSublistValue({
    sublistId: 'item',
    fieldId: 'item',
    value: <item-id>
});

salesOrder.setCurrentSublistValue({
    sublistId: 'item',
    fieldId: 'quantity',
    value: <quantity>
});

salesOrder.commitLine({
    sublistId: 'item'
});

// Get macros available on the record.
var macros = salesOrder.getMacros();

// Execute the macro.
if ('calculateTax' in macros) {
    macros.calculateTax();
}

// Retrieve tax total.
var taxTotal = salesOrder.getValue({
    fieldId: 'taxtotal'
});
log.debug({title: 'taxtotal', details: taxTotal});

// Retrieve tax details in the “taxdetails“ sublist.
var lineCount = salesOrder.getLineCount({
    sublistId: 'taxdetails'
});
for (var line = 0; line < lineCount; line++) {
    var taxAmount = salesOrder.getSublistValue({
        sublistId: 'taxdetails',
        fieldId: 'taxamount',
        line: line
    });
    log.debug({title: 'taxamount', details: taxAmount});
}


Keep in mind that as of version 2024.1, the calculateTax macro supports only SuiteScript 2.0. You don’t need to convert all your scripts to 2.0. Just implement only the Restlet in SuiteScript 2.0 that invokes the macro to retrieve calculated taxes.

Legacy Tax

If you support Legacy Tax customers, you can continue to use the existing logic for a Legacy Tax account. If you aim to integrate your Legacy Tax account calculation logic into the above Restlet, please refer to the following SuiteScript snippet. Upon setting entity, item, and quantity, taxes will be computed automatically. Subsequently, you can retrieve each tax detail by reading the “item“ sublist.

// Use SuiteScript 2.0 (ES5.1) if you aim to integrate Legacy Tax account logic into the Restlet above to invoke the calculateTax macro.
var salesOrder = record.create({
    type: record.Type.SALES_ORDER,
    isDynamic: true,
    defaultValues: {
        entity: <entity-id>
    }
});

salesOrder.selectNewLine({
    sublistId: 'item'
});

salesOrder.setCurrentSublistValue({
    sublistId: 'item',
    fieldId: 'item',
    value: <item-id>
});

salesOrder.setCurrentSublistValue({
    sublistId: 'item',
    fieldId: 'quantity',
    value: <quantity>
});

salesOrder.commitLine({
    sublistId: 'item'
});

// Retrieve tax total.
var taxTotal = salesOrder.getValue({ 
    fieldId: 'taxtotal'
});
log.debug({title: 'taxtotal', details: taxTotal});

// Retrieve tax details in the “item“ sublist.
var lineCount = salesOrder.getLineCount({ 
    sublistId: 'item' 
});
for (var line = 0; line < lineCount; line++) {
    var taxRate = salesOrder.getSublistValue({ 
        sublistId: 'item',
        fieldId: ' taxrate1', 
        line: line
    });
    log.debug({title: 'taxrate1', details: taxRate});
}

Case Study: Obtaining Tax Details in Transactions

While REST Web Services, REST API, SuiteQL, SuiteAnalytics, and N/query module support SuiteTax fields and records, they do not support Legacy Tax. To address this challenge, you need to determine whether the runtime environment is SuiteTax and retrieve tax details accordingly.

SuiteTax

•    Utilize StuiteTalk REST Web Services, SuiteQL, SuiteAnalytics, N/query, N/search, or N/record module in a SuiteTax.
•    When retrieving tax details in a Transaction record object, retrieve each tax detail by reading the “taxdetails“ sublist.

Legacy Tax

•    Utilize N/search or N/record module in a Legacy Tax account.
•    When retrieving tax details in a Transaction record object, retrieve each tax detail by reading the “item“ sublist.
•    If you support Legacy Tax customers, you can continue using the existing logic for a Legacy Tax account.

Conclusion

Integrating support for both Legacy Tax and SuiteTax customers in one SuiteApp is essential. By incorporating tailored logic for each framework and leveraging appropriate APIs, developers can ensure efficient tax calculation and retrieval across frameworks, thereby ensuring efficient management of your SuiteApp.