Analysis: Multi-PDF Print and Bugs
Executive Summary
Section titled “Executive Summary”This analysis compares the approved feature specifications (SAC::PRINT, SAC::PRINT-DX) and their use cases against the current implementation in operations (item module, kanban module, pdfRender module) and arda-frontend-app. The analysis identifies 12 gaps organized into three phases by priority.
The current printing pipeline works correctly for single-template batches but has three categories of issues: (1) data integrity bugs in the card notes mapping, (2) missing multi-template and batch-limiting capabilities, and (3) missing diagnostic API parameters. The frontend requires changes to handle composite responses (multiple PDFs per request) and to expose the unmark-as-printed action.
Specification vs. Implementation Comparison
Section titled “Specification vs. Implementation Comparison”Category 1: Breaking Behaviors (Bugs)
Section titled “Category 1: Breaking Behaviors (Bugs)”These are behaviors that contradict the specification and cause incorrect output for users.
Gap B-1: Card notes mapping sends wrong field to Documint
Section titled “Gap B-1: Card notes mapping sends wrong field to Documint”Specification: SAC::PRINT::FR-0014 — When printing Kanban Cards, the Documint notes field shall contain KanbanCard.notes.
Current Implementation (KanbanCardPrinter.kt:48):
notes = if(card.notes == null || card.notes.isBlank()) card.itemDetails.cardNotesDefault ?: card.itemDetails.notes ?: ""else card.notesGap: When KanbanCard.notes is null or blank, the code falls back to Item.cardNotesDefault and then to Item.notes (order history). Per SAC::PRINT::FR-0016, the Documint notes field should be an empty string when card notes are null or empty — notes from other sources should not leak into the card print output.
Impact: Cards print with order history text (e.g., “Ordered: 3/3/26, 9:22 AM”) in the notes area instead of being blank. Reported in #815 and #816.
Fix: Change the fallback to empty string:
notes = card.notes?.takeUnless { it.isBlank() } ?: ""Gap B-2: Label/breadcrumb notes mapping — verification needed
Section titled “Gap B-2: Label/breadcrumb notes mapping — verification needed”Specification: SAC::PRINT::FR-0015 — When printing Labels or Breadcrumbs, the Documint notes field shall contain Item.notes.
Current Implementation (ItemPrinter.kt:106-107):
notes = item.notes ?: "",card_notes = item.cardNotesDefault ?: "",Gap: The notes field correctly maps Item.notes. However, the card_notes field maps Item.cardNotesDefault — this is sent to Documint but the Documint template for labels/breadcrumbs may or may not use it. This is not a bug per se but should be verified against the Documint templates to confirm no unintended display.
Impact: Low — needs verification only.
Gap B-3: No mechanism to unmark a card as printed
Section titled “Gap B-3: No mechanism to unmark a card as printed”Specification: SAC::PRINT::FR-0012 — The system shall provide a mechanism to reset a Kanban Card’s print status from PRINTED to NOT_PRINTED.
Current Implementation (PrintLifecycleImpl.kt:62-72): The _resultPrintState() function maps event types to states. There is no event type that transitions to NOT_PRINTED. The KanbanCardPrintEventType enum has: PRINT, REPRINT, LOST, DEPRECATE, RETIRE, DESTROY, NONE.
Gap: No UNMARK or equivalent event type exists. No endpoint exposes this action. The frontend has no UI for it.
Impact: Users cannot recover from failed physical prints. Reported in #595.
Fix: Add UNMARK to KanbanCardPrintEventType, map it to NOT_PRINTED in _resultPrintState(), expose via a new endpoint, and add frontend UI.
Category 2: Significant Gaps (Features Not Implemented)
Section titled “Category 2: Significant Gaps (Features Not Implemented)”Gap F-1: Multi-template grouping not supported
Section titled “Gap F-1: Multi-template grouping not supported”Specification: SAC::PRINT::FR-0001 through FR-0004 — When a bulk print request contains items with different template sizes, the system shall group items by template and produce one PDF per group.
Current Implementation:
- Backend (
ItemPrintingService.kt:92-93): Enforces single template —"All items in a print batch must have the same required template". - Backend (
PrintLifecycleImpl.kt:56): Same enforcement for cards —"All cards in a print batch must have the same template required". - Frontend (
page.tsx:1505-1549): Cards are grouped bycardSizeclient-side and sent as separate requests. Labels/breadcrumbs send all IDs in one request and rely on backend rejection.
Gap: The backend rejects mixed-size batches instead of grouping them. The response shape (RenderResult) supports only a single PDF URL.
Impact: Users cannot bulk-print items with different sizes in a single action (labels/breadcrumbs). Cards work around this via frontend grouping but produce separate requests. Reported in #575.
Gap F-2: No batch size limiting
Section titled “Gap F-2: No batch size limiting”Specification: SAC::PRINT::FR-0007 through FR-0010 — The system shall enforce per-call and per-request batch size limits.
Current Implementation: No batch size limits exist. The entire batch is sent as one Documint call regardless of size.
Gap: Large batches (>40 items) overwhelm Documint and cause failures. No configuration for limits exists in application.conf.
Impact: Bulk printing of large inventories fails unpredictably. Reported in #519.
Gap F-3: Composite response shape not implemented
Section titled “Gap F-3: Composite response shape not implemented”Specification: SAC::PRINT::FR-0003 — The bulk print response shall contain one result entry per template group.
Current Implementation: RenderResult is a single-URL response:
data class RenderResult(val url: URL, val job: UUID, val asOF: TimeCoordinates, val templateId: String)Gap: No composite response type exists. The API returns exactly one URL or one error.
Impact: Blocks multi-template grouping (F-1). Frontend expects data.data.url as a single string (page.tsx:1520, 1661, 1771).
Gap F-4: No parallel Documint rendering
Section titled “Gap F-4: No parallel Documint rendering”Specification: SAC::PRINT::FR-0002 — Template groups shall be rendered in parallel, up to a configurable maximum concurrency limit.
Current Implementation: Single sequential Documint call per request. No concurrency configuration.
Gap: No parallel rendering capability. No maxParallelRenders configuration.
Gap F-5: Frontend does not handle multiple PDF URLs
Section titled “Gap F-5: Frontend does not handle multiple PDF URLs”Specification: SAC::PRINT::FR-0005, FR-0006 — The frontend shall open one new browser tab per successfully returned PDF URL and display error notifications for failed groups.
Current Implementation (page.tsx): All three handlers (handlePrintSelectedCards:1520, handlePrintSelectedLabels:1661, handlePrintSelectedBreadcrumbs:1771) expect a single data.data.url and call window.open() once. Error handling shows a single toast notification.
Gap: Frontend cannot process composite responses with multiple URLs and per-group errors.
Category 3: Missing API Exposure (Diagnostics)
Section titled “Category 3: Missing API Exposure (Diagnostics)”Gap D-1: No debug payload return
Section titled “Gap D-1: No debug payload return”Specification: SAC::PRINT-DX::FR-0003 through FR-0005 — When debug=true, the API response shall include the Documint payload.
Current Implementation: No debug query parameter on any print endpoint. The constructed payload is logged to CloudWatch but not returned to the caller.
Gap: Support team cannot inspect what was sent to Documint without AWS log access.
Impact: Reported in #762.
Gap D-2: No dry-run mode
Section titled “Gap D-2: No dry-run mode”Specification: SAC::PRINT-DX::FR-0006 through FR-0009 — When dry-run=true, the system shall construct the payload but not call Documint.
Current Implementation: No dry-run query parameter. Every print request calls Documint.
Gap: Support team cannot inspect payload construction without triggering a render and consuming quota.
Impact: Reported in #792.
Gap D-3: Live-print documentation alignment and OpenAPI descriptions
Section titled “Gap D-3: Live-print documentation alignment and OpenAPI descriptions”Note: All diagnostic parameters must include detailed descriptions in the endpoint route definitions so they appear in the OpenAPI specification.
Specification: SAC::PRINT-DX::FR-0001, FR-0002 — All print endpoints shall accept live-print with documented behavior.
Current Implementation: The live-print parameter exists on all three print endpoints. Cards default to live=true (KanbanCardEndpoint.kt:375), labels/breadcrumbs default to false (from module config documint.useLive). Frontend always sends live-print=true.
Gap: Not a code gap — parameter already works. Needs documentation alignment only to clarify the test/preview vs. production distinction.
Category 4: API Test Coverage
Section titled “Category 4: API Test Coverage”Gap A-1: Existing print API tests are disabled and incomplete
Section titled “Gap A-1: Existing print API tests are disabled and incomplete”Current Implementation: The api-test repository has 5 Bruno test files for print endpoints:
collections/item/bearer-auth/Printing/PrintLabels.bru— taggeddisabledcollections/item/bearer-auth/Printing/PrintBreadcrumb.bru— taggeddisabledcollections/kanban/Print-Cards/PrintOneCard.bru— taggeddisabledcollections/kanban/Print-Cards/PrintMultpleCards.bru— taggeddisabledcollections/partitionCheck/pdf-render/list-templates.bru— taggeddisabled
Gap: All printing tests are disabled. They test the current single-URL response shape and do not cover: composite responses, mixed-size batches, batch limiting, the unmark endpoint, debug/dry-run parameters, or the notes mapping fix. After Phases 1-3, these tests must be updated and new tests added to validate the changed API contracts.
Gap A-2: api-proxy types and methods do not match updated API contracts
Section titled “Gap A-2: api-proxy types and methods do not match updated API contracts”Current Implementation: The api-proxy package defines RenderResult (reference/shared/types.ts) as a single-URL response, and ItemProxy.printLabels(), ItemProxy.printBreadcrumbs(), KanbanProxy.printCards() all return Promise<RenderResult>. No unmarkPrinted() method exists on KanbanProxy. No diagnostic parameter support.
Gap: After Phases 1-3, the API contracts change: composite response shape, new unmark endpoint, and diagnostic query parameters. The TypeScript proxy types and methods must be updated to match.
Summary Table
Section titled “Summary Table”| # | Specification Requirement | Status | Category | Phase |
|---|---|---|---|---|
| B-1 | SAC::PRINT::FR-0014, FR-0016 (card notes mapping) | Bug | Breaking | 1 |
| B-2 | SAC::PRINT::FR-0015 (label/breadcrumb notes) | Verify | Breaking | 1 |
| B-3 | SAC::PRINT::FR-0012 (unmark as printed) | Gap | Breaking | 1 |
| F-1 | SAC::PRINT::FR-0001 to FR-0004 (multi-template grouping) | Gap | Feature | 2 |
| F-2 | SAC::PRINT::FR-0007 to FR-0010 (batch limiting) | Gap | Feature | 2 |
| F-3 | SAC::PRINT::FR-0003 (composite response) | Gap | Feature | 2 |
| F-4 | SAC::PRINT::FR-0002 (parallel rendering) | Gap | Feature | 2 |
| F-5 | SAC::PRINT::FR-0005, FR-0006 (frontend multi-URL) | Gap | Feature | 2 |
| D-1 | SAC::PRINT-DX::FR-0003 to FR-0005 (debug payload) | Gap | Diagnostics | 3 |
| D-2 | SAC::PRINT-DX::FR-0006 to FR-0009 (dry-run) | Gap | Diagnostics | 3 |
| D-3 | SAC::PRINT-DX::FR-0001, FR-0002 (live-print docs) | Docs only | Diagnostics | 3 |
| A-1 | API test coverage for all print endpoints | Gap | Testing | 4 |
| A-2 | api-proxy types and methods out of date | Gap | Proxy | 5 |
Prioritized Recommendations
Section titled “Prioritized Recommendations”Phase 1: Bug Fixes (Breaking Behaviors)
Section titled “Phase 1: Bug Fixes (Breaking Behaviors)”Scope: Gaps B-1, B-2, B-3.
Repositories: operations (kanban module, item module), arda-frontend-app.
Rationale: These are customer-reported bugs with immediate impact. B-1 causes incorrect printed output. B-3 prevents recovery from failed prints. B-2 is a low-risk verification.
Phase 2: Multi-Template Printing (New Features)
Section titled “Phase 2: Multi-Template Printing (New Features)”Scope: Gaps F-1, F-2, F-3, F-4, F-5.
Repositories: operations (item module, kanban module, pdfRender module), arda-frontend-app.
Rationale: This is the primary feature work. F-3 (composite response) and F-1 (grouping) are the architectural changes that enable F-2 (batch limiting), F-4 (parallel rendering), and F-5 (frontend handling). These should be implemented together.
Phase 3: Diagnostics (API Exposure)
Section titled “Phase 3: Diagnostics (API Exposure)”Scope: Gaps D-1, D-2, D-3.
Repositories: operations (item module, kanban module, pdfRender module).
Rationale: These are additive capabilities for the support persona. No breaking changes. Can be implemented independently after Phase 2 since they benefit from the refactored printing pipeline.
Phase 4: API Tests
Section titled “Phase 4: API Tests”Scope: Gap A-1.
Repositories: api-test.
Rationale: After all code changes are deployed to a test environment, the existing disabled print tests must be updated and new tests added to validate the changed API contracts. This phase runs against the deployed system to verify end-to-end behavior.
Phase 5: API Proxy Update
Section titled “Phase 5: API Proxy Update”Scope: Gap A-2.
Repositories: api-proxy.
Rationale: The @arda-cards/api-proxy TypeScript package must reflect the updated API contracts. Small scope (type definitions + proxy method updates) but requires its own PR since it’s a separate repository with independent CI/CD.
Cross-Module Impact
Section titled “Cross-Module Impact”| Module | Phase 1 | Phase 2 | Phase 3 | Phase 4 | Phase 5 |
|---|---|---|---|---|---|
item (ItemPrintingService, ItemPrinter, ItemEndpoint) | B-2 verify | F-1, F-3, F-4 | D-1, D-2 | — | — |
kanban (KanbanCardPrinter, PrintLifecycleImpl, KanbanCardEndpoint) | B-1 fix, B-3 new endpoint | F-1, F-3 (response shape) | D-1, D-2 | — | — |
pdfRender (PdfRenderService) | — | F-1, F-2, F-3, F-4 (grouping, batching, parallelism) | D-1, D-2 (passthrough) | — | — |
arda-frontend-app (page.tsx, API routes) | B-3 (unmark UI) | F-5 (multi-URL handling) | — | — | — |
api-test (Bruno) | — | — | — | A-1 | — |
api-proxy (TypeScript) | — | — | — | — | A-2 |
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved