The Business Situation

Larkspur Storage Goods is a fictional 42-person online retailer selling home-organization products from one warehouse. The business carries approximately 1,800 active Shopify variants and processes about 3,000 orders per month, averaging 1.8 line items per order.

The supply chain team includes an operations manager, an inventory control lead, four warehouse associates, and a quality analyst. A customer-service lead handles order exceptions that may affect delivery commitments.

Shopify is the commercial inventory system. Google Workspace is used for spreadsheets, forms, email, and shared files. Slack is the primary internal notification channel.

Inventory counting had previously depended on two irregular full-warehouse counts each year. Staff also performed occasional spot checks when a picker found an empty bin or a customer reported an incorrect shipment. Results were recorded in separate spreadsheets, Slack messages, or handwritten notes.

The business wanted to replace this reactive process with approximately 90 scheduled cycle counts per week. Items would be selected according to value, transaction volume, count history, open exceptions, and operational risk. Any difference between the system quantity and physical quantity would require an independent recount, a reason, approval, and a recorded inventory correction.

Note: This case study is provided as a representative example of the types of AI integration and digital transformation solutions Intelligex designs and delivers. Actual engagements are tailored to each client’s goals, constraints, existing systems, timeline, and available resources, so the approach, tools, and outcomes may vary.

The Existing Process

The original counting process followed a loose sequence:

  1. The inventory control lead exported a Shopify inventory report when a full count was planned.
  2. Warehouse associates counted every accessible bin, usually while normal picking continued.
  3. Counts were typed into a spreadsheet after the physical work was complete.
  4. The inventory control lead compared the spreadsheet with the earlier Shopify export.
  5. Large differences were sometimes recounted, but there was no consistent threshold.
  6. Potential causes were discussed in Slack or email.
  7. An authorized employee adjusted Shopify inventory.
  8. The count spreadsheet was filed in a shared folder without structured follow-up.
Problems in the original warehouse counting process
Current problem Practical business effect
System quantities were exported hours before counting. Orders and fulfilments during the count created timing differences that looked like inventory errors.
Counts were not assigned by value or risk. Low-impact products could consume the same attention as high-value or frequently miscounted items.
First counts and recounts were stored together. A second counter could see the first result, weakening the independence of the recount.
Variance reasons were optional free text. Recurring issues such as mis-binned stock or unprocessed returns could not be measured consistently.
Shopify events were disconnected from count records. Operations could not easily distinguish a physical variance from an order, cancellation, or fulfilment timing issue.
Approvals took place in chat. The final spreadsheet did not reliably show who approved an adjustment or what evidence was reviewed.
There was no defined task owner. Overdue recounts and unresolved investigations depended on the inventory lead remembering to follow up.
Historical files used inconsistent formats. Quality staff could not report recurring variances by SKU, bin, reason, counter, or product class.
The process depended heavily on one employee. Counting slowed when the inventory lead was absent and operating knowledge was difficult to transfer.

The main business requirement was not simply to count more often. Larkspur needed controlled evidence showing what was counted, which Shopify quantity was used, whether stock moved during the count, who performed the recount, why the difference occurred, and who authorized the correction.

What the New System Needed to Do

Business and technical requirements
Area Requirement
Scheduling Select approximately 90 item-location records each week using ABC class, movement, time since last count, prior variance, and open exceptions.
Intake Allow warehouse staff to start a count and submit a quantity without seeing the Shopify system quantity.
Identifiers Assign a deterministic task ID and retain Shopify product, variant, inventory item, location, order, and fulfilment identifiers.
Validation Reject unknown task IDs, negative counts, decimals where only whole units are allowed, inactive users, and invalid workflow transitions.
Recounts Require a second count by a different employee whenever the first physical count differs from the system snapshot.
Movement control Flag Shopify activity occurring between count start and count submission.
Investigation Require a controlled reason code, supporting notes, and optional evidence for confirmed variances.
Approval Route approval according to unit value, absolute quantity, and recurrence thresholds.
Inventory correction Keep the final Shopify adjustment under human control and record who confirmed it.
Notifications Notify warehouse operations, quality, or customer service only when an action or exception requires attention.
Reporting Show volume, completion time, overdue tasks, variance value, recurring SKUs, reasons, and automation failures.
Permissions Prevent counters from viewing system quantities, costs, approval fields, and other employees’ first-count results.
Reliability Prevent duplicate event processing, retry temporary failures, and route unresolved failures to a manual-review queue.
Audit evidence Retain timestamps, submitter identities, approval decisions, Shopify external IDs, automation execution references, and evidence links.
Manual override Allow the operations manager to suspend, reassign, reopen, or close a task with a documented reason.

Normal validation, formulas, thresholds, permissions, and routing rules were deliberately separated from AI. None of these controls required a probabilistic model.

Implementation Approaches Considered

Cycle-count implementation options
Approach Connected tools Effort Customization Main limitation
Improve the existing spreadsheet Shopify exports and Google Sheets Low Low Still depends on manual exports, follow-ups, and reconciliation.
Workspace workflow with n8n Shopify, Google Forms, Google Sheets, Google Drive, n8n, Slack Moderate High Requires disciplined sheet design, archiving, and workflow maintenance.
Dedicated warehouse inventory application Shopify and a specialized inventory platform Moderate to high Varies by product May add broader warehouse features and cost beyond the immediate requirement.
Custom database and web application Shopify API, database, application, identity provider, Slack High Very high Requires application hosting, support, security engineering, and a larger implementation budget.

Improving the existing spreadsheet

A single standardized spreadsheet would improve consistency, but it would not create blind counts, reliable reminders, event synchronization, approval evidence, or duplicate-event protection. It was suitable only as an interim control.

Using Google Workspace with n8n

This approach retained tools employees already understood while adding scheduled processing, Shopify integration, Slack routing, validation, retries, and operational logs. The expected volume was within a carefully managed Google Sheets design, provided high-volume event rows were archived regularly.

Purchasing dedicated warehouse software

A specialized warehouse management or inventory-counting platform could provide barcode scanning, mobile workflows, stronger concurrency controls, and more advanced inventory functions. Larkspur did not yet require a full warehouse management replacement, and the implementation would need a broader process change.

Building a custom application

A custom application would provide the strongest control over user experience and data integrity. It was not selected because 90 weekly count tasks and one warehouse did not justify the added hosting, software support, and security responsibilities.

The Selected Solution

Larkspur selected a Google Workspace workflow coordinated by n8n. Shopify remained the commercial inventory source, Google Sheets became the cycle-count system of record, Google Forms provided controlled intake, Google Drive stored evidence, and Slack delivered action notifications.

Responsibility of each selected tool
Tool Responsibility
Shopify Provides products, variants, inventory items, locations, inventory snapshots, orders, and fulfilment events.
Google Forms Captures count starts, first counts, recounts, investigations, approvals, and inventory-adjustment confirmations.
Google Sheets Stores item records, count tasks, normalized events, submissions, users, reason codes, logs, and reports.
n8n Schedules work, validates submissions, synchronizes systems, applies workflow rules, retries failures, and updates records.
Google Drive Stores the workbook and optional photographs or supporting variance evidence.
Slack Notifies warehouse, quality, customer-service, and approval owners without becoming the system of record.
Optional AI service Suggests a structured variance summary and likely investigation category after the core workflow is stable.

The selected design removed manual Shopify exports, task-list preparation, repeated status follow-ups, and spreadsheet consolidation. It did not automate the final inventory adjustment. An authorized employee still reviewed evidence, approved the correction, updated Shopify, and confirmed completion.

Slack was used for attention management rather than approvals. Approval decisions were submitted through a controlled form so the decision, approver identity, timestamp, and comments were written to the audit record.

System Architecture and Data Flow

  • Intake: Restricted Google Forms for count activity, investigation, approval, and adjustment confirmation.
  • System of record: A protected Google Sheets workbook.
  • Automation layer: n8n workflows using Shopify, Google Sheets, Google Drive, and Slack credentials.
  • Document storage: Restricted Google Drive folders created for variance evidence.
  • Notifications: Slack channels and direct messages mapped to responsible users.
  • Reporting: Google Sheets formulas, filtered views, pivot tables, and charts.
  • AI layer: An optional structured-output analysis step for variance investigation support.
  1. Synchronize the item master. A nightly n8n schedule reads active Shopify variants and inventory quantities for the warehouse location. It validates SKU and location mappings, then updates the corresponding Item_Master row. Unknown or duplicate SKUs are written to the exception queue instead of being silently merged.
  2. Receive commercial events. Shopify order and fulfilment events trigger n8n. The workflow extracts only required operational fields, creates a deterministic event key, and checks whether that key has already been processed.
  3. Normalize event lines. n8n converts each order or fulfilment line into a consistent movement record containing the Shopify resource ID, SKU, quantity, location, event time, and status. The returned row identifier is stored in the automation log.
  4. Flag event exceptions. Missing SKUs, unknown locations, failed fulfilment states, cancellations requiring review, or unexpected activity during an open count are routed to operations or customer service. Normal events update movement statistics without producing Slack noise.
  5. Generate weekly count tasks. Each Monday morning, n8n reads Item_Master, excludes inactive products and items with open tasks, calculates a risk score, and selects up to 90 item-location records.
  6. Assign ownership. The SKU zone determines the first counter. n8n appends a Count_Tasks row, updates Item_Master to show an open task, and posts the assignment to the warehouse channel.
  7. Start the blind count. The assigned employee submits a Start Count action through Google Forms. n8n confirms authorization, obtains a current Shopify on-hand quantity, records the count-window timestamp, and changes the task to Counting. The quantity is not shown to the counter.
  8. Capture the first count. The employee submits a non-negative whole-unit quantity. n8n checks for Shopify movement during the count window and compares the result with the hidden system snapshot.
  9. Route the recount. A matching first count closes the task. A difference creates an Awaiting Recount status and assigns another authorized warehouse employee. The recount form does not display the first count or Shopify quantity.
  10. Investigate confirmed variance. A matching recount confirms the variance. n8n requests a reason code, notes, corrective action, and optional evidence. Disagreement between the first count and recount goes to Manual Review.
  11. Approve and adjust. n8n selects the approval route according to variance value, units, and recurrence. After approval, an authorized employee adjusts Shopify manually and submits an adjustment confirmation.
  12. Close and report. n8n closes the task, updates the item’s last-count date and variance history, clears its open-task flag, and refreshes operational reporting. Any failed step records an error, increments the retry count, and enters a recovery queue.

Data Structure

The workbook uses separate tabs for source data, workflow records, configuration, intake responses, logs, and reporting. Automation-owned tabs and columns are protected from ordinary users.

Item_Master

Important Item_Master fields
Field Type Required Source Purpose
Item_Key Text Yes n8n Unique combination of Shopify inventory item and location.
SKU Text Yes Shopify Operational item identifier. Duplicate active SKUs are rejected.
Shopify_Product_GID Text Yes Shopify Shopify global product identifier.
Shopify_Variant_GID Text Yes Shopify Shopify global variant identifier.
Inventory_Item_GID Text Yes Shopify Identifier used for inventory queries.
Location_GID Text Yes Shopify Warehouse location identifier.
Product_Title Text Yes Shopify Readable product and variant description.
On_Hand_Qty Integer Yes Shopify Latest physical-stock quantity reported by Shopify.
Available_Qty Integer No Shopify Latest quantity available for sale or allocation.
Standard_Unit_Cost Decimal Yes Finance-maintained sheet Used to estimate variance value. Must be zero or greater.
ABC_Class Enum Yes Operations Allowed values A, B, or C.
Units_Ordered_30d Integer Yes Movement ledger Recent demand input to the scheduling score.
Units_Fulfilled_30d Integer Yes Movement ledger Recent warehouse movement input to the scheduling score.
Variance_Count_90d Integer Yes Count_Tasks Number of prior confirmed variances.
Open_Exception_Count Integer Yes Event_Exceptions Unresolved Shopify or inventory exceptions.
Last_Count_Date Date No n8n Most recent successfully closed count.
Open_Task Boolean Yes n8n Prevents overlapping count assignments.
Primary_Counter_Email Email Yes Users configuration Default assignment based on warehouse zone.
Active Boolean Yes Shopify and operations Controls scheduling eligibility.
Last_Shopify_Sync Timestamp Yes n8n Supports stale-data monitoring.

Count_Tasks

Important Count_Tasks fields
Field Type Required Purpose and validation
Task_ID Text Yes Deterministic unique task identifier.
Item_Key Text Yes References one Item_Master row.
SKU Text Yes Copied from Item_Master for reporting.
Scheduled_Date Date Yes Date selected by the scheduling workflow.
Due_Date Date Yes Normally two working days after assignment.
Priority Enum Yes Allowed values Critical, High, Normal, or Low.
Risk_Score Decimal Yes Calculated scheduling score retained for audit.
Owner_Email Email Yes Current action owner.
Status Enum Yes Controlled workflow stage.
System_Qty_At_Assignment Integer Yes Planning snapshot, hidden from counters.
System_Qty_At_Count_Start Integer Conditional Authoritative comparison snapshot recorded when the count starts.
Count_Window_Started_At Timestamp Conditional Beginning of movement-detection window.
First_Count Integer Conditional Must be a whole number of zero or greater.
First_Counter_Email Email Conditional Collected from the authenticated form submitter.
Variance_1 Integer Conditional First count minus system quantity.
Recount_Qty Integer Conditional Must be entered by a different authorized counter.
Final_Count Integer Conditional Recount quantity when a recount is required.
Final_Variance Integer Conditional Final count minus system quantity.
Variance_Value Decimal Conditional Final variance multiplied by standard unit cost.
Movement_During_Count Boolean Yes Forces manual review when Shopify activity overlaps the count window.
Reason_Code Enum Conditional Required for a confirmed non-zero variance.
Reason_Notes Text Conditional Required investigation detail with a configured minimum length.
Evidence_Link URL No Restricted Google Drive link.
Approval_Status Enum Yes Not Required, Pending, Approved, Returned, or Rejected.
Approval_Route Enum No Inventory Lead, Operations Manager, or Operations and Quality.
Approved_By Email No Validated against the active Users tab.
Approved_At Timestamp No Submission timestamp from the approval workflow.
Adjustment_Confirmation_ID Text No Identifier proving that an authorized user confirmed the Shopify adjustment.
Automation_Status Enum Yes Pending, Processing, Completed, Retry, or Failed.
Last_Automation_Run Timestamp No Most recent processing attempt.
Retry_Count Integer Yes Starts at zero and increments after recoverable failures.
Error_Message Text No Sanitized operational error without credentials or sensitive payloads.
Closed_At Timestamp No Completion timestamp used for processing-time reporting.

Supporting records

Movement_Ledger
One normalized row per relevant Shopify event line. Event_Key is the unique identifier used for idempotency.
Event_Exceptions
Contains unknown SKUs, unrecognized locations, failed fulfilments, overlapping count movements, and other events requiring human review.
Users
Maps employee email, role, warehouse zone, Slack user ID, active status, approval limit, and delegate.
Reason_Codes
Contains RECEIVING_ERROR, PICK_SHORT, MISBINNED, DAMAGE, RETURN_NOT_PROCESSED, BUNDLE_MAPPING, SKU_MAPPING, SYSTEM_TIMING, COUNT_ERROR, and OTHER.
Automation_Log
Stores workflow name, execution reference, record ID, start time, result, retry count, and sanitized error.
Failed_Events
Acts as the dead-letter queue for events that cannot be completed after automated retries.

Item_Master has a one-to-many relationship with Count_Tasks and Movement_Ledger. Count_Tasks has one-to-many relationships with raw form submissions and approval events. Google Sheets does not enforce relational constraints, so n8n validates references before every write.

Workflow Statuses and Ownership

Cycle-count workflow stages
Status Owner Entry condition Exit condition Reminder and escalation
Assigned First counter Weekly task created. Authorized employee starts the count. Reminder after one working day; escalate to inventory lead when overdue.
Counting First counter Shopify snapshot and start time recorded. Valid first count submitted. Alert after two hours because a long count window increases timing risk.
Awaiting Recount Second counter First count differs from system quantity. Independent recount submitted. Reminder after one working day; escalate after two.
Awaiting Reason Inventory lead Recount confirms a non-zero variance. Complete investigation submitted. Reminder after one working day.
Manual Review Inventory lead Counts disagree, movement occurred during counting, or validation is inconclusive. Lead documents resolution, orders another count, or cancels the task. Immediate Slack notification; escalate after one working day.
Awaiting Approval Configured approver Investigation passes validation. Approved, returned, or rejected. Reminder after 24 hours; delegate or escalate after 48 hours.
Quality Review Quality analyst High-value or recurring issue requires quality acknowledgment. Quality review submitted. Reminder after 24 hours.
Approved for Adjustment Inventory lead All required approvals are complete. Shopify adjustment is performed and confirmed. Reminder after four business hours.
Returned for Information Inventory lead Approver requests more evidence. Investigation is resubmitted. Reminder after one working day.
Rejected Operations manager Approver rejects the proposed adjustment. Manager closes or reopens the investigation. Included in daily exception review.
Closed System owner No variance exists or an approved adjustment is confirmed. Terminal unless an authorized manager reopens it. No reminders.
Automation Failed System owner A required integration remains unsuccessful after retries. Failure is repaired and record replayed. Immediate operations alert and daily reconciliation.

A task can move backward when an approver returns it, a manager requests another count, or reconciliation finds missing evidence. Only the operations manager can cancel or reopen a closed task, and the override reason is mandatory.

Step-by-Step Implementation

Step 1: Prepare the Accounts and Permissions

  1. Create a Shopify custom app or approved integration credential with read access to the products, variants, inventory, locations, orders, and fulfilment records required by the implementation. The exact access-scope names depend on the Shopify API version and app configuration.
  2. Store the Shopify shop domain and Admin API access token in an n8n credential. Do not place the token in a Code node, Google Sheet, workflow name, or Slack message.
  3. Use an n8n deployment that supports scheduled workflows, webhooks, execution history, encrypted credentials, Google Sheets, Google Drive, Shopify or HTTP requests, and Slack.
  4. Create a Google Workspace automation identity such as warehouse-automation@YOUR_DOMAIN. Grant it editor access only to the cycle-count workbook and evidence root folder.
  5. Create a Slack application or n8n Slack OAuth connection that can post to the approved warehouse, quality, customer-service, and automation-alert channels. It does not require broad access to private conversations.
  6. Create separate production and test workbooks. Test forms must write only to the test workbook.
  7. Create test users representing a counter, second counter, inventory lead, operations manager, quality analyst, and customer-service lead.
  8. Restrict forms to authenticated company accounts where supported. Collect the submitter email automatically.
  9. Protect system quantities, costs, formulas, approval data, event records, user mappings, and logs. Warehouse counters use forms rather than direct access to Count_Tasks.
  10. Record a backup approver and Slack user ID for each operational role.

Larkspur assigned Marcus Lee, the inventory control lead, as primary system owner. Operations manager Elena Ruiz was the approval owner. Quality analyst Priya Shah owned recurring-variance reporting, and customer-service lead Sam Carter received customer-impacting event exceptions.

Step 2: Build the Intake

Four restricted Google Forms were created. Their response tabs were connected to the central workbook, but response tabs were not treated as authoritative workflow records until n8n validated each submission.

Count Capture form

Count Capture form fields
Field Type and allowed values Validation
Submitter email Automatically collected email Must match an active counter in Users.
Task ID Required short text Uppercase pattern beginning with CC and matching an open task.
Submission type Start Count, First Count, or Recount Must be valid for the current task status.
Counted quantity Required for First Count and Recount Whole number from zero to a configured upper bound.
Bin condition Normal, Empty, Damaged, Mixed SKU, Label Problem, or Other Required for count submissions.
Operational note Optional paragraph Required when Bin condition is Other.

Form sections show the quantity fields only for First Count or Recount. The form never displays Shopify quantity or the previous count. Its confirmation message states that the submission is pending validation and does not guarantee task completion.

Variance Investigation form

This form collects Task ID, controlled reason code, investigation notes, corrective action, evidence upload or evidence link, and whether a customer order may be affected. Notes require enough detail to describe what was checked rather than simply stating that stock was wrong.

Variance Approval form

This form collects Task ID, decision, comments, and authenticated approver email. Allowed decisions are Approve, Return for Information, and Reject. n8n verifies that the submitter is the assigned approver or an active delegate.

Adjustment Confirmation form

This form collects Task ID, confirmation timestamp, the employee who updated Shopify, resulting Shopify on-hand quantity, adjustment reason used, and optional evidence link. The workflow verifies that the task has an Approved for Adjustment status before accepting the confirmation.

Duplicate form submissions are prevented through a Submission_Key made from the form identifier and response-row number. The workflow also checks whether the same task, stage, and submitter have already produced an accepted submission. Incomplete or invalid submissions remain in the response sheet with a Processed_Status of Rejected and a visible validation message.

Step 3: Create the System of Record

  1. Create one production workbook containing Item_Master, Count_Tasks, Movement_Ledger, Event_Exceptions, Users, Reason_Codes, Automation_Log, Failed_Events, Config, Dashboard, and the form response tabs.
  2. Freeze the header row, apply consistent date formats, and avoid merged cells in machine-read tabs.
  3. Use the exact header names expected by n8n. Renaming a machine-read header is treated as a controlled change.
  4. Create an Item_Key from Shopify inventory item ID and location ID. Do not rely only on SKU because duplicate or blank SKUs must be handled as exceptions.
  5. Use deterministic Task_ID values containing the schedule date and a hash of Item_Key.
  6. Protect formula columns and fields written by n8n.
  7. Create filtered views for open assignments, recounts, investigations, approvals, adjustment confirmations, overdue tasks, and failures.
  8. Set data validation lists for ABC_Class, Status, Priority, Reason_Code, Approval_Status, role, and active flags.
  9. Set defaults for Open_Task, Retry_Count, Automation_Status, and Approval_Status.
  10. Do not manually sort only part of a machine-read range. Sort through filtered views or named reporting tabs.

Google Sheets does not provide database-style unique constraints. Deterministic keys, pre-write lookups, serialized writer workflows where available, and daily duplicate reconciliation provide the necessary controls at this volume.

Step 4: Connect the Tools

Integration field mappings
Source Destination Trigger Important mapping Failure behavior
Shopify inventory Item_Master Nightly schedule Variant GID, inventory item GID, SKU, title, location, on-hand, available, active status Retry temporary API errors; route missing SKU and duplicate mapping to Event_Exceptions.
Shopify orders Movement_Ledger Order event Webhook ID, order ID, order number, line ID, SKU, quantity, event time, status Use Event_Key lookup before append; failed records enter Failed_Events.
Shopify fulfilments Movement_Ledger Fulfilment event Fulfilment ID, order ID, location ID, SKU, quantity, event time, status Unknown location or failure-like status creates an operations exception.
Count form response Count_Tasks Scheduled poll for unprocessed rows Task ID, type, count, condition, submitter, timestamp Invalid response is marked Rejected without changing the task.
Count_Tasks Slack Status or owner change Task ID, SKU, location, due date, required action, restricted record link Notification failure is logged and retried without rolling back the task update.
Investigation form Count_Tasks and Drive New unprocessed response Reason, notes, corrective action, evidence link, customer-impact flag Missing required evidence for a configured high-risk case returns the submission.
Approval form Count_Tasks New unprocessed response Decision, approver, comments, submitted timestamp Unauthorized decisions are rejected and reported to the system owner.
Adjustment form Count_Tasks and Item_Master New unprocessed response Confirmed quantity, employee, timestamp, confirmation ID Quantity mismatch creates Manual Review instead of closing the task.

The Shopify connection uses an Admin API access token stored in n8n. The Google connection uses OAuth or a controlled service identity. Slack uses a restricted OAuth credential. Returned Shopify GIDs, form response identifiers, Google Drive file IDs, and n8n execution references are retained in the system of record.

Interface labels can vary by n8n and application version. The implementation should preserve the trigger, data mapping, validation, destination action, and failure route even when a connector uses different labels.

Step 5: Build the Core Automation

Automation 1: Nightly Shopify item synchronization

  • Trigger: n8n Schedule Trigger each night after warehouse activity slows.
  • Conditions: Process the configured Shopify location and only records returned by the active API query.
  • Actions: Page through variants, normalize identifiers, locate Item_Key in Item_Master, update existing rows, append new rows, and flag missing or duplicate SKUs.
  • Fields updated: Shopify identifiers, title, quantities, active status, and Last_Shopify_Sync.
  • Notification: Post only when duplicate SKUs, missing mappings, stale synchronization, or API failures require attention.
  • Exception: Do not overwrite manually maintained unit cost, ABC class, zone, or counter assignment.

Automation 2: Shopify order and fulfilment event processing

  • Trigger: Shopify event trigger for configured order and fulfilment topics.
  • Conditions: Event_Key must not already exist.
  • Actions: Normalize event lines, validate SKU and location, append Movement_Ledger rows, update rolling movement inputs, and compare event time with open count windows.
  • Fields updated: Event status, movement quantity, Shopify external IDs, exception type, owner team, and processing timestamp.
  • Notification: Operations receives inventory or location exceptions. Customer service receives only exceptions that may affect an order.
  • Exception: A duplicate event is logged as Ignored Duplicate and causes no second movement update.

Automation 3: Weekly task generation

  • Trigger: Monday schedule at 06:00 in the warehouse time zone.
  • Conditions: Item is active, has a valid SKU and location, has no open task, and is due or has a qualifying risk exception.
  • Actions: Calculate risk, select up to 90 records, create deterministic Task_ID values, append tasks, set Open_Task to Yes, and assign the first counter.
  • Fields updated: Scheduled date, due date, risk score, priority, owner, status, system quantity at assignment, and automation status.
  • Notification: Post a summary to the warehouse channel and direct notifications to assigned counters when configured.
  • Exception: If the task ID already exists, skip creation and log the duplicate attempt.

Automation 4: Count submission processing

  • Trigger: n8n polls unprocessed Count Capture responses every two minutes.
  • Conditions: Submitter is active, task exists, stage is valid, and quantity passes validation.
  • Actions: Start the count, capture a Shopify snapshot, record the first count, assign an independent recount, or route the task to Manual Review.
  • Fields updated: Count timestamps, counters, quantities, variances, movement flag, owner, status, and submission processing result.
  • Notification: Send the next action to the appropriate counter or inventory lead.
  • Exception: Reject a recount from the original counter and leave the task awaiting an independent recount.

Automation 5: Investigation and approval

  • Trigger: New unprocessed investigation or approval response.
  • Conditions: The task is at the expected stage and the submitter has the required role.
  • Actions: Validate reason and evidence, determine the approval route, record decisions, and move the task forward or backward.
  • Fields updated: Reason, notes, evidence, approval route, approval status, approver, timestamps, and comments.
  • Notification: Notify the next approver, delegate, inventory lead, or quality analyst.
  • Exception: Unauthorized or duplicate approval submissions are retained as rejected evidence but do not alter the task.

Automation 6: Closure and reconciliation

  • Trigger: Accepted adjustment confirmation or zero-variance completion.
  • Conditions: All required approvals and fields are present.
  • Actions: Close the task, update Item_Master, clear Open_Task, calculate processing time, and write an audit log entry.
  • Fields updated: Final quantity, last-count date, variance history, confirmation ID, closed timestamp, and automation status.
  • Notification: Send a completion message only for material or escalated cases.
  • Exception: If the confirmed Shopify quantity does not match the expected final quantity, route the task to Manual Review.

Step 6: Add Approvals, Reminders, and Escalations

The representative approval thresholds are operational assumptions and should be replaced with values approved by finance and warehouse management.

  • A zero final variance requires no approval and closes automatically.
  • A confirmed variance below an absolute value of $25 and below five units routes to the inventory lead.
  • A variance of $25 or more, five units or more, or two prior variances within 90 days routes to the operations manager.
  • A variance of $250 or more, or three related occurrences within 90 days, requires operations approval and quality acknowledgment in parallel.
  • The final Shopify inventory correction remains blocked until every required approval is complete.

The reminder workflow runs each business morning. It reads open tasks, compares the current time with Due_Date and the last reminder timestamp, and posts only when the configured interval has elapsed.

After 24 hours, an approver receives a reminder. After 48 hours, n8n checks Users for an active delegate. If a delegate exists, the task is reassigned and both users are notified. If no delegate is available, the operations manager receives an escalation.

A Return for Information decision changes the task to Returned for Information and restores ownership to the inventory lead. Reject changes the task to Rejected and requires the operations manager to close it without adjustment or reopen the investigation. Every decision retains the form response ID, approver email, timestamp, and comments.

Step 7: Add Documents and File Management

Routine counts do not require documents. Evidence is requested for material, recurring, damaged, mislabeled, or mixed-SKU variances.

  1. Create a restricted Drive root named Warehouse Cycle Count Evidence.
  2. Create year and month folders beneath the root.
  3. When a task first needs evidence, use n8n to create a Task_ID folder.
  4. Name files using Task_ID_SKU_EvidenceType_YYYYMMDD.
  5. Store only Drive file IDs and restricted links in Count_Tasks.
  6. Do not enable public or organization-wide links.
  7. Keep the original uploaded file and use Drive version history for replacements where practical.
  8. When an upload fails, retain the investigation as incomplete and notify the submitter rather than losing the rest of the response.
  9. Reject file types or sizes that conflict with the organization’s Google Workspace policy.
  10. Archive evidence according to the company’s inventory and financial record-retention policy.

If Google Forms file upload is unavailable under the organization’s policy, the form accepts a link to a file already stored in the restricted evidence folder. n8n verifies that the link refers to the approved Drive location before accepting it.

Step 8: Add Reporting and Operational Views

Operational views and filters
View Filter Owner
New assignments Status equals Assigned and Scheduled_Date is current week Warehouse lead
Work awaiting action Status is not Closed and Owner_Email is populated Operations
Overdue tasks Due_Date is before today and Status is not Closed Inventory lead
Incomplete investigations Status equals Awaiting Reason or Returned for Information Inventory lead
Manual-review queue Status equals Manual Review Inventory lead
Rejected items Status equals Rejected Operations manager
Upcoming deadlines Due_Date is within the next two working days Warehouse team
Recently completed Closed_At is within the last seven days Operations and quality
Automation failures Automation_Status equals Retry or Failed System owner
Recurring variance Variance_Count_90d is two or greater Quality analyst

The Dashboard reads only normalized tabs. It reports weekly tasks scheduled, count completion rate, median processing time, overdue count, confirmed variance units, signed and absolute variance value, top recurring SKUs, reasons by warehouse zone, Shopify event exceptions, and failed automation runs.

Rolling 30-day movement and 90-day variance measures refresh nightly. Pivot tables group by week, SKU, ABC class, reason, and owner. The quality analyst reviews trends weekly, while the inventory lead owns daily operational views.

Alerts are generated from record-level rules, not chart appearance. For example, the workflow alerts when failed automation records exceed zero, overdue tasks exceed ten, or the same SKU records three confirmed variances within 90 days.

Step 9: Add Security and Governance Controls

  • Use least-privilege Shopify, Google, Slack, and n8n credentials.
  • Restrict counters to forms and warehouse notifications. Do not grant them access to costs or hidden comparison quantities.
  • Limit Count_Tasks editing to the automation identity, inventory lead, operations manager, and designated backup.
  • Store all credentials in n8n’s encrypted credential store or an approved secret manager.
  • Never write Shopify tokens, OAuth tokens, request signatures, or complete customer payloads to Sheets or Slack.
  • Store only the customer-impact flag and Shopify order identifier needed for exception handling. Do not copy customer addresses into the count tracker.
  • Review workbook sharing, Drive links, Slack application permissions, and n8n users quarterly.
  • Remove former employee access from Google Workspace, Slack, Shopify, n8n, and Users promptly.
  • Retain n8n execution logs according to operational need while avoiding unnecessary full-payload retention.
  • Export or back up the workbook on an approved schedule and test restoration.
  • Document every manual override with user, timestamp, previous status, new status, and reason.
  • Do not send personal customer information, credentials, or unrestricted evidence files to an AI service.
  • Require human approval for inventory adjustments, high-value write-offs, customer communications, and policy exceptions.

Step 10: Deploy and Test

  1. Build workflows against the test workbook and test forms.
  2. Use sanitized Shopify webhook samples or a Shopify test environment where available.
  3. Create test items covering A, B, and C classes, zero stock, duplicate SKUs, missing costs, inactive products, and open exceptions.
  4. Run technical tests for every workflow branch and failure route.
  5. Run user acceptance testing with one inventory lead, two counters, one approver, one quality user, and one customer-service user.
  6. Pilot 20 tasks for one zone before increasing to 90 weekly tasks.
  7. Compare each pilot task with Shopify and the physical bin before permitting inventory adjustments.
  8. Document form use, recount independence, reason selection, approval responsibilities, and failure recovery.
  9. Freeze configuration changes during the first production week except for urgent corrections.
  10. Activate workflows in dependency order: master sync, events, forms, task scheduling, reminders, approvals, closure, and reporting.
  11. Monitor every production execution during the pilot and review failed runs daily afterward.
  12. Keep the prior count workbook available as read-only rollback evidence. If the new workflow is suspended, stop task creation but preserve submitted records.
  13. Publish a launch message containing process ownership, support contacts, form links, and escalation rules.

Code and Configuration

The implementation uses native n8n triggers and application nodes where possible. Code nodes are used for deterministic normalization, scoring, and validation that would otherwise require many fragile expression nodes.

Configuration values

Required configuration placeholders
Value Purpose
YOUR_SHOP Shopify shop subdomain.
YOUR_SUPPORTED_API_VERSION A currently supported Shopify Admin API version selected during implementation.
YOUR_ADMIN_API_ACCESS_TOKEN Stored only in an n8n credential.
YOUR_LOCATION_GID Shopify warehouse location identifier.
YOUR_SPREADSHEET_ID Production cycle-count workbook.
YOUR_TEST_SPREADSHEET_ID Test workbook.
YOUR_FOLDER_ID Restricted evidence root folder.
YOUR_SLACK_CHANNEL_ID Approved warehouse or alert channel.

Shopify inventory snapshot request

When the installed n8n Shopify node does not expose all required inventory quantities, use an HTTP Request node against this endpoint pattern:

POST https://YOUR_SHOP.myshopify.com/admin/api/YOUR_SUPPORTED_API_VERSION/graphql.json
Content-Type: application/json
X-Shopify-Access-Token: stored in the n8n Shopify credential

The GraphQL query requests variants and the warehouse inventory level:

