Build an asset or inventory request form
Track stock requests through approval — Northwind teammates ask, Awadesh approves, the warehouse ships.
Published Aug 30, 2025
When the Northwind studio runs out of camera batteries on a Friday afternoon, the fix is rarely the hard part — the hard part is knowing about it before the shoot on Monday. A shared spreadsheet works for a week, then someone forgets the columns, someone else overwrites a row, and the approver only hears about a request when it has already gone wrong.
This script turns a small Google Form into a proper request queue. Every
submission gets a short ID, lands in a tracking sheet as pending, and pings
the approver by email — so nothing falls through the gap between “I asked” and
“the warehouse shipped it”.
What you’ll need
- A Google Form with three short-answer questions named exactly
Item,QuantityandRequester. Field names are case-sensitive — they have to match thee.namedValueskeys in the script. - A Google Sheet linked to the form (or any sheet you want to use as the
tracker) with the columns
ID,Requester,Item,Quantity,Status,Submittedin row 1. - An approver email — here, Awadesh — with permission to read the sheet.
The script
// The sheet that stores the request queue. Form-linked sheets work; a
// separate tracker works too, as long as the header row matches.
const REQUESTS_SHEET_ID = '1abcAssetRequestsId';
// Where the approval ping goes. Change this if the approver moves on.
const APPROVER_EMAIL = '[email protected]';
// Default status for a new request. Approvals flip it to "approved" or
// "declined" later, by hand or by a second script.
const DEFAULT_STATUS = 'pending';
/**
* Runs on every form submission. Reads the answers, writes a row to the
* tracker with a short ID, and emails the approver so the request does
* not sit unread in the sheet.
*
* @param {GoogleAppsScript.Events.FormsOnFormSubmit} e Form submit event.
*/
function onFormSubmit(e) {
// 1. A short, unguessable ID. Used in the email subject and the sheet
// so the approver can reply with "approve <id>" later.
const id = Utilities.getUuid().slice(0, 8);
// 2. Pull the three answers out of namedValues. Forms always returns
// arrays — even single-answer questions — so index [0] is correct.
const item = (e.namedValues.Item && e.namedValues.Item[0]) || '';
const qtyRaw = (e.namedValues.Quantity && e.namedValues.Quantity[0]) || '';
const requester = (e.namedValues.Requester && e.namedValues.Requester[0]) || '';
// 3. Parse the quantity defensively. Forms hands strings back even for
// number fields, and a missing answer should not crash the trigger.
const qty = parseInt(qtyRaw, 10) || 0;
if (!item || !requester || qty <= 0) {
Logger.log('Skipping malformed submission: ' + JSON.stringify(e.namedValues));
return;
}
// 4. Append the row to the tracker. Order has to match the header row.
SpreadsheetApp.openById(REQUESTS_SHEET_ID).getSheets()[0]
.appendRow([id, requester, item, qty, DEFAULT_STATUS, new Date()]);
// 5. Ping the approver. Plain text keeps it readable on mobile Gmail.
GmailApp.sendEmail(
APPROVER_EMAIL,
`Asset request: ${qty} × ${item}`,
`Requested by ${requester}.\nID: ${id}\nStatus: ${DEFAULT_STATUS}\n\n` +
`Open the tracker to approve or decline.`
);
}
How it works
onFormSubmitfires on every new submission, receiving the form eventewith answers grouped undere.namedValues.- It mints a short ID with
Utilities.getUuid().slice(0, 8)— long enough to be unique across a studio’s worth of requests, short enough to paste into an email subject. - It reads the three answers, falling back to empty strings if a field is missing so the script never throws on a partial submission.
- It parses the quantity as an integer and bails out if any required field is blank or the quantity is zero. Bad data stays out of the sheet.
- It appends a row to the tracker in the exact order of the header row: ID, requester, item, quantity, status, submitted timestamp.
- It emails the approver with the quantity and item in the subject line — so the request is visible from the inbox list, no opening required.
Example run
A teammate submits the form with:
| Field | Value |
|---|---|
| Item | Camera batteries (NP-FZ100) |
| Quantity | 4 |
| Requester | Priya Shah |
The tracker gains a new row:
| ID | Requester | Item | Quantity | Status | Submitted |
|---|---|---|---|---|---|
| 7f3a9c2e | Priya Shah | Camera batteries (NP-FZ100) | 4 | pending | 2026-05-27 10:14 |
And Awadesh receives an email with the subject Asset request: 4 × Camera batteries (NP-FZ100) and the ID in the body, ready to approve.
Trigger it
This script runs from a form submission, not a clock:
- Open the Apps Script project bound to the form (Extensions -> Apps Script from the form editor).
- In Triggers, add a new trigger for
onFormSubmit, event source From form, event type On form submit. - Approve the authorisation prompt — the script needs Sheets and Gmail scopes the first time it runs.
Submit a test response yourself before announcing the form to the team.
Watch out for
- Field names are case-sensitive. If you rename
QuantitytoHow many?, the script silently writes zero — guard against this by keeping the question titles in sync with thenamedValueskeys, or rename the keys to match. Utilities.getUuid()produces a long ID; slicing to eight characters trades a tiny collision risk for readability. For a small studio that is fine; for thousands of requests a year, keep the full UUID.- The status column is plain text. If the approver flips a row to
approved, a downstream script can pick it up — see the warehouse notification idea in the forms cluster for the second half of the workflow. - Gmail has a daily send quota. One email per submission is well under the limit; if a campaign drives hundreds of requests in a day, batch the approver email into a digest instead.
Related
Trigger an onboarding sequence on form submit
Kick off tasks when a new Northwind hire submits their starter form.
Updated Oct 17, 2025
Build a content-submission queue
Collect Northwind guest posts or ideas for review through a Form.
Updated Oct 9, 2025
Score sentiment in open-text feedback
Rate Northwind feedback comments without manual review — using the in-Sheet sentiment function.
Updated Oct 5, 2025
Build a peer-nomination and voting system
Collect and tally Northwind nominations for awards or initiatives — one ballot, anonymous.
Updated Oct 1, 2025
Roll a form over each cycle
Archive old responses and reset for the next Northwind cycle — quarterly OKR check-ins.
Updated Sep 27, 2025