Skip to main content

Query Commodity Category / Excise Duty by Issue Date (T146)

Retrieve commodity category or excise duty details as they were effective on a specific historical date. This endpoint supports audit trails and historical tax rate validation by returning the configuration valid at the time of issueDate. Request and response are encrypted.


Endpoint Overview​

PropertyValue
Interface CodeT146
Request Encryptedβœ… Yes
Response Encryptedβœ… Yes
Request Body{ "categoryCode": "...", "type": "1", "issueDate": "..." }
Response FormatJSON Object

Flow Description​

  1. Client submits categoryCode (commodity or excise code), type (1=Category, 2=Excise), and issueDate.
  2. Server validates the code exists and retrieves the version effective on the specified date.
  3. Server returns configuration details including tax rates, exemption flags, and validity periods.
  4. Client uses response to validate historical transactions or reconstruct past tax calculations.

πŸ’‘ Tip: Use T146 when auditing past invoices to ensure the tax rates and commodity categories applied match the rules effective on the invoice date, not current rules.


try {
// Call T146: Query Commodity/Excise by Issue Date
$response = $client->queryCommodityByDate(
categoryCode: '13101501', // Commodity or Excise code
type: '1', // 1=Commodity Category, 2=Excise Duty
issueDate: '2025-02-19 10:02:02' // Historical date
);

$content = $response['data']['content'] ?? $response;

// Check Commodity Category response
if (isset($content['commodityCategory'])) {
$cat = $content['commodityCategory'];
echo "βœ… Commodity Category: {$cat['commodityCategoryName']}\n";
echo " Code: {$cat['commodityCategoryCode']}\n";
echo " Rate: {$cat['rate']} ({$cat['isZeroRate']} zero-rated, {$cat['isExempt']} exempt)\n";
echo " Excisable: {$cat['excisable']}\n";
}

// Check Excise Duty response
if (isset($content['exciseDuty'])) {
$excise = $content['exciseDuty'];
echo "βœ… Excise Duty: {$excise['goodService']}\n";
echo " Code: {$excise['exciseDutyCode']}\n";
echo " Rate Text: {$excise['rateText']}\n";

if (!empty($excise['exciseDutyDetailsList'])) {
foreach ($excise['exciseDutyDetailsList'] as $detail) {
echo " Detail: Type {$detail['type']}, Rate {$detail['rate']}\n";
}
}
}

} catch (\UraEfrisSdk\Exceptions\APIException $e) {
echo "❌ Query failed: " . $e->getMessage() . "\n";
echo " Return Code: " . $e->getReturnCode() . "\n";
}

Request Structure​

{
"data": {
"content": "BASE64_ENCRYPTED_PAYLOAD",
"signature": "JKQWJK34K32JJEK2JQWJ5678",
"dataDescription": {
"codeType": "1",
"encryptCode": "2",
"zipCode": "0"
}
},
"globalInfo": {
"interfaceCode": "T146",
"appId": "AP04",
"version": "1.1.20191201",
"tin": "1000029771",
"deviceNo": "TCS9e0df01728335239",
"taxpayerID": "1"
}
}

Request Fields (Encrypted Payload)​

FieldRequiredTypeLengthDescription
categoryCodeβœ… YesString≀20Commodity category code (if type=1) or Excise duty code (if type=2)
typeβœ… YesString (1)11=Commodity Category, 2=Excise Duty
issueDateβœ… YesString20Historical date to query (format: yyyy-MM-dd HH:mm:ss)

πŸ’‘ Tip: The issueDate determines which version of the commodity category or excise duty configuration is returned. Use this to validate historical transactions.


Response Structure​

{
"data": {
"content": {
"commodityCategory": {
"commodityCategoryCode": "100000000",
"parentCode": "0",
"commodityCategoryName": "Standard",
"commodityCategoryLevel": "1",
"rate": "0.18",
"isLeafNode": "101",
"serviceMark": "101",
"isZeroRate": "101",
"zeroRateStartDate": "19/02/2025",
"zeroRateEndDate": "28/02/2025",
"isExempt": "101",
"exemptRateStartDate": "19/02/2025",
"exemptRateEndDate": "28/02/2025",
"enableStatusCode": "1",
"exclusion": "1",
"excisable": "101",
"vatOutScopeCode": "102"
},
"exciseDuty": {
"effectiveDate": "19/02/2025",
"exciseDutyCode": "LED010300",
"goodService": "Un-denatured spirits m",
"id": "320389606969887681",
"isLeafNode": "1",
"parentCode": "LED010000",
"rateText": "60%,UGX2000 per Kg",
"exciseDutyDetailsList": [
{
"currency": "101",
"exciseDutyId": "320389606969887681",
"rate": "2000",
"type": "102",
"unit": "103"
}
]
}
}
},
"globalInfo": {
"interfaceCode": "T146",
"returnStateInfo": {
"returnCode": "00",
"returnMessage": "SUCCESS"
}
}
}

