Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

19KB


title: ‘Print Delivery Paperwork for Purple Envelope Jobs’ slug: ‘print-delivery-paperwork-purple-envelope’ created: ‘2026-04-01’ status: ‘Implementation Complete’ stepsCompleted: [1, 2, 3, 4] tech_stack: [‘Classic ASP’, ‘VBScript’, ‘Access MDB (Jet/ACE)', ‘ReportMan ActiveX’, ‘ADO’, ‘FSO’, ‘ASPUnit’] files_to_modify:

  • ‘Data/Migrations/Migration_21_Create_DeliveryLabelPage_Table.asp’
  • ‘App/ViewModels/KitViewModels.asp’
  • ‘App/DomainModels/KitRepository.asp’
  • ‘App/Controllers/Kit/KitController.asp’
  • ‘App/Views/Kit/SwitchBoardPurpleEnvelopeEdit.asp’
  • ‘Tests/TestCase_PurpleEnvelopeReport.asp’
  • ‘Data/Delivery_Labels.rep’
  • ‘Data/Delivery_PackingSlip.rep’ code_patterns: [‘Controller GET action (parameterless Sub, reads QueryString)', ‘Fresh ReportManX object per report export’, ‘Repository with DAL.BeginTransaction + DAL.Execute array params’, ‘Classic ASP migration class under Data/Migrations’, ‘HTML.LinkToExt for GET links’] test_patterns: [‘ASPUnit DB-backed tests using disposable MDB copies’, ‘Manual IIS verification required for COM/ReportMan/FSO/network share’, ‘Boundary testing at 1100-label increments’]

Tech-Spec: Print Delivery Paperwork for Purple Envelope Jobs

Created: 2026-04-01

Overview

Problem Statement

After inkjet export completes and the ImportService sets a Purple Envelope kit to Status = "Done", staff still need delivery paperwork to ship the job: shipping labels showing box counts and a packing slip for signed receipt. The Purple Envelope switchboard currently has no way to generate those documents.

Solution

Add a Print Delivery Paperwork action to the Purple Envelope workflow. When a kit is in Done status, the edit screen will show a button that calls a new KitController.PrintDeliveryPaperwork GET action. That action will:

  1. Reload the kit through SwitchBoardPurpleEnvelopeEditFindById.
  2. Reject direct access unless the kit status is exactly Done.
  3. Compute total shipping boxes with ceiling division by 1100 labels per box.
  4. Build staged label-page rows in reverse box order, six labels per page.
  5. Persist those rows into a new DeliveryLabelPage staging table.
  6. Generate a labels PDF from Data\Delivery_Labels.rep.
  7. Generate a packing slip PDF from Data\Delivery_PackingSlip.rep.
  8. Save both PDFs into the existing export folder for the jurisdiction.
  9. Remove staged rows after both files are successfully generated and moved.
  10. Redirect back to SwitchBoardPurpleEnvelopeEdit with flash messaging.

Scope

In Scope:

  • New Access migration for DeliveryLabelPage
  • New DeliveryLabelPage_Class view model
  • New KitRepository.SaveDeliveryLabelPages and KitRepository.DeleteDeliveryLabelPages
  • New KitController.PrintDeliveryPaperwork GET action
  • New Print Delivery Paperwork button on SwitchBoardPurpleEnvelopeEdit.asp
  • Reverse-order page building for any number of boxes, six labels per page
  • Two new ReportMan templates and their required parameters/query contracts
  • Overwrite-safe PDF output to the existing export folder
  • ASPUnit coverage for staging-table persistence and source-level UI/controller guardrails

Out of Scope:

  • Changing how kit status becomes Done
  • Emailing, printing, or otherwise distributing the PDFs after generation
  • Generalizing this into a reusable report-generation framework
  • Changing existing label export behavior outside this Purple Envelope paperwork flow

Context for Development

Codebase Patterns

  • ImportService/TrackingDataImport.vbs sets kit status to Done after inkjet export. The web app should treat that as the gate for paperwork generation, not redefine the lifecycle.
  • KitController.SwitchBoardPurpleEnvelopeEdit already loads the exact header data this feature needs, including JCode, Jurisdiction, JobNumber, Status, and LabelCount.
  • App/Views/Kit/SwitchBoardPurpleEnvelopeEdit.asp already conditionally shows UI based on Model.Kit.Status; the new button should follow that same pattern.
  • ExportTrackingLabels in KitController.asp is the nearest existing ReportMan export example, including connection-string setup, overwrite behavior, and FSO.MoveFile.
  • MVC/activeXdepedancies.asp exposes a shared ReportManager, but HomeController.asp shows that creating a fresh Server.CreateObject("ReportMan.ReportManX") instance is acceptable when isolated state is preferable.
  • MVC/lib.Data.asp confirms DAL.BeginTransaction, DAL.CommitTransaction, and DAL.RollbackTransaction are available.
  • Existing purple-envelope tests in Tests/TestCase_PurpleEnvelopeReport.asp already use disposable MDB copies and are the right place to add repository-level coverage for this feature.

Files to Reference

File Purpose
App/Controllers/Kit/KitController.asp Add PrintDeliveryPaperwork; reference ExportTrackingLabels for ReportMan + FSO export flow
App/DomainModels/KitRepository.asp SwitchBoardPurpleEnvelopeEditFindById already exposes LabelCount, JCode, and Jurisdiction
App/ViewModels/KitViewModels.asp Add DeliveryLabelPage_Class
App/Views/Kit/SwitchBoardPurpleEnvelopeEdit.asp Add the Done-gated button
Data/Migrations/Migration_20_Add_ColorId_To_InkjetRecords.asp Migration naming/style reference; next migration is Migration_21_...
MVC/lib.Data.asp Confirms transaction and parameter-array behavior
MVC/activeXdepedancies.asp Confirms ReportManX is already installed/used in the app
Tests/TestCase_PurpleEnvelopeReport.asp Existing purple-envelope test fixture patterns to extend

Technical Decisions

  1. Use the existing Purple Envelope edit query. SwitchBoardPurpleEnvelopeEditFindById already returns the required header fields and the inkjet-record-derived LabelCount. No new count query is needed.

  2. Add a dedicated staging table. ReportMan should read normal rows from Access rather than trying to derive reverse-ordered page slots inside the .rep file.

    CREATE TABLE [DeliveryLabelPage] (
        [ID] COUNTER CONSTRAINT [PrimaryKey] PRIMARY KEY,
        [KitID] LONG NOT NULL,
        [PageNum] INTEGER NOT NULL,
        [Label1] TEXT(500),
        [Label2] TEXT(500),
        [Label3] TEXT(500),
        [Label4] TEXT(500),
        [Label5] TEXT(500),
        [Label6] TEXT(500)
    );
    
  3. Repository owns staging-table lifecycle. KitRepository.SaveDeliveryLabelPages will delete existing rows for the kit and insert the new set inside one transaction. KitRepository.DeleteDeliveryLabelPages will remove rows after both PDFs succeed. The controller should not issue raw DAL.Execute cleanup for this feature.

  4. Page-building happens in VBScript, in reverse order. For totalBoxes, start at boxNum = totalBoxes and fill Label1 through Label6 before creating the next page row. That yields page 1 containing the highest box numbers first.

  5. Each PDF generation uses a fresh ReportManX instance. Use Server.CreateObject("ReportMan.ReportManX") separately for labels and packing slip. This avoids any parameter/template state bleed between the two exports.

  6. Direct URL access is guarded server-side. The button is hidden unless status is Done, but the controller must also reject non-Done requests with a warning flash and redirect. UI-only gating is not sufficient.

  7. Staging cleanup happens only after both PDFs are safely moved. If generation fails after staging rows are written, leave them in place. A subsequent run will replace them because SaveDeliveryLabelPages always deletes and re-inserts by KitID.

  8. Match existing export-folder naming. Use the same ExportDirectory & JCode & "-" & Jurisdiction & "\" pattern already used by ExportTrackingLabels. Do not introduce new filename sanitization behavior in this feature.

  9. Label text format is fixed. Each populated label slot is multiline text:

    {Jurisdiction}
    Box {X} of {TotalBoxes}
    Total Printed {InkjetCount}
    Job# {JobNumber}
    

    Empty slots on the last page are "".

  10. Output filenames are deterministic.

    • Labels PDF: {JCode}-{Jurisdiction}_delivery_labels.pdf
    • Packing slip PDF: {JCode}-{Jurisdiction}_delivery_packing_slip.pdf

Implementation Plan

Tasks

  • Task 1: Add the DeliveryLabelPage migration

File: Data/Migrations/Migration_21_Create_DeliveryLabelPage_Table.asp

<%
Class Migration_21_Create_DeliveryLabelPage_Table
    Public Migration

    Public Sub Up
        Migration.Do "CREATE TABLE [DeliveryLabelPage] (" & _
            "[ID] COUNTER CONSTRAINT [PrimaryKey] PRIMARY KEY, " & _
            "[KitID] LONG NOT NULL, " & _
            "[PageNum] INTEGER NOT NULL, " & _
            "[Label1] TEXT(500), " & _
            "[Label2] TEXT(500), " & _
            "[Label3] TEXT(500), " & _
            "[Label4] TEXT(500), " & _
            "[Label5] TEXT(500), " & _
            "[Label6] TEXT(500));"
    End Sub

    Public Sub Down
        Migration.Do "DROP TABLE [DeliveryLabelPage];"
    End Sub
End Class

Migrations.Add "Migration_21_Create_DeliveryLabelPage_Table"
%>
  • Task 2: Add the staging-row view model and repository methods

Files:

  • App/ViewModels/KitViewModels.asp
  • App/DomainModels/KitRepository.asp

Add:

Class DeliveryLabelPage_Class
    Public PageNum
    Public Label1
    Public Label2
    Public Label3
    Public Label4
    Public Label5
    Public Label6
End Class

Add repository methods near the end of KitRepository_Class:

Public Sub SaveDeliveryLabelPages(ByVal kitId, ByVal pages)
    dim sql, pageIt, page
    sql = "INSERT INTO [DeliveryLabelPage] ([KitID],[PageNum],[Label1],[Label2],[Label3],[Label4],[Label5],[Label6]) VALUES (?,?,?,?,?,?,?,?)"

    DAL.BeginTransaction
    On Error Resume Next

    DAL.Execute "DELETE FROM [DeliveryLabelPage] WHERE [KitID] = ?", CLng(kitId)

    set pageIt = pages.Iterator
    Do While Err.Number = 0 And pageIt.HasNext
        set page = pageIt.GetNext()
        DAL.Execute sql, Array(CLng(kitId), page.PageNum, page.Label1, page.Label2, page.Label3, page.Label4, page.Label5, page.Label6)
    Loop

    If Err.Number <> 0 Then
        dim errNumber : errNumber = Err.Number
        dim errDescription : errDescription = Err.Description
        DAL.RollbackTransaction
        On Error GoTo 0
        Err.Raise errNumber, "KitRepository_Class.SaveDeliveryLabelPages", errDescription
    End If

    On Error GoTo 0
    DAL.CommitTransaction
End Sub

Public Sub DeleteDeliveryLabelPages(ByVal kitId)
    DAL.Execute "DELETE FROM [DeliveryLabelPage] WHERE [KitID] = ?", CLng(kitId)
End Sub
  • Task 3: Add PrintDeliveryPaperwork to KitController

File: App/Controllers/Kit/KitController.asp

Add a new parameterless GET action after SwitchBoardPurpleEnvelopeEdit and before Index.

Implementation requirements:

  • Read Id from Request.QueryString("Id")
  • Load the kit through KitRepository.SwitchBoardPurpleEnvelopeEditFindById
  • If status is not Done, set Flash.Warning and redirect back to SwitchBoardPurpleEnvelopeEdit
  • If LabelCount <= 0, set Flash.Warning and redirect
  • Verify both .rep files exist before writing staging rows
  • Create the export folder if missing
  • Build a LinkedList_Class of DeliveryLabelPage_Class rows in reverse order
  • Persist rows through KitRepository.SaveDeliveryLabelPages
  • Generate labels PDF with a fresh local ReportMan.ReportManX object using PBKITID
  • Generate packing slip PDF with a second fresh local ReportMan.ReportManX object using parameters:
    • PJURISDICTION
    • PJOB_NUMBER
    • PTOTAL_PRINTED
    • PTOTAL_BOXES
  • Delete any existing final PDFs before moving the temp outputs into place
  • Call KitRepository.DeleteDeliveryLabelPages only after both PDFs succeed
  • Set Flash.Success and redirect back to SwitchBoardPurpleEnvelopeEdit

Concrete page-build algorithm:

If inkjetCount > 0 Then
    totalBoxes = Int((inkjetCount + 1099) / 1100)
Else
    totalBoxes = 0
End If

set pages = new LinkedList_Class
boxNum = totalBoxes
pageNum = 1

Do While boxNum >= 1
    set page = new DeliveryLabelPage_Class
    page.PageNum = pageNum

    For slot = 1 To 6
        labelText = ""
        If boxNum >= 1 Then
            labelText = jurisdiction & vbCrLf & _
                        "Box " & boxNum & " of " & totalBoxes & vbCrLf & _
                        "Total Printed " & inkjetCount & vbCrLf & _
                        "Job# " & jobNumber
            boxNum = boxNum - 1
        End If

        Select Case slot
            Case 1 : page.Label1 = labelText
            Case 2 : page.Label2 = labelText
            Case 3 : page.Label3 = labelText
            Case 4 : page.Label4 = labelText
            Case 5 : page.Label5 = labelText
            Case 6 : page.Label6 = labelText
        End Select
    Next

    pages.Push page
    pageNum = pageNum + 1
Loop

Connection-string and export behavior should mirror ExportTrackingLabels, but use local report objects:

dim labelsReport : set labelsReport = Server.CreateObject("ReportMan.ReportManX")
dim slipReport   : set slipReport   = Server.CreateObject("ReportMan.ReportManX")
  • Task 4: Add the Done-gated button to the Purple Envelope edit view

File: App/Views/Kit/SwitchBoardPurpleEnvelopeEdit.asp

Insert this above the existing Ready To Assign STIDS block so it is visible when the job is already complete:

<% If Model.Kit.Status = "Done" Then %>
    <p>
        <%= HTML.LinkToExt("<i class='glyphicon glyphicon-print'></i> Print Delivery Paperwork", "Kit", "PrintDeliveryPaperwork", Array("Id", Model.Kit.ID), Array("class", "btn btn-primary")) %>
    </p>
<% End If %>
  • Task 5: Extend ASPUnit coverage

File: Tests/TestCase_PurpleEnvelopeReport.asp

Add:

  • A DB-backed test that seeds a Purple Envelope kit, calls SaveDeliveryLabelPages, and verifies inserted row count and label values

  • A DB-backed test that calls SaveDeliveryLabelPages twice for the same kit and proves the second run fully replaces the first

  • A DB-backed test for DeleteDeliveryLabelPages

  • A source-level guard test asserting the view contains the Model.Kit.Status = "Done" condition and PrintDeliveryPaperwork link

  • A source-level guard test asserting KitController.asp defines Public Sub PrintDeliveryPaperwork

  • Task 6: Create the two ReportMan templates

Files:

  • Data/Delivery_Labels.rep
  • Data/Delivery_PackingSlip.rep

Delivery_Labels.rep

  • Connection: TRACKINGKITLABELS
  • Parameter: PBKITID
  • Query: SELECT Label1, Label2, Label3, Label4, Label5, Label6 FROM DeliveryLabelPage WHERE KitID = [PBKITID] ORDER BY PageNum
  • Layout: one detail row per page, with six multiline label areas arranged 2 columns by 3 rows

Delivery_PackingSlip.rep

  • Parameters only:
    • PJURISDICTION
    • PJOB_NUMBER
    • PTOTAL_PRINTED
    • PTOTAL_BOXES
  • Layout: single-page packing slip with jurisdiction, job number, totals, signature line, and date line

Acceptance Criteria

  • AC1 - Button visibility: Given a Purple Envelope kit with Status = "Done", when SwitchBoardPurpleEnvelopeEdit renders, then the Print Delivery Paperwork button is visible.
  • AC2 - Button hidden when not done: Given a Purple Envelope kit whose status is anything other than Done, when the page renders, then the button is not visible.
  • AC3 - Direct route guard: Given a user browses directly to PrintDeliveryPaperwork for a kit whose status is not Done, when the action runs, then it redirects back with a warning flash and produces no PDFs.
  • AC4 - Exact 1100 boundary: Given a kit with exactly 1100 inkjet records, when paperwork is generated, then totalBoxes = 1 and the staged row has Label1 = "Box 1 of 1" with Label2 through Label6 blank.
  • AC5 - One over boundary: Given a kit with 1101 inkjet records, when paperwork is generated, then totalBoxes = 2 and the staged row contains Label1 = "Box 2 of 2" and Label2 = "Box 1 of 2".
  • AC6 - Multi-page reverse ordering: Given a kit with 7701 inkjet records, when paperwork is generated, then two staged rows are written and page 1 contains boxes 7 through 2 while page 2 contains box 1 only.
  • AC7 - Label text contract: Given any populated label slot, when its text is inspected, then it includes jurisdiction, Box X of Y, Total Printed N, and Job# N on separate lines.
  • AC8 - Labels PDF output: Given a kit with JCode = "01234" and Jurisdiction = "Anytown", when paperwork generation succeeds, then 01234-Anytown_delivery_labels.pdf exists in ExportDirectory\01234-Anytown\.
  • AC9 - Packing slip PDF output: Given the same kit, when paperwork generation succeeds, then 01234-Anytown_delivery_packing_slip.pdf exists in the same export folder.
  • AC10 - Overwrite behavior: Given one or both delivery PDFs already exist, when paperwork is generated again, then the existing files are replaced without error.
  • AC11 - Success redirect: Given both PDFs are generated and moved successfully, when the action completes, then the user is redirected back to SwitchBoardPurpleEnvelopeEdit with a success flash.
  • AC12 - Staging-row replacement and cleanup: Given existing DeliveryLabelPage rows already exist for the kit, when paperwork is generated, then those rows are replaced by the new set and removed after both PDFs succeed.
  • AC13 - Missing template failure is handled: Given either .rep file is missing, when the action runs, then the user receives a warning flash and the request does not crash with a 500.
  • AC14 - Packing slip parameters stay isolated: Given paperwork generation runs end-to-end, when the packing slip PDF is inspected, then it shows the expected jurisdiction and job number rather than stale parameters from the labels export.

Additional Context

Dependencies

  • ReportMan.ReportManX ActiveX installed on the IIS host
  • FSO and DAL globals already loaded through App/include_all.asp
  • ExportDirectory and dev from App/app.config.asp
  • LinkedList_Class from the shared MVC libraries
  • Delivery_Labels.rep and Delivery_PackingSlip.rep present in Data\
  • DeliveryLabelPage migration applied to the active MDB before use

Testing Strategy

  • Run ASPUnit through Tests/Test_All.asp after adding the repository and source-guard tests.
  • Add DB-backed tests using the same disposable MDB pattern already present in Tests/TestCase_PurpleEnvelopeReport.asp.
  • Perform manual IIS verification for:
    • status Done button visibility
    • direct-route guard for non-Done kits
    • 1100 / 1101 / 6600 / 6601 label-count boundaries
    • overwrite behavior when PDFs already exist
    • missing-template warning behavior
    • correct output in both dev and prod-style connection-string branches
    • final export folder/file placement on the configured filesystem share

Notes

  • This spec is ready for implementation. The next BMAD step for this artifact is Quick Dev using /bmad-bmm-quick-dev in a fresh context.
  • The report templates are the only manual-designer portion of the work. Everything else is ordinary Classic ASP / VBScript / Access code and test coverage.
  • Keeping fresh ReportManX instances per PDF is the main design choice that removes the last meaningful ambiguity from the earlier review draft.

Powered by TurnKey Linux.