""" DOT Drug & Alcohol Compliance Program binder generator. Produces the instant-delivery PDF "binder" that ships when a customer buys the $149 DOT Drug & Alcohol Compliance Program. The binder is a single, print-ready PDF that bundles everything a small motor carrier needs to run a compliant program under the applicable DOT testing regulation: 1. How to manage your program (step-by-step instructions) 2. Written drug & alcohol testing policy for employees (mode-specific) 3. Supervisor reasonable-suspicion training materials + live/online access 4. Employee Assistance Program (EAP) + rehab/treatment resources 5. Substance Abuse Professional (SAP) access for DOT violations 6. Copies / citations of the applicable regulations 7. Random testing instructions (consortium / pool management) 8. All required compliance forms (chain-of-custody, consent, refusal, etc.) 9. Recordkeeping instructions + retention schedule DOT operating administrations (policy variants): - FMCSA : 49 CFR Part 382 (motor carriers / CDL drivers) <- default - FRA : 49 CFR Part 219 (railroad / MOW) - PHMSA : 49 CFR Part 199 (pipeline) - FTA : 49 CFR Part 655 (transit) - FAA : 14 CFR Part 120 (aviation) - USCG : 46 CFR Part 16 (maritime) Plus an optional state Drug-Free Workplace Program addendum. For a trucking carrier the program is almost always FMCSA (Part 382). The ``mode`` argument selects the variant; ``state_dfwp`` appends a state Drug-Free Workplace addendum. Usage: from scripts.document_gen.templates.dot_da_binder_generator import ( generate_da_binder, ) pdf_path = generate_da_binder( output_path="/tmp/da_binder.pdf", carrier_name="Acme Trucking LLC", dot_number="1234567", mode="fmcsa", cdl_drivers=4, der_name="Jane Owner", der_title="Owner / Designated Employer Representative", provider_name="Performance West Consortium", ) """ from __future__ import annotations import logging from datetime import datetime from pathlib import Path from typing import Optional LOG = logging.getLogger("document_gen.da_binder") # ── Mode metadata ────────────────────────────────────────────────────────── # Each DOT operating administration has its own testing rule. The binder text # is written for the chosen mode; FMCSA is the trucking default. MODE_META: dict[str, dict[str, str]] = { "fmcsa": { "agency": "Federal Motor Carrier Safety Administration (FMCSA)", "part": "49 CFR Part 382", "procedures": "49 CFR Part 40", "covered": "safety-sensitive employees who operate a commercial motor " "vehicle (CMV) requiring a commercial driver's license (CDL)", "function": "operating a commercial motor vehicle requiring a CDL", "clearinghouse": True, }, "fra": { "agency": "Federal Railroad Administration (FRA)", "part": "49 CFR Part 219", "procedures": "49 CFR Part 40", "covered": "employees who perform covered service (train and engine, " "signal, dispatching, and maintenance-of-way functions)", "function": "performing FRA-covered service", "clearinghouse": False, }, "phmsa": { "agency": "Pipeline and Hazardous Materials Safety Administration (PHMSA)", "part": "49 CFR Part 199", "procedures": "49 CFR Part 40", "covered": "employees who perform operation, maintenance, or emergency-" "response functions on a pipeline or LNG facility", "function": "performing PHMSA-covered pipeline functions", "clearinghouse": False, }, "fta": { "agency": "Federal Transit Administration (FTA)", "part": "49 CFR Part 655", "procedures": "49 CFR Part 40", "covered": "employees who perform safety-sensitive functions for a " "recipient of FTA funding", "function": "performing FTA safety-sensitive functions", "clearinghouse": False, }, "faa": { "agency": "Federal Aviation Administration (FAA)", "part": "14 CFR Part 120", "procedures": "49 CFR Part 40", "covered": "employees who perform safety-sensitive aviation functions", "function": "performing FAA safety-sensitive functions", "clearinghouse": False, }, "uscg": { "agency": "United States Coast Guard (USCG)", "part": "46 CFR Part 16", "procedures": "49 CFR Part 40", "covered": "crewmembers who occupy or fill a position that affects the " "safe operation of a vessel", "function": "occupying a safety-sensitive crew position", "clearinghouse": False, }, } # The five DOT test types & six prohibited-conduct categories are shared by all # modes (Part 40 + the mode rule). Random rates differ by mode/year; FMCSA 2024+ # is 50% drug / 10% alcohol of the average number of covered positions. _RANDOM_RATES = { "fmcsa": "50% (controlled substances) and 10% (alcohol)", "fra": "the FRA-published annual minimum random testing rates", "phmsa": "50% (controlled substances) of covered employees", "fta": "50% (controlled substances) and 10% (alcohol)", "faa": "the FAA-published annual minimum random testing rates", "uscg": "the USCG annual minimum random testing rate (currently 25%)", } def generate_da_binder( *, output_path: str, carrier_name: str, dot_number: str = "", mode: str = "fmcsa", cdl_drivers: int | str = "", owner_operators: int | str = "", der_name: str = "", der_title: str = "Designated Employer Representative (DER)", provider_name: str = "Performance West Consortium / C-TPA", state_dfwp: str = "", ) -> str | None: """Render the D&A compliance binder PDF. Returns the path on success.""" try: from reportlab.lib.colors import HexColor from reportlab.lib.enums import TA_CENTER, TA_LEFT from reportlab.lib.pagesizes import letter from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet from reportlab.lib.units import inch from reportlab.platypus import ( BaseDocTemplate, Frame, ListFlowable, ListItem, PageBreak, PageTemplate, Paragraph, Spacer, Table, TableStyle, ) except ImportError: LOG.warning("reportlab not installed — D&A binder generation unavailable") return None mode = (mode or "fmcsa").lower().strip() meta = MODE_META.get(mode, MODE_META["fmcsa"]) random_rate = _RANDOM_RATES.get(mode, _RANDOM_RATES["fmcsa"]) today = datetime.now().strftime("%B %d, %Y") NAVY = HexColor("#0b1f3a") BLUE = HexColor("#1d4ed8") SLATE = HexColor("#475569") LIGHT = HexColor("#eef2f7") styles = getSampleStyleSheet() h1 = ParagraphStyle( "H1", parent=styles["Heading1"], fontName="Helvetica-Bold", fontSize=18, textColor=NAVY, spaceBefore=4, spaceAfter=10, leading=22, ) h2 = ParagraphStyle( "H2", parent=styles["Heading2"], fontName="Helvetica-Bold", fontSize=13, textColor=NAVY, spaceBefore=14, spaceAfter=6, leading=16, ) h3 = ParagraphStyle( "H3", parent=styles["Heading3"], fontName="Helvetica-Bold", fontSize=11, textColor=BLUE, spaceBefore=10, spaceAfter=4, leading=14, ) body = ParagraphStyle( "Body", parent=styles["BodyText"], fontName="Helvetica", fontSize=9.5, leading=14, spaceAfter=6, textColor=HexColor("#1f2937"), ) body_i = ParagraphStyle("BodyI", parent=body, fontName="Helvetica-Oblique") small = ParagraphStyle( "Small", parent=body, fontSize=8, textColor=SLATE, leading=11, ) cover_title = ParagraphStyle( "CoverTitle", parent=h1, fontSize=26, alignment=TA_CENTER, leading=30, ) cover_sub = ParagraphStyle( "CoverSub", parent=body, fontSize=13, alignment=TA_CENTER, textColor=SLATE, leading=18, ) def bullets(items: list[str], style=body) -> ListFlowable: return ListFlowable( [ListItem(Paragraph(t, style), leftIndent=10) for t in items], bulletType="bullet", bulletColor=BLUE, leftIndent=14, bulletFontSize=7, spaceAfter=4, ) def numbered(items: list[str], style=body) -> ListFlowable: return ListFlowable( [ListItem(Paragraph(t, style)) for t in items], bulletType="1", leftIndent=18, spaceAfter=4, ) story: list = [] # ── Cover ────────────────────────────────────────────────────────────── story.append(Spacer(1, 1.4 * inch)) story.append(Paragraph("DOT Drug & Alcohol", cover_title)) story.append(Paragraph("Compliance Program Binder", cover_title)) story.append(Spacer(1, 0.25 * inch)) story.append(Paragraph( f"Prepared under {meta['part']} and the U.S. DOT procedures at " f"{meta['procedures']}", cover_sub)) story.append(Spacer(1, 0.55 * inch)) cover_rows = [ ["Motor Carrier", carrier_name or "—"], ["USDOT Number", str(dot_number) or "—"], ["Regulating Agency", meta["agency"]], ["Covered Employees", _covered_count(cdl_drivers, owner_operators)], ["Designated Employer Rep.", f"{der_name or '—'}" + (f" — {der_title}" if der_name else "")], ["Testing Program / C-TPA", provider_name], ["Program Effective Date", today], ] t = Table(cover_rows, colWidths=[2.1 * inch, 3.9 * inch]) t.setStyle(TableStyle([ ("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"), ("FONTNAME", (1, 0), (1, -1), "Helvetica"), ("FONTSIZE", (0, 0), (-1, -1), 10), ("TEXTCOLOR", (0, 0), (0, -1), NAVY), ("TEXTCOLOR", (1, 0), (1, -1), HexColor("#1f2937")), ("BACKGROUND", (0, 0), (0, -1), LIGHT), ("LINEBELOW", (0, 0), (-1, -1), 0.5, HexColor("#cbd5e1")), ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), ("TOPPADDING", (0, 0), (-1, -1), 7), ("BOTTOMPADDING", (0, 0), (-1, -1), 7), ("LEFTPADDING", (0, 0), (-1, -1), 10), ])) story.append(t) story.append(Spacer(1, 0.5 * inch)) story.append(Paragraph( "Prepared by Performance West Inc. This binder is a compliance " "resource, not legal advice. The motor carrier remains responsible " "for implementing and maintaining its program.", small)) story.append(PageBreak()) # ── Table of contents ────────────────────────────────────────────────── story.append(Paragraph("What's Inside This Binder", h1)) toc = [ "Section 1 — How to Manage Your Program. Step-by-step setup and " "ongoing operating instructions for the Designated Employer " "Representative (DER).", "Section 2 — Written Testing Policy. Your company drug & " "alcohol testing policy to distribute to every covered employee.", "Section 3 — When Testing Is Required. The six DOT test " "scenarios and what triggers each one.", "Section 4 — Random Testing Program. How the consortium random " "pool works and your obligations.", "Section 5 — Supervisor Training. Reasonable-suspicion training " "materials and how to access live/online training.", "Section 6 — Violations, SAP & Return-to-Duty. What happens " "after a positive test, and Substance Abuse Professional access.", "Section 7 — EAP, Rehab & Treatment Resources. Employee " "Assistance Program information and treatment referrals.", "Section 8 — Recordkeeping. What to keep, where, and for how long.", "Section 9 — Required Forms. Every form you need, ready to use.", "Section 10 — The Regulations. Citations and how to read the " "actual rule text.", ] story.append(bullets(toc)) if state_dfwp: story.append(Spacer(1, 4)) story.append(Paragraph( f"Addendum — {state_dfwp} Drug-Free Workplace Program. " "State-specific policy supplement that runs alongside your DOT " "program.", body)) story.append(PageBreak()) # ── Section 1 — Manage your program ──────────────────────────────────── story.append(Paragraph("Section 1 — How to Manage Your Program", h1)) story.append(Paragraph( f"As a motor carrier subject to {meta['part']}, you must operate a " "drug and alcohol testing program covering every " f"{meta['covered']}. The person who runs the program day-to-day is the " "Designated Employer Representative (DER). Follow these steps.", body)) story.append(Paragraph("Initial setup (do once)", h3)) story.append(numbered([ "Name your DER. Record the DER's name and contact information " "(this binder lists them on the cover). The DER receives test results " "and removes employees from safety-sensitive duty when required.", "Adopt the written policy in Section 2. Sign it, date it, and " "keep the signed master copy.", "Distribute the policy to every covered employee and collect a " "signed acknowledgment (Form A in Section 9). Give a copy to each new " "hire before they perform safety-sensitive duties.", "Join a testing program / consortium (C-TPA). Your program is " f"administered through {provider_name}, which manages your random " "pool, scheduling, collection sites, the lab, and the Medical Review " "Officer (MRO).", "Conduct pre-employment drug tests with a verified negative " "result before any new covered employee performs safety-sensitive " "functions.", ] + ([ "Register with the FMCSA Clearinghouse at " "clearinghouse.fmcsa.dot.gov and run the required queries (see below)." ] if meta.get("clearinghouse") else []))) story.append(Paragraph("Ongoing operations (every year)", h3)) ongoing = [ "Conduct random tests throughout the year at the required " f"minimum annual rate of {random_rate} of covered positions, spread " "evenly across the year (see Section 4).", "Test for reasonable suspicion when a trained supervisor " "observes the signs (see Section 5).", "Conduct post-accident testing when DOT criteria are met " "(see Section 3).", "Run return-to-duty and follow-up tests for any employee who " "violated the rule and completed the SAP process (see Section 6).", "Keep all records for the required retention periods " "(see Section 8).", ] if meta.get("clearinghouse"): ongoing.append( "Run an annual FMCSA Clearinghouse query on every CDL " "driver, and a full query (with driver consent) before " "hiring any new driver. Report violations and refusals to the " "Clearinghouse.") story.append(bullets(ongoing)) story.append(PageBreak()) # ── Section 2 — Written policy ───────────────────────────────────────── story.append(Paragraph("Section 2 — Written Drug & Alcohol Testing Policy", h1)) story.append(Paragraph( f"This is the policy {carrier_name or 'the Company'} adopts and gives " f"to every covered employee. It satisfies the written-policy " f"requirement of {meta['part']} and {meta['procedures']}.", body_i)) story.append(Paragraph("1. Purpose and Authority", h3)) story.append(Paragraph( f"{carrier_name or 'The Company'} (the \"Company\") is committed to a " "safe, drug- and alcohol-free workplace. This policy implements the " f"requirements of the {meta['agency']} at {meta['part']} and the U.S. " f"Department of Transportation testing procedures at {meta['procedures']}. " "Where this policy conflicts with DOT regulations, the regulations " "govern.", body)) story.append(Paragraph("2. Who Is Covered", h3)) story.append(Paragraph( f"This policy applies to every {meta['covered']}. A covered employee " f"is subject to testing whenever {meta['function']}.", body)) story.append(Paragraph("3. Prohibited Conduct", h3)) story.append(Paragraph("A covered employee must not:", body)) story.append(bullets([ "Report for duty or remain on duty with an alcohol concentration of " "0.04 or greater.", "Use alcohol while performing safety-sensitive functions, within " "4 hours before, or within 8 hours after an accident (or until " "tested).", "Use any illegal drug, or use a controlled substance unless under a " "valid prescription consistent with safe operation.", "Report for duty or remain on duty when using a controlled substance " "that the prescribing physician has not authorized as consistent with " "safe performance.", "Refuse to submit to a required test (a refusal is treated the same as " "a verified positive test).", "Test positive on any DOT-required drug or alcohol test.", ])) story.append(Paragraph("4. Required Tests", h3)) story.append(Paragraph( "The Company conducts the following DOT tests: pre-employment, random, " "reasonable suspicion, post-accident, return-to-duty, and follow-up. " "Section 3 explains when each applies. Drug tests screen for marijuana, " "cocaine, opioids, amphetamines, and phencyclidine (PCP) using the " "DOT 5-panel and are verified by a Medical Review Officer (MRO).", body)) story.append(Paragraph("5. Consequences of a Violation", h3)) story.append(Paragraph( "An employee who has a verified positive test, an alcohol " "concentration of 0.04 or greater, or who refuses a test is " "immediately removed from safety-sensitive duty. The employee may not " "return until they complete the return-to-duty process with a DOT-" "qualified Substance Abuse Professional (SAP) and pass a return-to-duty " "test (see Section 6).", body)) story.append(Paragraph("6. Designated Employer Representative", h3)) story.append(Paragraph( f"The Company's DER is {der_name or '________________________'}" f"{(', ' + der_title) if der_name else ''}. The DER receives test " "results, schedules tests, and removes employees from duty as required. " "Questions about this policy should be directed to the DER.", body)) story.append(Paragraph("7. Employee Assistance", h3)) story.append(Paragraph( "Information about substance abuse, its effects, and available " "treatment and Employee Assistance resources is provided in Section 7 " "of this binder and is available from the DER.", body)) story.append(PageBreak()) # ── Section 3 — When testing is required ─────────────��───────────────── story.append(Paragraph("Section 3 — When Testing Is Required", h1)) test_table = [ ["Test Type", "When It Happens"], ["Pre-employment", "Before a new covered employee first performs safety-sensitive " "functions. Drug test with a verified negative result is required."], ["Random", "Unannounced, spread throughout the year, selected by the consortium " "from the random pool (see Section 4)."], ["Reasonable suspicion", "When a trained supervisor observes specific, articulable signs of " "drug or alcohol use (see Section 5)."], ["Post-accident", "After a DOT-recordable accident meeting the rule's criteria " "(fatality; citation + injury requiring medical treatment away from " "the scene; or citation + a vehicle towed from the scene). Alcohol " "test within 8 hours, drug test within 32 hours."], ["Return-to-duty", "Before an employee who violated the rule may return to safety-" "sensitive duty, after completing the SAP process. Directly observed."], ["Follow-up", "A SAP-directed schedule of unannounced tests (at least 6 in the " "first 12 months) after return to duty. Directly observed."], ] tt = Table(test_table, colWidths=[1.5 * inch, 4.5 * inch]) tt.setStyle(TableStyle([ ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ("FONTNAME", (0, 1), (0, -1), "Helvetica-Bold"), ("FONTSIZE", (0, 0), (-1, -1), 9), ("TEXTCOLOR", (0, 0), (-1, 0), HexColor("#ffffff")), ("BACKGROUND", (0, 0), (-1, 0), NAVY), ("BACKGROUND", (0, 1), (0, -1), LIGHT), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("GRID", (0, 0), (-1, -1), 0.4, HexColor("#cbd5e1")), ("TOPPADDING", (0, 0), (-1, -1), 6), ("BOTTOMPADDING", (0, 0), (-1, -1), 6), ("LEFTPADDING", (0, 0), (-1, -1), 7), ("RIGHTPADDING", (0, 0), (-1, -1), 7), ])) story.append(tt) story.append(PageBreak()) # ── Section 4 — Random testing ───────────────────────────────────────── story.append(Paragraph("Section 4 — Random Testing Program", h1)) story.append(Paragraph( f"Random testing is the backbone of your program. The minimum annual " f"random testing rate is {random_rate} of your average number of " "covered positions. Because most small carriers cannot meet the " "spread-and-rate requirements on their own, your covered employees are " f"enrolled in a random pool managed by {provider_name} " "(a Consortium/Third-Party Administrator, or C-TPA).", body)) story.append(Paragraph("How the random pool works", h3)) story.append(numbered([ "Every covered employee is placed in the consortium's random pool.", "Each selection period, the consortium randomly selects names using a " "scientifically valid method.", "Selected employees are notified and must proceed immediately " "to the collection site — no advance notice, no delay.", "Selections are spread reasonably throughout the year so testing is " "truly unannounced and unpredictable.", "The consortium tracks the rate and gives you a year-end summary for " "your records and any DOT audit.", ])) story.append(Paragraph("Your responsibilities", h3)) story.append(bullets([ "Keep the consortium's roster of covered employees current — add new " "hires and remove departures promptly.", "Send selected employees to testing right away and document that they " "went.", "Never select or 'volunteer' specific people — selection must stay " "random.", "Keep the consortium's selection and results records (see Section 8).", ])) story.append(PageBreak()) # ── Section 5 — Supervisor training ──────────────────────────────────── story.append(Paragraph("Section 5 — Supervisor Reasonable-Suspicion Training", h1)) story.append(Paragraph( f"{meta['part']} requires that any supervisor who may make a " "reasonable-suspicion testing decision complete at least 60 minutes " "of training on the symptoms of alcohol misuse and at least " "60 minutes on the symptoms of controlled-substance use " "(120 minutes total). Keep a record of who was trained and when.", body)) story.append(Paragraph("Signs a trained supervisor watches for", h3)) story.append(bullets([ "Appearance: bloodshot or watery eyes, dilated/constricted " "pupils, flushed face, disheveled clothing, drug paraphernalia.", "Behavior: mood swings, agitation, euphoria, drowsiness, " "paranoia, unusual or unsafe risk-taking.", "Speech: slurred, rapid, incoherent, or unusually talkative.", "Coordination: unsteady gait, tremors, poor balance, fumbling.", "Odor: alcohol or marijuana odor on breath, body, or clothing.", "Performance: sudden decline, missed deadlines, accidents, " "near-misses, frequent absences.", ])) story.append(Paragraph("Making a reasonable-suspicion decision", h3)) story.append(numbered([ "Two trained supervisors are not required — one trained supervisor's " "specific, contemporaneous, articulable observations are enough.", "Document the observations in writing immediately (Form D in " "Section 9), signed and dated within 24 hours.", "Direct the employee to testing and do not let them drive themselves.", "Remove the employee from safety-sensitive duty pending results.", ])) story.append(Paragraph("Live / online supervisor training access", h3)) story.append(Paragraph( "Your program includes access to DOT-compliant supervisor training. " "To complete the required 120-minute course online (or schedule a live " "session), contact us and we will send each supervisor an enrollment " "link and issue a completion certificate for your records:", body)) story.append(bullets([ "Email: compliance@performancewest.com — subject line " "\"Supervisor Training\" with your USDOT number.", "What you get: self-paced online modules, a knowledge check, " "and a dated certificate of completion to keep on file.", ])) story.append(PageBreak()) # ── Section 6 — Violations / SAP ─────────────────────────────────────── story.append(Paragraph("Section 6 — Violations, SAP & Return-to-Duty", h1)) story.append(Paragraph( "If a covered employee has a verified positive test, an alcohol " "concentration of 0.04 or greater, or refuses a test, you must " "immediately remove them from safety-sensitive duty. They cannot " "return until they complete the DOT return-to-duty process.", body)) story.append(Paragraph("The return-to-duty process", h3)) story.append(numbered([ "Remove from duty and give the employee the contact information " "for a DOT-qualified Substance Abuse Professional (SAP).", "SAP evaluation. The SAP evaluates the employee and prescribes " "education and/or treatment.", "Treatment / education is completed as directed by the SAP.", "Follow-up evaluation. The SAP confirms the employee complied " "and is eligible to return.", "Return-to-duty test. The employee must pass a directly " "observed return-to-duty test before resuming duties.", "Follow-up testing. The SAP sets an unannounced follow-up " "testing schedule (at least 6 tests in the first 12 months, for up to " "5 years).", ])) story.append(Paragraph("SAP access", h3)) story.append(Paragraph( "Your program includes access to a network of DOT-qualified Substance " "Abuse Professionals. To get a SAP referral for an employee, contact " "the DER or email compliance@performancewest.com with your USDOT " "number; we will provide qualified SAP contacts in the employee's " "area. You can also locate a SAP through the DOT and industry " "directories listed in Section 7.", body)) if meta.get("clearinghouse"): story.append(Paragraph("FMCSA Clearinghouse reporting", h3)) story.append(Paragraph( "Report the violation, refusal, the SAP's name, the negative " "return-to-duty test, and completion of follow-up testing to the " "FMCSA Clearinghouse within the required timeframes. A driver with " "an unresolved Clearinghouse violation is 'prohibited' and may not " "operate a CMV.", body)) story.append(PageBreak()) # ── Section 7 — EAP / rehab ──────────────────────────────────────────── story.append(Paragraph("Section 7 — EAP, Rehab & Treatment Resources", h1)) story.append(Paragraph( "DOT rules require that you give covered employees educational " "materials that explain the requirements of the rule and the " "employer's policies and procedures, plus information on the effects " "of alcohol and controlled-substance use and available resources. Use " "the resources below and share them with employees.", body)) story.append(Paragraph("National help lines and directories", h3)) story.append(bullets([ "SAMHSA National Helpline: 1-800-662-HELP (4357) — free, " "confidential, 24/7 treatment referral and information service.", "SAMHSA treatment locator: findtreatment.gov — searchable " "directory of treatment facilities by location.", "988 Suicide & Crisis Lifeline: call or text 988.", "Alcoholics Anonymous: aa.org — local meeting finder.", "Narcotics Anonymous: na.org — local meeting finder.", "DOT SAP information: transportation.gov/odapc — Office of " "Drug & Alcohol Policy and Compliance, including the SAP and " "return-to-duty guidance.", ])) story.append(Paragraph("Employee Assistance Program (EAP)", h3)) story.append(Paragraph( "An EAP gives employees confidential access to counseling and referral " "services. If your company offers an EAP, list its contact " "information on the acknowledgment you give employees. If you do not " "have an EAP, the national resources above and a SAP referral satisfy " "the educational and referral expectations of the rule. We can help " "you add a low-cost EAP — email compliance@performancewest.com.", body)) story.append(PageBreak()) # ── Section 8 — Recordkeeping ────────────────────────────────────────── story.append(Paragraph("Section 8 — Recordkeeping", h1)) story.append(Paragraph( "Keep your records organized and secure; you must produce them in a " "DOT audit. The DER (or your C-TPA on your behalf) maintains them. " "Retention periods under Part 40 / the mode rule:", body)) rec_table = [ ["Record", "Keep For"], ["Verified positive test results; refusals; SAP reports; " "return-to-duty and follow-up test records; alcohol tests of 0.02+", "5 years"], ["Random selection records and the annual random testing rate " "calculation", "5 years"], ["Reasonable-suspicion and post-accident test records", "5 years"], ["EBT calibration / collection-site documentation", "5 years"], ["Annual MIS summary (if required of your operation)", "5 years"], ["Negative test results and cancelled tests", "1 year"], ["Supervisor training records", "While employed + as required"], ["Employee policy acknowledgments", "Duration of employment + 1 year"], ] rt = Table(rec_table, colWidths=[4.4 * inch, 1.6 * inch]) rt.setStyle(TableStyle([ ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ("FONTSIZE", (0, 0), (-1, -1), 9), ("TEXTCOLOR", (0, 0), (-1, 0), HexColor("#ffffff")), ("BACKGROUND", (0, 0), (-1, 0), NAVY), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("GRID", (0, 0), (-1, -1), 0.4, HexColor("#cbd5e1")), ("ROWBACKGROUNDS", (0, 1), (-1, -1), [HexColor("#ffffff"), LIGHT]), ("TOPPADDING", (0, 0), (-1, -1), 6), ("BOTTOMPADDING", (0, 0), (-1, -1), 6), ("LEFTPADDING", (0, 0), (-1, -1), 7), ])) story.append(rt) story.append(Spacer(1, 8)) story.append(Paragraph( "Store records so they are confidential and retrievable. Your C-TPA " "keeps a parallel set of consortium records; request a copy any time.", small)) story.append(PageBreak()) # ── Section 9 — Forms ────────────────────────────────────────────────── story.append(Paragraph("Section 9 — Required Compliance Forms", h1)) story.append(Paragraph( "The forms on the following pages are ready to print and use. The " "federal chain-of-custody and alcohol-testing forms (the CCF and the " "DOT Alcohol Testing Form) are provided by the collection site and " "lab; you do not print those yourself, but your C-TPA supplies them at " "each collection.", body)) story.append(bullets([ "Form A — Employee Policy Receipt & Acknowledgment.", "Form B — Pre-Employment Testing Consent & Prior-Employer " "Inquiry.", "Form C — Test Notification / Authorization to Test.", "Form D — Reasonable-Suspicion Observation Record.", "Form E — Post-Accident Testing Decision Worksheet.", "Form F — Supervisor Training Completion Record.", ])) _append_form_a(story, h2, h3, body, small, carrier_name, meta) _append_form_d(story, h2, body, small, carrier_name) _append_form_e(story, h2, body, small, carrier_name) # ── Section 10 — Regulations ─────────────────────────────────────────── story.append(Paragraph("Section 10 — The Regulations", h1)) story.append(Paragraph( "Your program is governed by two rules that work together: the DOT-" f"wide testing procedures at {meta['procedures']}, and the mode rule " f"at {meta['part']} from the {meta['agency']}. Read the official, " "always-current text on the free government sites below.", body)) story.append(bullets([ f"{meta['procedures']} (DOT testing procedures): " "ecfr.gov/current/title-49/subtitle-A/part-40", f"{meta['part']} (your mode rule): ecfr.gov — search the part " "number; this is the rule that defines who is covered and the testing " "scenarios.", "DOT ODAPC (plain-language guidance, forms, brochures): " "transportation.gov/odapc", ] + ([ "FMCSA Drug & Alcohol Clearinghouse: " "clearinghouse.fmcsa.dot.gov", "FMCSA testing overview: fmcsa.dot.gov/regulations/" "drug-alcohol-testing-program", ] if meta.get("clearinghouse") else []))) story.append(Spacer(1, 10)) story.append(Paragraph( "Regulations change. This binder reflects the rules in effect on " f"{today}. Always confirm the current rule text on ecfr.gov, and " "contact us with any questions at compliance@performancewest.com.", small)) # ── Optional state DFWP addendum ─────────────────────────────────────── if state_dfwp: story.append(PageBreak()) story.append(Paragraph( f"Addendum — {state_dfwp} Drug-Free Workplace Program", h1)) story.append(Paragraph( f"In addition to your federal DOT program, " f"{carrier_name or 'the Company'} maintains a Drug-Free Workplace " f"Program consistent with {state_dfwp} law. This program runs " "alongside (and does not " "replace) the DOT testing program in this binder.", body)) story.append(bullets([ "The Company prohibits the unlawful manufacture, distribution, " "possession, or use of controlled substances in the workplace.", "Employees must notify the Company of any criminal drug statute " "conviction for a violation occurring in the workplace as required " "by state law.", "The Company makes a good-faith effort to maintain a drug-free " "workplace through this policy, the awareness materials in Section " "7, and available counseling and treatment resources.", f"State-specific notice, testing, and appeal rights under " f"{state_dfwp} law apply where they exceed federal requirements; " "DOT rules always govern DOT-required tests.", ])) story.append(Spacer(1, 8)) story.append(Paragraph( f"Confirm your {state_dfwp} program registration, premium-discount, " "and notice requirements with the state agency or your insurer. We " "can help — email compliance@performancewest.com.", small)) # ── Footer drawing ───────────────────────────────────────────────────── def _decorations(canvas, doc): canvas.saveState() w, h = letter # top accent bar (skip cover) if doc.page > 1: canvas.setFillColor(NAVY) canvas.rect(0, h - 0.28 * inch, w, 0.28 * inch, fill=1, stroke=0) canvas.setFillColor(HexColor("#ffffff")) canvas.setFont("Helvetica-Bold", 8) canvas.drawString(0.85 * inch, h - 0.19 * inch, "DOT Drug & Alcohol Compliance Program") canvas.drawRightString(w - 0.85 * inch, h - 0.19 * inch, (carrier_name or "")[:48]) # footer canvas.setFillColor(SLATE) canvas.setFont("Helvetica", 7.5) canvas.drawString(0.85 * inch, 0.42 * inch, f"Performance West Inc. • {meta['part']}") canvas.drawRightString(w - 0.85 * inch, 0.42 * inch, f"Page {doc.page}") canvas.setStrokeColor(HexColor("#cbd5e1")) canvas.setLineWidth(0.4) canvas.line(0.85 * inch, 0.58 * inch, w - 0.85 * inch, 0.58 * inch) canvas.restoreState() out = Path(output_path) out.parent.mkdir(parents=True, exist_ok=True) doc = BaseDocTemplate( str(out), pagesize=letter, leftMargin=0.85 * inch, rightMargin=0.85 * inch, topMargin=0.55 * inch, bottomMargin=0.75 * inch, title="DOT Drug & Alcohol Compliance Program", author="Performance West Inc.", ) frame = Frame( doc.leftMargin, doc.bottomMargin, doc.width, doc.height - 0.18 * inch, id="body", ) doc.addPageTemplates([ PageTemplate(id="main", frames=[frame], onPage=_decorations) ]) doc.build(story) LOG.info("Generated D&A binder (%s mode) -> %s", mode, out) return str(out) # ── Helpers ──────────────────────────────────────────────────────────────── def _covered_count(cdl_drivers, owner_operators) -> str: try: n = int(str(cdl_drivers).strip() or 0) except (TypeError, ValueError): n = 0 try: oo = int(str(owner_operators).strip() or 0) except (TypeError, ValueError): oo = 0 total = n + oo if total <= 0: return "—" extra = f" ({oo} owner-operator)" if oo else "" return f"{total} covered position(s){extra}" def _append_form_a(story, h2, h3, body, small, carrier_name, meta): from reportlab.lib.units import inch from reportlab.platypus import PageBreak, Paragraph, Spacer story.append(PageBreak()) story.append(Paragraph("Form A — Employee Policy Receipt & Acknowledgment", h2)) story.append(Paragraph( f"I acknowledge that I have received, read, and understand the " f"{carrier_name or 'Company'} Drug & Alcohol Testing Policy adopted " f"under {meta['part']}. I understand that I am subject to pre-" "employment, random, reasonable-suspicion, post-accident, return-to-" "duty, and follow-up testing, and that a positive test or refusal will " "remove me from safety-sensitive duty.", body)) story.append(Spacer(1, 22)) for label in ["Employee name (print): ______________________________________", "Employee signature: _________________________________________", "Date: ____________________ Position: ____________________", "DER / witness: ______________________________________________"]: story.append(Paragraph(label, body)) story.append(Spacer(1, 10)) story.append(Paragraph( "Keep the signed original in the employee's file for the duration of " "employment plus one year.", small)) def _append_form_d(story, h2, body, small, carrier_name): from reportlab.platypus import PageBreak, Paragraph, Spacer story.append(PageBreak()) story.append(Paragraph("Form D — Reasonable-Suspicion Observation Record", h2)) story.append(Paragraph( "Complete immediately when directing an employee to a reasonable-" "suspicion test. Must be signed and dated within 24 hours.", body)) story.append(Spacer(1, 10)) for label in [ "Employee observed: __________________________________________", "Date / time of observation: _________________________________", "Location: ____________________________________________________", "Specific observations (appearance, behavior, speech, odor, " "coordination, performance):", "_____________________________________________________________", "_____________________________________________________________", "_____________________________________________________________", "Test directed: [ ] Drug [ ] Alcohol [ ] Both", "Trained supervisor (print): _________________________________", "Supervisor signature: ________________________ Date: _______", ]: story.append(Paragraph(label, body)) story.append(Spacer(1, 8)) story.append(Paragraph( "Retain for 5 years. The supervisor must have completed the required " "120 minutes of reasonable-suspicion training (see Form F).", small)) def _append_form_e(story, h2, body, small, carrier_name): from reportlab.platypus import PageBreak, Paragraph, Spacer story.append(PageBreak()) story.append(Paragraph("Form E — Post-Accident Testing Decision Worksheet", h2)) story.append(Paragraph( "Use immediately after any accident to decide whether DOT post-" "accident testing is required. Test if ANY box below is checked.", body)) story.append(Spacer(1, 8)) for label in [ "[ ] The accident involved a fatality.", "[ ] The driver received a citation AND a person was " "injured and required medical treatment away from the scene.", "[ ] The driver received a citation AND a vehicle was " "towed from the scene due to disabling damage.", ]: story.append(Paragraph(label, body)) story.append(Spacer(1, 4)) story.append(Spacer(1, 8)) story.append(Paragraph( "If testing is required: alcohol test as soon as possible " "(within 8 hours) and drug test within 32 hours. Document any reason a " "required test could not be completed.", body)) story.append(Spacer(1, 12)) for label in [ "Driver: ____________________________ Date/time of accident: ______", "Decision: [ ] Test required [ ] Not required", "DER (print): ______________________ Signature: __________________", ]: story.append(Paragraph(label, body)) story.append(Spacer(1, 10)) story.append(Paragraph("Retain for 5 years.", small))