Skip to main content

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:

  1. Read the triggering Goods Receipt record from input.recordId
  2. Check whether the receipt has already been posted
  3. Load all child records of type Goods Receipt Lines
  4. Validate each line's inventory reference and quantity
  5. Create an Inventory Transaction record under the referenced inventory item
  6. Mark the Goods Receipt as Posted
  7. 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

SectionKeyDescriptionExample
sourcelineTypeChild record type to processGoods Receipt Lines
source.fieldsprocessedStatus fieldInventory Status
source.fieldsinventoryRefInventory reference fieldItem Ref
source.fieldsquantityQuantity fieldReceived Quantity
targettypeTarget record typeInventory Transaction

Inputs

InputRequiredDescription
recordIdYesGoods Receipt record ID

Outputs

OutputDescription
createdCountNumber of records created
errorCountNumber of errors
errorsError messages

Notes

  • Skips invalid lines
  • Prevents duplicate posting