mirror of
https://github.com/System-End/theseus.git
synced 2026-04-19 16:38:18 +00:00
Add batch-level HCB disbursement support
- HCB::BatchPurchaseService for single disbursement per batch - Add hcb_payment_account and hcb_transfer_id to batches - Wire into Letter::Batch#process! and batches controller
This commit is contained in:
parent
11c5f53074
commit
b860c9d112
7 changed files with 131 additions and 16 deletions
|
|
@ -103,9 +103,12 @@ class Letter::BatchesController < BaseBatchesController
|
|||
end
|
||||
end
|
||||
|
||||
hcb_payment_account = current_user.hcb_payment_accounts.find_by(id: letter_batch_params[:hcb_payment_account_id])
|
||||
|
||||
begin
|
||||
@batch.process!(
|
||||
payment_account: payment_account,
|
||||
hcb_payment_account: hcb_payment_account,
|
||||
us_postage_type: letter_batch_params[:us_postage_type],
|
||||
intl_postage_type: letter_batch_params[:intl_postage_type],
|
||||
template_cycle: letter_batch_params[:template_cycle].to_s.split(",").compact_blank,
|
||||
|
|
@ -219,6 +222,7 @@ class Letter::BatchesController < BaseBatchesController
|
|||
:us_postage_type,
|
||||
:intl_postage_type,
|
||||
:usps_payment_account_id,
|
||||
:hcb_payment_account_id,
|
||||
:include_qr_code,
|
||||
:print_immediately,
|
||||
:template_cycle,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
# warehouse_user_facing_title :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# hcb_payment_account_id :bigint
|
||||
# hcb_transfer_id :string
|
||||
# letter_mailer_id_id :bigint
|
||||
# letter_queue_id :bigint
|
||||
# letter_return_address_id :bigint
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_batches_on_hcb_payment_account_id (hcb_payment_account_id)
|
||||
# index_batches_on_letter_mailer_id_id (letter_mailer_id_id)
|
||||
# index_batches_on_letter_queue_id (letter_queue_id)
|
||||
# index_batches_on_letter_return_address_id (letter_return_address_id)
|
||||
|
|
@ -36,6 +39,7 @@
|
|||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (hcb_payment_account_id => hcb_payment_accounts.id)
|
||||
# fk_rails_... (letter_mailer_id_id => usps_mailer_ids.id)
|
||||
# fk_rails_... (letter_queue_id => letter_queues.id)
|
||||
# fk_rails_... (letter_return_address_id => return_addresses.id)
|
||||
|
|
@ -69,6 +73,7 @@ class Batch < ApplicationRecord
|
|||
self.inheritance_column = "type"
|
||||
belongs_to :user
|
||||
belongs_to :letter_queue, optional: true, class_name: "Letter::Queue"
|
||||
belongs_to :hcb_payment_account, class_name: "HCB::PaymentAccount", optional: true
|
||||
has_one_attached :csv
|
||||
has_one_attached :labels_pdf
|
||||
has_one_attached :pdf_document
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
# warehouse_user_facing_title :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# hcb_payment_account_id :bigint
|
||||
# hcb_transfer_id :string
|
||||
# letter_mailer_id_id :bigint
|
||||
# letter_queue_id :bigint
|
||||
# letter_return_address_id :bigint
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_batches_on_hcb_payment_account_id (hcb_payment_account_id)
|
||||
# index_batches_on_letter_mailer_id_id (letter_mailer_id_id)
|
||||
# index_batches_on_letter_queue_id (letter_queue_id)
|
||||
# index_batches_on_letter_return_address_id (letter_return_address_id)
|
||||
|
|
@ -36,6 +39,7 @@
|
|||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (hcb_payment_account_id => hcb_payment_accounts.id)
|
||||
# fk_rails_... (letter_mailer_id_id => usps_mailer_ids.id)
|
||||
# fk_rails_... (letter_queue_id => letter_queues.id)
|
||||
# fk_rails_... (letter_return_address_id => return_addresses.id)
|
||||
|
|
@ -136,7 +140,7 @@ class Letter::Batch < Batch
|
|||
raise "...we're out of money (ask Nora to put at least #{ActiveSupport::NumberHelper.number_to_currency(indicia_cost)} in the #{options[:payment_account].display_name} account!)"
|
||||
end
|
||||
|
||||
purchase_batch_indicia(options[:payment_account])
|
||||
purchase_batch_indicia(options[:payment_account], hcb_payment_account: options[:hcb_payment_account])
|
||||
end
|
||||
|
||||
# Generate PDF labels with the provided options
|
||||
|
|
@ -151,21 +155,30 @@ class Letter::Batch < Batch
|
|||
end
|
||||
|
||||
# Purchase indicia for all letters in the batch using a single payment token
|
||||
def purchase_batch_indicia(payment_account)
|
||||
# Create a single payment token for the entire batch
|
||||
payment_token = payment_account.create_payment_token
|
||||
|
||||
# Preload associations to avoid N+1 queries
|
||||
letters.includes(:address).each do |letter|
|
||||
next unless letter.postage_type == "indicia" && letter.usps_indicium.nil?
|
||||
|
||||
# Create and purchase indicia for each letter using the same payment token
|
||||
indicium = USPS::Indicium.new(
|
||||
letter: letter,
|
||||
payment_account: payment_account,
|
||||
mailing_date: letter_mailing_date,
|
||||
# If hcb_payment_account is provided, creates a single disbursement for the whole batch
|
||||
def purchase_batch_indicia(usps_payment_account, hcb_payment_account: nil)
|
||||
if hcb_payment_account.present?
|
||||
service = HCB::BatchPurchaseService.new(
|
||||
batch: self,
|
||||
hcb_payment_account: hcb_payment_account,
|
||||
usps_payment_account: usps_payment_account,
|
||||
)
|
||||
indicium.buy!(payment_token)
|
||||
unless service.call
|
||||
raise StandardError, service.errors.join(", ")
|
||||
end
|
||||
else
|
||||
payment_token = usps_payment_account.create_payment_token
|
||||
|
||||
letters.includes(:address).each do |letter|
|
||||
next unless letter.postage_type == "indicia" && letter.usps_indicium.nil?
|
||||
|
||||
indicium = USPS::Indicium.new(
|
||||
letter: letter,
|
||||
payment_account: usps_payment_account,
|
||||
mailing_date: letter_mailing_date,
|
||||
)
|
||||
indicium.buy!(payment_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
# warehouse_user_facing_title :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# hcb_payment_account_id :bigint
|
||||
# hcb_transfer_id :string
|
||||
# letter_mailer_id_id :bigint
|
||||
# letter_queue_id :bigint
|
||||
# letter_return_address_id :bigint
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_batches_on_hcb_payment_account_id (hcb_payment_account_id)
|
||||
# index_batches_on_letter_mailer_id_id (letter_mailer_id_id)
|
||||
# index_batches_on_letter_queue_id (letter_queue_id)
|
||||
# index_batches_on_letter_return_address_id (letter_return_address_id)
|
||||
|
|
@ -36,6 +39,7 @@
|
|||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (hcb_payment_account_id => hcb_payment_accounts.id)
|
||||
# fk_rails_... (letter_mailer_id_id => usps_mailer_ids.id)
|
||||
# fk_rails_... (letter_queue_id => letter_queues.id)
|
||||
# fk_rails_... (letter_return_address_id => return_addresses.id)
|
||||
|
|
|
|||
79
app/services/hcb/batch_purchase_service.rb
Normal file
79
app/services/hcb/batch_purchase_service.rb
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
class HCB::BatchPurchaseService
|
||||
class DisbursementError < StandardError; end
|
||||
|
||||
attr_reader :batch, :hcb_payment_account, :usps_payment_account, :errors
|
||||
|
||||
def initialize(batch:, hcb_payment_account:, usps_payment_account:)
|
||||
@batch = batch
|
||||
@hcb_payment_account = hcb_payment_account
|
||||
@usps_payment_account = usps_payment_account
|
||||
@errors = []
|
||||
end
|
||||
|
||||
def call
|
||||
return failure("No HCB payment account provided") unless hcb_payment_account
|
||||
return failure("No USPS payment account provided") unless usps_payment_account
|
||||
|
||||
letters_needing_indicia = batch.letters.select do |letter|
|
||||
letter.postage_type == "indicia" && letter.usps_indicium.nil?
|
||||
end
|
||||
|
||||
return true if letters_needing_indicia.empty?
|
||||
|
||||
total_cost_cents = estimate_total_cost_cents(letters_needing_indicia)
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
transfer = create_disbursement!(total_cost_cents)
|
||||
batch.update!(
|
||||
hcb_payment_account: hcb_payment_account,
|
||||
hcb_transfer_id: transfer.id,
|
||||
)
|
||||
|
||||
purchase_indicia_for_letters!(letters_needing_indicia)
|
||||
end
|
||||
|
||||
true
|
||||
rescue HCBV4::APIError => e
|
||||
failure("HCB disbursement failed: #{e.message}")
|
||||
rescue => e
|
||||
failure("Batch purchase failed: #{e.message}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_disbursement!(amount_cents)
|
||||
hcb_payment_account.create_disbursement!(
|
||||
amount_cents: amount_cents,
|
||||
memo: "Theseus batch postage: #{batch.letters.count} letters",
|
||||
)
|
||||
end
|
||||
|
||||
def purchase_indicia_for_letters!(letters)
|
||||
payment_token = usps_payment_account.create_payment_token
|
||||
|
||||
letters.each do |letter|
|
||||
indicium = USPS::Indicium.create!(
|
||||
letter: letter,
|
||||
payment_account: usps_payment_account,
|
||||
hcb_payment_account: hcb_payment_account,
|
||||
mailing_date: batch.letter_mailing_date,
|
||||
)
|
||||
indicium.buy!(payment_token)
|
||||
end
|
||||
end
|
||||
|
||||
def estimate_total_cost_cents(letters)
|
||||
letters.sum do |letter|
|
||||
if letter.processing_category == "flat"
|
||||
150
|
||||
else
|
||||
73
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def failure(message)
|
||||
@errors << message
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class AddHCBPaymentAccountToBatches < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_reference :batches, :hcb_payment_account, null: true, foreign_key: true
|
||||
add_column :batches, :hcb_transfer_id, :string
|
||||
end
|
||||
end
|
||||
6
db/schema.rb
generated
6
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_12_18_194459) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_12_18_194622) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "citext"
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
|
@ -106,6 +106,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_18_194459) do
|
|||
t.string "template_cycle", default: [], array: true
|
||||
t.string "letter_return_address_name"
|
||||
t.bigint "letter_queue_id"
|
||||
t.bigint "hcb_payment_account_id"
|
||||
t.string "hcb_transfer_id"
|
||||
t.index ["hcb_payment_account_id"], name: "index_batches_on_hcb_payment_account_id"
|
||||
t.index ["letter_mailer_id_id"], name: "index_batches_on_letter_mailer_id_id"
|
||||
t.index ["letter_queue_id"], name: "index_batches_on_letter_queue_id"
|
||||
t.index ["letter_return_address_id"], name: "index_batches_on_letter_return_address_id"
|
||||
|
|
@ -599,6 +602,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_18_194459) do
|
|||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "addresses", "batches"
|
||||
add_foreign_key "api_keys", "users"
|
||||
add_foreign_key "batches", "hcb_payment_accounts"
|
||||
add_foreign_key "batches", "letter_queues"
|
||||
add_foreign_key "batches", "return_addresses", column: "letter_return_address_id"
|
||||
add_foreign_key "batches", "users"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue