Issue #3612572: Handle existing enrollment/fulfillment rows during checkout
- course_enroll() guards with the status-agnostic course_enrollment_load() instead of course_enrollment_check() so a pre-existing inactive row no longer collides on the nid_uid unique key.
- CourseEnrollment::save() and CourseObjectFulfillment::save() catch a duplicate-key PDOException (SQLSTATE 23000), adopt the existing row (preserving the fulfillment uuid), and re-save as an update, covering the concurrent / double-submit race a pre-check cannot.
Happy path is unchanged; recovery only runs on an actual collision. No schema change, no hook_update_N.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Closes #3612572