2025-11-25: VGBC Ticket Strategie - Update¶
Wijzigingen in Ticket Types¶
Oorspronkelijk Plan (24 nov)¶
- Early bird (manuele prijs aanpassing)
- Gewone kaarten
- Studenten kaarten (20% korting)
- Standhouders kaarten (quotum)
- Crew (niet via shop)
Nieuw Plan (25 nov) ✨¶
Definitief 4 soorten:
- Standaard Ticket (vervangd Early Bird)
- Prijs handmatig verhoogd via Commerce UI (geen datum-gebaseerde automatisering)
- Betaald via Mollie of Cash
-
Normale checkout flow
-
Student Ticket
- 20% korting via
field_beroep = student - Betaald via Mollie of Cash
-
Normale checkout flow
-
Standhouder Ticket
- Gekoppeld aan Partner entity met quotum
- Betaald via Mollie of Cash
-
Normale checkout flow
-
Custom/Admin Ticket ⭐ NIEUW
- Ad hoc aangemaakt door redactie/admin
- Voor uitzonderingen (VIPs, ter plekke cash, etc.)
- Geen Mollie betaling nodig - admin markeert als "betaald"
- Soortgelijk als "test payment" maar officieel ticket
Custom/Admin Ticket Workflow¶
Scenario: Ter plekke Cash Betaling¶
Balie (Medewerker A)
│
├─ Customer betaalt cash €25
├─ Medewerker A gaat naar /admin/commerce/create-custom-ticket
├─ Form: Customer naam, email, ticket type
├─ Drupal genereert order (status: betaald)
├─ Drupal genereert License entity
├─ QR-code gegenereerd
├─ Print QR-code label (voor balie of ticket)
│
Deur (Medewerker B)
│
├─ Bezoeker scant QR-code
├─ Scan interface toont: GROEN ✓
├─ Accreditatiebewijs PDF gegenereerd
├─ Email naar customer (via MailChimp)
│
End Result: Bezoeker heeft bewijs van deelname
Implementatie: Custom Ticket Creation¶
Admin Interface¶
Locatie: /admin/commerce/custom-ticket (permission: create custom tickets)
Form velden: - Klant naam (required) - Klant email (required) - Ticket type (radio): Standaard | Student | Standhouder | Custom - Opmerkingen (textarea) - Betalingsmethode (radio): Mollie | Cash | Gratis
Submit → Drupal doet: 1. Order aangemaakt (commerce_order) 2. Order state: paid (skip payment gateway) 3. Order email: sturen naar customer 4. License entity: gegenereerd 5. QR-code: gegenereerd (endroid/qr-code) 6. Redirect naar print page (of download label)
Order Status Handling¶
Standaard Ticket / Student / Standhouder: - Payment gateway: Mollie (user kiest in checkout) - Order state: pending → paid (via webhook)
Custom/Admin Ticket: - Payment gateway: none (admin kiest "Cash" of "Gratis") - Order state: direct paid (geen webhook) - Invoice: gegenereerd direct (zoals normal order)
Database/Config Changes¶
Nieuwe field op commerce_order:
- field_payment_method (select): Mollie | Cash | Gratis | Custom
Nieuwe order state:
- admin_created (optional, for tracking)
Order Type config: - Workflows unchanged - Invoice generation: still on order.paid
Technical: Custom Ticket Controller¶
Route: /admin/commerce/custom-ticket
Permission: create custom vgbc tickets
<?php
// src/Controller/CustomTicketController.php in vg_ticket_scanner module
namespace Drupal\vg_ticket_scanner\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_license\Entity\License;
use Endroid\QrCode\QrCode;
class CustomTicketController extends ControllerBase {
public function createForm() {
// Form builder
}
public function submitForm($form, $form_state) {
$values = $form_state->getValues();
// 1. Create order
$order = Order::create([
'type' => 'default',
'store_id' => 1,
'uid' => 0, // Anonymous or specific admin user
'mail' => $values['customer_email'],
'state' => 'completed', // Direct to completed (paid)
'field_payment_method' => 'cash', // Custom field
]);
$order->save();
// 2. Create order item (dummy product)
$order_item = OrderItem::create([
'type' => 'default',
'order_id' => $order->id(),
'purchased_entity' => $product_variation, // Which variation?
'quantity' => 1,
'unit_price' => $price,
]);
$order_item->save();
$order->addItem($order_item);
$order->save();
// 3. Generate License
$license = License::create([
'type' => 'vgbc_ticket',
'uid' => $order->getCustomerId(),
'product_variation' => $product_variation->id(),
'granted' => TRUE,
'expiration' => NULL, // Or set to event date
]);
$license->save();
// 4. Generate QR-code
$qr_content = "VGBC2026-USER{$order->getCustomerId()}-LICENSE{$license->id()}";
$qr_code = new QrCode($qr_content);
$qr_path = "public://qrcodes/{$license->id()}.png";
file_put_contents($qr_path, $qr_code->getImage());
// 5. Attach to license
$license->field_qr_code->setValue([
'target_id' => $file->id(),
]);
$license->save();
// 6. Trigger invoice generation
// (via event: commerce_order.place.post_transition)
// 7. Send email
$mail_service = \Drupal::service('plugin.manager.mail');
$mail_service->mail('commerce_order', 'order_confirmation', $order->getEmail(), 'en', [
'order' => $order,
'license' => $license,
]);
return $this->redirect('entity.commerce_order.canonical', [
'commerce_order' => $order->id(),
]);
}
}
UI/UX Considerations¶
Admin Create Ticket Form¶
╔═══════════════════════════════════════╗
║ Custom Ticket Aanmaken (VGBC2026) ║
╠═══════════════════════════════════════╣
║ ║
║ Klant Naam: [___________________] ║
║ Klant Email: [__________________] ║
║ ║
║ Ticket Type: ║
║ ○ Standaard (€25) ║
║ ○ Student (€20) ║
║ ○ Standhouder ║
║ ○ Custom (beschrijving) ║
║ ║
║ Betalingsmethode: ║
║ ○ Mollie (online payment button) ║
║ ○ Cash (direct marked paid) ║
║ ○ Gratis (sponsor/crew) ║
║ ║
║ Opmerkingen: ║
║ [_____________________________] ║
║ [_____________________________] ║
║ ║
║ [Ticket Aanmaken] [Annuleer] ║
║ ║
╚═══════════════════════════════════════╝
Result page:
┌─────────────────────────────────────────┐
│ ✓ Ticket aangemaakt! │
│ │
│ Order #VG-2025-042 │
│ Customer: Jan Jansen │
│ Email: jan@example.com │
│ │
│ QR-code: │
│ [████████ QR IMAGE ████████] │
│ │
│ [Print Label] [Download PDF] │
│ [Email versturen] [View Order] │
└─────────────────────────────────────────┘
Betalingsmethode Veld¶
On commerce_order entity:
field_payment_method:
type: list_string
settings:
allowed_values:
mollie: 'Online betaling (Mollie)'
cash: 'Cash (ter plekke)'
gratis: 'Gratis/Sponsor'
custom: 'Custom (admin)'
Impact op invoice/confirmation: - Mollie: "Betaling via Mollie: verwerkt" - Cash: "Betaling ter plekke: contant €25" - Gratis: "Gratis kaart" - Custom: "Admin aangemaakt"
QR-code Label Print¶
Option 1: Browser Print
<div class="qr-label">
<h3>VGBC 2026</h3>
<img src="/path/to/qr.png" />
<p>{{ ticket_type }}</p>
<p>{{ customer_name }}</p>
</div>
Option 2: PDF Download - Maak label PDF (wkhtmltopdf) - Download + print op balie
Invoice Aanpassingen¶
Invoice template moet toonen: - Order #VG-2025-042 - Customer (if known) - Ticket type - QR-code (klein formaat, rechtsboven) - Betalingsmethode: "Cash" of "Mollie" of "Gratis" - Amount: €0 als gratis, €25 als betaald
Workflow Samenvatting¶
Normale Koper (Mollie)¶
Student (Mollie met Korting)¶
Standhouder (Mollie)¶
Ter Plekke Cash (Balie Medewerker)¶
VIP/Gratis (Redactie)¶
Open Vragen¶
- Mollie Payment Skip - Werkt
state = completedzonder webhook trigger? - Antwoord: Ja, maar test eerst!
-
Event
commerce_order.place.post_transitiontriggert invoice generation -
Anonymous vs Admin User - Order uid bij cash ticket?
- Optie A: uid = 1 (admin)
- Optie B: uid = 0 (anonymous)
-
Optie C: uid = customer (als reeds ingelogd)
-
QR Label Format - Wat voor label bij balie printen?
- Suggestie: 10cm x 10cm sticker of kaartje
- Handmatig inplakken in physical ticket?
-
Of direct op invoice afdrukken?
-
Email naar Customer - Wat zeggen?
- "Bedankt! Uw ticket is hier gedownload: [link]"
-
"Scan de QR-code bij de deur voor accreditatiebewijs"
-
Quotum voor Cash Tickets - Tellen mee naar standhouder quotum?
- Waarschijnlijk JA (ter plekke standhouder ook quotum hebben)
- Dus: check
field_vgbc2026_ticketsook voor admin tickets
Implementation Checklist¶
- Mollie skip test (order.state = completed direct)
- Event subscriber: invoice generation op completed (niet alleen paid)
- Custom Ticket form builder
- Permission:
create custom vgbc tickets - Payment method field on order
- QR label printer (Symfony console command of form)
- Invoice template update (show payment method)
- Email template update (show payment method)
- UI buttons op order page (print label, resend email, etc.)
- Quotum validation ook voor admin tickets
- Testing: E2E flow cash → scan → accreditatiebewijs
Status¶
Planning: Volgende sessie starten met custom ticket form
Priority: Hoog (blocking for testing ter plekke scenario)
Modules affected: - vg_commerce (order handling) - vg_ticket_scanner (new: custom ticket form + controller)