Response Fields​

Commodity Category Object (When type=1)​

FieldRequiredTypeLengthDescription
commodityCategoryCodeβœ… YesString≀18Commodity category identifier
parentCodeβœ… YesString≀18Parent category code (0 for root)
commodityCategoryNameβœ… YesString≀200Category name
commodityCategoryLevelβœ… YesNumber1Hierarchy level (1=root)
rateβœ… YesString≀4Tax rate (e.g., 0.18 for 18%)
isLeafNodeβœ… YesString (1)1101=Yes (leaf), 102=No (has children)
serviceMarkβœ… YesString (1)1101=Service, 102=Goods
isZeroRateβœ… YesString (1)1101=Yes (zero-rated), 102=No
zeroRateStartDate❌ NoDate-Zero-rate validity start (dd/MM/yyyy)
zeroRateEndDate❌ NoDate-Zero-rate validity end (dd/MM/yyyy)
isExemptβœ… YesString (1)1101=Yes (exempt), 102=No
exemptRateStartDate❌ NoDate-Exemption validity start (dd/MM/yyyy)
exemptRateEndDate❌ NoDate-Exemption validity end (dd/MM/yyyy)
enableStatusCodeβœ… YesString (1)11=Enabled, 0=Disabled
exclusionβœ… YesString (1)10=Zero, 1=Exempt, 2=No exclusion, 3=Both
excisableβœ… YesString (3)3101=Yes (excisable), 102=No
vatOutScopeCodeβœ… YesString (3)3101=Yes (out of scope), 102=No

Excise Duty Object (When type=2)​

FieldRequiredTypeLengthDescription
idβœ… YesString≀20Excise duty record ID
exciseDutyCodeβœ… YesString≀20Excise duty identifier
goodServiceβœ… YesString≀500Description of goods/service
parentCodeβœ… YesString≀20Parent excise category code
rateTextβœ… YesString≀50Human-readable rate description
isLeafNodeβœ… YesString (1)11=Yes, 0=No
effectiveDateβœ… YesDate-Effective date (dd/MM/yyyy)
exciseDutyDetailsList❌ NoArray-List of rate calculation details

Excise Duty Details Array Items​

FieldRequiredTypeDescription
currencyβœ… YesString (3)Currency code from T115
exciseDutyIdβœ… YesString (18)Excise duty ID
rateβœ… YesNumberTax rate value
typeβœ… YesString (10)101=Percentage, 102=Unit of measurement
unit❌ NoString (3)Unit code from T115 (required if type=102)

Return Codes​

CodeMessageDescription
00SUCCESSQuery completed successfully
99Unknown errorGeneric server error
400Device does not existdeviceNo not registered for this TIN
402Device key expiredDevice credentials expired; re-run T102
403Device status is abnormalDevice blocked or suspended
2860type: cannot be empty!Missing type parameter
2861type: Byte length cannot be greater than 1!Invalid type length
2862type: Invalid field value!type must be 1 or 2
2863categoryCode: cannot be empty!Missing categoryCode
2864categoryCode: Byte length cannot be greater than 1!Invalid code length (note: docs say 20, error says 1β€”validate both)
2865issuedDate: cannot be empty!Missing issueDate
2866issuedDate: The time format must be yyyy-MM-dd HH:mm:ssInvalid date format
2867categoryCode: does not exist!Provided code not found in system

πŸ’‘ Tip: If the categoryCode did not exist on the specified issueDate, the server may return an empty object or error 2867. Always validate the response content exists.


Common Use Cases​

  1. Historical Tax Audit
    Verify the tax rate and category configuration that was effective on a past invoice date during audits.

  2. Transaction Reconstruction
    Rebuild historical tax calculations using the exact rates and rules active at the time of transaction.

  3. Rate Change Validation
    Confirm when a commodity category changed from zero-rated to standard-rated (or vice versa) by querying multiple dates.

  4. Excise Duty Verification
    Validate excise duty rates and calculation types (percentage vs. unit-based) for historical excisable goods.

  5. Compliance Reporting
    Generate reports showing tax treatment changes over time for specific commodity categories.

  6. Dispute Resolution
    Provide evidence of applicable tax rules on a specific date when disputing tax assessments.


Integration Checklist​

βœ… Validate type is either 1 (Category) or 2 (Excise) before calling
βœ… Ensure issueDate format is yyyy-MM-dd HH:mm:ss
βœ… Verify categoryCode exists via T123/T125 before querying historical data
βœ… Handle cases where category/excise did not exist on the specified date
βœ… Parse commodityCategory vs exciseDuty response objects based on type
βœ… Check enableStatusCode to confirm category was active on the query date
βœ… Validate zeroRateStartDate/EndDate and exemptRateStartDate/EndDate for temporal validity
βœ… Cache historical query results to reduce API calls for repeated audit checks
βœ… Log issueDate and categoryCode queries for audit trail compliance
βœ… Cross-reference rate with T115 dictionary for tax rate code validation