Initial commit: Lagerbestand Blazor Projekt
This commit is contained in:
commit
cba9414e92
105 files changed
+36431
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user