Skip to content

Write-Off

Executive Summary

Purpose & Scope

  • Support AR write-off processes that comply with GAAP, streamline approvals, and provide a robust audit trail.
  • Scope includes: Eligibility review, packet creation and management, multi-level approvals (based on commission thresholds), NetSuite GL posting, packet and recovery workflows, status tracking, email notifications, and audit documentation.
  • Out of scope: Commission booking, general AR collections, client onboarding, and tax reporting.

Objectives

  • Establish standardized eligibility and documentation requirements
  • Enforce dual-approval controls and proper authorization
  • Ensure consistent execution and data retention for reliable reporting and audits

Process Overview

Approval Flow

mermaid
graph TD
  Draft((Draft Packet))
  Submitted((Submitted))
  Agent((Agent Approval))
  DH((Dept Head Approval))
  VP((VP Client Acct Approval))
  CFO((CFO Approval))
  MD((MD Approval))
  Complete((Complete - Executed))
  Rejected((Rejected))
  Cancelled((Cancelled))

  Draft --> Submitted
  Submitted --> Agent
  Agent --> DH
  DH --> VP
  VP -->|< $50K| Complete
  VP -->|>= $50K| CFO
  CFO -->|$50K - $250K| Complete
  CFO -->|> $250K| MD
  MD --> Complete
  Agent -->|Reject| Rejected
  DH -->|Reject| Rejected
  VP -->|Reject| Rejected
  CFO -->|Reject| Rejected
  MD -->|Reject| Rejected
  Rejected -->|Resubmit| Submitted
  Rejected -->|Cancel| Cancelled

Figure 1: Packet Approval Flow with Commission-Based Routing

Core Rules & Requirements

Eligibility & Packet Creation

  • Every receivable in a packet must have one eligibility criterion and supporting documentation—either attached at the receivable level or to the packet as a whole (if the 'Packet Document' flag is set).
Eligibility CriterionDocumentation Required
AgedCollection activity log
UncollectibleClient communication or legal documentation
BankruptcyCourt document
Agent RequestFormal written agent request
  • All receivables in a packet must relate to a single client, and packet names must be unique.

  • Receivable Assignment Rule:

    • A receivable can only be part of one packet at a time unless that packet has been Cancelled or Recovered.
  • Draft packets support add/remove/edit, mass-update, justification text, full delete, and multiple saves.

Packet submission is blocked unless all above requirements are satisfied.

Approval Matrix & Workflow

IMPORTANT

Future Enhancement: Threshold-based routing is documented below but not yet implemented in code. Current implementation uses simplified approval (DRAFT/SUBMITTED → APPROVED).

Total CommissionRoute/Required Approvers
< $50,000Agent → Dept Head → VP, Client Accounting
$50,000–$250,000Agent → Dept Head → VP CA → CFO
> $250,000Agent → Dept Head → VP CA → CFO → MD
  • Approval is sequential/role-driven as per above.
  • Approver actions: approve (advance), or reject (return to Client Accounting with comments).
  • All actions/time/reason/actor logged. Each transition triggers email notifications with actionable links.
  • On resubmission, packet resumes workflow at appropriate stage.

Write-Off Execution & Integration

  • Final approval triggers automated execution:
    • The system creates a dedicated Cash Receipt of type WRITE_OFF.
      • Amount: Sum of all receivable balances (Revenue + Payables) in the packet.
      • Status: Auto-approved.
    • A corresponding Cash Receipt Worksheet is created and auto-approved (Status = 'A').
    • Applications are created for each receivable in the packet:
      • Applied Amount equals the outstanding balance (clearing the receivable).
      • Both Revenue and Payable sides of the billing item are addressed if balances exist.
    • Updates:
      • billing_item.openItemInd set to false.
      • billing_item_detail.writeOffStatusCd set to 'WRITTEN_OFF'.
      • billing_item_detail.excludeFromCeclInd set to true.
      • write_off_packet.cashReceiptId linked to the created receipt.
    • GL Posting:
      • Summarized AR posting to NetSuite via the Cash Receipt sync.
      • DR: Bad Debt Expense / CR: Accounts Receivable.

Recovery & Audit

  • Recovery (After Packet Completion):
    • When a receivable that was written off is to be recovered, the user initiates the Recover action.
    • System Action:
      • Verifies packet is in APPROVED or COMPLETE status.
      • Creates a Reversal Worksheet linked to the original Write-Off Cash Receipt.
      • Creates negative applications to reverse the original write-off amounts.
      • Updates billing_item.openItemInd to true (Reopens receivable).
      • Updates billing_item_detail.writeOffStatusCd to 'RECOVERED'.
      • Updates write_off_packet.packetStatusCd to 'RECOVERED'.
    • Result: Receivables become available for standard cash application processing (e.g., applying a new check).
    • Process:
      • Partial Recovery is not permitted: Recovery reverses all receivables in the packet; individual receivable recovery within a packet is not supported.
      • If only some receivables in a packet require recovery, the entire packet must still be recovered.
      • Any receivables within the recovered packet that do not receive a recovery payment must be re-written off by creating a new packet and following the standard approval process.

User Interface Specifications

Write-Off Packet Screens

  • Key Features:
    • Receivable search (multiple fields/filters):
      • Entity
      • Department
      • Deal
      • Client
      • Buyer
      • Agent
      • Invoice Number
      • Invoice Date Range
      • Commission Amount Min-Max Range
      • Age (Days) Min-Max Range
      • Packet Name
      • Packet Status
      • Write-Off Recommended Flag
    • Search results grid with multi-select and “Create Packet” from selection
      • Entity
      • Department
      • Deal
      • Client
      • Buyer
      • Agent
      • Invoice Number
      • Invoice Date
      • Commission Amount
      • Age (Days)
      • Write-Off Recommended Flag
      • Packet Name Link to Packet Detail View
      • Packet Status
    • Validations: all must be for one client; checks for packet name uniqueness/required docs
  • Draft Mode:
    • Packet header (name, client, totals, status)
      • Packet Name
      • Client
      • Total Commission
      • Packet Status
    • Mass eligibility criteria updates and per-row override
    • Document upload per row or for packet
    • “Submit for Approval” only enabled once all docs/criteria present

Approval Screen (All Roles)

  • Approvers access this screen either by:
    • Clicking actionable links in their email notifications (sent when a packet requires their review/approval), or
    • Searching for packets on the Write-Off Packet screen that are awaiting their approval (filtered by status).
  • The Approval screen is functionally identical to the Packet Detail View, displaying all the same fields and information.
    • The only difference is that the Packet Status field is editable, limited to the status options valid for the current approver’s role (e.g., Approve, Reject).
    • Packet Status Comment field will be required when rejecting.
  • All non-approval edits (e.g., modifying receivables, documentation, or eligibility) remain restricted to Client Accounting; approvers cannot change packet content—only status and comments.
  • After a status change, the system displays a completion/confirmation message and triggers appropriate workflow updates (e.g., advancing to next approver or notifying Client Accounting if rejected).

Recovery Processing Screen

  • Client Accounting users must search for receivables associated with a completed packet using the Write-Off Packet search screen.
  • When a completed packet is opened from the search results, the screen shows the Packet Detail View, with all fields read-only except for the Recover action.
  • The Recover action is available only to users with the Client Accounting role and only for completed packets.
  • Clicking Recover initiates the actions outlined in Recovery & Audit, including reversal of all receivables in the packet and triggering standard cash application processing.

Packet Detail View

  • Collapsible header showing all packet-level properties:

    • Packet Name
    • Client
    • Total Commission
    • Packet Status
    • Approval History (all status changes, actions, approvers, timestamps, comments)
    • Created By / Creation Date
    • Upload Documentation [Button] (for packet-level documentation)
  • Receivables in Packet List:

    • Invoice Number
    • Invoice Date
    • Commission Amount
    • Age (Days)
    • Eligibility Criterion
    • Comments:
      • Field to add or display justification for each receivable.
      • Comments are versioned: The full history for each receivable’s comments is available, showing every previous comment along with the user and timestamp.
    • Docs [Action]:
      • Button/icon for uploading, downloading, or viewing documents at the receivable level.
    • Delete (to remove individual receivable, if allowed by workflow/state)
  • Receivables-Level Actions (buttons for batch/packet operations):

    • Add Receivables
    • Remove Selected
    • Cancel
    • Delete Packet (entire packet)
    • Save as Draft
    • Submit for Approval (enabled only when all validations pass)

Data Requirements

All tables below have tracking for creation, last update, user, and status—all actions auditable.

Write-Off Packet Table (write_off_packet)

Field NameData TypeDescription
write_off_packet_id (PK)GUIDUnique identifier for the packet.
packet_nameStringUser-entered name; unique constraint.
client_id (FK)IntegerReference to Client (party_id).
packet_eligibility_criteria_cdStringDefault eligibility for packet.
cash_receipt_id (FK)IntegerLinks to the cash_receipt created upon approval.
total_commission_amtDecimalDenormalized sum of commission.
receivable_countIntegerNumber of receivables.
packet_status_cdEnumCurrent workflow state.
current_approver_roleStringRole currently required to approve.
submitted_dtDateTimeTimestamp of submission.
submitted_by_user_id (FK)IntegerUser who submitted the packet.
completed_dtDateTimeTimestamp of write-off execution.
completed_by_user_id (FK)IntegerUser who approved final completion.
recovered_dtDateTimeTimestamp of recovery execution.
recovered_by_user_id (FK)IntegerUser who initiated recovery.
rejected_dtDateTimeTimestamp of last rejection.
rejected_by_user_id (FK)IntegerUser who rejected the packet.
rejection_reasonStringReason for last rejection.

