Implement product multi-buys and variants system

Major restructuring of product variations:
- Renamed ProductVariation to ProductMultiBuy for quantity-based pricing (e.g., "3 for £25")
- Added new ProductVariant model for string-based options (colors, flavors)
- Complete separation of multi-buy pricing from variant selection

Features implemented:
- Multi-buy deals with automatic price-per-unit calculation
- Product variants for colors/flavors/sizes with stock tracking
- TeleBot checkout supports both multi-buys and variant selection
- Shopping cart correctly calculates multi-buy bundle prices
- Order system tracks selected variants and multi-buy choices
- Real-time bot activity monitoring with SignalR
- Public bot directory page with QR codes for Telegram launch
- Admin dashboard shows multi-buy and variant metrics

Technical changes:
- Updated all DTOs, services, and controllers
- Fixed cart total calculation for multi-buy bundles
- Comprehensive test coverage for new functionality
- All existing tests passing with new features

Database changes:
- Migrated ProductVariations to ProductMultiBuys
- Added ProductVariants table
- Updated OrderItems to track variants

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-21 00:30:12 +01:00
parent 7683b7dfe5
commit 034b8facee
46 changed files with 3190 additions and 332 deletions

View File

@@ -183,7 +183,7 @@ public class ProductImportService : IProductImportService
// Import variations if provided
if (!string.IsNullOrEmpty(importDto.Variations))
{
await ImportProductVariationsAsync(product.Id, importDto.Variations);
await ImportProductMultiBuysAsync(product.Id, importDto.Variations);
}
// Import photos if provided
@@ -206,7 +206,7 @@ public class ProductImportService : IProductImportService
}
}
private async Task ImportProductVariationsAsync(Guid productId, string variationsText)
private async Task ImportProductMultiBuysAsync(Guid productId, string variationsText)
{
// Format: "Single Item:1:10.00;Twin Pack:2:19.00;Triple Pack:3:25.00"
var variations = variationsText.Split(';', StringSplitOptions.RemoveEmptyEntries);
@@ -216,7 +216,7 @@ public class ProductImportService : IProductImportService
var parts = variations[i].Split(':', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 3)
{
var variationDto = new CreateProductVariationDto
var multiBuyDto = new CreateProductMultiBuyDto
{
ProductId = productId,
Name = parts[0].Trim(),
@@ -226,7 +226,7 @@ public class ProductImportService : IProductImportService
SortOrder = i
};
await _productService.CreateProductVariationAsync(variationDto);
await _productService.CreateProductMultiBuyAsync(multiBuyDto);
}
}
}
@@ -275,7 +275,7 @@ public class ProductImportService : IProductImportService
foreach (var product in products)
{
// Build variations string
var variationsText = string.Join(";", product.Variations.OrderBy(v => v.SortOrder)
var variationsText = string.Join(";", product.MultiBuys.OrderBy(v => v.SortOrder)
.Select(v => $"{v.Name}:{v.Quantity}:{v.Price:F2}"));
// Build photo URLs string