When a customer needs to switch from Credit Card to Purchase Order (PO) payment, simply updating the company settings or sending an invoice isn't enough. The order itself has payment configuration that controls whether it will be automatically charged by the RecurringCardChargeJob.
The RecurringCardChargeJob (runs daily at 8am) automatically charges orders that meet these criteria:
# From app/jobs/recurring_card_charge_job.rb
Order.active_rentals
.where(is_recurring: true)
.where(payment_method: "credit_card") # ← KEY FILTER
.where("next_billing_date <= ?", Date.current)
Key takeaway: If payment_method is "credit_card", the order will be auto-charged regardless of invoices sent or customer requests.
ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)
puts \"Current payment_method: #{o.payment_method}\"
puts \"Current payment_terms: #{o.payment_terms}\"
puts \"Current payment_status: #{o.payment_status}\"
"'
ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)
# Update payment configuration
o.payment_method = \"purchase_order\"
o.payment_terms = \"net_30\" # or net_15, net_60, on_receipt
o.payment_profile_id = nil # Clear credit card reference
o.payment_status = \"unpaid\" # Invoice sent, awaiting payment
o.save!
puts \"✓ Order switched to Purchase Order payment\"
"'
ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(ORDER_ID)
puts \"payment_method: #{o.payment_method}\"
puts \"Will be auto-charged: #{o.payment_method == \"credit_card\" ? \"YES\" : \"NO\"}\"
"'
From app/models/order.rb:
PAYMENT_METHODS = %w[
credit_card
purchase_order
].freeze
From app/models/order.rb:
PAYMENT_TERMS = %w[
on_receipt
net_15
net_30
net_60
].freeze
Save this as scripts/switch_order_to_po.rb:
# Usage: bin/rails runner scripts/switch_order_to_po.rb ORDER_ID [PAYMENT_TERMS]
order_id = ARGV[0] || raise("Order ID required")
payment_terms = ARGV[1] || "net_30"
o = Order.find(order_id)
puts "Switching Order #{order_id} from #{o.payment_method} to Purchase Order"
puts "="*60
puts "BEFORE:"
puts " payment_method: #{o.payment_method}"
puts " payment_terms: #{o.payment_terms}"
puts " payment_status: #{o.payment_status}"
puts ""
o.payment_method = "purchase_order"
o.payment_terms = payment_terms
o.payment_profile_id = nil
o.payment_status = "unpaid"
if o.save
puts "✓ Successfully updated order #{order_id}"
puts ""
puts "AFTER:"
puts " payment_method: #{o.payment_method}"
puts " payment_terms: #{o.payment_terms}"
puts " payment_status: #{o.payment_status}"
puts ""
puts "✓ Order will NO LONGER be auto-charged"
puts "✓ Customer will receive invoices instead"
else
puts "✗ ERROR: #{o.errors.full_messages.join('; ')}"
exit 1
end
# On production server
kamal app exec -i "bin/rails runner scripts/switch_order_to_po.rb 223 net_30"
Or remotely via SSH:
ssh root@5.78.141.175 'docker exec $(docker ps --filter label=service=quarry_rentals --filter label=role=web --format "{{.Names}}" | head -1) bin/rails runner "
o = Order.find(223)
o.payment_method = \"purchase_order\"
o.payment_terms = \"net_30\"
o.payment_profile_id = nil
o.payment_status = \"unpaid\"
o.save!
"'
payment_method: "purchase_order"payment_termsCheck that payment_method was actually updated:
Order.find(ORDER_ID).payment_method
# Should return "purchase_order", not "credit_card"
Purchase orders require payment terms:
o.payment_terms = "net_30" # Required!
app/models/order.rb - Order model with payment constantsapp/jobs/recurring_card_charge_job.rb - Auto-charge jobapp/services/recurring_card_charge_service.rb - Charge processing logic