Skip to content

QRun-IO/qbit-wms

QBit: WMS

Version License Java

Warehouse Management System for QQQ Applications - Task-Centric, Perpetual Inventory

This QBit provides a complete WMS for QQQ applications, covering receiving, putaway, inventory management, order fulfillment (pick/pack/ship), returns processing, kitting, cycle counting, replenishment, and 3PL billing. The system is metadata-driven, multi-warehouse capable, and supports both brand-direct and 3PL operating models.

Core Capabilities

  • Task-Centric Work Engine: A unified wms_task table is the heart of the system. Every warehouse operation (putaway, picking, packing, counting, moving, replenishment, loading, QC inspection, kit assembly) creates directed work tasks. Workers interact through a single task queue. Supervisors monitor, reprioritize, and reassign tasks.
  • Perpetual Inventory Through Transactions: The wms_inventory_transaction table is the authoritative ledger. Every quantity change is recorded as an immutable transaction first. Inventory can be fully reconstructed from the transaction log at any point in time.
  • Receiving & Putaway: PO-based, blind, and ASN receiving with directed or manual putaway. QC inspection gates putaway when required.
  • Order Fulfillment: Configurable allocation (FEFO/FIFO/LIFO), wave-based pick release, cartonization, and short-pick resolution.
  • Shipping: Carrier label generation (parcel and LTL/FTL), manifesting, dock appointment scheduling, and ship confirmation.
  • Returns Processing: Full RMA lifecycle with inspection grading, disposition rules, and restock/scrap handling.
  • Kitting: Bill-of-materials-driven kit assembly flowing through the task engine with component deduction and finished kit creation.
  • Cycle Counting: ABC-based, location-based, SKU-based, and random counting with blind count support and supervisor variance approval.
  • Replenishment: Rule-based pick-face replenishment with configurable min/max thresholds.
  • 3PL Billing: Activity-based billing automatically captured from task completion hooks. Rate cards, invoicing, and storage snapshots.
  • Multi-Warehouse: Warehouse-scoped data isolation from day one via warehouse_id FK and RecordSecurityLock.
  • License Plate Tracking: First-class LPN entity for pallet-level operations and container lifecycle tracking.
  • 11 Dashboard Widgets: Task queue summary, worker productivity, fulfillment pipeline, SLA risk, inventory accuracy, billing dashboard, and more.

Open Source & Full Control

QBit WMS is 100% open source under AGPL v3. All data stays in your systems. No external WMS services required.

Architecture

Design Principles

  1. Task-centric work engine: A unified wms_task table sits at the heart of the system. Every warehouse operation, from putaway to picking to cycle counting, creates directed work tasks. Workers interact with the system through a single task queue. Supervisors monitor, reprioritize, and reassign tasks. This gives the warehouse a single source of truth for "what work needs to happen" and "who is doing it."

  2. Perpetual inventory through transactions: The wms_inventory_transaction table is the authoritative ledger. Inventory quantities on wms_inventory are never modified directly. Every quantity change is recorded as an immutable transaction first, and wms_inventory.quantity_on_hand is updated as a consequence. Inventory can be fully reconstructed from the transaction ledger at any point in time, and every unit movement is traceable to the task, user, and timestamp that caused it.

  3. Completion hook dispatcher: When a task is completed, a TaskCompletionDispatcher switches on task_type and calls the appropriate type-specific handler. Each handler contains the full chain of side effects (deduct inventory, update order lines, log transactions, create downstream tasks, capture billing activities). This explicit dispatcher pattern keeps completion logic testable in isolation and avoids unintended triggering from non-completion task updates.

Technology Stack

  • Java 17+ with QQQ backend modules
  • QQQ Framework: Entities, processes, widgets, permissions, API layer
  • Database: RDBMS through QQQ's backend abstraction
  • MemoryRecordStore: Full test suite runs in-memory (no database required)

Module Organization

qbit-wms/
  src/main/java/com/kingsrook/qbits/wms/
    WmsQBitConfig.java              -- Configuration (backend, strategies, companion QBits)
    WmsQBitProducer.java            -- Gen 2 QBitMetaDataProducer (auto-discovers all entities)
    core/                           -- Warehouse, Zone, Location, Item, ItemCategory, UOM, Client, Vendor
    tasks/                          -- Unified task engine (wms_task, task type config, task processes)
    receiving/                      -- POs, Receipts, ASNs, Putaway rules
    inventory/                      -- Tracking, Counting, Adjustments, Holds, License Plates
    fulfillment/                    -- Orders, Waves, Cartons, Allocation, Kitting
    shipping/                       -- Shipments, Manifests, Dock Appointments, Labels
    returns/                        -- RMA, Inspection, Disposition
    billing/                        -- 3PL activity capture, Rate Cards, Invoicing
    widgets/                        -- 11 dashboard widgets (producers + renderers)

Quick Start: Complete Application

Below is a full working example showing how to bootstrap a QQQ application with the WMS QBit. This assumes you have an RDBMS backend already configured.

import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
import com.kingsrook.qbits.wms.WmsQBitConfig;
import com.kingsrook.qbits.wms.WmsQBitProducer;

public class MyWarehouseApp
{
   public static void main(String[] args) throws Exception
   {
      ///////////////////////////
      // 1. Create QInstance   //
      ///////////////////////////
      QInstance qInstance = new QInstance();

      //////////////////////////////////
      // 2. Add your database backend //
      //////////////////////////////////
      // Register your RDBMS backend with QQQ (PostgreSQL, MySQL, H2, etc.)
      // The backend name you choose here is what you pass to WmsQBitConfig.
      // See QQQ documentation for backend configuration details.

      ////////////////////////////////////
      // 3. Configure the WMS QBit      //
      ////////////////////////////////////
      WmsQBitConfig config = new WmsQBitConfig()
         .withBackendName("rdbms")
         .withDefaultAllocationStrategy("FEFO")
         .withDefaultPickStrategy("BATCH")
         .withDefaultPutawayStrategy("DIRECTED")
         .withBlindCountDefault(true)
         .withAdjustmentApprovalThreshold(100)
         .withCycleCountVarianceThreshold(new java.math.BigDecimal("5.0"));

      ////////////////////////////////////
      // 4. Produce and register        //
      ////////////////////////////////////
      WmsQBitProducer producer = new WmsQBitProducer()
         .withQBitConfig(config);

      MetaDataProducerMultiOutput output = producer.produce(qInstance);
      output.addSelfToInstance(qInstance);

      // At this point, qInstance contains all 46 WMS tables, 47 processes,
      // 11 widgets, security locks, and permission rules.

      ////////////////////////////////////
      // 5. Start your QQQ application  //
      ////////////////////////////////////
      // Use standard QQQ application startup (e.g., Javalin middleware).
   }
}

With a custom carrier and accounting integration

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withCarrierAdapter(new UpsCarrierAdapter())        // your CarrierAdapter implementation
   .withAccountingAdapter(new QuickBooksAdapter());     // your AccountingAdapter implementation

WmsQBitProducer producer = new WmsQBitProducer()
   .withQBitConfig(config);

MetaDataProducerMultiOutput output = producer.produce(qInstance);
output.addSelfToInstance(qInstance);

Getting Started

Prerequisites

  • Java 17+
  • Maven 3.8+
  • QQQ Application (this is a QBit, not a standalone application)

Usage

Maven dependency

<dependency>
    <groupId>com.kingsrook.qbits</groupId>
    <artifactId>qbit-wms</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

If you are using the QQQ BOM POM, the version is managed for you:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.kingsrook.qqq</groupId>
            <artifactId>qqq-bom-pom</artifactId>
            <version>${qqq.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Minimal setup

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("yourBackendName");

WmsQBitProducer producer = new WmsQBitProducer()
   .withQBitConfig(config);

MetaDataProducerMultiOutput output = producer.produce(qInstance);
output.addSelfToInstance(qInstance);

Default security locks (warehouseId DENY + clientId ALLOW) and permission rules are applied automatically in postProduceActions.

With custom security locks

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("yourBackendName")
   .withRecordSecurityLocks(List.of(
      new RecordSecurityLock()
         .withFieldName("warehouseId")
         .withSecurityKeyType("warehouseAccess")
         .withNullValueBehavior(RecordSecurityLock.NullValueBehavior.DENY),
      new RecordSecurityLock()
         .withFieldName("clientId")
         .withSecurityKeyType("clientAccess")
         .withNullValueBehavior(RecordSecurityLock.NullValueBehavior.ALLOW)
   ));

Configuration Reference

All configuration is provided through WmsQBitConfig. The only required field is backendName.

Field Type Default Description
backendName String (required) Name of the QQQ backend where all WMS tables are stored. Must match a backend already registered on your QInstance.
tableNamePrefix String null Optional prefix prepended to all WMS table names (e.g., "acme" produces acme_wmsWarehouse). Useful for running multiple WMS instances in the same database.
defaultAllocationStrategy String "FIFO" Default inventory allocation strategy. Options: FEFO (first expired, first out), FIFO (first in, first out), LIFO (last in, first out).
defaultPickStrategy String "BATCH" Default pick strategy for wave release. Options: DISCRETE, BATCH, ZONE, CLUSTER, WAVE.
defaultPutawayStrategy String "DIRECTED" Default putaway strategy for received goods. Options: DIRECTED (system-recommended locations), MANUAL (operator chooses).
adjustmentApprovalThreshold Integer 100 Inventory adjustments with a quantity above this value require supervisor approval.
cycleCountVarianceThreshold BigDecimal 5.0 Variance percentage that triggers a recount during cycle counting.
blindCountDefault Boolean true When true, expected quantities are hidden from counters during cycle counts. Reduces confirmation bias.
recordSecurityLocks List<RecordSecurityLock> auto Custom record security locks applied to all WMS tables. When null, defaults to warehouseId (DENY on null) and clientId (ALLOW on null).
carrierAdapter CarrierAdapter DefaultCarrierAdapter Pluggable carrier integration for label generation and rate lookup. The default adapter returns synthetic tracking numbers.
accountingAdapter AccountingAdapter DefaultAccountingAdapter Pluggable accounting system integration for invoice sync. The default adapter returns synthetic external IDs.
opensearchHost String null OpenSearch/ElasticSearch host for quick-search integration (used with the qbit-quick-search companion).
opensearchPort Integer null OpenSearch/ElasticSearch port.
opensearchIndexName String null OpenSearch/ElasticSearch index name.

Configuration examples

3PL operation with FEFO allocation and strict counting:

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withDefaultAllocationStrategy("FEFO")
   .withCycleCountVarianceThreshold(new BigDecimal("2.0"))
   .withAdjustmentApprovalThreshold(50)
   .withBlindCountDefault(true);

Brand-direct with manual putaway and OpenSearch:

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withDefaultPutawayStrategy("MANUAL")
   .withDefaultPickStrategy("DISCRETE")
   .withOpensearchHost("search.example.com")
   .withOpensearchPort(9200)
   .withOpensearchIndexName("wms-items");

Multi-instance with table prefix:

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withTableNamePrefix("east");
// Tables become: east_wmsWarehouse, east_wmsTask, etc.

How the Task Engine Works

The task engine is the central nervous system of the WMS. Every physical warehouse operation -- receiving, putaway, picking, packing, counting, moving, replenishing, loading, QC inspection, kit assembly -- is modeled as a task in the wms_task table. Here is a complete walkthrough of a real-world receiving-to-shipping flow.

End-to-end walkthrough: receiving through shipping

RECEIVING CLERK (desktop)
  1. Opens "Receive Against PO" process
  2. Scans PO barcode -> system loads PO lines
  3. Scans each item, enters quantity received
  4. Clicks "Complete Receipt"
     -> System creates PUTAWAY tasks for each received line
     -> If item requires QC, creates QC_INSPECT task (PUTAWAY stays ON_HOLD)

FORKLIFT DRIVER (RF gun / mobile)
  1. Opens "Get Next Task" -> system assigns highest-priority PUTAWAY task
  2. Sees: "Go to Receiving Dock, pick up SKU-12345, qty 48"
  3. Scans source location (receiving staging area)
  4. Scans item barcode (system validates correct item)
  5. Drives to directed putaway location (e.g., A-03-02-B)
  6. Scans destination location (system validates)
  7. Confirms quantity -> task status set to COMPLETED
     -> Inventory record created at location A-03-02-B
     -> Inventory transaction logged to wms_inventory_transaction
     -> Receipt line updated to PUTAWAY_COMPLETE
     -> PO status cascades to RECEIVED when all lines done
     -> Billing activity captured for 3PL clients

ORDER FULFILLMENT (system / desktop)
  1. Orders arrive via API -> status PENDING
  2. "Allocate Orders" process reserves inventory -> status ALLOCATED
  3. "Create Wave" groups orders by carrier cutoff -> wave created
  4. "Release Wave" generates PICK tasks -> status PICK_RELEASED

PICKER (RF gun / mobile)
  1. "Get Next Task" -> system assigns next PICK task
  2. Walks to pick location, scans location barcode
  3. Scans item barcode (system validates correct SKU)
  4. Confirms quantity picked
  5. Task COMPLETED
     -> Inventory deducted via transaction
     -> Order line pick quantity updated
     -> When all picks done for an order -> PACK task auto-created

PACKER (pack station / desktop)
  1. Scans order at pack station
  2. Scans each item into carton (system verifies against order lines)
  3. System confirms all items present and accounted for
  4. "Generate Shipping Label" -> carrier label printed
     -> Carton status set to LABELED

SHIPPING (dock door)
  1. "Ship Confirm" at dock door -> LOAD task completed
  2. Order status -> SHIPPED
  3. Tracking number pushed to source system
  4. Manifest created for end-of-day carrier pickup

Task lifecycle

Every task moves through a standard set of statuses:

PENDING -> ASSIGNED -> IN_PROGRESS -> COMPLETED
                  \-> PAUSED -> IN_PROGRESS
                  \-> ON_HOLD -> PENDING
                  \-> CANCELLED

The TaskCompletionDispatcher is the key mechanism. When any task reaches COMPLETED status, the dispatcher routes to the appropriate type-specific handler:

Task Type Completion Handler Side Effects
PUTAWAY PutawayTaskCompletionHandler Create inventory, log transaction, update receipt line, capture billing
PICK PickCompletionHandler Deduct inventory, update order line, create PACK task when all picks done
PACK PackCompletionHandler Update carton status, verify all items packed
COUNT CountTaskCompletionHandler Record count, calculate variance, trigger recount or approval
MOVE MoveTaskCompletionHandler Transfer inventory between locations, log transaction
REPLENISH ReplenishCompletionHandler Move inventory from bulk to pick face, log transaction
LOAD LoadCompletionHandler Confirm shipment, update order to SHIPPED
QC_INSPECT QcInspectTaskCompletionHandler Release or reject inventory, unblock downstream PUTAWAY tasks
KIT_ASSEMBLE KitAssembleCompletionHandler Deduct components, create finished kit inventory
RETURN_PUTAWAY ReturnPutawayCompletionHandler Restock returned items, log transaction

Mobile Scanning Workflows

Mobile workers (forklift drivers, pickers, packers) interact with the WMS through a repeating scan-verify-confirm loop built on two core processes: getNextTask and completeTask.

The universal pattern

1. Worker calls GET NEXT TASK
   -> System finds the highest-priority pending task
      matching the worker's zone and equipment qualifications
   -> Task status moves from PENDING to ASSIGNED

2. Worker navigates to the source location
   -> Scans the LOCATION barcode (system validates it matches the task)

3. Worker identifies the item
   -> Scans the ITEM barcode (system validates correct SKU, lot, serial)

4. Worker confirms the quantity
   -> Enters or confirms the quantity to move/pick/count

5. Worker navigates to the destination (if applicable)
   -> Scans the DESTINATION location barcode (system validates)

6. Worker calls COMPLETE TASK
   -> TaskCompletionDispatcher fires the type-specific handler
   -> All side effects execute (inventory updates, downstream tasks, billing)
   -> Worker returns to step 1

Scan verification

Each task type has configurable scan requirements set in wms_task_type_config per warehouse. Common configurations:

Setting Purpose
Source location scan Confirms the worker is at the correct pick/source location
Item barcode scan Validates the correct SKU is being handled
Destination location scan Confirms putaway/move to the correct target location
Quantity confirmation Worker enters or confirms the quantity moved

Scan requirements can be adjusted per warehouse and task type. For example, a high-value warehouse might require all four scans, while a small operation might only require item and quantity confirmation.

Supervisor overrides

Supervisors have access to additional task management processes that operate outside the mobile scan loop:

  • reassignTask: Move a task from one worker to another
  • reprioritizeTask: Change task priority to reorder the queue
  • holdTask / releaseTask: Temporarily freeze and unfreeze tasks
  • cancelTask: Cancel a task with type-specific reversal logic (e.g., deallocate inventory for cancelled picks)
  • staleTaskCheck: Scheduled process that detects and unassigns abandoned tasks

Data Model

Tables (46 total)

Core

Table Description
wmsWarehouse Top-level organizational unit with timezone and address
wmsZone Logical grouping of locations (Bulk, Pick Face, Cold, Hazmat, Staging, etc.)
wmsLocation Individual storage positions (bins, shelves, floor spots) with capacity tracking
wmsItem Product/SKU master with lot, serial, expiration tracking and velocity classification
wmsItemCategory Hierarchical item categories with default storage requirements
wmsUnitOfMeasure Item-specific UOM conversions (Each, Case, Pallet, Custom)
wmsClient 3PL client records (empty in brand-direct mode)
wmsVendor Supplier/vendor master with lead time and contact info

Task Engine

Table Description
wmsTask Central work table for all warehouse operations (10 task types, 8 statuses)
wmsTaskTypeConfig Configurable defaults per task type per warehouse (priority, equipment, auto-assign, scan requirements)

Receiving

Table Description
wmsPurchaseOrder Inbound purchase orders with vendor reference and dock door assignment
wmsPurchaseOrderLine PO line items with over/under receiving tolerance percentages
wmsReceipt Receiving events (PO-based, blind, ASN, return, cross-dock)
wmsReceiptLine Receipt line items with QC status tracking through putaway
wmsAsn Advance ship notices with carrier and tracking info
wmsAsnLine ASN line-level detail for pre-validation and variance detection

Inventory

Table Description
wmsInventory Core inventory record at item+location+lot+status intersection
wmsInventoryTransaction Immutable append-only ledger of every inventory movement
wmsCycleCount Count plans (full, ABC-based, location-based, SKU-based, random)
wmsCycleCountLine Individual count lines with variance tracking and supervisor approval
wmsInventoryHold Hold records for QC, damage, recall, regulatory, and custom holds
wmsLicensePlate First-class LPN entity for pallets, cases, and totes

Fulfillment

Table Description
wmsOrder Customer orders with carrier, service level, and ship-to address
wmsOrderLine Order line items with allocation, pick, pack, ship quantity tracking
wmsWave Wave groupings for coordinated pick release (carrier cutoff, priority, zone)
wmsCarton Physical shipping containers with dimensions, weight, and tracking
wmsCartonLine Items packed into cartons with lot and serial tracking
wmsCartonType Available box/container sizes for cartonization
wmsAllocationRule Configurable allocation strategy rules (FEFO, FIFO, LIFO) per category
wmsKitBom Kit bill of materials linking finished items to components

Shipping

Table Description
wmsShipment Shipment records with carrier, tracking, and parcel/LTL/FTL mode
wmsShipmentOrder Many-to-many junction for split shipments and order consolidation
wmsManifest End-of-day carrier manifests
wmsDockAppointment Inbound/outbound dock door scheduling

Returns

Table Description
wmsReturnAuthorization RMA records with reason codes and lifecycle status
wmsReturnAuthorizationLine Return line items with authorized quantities and expected condition
wmsReturnReceipt Physical receipt of returned goods
wmsReturnReceiptLine Inspected return items with grade, disposition, and restock location

Billing

Table Description
wmsBillingRateCard Client-specific rate card definitions with effective dates
wmsBillingRate Individual rates per activity type (receiving, storage, pick, pack, ship, etc.)
wmsBillingActivity Automatically captured billable events from task completion hooks
wmsInvoice Generated invoices with billing period, totals, and accounting sync
wmsInvoiceLine Invoice line items aggregated by activity type

Configuration

Table Description
wmsPutawayRule Configurable directed putaway rules (zone type, velocity, category, storage)
wmsReplenishmentRule Min/max thresholds for pick-face replenishment by item and location

Processes (47)

Receiving

Process Description
receiveAgainstPO Receive goods against a PO with tolerance validation, QC check, and PUTAWAY task creation
blindReceive Receive unexpected goods without a PO
receiveASN Receive against an advance ship notice with per-line variance detection
directedPutaway Evaluate configurable rules to recommend directed putaway locations
qualityInspection QC inspection gate for received goods

Inventory

Process Description
wmsCycleCount Generate count plans (ABC, random, location-based) and create COUNT tasks
approveCountVariance Supervisor reviews count variances, approves adjustments or triggers recounts
inventoryAdjustment Manual adjustment with reason codes and optional supervisor approval
inventoryMove Request an inventory relocation, creates MOVE task
inventoryHold Place inventory on hold (QC, damage, recall, regulatory)
inventoryRelease Release held inventory back to available status

Fulfillment

Process Description
allocateOrders Reserve inventory using FEFO/FIFO/LIFO allocation rules
createWave Create a wave grouping for coordinated pick release
releaseWave Release a wave and create optimized PICK tasks
packOrder Pack picked items into cartons with scan verification
kitAssembly Assemble kits from BOM components via KIT_ASSEMBLE tasks
shortPickResolution Handle short picks with reallocation from alternate locations or backorder
voidCarton Void a packed carton and return items to the pick queue
cancelWave Cancel a wave with allocation reversal and order status rollback
holdOrder Place an order on hold with associated task hold management
releaseOrder Release a held order and resume fulfillment

Shipping

Process Description
generateShippingLabel Create carrier labels for parcel; generate BOL for LTL/FTL
manifestShipments Create and transmit end-of-day carrier manifests
shipConfirm Confirm shipment departure and push tracking to order source

Returns

Process Description
createRMA Generate return authorization from original order
receiveReturn Receive returned items and create QC_INSPECT tasks
inspectReturn Inspect and grade returned items per quality criteria
dispositionReturn Apply disposition rules (restock, scrap, return to vendor)

Task Management

Process Description
getNextTask Mobile: assign and return the next available task for a worker
completeTask Mobile: complete a task with scan verification and fire completion dispatcher
reassignTask Supervisor: move a task to a different worker
reprioritizeTask Supervisor: change task priority
holdTask Supervisor: place a task on hold
releaseTask Supervisor: release a held task back to the queue
cancelTask Supervisor: cancel a task with type-specific reversal logic
pauseTask Pause a task in progress
resumeTask Resume a paused task
staleTaskCheck Scheduled: find and unassign stuck or abandoned tasks

Billing

Process Description
generateInvoice Create invoice for a billing period with rate card application
storageSnapshot Scheduled nightly: calculate daily storage billing per client
syncInvoiceToAccounting Push invoices to accounting systems (QuickBooks, Xero)

Advanced / Scheduled

Process Description
abcAnalysis Reclassify SKU velocity (A/B/C) from order history
autoAllocateAndRelease Scheduled: allocate new orders and release picks automatically
autoAssignTasks Scheduled: auto-assign pending tasks to available workers by zone and equipment
expirationAlertCheck Scheduled: flag inventory approaching expiration
lowStockAlertCheck Scheduled: flag items below reorder point
replenishCheck Scheduled: detect low pick-face inventory and create REPLENISH tasks

Dashboard Widgets (11)

Task Dashboard

Widget Type Description
wmsTaskQueueSummary Multi-Statistics Task counts by type and status
wmsTaskAging Bar Chart Pending/assigned tasks by age bucket
wmsActiveWorkers Table Active workers with current task and time on task
wmsWorkerProductivity Table Per-worker task completion metrics

Operations Dashboard

Widget Type Description
wmsOrdersToday Multi-Statistics Orders received / picked / packed / shipped today
wmsFulfillmentPipeline Stacked Bar Orders by status from pending to shipped
wmsSlaRisk Table Orders at risk of missing ship-by date

Inventory Dashboard

Widget Type Description
wmsInventorySummary Statistics Total SKUs, units, locations, and utilization percentage
wmsLowStockAlerts Table Items below reorder point
wmsInventoryAccuracy Statistics Accuracy percentage from recent cycle counts

Billing Dashboard

Widget Type Description
wmsBillingDashboard Multi-Statistics Revenue, unbilled activities, and invoice status

Companion QBit Integration

qbit-quick-search (ElasticSearch)

Index wmsItem, wmsOrder, wmsInventory, and wmsTask for full-text global search.

qbit-webhooks

Supported event types:

Event Fires When
wms.task.assigned Task assigned to a worker
wms.task.completed Task completed
wms.task.short Task completed with short quantity
wms.task.cancelled Task cancelled
wms.order.created / wms.order.shipped Order created or shipped
wms.receipt.completed Receipt fully received and put away
wms.inventory.adjusted Inventory adjustment recorded
wms.inventory.low Item fell below reorder point
wms.return.dispositioned Return disposition complete (enables refund processing)
wms.shipment.confirmed Shipment confirmed with tracking

qbit-easypost-tracking (Carrier Integration)

Carrier label generation, rate shopping, and tracking number retrieval for parcel shipments. The WMS defines a CarrierAdapter interface so multiple carrier QBits can be plugged in.

Extending the WMS

The WMS is designed to be extended through two adapter interfaces and the QQQ metadata system.

Implementing a custom CarrierAdapter

The CarrierAdapter interface (com.kingsrook.qbits.wms.shipping.CarrierAdapter) defines two methods for carrier integration. The default DefaultCarrierAdapter returns synthetic tracking numbers -- replace it for production use.

import com.kingsrook.qbits.wms.shipping.CarrierAdapter;
import java.math.BigDecimal;

public class UpsCarrierAdapter implements CarrierAdapter
{
   @Override
   public String generateLabel(String carrier, String serviceLevel,
      String shipFromAddress, String shipToAddress, BigDecimal weightLbs)
   {
      // Call UPS Shipping API to create a shipment and generate a label.
      // Return the tracking number assigned by UPS.
      UpsClient client = new UpsClient(apiKey);
      UpsShipment shipment = client.createShipment(
         serviceLevel, shipFromAddress, shipToAddress, weightLbs);
      return shipment.getTrackingNumber();
   }

   @Override
   public BigDecimal getRate(String carrier, String serviceLevel, BigDecimal weightLbs)
   {
      // Call UPS Rating API to get a shipping rate quote.
      UpsClient client = new UpsClient(apiKey);
      return client.getRate(serviceLevel, weightLbs);
   }
}

Register it on the config:

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withCarrierAdapter(new UpsCarrierAdapter());

Implementing a custom AccountingAdapter

The AccountingAdapter interface (com.kingsrook.qbits.wms.billing.AccountingAdapter) defines one method for syncing invoices to an external accounting system. The default DefaultAccountingAdapter returns synthetic external IDs.

import com.kingsrook.qbits.wms.billing.AccountingAdapter;
import java.math.BigDecimal;

public class QuickBooksAdapter implements AccountingAdapter
{
   @Override
   public String syncInvoice(Integer invoiceId, String invoiceNumber, BigDecimal total)
   {
      // Call QuickBooks API to create an invoice in the accounting system.
      // Return the QuickBooks invoice ID for cross-reference.
      QBooksClient qb = new QBooksClient(oauthToken);
      QBooksInvoice invoice = qb.createInvoice(invoiceNumber, total);
      return invoice.getId();
   }
}

Register it on the config:

WmsQBitConfig config = new WmsQBitConfig()
   .withBackendName("rdbms")
   .withAccountingAdapter(new QuickBooksAdapter());

Adding new task types

The task engine is designed around a dispatcher pattern. Adding a new task type conceptually involves:

  1. Define the task type: Add a new value to the task type enumeration (e.g., QUALITY_AUDIT).

  2. Create a completion handler: Implement a handler class that defines what happens when a task of this type is completed. Follow the pattern of existing handlers like PutawayTaskCompletionHandler or PickCompletionHandler. Each handler receives the completed task record and executes the appropriate side effects (inventory changes, status updates, downstream task creation, billing capture).

  3. Register with the dispatcher: Add the new task type to TaskCompletionDispatcher so it routes completed tasks to your handler.

  4. Configure per warehouse: Add a wmsTaskTypeConfig record for each warehouse to control priority, equipment requirements, auto-assignment behavior, and scan requirements for the new task type.

The dispatcher pattern ensures that each task type's completion logic is isolated and independently testable.

Testing

mvn test                    # Run all tests
mvn test -Dtest=BaseTest    # Verify QBit produces metadata

Coverage

  • All tests run in-memory via MemoryRecordStore (no database required)
  • Test classes cover all entities, processes, widgets, completion hooks, and task dispatching
mvn org.jacoco:jacoco-maven-plugin:0.8.11:prepare-agent test \
    org.jacoco:jacoco-maven-plugin:0.8.11:report
# Report at: target/site/jacoco/index.html

Phased Delivery

Phase Tables Processes Widgets Focus
1 12 14 10 Core + Task Engine + Inventory
2 8 5 1 Receiving + Putaway
3 8 9 2 Fulfillment (Pick/Pack/Kit)
4 4 3 0 Shipping
5 4 4 0 Returns + Kitting
6 5 4 5 3PL Billing
7 4 6+ 6 Replenishment, Analytics, Optimization
Total 46 47 11

Documentation

  • QQQ Wiki - Framework documentation
  • QBit Development Guide - How QBits work
  • Design Spec - Full data model and architecture in QRun-IO/specs/2026-03-28-qbit-wms-design-v2.md

Contributing

QBit WMS is open source and welcomes contributions.

About Kingsrook

QBit WMS is built by Kingsrook - making engineers more productive through intelligent automation and developer tools.

License

This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.

About

QQQ Application QBit: Warehouse Management System (WMS) with unified task engine and perpetual inventory

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages