Ana içeriğe geç
v26.3

Online Biletleme Sistemi

Biletleme sistemi, QR kod tabanlı giriş, kapasite yönetimi ve gün sonu raporuna entegrasyon ile etkinlik biletlerinin (Disco, etkinlikler) online satışını ve yönetimini sağlar.

Mimari

Biletleme, Disco modülüne benzer şekilde bir Feature-Pack (Dikas.Features.Ticketing) olarak uygulanmıştır. IFeatureModule arayüzünü kullanır ve DI aracılığıyla ana uygulamaya entegre edilir.

Dikas.Features.Ticketing/
├── Entities/           # Ticket, TicketArticleConfig
├── Commands/           # 10 Commands (CQRS)
├── Queries/            # 12 Queries (CQRS)
├── Contracts/          # DTOs (Request/Response)
├── Controllers/        # 3 Controller (33 Endpoints)
├── Services/           # QR, PDF, ExpireJob, DayCloseStats
├── Validators/         # FluentValidation
└── TicketingModule.cs  # DI-Registrierung

Entities

Ticket

SoftDeletableDocument, DocumentType "Ticket", ID-Prefix tkt_, cacheAll: false

Alan Tür Açıklama
TicketNumber string Okunabilir numara (TKT-YYYYMMDD-XXXXXX)
QrCode string Benzersiz QR kodu (GUID)
Name string Etkinlik/bilet adı
Status int 0=Purchased, 1=CheckedIn, 2=Cancelled, 3=Expired, 4=Refunded
ArticleId string Bilet ürününe referans
EventDate DateTime Etkinlik tarihi
TimeWindowStart/End string? Giriş zaman aralığı (HH:MM)
ExpiresAt DateTime Son kullanma tarihi (EventDate + 1 gün)
EnterGroupId string? Disco giriş grubu
EnterRuleId string? Disco giriş kuralı
AssignedCardId string? Check-in sırasında atanan NFC kartı
FreeDrinks int Dahil ücretsiz içecekler
FreeAccount decimal Dahil bakiye
FreeArticles List\<FreeArticleItem> Dahil ücretsiz ürünler
IncludesWardrobe bool Vestiyer dahil
GuestName string? Kişiselleştirilmiş misafir adı
GuestEmail string? Misafir e-postası
IsPersonalized bool Kişiselleştirme zorunluluğu
GroupOrderId string? Grup sipariş ID'si
GroupIndex/GroupTotal int? Gruptaki konum/toplam
Price decimal Bilet fiyatı
PaidAmount decimal Halihazırda ödenen tutar
RemainingAmount decimal Kalan açık tutar
RefundAmount decimal? İade tutarı
RefundMethod string? İade yöntemi
PaymentProvider string Ödeme sağlayıcısı (stripe, paypal, etc.)
PaymentTransactionId string Harici işlem ID'si
TaxClass int Vergi sınıfı
CheckedInAt/By/WorkplaceId DateTime?/string? Check-in verileri
CancelledAt/By/CancelReason DateTime?/string? İptal verileri
RefundedAt DateTime? İade zamanı
StatusHistory List\<TicketStatusEntry> Durum değişikliklerinin denetim izi
OrderId string Mağaza sipariş ID'si
CustomerId string? Müşteri referansı

TicketArticleConfig

BaseDocument, DocumentType "TicketArticleConfig", ID-Prefix tktcfg_, cacheAll: true

Bir ürünü tüm varsayılanları ve kurallarıyla bilet türü olarak yapılandırır.

Alan Tür Açıklama
ArticleId string İlişkilendirilmiş ürün
EnterGroupId string? Standart Disco giriş grubu
EnterRuleId string? Standart Disco giriş kuralı
FreeDrinks int Standart ücretsiz içecekler
FreeAccount decimal Standart ücretsiz bakiye
FreeArticles List\<FreeArticleItem> Standart ücretsiz ürünler
IncludesWardrobe bool Vestiyer dahil
MaxCapacityPerDate int? Tarih başına maks. bilet
OverbookingPercent int? % cinsinden aşırı rezervasyon (örn. 10 = +%10)
CancelDeadlineHours int? Etkinlikten önce saat cinsinden iptal süresi
AllowRefund bool İade izni
IsPersonalized bool Kişiselleştirme zorunluluğu
AllowGroupPurchase bool Grup satın alma izni
MaxGroupSize int Maks. grup boyutu (Standart: 10)
TimeWindowStart/End string? Standart giriş zaman aralığı
OnlineOnly bool Yalnızca online mağazada mevcut
AllowPartialPayment bool Kısmi ödeme/peşinat izni
MinPrepayment decimal Minimum peşinat
SoldPerDate Dictionary\<string, int> Tarih başına satılan biletler (yyyy-MM-dd → adet)

API Uç Noktaları

TicketController — /api/v1/tickets [Authorize]

Biletler:

Yöntem Uç Nokta Açıklama
GET /{id} ID'ye göre bilet
GET /by-qr/{qrCode} QR koduna göre bilet
GET /by-date?date= Tarihe göre biletler
GET /by-order/{orderId} Bir siparişin biletleri
GET /by-customer/{customerId} Bir müşterinin biletleri
GET /search?search=&status=&dateFrom=&dateTo=&limit= Bilet ara
GET /capacity?date=&articleId= Kapasiteleri sorgula
GET /stats?dateFrom=&dateTo= İstatistikler
POST /validate QR kodunu doğrula (check-in olmadan)
POST /check-in QR girişi gerçekleştir
POST /{id}/cancel Bileti iptal et
POST /{id}/pay Kalan tutarı öde
POST /{id}/refund İade gerçekleştir
POST /cancel-group/{groupOrderId} Grup siparişini iptal et

Yapılandırmalar:

Yöntem Uç Nokta Açıklama
GET /configs Tüm bilet yapılandırmaları
GET /configs/{id} ID'ye göre yapılandırma
POST /configs Yapılandırma oluştur (201)
PUT /configs/{id} Yapılandırmayı güncelle
DELETE /configs/{id} Yapılandırmayı sil

TicketPortalController — /api/v1/portal/tickets [Authorize(Policy="Portal")]

Yöntem Uç Nokta Açıklama
GET /my?customerId= Müşterinin kendi biletleri
POST /{id}/cancel Müşteri kendi biletini iptal eder
GET /{id}/qr PNG olarak QR kodu [AllowAnonymous]
GET /{id}/pdf PDF olarak bilet [AllowAnonymous]

TicketShopController — /api/v1/ticket-shop [AllowAnonymous]

Yöntem Uç Nokta Açıklama
GET /catalog Mevcut bilet türleri
GET /capacity?date=&articleId= Müsaitliği kontrol et
POST /validate Ödemeyi doğrula
POST /purchase Bilet satın al

Commands

Command Geri Dönüş Açıklama
CreateTicketCommand List\<TicketResponse> 1+ bilet oluşturur, QR + numara üretir, PDF ile onay e-postası gönderir
CheckInTicketCommand CheckInResult QR'ı doğrular, durumu CheckedIn olarak ayarlar, zaman damgasını kaydeder
CancelTicketCommand TicketResponse Bileti iptal eder (yalnızca Status=Purchased), iptal süresini kontrol eder, iptal e-postası gönderir
ProcessTicketPaymentCommand TicketResponse Kalan ödemeyi kaydeder, PaidAmount/RemainingAmount günceller
RefundTicketCommand TicketResponse İade (yalnızca Status=Cancelled), AllowRefund kontrol eder
CancelTicketGroupCommand List\<TicketResponse> Bir grup siparişinin tüm biletlerini iptal eder
ValidateTicketCheckoutCommand TicketCheckoutValidationResult Mağaza için kapasite ve yapılandırma kontrolü
CreateTicketArticleConfigCommand TicketArticleConfigResponse Yeni bilet yapılandırması oluştur
UpdateTicketArticleConfigCommand TicketArticleConfigResponse Yapılandırmayı güncelle
DeleteTicketArticleConfigCommand bool Yapılandırmayı sil

Queries

Query Geri Dönüş Açıklama
GetTicketByIdQuery TicketResponse Tek bir bilet
GetTicketByQrCodeQuery TicketResponse QR koduyla bilet ara
GetTicketsByDateQuery List\<TicketResponse> Etkinlik tarihine göre biletler
GetTicketsByCustomerQuery List\<TicketResponse> Bir müşterinin biletleri
GetTicketsByOrderQuery List\<TicketResponse> Bir siparişin biletleri
SearchTicketsQuery List\<TicketResponse> Filtrelerle tam metin araması
GetTicketCapacityQuery List\<TicketCapacityResponse> Ürün/tarih başına kapasite
GetTicketStatsQuery TicketStatsResponse Toplu istatistikler
ValidateTicketQuery CheckInResult Yan etkisiz check-in doğrulaması
GetTicketArticleConfigsQuery List\<TicketArticleConfigResponse> Tüm yapılandırmalar
GetTicketArticleConfigQuery TicketArticleConfigResponse Tek bir yapılandırma
GetTicketCatalogQuery List\<TicketShopItemResponse> Online mağaza kataloğu

Services

TicketQrService (Singleton)

QRCoder kütüphanesi ile QR kodları üretir.

interface ITicketQrService {
    byte[] GeneratePng(string content, int pixelsPerModule = 10);
    string GenerateSvg(string content);
}

TicketPdfService (Scoped)

QuestPDF ile A6 formatında bilet PDF'leri oluşturur. Gömülü QR kodu, bilet ayrıntıları ve isteğe bağlı mekan bilgileri içerir.

interface ITicketPdfService {
    byte[] GenerateTicketPdf(Ticket ticket, string? venueInfo = null);
}

ExpireTicketsJob (HostedService)

Her 60 dakikada bir çalışan ve süresi dolmuş biletleri (Status=Purchased, ExpiresAt < şimdi) otomatik olarak Status=Expired olarak ayarlayan arka plan işi.

DayCloseTicketStatsProvider (Scoped)

Gün sonu raporu için bilet istatistikleri sunar:

interface IDayCloseTicketStatsProvider {
    Task<DayCloseTicketStats> GetStatsForPeriodAsync(DateTime from, DateTime to, CancellationToken ct);
}

Geri dönüş: SoldOnlineCount/Amount, CheckedInCount, RefundedCount/Amount

İsteğe bağlı modüller arası bağımlılık: Varsayılan olarak NullDayCloseTicketStatsProvider kaydedilir, TicketingModule tarafından gerçek uygulamayla geçersiz kılınır.

Check-in akışı

QR-Code scannen
ValidateTicketQuery
    ├── Ticket nicht gefunden → Invalid
    ├── Status != Purchased → AlreadyUsed / Cancelled / Expired
    ├── EventDate != heute → WrongDate
    ├── Außerhalb TimeWindow → OutsideTimeWindow
    ├── RemainingAmount > 0 → HasRemainingAmount (Warnung, Einlass möglich)
    └── Alles OK → Valid
CheckInTicketCommand
    ├── Status → CheckedIn
    ├── CheckedInAt/By/WorkplaceId setzen
    ├── AssignedCardId setzen (optional, für Disco-Karte)
    └── StatusHistory-Eintrag erstellen

CheckInStatus-Enum:

Değer Anlamı
0 - Valid Bilet geçerli, giriş izni var
1 - Invalid Bilet geçersiz/bulunamadı
2 - Expired Biletin süresi dolmuş
3 - WrongDate Yanlış etkinlik tarihi
4 - OutsideTimeWindow Giriş zaman aralığının dışında
5 - AlreadyUsed Zaten check-in yapılmış
6 - Cancelled Bilet iptal edilmiş
7 - NameMismatch İsim uyuşmazlığı (kişiselleştirilmiş bilet)
8 - HasRemainingAmount Açık kalan tutar (yine de giriş mümkün)

Online Mağaza Ödeme Akışı

1. GET /ticket-shop/catalog
   → Liste verfügbarer Ticket-Typen

2. GET /ticket-shop/capacity?date=2026-03-15
   → Verfügbarkeit für gewähltes Datum

3. POST /ticket-shop/validate
   { articleId, eventDate, quantity }
   → Kapazitätsprüfung, Preisberechnung

4. Zahlung extern (Stripe.js / PayPal SDK)

5. POST /ticket-shop/purchase
   { articleId, eventDate, quantity, customerEmail, customerName, paymentProvider }
   → Tickets erstellen, Bestätigungs-Email mit PDF-Tickets

Kapasite kontrolü MaxCapacityPerDate ve OverbookingPercent değerlerini dikkate alır:

Effektive Kapazität = MaxCapacity + (MaxCapacity × OverbookingPercent / 100)
Verfügbar = Effektive Kapazität - SoldPerDate[datum]

Kapasite Yönetimi

Satılan biletler TicketArticleConfig.SoldPerDate içinde sözlük (Dictionary) olarak saklanır:

{
  "2026-03-15": 42,
  "2026-03-16": 85
}
  • MaxCapacityPerDate olmadan: Sınırsız müsaitlik
  • MaxCapacityPerDate ile: Tarih başına kesin sınır
  • OverbookingPercent ile: Kontrollü aşırı rezervasyona izin verir (örn. %10 = Maks 100'de 110)

Grup Satın Alma

Biletler grup olarak satın alınabilir:

  • TicketArticleConfig üzerinde AllowGroupPurchase ve MaxGroupSize
  • GroupOrderId bir siparişin tüm biletlerini birbirine bağlar
  • Takip için GroupIndex (1 tabanlı) ve GroupTotal
  • CancelTicketGroupCommand tüm grubu iptal eder
  • Kişiselleştirilmiş grup biletleri için isteğe bağlı GuestNames

Disco Entegrasyonu

Bilet yapılandırmaları Disco giriş gruplarıyla ilişkilendirilebilir:

  • EnterGroupId → Bilet satın alımında otomatik atama
  • EnterRuleId → Belirli giriş kuralı
  • FreeArticles → Disco modülünden ücretsiz ürünler
  • Check-in sırasında: Disco kartı (AssignedCardId) atanabilir

Gün Sonu Raporu Entegrasyonu

IDayCloseTicketStatsProvider gün sonu raporu için şu göstergeleri sunar:

Gösterge Açıklama
SoldOnlineCount Dönem içinde online satılan biletler
SoldOnlineAmount Online satılan biletlerin toplam cirosu
CheckedInCount Dönem içinde check-in yapılan biletler
RefundedCount Dönem içinde iade edilen biletler
RefundedAmount Toplam iade tutarı

E-posta Entegrasyonu

İki e-posta şablonu:

  • ticket_confirmation: Bilet satın alımından sonra gönderilir, ek olarak PDF bilet içerir
  • ticket_cancellation: İptal sonrasında gönderilir

OperationalConfig Alanları

Alan Tür Standart Açıklama
TicketingEnabled bool false Biletleme modülü etkin
TicketScanInDirectSale bool true POS biletleri tarayabilir

Frontend Bileşenleri

Yönetici — Biletler sayfası (/admin/tickets)

4 sekme: 1. Biletler: Arama, durum filtresi, tarih aralığı, TicketNumber/Name/EventDate/Status/Price içeren tablo, iptal işlemi, PDF bağlantısı 2. Kapasiteler: Tarih seçimi, ilerleme çubuğuyla kapasite kartları (Satılan/Maks/Mevcut) 3. İstatistikler: Gösterge kartları (Satılan, Check-in yapılan, Açık, İptal edilen, Süresi dolan, İade edilen, Ciro) 4. Yapılandırma: Tüm bilet ürün yapılandırmalarının tablosu

Yönetici — Ürün düzenleyici (Bilet sekmesi)

ExtraOption === 15 (bilet türü) olduğunda gösterilir: - Disco bağlantısı (EnterGroup açılır menüsü) - Ekstralar (Ücretsiz içecekler, ücretsiz bakiye, vestiyer) - Kontenjan (MaxCapacity, Overbooking) - İptal kuralları (Süre, iade) - Seçenekler (Kişiselleştirme, grup satın alma, zaman aralığı, kısmi ödeme)

POS — Bilet taraması (/pos/ticket-scan)

  • Otomatik odaklı QR kod giriş alanı
  • Renkli durumla doğrulama sonucu (Yeşil/Turuncu/Kırmızı)
  • Bilet ayrıntıları (Numara, ad, tarih, misafir, ekstralar)
  • Geçerli biletlerde giriş düğmesi
  • Açık ödemede kalan tutar uyarısı

Disco — Giriş (QR bilet bölümü)

  • Kart taramasının altında QR kod girişi
  • Kart atamasıyla doğrulama ve check-in
  • Yalnızca TicketingEnabled olduğunda görünür

Online Mağaza — Bilet kataloğu

  • Fiyat, avantajlar (Ücretsiz içecekler, bakiye, vestiyer), tarih seçimi içeren bilet kartları
  • ticket:{date} notuyla sepet entegrasyonu

Extension kaydı

// Admin-Sidebar
{ id: 'tickets', label: 'Tickets', icon: 'fa-solid fa-ticket', order: 115 }

// POS-Menü
{ id: 'ticket-scan', name: 'Ticket-Scan', icon: 'fa-solid fa-qrcode', order: 120 }

DI kaydı (TicketingModule)

// Entities
Ticket:              cacheAll=false, BackupCategory="tickets"
TicketArticleConfig: cacheAll=true,  BackupCategory="tickets"

// Services
ITicketQrService             TicketQrService (Singleton)
ITicketPdfService            TicketPdfService (Scoped)
IDayCloseTicketStatsProvider  TicketDayCloseStatsProvider (Scoped)
ExpireTicketsJob             HostedService

// MediatR + FluentValidation
services.AddMediatR(...)
services.AddValidatorsFromAssembly(...)

Veritabanı: CouchDB ve EF Core (SQLite/SQL Server) destekler. String-MaxLength kısıtlamaları ve Decimal-Precision(18,4) ile EF Core yapılandırması. SoldPerDate, JSON-TEXT sütunu olarak saklanır.

ID önekleri (IdGenerator): "Ticket" → "tkt", "TicketArticleConfig" → "tktcfg"

Bağımlılıklar

Paket Kullanım
QRCoder QR kod üretimi (PNG, SVG)
QuestPDF PDF bilet oluşturma (A6 formatı)
MediatR CQRS deseni
FluentValidation İstek doğrulama