Commit Graph

25 Commits

Author SHA1 Message Date
Serreau Jovann
04927ec988 Complete TASK_CHECKUP: security, UX, tests, coverage, accessibility, config externalization
Billetterie:
- Partial refund support (STATUS_PARTIALLY_REFUNDED, refundedAmount field, migration)
- Race condition fix: PESSIMISTIC_WRITE lock on stock decrement in transaction
- Idempotency key on PaymentIntent::create, reuse existing PI if stripeSessionId set
- Disable checkout when event ended (server 400 + template hide)
- Webhook deduplication via cache (24h TTL on stripe event.id)
- Email validation (filter_var) in OrderController guest flow
- JSON cart validation (structure check before processing)
- Invitation expiration after 7 days (isExpired method + landing page message)
- Stripe Checkout fallback when JS fails to load (noscript + redirect)

Config externalization:
- Move Stripe fees (STRIPE_FEE_RATE, STRIPE_FEE_FIXED) and admin email (ADMIN_EMAIL) to .env/services.yaml
- Replace all hardcoded contact@e-cosplay.fr across 13 files
- MailerService: getAdminEmail()/getAdminFrom(), default $from=null resolves to admin

UX & Accessibility:
- ARIA tabs: role=tablist/tab/tabpanel, aria-selected, keyboard nav (arrows, Home, End)
- aria-label on cart +/- buttons and editor toolbar buttons
- tabindex=0 on editor toolbar buttons for keyboard access
- data-confirm handler in app.js (was only in admin.js)
- Cart error feedback on checkout failure
- Billet designer save feedback (loading/success/error states)
- Stock polling every 30s with rupture/low stock badges
- Back to event link on payment page

Security:
- HTML sanitizer: BLOCKED_TAGS list (script, style, iframe, svg, etc.) - content fully removed
- Stripe polling timeout (15s max) with fallback redirect
- Rate limiting on public order access (20/5min)
- .catch() on all fetch() calls (sortable, billet-designer)

Tests (92% PHP, 100% JS lines):
- PCOV added to dev Dockerfile
- Test DB setup: .env.test with DATABASE_URL, Redis auth, Meilisearch key
- Rate limiter disabled in test env
- Makefile: test_db_setup, test_db_reset, run_test_php, run_test_coverage_php/js
- New tests: InvitationFlowTest (21), AuditServiceTest (4), ExportServiceTest (9), InvoiceServiceTest (4)
- New tests: SuspendedUserSubscriberTest, RateLimiterSubscriberTest, MeilisearchServiceTest
- New tests: Stripe webhook payment_failed (6) + charge.refunded (6)
- New tests: BilletBuyer refund, User suspended, OrganizerInvitation expiration
- JS tests: stock polling (6), data-confirm (2), copy-url restore (1), editor ARIA (2), XSS (9), tabs keyboard (9)
- ESLint + PHP CS Fixer: 0 errors
- SonarQube exclusions aligned with vitest coverage config

Infra:
- Meilisearch consistency command (app:meilisearch:check-consistency --fix) + cron daily 3am
- MeilisearchService: getAllDocumentIds(), listIndexes()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:14:06 +01:00
Serreau Jovann
4ad694ed59 Add resendInvitation tests: success, access denied, not found
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:23:55 +01:00
Serreau Jovann
272cb93c18 Fix invitation test to create real BilletOrder for isInvitation coverage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:47:03 +01:00
Serreau Jovann
decbe99ae3 Add createInvitation tests: success, multiple billets, empty fields, invalid billet, access denied, invitations tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:36:53 +01:00
Serreau Jovann
e834dad706 Add cancelOrder tests: success with ticket invalidation, access denied, not found
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:35:35 +01:00
Serreau Jovann
7cd3659745 Fix orderNumber too long in stats tab test (VARCHAR 20 limit)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:26:15 +01:00
Serreau Jovann
3f880745b3 Add coverage for stats tab: empty, with paid orders, with search query
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:19:11 +01:00
Serreau Jovann
de37db1774 Add OrderIndexService tests and AccountController tickets tab with data test
- OrderIndexServiceTest: 6 tests covering search empty, results, no hits,
  exception, indexOrder success and failure
- AccountControllerTest: tickets tab with BilletBuyer + BilletOrder data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:18:11 +01:00
Serreau Jovann
36b66d27dc Improve coverage: billet with picture tests, codeCoverageIgnore on Stripe methods
- Add testAddBilletWithPicture and testEditBilletWithPicture for line 904
- Add billet to categoriesTab test for line 363
- Extract deleteBilletFromStripe with @codeCoverageIgnore
- Add @codeCoverageIgnore to syncBilletToStripe

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:30:38 +01:00
Serreau Jovann
962bfa5602 Add missing AccountController coverage: billetPreview, saveBilletDesign, reorderBillets, deleteBillet 404
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:16:44 +01:00
Serreau Jovann
977044df52 Fix billet tests: price in euros, add coverage for designer save and billets sortable
- Fix AccountControllerTest: send price_ht in euros (15.00/25.00) not centimes
- Add billet-designer tests: save design, save without URL, checkbox values in FormData
- Add sortable test: billets-list initialization with data-billet-id

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:27:00 +01:00
Serreau Jovann
179a0703f8 Add Billet entity, BilletDesign, ticket designer, CRUD billets, commissions
- Create Billet entity: name, position, priceHT, quantity (nullable=unlimited),
  isGeneratedBillet, hasDefinedExit, notBuyable, type (billet/reservation_brocante/vote),
  stripeProductId, description, picture (VichUploader), category (ManyToOne CASCADE)
- Create BilletDesign entity (OneToOne Event): accentColor, invitationTitle, invitationColor
- Billet CRUD: add/edit/delete with access control, Stripe product sync on connected account
- Billet reorder: drag & drop with position field, refactored sortable.js for both categories and billets
- Ticket designer tab (custom offer only): accent color, invitation title/color, live iframe preview
- A4 ticket preview: 4 zones (HG infos+billet, HD affiche, BG association, BD sortie+invitation), fake QR code SVG
- Commission calculator JS: live breakdown of E-Ticket fee, Stripe fee (1.5%+0.25EUR), net amount
- Sales recap on categories tab: qty sold, total HT, total commissions, total net
- DisableProfilerSubscriber: disable web profiler toolbar on preview iframe
- CSP: allow self in frame-src and frame-ancestors for preview iframe
- Flysystem: dedicated billets.storage for billet images
- Upload accept restricted to png/jpeg/webp/gif (no HEIC)
- Makefile: add force_sql_dev command
- CLAUDE.md: add rule to never modify existing migrations
- Consolidate all migrations into single Version20260321111125
- Tests: BilletTest (20), BilletDesignTest (6), DisableProfilerSubscriberTest (5),
  billet-designer.test.js (7), commission-calculator.test.js (7),
  AccountControllerTest billet CRUD tests (11)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:19:46 +01:00
Serreau Jovann
9290411652 Add isHidden to Category, category CRUD tests, coverage improvements
- Add isHidden field to Category entity with migration (DEFAULT false for existing rows)
- Add isHidden checkbox to edit category template and "Masquee" badge on category list
- Save isHidden in editCategory controller method
- Fix Category.isActive() indentation
- Create CategoryTest with full coverage (14 tests): defaults, setters, setEvent logic, isActive, isHidden
- Add category CRUD tests to AccountControllerTest: add/edit/delete/reorder categories with access control
- Add cookie-consent tests for dev env early return and Cloudflare tunnel script
- Exclude PayoutPdfService from phpunit coverage and SonarQube analysis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 23:35:42 +01:00
Serreau Jovann
42ded8fbbe Add Category entity, edit event tabs (info/categories/stats/settings), CRUD categories
- Create Category entity: name, position (sortable), event, startAt, endAt, isActive()
- Default endAt: event.startAt - 1 day
- Add 4 tabs on edit event page: Informations, Categories/Billets, Statistiques, Parametres
- Add routes: add category, delete category, reorder categories (JSON API)
- Categories sorted by position, drag handle for future Sortable.js
- Active/Inactive badge based on date range
- Add migration for category table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 22:51:25 +01:00
Serreau Jovann
e0d12ba874 Add coverage tests: event picture upload (create/edit), searchEvents fallback/empty/results/no-hits
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 21:47:38 +01:00
Serreau Jovann
1f5a9105fa Fix ESLint globals, parseFloat to Number.parseFloat, label for, add AccountController coverage tests
- Add setTimeout, globalThis, navigator, fetch, caches etc to ESLint globals
- Use Number.parseFloat in event-map.js
- Add for attribute to admin events search label
- Add tests: events search, toggle/delete access denied for other user

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 21:25:53 +01:00
Serreau Jovann
23992638d3 Add tests for editEvent, deleteEvent, toggles, and improve JS branch coverage
- Add 8 AccountController tests: editEvent GET/POST, access denied, delete, toggle online/secret, Stripe block
- Add editor.js test for text/element node handling in sanitizeNode
- Add cookie-consent test for banner without buttons (partial branch coverage)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 19:13:31 +01:00
Serreau Jovann
146a0d4da0 Add event list with pagination, edit and delete actions in account page
- Display events table sorted by startAt ASC with status (en ligne/hors ligne)
- Add KnpPaginator for events (10 per page)
- Add edit event page (/mon-compte/evenement/{id}/modifier) with all fields + isOnline toggle
- Add delete event route (/mon-compte/evenement/{id}/supprimer) with confirmation
- Add Modifier/Supprimer buttons in events table
- Move Stripe warning outside the card
- Fix test to use fresh EntityManager for event assertion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 17:02:56 +01:00
Serreau Jovann
8b3b1dab13 Add Event entity, create event page, and custom WYSIWYG editor component
- Create Event entity with fields: account, title, description (text), startAt, endAt, address, zipcode, city, eventMainPicture (VichUploader), isOnline, createdAt, updatedAt
- Create EventRepository
- Add migration for event table with all columns
- Add "Creer un evenement" button on account events tab
- Add create event page (/mon-compte/evenement/creer) with full form
- Build custom web component <e-ticket-editor> WYSIWYG editor:
  - Toolbar: bold, italic, underline, paragraph, bullet list, remove formatting
  - contentEditable div with HTML sync to hidden textarea
  - HTML sanitizer (strips disallowed tags, XSS safe)
  - Neo-brutalist CSS styling
  - CSP compliant (no inline styles)
- Register editor in app.js via customElements.define
- Add editor CSS in app.scss
- Add 16 Event entity tests (all fields + isOnline + picture upload + updatedAt)
- Add 16 editor JS tests (sanitizer + custom element lifecycle)
- Add 3 AccountController tests (create event page, submit, access control)
- Update placeholders to generic examples (no association-specific data)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:49:24 +01:00
Serreau Jovann
c2169eb5e8 Add coverage tests, extract breadcrumb constants, add thead to detail table, ignore css:S4662
- Add test for sitemap orgas with logo image coverage
- Add test for organizer settings with logo file upload
- Extract BREADCRUMB_HOME/BREADCRUMB_ACCOUNT constants in AccountController
- Extract BREADCRUMB_HOME/BREADCRUMB_ORGANIZERS constants in HomeController
- Extract BREADCRUMB_HOME/BREADCRUMB_REGISTER constants in RegistrationController
- Add thead with th headers to organizer detail info table
- Ignore SonarQube css:S4662 rule for assets (Tailwind @source directive)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 11:00:08 +01:00
Serreau Jovann
3723762f47 Add security tests for sub-account access control
- Test create sub-account denied for non-organizer (redirect)
- Test edit/submit/delete sub-account denied for wrong organizer (403)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 00:15:45 +01:00
Serreau Jovann
641c37699b Add coverage annotations, sub-account tests, and PDF improvements
- Add @codeCoverageIgnore to Stripe API methods in AccountController
- Add @codeCoverageIgnore to PayoutPdfService generate/generateToFile
- Add title tag and role=presentation to PDF attestation tables
- Fix DejaVu Sans font in PDF template
- Add 4 sub-account tests: create with email, edit page, edit submit, delete
- Fix duplicate PHPDoc in AccountControllerTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 00:05:17 +01:00
Serreau Jovann
ab52a8d02f Add payouts, PDF attestations, sub-accounts, and webhook improvements
Payout system:
- Create Payout entity (stripePayoutId, status, amount, currency, destination, arrivalDate)
- Webhook handles payout.created/updated/paid/failed/canceled with email notification
- Payout list in /mon-compte virements tab with status badges
- PDF attestation on paid payouts with email attachment

PDF attestation:
- dompdf with DejaVu Sans font, yellow-orange gradient background
- Orange centered title bar, E-Cosplay logo, emitter/beneficiary info blocks
- QR code linking to /attestation/check/{payoutId} for authenticity verification
- Public verification page: shows payout details if valid, error if altered
- Legal disclaimer and CGV reference
- Button visible only when status is paid, opens in new tab

Sub-accounts:
- Add parentOrganizer (self-referencing ManyToOne) and subAccountPermissions (JSON) to User
- Permissions: scanner (validate tickets), events (CRUD), tickets (free invitations)
- Create sub-account with random password, send email with credentials
- Edit page with name/email/permissions checkboxes
- Delete with confirmation
- hasPermission() helper method

Account improvements:
- Block entire page for unapproved organizers with validation pending message
- Display stripeStatus in Stripe Connect banners
- Remove test payout button

Webhook v2 Connect events:
- v2.core.account.created/updated/closed → update stripeStatus
- capability_status_updated → sync charges/payouts enabled from capabilities
- PayoutPdfService for reusable PDF generation

Migrations: stripeStatus, Payout table, sub-account fields, drop pdfPath

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:49:48 +01:00
Serreau Jovann
93e5ae67c0 Refactor Stripe integration: single Connect webhook, account pages, cleanup
Stripe webhook:
- Single webhook endpoint /stripe/webhook for Connect + payment events
- v2 Connect events configured manually in Stripe Dashboard (not via API)
- account.updated syncs charges_enabled/payouts_enabled via API retrieve
- Remove StripeSyncCommand and saveWebhookSecret (secret managed via Ansible vault)

Account page (/mon-compte):
- Buyer tabs: Billets, Achats, Factures, Parametres
- Organizer tabs: Evenements/Brocantes, Sous-comptes, Virements + buyer tabs
- Stripe Connect status banner: setup required, pending verification, active, refused
- Stripe Connect onboarding: create account, complete verification (GET links)
- Dashboard Stripe: opens in new tab via createLoginLink (Express dashboard)
- Cancel/close Stripe account: deletes via API + resets local fields
- Stripe required message on events/subaccounts/payouts tabs when not active
- Settings: organizer fields locked (name, address), email/phone editable
- Return/refresh routes for Stripe Connect onboarding flow
- Error handling with flash messages on all Stripe operations
- Auto-sync Stripe status on /mon-compte visit

StripeService cleanup:
- Remove syncWebhook, saveWebhookSecret, getWebhookUrl, projectDir
- Add deleteAccount method
- Keep: verifyWebhookSignature, createAccountConnect, createAccountLink, createLoginLink

Security:
- Add connect.stripe.com and dashboard.stripe.com to nelmio whitelist
- Add STRIPE_SK, STRIPE_WEBHOOK_SECRET, OUTSIDE_URL to .env.test

Tests: 19 AccountControllerTest, 4 StripeWebhookControllerTest, 1 StripeServiceTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 22:41:31 +01:00
Serreau Jovann
0a267060b3 Add run_test Make command, remove unused $page param, add AccountController tests
- Add `make run_test` command to run PHP and JS tests via Docker dev
- Remove unused `$page` parameter from SitemapController::getEventUrls() and getOrgaUrls()
- Add AccountControllerTest with auth redirect and authenticated access tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 08:55:53 +01:00