-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Problem
Nacha.File.build_batch/4 hardcodes the batch header's company_name to params.immediate_origin_name:
# deps/nacha/lib/nacha/file.ex, line 120
defp build_batch({{sec, entries}, batch_num}, file, params, offset) do
entries
|> Batch.build(
%{
...
company_name: params.immediate_origin_name, # <-- hardcoded
...
},
offset
)
In the NACHA file format, these are two distinct fields at different levels:
- File Header
immediate_origin_name(23 chars) — identifies the originator to the receiving bank - Batch Header
company_name(16 chars) — the name that appears on the end customer's bank statement
Today there is no way to set company_name independently from immediate_origin_name when using Nacha.build/3.
Real-world use case
We are integrating with Axos Bank as an ACH processor. Axos requires different company_name values depending on the transaction direction:
| Field | Debits | Credits |
|---|---|---|
immediate_origin_name (file header) |
RIVER. |
RIVER. |
company_name (batch header) |
RIVERFININCDEBT |
RIVERFININC |
Because the library conflates these two fields, we had to add a post-processing workaround that mutates the batch headers after Nacha.build/3 returns:
result = Nacha.build(entry_details, params)
apply_batch_company_name(result, company_name)
defp apply_batch_company_name({:ok, file}, company_name) do
updated_batches =
Enum.map(file.batches, fn batch ->
%{batch | header_record: %{batch.header_record | company_name: company_name}}
end)
{:ok, %{file | batches: updated_batches}}
endProposed solution
Allow an optional company_name key in the params map passed to Nacha.build/3, falling back to immediate_origin_name when not provided. This is a one-line change in build_batch/4:
# file.ex, line 120
- company_name: params.immediate_origin_name,
+ company_name: Map.get(params, :company_name, params.immediate_origin_name),And update the build_file_params type:
@type build_file_params :: %{
...
optional(:company_name) => String.t(),
...
}This is fully backwards compatible — existing callers that don't pass company_name get the same behavior as today.