packet_status_cd Values: DRAFT, SUBMITTED, RESUBMITTED, APPROVED, APPROVED_AGENT, APPROVED_DH, APPROVED_VP, APPROVED_CFO, APPROVED_MD, REJECTED_AGENT, REJECTED_DH, REJECTED_VP, REJECTED_CFO, REJECTED_MD, COMPLETE, RECOVERED.

Field NameData TypeDescription
packet_receivable_id (PK)GUIDUnique ID.
write_off_packet_id (FK)GUIDParent packet.
billing_item_detail_id (FK)IntegerReference to the Receivable (billing_item_detail).
eligibility_criteria_cdStringAGED, UNCOLLECTIBLE, BANKRUPTCY, AGENT_REQUEST.
use_packet_document_indBooleanIf true, uses packet-level docs.

Packet Status History Table (packet_status_history)

Field NameData TypeDescription
packet_status_history_id (PK)GUIDUnique ID.
write_off_packet_id (FK)GUIDParent packet.
from_status_cdStringStatus before change.
to_status_cdStringStatus after change.
action_cdStringAction taken (SUBMIT, APPROVE, REJECT, RECOVER).
approver_roleStringRole of user taking action.
comment_textStringRejection reason or approval comment.

Validations Table

Validation RuleContextEnforcement Method
Unique packet namePacketAPI: isPacketNameUnique
All receivables share client_idPacket-Receivable linkAPI: validateForSubmission
One eligibility per receivablePacket-Receivable linkDatabase / API Schema
Documentation per receivable or packet (flagged)Packet/Packet-Receivable linkAPI: validateForSubmission
Receivable only in one packet at a timePacket-Receivable linkApp Logic / Workflow Status
Sequential approval enforcedApproval workflowService Logic

Security & Data Retention

  • Only Client Accounting users may create/edit/submit packets or process recoveries.
  • Approval and status-change restricted by user’s role and workflow state.
  • Initiators cannot approve their own submission.
  • All actions, approvals, rejections, edits, and recoveries logged with timestamp/user.
  • Data, audit trail, and docs retention per regulatory guidelines.

Gherkin Scenarios

Scenario: Successfully Write-Off an Aged Receivable

gherkin
Feature: Write-Off Packet Creation and Approval

  Scenario: User creates and approves a write-off packet for an aged invoice
    Given I am a "Client Accounting" user
    And I have a receivable for Client "XYZ Corp" with 120 days aging
    
    When I create a new write-off packet named "XYZ-2025-Q1" for Client "XYZ Corp"
    And I add the aged receivable to the packet
    And I set the eligibility criterion to "Aged"
    And I upload supporting "Collection Log" documentation
    And I submit the packet for approval
    
    Then the packet status should correspond to the lowest required approval level
    
    When the "Agent" approves the packet
    And the "Department Head" approves the packet
    And the "VP Client Accounting" approves the packet
    
    Then the packet status should be "APPROVED"
    And a "WRITE_OFF" Cash Receipt should be created
    And the receivable open balance should be 0.00
    And the receivable status should be "WRITTEN_OFF"

Scenario: Reject a Write-Off Packet

gherkin
Feature: Write-Off Packet Rejection

  Scenario: Approver rejects a packet due to insufficient documentation
    Given a write-off packet "XYZ-Draft" is in "SUBMITTED" status
    And the current approver is "Agent"
    
    When the "Agent" rejects the packet with reason "Missing latest email correspondence"
    
    Then the packet status should be "DRAFT"
    And the rejection reason should be recorded in the history
    And the "Client Accounting" user should be notified

Scenario: Recover a Written-Off Packet

gherkin
Feature: Write-Off Recovery

  Scenario: Recovering a packet that was previously written off
    Given a write-off packet "XYZ-2025-Q1" is in "APPROVED" status
    And the associated receivables are closed
    
    When I click "Recover" on the packet details page
    
    Then a "Reversal Worksheet" should be created
    And the packet status should be "RECOVERED"
    And the receivables should be reopened (Open Item Indicator = true)
    And the receivable write-off status should be "RECOVERED"

Scenario: Validation Failure on Submission

gherkin
Feature: Packet Submission Validation

  Scenario: User attempts to submit packet without unique name
    Given a write-off packet named "Existing-Name" already exists
    When I attempt to create a new packet with name "Existing-Name"
    Then the system should prevent creation
    And display an error "Packet name already exists"

  Scenario: User attempts to submit packet with mixed clients
    Given a packet created for Client A
    When I attempt to add a receivable belonging to Client B
    Then the system should prevent the addition
    And display an error "Receivable must belong to the same client"

Confidential. For internal use only.