Update Inventory on Goods Receipt
This script processes a Goods Receipt record, reads all of its Goods Receipt Lines, creates related Inventory Transaction records under the referenced inventory items, and then marks the source Goods Receipt as Posted.
Use Case
Use this when you want a workflow trigger to post a goods receipt into inventory transactions, for example:
- Create stock movement records from received goods
- Convert each Goods Receipt Line into an Inventory Transaction
- Prevent double-posting by marking the source receipt as
Posted
What It Does
On trigger, the script will:
- Read the triggering Goods Receipt record from
input.recordId - Check whether the receipt has already been posted
- Load all child records of type Goods Receipt Lines
- Validate each line's inventory reference and quantity
- Create an Inventory Transaction record under the referenced inventory item
- Mark the Goods Receipt as
Posted - Return counts and any line-level errors
Script
const CONFIG = {
source: {
lineType: "Goods Receipt Lines",
fields: {
processed: "Inventory Status",
inventoryRef: "Item Ref",
quantity: "Received Quantity",
},
},
target: {
type: "Inventory Transaction",
fields: {
transactionType: "Transaction Type",
quantity: "Quantity",
date: "Date",
unitPrice: "Unit Price",
},
defaults: {
transactionType: "Stock Out",
unitPrice: 0,
},
},
};
if (!input.recordId) {
throw new Error("No Goods Receipt record ID provided.");
}
const sourceRecord = await base.getRecordById(input.recordId);
if (!sourceRecord) {
throw new Error(`Goods Receipt not found: ${input.recordId}`);
}
if (sourceRecord.cellValues[CONFIG.source.fields.processed] === 'Posted') {
log("Inventory already updated. Exiting.");
output.set("createdCount", 0);
return;
}
const lines = await base.getChildren(sourceRecord.id, {
type: CONFIG.source.lineType,
});
let createdCount = 0;
const errors = [];
for (const line of lines) {
await base.yield();
const inventoryRef = line.cellValues[CONFIG.source.fields.inventoryRef];
const qtyRaw = line.cellValues[CONFIG.source.fields.quantity];
const qty = Number(qtyRaw || 0);
const inventoryItemId = inventoryRef?.adoid;
if (!inventoryItemId) {
errors.push(`Line ${line.id}: missing Inventory Item reference`);
continue;
}
if (!qty || qty <= 0) {
errors.push(`Line ${line.id}: invalid Quantity "${qtyRaw}"`);
continue;
}
await base.createRecord({
name: `SO ${sourceRecord.name} - ${line.name || line.id}`,
parentid: inventoryItemId,
typename: CONFIG.target.type,
cellValues: {
[CONFIG.target.fields.transactionType]: CONFIG.target.defaults.transactionType,
[CONFIG.target.fields.quantity]: qty,
[CONFIG.target.fields.date]: Math.floor(Date.now() / 1000),
[CONFIG.target.fields.unitPrice]: CONFIG.target.defaults.unitPrice,
},
});
createdCount += 1;
}
await base.updateRecord({
adoid: sourceRecord.id,
cellValues: {
[CONFIG.source.fields.processed]: 'Posted',
},
});
output.set("createdCount", createdCount);
output.set("errorCount", errors.length);
output.set("errors", errors);
log(`Created ${createdCount} stock transactions.`);
if (errors.length) {
log(`Errors: ${JSON.stringify(errors)}`);
}
Configuration
| Section | Key | Description | Example |
|---|---|---|---|
| source | lineType | Child record type to process | Goods Receipt Lines |
| source.fields | processed | Status field | Inventory Status |
| source.fields | inventoryRef | Inventory reference field | Item Ref |
| source.fields | quantity | Quantity field | Received Quantity |
| target | type | Target record type | Inventory Transaction |
Inputs
| Input | Required | Description |
|---|---|---|
| recordId | Yes | Goods Receipt record ID |
Outputs
| Output | Description |
|---|---|
| createdCount | Number of records created |
| errorCount | Number of errors |
| errors | Error messages |
Notes
- Skips invalid lines
- Prevents duplicate posting