query InventorySnapshot($cursor: String, $locationId: ID!) {
  productVariants(first: 100, after: $cursor) {
    edges {
      cursor
      node {
        id
        sku
        displayName
        product {
          id
          title
        }
        inventoryItem {
          id
          tracked
          inventoryLevel(locationId: $locationId) {
            id
            quantities(names: ["available", "on_hand"]) {
              name
              quantity
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Send variables as JSON:

{
  "cursor": null,
  "locationId": "YOUR_LOCATION_GID"
}

After each response, read pageInfo.hasNextPage. If it is true, set cursor to pageInfo.endCursor and request the next page. Stop only when hasNextPage is false.

Validate GraphQL errors before processing data. Monitor the response cost information when returned, pace requests according to available capacity, and retry temporary rate-limit or server errors after 5, 15, and 45 seconds. Authentication failures should not be retried repeatedly because they normally require credential repair.

Weekly cycle-count scheduler

Place the following JavaScript in an n8n Code node configured to run once for all Item_Master rows. The preceding Google Sheets node must return the full active item range using headers matching the script.

const CONFIG = {
  MAX_TASKS: 90,
  TASK_DUE_DAYS: 2,
  CLASS_INTERVAL_DAYS: {
    A: 30,
    B: 90,
    C: 180,
  },
  CLASS_POINTS: {
    A: 40,
    B: 25,
    C: 10,
  },
};

const now = new Date();
const scheduledDate = now.toISOString().slice(0, 10);

function toNumber(value, fallback = 0) {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
}

function toBoolean(value) {
  return ["yes", "true", "1", "y"].includes(
    String(value ?? "").trim().toLowerCase()
  );
}

function parseDate(value) {
  if (!value) return null;
  const parsed = new Date(value);
  return Number.isNaN(parsed.getTime()) ? null : parsed;
}

function daysSince(date) {
  if (!date) return 999;
  return Math.max(
    0,
    Math.floor((now.getTime() - date.getTime()) / 86400000)
  );
}

function addDays(date, days) {
  const result = new Date(date);
  result.setUTCDate(result.getUTCDate() + days);
  return result.toISOString().slice(0, 10);
}

function hashString(value) {
  let hash = 2166136261;
  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(16).padStart(8, "0");
}

function cleanText(value) {
  return String(value ?? "").trim();
}

const candidates = [];

for (const input of $input.all()) {
  const row = input.json;
  const itemKey = cleanText(row.Item_Key);
  const sku = cleanText(row.SKU);
  const locationGid = cleanText(row.Location_GID);

  if (!itemKey || !sku || !locationGid) continue;
  if (!toBoolean(row.Active)) continue;
  if (toBoolean(row.Open_Task)) continue;

  const abcClass = ["A", "B", "C"].includes(
    cleanText(row.ABC_Class).toUpperCase()
  )
    ? cleanText(row.ABC_Class).toUpperCase()
    : "C";

  const lastCountDate = parseDate(row.Last_Count_Date);
  const elapsedDays = daysSince(lastCountDate);
  const intervalDays = CONFIG.CLASS_INTERVAL_DAYS[abcClass];
  const isDue = elapsedDays >= intervalDays;

  const ordered30d = Math.max(0, toNumber(row.Units_Ordered_30d));
  const fulfilled30d = Math.max(0, toNumber(row.Units_Fulfilled_30d));
  const movementUnits = ordered30d + fulfilled30d;
  const varianceCount90d = Math.max(
    0,
    toNumber(row.Variance_Count_90d)
  );
  const openExceptions = Math.max(
    0,
    toNumber(row.Open_Exception_Count)
  );
  const previousVarianceValue = Math.abs(
    toNumber(row.Last_Absolute_Variance_Value)
  );

  const agePoints = Math.min(
    25,
    Math.round((elapsedDays / intervalDays) * 25)
  );
  const movementPoints = Math.min(
    15,
    Math.round(Math.log10(movementUnits + 1) * 6)
  );
  const variancePoints = Math.min(15, varianceCount90d * 5);
  const exceptionPoints = Math.min(10, openExceptions * 5);

  const riskScore =
    CONFIG.CLASS_POINTS[abcClass] +
    agePoints +
    movementPoints +
    variancePoints +
    exceptionPoints;

  const riskOverride =
    openExceptions > 0 || previousVarianceValue >= 25;

  if (!isDue && !riskOverride) continue;

  let priority = "Normal";
  if (riskScore >= 80 || openExceptions >= 2) {
    priority = "Critical";
  } else if (riskScore >= 60) {
    priority = "High";
  } else if (riskScore < 30) {
    priority = "Low";
  }

  candidates.push({
    row,
    itemKey,
    sku,
    locationGid,
    abcClass,
    elapsedDays,
    riskScore,
    priority,
  });
}

candidates.sort((left, right) => {
  if (right.riskScore !== left.riskScore) {
    return right.riskScore - left.riskScore;
  }
  if (right.elapsedDays !== left.elapsedDays) {
    return right.elapsedDays - left.elapsedDays;
  }
  return left.sku.localeCompare(right.sku);
});

return candidates.slice(0, CONFIG.MAX_TASKS).map((candidate) => {
  const row = candidate.row;
  const taskHash = hashString(
    `${candidate.itemKey}|${scheduledDate}`
  );

  return {
    json: {
      Task_ID: `CC-${scheduledDate.replaceAll("-", "")}-${taskHash}`,
      Item_Key: candidate.itemKey,
      SKU: candidate.sku,
      Location_GID: candidate.locationGid,
      Scheduled_Date: scheduledDate,
      Due_Date: addDays(now, CONFIG.TASK_DUE_DAYS),
      Priority: candidate.priority,
      Risk_Score: candidate.riskScore,
      ABC_Class: candidate.abcClass,
      Owner_Email: cleanText(row.Primary_Counter_Email),
      Status: "Assigned",
      System_Qty_At_Assignment: Math.trunc(
        toNumber(row.On_Hand_Qty)
      ),
      Standard_Unit_Cost: toNumber(row.Standard_Unit_Cost),
      Approval_Status: "Not Required",
      Automation_Status: "Created",
      Retry_Count: 0,
      Created_At: now.toISOString(),
    },
  };
});

The next Google Sheets node performs a Task_ID lookup before appending. If no task exists, append the returned row and update Item_Master Open_Task to Yes. If the task exists, log an idempotent skip.

Test the script with at least one due item, one non-due item, one item with an open task, one missing SKU, and one risk override. The expected output is zero to 90 task objects. An empty output is valid when no items qualify.

Shopify event normalization

Use one workflow branch per event topic. Immediately after the trigger, add an Edit Fields node that sets Event_Type to one of orders_create, orders_updated, orders_cancelled, fulfillments_create, or fulfillments_update. Preserve the webhook identifier when available.

const source = $input.first().json;
const body = source.body ?? source;

const eventType = String(
  source.Event_Type ?? source.event_type ?? ""
)
  .trim()
  .toLowerCase()
  .replaceAll("/", "_");

const allowedTypes = new Set([
  "orders_create",
  "orders_updated",
  "orders_cancelled",
  "fulfillments_create",
  "fulfillments_update",
]);

if (!allowedTypes.has(eventType)) {
  throw new Error(`Unsupported Event_Type: ${eventType}`);
}

function text(value) {
  return String(value ?? "").trim();
}

function hashString(value) {
  let hash = 2166136261;
  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(16).padStart(8, "0");
}

function headerValue(headers, name) {
  if (!headers || typeof headers !== "object") return "";
  const target = name.toLowerCase();
  const key = Object.keys(headers).find(
    (candidate) => candidate.toLowerCase() === target
  );
  return key ? text(headers[key]) : "";
}

const headers = source.headers ?? {};
const resourceId = text(
  body.admin_graphql_api_id ?? body.id ?? body.order_id
);

if (!resourceId) {
  throw new Error("Shopify event has no resource identifier.");
}

const eventTime = text(
  body.updated_at ?? body.created_at ?? new Date().toISOString()
);

const webhookId =
  text(source.Webhook_ID) ||
  headerValue(headers, "x-shopify-webhook-id");

const baseEventId =
  webhookId ||
  hashString(
    `${eventType}|${resourceId}|${eventTime}|${text(body.status)}`
  );

const rawStatus = text(
  body.status ??
    body.fulfillment_status ??
    body.financial_status ??
    "unknown"
);

const orderId = text(
  body.order_id ??
    body.order?.id ??
    (eventType.startsWith("orders_") ? body.id : "")
);

const fulfilmentId = eventType.startsWith("fulfillments_")
  ? text(body.id)
  : "";

const locationId = text(body.location_id ?? body.location?.id);
const orderNumber = text(body.name ?? body.order?.name);

const lineItems =
  Array.isArray(body.line_items) && body.line_items.length > 0
    ? body.line_items
    : [{}];

const failureLikeStatus = /(fail|error)/i.test(rawStatus);
const cancellationEvent = eventType === "orders_cancelled";

const output = [];

for (let index = 0; index < lineItems.length; index += 1) {
  const line = lineItems[index];
  const lineId = text(
    line.admin_graphql_api_id ??
      line.id ??
      line.variant_id ??
      `line-${index + 1}`
  );
  const sku = text(line.sku);
  const quantityValue = Number(line.quantity ?? 0);
  const quantity = Number.isFinite(quantityValue)
    ? Math.max(0, Math.trunc(quantityValue))
    : 0;

  let exceptionType = "";
  if (!sku) {
    exceptionType = "MISSING_SKU";
  } else if (
    eventType.startsWith("fulfillments_") &&
    !locationId
  ) {
    exceptionType = "MISSING_LOCATION";
  } else if (failureLikeStatus) {
    exceptionType = "FULFILMENT_STATUS_REVIEW";
  }

  let movementType = "STATUS_ONLY";
  if (eventType === "orders_create") {
    movementType = "ORDERED";
  } else if (eventType === "fulfillments_create") {
    movementType = "FULFILLED";
  }

  output.push({
    json: {
      Event_Key: `${baseEventId}:${hashString(lineId)}`,
      Webhook_ID: webhookId,
      Event_Type: eventType,
      Event_Time: eventTime,
      Shopify_Resource_ID: resourceId,
      Shopify_Order_ID: orderId,
      Shopify_Order_Number: orderNumber,
      Shopify_Fulfilment_ID: fulfilmentId,
      Shopify_Line_ID: lineId,
      Shopify_Location_ID: locationId,
      SKU: sku,
      Quantity: quantity,
      Movement_Type: movementType,
      Raw_Status: rawStatus,
      Exception_Type: exceptionType,
      Operations_Flag: Boolean(exceptionType),
      Customer_Service_Flag:
        cancellationEvent || failureLikeStatus,
      Processing_Status: "Normalized",
      Processed_At: new Date().toISOString(),
    },
  });
}

return output;

After this Code node, look up Event_Key in Movement_Ledger. Append only when it does not exist. Then look up SKU and location in Item_Master. Set UNKNOWN_SKU or UNKNOWN_LOCATION when no valid match exists.

The workflow stores normalized fields rather than the full Shopify payload. Order-create and fulfilment-create events contribute to movement statistics. Update events revise status and support exception detection but do not add the entire quantity again.

Count validation Code node

The form-processing workflow looks up the task and merges the task fields with the form response. Place this code after the merge. It validates First Count and Recount submissions without changing external systems directly.

function clean(value) {
  return String(value ?? "").trim();
}

function parseWholeCount(value) {
  const parsed = Number(value);
  if (
    !Number.isFinite(parsed) ||
    !Number.isInteger(parsed) ||
    parsed < 0
  ) {
    return null;
  }
  return parsed;
}

function invalid(row, message) {
  return {
    json: {
      Task_ID: clean(row.Task_ID),
      Submission_Key: clean(row.Submission_Key),
      Validation_Result: "Rejected",
      Validation_Message: message,
      Task_Update_Allowed: false,
      Processed_At: new Date().toISOString(),
    },
  };
}

const results = [];

for (const input of $input.all()) {
  const row = input.json;
  const taskId = clean(row.Task_ID);
  const stage = clean(row.Submission_Type);
  const status = clean(row.Task_Status ?? row.Status);
  const submitter = clean(row.Submitter_Email).toLowerCase();
  const owner = clean(row.Owner_Email).toLowerCase();
  const firstCounter = clean(
    row.First_Counter_Email
  ).toLowerCase();
  const submittedCount = parseWholeCount(row.Submitted_Count);
  const systemCount = parseWholeCount(
    row.System_Qty_At_Count_Start
  );
  const firstCount = parseWholeCount(row.First_Count);
  const movementDuringCount = [
    "yes",
    "true",
    "1",
  ].includes(
    clean(row.Movement_During_Count).toLowerCase()
  );

  if (!taskId) {
    results.push(invalid(row, "Task ID is required."));
    continue;
  }

  if (!submitter) {
    results.push(
      invalid(row, "Authenticated submitter email is required.")
    );
    continue;
  }

  if (submittedCount === null) {
    results.push(
      invalid(row, "Count must be a non-negative whole number.")
    );
    continue;
  }

  if (systemCount === null) {
    results.push(
      invalid(row, "The count-start system snapshot is missing.")
    );
    continue;
  }

  if (stage === "First Count") {
    if (status !== "Counting") {
      results.push(
        invalid(
          row,
          `First Count is not valid while status is ${status}.`
        )
      );
      continue;
    }

    if (owner && owner !== submitter) {
      results.push(
        invalid(row, "Submitter is not the assigned first counter.")
      );
      continue;
    }

    const variance = submittedCount - systemCount;
    let nextStatus = "Closed";

    if (movementDuringCount) {
      nextStatus = "Manual Review";
    } else if (variance !== 0) {
      nextStatus = "Awaiting Recount";
    }

    results.push({
      json: {
        Task_ID: taskId,
        Submission_Key: clean(row.Submission_Key),
        Validation_Result: "Accepted",
        Validation_Message: "",
        Task_Update_Allowed: true,
        First_Count: submittedCount,
        First_Counter_Email: submitter,
        First_Count_At: clean(row.Submitted_At),
        Variance_1: variance,
        Next_Status: nextStatus,
        Requires_Recount:
          variance !== 0 && !movementDuringCount,
        Processed_At: new Date().toISOString(),
      },
    });
    continue;
  }

  if (stage === "Recount") {
    if (status !== "Awaiting Recount") {
      results.push(
        invalid(
          row,
          `Recount is not valid while status is ${status}.`
        )
      );
      continue;
    }

    if (firstCount === null) {
      results.push(
        invalid(row, "The accepted first count is missing.")
      );
      continue;
    }

    if (submitter === firstCounter) {
      results.push(
        invalid(
          row,
          "Recount must be performed by a different employee."
        )
      );
      continue;
    }

    const finalVariance = submittedCount - systemCount;
    const countsAgree = submittedCount === firstCount;

    let nextStatus = "Awaiting Reason";
    if (movementDuringCount || !countsAgree) {
      nextStatus = "Manual Review";
    }

    results.push({
      json: {
        Task_ID: taskId,
        Submission_Key: clean(row.Submission_Key),
        Validation_Result: "Accepted",
        Validation_Message: "",
        Task_Update_Allowed: true,
        Recount_Qty: submittedCount,
        Recount_Counter_Email: submitter,
        Recount_At: clean(row.Submitted_At),
        Final_Count: submittedCount,
        Final_Variance: finalVariance,
        Counts_Agree: countsAgree,
        Next_Status: nextStatus,
        Processed_At: new Date().toISOString(),
      },
    });
    continue;
  }

  results.push(
    invalid(row, `Unsupported submission type: ${stage}`)
  );
}

return results;

Route records with Task_Update_Allowed equal to true to the Google Sheets update node. Route rejected records to a response-status update and optional Slack message. A temporary Sheets failure must not mark the form response as processed.

Control formulas

Assuming the documented Count_Tasks column order, these row formulas provide visible reconciliation checks. n8n also calculates canonical values so the workflow does not depend solely on formulas.

Q2: =IF(OR(L2="",N2=""),"",N2-L2)
U2: =IF(R2<>"",R2,N2)
V2: =IF(OR(L2="",U2=""),"",U2-L2)
X2: =IF(V2="","",V2*W2)

Copy formulas when each task is appended and protect the formula cells. Test using positive, negative, zero, and blank values. Locale-specific separators may need adjustment.

Deployment and troubleshooting

  • Paste each script into the identified n8n Code node rather than a Google Apps Script editor.
  • No external JavaScript dependencies are required.
  • Replace configuration placeholders in credentials and workflow settings, not inside event payloads.
  • Run each node with pinned test data before activating the workflow.
  • Inspect n8n execution data, Automation_Log, and Failed_Events when a run fails.
  • A missing header normally indicates that a Google Sheet column was renamed or omitted.
  • A Shopify 401 or 403 response normally indicates an invalid credential or insufficient permission.
  • A Shopify 429 or temporary server response should follow the configured retry schedule.
  • A Sheets lookup returning multiple rows indicates duplicate keys and must enter Manual Review.
  • Activate workflows only after test credentials and spreadsheet IDs have been replaced with production values.

Failure Handling and Operational Reliability

Failure and recovery controls
Failure Automated response Manual recovery Owner
Missing SKU Create UNKNOWN_SKU exception and stop item update. Correct Shopify SKU or approve a mapping, then replay. Inventory lead
Duplicate webhook Detect Event_Key and record Ignored Duplicate. No action unless duplicate reconciliation finds conflicting data. System owner
Duplicate task Skip append when Task_ID already exists. Review Open_Task and close the obsolete record if required. Inventory lead
Invalid count Reject submission without changing task status. Counter submits a corrected whole-unit count. Assigned counter
Movement during count Set Movement_During_Count and route to Manual Review. Review movements and perform another controlled count. Inventory lead
Partial workflow completion Leave response unprocessed, record completed action IDs, and retry idempotently. Replay from the failed node after checking destination state. System owner
Shopify API failure Retry temporary failures after 5, 15, and 45 seconds. Repair credential or API query and replay Failed_Events. System owner
Expired Google credential Stop writes and send an automation alert. Reconnect OAuth, test access, and replay pending records. Google Workspace administrator
Unavailable approver Check delegate after the escalation threshold. Operations manager assigns a temporary approver. Operations manager
Failed folder creation Keep task awaiting evidence and retry. Create the folder manually and enter its Drive ID. System owner
Failed file upload Retain investigation as incomplete. Upload to the approved folder and resubmit the evidence link. Investigator
Invalid email Reject assignment or notification mapping. Correct Users and rerun assignment. Inventory lead
Slack notification failure Retry separately without rolling back the business record. Review the queue and contact the owner through an approved fallback. System owner
Rate limit Pause according to response guidance and retry with backoff. Reduce concurrency or reschedule non-urgent synchronization. System owner
Timeout Retry using the same idempotency key. Verify destination state before manual replay. System owner
Repeated unrecoverable failure Move record to Failed_Events and notify operations. Correct the root cause and use the controlled replay workflow. System owner

The replay workflow accepts a Failed_Event_ID, verifies the current destination state, and re-enters processing using the original Event_Key or Submission_Key. It never creates a new identity for the same business event.

A nightly reconciliation compares unprocessed form rows, open tasks, Item_Master Open_Task flags, failed event counts, and stale Shopify synchronization timestamps. Differences enter the manual-review queue. This catches cases where an external write succeeded but the n8n execution ended before its final status update.

A Complete Example

On July 20, 2026, the scheduler evaluated SKU BIN-14-WHT at the warehouse location. It was an A-class item, had 184 units of recent movement, and had one prior variance. Its risk score placed it within the weekly limit.

  1. n8n generated Task ID CC-20260720-7c91a24f and appended it to Count_Tasks.
  2. The task was assigned to warehouse associate Jordan Kim with a due date of July 22.
  3. Slack posted the Task ID, SKU, bin, due date, and Count Capture form link. The message did not include the Shopify quantity.
  4. Jordan submitted Start Count. n8n queried the current Shopify inventory level and recorded 84 units as System_Qty_At_Count_Start.
  5. Jordan counted 79 units and submitted First Count. n8n found no fulfilment event for that SKU and location during the count window.
  6. The calculated variance was 79 minus 84, or negative 5 units. At a standard cost of $8.50, the signed variance value was negative $42.50.
  7. The task moved to Awaiting Recount and was assigned to a different associate, Kai Morgan.
  8. Kai submitted a recount of 79 units. Because the counts agreed and the final variance remained non-zero, the task moved to Awaiting Reason.
  9. Marcus inspected the bin and nearby locations. He found five units placed in an adjacent bin with a similar label. He selected MISBINNED, entered the investigation notes, attached a restricted photograph, and recorded relabeling as the corrective action.
  10. Because the absolute variance value exceeded the representative $25 threshold, n8n routed approval to Elena Ruiz.
  11. Elena approved the correction through the approval form. The response ID, approver email, timestamp, and comments were written to Count_Tasks.
  12. The task changed to Approved for Adjustment. Marcus corrected the Shopify quantity to 79 and submitted the Adjustment Confirmation form.
  13. n8n verified the workflow status and confirmation, closed the task, cleared Item_Master Open_Task, and updated Last_Count_Date and variance history.
  14. The evidence remained linked to the task. The MISBINNED reason contributed to the quality dashboard’s recurring-cause report.

If a fulfilment event had occurred between count start and first-count submission, the workflow would have set Movement_During_Count to Yes and routed the task to Manual Review instead of treating negative five as a confirmed physical variance.

Implementation Cost

All amounts below are representative planning assumptions, not vendor quotes or verified client results. Existing Shopify, Google Workspace, and Slack subscriptions may already be business operating costs, but they still require suitable features and administration.

Representative one-time implementation costs
Category Assumption Estimated amount
Internal discovery and data preparation 22 hours at $40 per hour $880
Internal user testing and training 18 hours at $40 per hour $720
Professional workflow implementation 80 hours at $110 per hour $8,800
Total representative implementation External implementation plus internal participation $10,400

The professional implementation assumption includes process design, workbook construction, form configuration, n8n workflows, Shopify integration, Slack routing, testing support, technical documentation, and launch assistance.

Representative recurring monthly costs
Category Assumption Estimated amount
n8n capacity allowance Planning allowance for the required executions and features $40
Existing Shopify, Workspace, and Slack No incremental license assumed for the representative calculation $0 incremental
Core Shopify API access No separate usage fee assumed for the configured app access $0 incremental
Internal maintenance Four hours per month at a loaded rate of $32 $128 in staff capacity
Optional AI usage Controlled monthly usage allowance $8

An internal-only implementation could reduce external spending but would still require substantial staff time for architecture, integration, security, testing, documentation, and maintenance. The cost should not be treated as zero simply because some applications are already licensed.

Estimated Time and Cost Savings

The calculation uses the following representative assumptions:

  • 360 cycle-count tasks per month.
  • 12 minutes of current administrative handling per task.
  • 4 minutes of routine handling after core automation.
  • 12 percent of tasks require variance exception work.
  • 10 additional manual minutes for each exception.
  • 4 monthly hours for monitoring and maintenance.
  • $32 loaded hourly labour cost.
  • $40 recurring monthly automation cost.
  • $10,400 one-time implementation cost.

Current monthly labour hours: Monthly volume × current minutes per record ÷ 60

360 × 12 ÷ 60 = 72 hours

New monthly labour hours: Monthly volume × new minutes per record ÷ 60, plus exception handling and maintenance

Routine handling: 360 × 4 ÷ 60 = 24 hours

Exception handling: 360 × 12% × 10 ÷ 60 = 7.2 hours

Maintenance: 4 hours

Total new monthly labour: 24 + 7.2 + 4 = 35.2 hours

Monthly hours recovered: Current monthly labour hours minus new monthly labour hours

72 − 35.2 = 36.8 hours

Estimated monthly labour value: Monthly hours recovered × loaded hourly labour cost

36.8 × $32 = $1,177.60

Net estimated monthly value: Monthly labour value minus recurring tool costs

$1,177.60 − $40 = $1,137.60

Estimated payback period: One-time implementation cost ÷ net estimated monthly value

$10,400 ÷ $1,137.60 = approximately 9.1 months

Representative monthly capacity calculation
Measure Result
Current labour 72 hours
New labour including exceptions and maintenance 35.2 hours
Hours recovered 36.8 hours
Gross labour value $1,177.60
Recurring automation allowance $40
Net estimated monthly value $1,137.60
Estimated payback 9.1 months

Recovered time does not automatically reduce payroll. It can represent additional counting capacity, quicker investigations, reduced overtime, fewer administrative follow-ups, and lower dependence on one employee.

Non-financial benefits include clearer ownership, more independent recounts, consistent variance reasons, better audit evidence, fewer incomplete records, earlier identification of recurring problems, and more targeted communication with customer service.

Readers should replace volume, handling time, exception rate, review time, maintenance time, labour cost, subscription cost, and implementation cost with their own measured figures.

Adding AI to the Automation

AI is introduced only after the deterministic cycle-count workflow is stable. Scheduling thresholds, count validation, exact quantity comparison, duplicate keys, permissions, approval limits, and inventory adjustments remain rule-based.

Potential AI applications include summarizing investigation notes, suggesting a reason category, comparing recent variance descriptions, detecting semantically similar recurring issues, and identifying missing investigation questions.

Normal automation already provides scheduling, forms, validation, recount routing, approval evidence, reminders, Shopify synchronization, Slack notifications, and reporting. AI adds value only when staff must interpret unstructured notes and event context.

The recommended enhancement is an investigation assistant that reviews sanitized variance history and suggests a structured summary, likely category, recurrence pattern, and follow-up questions. It does not approve inventory changes or overwrite the investigator’s selected reason.

  • Trigger: A task enters Awaiting Reason or high-risk Quality Review.
  • AI input: SKU, anonymous location code, final variance, unit cost, prior reason codes, sanitized notes, movement timestamps, and normalized event exceptions.
  • System instruction: Analyze only supplied evidence, distinguish facts from suggestions, and never approve an adjustment.
  • Expected output: Valid JSON matching the configured schema.
  • Validation: Parse JSON, enforce allowed categories, verify confidence range, and limit string lengths.
  • Record update: Write to separate AI_Suggestion fields without changing human-entered reason or status.
  • Human review: Inventory lead accepts, edits, or rejects the suggestion.
  • Low confidence: Confidence below 0.75 creates follow-up questions but no category recommendation.
  • Prohibited data: Customer names, addresses, email addresses, payment data, credentials, and unrestricted evidence files.
  • Failure behavior: Continue with the normal investigation form when the AI service is unavailable.

Use this reusable system instruction:

You are an inventory variance analysis assistant.

Analyze only the evidence supplied in the user message. Do not invent events, counts, causes, or corrective actions. Distinguish observed facts from suggested explanations.

You may suggest an investigation category and follow-up questions. You must not approve an inventory adjustment, make an accounting decision, contact a customer, or change a workflow status.

Return only JSON that matches the supplied schema. If evidence is insufficient, use category OTHER, lower the confidence score, and explain what information is missing.

Use this user prompt template:

Review this warehouse cycle-count variance.

Current task:
{{ JSON.stringify($json.current_task) }}

Normalized Shopify events during the review window:
{{ JSON.stringify($json.shopify_events) }}

Prior sanitized variances for the same SKU:
{{ JSON.stringify($json.prior_variances) }}

Allowed categories:
RECEIVING_ERROR, PICK_SHORT, MISBINNED, DAMAGE,
RETURN_NOT_PROCESSED, BUNDLE_MAPPING, SKU_MAPPING,
SYSTEM_TIMING, COUNT_ERROR, OTHER

Identify:
1. A concise factual summary.
2. The most likely category, if supported.
3. Evidence supporting the suggestion.
4. Missing information.
5. Follow-up questions.
6. Whether the pattern appears recurring.
7. A confidence score from 0 to 1.

Do not approve or reject an inventory adjustment.

The required structured output is:

{
  "summary": "Five units were missing from the assigned bin after two matching counts.",
  "suggested_category": "MISBINNED",
  "supporting_evidence": [
    "First count and recount both recorded 79 units.",
    "The system snapshot was 84 units.",
    "A prior variance used the same reason category."
  ],
  "missing_information": [
    "Adjacent bins have not yet been checked."
  ],
  "follow_up_questions": [
    "Were nearby bins and receiving staging areas inspected?"
  ],
  "recurring_pattern": true,
  "confidence": 0.82,
  "human_review_required": true
}

In n8n, aggregate the sanitized context, send it to the approved AI model node with structured output enabled, parse the response, and validate every field. If parsing fails, write AI_Status as Failed Validation and continue with the ordinary investigation workflow.

Log task ID, model identifier, request time, token or usage measure when available, validation result, confidence, and reviewer disposition. Do not log full customer or credential data.

Benefits of the AI Enhancement

  • Reduces the time required to read several prior variance notes.
  • Produces more consistent summaries for quality reporting.
  • Suggests semantically similar recurring issues even when staff used different wording.
  • Identifies missing investigation steps before approval.
  • Supports prioritization by presenting evidence and uncertainty together.
  • Improves search and reporting over historical free-text notes.

These are AI-specific benefits. They do not replace the larger benefits already delivered by risk-based scheduling, form validation, recount rules, workflow ownership, event synchronization, approval routing, and reporting.

What Remains Rule-Based or Human-Controlled

  • Count comparison: Exact arithmetic remains deterministic.
  • Duplicate prevention: Event and task keys remain rule-based.
  • Recount requirement: Any configured variance follows an explicit rule.
  • Approval route: Value, quantity, and recurrence thresholds remain deterministic.
  • Final reason selection: The inventory lead reviews evidence and chooses the recorded cause.
  • Inventory adjustment: An authorized employee makes the final Shopify change.
  • Accounting treatment: Finance decides whether a variance requires a financial entry.
  • Customer communication: Customer service reviews the order context and controls outbound communication.
  • Safety or disciplinary decisions: Managers handle these under company policy rather than AI output.
  • Risk acceptance: The operations manager decides whether unresolved evidence is sufficient.

These controls remain human-controlled because they can affect inventory valuation, financial records, employees, customers, and audit evidence.

Estimating the Additional Value of AI

The representative core workflow produces approximately 43 variance exceptions per month. Without AI, assume each exception requires 10 minutes of investigation preparation and review. With AI, assume the same human remains responsible but saves three minutes reviewing prior notes and preparing a summary.

Additional time recovered: 43 × 3 ÷ 60 = 2.15 hours per month.

Additional labour capacity value: 2.15 × $32 = $68.80 per month.

Less representative AI usage allowance: $68.80 − $8 = $60.80 net monthly capacity value.

Representative process comparison with optional AI
Process Estimated monthly labour Human involvement
Original manual process 72 hours Full manual preparation, comparison, follow-up, and reporting.
Core automation 35.2 hours Physical counts, investigations, approvals, corrections, and exception review.
Core automation with AI Approximately 33.05 hours Same decisions, with faster review of unstructured history.

For planning, Larkspur assumes that 15 percent of AI suggestions may require material editing and 3 percent may fail validation or service delivery. These are pilot assumptions, not guaranteed performance. Actual correction and failure rates must be measured through sampled human review.

Testing Checklist

Use sample or sanitized data before processing real warehouse or customer information.

Required implementation tests
Test Expected result
Normal assignment and zero count variance Task closes and Item_Master updates.
Missing required field Form or n8n rejects the submission without changing the task.
Negative or decimal count Submission is rejected with a clear validation message.
Duplicate form submission Submission_Key prevents a second task update.
Duplicate Shopify event Event_Key is logged as duplicate and movement is not counted twice.
Unknown SKU Event enters the operations exception queue.
Failed authentication Workflow stops, records the error, and alerts the system owner.
Expired credential Credential is repaired before controlled replay.
Temporary API failure Workflow retries using the same idempotency key.
Unavailable approver Reminder and delegate rules execute.
Approval rejection Task enters Rejected and no adjustment confirmation is accepted.
Return for information Ownership returns to the inventory lead.
Reassignment New owner receives the task and audit history records the change.
Overdue item Dashboard and reminder workflow identify it.
Reminder timing No duplicate reminder is sent within the configured interval.
Escalation timing Delegate or manager receives the escalation after the threshold.
Same employee attempts recount Recount is rejected and task remains Awaiting Recount.
First and recount values disagree Task enters Manual Review.
Shopify movement during count Movement flag is set and variance is not automatically confirmed.
Failed file upload Investigation remains incomplete and can be recovered.
Failed folder creation Workflow retries and reports the missing folder.
Failed Slack notification Business record remains valid and notification retries separately.
Unauthorized approver Decision is rejected and logged.
Malformed AI output AI validation fails and normal human investigation continues.
Inaccurate AI suggestion Reviewer rejects or edits the suggestion without changing the source evidence.
AI service unavailable Core workflow continues without AI.
Successful approved adjustment Confirmation closes the task and updates the item history.
Reporting accuracy Dashboard totals reconcile with source task and event rows.
Audit record Task retains submitters, timestamps, decisions, external IDs, and evidence.
Retry behavior Temporary errors retry without duplicate destination records.

Ongoing Maintenance

The inventory control lead owns the business process. A technical operations employee is the backup automation owner. The quality analyst owns reason-code and recurrence reporting.

Maintenance schedule
Frequency Maintenance activity
Daily Review failed runs, stale count windows, overdue tasks, unprocessed form responses, and failed notifications.
Weekly Reconcile task totals, review recurring variances, sample approval evidence, and confirm Shopify synchronization freshness.
Monthly Archive old Movement_Ledger rows, review execution volume and cost, test a controlled failure, and update operational documentation.
Quarterly Review permissions, Slack application access, Google sharing, n8n users, delegates, reason codes, approval thresholds, and credential rotation requirements.
Semi-annually Test workbook restoration, replay procedures, integration credentials, and rollback documentation.
After a platform change Retest Shopify fields, API version, n8n nodes, Google headers, Slack destinations, and all critical workflow branches.
When an employee leaves Deactivate the user, remove application access, update ownership, and assign outstanding tasks.
AI pilot weekly Sample outputs, measure correction rate, inspect prohibited-data controls, and review usage cost.

Archiving is particularly important because Shopify event lines can grow faster than cycle-count records. Completed movement rows can be exported to a controlled archive while the workbook retains the rolling period needed for operations and reporting.

When to Move to Dedicated Software

The implementation remains appropriate while one warehouse can operate within the workbook’s performance, permission, and concurrency limits. It should be reassessed when several of these conditions appear:

  • Transaction or movement volume causes slow Google Sheets reads and writes.
  • Multiple warehouses require local workflows, separate time zones, or complex transfer counting.
  • Several counters need simultaneous mobile scanning and offline operation.
  • Bin-level inventory, lot tracking, serial numbers, expiry dates, or regulated traceability become mandatory.
  • Formal audit requirements demand database-enforced relationships and immutable logs.
  • Approval permissions become too detailed for form and spreadsheet controls.
  • Exception rates or custom workflow branches create excessive n8n maintenance.
  • Customer-facing or supplier-facing portals are required.
  • Real-time inventory reservation and warehouse execution must be coordinated in one platform.
  • Operations requires vendor-supported service levels and application support.
  • Security risk increases because too many users or integrations require workbook access.
  • Advanced forecasting, slotting, replenishment, labor management, or warehouse analytics become core requirements.

Potential next steps include a warehouse management system, inventory management platform, no-code operational database, or a custom application backed by PostgreSQL or another managed database. Migration is not automatic. The existing workflow can remain useful until measured limitations justify the change.

Implementation Checklist

  • Confirm cycle-count volume, ABC rules, risk factors, and approval thresholds.
  • Confirm Shopify, Google Workspace, n8n, Google Drive, and Slack as the selected tools.
  • Create production and test accounts, workbooks, forms, folders, and credentials.
  • Define administrators, counters, approvers, delegates, quality users, and customer-service owners.
  • Build Item_Master, Count_Tasks, Movement_Ledger, exception, user, configuration, and log structures.
  • Configure blind count, investigation, approval, and adjustment-confirmation intake.
  • Implement deterministic Item_Key, Task_ID, Event_Key, and Submission_Key values.
  • Connect Shopify products, inventory, orders, and fulfilments to n8n.
  • Map all source and destination fields.
  • Build nightly synchronization and weekly scheduling workflows.
  • Build count-start, first-count, recount, investigation, approval, and closure workflows.
  • Configure approval thresholds, sequential decisions, parallel quality review, and delegates.
  • Configure reminders, overdue rules, and escalation timing.
  • Configure Slack channels, user mappings, and notification suppression for normal events.
  • Create restricted Drive evidence folders and retention rules.
  • Create operational views, pivots, charts, reconciliation measures, and failure reports.
  • Apply least-privilege permissions and credential controls.
  • Install and test the n8n Code nodes and Shopify request configuration.
  • Test normal, duplicate, invalid, rejected, unavailable, failed, and recovered scenarios.
  • Pilot one warehouse zone before full activation.
  • Record representative implementation and recurring cost assumptions.
  • Replace savings assumptions with measured internal figures.
  • Add the optional AI enhancement only after the deterministic workflow is reliable.
  • Assign primary and backup maintenance owners.
  • Document the volume, security, workflow, and reporting criteria that would justify dedicated software.

Get a FREE
Proof of Concept
& Consultation

No Cost, No Commitment!