Initial commit: Lagerbestand Blazor Projekt

This commit is contained in:
spaethf committed 2026-09-21 21:28:23 +02:00
commit cba9414e92
105 files changed
+36431

No files matched your search

+315
View File
@@ -0,0 +1,315 @@
using Lagerbestand.Data;
using Lagerbestand.Models;
using Microsoft.EntityFrameworkCore;
namespace Lagerbestand.Services;
public interface ICustomerService
{
Task<List<Customer>> GetAllAsync(bool includeInactive = false);
Task<Customer?> GetByIdAsync(Guid id);
Task<Customer?> GetByCustomerNumberAsync(string customerNumber);
Task<List<Customer>> SearchAsync(string searchTerm);
Task<Customer?> FindByVehicleAsync(string searchTerm);
Task<Customer> CreateAsync(Customer customer);
Task<Customer> UpdateAsync(Customer customer);
Task<bool> DeleteAsync(Guid id);
Task<string> GenerateCustomerNumberAsync();
Task<int> GetStoredTireStackCountAsync(Guid customerId);
// Vehicle methods
Task<List<Vehicle>> GetVehiclesAsync(Guid customerId);
Task<Vehicle?> GetVehicleByIdAsync(Guid vehicleId);
Task<Vehicle> AddVehicleAsync(Guid customerId, Vehicle vehicle);
Task<Vehicle> UpdateVehicleAsync(Vehicle vehicle);
Task<bool> DeleteVehicleAsync(Guid vehicleId);
Task SetPrimaryVehicleAsync(Guid customerId, Guid vehicleId);
Task<Customer> CreateWithVehicleAsync(Customer customer, Vehicle vehicle);
}
public class CustomerService : ICustomerService
{
private readonly AppDbContext _context;
private readonly ILogger<CustomerService> _logger;
public CustomerService(AppDbContext context, ILogger<CustomerService> logger)
{
_context = context;
_logger = logger;
}
public async Task<List<Customer>> GetAllAsync(bool includeInactive = false)
{
var query = _context.Customers
.Include(c => c.Vehicles.Where(v => v.IsActive))
.AsQueryable();
if (!includeInactive)
query = query.Where(c => c.IsActive);
return await query
.OrderBy(c => c.LastName)
.ThenBy(c => c.FirstName)
.ToListAsync();
}
public async Task<Customer?> GetByIdAsync(Guid id)
{
return await _context.Customers
.Include(c => c.Vehicles.Where(v => v.IsActive))
.Include(c => c.TireStacks.Where(ts => ts.Status == TireStackStatus.Stored))
.ThenInclude(ts => ts.StorageLocation)
.Include(c => c.TireStacks.Where(ts => ts.Status == TireStackStatus.Stored))
.ThenInclude(ts => ts.Vehicle)
.AsSplitQuery()
.FirstOrDefaultAsync(c => c.Id == id);
}
public async Task<Customer?> GetByCustomerNumberAsync(string customerNumber)
{
return await _context.Customers
.Include(c => c.Vehicles.Where(v => v.IsActive))
.FirstOrDefaultAsync(c => c.CustomerNumber == customerNumber);
}
public async Task<List<Customer>> SearchAsync(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
return await GetAllAsync();
var term = searchTerm.Trim().ToLower();
return await _context.Customers
.AsNoTracking()
.AsSplitQuery()
.Include(c => c.Vehicles.Where(v => v.IsActive))
.Include(c => c.TireStacks.Where(ts => ts.Status == TireStackStatus.Stored))
.Where(c => c.IsActive && (
c.FirstName != null && c.FirstName.ToLower().Contains(term) ||
c.LastName.ToLower().Contains(term) ||
(c.Company != null && c.Company.ToLower().Contains(term)) ||
c.CustomerNumber.ToLower().Contains(term) ||
(c.Phone != null && c.Phone.Contains(term)) ||
// Search in vehicles
c.Vehicles.Any(v => v.IsActive && (
v.Make.ToLower().Contains(term) ||
(v.Model != null && v.Model.ToLower().Contains(term))))
))
.OrderBy(c => c.LastName)
.ThenBy(c => c.FirstName)
.Take(50)
.ToListAsync();
}
public async Task<Customer?> FindByVehicleAsync(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
return null;
var term = searchTerm.Trim().ToLower();
// Server-side search in Vehicles table by Make or Model
var matchingVehicle = await _context.Vehicles
.Include(v => v.Customer)
.Where(v => v.IsActive && v.Customer != null && v.Customer.IsActive)
.FirstOrDefaultAsync(v =>
v.Make.ToLower().Contains(term) ||
(v.Model != null && v.Model.ToLower().Contains(term)));
if (matchingVehicle?.Customer != null)
{
return await GetByIdAsync(matchingVehicle.Customer.Id);
}
return null;
}
public async Task<Customer> CreateAsync(Customer customer)
{
if (string.IsNullOrEmpty(customer.CustomerNumber))
customer.CustomerNumber = await GenerateCustomerNumberAsync();
customer.CreatedAt = DateTime.UtcNow;
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
_logger.LogInformation("Customer created: {CustomerNumber} - {Name}",
customer.CustomerNumber, customer.FullName);
return customer;
}
public async Task<Customer> CreateWithVehicleAsync(Customer customer, Vehicle vehicle)
{
if (string.IsNullOrEmpty(customer.CustomerNumber))
customer.CustomerNumber = await GenerateCustomerNumberAsync();
customer.CreatedAt = DateTime.UtcNow;
vehicle.IsPrimary = true;
vehicle.CreatedAt = DateTime.UtcNow;
customer.Vehicles.Add(vehicle);
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
_logger.LogInformation("Customer created with vehicle: {CustomerNumber} - {Name}, Vehicle: {Make}",
customer.CustomerNumber, customer.FullName, vehicle.Make);
return customer;
}
public async Task<Customer> UpdateAsync(Customer customer)
{
customer.UpdatedAt = DateTime.UtcNow;
_context.Customers.Update(customer);
await _context.SaveChangesAsync();
_logger.LogInformation("Customer updated: {CustomerNumber}", customer.CustomerNumber);
return customer;
}
public async Task<bool> DeleteAsync(Guid id)
{
var customer = await _context.Customers.FindAsync(id);
if (customer == null)
return false;
// Soft delete - just mark as inactive
customer.IsActive = false;
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Customer deactivated: {CustomerNumber}", customer.CustomerNumber);
return true;
}
public async Task<string> GenerateCustomerNumberAsync()
{
var year = DateTime.UtcNow.Year;
var prefix = $"K-{year}-";
var lastNumber = await _context.Customers
.Where(c => c.CustomerNumber.StartsWith(prefix))
.OrderByDescending(c => c.CustomerNumber)
.Select(c => c.CustomerNumber)
.FirstOrDefaultAsync();
var nextNumber = 1;
if (lastNumber != null)
{
var numPart = lastNumber.Replace(prefix, "");
if (int.TryParse(numPart, out var lastNum))
nextNumber = lastNum + 1;
}
return $"{prefix}{nextNumber:D4}";
}
public async Task<int> GetStoredTireStackCountAsync(Guid customerId)
{
return await _context.TireStacks
.CountAsync(ts => ts.IsActive && ts.CustomerId == customerId && ts.Status == TireStackStatus.Stored);
}
// Vehicle methods
public async Task<List<Vehicle>> GetVehiclesAsync(Guid customerId)
{
return await _context.Vehicles
.Where(v => v.CustomerId == customerId && v.IsActive)
.OrderByDescending(v => v.IsPrimary)
.ThenBy(v => v.CreatedAt)
.ToListAsync();
}
public async Task<Vehicle?> GetVehicleByIdAsync(Guid vehicleId)
{
return await _context.Vehicles
.Include(v => v.Customer)
.Include(v => v.TireStacks.Where(ts => ts.Status == TireStackStatus.Stored))
.FirstOrDefaultAsync(v => v.Id == vehicleId);
}
public async Task<Vehicle> AddVehicleAsync(Guid customerId, Vehicle vehicle)
{
vehicle.CustomerId = customerId;
vehicle.CreatedAt = DateTime.UtcNow;
// If this is the first vehicle, make it primary
var existingVehicles = await _context.Vehicles
.CountAsync(v => v.CustomerId == customerId && v.IsActive);
if (existingVehicles == 0)
vehicle.IsPrimary = true;
_context.Vehicles.Add(vehicle);
await _context.SaveChangesAsync();
_logger.LogInformation("Vehicle added for customer {CustomerId}: {Make}",
customerId, vehicle.Make);
return vehicle;
}
public async Task<Vehicle> UpdateVehicleAsync(Vehicle vehicle)
{
vehicle.UpdatedAt = DateTime.UtcNow;
_context.Vehicles.Update(vehicle);
await _context.SaveChangesAsync();
_logger.LogInformation("Vehicle updated: {VehicleId} - {Make}",
vehicle.Id, vehicle.Make);
return vehicle;
}
public async Task<bool> DeleteVehicleAsync(Guid vehicleId)
{
var vehicle = await _context.Vehicles.FindAsync(vehicleId);
if (vehicle == null)
return false;
// Soft delete
vehicle.IsActive = false;
vehicle.UpdatedAt = DateTime.UtcNow;
// If this was primary, assign primary to another vehicle
if (vehicle.IsPrimary)
{
vehicle.IsPrimary = false;
var nextVehicle = await _context.Vehicles
.Where(v => v.CustomerId == vehicle.CustomerId && v.IsActive && v.Id != vehicleId)
.FirstOrDefaultAsync();
if (nextVehicle != null)
nextVehicle.IsPrimary = true;
}
await _context.SaveChangesAsync();
_logger.LogInformation("Vehicle deactivated: {VehicleId}", vehicleId);
return true;
}
public async Task SetPrimaryVehicleAsync(Guid customerId, Guid vehicleId)
{
var vehicles = await _context.Vehicles
.Where(v => v.CustomerId == customerId && v.IsActive)
.ToListAsync();
foreach (var v in vehicles)
{
v.IsPrimary = v.Id == vehicleId;
v.UpdatedAt = DateTime.UtcNow;
}
await _context.SaveChangesAsync();
_logger.LogInformation("Primary vehicle set for customer {CustomerId}: {VehicleId}",
customerId, vehicleId);
}
}