View Categories

Serial Numbers

4 min read

On a serial-tracked product each physical unit is a row of its own, with its own code, cost, acquisition date and status. That is what lets you say not just “two cameras went out” but which two, and it is what the send and return screens move around. These fields let you list, add, change and remove them.

They all need a token holding SalesIgniter_Rental::serial.

Listing serials #

query WhatIsOnTheShelf {
  rentalSerialNumbers(
    filter: { sku: { eq: "CANON-R6" }, status: { eq: "available" } }
    sort: { serial_number: ASC }
    pageSize: 50
  ) {
    total_count
    items {
      id
      serial_number
      sku
      product_name
      status
      cost
      date_acquired
      notes
      source_code
      reservation_id
      maintenance_ticket_id
    }
    page_info { current_page total_pages }
  }
}
FilterNotes
product_id, sku{eq} or {in}. Everything for one product.
serial_number{match: "CR6"} — a substring search, for a search box.
status{eq} or {in}, using the stored status string.
source_codeWhich depot the unit lives at, on an MSI store.
reservation_idThe units out on one particular booking.
date_acquired{from, to} — useful for depreciation and fleet-age reporting.

Sort on id, serial_number, product_id, status, date_acquired or cost. Fetch one with rentalSerialNumber(id: 5012).

Status #

The status you read back is one of a fixed set — AVAILABLE, RENTED, MAINTENANCE, RETIRED, or UNKNOWN for a value this version does not have a name for. A unit is only rentable when it is AVAILABLE.

You rarely need to set it by hand. Sending a unit out moves it to RENTED and returning it moves it back; putting it on a maintenance ticket moves it to MAINTENANCE and takes it off the rentable fleet until the ticket is closed. Set it yourself mainly to retire a unit.

Two related fields tell you why a unit is unavailable: reservation_id is the booking it is out on, and maintenance_ticket_id is the ticket holding it. Both are null for a unit on the shelf.

Adding units #

mutation AddOne {
  createRentalSerialNumber(input: {
    sku: "CANON-R6"
    serial_number: "CR6-0042"
    cost: 2100.00
    date_acquired: "2026-09-30"
    notes: "Bought from Dealer Ltd, 2yr warranty"
    source_code: "manchester"
  }) {
    serial_number { id serial_number status date_acquired }
  }
}

status defaults to available and date_acquired to today. A serial code must be unique for that product — the same code on a different product is fine, which matters when manufacturers reuse sequences. A duplicate is refused with a graphql-already-exists error.

A whole delivery at once #

mutation AddDelivery {
  createRentalSerialNumbers(input: [
    { sku: "CANON-R6", serial_number: "CR6-0043", cost: 2100.00 },
    { sku: "CANON-R6", serial_number: "CR6-0044", cost: 2100.00 },
    { sku: "CANON-R6", serial_number: "CR6-0045", cost: 2100.00 }
  ]) {
    created_count
    serial_numbers { id serial_number }
  }
}

Up to 500 at a time, and it is all or nothing — if any row is rejected, none are written and you can fix the input and resend the whole batch without worrying about what got halfway in. Duplicates inside the batch itself are caught too, not just clashes with what is already there.

Updating a unit #

mutation Retire {
  updateRentalSerialNumber(
    id: 5012
    input: { status: "retired", notes: "Sensor damaged beyond economic repair, 2026-10-01" }
  ) {
    serial_number { id serial_number status notes }
  }
}

Only the fields you send are written — everything else keeps its value, so you can update a note without resending the cost. Renaming a unit re-runs the uniqueness check against its product.

A unit cannot be moved between products. If a unit really has been reassigned, retire it and add it under the new product, so the history of what went out under the old code stays intact.

Deleting a unit #

mutation Remove {
  deleteRentalSerialNumber(id: 5012) { success id }
}

Deleting is refused while the unit is out on a booking or held by a maintenance ticket, with an error saying which. That is deliberate: deleting a unit that is with a customer leaves a booking pointing at nothing and the kit effectively disappears from the system while still being in someone’s van.

For a unit that has left the fleet, prefer setting status to retired over deleting. A retired unit stops being rentable but its rental history and cost stay available for reporting; a deleted one takes all of that with it.

Finding what is where #

A stock-take of everything not on the shelf, and why:

query NotOnTheShelf {
  rentalSerialNumbers(
    filter: { status: { in: ["rented", "maintenance"] } }
    pageSize: 200
  ) {
    total_count
    items { serial_number sku status reservation_id maintenance_ticket_id }
  }
}

Follow reservation_id into rentalReservation for the customer and due-back date, or maintenance_ticket_id into rentalMaintenanceTicket for what is being done to it.