Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fleet-card-plsql

A commercial fuel-card platform whose business logic lives in Oracle PL/SQL, with a .NET 9 data layer that does nothing but call it.

Modelled on the shape of a real fleet-payments programme — card issuing and acceptance, real-time authorization at the pump, nightly settlement and interchange, and volume-tier rebates back to the customer.

Why this is a hard migration

sql/ is 1,370 lines across four packages, and it is not boilerplate:

Routine Lines Cyclomatic What makes it hard
pkg_authorization.authorize 212 28 11 parameters, 9 tables, a custom exception used as control flow, a MERGE on the velocity counter, and two different partial-approval paths (daily limit, then credit) that can each downgrade an approval
pkg_settlement.sweep_transactions 61 5 cursor loop that CONTINUEs past bad rows into an exception queue rather than failing the batch
pkg_rebate_billing.tiered_rebate 34 6 marginal tier walk — each slice of volume earns its own rate, not the top rate on everything
pkg_settlement.interchange_for 37 5 MCC-specific rate, falling back to network-wide, falling back to a floor
pkg_rebate_billing.convert_amount 44 6 FX at the day's fixing, falling back to the last fixing on or before it
pkg_settlement.reprice_batch 27 1 EXECUTE IMMEDIATE — dynamic SQL the analyzer flags

Across the corpus: 23 migratable routines, 13 that write, 5 returning ref cursors, 10 stamping rows with the database clock, and every risk flag the analyzer knows — cursors, dynamic-sql, exception-handling, merge.

The marginal tier walk is the one worth watching in a demo. It is the rule most often got wrong when this logic is reimplemented by hand, because the naive reading — look up the customer's tier, apply that rate to everything — is both simpler and wrong, and it overpays.

The starting state

Every method in src/FleetCard.Data/ is a marshalling shim. This is the whole of AuthorizeAsync, the entry point to 212 lines of decisioning:

p.Add("p_card_id", cardId);
// … nine more …
p.Add("p_reason", dbType: DbType.String, direction: ParameterDirection.Output, size: 60);

await _db.ExecuteAsync(new CommandDefinition(
    "pkg_authorization.authorize", p,
    commandType: CommandType.StoredProcedure, cancellationToken: ct));

No card control, no velocity rule, no partial-approval logic. 23 live CommandType.StoredProcedure call sites, and not one line of business logic on the .NET side.

Contents

sql/01_schema.sql               17 tables, sequences, indexes
sql/02_pkg_authorization.sql    the real-time decision path
sql/03_pkg_settlement.sql       nightly batch, interchange, reprice
sql/04_pkg_rebate_billing.sql   marginal tier rebates, FX, invoicing

src/FleetCard.Data/             3 repositories — 23 delegating methods
src/FleetCard.App/              console host that wires them together

Running it

  1. Open in CogniDev Workbench (Open from GitHub → cognidevwb/fleet-card-plsql).
  2. Run Understand.
  3. Run Move stored procedures to .NET.

One task per routine, ported into the repository that already calls it, compiled after each one. The run finishes with a coverage report reconciling all 23 against the delivered C# — every parameter accounted for, every table the procedure wrote still written, the return shape unchanged, and no call site still reaching for the procedure.

dotnet build -c Release   # no database required

About

Commercial fuel-card platform: 1,370 lines of Oracle PL/SQL (authorization, settlement, interchange, marginal-tier rebates) behind a .NET 9 layer that only marshals parameters — test corpus for the stored-procedure migration playbook

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages