DEV Community

life
life

Posted on

Model a hazmat shipping paper as ordered segments, not five columns

A hazmat shipping paper becomes testable when the basic description is stored as one ordered array, the mixed-load marker as an enum, and the retention clock as a date field captured from the carrier rather than the order.

Most shipping-paper bugs are not missing data. They are data that arrived in the wrong shape. A description split across five nullable columns cannot express "these five entries, in this order, as one block, with everything else after them." A boolean named is_hazmat cannot express which of three permitted mixed-load markers the offeror chose. A retention_years integer computed from the order timestamp cannot express the rule in 49 CFR 172.201(e), because the clock starts when the initial carrier accepts the material. Reshaping the model is the whole job. Validation falls out of it.

The basic description is an ordered array, not five fields

49 CFR 172.202(a) lists what the shipping description must include: the identification number from Column (4) of the § 172.101 table, the proper shipping name from Column (2), the hazard class or division number from Column (3), the packing group in Roman numerals from Column (5), and the total quantity with its unit of measurement. The regulation treats that set as a single object called the basic description, and and 49 CFR 172.201(a)(4) constrains everything else: anything extra goes after that block, not inside it.

That ordering constraint is the reason to model the description as an array of typed segments with a position index, not as sibling columns. Each segment carries a kind drawn from a closed set, and the validator asserts that the five required kinds appear once each, in the sequence the regulation lists, with no additional-information segment at a lower index than the last required one. Packing group has its own wrinkle. 49 CFR 172.202(a)(4) excepts Class 1 materials, self-reactive substances, Division 5.2 materials, and entries not assigned a packing group from the requirement, so the assertion is conditional on the material, not unconditional. Store the exception basis alongside the segment or the validator will fire on correct papers.

Hazard class carries two separate conditionals under 49 CFR 172.202(a)(3). A subsidiary class or division number need not be entered at all when no matching subsidiary label is required. And when it is entered, it goes in parentheses immediately after the primary class or division number, except for combustible liquids.

segment := {
  position:      int,          # 1-based, dense, no gaps
  kind:          enum,         # id_number | psn | hazard_class | packing_group | quantity | additional
  value:         string,
  unit:          string|null,  # required when kind = quantity
  exception_ref: string|null   # e.g. "172.202(a)(4)" when the segment is legitimately absent
}

assert description.segments
  .filter(s => s.kind in REQUIRED_FIVE)
  .map(s => s.kind)
  == ["id_number","psn","hazard_class","packing_group","quantity"]

assert description.segments
  .filter(s => s.kind == "additional")
  .every(s => s.position > position_of("quantity"))
Enter fullscreen mode Exit fullscreen mode

Quantity deserves its own note. 49 CFR 172.202(a)(5) requires the total quantity by mass or volume, or by activity for Class 7 materials, together with the applicable unit of measurement, and excepts transportation by aircraft from the requirement. A quantity segment without a unit should fail validation rather than pass as an empty string.

The mixed-load marker is an enum with three members

When a hazardous material and a non-regulated material share one shipping paper, 49 CFR 172.201(a)(1) gives the offeror three acceptable ways to distinguish the hazardous entry: enter it first, enter it in a color that clearly contrasts with the non-regulated description, or place an "X" before the basic description in a column captioned "HM." A boolean collapses those into one bit and loses the choice. Store the marker as an enum whose members are first_position, contrasting_color, and hm_column_x, then assert that exactly one is set whenever the paper carries both kinds of material.

Two smaller rules ride along. 49 CFR 172.201(a)(2) requires the shipping description and all copies used for transportation to be legible and printed in English, manually or mechanically. 49 CFR 172.201(a)(3) bars any code or abbreviation in the required description unless the subchapter specifically authorizes or requires it. The second is checkable against a maintained allowlist of authorized abbreviations, and it is worth running as a lint rather than a hard block, because the allowlist has to track the subchapter.

Page count and the total-page notation are their own fields

A multi-page shipping paper has a structural requirement that gets skipped when documents are assembled at print time. Under 49 CFR 172.201(c) a paper may run over several pages provided the pages number in sequence and page one states how many pages there are in total, the rule offering "Page 1 of 4 pages" as its own example.

Model page_number, page_total, and total_notation_present as first-class fields on the document, not as artifacts of the PDF renderer. The assertion is trivial once the fields exist: page numbers form a dense sequence from one to page_total, and total_notation_present is true on the first page whenever page_total exceeds one. A renderer that paginates after the fact cannot satisfy this, which is the point. The notation is a property of the document the offeror provides, not of the file that leaves the printer.

The emergency phone needs two checks, not one

49 CFR 172.201(d) asks for two things on the face of the paper: an emergency response telephone number, and where that number routes through a service provider, the name or contract number of whoever holds the agreement with the provider. Those are separate obligations and they fail separately.

The first check is availability: a number is present and parses as a dialable number in the expected format. The second is accountability: if a service provider is in use, a named person or contract number is recorded against that provider. A paper can pass the first and fail the second, which is exactly the case a single emergency_contact string hides. Keep the provider relationship as a reference to a record rather than free text, so the contract number can be re-verified when it changes.

Retention forks on cargo class and starts at carrier acceptance

49 CFR 172.201(e) sets two periods. Hazardous waste keeps its copy for three years past acceptance by the first carrier; everything else covered by the subpart keeps two, and the trigger event is the same in both branches. Each copy must include the date of acceptance by the initial carrier, with a stated alternative for rail, vessel, or air shipments where the date on the waybill, airbill, or bill of lading may be used instead.

The engineering consequence is that acceptance date must be an independent, immutable field. Deriving a retention deadline from an order timestamp produces a date that is wrong by however long the goods sat before handover, and the error runs in the direction that loses records early. Store carrier_accepted_on and acceptance_date_source, the latter distinguishing a direct acceptance date from a transport-document date, then compute the deadline from the cargo classification. The same section requires the copy to be accessible at or through the principal place of business and producible to an authorized official at reasonable times and locations, so the record needs a location pointer as well as a date.

The shipper's certification is stored as wording, signer and time

49 CFR 172.204(a) requires the person offering the material to certify conformity by printing on the shipping paper either the certification in 49 CFR 172.204(a)(1) or the declaration in 49 CFR 172.204(a)(2). The two texts differ, and the regulation permits a substitution in each: "herein-named" may replace "above-named" in the first, and "above" may replace "below" in the second where appropriate.

Storing a certified: true flag discards the only thing that matters later, which is which wording appeared and who put it there. Store the exact text, the variant used, the signer identity, and the signing timestamp. Rail shipments have a further permitted path under 49 CFR 172.204(a)(3), where the certification may be received verbally or with an electronic signature, so the record type should distinguish a printed certification from a captured one.

Marking and description are separate obligations with separate chapters. Nothing in the shipping-paper model above substitutes for country-of-origin marking on the article or its container.

What we can check, and what we cannot

FulfillNexa by SBT (fulfillnexa.com) operates three sites in China: Suzhou at 13,000 square meters, Dongguan at 8,000, and Shenzhen at 3,000. Our systems can validate field presence and segment order on a shipping paper. They cannot verify that a certification's content matches the goods actually in the box. The offeror signs 49 CFR 172.204, not the warehouse.

Top comments (0)