Skip to content

feat(assets): track physical QR label assignment - #2717

Draft
ChaddBrenner wants to merge 1 commit into
Shelf-nu:mainfrom
ChaddBrenner:chaddb/qr-label-assignment
Draft

feat(assets): track physical QR label assignment#2717
ChaddBrenner wants to merge 1 commit into
Shelf-nu:mainfrom
ChaddBrenner:chaddb/qr-label-assignment

Conversation

@ChaddBrenner

Copy link
Copy Markdown

What changed

Adds a nullable Asset.qrLabelAppliedAt timestamp and an organization/timestamp index. The new "Has ID Assigned" state is available in the asset overview, advanced index columns and filters, asset queries, and CSV exports.

The timestamp is set when an existing QR is assigned during asset creation, linked to an asset, or relinked to an asset. Creating from an existing QR and the other assignment paths re-read QR ownership in serializable transactions with bounded retries when PostgreSQL reports a write conflict.

Why this matters

Having a generated QR in the database does not necessarily mean someone applied a physical label. Tracking the assignment separately makes it possible to find assets that still need labels without treating every auto-generated QR as applied.

How it works

The normal and advanced asset queries both project qrLabelAppliedAt, so the overview, filters, sorting, columns, and exports use the same value. Sorting puts null values last.

The migration uses the relationship between asset and QR timestamps to infer historical assignments. It excludes QRs created at the same time as their assets, since those are the normal auto-generated records. A linked QR is backfilled only when its creation timestamp differs from the asset and it was updated after it was created. A database trigger records assignments made by an older application process during a rolling deploy. Cross-organization and asset-plus-kit legacy links are excluded from both paths.

Migration notes

The migration backfills the earliest qualifying QR update timestamp for each asset. Fresh auto-generated, replacement, and import-created QR rows remain null when their timestamps do not show a later assignment.

This is intentionally a conservative inference. The database cannot prove every historical physical action, but it can distinguish the existing-then-linked pattern from asset and QR rows created together.

The change is split into schema/trigger, backfill, and index migrations. I rehearsed the public migration history on PostgreSQL 15 with 50,007 assets and 55,844 QRs. The three phases took 9 ms, 152 ms, and 28 ms. Expected auto-generated, pre-generated, replacement, secondary, unclaimed, cross-organization, and dual-linked cases were checked individually, and hashes of every pre-existing column matched afterward.

During the migration rehearsal, 89,393 concurrent asset updates completed without failures, serialization errors, or deadlocks. The slowest observed update took 58.6 ms.

Tests run

  • pnpm db:generate
  • Focused asset service, query, advanced filter, index setting, and CSV tests: 226 passed, 1 skipped
  • pnpm --filter @shelf/webapp typecheck
  • Changed-file ESLint and Prettier checks
  • git diff --check origin/main...chaddb/qr-label-assignment

I also ran pnpm webapp:validate. The branch tests and typecheck pass separately, but the combined command still exits on the existing import-order error in apps/webapp/app/components/list/list-title.tsx, which this branch does not modify.

Review notes

Generating or downloading a QR file does not set this timestamp. It changes only on an assignment or linking write path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant