feat(govfee): exact fees + agency processing fees; IRP email/invoice reconciliation
- gov_fee: add AGENCY_PROCESSING_FEE (per-service card/convenience fee passed through so the customer pays the true all-in cost); estimate_gov_fee now folds it into the billed total. IFTA/intrastate/UCR fees are published/near-exact. - IRP fees can't be looked up — only the base state computes them. New irp_filing.py: emails the base-state IRP unit a Schedule A/B request (Reply-To the IRP filings mailbox, [PW-IRP CO-...] subject tag), and a 15-min cron (irp_invoice_poller) scans the mailbox for the state's invoice reply, parses the exact apportioned fee, Telegram-alerts you, and bills the customer the EXACT amount via a gov-fee child order + payment link. Then it proceeds to ready_to_file for your final approval. - state_trucking gov-fee gate now routes IRP to the email/invoice path and IFTA/intrastate to immediate exact-fee billing. - Mailbox is configurable (IRP_FILINGS_IMAP_* in app.env.j2); falls back to OPS_IMAP_* filtered by the [PW-IRP] tag until a dedicated mailbox exists. Telegram alerts fire on IRP submission sent, invoice received (billed), and un-parseable replies (so you can read + enter the fee manually).
This commit is contained in:
parent
861f2fbfd4
commit
ea695d6828
6 changed files with 406 additions and 3 deletions
|
|
@ -66,6 +66,25 @@ IRP_WEIGHT_MULTIPLIERS = {
|
|||
"_default": 1.0,
|
||||
}
|
||||
|
||||
# Agency / government payment-processing (card convenience) fees. Many state
|
||||
# portals add a card convenience fee on top of the statutory fee; we pass it
|
||||
# through so the customer pays the true all-in cost. Modeled as a percentage of
|
||||
# the government fee plus an optional flat add-on, per service. Refine per state
|
||||
# as confirmed. (pct_of_gov_fee, flat_cents)
|
||||
AGENCY_PROCESSING_FEE = {
|
||||
"irp-registration": (0.0, 0), # SC IRP invoice states the all-in total; no extra
|
||||
"ifta-application": (0.0, 0), # decals billed at cost; no card fee on a tiny amount
|
||||
"intrastate-authority": (0.0, 0), # filing fee paid by check/ACH; no card fee
|
||||
"ucr-registration": (0.0, 0), # ucr.gov includes no separate convenience fee
|
||||
"_default": (0.0, 0),
|
||||
}
|
||||
|
||||
|
||||
def agency_processing_fee_cents(slug: str, gov_fee_cents: int) -> int:
|
||||
"""Government/agency card-convenience fee passed through to the customer."""
|
||||
pct, flat = AGENCY_PROCESSING_FEE.get(slug, AGENCY_PROCESSING_FEE["_default"])
|
||||
return int(round(gov_fee_cents * pct / 100.0)) + flat
|
||||
|
||||
|
||||
@dataclass
|
||||
class GovFeeEstimate:
|
||||
|
|
@ -83,7 +102,19 @@ def _int(v, default=0) -> int:
|
|||
|
||||
|
||||
def estimate_gov_fee(slug: str, intake: dict) -> GovFeeEstimate:
|
||||
"""Estimate the government/state fee for an at-cost trucking service."""
|
||||
"""Estimate the all-in government fee (statutory fee + any agency card/
|
||||
convenience processing fee) for an at-cost trucking service."""
|
||||
est = _estimate_gov_fee_base(slug, intake)
|
||||
proc = agency_processing_fee_cents(slug, est.cents)
|
||||
if proc > 0:
|
||||
est.breakdown.append(f"Agency processing fee: ${proc/100:.2f}")
|
||||
est.cents += proc
|
||||
est.label += f" (incl. ${proc/100:.2f} agency processing fee)"
|
||||
return est
|
||||
|
||||
|
||||
def _estimate_gov_fee_base(slug: str, intake: dict) -> GovFeeEstimate:
|
||||
"""Statutory/base government fee before any agency processing surcharge."""
|
||||
intake = intake or {}
|
||||
power_units = max(_int(intake.get("power_units"), 1), 1)
|
||||
base_state = (intake.get("base_state") or intake.get("address_state") or "").upper()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue