Goods Upload (T130)
Register or modify goods and services in the EFRIS system. This endpoint allows taxpayers to maintain their product catalog, including pricing, tax categories, excise duty details, and units of measure. Requires encrypted request and response.
Endpoint Overview
| Property | Value |
|---|---|
| Interface Code | T130 |
| Request Encrypted | ✅ Yes |
| Response Encrypted | ✅ Yes |
| Request Body | Array of goods objects |
| Response Format | Array of goods objects with status |
Flow Description
- Client constructs an array of goods items to add or modify.
- Client specifies
operationType(101=Add,102=Modify). - Client sends encrypted request to server.
- Server validates commodity categories, excise codes, and units of measure against system dictionary (T115).
- Server returns an array of results. Successful items may return empty messages; failed items return
returnCodeandreturnMessage.
📦 Note: Goods must be registered before they can be included in invoices (T109) or stock operations (T131). Ensure
commodityCategoryIdandmeasureUnitmatch the system dictionary.
- PHP
- JavaScript / TypeScript
- Python
try {
// Construct goods payload
$goodsData = [
[
'operationType' => '101', // 101=Add, 102=Modify
'goodsName' => 'Test Product',
'goodsCode' => 'ITEM001',
'measureUnit' => '101', // From T115 rateUnit
'unitPrice' => '1000.00',
'currency' => '101', // From T115 currencyType
'commodityCategoryId' => '10111301',
'haveExciseTax' => '102', // 101=Yes, 102=No
'description' => 'Test product for integration',
'stockPrewarning' => '10',
'havePieceUnit' => '102', // 101=Yes, 102=No
'haveOtherUnit' => '102', // 101=Yes, 102=No
'goodsTypeCode' => '101', // 101=Goods, 102=Fuel
'haveCustomsUnit' => '102' // 101=Yes, 102=No
]
];
// Call T130: Upload Goods
$response = $client->uploadGoods($goodsData);
$results = $response['data']['content'] ?? $response;
if (is_array($results)) {
foreach ($results as $index => $result) {
$code = $result['returnCode'] ?? '00';
$msg = $result['returnMessage'] ?? 'SUCCESS';
if ($code === '00' || empty($code)) {
echo "✅ Goods #{$index} uploaded successfully\n";
} else {
echo "❌ Goods #{$index} failed: {$code} - {$msg}\n";
}
}
}
} catch (\UraEfrisSdk\Exceptions\APIException $e) {
echo "❌ Upload failed: " . $e->getMessage() . "\n";
echo " Return Code: " . $e->getReturnCode() . "\n";
}
try {
// Construct goods payload
const goodsData = [
{
operationType: '101', // 101=Add, 102=Modify
goodsName: 'Test Product',
goodsCode: 'ITEM001',
measureUnit: '101', // From T115 rateUnit
unitPrice: '1000.00',
currency: '101', // From T115 currencyType
commodityCategoryId: '10111301',
haveExciseTax: '102', // 101=Yes, 102=No
description: 'Test product for integration',
stockPrewarning: '10',
havePieceUnit: '102', // 101=Yes, 102=No
haveOtherUnit: '102', // 101=Yes, 102=No
goodsTypeCode: '101', // 101=Goods, 102=Fuel
haveCustomsUnit: '102' // 101=Yes, 102=No
}
];
// Call T130: Upload Goods
const response = await client.uploadGoods(goodsData);
const results = response?.data?.content ?? response;
if (Array.isArray(results)) {
results.forEach((result: any, index: number) => {
const code = result.returnCode ?? '00';
const msg = result.returnMessage ?? 'SUCCESS';
if (code === '00' || !code) {
console.log(`✅ Goods #${index} uploaded successfully`);
} else {
console.error(`❌ Goods #${index} failed: ${code} - ${msg}`);
}
});
}
} catch (error: any) {
console.error(`❌ Upload failed: ${error.message}`);
if (error.returnCode) {
console.error(` Return Code: ${error.returnCode}`);
}
throw error;
}
try:
# Construct goods payload
goods_data = [
{
"operationType": "101", # 101=Add, 102=Modify
"goodsName": "Test Product",
"goodsCode": "ITEM001",
"measureUnit": "101", # From T115 rateUnit
"unitPrice": "1000.00",
"currency": "101", # From T115 currencyType
"commodityCategoryId": "10111301",
"haveExciseTax": "102", # 101=Yes, 102=No
"description": "Test product for integration",
"stockPrewarning": "10",
"havePieceUnit": "102", # 101=Yes, 102=No
"haveOtherUnit": "102", # 101=Yes, 102=No
"goodsTypeCode": "101", # 101=Goods, 102=Fuel
"haveCustomsUnit": "102" # 101=Yes, 102=No
}
]
# Call T130: Upload Goods
response = client.upload_goods(goods_data)
results = response.get("data", {}).get("content", response)
if isinstance(results, list):
for index, result in enumerate(results):
code = result.get("returnCode", "00")
msg = result.get("returnMessage", "SUCCESS")
if code == "00" or not code:
print(f"✅ Goods #{index} uploaded successfully")
else:
print(f"❌ Goods #{index} failed: {code} - {msg}")
except Exception as e:
print(f"❌ Upload failed: {e}")
if hasattr(e, "return_code"):
print(f" Return Code: {e.return_code}")
raise
Request Structure
[
{
"operationType": "101",
"goodsName": "Test Product",
"goodsCode": "ITEM001",
"measureUnit": "101",
"unitPrice": "1000.00",
"currency": "101",
"commodityCategoryId": "10111301",
"haveExciseTax": "102",
"description": "Test product",
"stockPrewarning": "10",
"havePieceUnit": "102",
"pieceMeasureUnit": "",
"pieceUnitPrice": "",
"packageScaledValue": "",
"pieceScaledValue": "",
"exciseDutyCode": "",
"haveOtherUnit": "102",
"goodsTypeCode": "101",
"haveCustomsUnit": "102",
"commodityGoodsExtendEntity": {
"customsMeasureUnit": "",
"customsUnitPrice": "",
"packageScaledValueCustoms": "",
"customsScaledValue": ""
},
"customsUnitList": [],
"goodsOtherUnits": []
}
]
Response Structure
[
{
"operationType": "101",
"goodsName": "Test Product",
"goodsCode": "ITEM001",
"measureUnit": "101",
"unitPrice": "1000.00",
"currency": "101",
"commodityCategoryId": "10111301",
"haveExciseTax": "102",
"description": "Test product",
"stockPrewarning": "10",
"havePieceUnit": "102",
"haveOtherUnit": "102",
"goodsTypeCode": "101",
"haveCustomsUnit": "102",
"returnCode": "00",
"returnMessage": "SUCCESS"
}
]
Field Descriptions
Basic Goods Information
| Field | Required | Type | Description |
|---|---|---|---|
operationType | ❌ No | String (3) | 101=Add goods (default), 102=Modify product |
goodsName | ✅ Yes | String (200) | Product name. Cannot be empty. |
goodsCode | ✅ Yes | String (50) | Product code. Cannot be empty. Must be unique. |
measureUnit | ✅ Yes | String (3) | Unit of measure from T115 rateUnit (e.g., 101=per stick) |
unitPrice | ✅ Yes | Number | Unit price. Integer ≤12 digits, Decimal ≤8 digits. Can be empty for services. |
currency | ✅ Yes | String (3) | Currency from T115 currencyType (e.g., 101=UGX) |
commodityCategoryId | ✅ Yes | String (18) | Commodity category ID from T115/T123. Must be a leaf node. |
haveExciseTax | ✅ Yes | String (3) | 101=Yes, 102=No |
description | ❌ No | String (1024) | Product description |
stockPrewarning | ✅ Yes | Number | Stock warning threshold. Integer ≤12 digits. Can be zero. Empty for services. |
goodsTypeCode | ❌ No | String (3) | 101=Goods (default), 102=Fuel |
Piece Unit Configuration (If havePieceUnit = 101)
| Field | Required | Type | Description |
|---|---|---|---|
havePieceUnit | ✅ Yes | String (3) | 101=Yes, 102=No. If haveExciseTax=101 and excise has unit, must be 101. |
pieceMeasureUnit | ⚠️ Conditional | String (3) | Required if havePieceUnit=101. From T115 rateUnit. |
pieceUnitPrice | ⚠️ Conditional | Number | Required if havePieceUnit=101. Integer ≤12, Decimal ≤8. |
packageScaledValue | ⚠️ Conditional | Number | Required if havePieceUnit=101. If measureUnit = excise unit, must be 1. |
pieceScaledValue | ⚠️ Conditional | Number | Required if havePieceUnit=101. If measureUnit = excise unit, must be 1. |
Excise Duty Configuration (If haveExciseTax = 101)
| Field | Required | Type | Description |
|---|---|---|---|
exciseDutyCode | ⚠️ Conditional | String (20) | Required if haveExciseTax=101. Must be valid leaf node from T125. |
Other Unit Configuration (If haveOtherUnit = 101)
| Field | Required | Type | Description |
|---|---|---|---|
haveOtherUnit | ❌ No | String (3) | 101=Yes, 102=No. If havePieceUnit=102, must be 102. |
goodsOtherUnits | ⚠️ Conditional | Array | Required if haveOtherUnit=101. Cannot contain duplicate units. |
goodsOtherUnits Array Fields
| Field | Required | Type | Description |
|---|---|---|---|
otherUnit | ✅ Yes | String (3) | From T115 rateUnit. Cannot equal measureUnit or pieceMeasureUnit. |
otherPrice | ❌ No | Number | Price for this unit. |
otherScaled | ✅ Yes | Number | Conversion scale. Integer ≤12, Decimal ≤8. |
packageScaled | ✅ Yes | Number | Package scale. Integer ≤12, Decimal ≤8. |
Customs Unit Configuration (If haveCustomsUnit = 101)
| Field | Required | Type | Description |
|---|---|---|---|
haveCustomsUnit | ✅ Yes | String (3) | 101=Yes, 102=No |
commodityGoodsExtendEntity | ⚠️ Conditional | Object | Required if haveCustomsUnit=101. |
customsUnitList | ⚠️ Conditional | Array | Required if haveCustomsUnit=101. |
commodityGoodsExtendEntity Fields
| Field | Required | Type | Description |
|---|---|---|---|
customsMeasureUnit | ✅ Yes | String (3) | From T115 exportRateUnit. |
customsUnitPrice | ✅ Yes | Number | Integer ≤12, Decimal ≤8. |
packageScaledValueCustoms | ✅ Yes | Number | Integer ≤12, Decimal ≤8. |
customsScaledValue | ✅ Yes | Number | Integer ≤12, Decimal ≤8. |
Return Codes (T130 Specific)
| Code | Message | Description |
|---|---|---|
00 | SUCCESS | Goods uploaded successfully |
99 | Unknown error | Generic server error |
600 | GoodsCode cannot be empty | Missing goods code |
601 | GoodsCode cannot be greater than 50! | Goods code too long |
602 | goodsCode already exists | Duplicate goods code (for Add operation) |
603 | GoodsName cannot be empty | Missing goods name |
605 | Measureunit cannot be empty | Missing unit of measure |
606 | measureUnit:Invalid field value | Unit not found in dictionary |
614 | commodityCategoryId:Cannot be empty! | Missing category ID |
617 | commodityCategoryId: commodity category has been deleted! | Invalid category |
619 | commodityCategoryId: commodity category is not a leaf node! | Must select lowest level category |
620 | haveExciseTax:Cannot be empty | Missing excise flag |
670 | haveExciseTax is '102', exciseDutyCode must be empty! | Conflict between flags |
671 | exciseDutyCode: haveExciseTax is '101', exciseDutyCode cannot be empty! | Missing excise code |
682 | This product is a 'service' product, cannot operate inventory! | Service items cannot have stock |
684 | product does not exist! | Product not found (for Modify operation) |
3341 | If 'haveCustomsUnit' is '101', commodityGoodsExtendEntity... cannot be empty! | Missing customs entity |
3342 | If 'haveCustomsUnit' is '101', please use the measurement unit accepted by customs... | Invalid customs unit |
💡 Tip: Always fetch the latest system dictionary (T115) before uploading goods to ensure
measureUnit,currency, andcommodityCategoryIdvalues are valid.
Common Use Cases
-
Product Catalog Setup
Register all sellable items before issuing invoices. Required for stock-managed goods. -
Excise Duty Configuration
Configure excisable products (e.g., alcohol, fuel) with correct duty codes and units. -
Multi-Unit Pricing
Define secondary units (e.g., sell by piece vs. by box) usinggoodsOtherUnits. -
Export Goods Setup
Configure customs units of measure for export invoices usinghaveCustomsUnit. -
Price Updates
UseoperationType=102to modify unit prices or stock warnings for existing goods.