URL class: VirtualInventory — /Windward/WebAPI/VirtualInventory/...

Source of truth: ServerMethodsVirtualInventory.pas, CM_VirtualInventory.pas, AO_VirtualInventory.pas.


What virtual inventory is

Virtual inventory is a supplier catalogue: parts a supplier can sell you that do not exist as stocked inventory records in System Five. It is stored in the transfer file and keyed by the pair (supplier, supplier part number).

Its main job is to feed part creation: when a real inventory part is inserted through POST /Inventory/addInventory, the service looks the identifiers up in the virtual warehouse and copies matching catalogue data onto the new part (see Inventory). Loading a supplier's catalogue here first therefore enriches every part you subsequently create from it.


Operations

VerbPathDelphi methodEnvelope
GET/VirtualInventory/VirtualInventory_HandshakeVirtualInventory_Handshakead hoc
GET/VirtualInventory/GetVirtualInventoryRecordCountGetVirtualInventoryRecordCountad hoc
GET/VirtualInventory/VirtualInventory/{VirtualPartUnique}VirtualInventoryAPIResponse + VirtualInventory
GET/VirtualInventory/VirtualInventoryBySupplier/{SupplierUnique}/{SupplierPartNumber}VirtualInventoryBySupplierAPIResponse + VirtualInventory
POST/VirtualInventory/addVirtualInventoryupdateaddVirtualInventoryAPIResponse + VirtualInventory

Note the handshake's operation id in Swagger is Inventory_Handshake, the same as the stocked-inventory class. The path is what disambiguates them.


POST /VirtualInventory/addVirtualInventory

Request shape

{
  "ConnectionInfo": { "TerminalNumber": 1 },
  "Inventory": [
    {
      "VirtualPartUnique": 0,
      "SupplierUnique": 42,
      "SupplierPartNumber": "SUP-991",
      "PartNumber": "WIDGET-1",
      "ItemNumber": "WID1",
      "Description": "Blue widget",
      "Category": "01.02",
      "BrandName": "Acme",
      "Wholesale_Price": 12.50,
      "List_Price": 29.99,
      "Retail_Price": 24.99,
      "Min_Advt_Price": 22.99,
      "Weight": 1.2,
      "Size_1": "", "Size_2": "", "Size_3": "",
      "Barcode_1": "0123456789012",
      "CountryCode": "CA",
      "Date": "2026-09-01T00:00:00"
    }
  ]
}

Note the request array is named Inventory, not VirtualInventory — the response array is named VirtualInventory. They differ.

Match rule — both identifiers are mandatory

Default operation is NoOp; this endpoint refuses rather than guesses.

OrderField(s)Index
1VirtualPartUniquekey 0
2SupplierUnique and SupplierPartNumberkey 3

Missing either half of the pair when there is no unique:

No Supplier Unique provided. Insufficient info to update or insert; Inventory record skipped
No Supplier Part Number provided. Insufficient info to update or insert; Inventory record skipped

Because the (supplier, supplier part number) pair is a real natural key, addVirtualInventory is properly idempotent as long as you always send both. This is the cleanest write endpoint in the API for repeated catalogue loads.

Two additional guards, applied before the write

Unlike most domains, virtual inventory validates in AddUpdateRecord as well as in the matcher, and both guards set ActionSuccess: false and downgrade the operation to a no-op:

GuardMessage
The SupplierUnique must resolve to a real supplier accountThe Supplier does not exist, record not created.
On update, the incoming ItemNumber and PartNumber must be consistent with the matched recordAn error occurred during the update of the record. The Item Number and Part Number of the passed item are not the same.

The second guard prevents a supplier part number from being silently repointed at a different part. If you genuinely need to repoint it, delete and re-create the catalogue entry in System Five.

ActionResult values

ValueActionSuccessMeaning
Inserted RecordtrueCreated
Updated RecordtrueUpdated
An error occurred during the insert of the recordfalseWrite failed
An error occurred during the update of the recordfalseWrite failed
The Supplier does not exist, record not created.falseSkipped
... Item Number and Part Number of the passed item are not the same.falseSkipped
No Supplier Unique provided. ...falseSkipped
No Supplier Part Number provided. ...falseSkipped

Unlike stocked inventory, virtual inventory's no-op branch correctly reports ActionSuccess: false. You can trust the flag here.

Query parameters

Fields=... and DetailedResponse=Y behave as described in Common Parameters.


GET /VirtualInventory/VirtualInventory/{VirtualPartUnique}

0 returns everything. Supports Fields, PageSize and PageNumber (both or neither; PageNumber is 1-based).

Supplier catalogues are frequently very large — this is one of the endpoints where an unpaginated fetch will genuinely exhaust memory. Always paginate, and remember the forward-scan cost of deep page numbers described in Customers.


GET /VirtualInventory/VirtualInventoryBySupplier/{SupplierUnique}/{SupplierPartNumber}

Fetches the single catalogue entry for a (supplier, supplier part number) pair, using index 3 directly.

GET /Windward/WebAPI/VirtualInventory/VirtualInventoryBySupplier/42/SUP-991

Both parameters are path segments, in that order, and both are declared as strings in the Swagger despite SupplierUnique being numeric.

Practical notes:

  • SupplierPartNumber goes in the URL path, so it must be percent-encoded. Supplier part numbers containing /, #, ? or spaces will break the route or be misparsed — encode them, and be aware that an encoded / may still be rejected by the dispatcher. For those parts, fall back to fetching by VirtualPartUnique.
  • Supports Fields.
  • No match returns IsSuccess: false with an empty array, which is the same shape as a failure — see Response Envelopes and Error Semantics.

This is the right endpoint for a "does this supplier part exist in the catalogue?" check before creating a stocked part from it.


GET /VirtualInventory/GetVirtualInventoryRecordCount

{ "SystemFive API Record Count": "VirtualInventory", "Record Count": "204118" }

Computed by walking the file. On a large supplier catalogue this is a slow call held under the service's global lock — call it once per sync, not per page.


Field reference

Identity

FieldTypeNotes
VirtualPartUniqueintegerMatch key 1
SupplierUniqueintegerMatch key 2a. Required. Must resolve to a real supplier
SupplierPartNumberstringMatch key 2b. Required
PartUniqueintegerLink to a stocked inventory part, when one exists
PartNumber, ItemNumberstringConsistency-checked on update
CategorystringLedger-style category number
Brand, BrandNameinteger / string

Descriptive

Description, Size_1, Size_2, Size_3, Weight, CountryCode, Date

Pricing

FieldMeaning
Wholesale_PriceSupplier cost
List_PriceSupplier list price
Retail_PriceSuggested retail
Min_Advt_PriceMinimum advertised price
Foreign_PriceForeign-currency price

Barcodes

Barcode_1, Barcode_2, Barcode_3, Barcode_4 — four fixed slots, not an array. This differs from stocked inventory, where Barcodes is a child collection of unbounded size.