Add database seeder with sample data and test guide

This commit is contained in:
steinhelge
2025-11-23 21:46:18 +01:00
parent 4b61bd7c29
commit 646ab0aef1
3 changed files with 569 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
# Test Guide - Digital Access and Serving System
## Quick Start
1. **Start the system** (if not already running):
```bash
# Terminal 1: Database
docker-compose up -d
# Terminal 2: Backend (will auto-seed database)
dotnet run --project src/Hospitality.Backend
# Terminal 3: Frontend
cd src/hospitality-web
npm run dev
```
2. **Access the application**: http://localhost:5173
## Sample Data Overview
The database is pre-loaded with:
### Events
1. **Summer Festival 2025** (July 1-3, Oslo)
2. **Winter Gala 2025** (December 15, Bergen)
### Groups (Summer Festival)
1. **VIP Sponsors** - 3 people
2. **Event Staff** - 2 people
3. **General Admission** - 2 people
### Products (Summer Festival)
- Beer (Drink)
- Wine (Drink)
- Lunch (Meal)
- Dinner (Meal)
- VIP Lounge Access (Access)
### Test Guests with QR Codes
**Note**: QR codes are GUIDs that change each time you seed. To get the actual QR codes:
1. Go to Admin → Summer Festival 2025 → VIP Sponsors
2. Click on a person to see their QR code
## Test Scenarios
### Scenario 1: Admin - View Existing Data
1. Go to http://localhost:5173
2. You should see 2 events: "Summer Festival 2025" and "Winter Gala 2025"
3. Click on "Summer Festival 2025"
4. You should see:
- 3 groups (VIP Sponsors, Event Staff, General Admission)
- 5 products (Beer, Wine, Lunch, Dinner, VIP Lounge Access)
### Scenario 2: Admin - View Group Details
1. From Event Detail page, click on "VIP Sponsors"
2. You should see 3 people:
- **John Doe** (john.doe@example.com)
- Beer: 8/10 remaining
- Wine: 4/5 remaining
- Lunch: 2/3 remaining
- Dinner: 3/3 remaining
- VIP Access: 1/1 remaining
- **Jane Smith** (jane.smith@example.com)
- All quotas unused (full amounts)
- **Bob Wilson** (bob.wilson@example.com)
- Beer: 9/12 remaining
- Wine: 2/4 remaining
- Lunch: 1/3 remaining
- Dinner: 2/3 remaining
- VIP Access: 0/1 (used)
### Scenario 3: Staff Scanner - Lookup Guest
1. Go to http://localhost:5173/staff
2. From Admin, get John Doe's QR code:
- Go to Summer Festival → VIP Sponsors → John Doe
- Copy the QR code (GUID under "QR: ...")
3. Paste the QR code in Staff Scanner
4. Click "Lookup Person"
5. You should see:
- Name: John Doe
- Email: john.doe@example.com
- All quotas with remaining amounts
6. Click "Use 1" on Beer
7. Verify quota decreases from 8 to 7
### Scenario 4: Guest View - Check Quotas
1. Go to http://localhost:5173/guest
2. Enter John Doe's QR code (same as above)
3. Click "Show My QR Code"
4. You should see:
- Large QR code for scanning
- All quotas with progress bars
- Updated Beer quota (7/10 if you used 1 in Scenario 3)
### Scenario 5: Admin - Add New Person
1. Go to Summer Festival → VIP Sponsors
2. Click "Add Person"
3. Fill in:
- Name: "Test User"
- Email: "test@example.com"
- Phone: "+47 999 88 777"
4. Click "Add Person"
5. Person appears in the list with a new QR code
6. Click "Assign Quota" on the new person
7. Select "Beer" and amount "5"
8. Click "Assign Quota"
9. Verify quota appears under the person
### Scenario 6: Test Different User Types
**VIP Sponsor (High Quotas)**
- Use Jane Smith's QR code
- Has full quotas: Beer (8), Wine (6), Lunch (3), Dinner (3), VIP Access (1)
**Staff Member (Limited Quotas)**
- Use Alice Staff's QR code
- Has: Beer (3/4), Lunch (1/2)
**General Guest (Basic Quotas)**
- Use Emily Guest's QR code
- Has: Beer (2/3), Lunch (1/1)
**Heavy User (Mostly Used)**
- Use Bob Wilson's QR code
- Has used most quotas, VIP Access is depleted
## Testing Checklist
- [ ] View events list
- [ ] View event details with groups and products
- [ ] View group details with people
- [ ] View person's QR code and quotas
- [ ] Add new person to group
- [ ] Assign quota to person
- [ ] Use Staff Scanner to lookup person
- [ ] Record transaction (Use 1 or Use 2)
- [ ] Verify quota decreases after transaction
- [ ] Use Guest View to see QR code
- [ ] Verify progress bars show correct status
- [ ] Test with different user types (VIP, Staff, Guest)
- [ ] Try to use more than remaining quota (should fail)
## Expected Behaviors
### Success Cases
- ✅ Quotas display correctly with remaining amounts
- ✅ Transactions record successfully when quota available
- ✅ Quota decrements after transaction
- ✅ Progress bars update in real-time
- ✅ QR codes are unique per person
### Error Cases
- ❌ Cannot use more than remaining quota
- ❌ Invalid QR code shows "Not Found"
- ❌ Empty fields in forms show validation errors
## Quick Reference - Sample QR Codes
**To get actual QR codes:**
1. Go to Admin → Summer Festival 2025
2. Click on a group (e.g., VIP Sponsors)
3. Each person's card shows their QR code (starts with "QR: ...")
4. Copy the full GUID for testing
**Sample People:**
- John Doe - Has some usage, good for testing transactions
- Jane Smith - Fresh quotas, good for first-time testing
- Bob Wilson - Heavy usage, good for testing limits
- Alice Staff - Staff member with limited quotas
- Emily Guest - Basic guest with minimal quotas
## Troubleshooting
**No data showing?**
- Restart backend: `dotnet run --project src/Hospitality.Backend`
- Database will auto-seed on startup
**QR code not working?**
- Make sure you copied the full GUID
- QR codes are case-sensitive
- Get fresh QR code from Admin interface
**Transaction fails?**
- Check remaining quota is > 0
- Verify you're using correct QR code
- Check browser console for errors
Enjoy testing! 🎉
+364
View File
@@ -0,0 +1,364 @@
using Hospitality.Domain.Entities;
using Hospitality.Domain.Enums;
using Hospitality.Infrastructure.Data;
namespace Hospitality.Backend.Data;
public static class DbSeeder
{
public static async Task SeedAsync(HospitalityDbContext context)
{
// Check if already seeded
if (context.Events.Any())
{
return;
}
// Create Events
var summerFestival = new Event
{
Id = Guid.NewGuid(),
Name = "Summer Festival 2025",
StartDate = DateTime.SpecifyKind(new DateTime(2025, 7, 1, 10, 0, 0), DateTimeKind.Utc),
EndDate = DateTime.SpecifyKind(new DateTime(2025, 7, 3, 22, 0, 0), DateTimeKind.Utc),
Location = "Oslo"
};
var winterGala = new Event
{
Id = Guid.NewGuid(),
Name = "Winter Gala 2025",
StartDate = DateTime.SpecifyKind(new DateTime(2025, 12, 15, 18, 0, 0), DateTimeKind.Utc),
EndDate = DateTime.SpecifyKind(new DateTime(2025, 12, 15, 23, 0, 0), DateTimeKind.Utc),
Location = "Bergen"
};
context.Events.AddRange(summerFestival, winterGala);
// Create Products for Summer Festival
var beer = new Product
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "Beer",
Type = ProductType.Drink
};
var wine = new Product
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "Wine",
Type = ProductType.Drink
};
var lunch = new Product
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "Lunch",
Type = ProductType.Meal
};
var dinner = new Product
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "Dinner",
Type = ProductType.Meal
};
var vipAccess = new Product
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "VIP Lounge Access",
Type = ProductType.Access
};
context.Products.AddRange(beer, wine, lunch, dinner, vipAccess);
// Create Products for Winter Gala
var champagne = new Product
{
Id = Guid.NewGuid(),
EventId = winterGala.Id,
Name = "Champagne",
Type = ProductType.Drink
};
var galaDinner = new Product
{
Id = Guid.NewGuid(),
EventId = winterGala.Id,
Name = "Gala Dinner",
Type = ProductType.Meal
};
context.Products.AddRange(champagne, galaDinner);
// Create Groups for Summer Festival
var vipSponsors = new Group
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "VIP Sponsors",
ContactPersonName = "Sarah Johnson",
ContactEmail = "sarah@sponsors.com"
};
var staff = new Group
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "Event Staff",
ContactPersonName = "Mike Anderson",
ContactEmail = "mike@eventstaff.com"
};
var generalAdmission = new Group
{
Id = Guid.NewGuid(),
EventId = summerFestival.Id,
Name = "General Admission",
ContactPersonName = "Lisa Chen",
ContactEmail = "lisa@tickets.com"
};
context.Groups.AddRange(vipSponsors, staff, generalAdmission);
// Create People for VIP Sponsors
var johnDoe = new Person
{
Id = Guid.NewGuid(),
GroupId = vipSponsors.Id,
QrCode = Guid.NewGuid(),
Name = "John Doe",
Email = "john.doe@example.com",
PhoneNumber = "+47 123 45 678"
};
var janeSmith = new Person
{
Id = Guid.NewGuid(),
GroupId = vipSponsors.Id,
QrCode = Guid.NewGuid(),
Name = "Jane Smith",
Email = "jane.smith@example.com",
PhoneNumber = "+47 234 56 789"
};
var bobWilson = new Person
{
Id = Guid.NewGuid(),
GroupId = vipSponsors.Id,
QrCode = Guid.NewGuid(),
Name = "Bob Wilson",
Email = "bob.wilson@example.com",
PhoneNumber = "+47 345 67 890"
};
// Create People for Staff
var aliceStaff = new Person
{
Id = Guid.NewGuid(),
GroupId = staff.Id,
QrCode = Guid.NewGuid(),
Name = "Alice Staff",
Email = "alice@staff.com",
PhoneNumber = "+47 456 78 901"
};
var charlieStaff = new Person
{
Id = Guid.NewGuid(),
GroupId = staff.Id,
QrCode = Guid.NewGuid(),
Name = "Charlie Staff",
Email = "charlie@staff.com",
PhoneNumber = "+47 567 89 012"
};
// Create People for General Admission
var emilyGuest = new Person
{
Id = Guid.NewGuid(),
GroupId = generalAdmission.Id,
QrCode = Guid.NewGuid(),
Name = "Emily Guest",
Email = "emily@guest.com",
PhoneNumber = "+47 678 90 123"
};
var davidGuest = new Person
{
Id = Guid.NewGuid(),
GroupId = generalAdmission.Id,
QrCode = Guid.NewGuid(),
Name = "David Guest",
Email = "david@guest.com",
PhoneNumber = "+47 789 01 234"
};
context.People.AddRange(johnDoe, janeSmith, bobWilson, aliceStaff, charlieStaff, emilyGuest, davidGuest);
// Assign Quotas to VIP Sponsors
var johnQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = johnDoe.Id, ProductId = beer.Id, InitialAmount = 10, UsedAmount = 2 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = johnDoe.Id, ProductId = wine.Id, InitialAmount = 5, UsedAmount = 1 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = johnDoe.Id, ProductId = lunch.Id, InitialAmount = 3, UsedAmount = 1 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = johnDoe.Id, ProductId = dinner.Id, InitialAmount = 3, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = johnDoe.Id, ProductId = vipAccess.Id, InitialAmount = 1, UsedAmount = 0 }
};
var janeQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = janeSmith.Id, ProductId = beer.Id, InitialAmount = 8, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = janeSmith.Id, ProductId = wine.Id, InitialAmount = 6, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = janeSmith.Id, ProductId = lunch.Id, InitialAmount = 3, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = janeSmith.Id, ProductId = dinner.Id, InitialAmount = 3, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = janeSmith.Id, ProductId = vipAccess.Id, InitialAmount = 1, UsedAmount = 0 }
};
var bobQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = bobWilson.Id, ProductId = beer.Id, InitialAmount = 12, UsedAmount = 3 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = bobWilson.Id, ProductId = wine.Id, InitialAmount = 4, UsedAmount = 2 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = bobWilson.Id, ProductId = lunch.Id, InitialAmount = 3, UsedAmount = 2 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = bobWilson.Id, ProductId = dinner.Id, InitialAmount = 3, UsedAmount = 1 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = bobWilson.Id, ProductId = vipAccess.Id, InitialAmount = 1, UsedAmount = 1 }
};
// Assign Quotas to Staff
var aliceQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = aliceStaff.Id, ProductId = beer.Id, InitialAmount = 4, UsedAmount = 1 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = aliceStaff.Id, ProductId = lunch.Id, InitialAmount = 2, UsedAmount = 1 }
};
var charlieQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = charlieStaff.Id, ProductId = beer.Id, InitialAmount = 4, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = charlieStaff.Id, ProductId = lunch.Id, InitialAmount = 2, UsedAmount = 0 }
};
// Assign Quotas to General Admission
var emilyQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = emilyGuest.Id, ProductId = beer.Id, InitialAmount = 3, UsedAmount = 1 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = emilyGuest.Id, ProductId = lunch.Id, InitialAmount = 1, UsedAmount = 0 }
};
var davidQuotas = new[]
{
new PersonQuota { Id = Guid.NewGuid(), PersonId = davidGuest.Id, ProductId = beer.Id, InitialAmount = 3, UsedAmount = 0 },
new PersonQuota { Id = Guid.NewGuid(), PersonId = davidGuest.Id, ProductId = lunch.Id, InitialAmount = 1, UsedAmount = 0 }
};
context.PersonQuotas.AddRange(johnQuotas);
context.PersonQuotas.AddRange(janeQuotas);
context.PersonQuotas.AddRange(bobQuotas);
context.PersonQuotas.AddRange(aliceQuotas);
context.PersonQuotas.AddRange(charlieQuotas);
context.PersonQuotas.AddRange(emilyQuotas);
context.PersonQuotas.AddRange(davidQuotas);
// Create some sample transactions
var transactions = new[]
{
new Transaction
{
Id = Guid.NewGuid(),
PersonId = johnDoe.Id,
ProductId = beer.Id,
Amount = 2,
Timestamp = DateTime.UtcNow.AddHours(-5)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = johnDoe.Id,
ProductId = wine.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-3)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = johnDoe.Id,
ProductId = lunch.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-2)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = bobWilson.Id,
ProductId = beer.Id,
Amount = 3,
Timestamp = DateTime.UtcNow.AddHours(-4)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = bobWilson.Id,
ProductId = wine.Id,
Amount = 2,
Timestamp = DateTime.UtcNow.AddHours(-3)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = bobWilson.Id,
ProductId = lunch.Id,
Amount = 2,
Timestamp = DateTime.UtcNow.AddHours(-2)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = bobWilson.Id,
ProductId = dinner.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-1)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = bobWilson.Id,
ProductId = vipAccess.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-6)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = aliceStaff.Id,
ProductId = beer.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-1)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = aliceStaff.Id,
ProductId = lunch.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddHours(-2)
},
new Transaction
{
Id = Guid.NewGuid(),
PersonId = emilyGuest.Id,
ProductId = beer.Id,
Amount = 1,
Timestamp = DateTime.UtcNow.AddMinutes(-30)
}
};
context.Transactions.AddRange(transactions);
await context.SaveChangesAsync();
}
}
+8
View File
@@ -22,6 +22,14 @@ builder.Services.AddScoped<ITransactionService, TransactionService>();
var app = builder.Build(); var app = builder.Build();
// Seed database with sample data
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<HospitalityDbContext>();
await Hospitality.Backend.Data.DbSeeder.SeedAsync(context);
}
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {