0%

Capstone: A Persistent Lending Tool · practice

Check Out One Item

A checkout can fail for three known reasons, and those reasons are useful to the person running the program. The item may already be active, the member may be at the limit, or the requested IDs may conflict with a catalog rule. checkout_command must preserve those meanings without hiding a different defect.

The demo calls:

main([
    "checkout", str(database_path), "501", "1", "17", "2026-08-26"
])

Add checkout_command(args). Its namespace contains args.database, args.loan_id, args.item_id, args.member_id, and args.checked_out_on. Argparse has already converted the three IDs to positive integers. Trim the checked-out label with trimmed(args.checked_out_on, "CHECKED_OUT_ON"). Here, dates remain text rather than being parsed or checked against a calendar.

Require the database path before validation can open anything. Then call prepare_database, open the application connection with open_database, and make this one boundary call:

recorded_id = checkout_item(
    connection, args.loan_id, args.item_id, args.member_id, checked_out_on
)

Close the connection in finally. checkout_item performs both application checks, uses one for its write, translates its insert’s integrity failure, and leaves caller-owned connections open. Keep that logic in catalog_db.py.

Catch the documented messages at the boundary. They must reach stderr unchanged after the Error: prefix:

That item is already checked out.
That member has reached the loan limit.
The checkout conflicts with a catalog rule.

Each refusal returns 1, writes nothing to stdout, and changes no loan row. Do not catch OperationalError, ProgrammingError, TypeError, or every Exception. A missing table is not a normal checkout refusal and should remain visible as the defect it is.

After a successful commit and close, print Loan recorded: ID using the returned loan ID, then return 0. Add the exact branch if args.command == "checkout": return checkout_command(args) in main.

Press Run. The temporary catalog already has three items and member 17:

== checkout ==
Status: 0
stdout: Loan recorded: 501
stderr: <empty>
Active item IDs after reopening: [1]

A thorough check also tries an active item, a member exactly at the limit, unknown , a duplicate loan ID, a blank label, and a missing database. It reopens after every case so output and durable state must agree.

Task

Add or complete checkout_command(args) and add this branch in main:

if args.command == "checkout":
    return checkout_command(args)

Refuse a missing database, trim CHECKED_OUT_ON, prepare and open the existing catalog, and call checkout_item once with the five parsed values. Close the application connection in finally. On success print Loan recorded: ID and return 0; map only documented results through report_error.