Jacob Schmidt ff7ff0c4e5 Implement org credit line debt and bank repayment flow (#2)
## Summary

This finishes the org credit line workflow so it behaves like reserved treasury-backed credit instead of a simple member allowance.

## What changed

- reserve org funds immediately when a credit line is assigned
- track credit lines with:
  - approved amount
  - available amount
  - outstanding principal
  - interest rate
  - amount due
- consume reserved credit during store checkout without charging org funds a second time
- add credit line repayment through the bank app
- sync richer credit line state into org and bank payloads/UI
- keep legacy `amount` compatibility mapped to available credit for older consumers

## User-facing behavior

- assigning a credit line now reduces available org funds immediately
- spending on `credit_line` reduces available credit and creates debt with interest
- the bank app now shows outstanding credit debt and allows repayment from personal bank funds
- the org treasury view now shows reserved credit and outstanding due totals

## Validation

- `cargo fmt`
- `npm run build:webui`
- `cargo test -p forge-services --quiet`
- `cargo test -p forge-server --quiet`

## Follow-up checks

- validate in-game that assigning a credit line reduces org funds immediately
- validate store checkout with `credit_line` updates available credit and debt correctly
- validate bank repayment decreases player bank balance, increases org funds, and reduces amount due

Co-authored-by: Jacob Schmidt <innovativestudios@outlook.com>
Reviewed-on: #2
2026-04-02 16:50:38 -05:00
..
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00
2025-11-26 18:33:09 -06:00

forge_client_org

Player organization UI and client integration.

UI Login Contract

The web UI sends the following request through A3API.SendAlert:

{
    "event": "org::login::request",
    "data": {
        "email": "admin@spearnet.mil",
        "password": "secret"
    }
}

On success, SQF should call the browser bridge with:

private _payload = createHashMapFromArray [
    ["session", createHashMapFromArray [
        ["actorName", name player],
        ["role", "Leader"]
    ]],
    ["portalData", createHashMapFromArray [
        ["org", createHashMapFromArray [
            ["name", "Black Rifle Company"],
            ["tag", "BRC-0160566824"],
            ["type", "Private Military Company"],
            ["status", "Operational"],
            ["headquarters", "Georgetown Command Annex"],
            ["owner", "Jacob Schmidt"]
        ]],
        ["funds", 482750],
        ["reputation", 72],
        ["members", [
            createHashMapFromArray [["name", "Jacob Schmidt"]],
            createHashMapFromArray [["name", "Mara Velez"]]
        ]],
        ["fleet", [
            createHashMapFromArray [
                ["name", "UH-80 Ghost Hawk"],
                ["type", "helicopter"],
                ["status", "Ready"],
                ["damage", "16%"]
            ]
        ]],
        ["assets", [
            createHashMapFromArray [
                ["name", "First Aid Kits"],
                ["type", "items"],
                ["quantity", "36"]
            ]
        ]],
        ["activity", []],
        ["roadmap", []]
    ]]
];

_control ctrlWebBrowserAction [
    "ExecJS",
    format ["OrgUIBridge.receiveLoginSuccess(%1)", toJSON _payload]
];

On failure:

private _payload = createHashMapFromArray [
    ["message", "Invalid credentials."]
];

_control ctrlWebBrowserAction [
    "ExecJS",
    format ["OrgUIBridge.receiveLoginFailure(%1)", toJSON _payload]
];

Current implementation:

  • fnc_handleUIEvents.sqf now handles org::login::request
  • success hydrates the portal with session + portalData
  • failure returns a single message string for inline UI feedback