- Bump phpstan/phpstan ^2.0 and szepeviktor/phpstan-wordpress ^2.0
- Move the analysis level into phpstan.neon (single source) and raise it to 10
- Add Val, a runtime coercion helper that narrows untyped WordPress boundary
values (wpdb rows, REST params, superglobals, options) with explicit checks
instead of blind casts, plus unit tests
- Type value-object fromRow() params as stdClass (what wpdb returns) and map
columns through Val so unexpected shapes degrade safely
- Use %i identifier placeholders for table names in all wpdb::prepare() calls
so every query string is a literal and identifiers are escaped by WordPress;
raises the minimum WordPress version to 6.2 where %i was introduced
- Guard wpdb::prepare() null result before wpdb::query() in updateTax()
- Fix nullable get_permalink()/strtotime() handling, list types at REST and
capability call sites, dead null-coalescing on checked superglobals, and
narrow get_users() results before mapping
- Register Val method names with the ValidatedSanitizedInput sniff so it
validates the real sanitizer around each superglobal read
- Update repository unit tests for the %i placeholder arguments
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The e-transfer destination is resolved at booking time (offering override ->
studio default) and frozen onto the payment, so each record keeps where the
student was directed. It can then be corrected per booking.
- StudioSettings: us_etransfer_email option + a Default e-transfer email field
on the Studio Settings page.
- Offering: etransfer_email column/field (instructor override) across VO, repo,
REST endpoint, admin controller, and form.
- Payment: etransfer_email column on the payment (frozen record) +
PaymentRepository::updateEtransferEmail; PaymentService freezes it from the
offering override or studio default at creation; booking/enrolment pass the
offering override.
- My Lessons: instructors edit the e-transfer email per pending lesson payment
(ownership-checked).
- Payments queue: studio admin can correct the email at confirmation (for when
a student sends it to the wrong place).
- Docs updated.
Tests: Payment/Offering rows + PaymentService freezing. composer test (148),
cs, and PHPStan level 6 all pass.
Refs #7
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Availability (#2):
- us_availability gains offering_id, duration_minutes (default 60), and
recurrence_group; AvailabilitySlot carries the new fields.
- AvailabilityRepository::createWeeklySeries() generates N weekly rows
sharing a recurrence_group; findAvailable() filters by offering and
duration. Date math uses DateTimeImmutable::modify() (the no-debug CI
regex `dd\(` matches `->add(`).
- REST GET filters by offering_id/duration_minutes; POST accepts
duration_minutes, offering_id, recurrence (single|weekly) + weeks.
- Admin form adds duration, an offering picker, and one-off/weekly options
(OfferingRepository wired into AvailabilityController).
- booking.js renders an agenda calendar (slots grouped by day, with
duration). The richer booking UX lands with the booking-flow work.
Offering price in dollars:
- Switch us_offerings.price_cents (INT) to price DECIMAL(10,2); Offering
uses float $price. Admin form and REST take dollars.
- Fix a pre-existing misalignment in the Offering insert/update $wpdb
format arrays (billing_mode/capacity/is_active were mapped to the wrong
specifiers, which would corrupt values) via a single COLUMN_FORMATS list.
Also bump PHPStan to --memory-limit=1G in the lint script; 128M now
crashes analysis as the codebase has grown.
Refs #2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the offerings catalog (#1): private-lesson types and group
classes carrying pricing, billing mode (one_time/full_term), duration,
capacity, and term details. Adds the src/Offering/ domain (value object,
repository, REST endpoint, admin controller + template), the us_offerings
table, and an Offerings admin page.
Also lands the capability slice of #9: registers the us_studio_admin role
and the new capability strings (manage_instructors, manage_offerings,
manage_questions, manage_policies, manage_billing, view_all_payments,
view_own_payments, export_payments) so offering management gates correctly.
Tests: tests/Unit/Offering/ (value object + repository) and a studio-admin
case in RoleManagerTest. composer test, cs, and PHPStan level 6 all pass.
Refs #1#9
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>