DEV Community

AlexfamDan
AlexfamDan

Posted on

Helping with Dev

public async Task CreateBookingAsync(
CreateBookingCommand request,
CancellationToken cancellationToken)
{
// Get current logged-in employee
var employeeId = _currentUserService.UserId;

// Rule 1: Purpose is required
if (string.IsNullOrWhiteSpace(request.Purpose))
{
    throw new BadRequestException(
        "Purpose is required.");
}

// Rule 2: Travel date cannot be in the past
if (request.TravelDate < DateTime.UtcNow)
{
    throw new BadRequestException(
        "Travel date cannot be in the past.");
}

// Rule 3: Return date cannot be before travel date
if (request.ReturnDate < request.TravelDate)
{
    throw new BadRequestException(
        "Return date cannot be before travel date.");
}

// Get vehicle
var vehicle = await _vehicleRepository.GetByIdAsync(
    request.VehicleId,
    cancellationToken);

if (vehicle == null)
{
    throw new NotFoundException(
        "Vehicle not found.");
}

// Rule 4: Vehicle must be active
if (!vehicle.IsActive)
{
    throw new BadRequestException(
        "This vehicle is not active.");
}

// Rule 5: Check whether vehicle already has
// an approved/allocated booking for these dates
var vehicleAlreadyBooked =
    await _vehicleBookingRepository
        .HasVehicleBookingOverlapAsync(
            request.VehicleId,
            request.TravelDate,
            request.ReturnDate,
            cancellationToken);

if (vehicleAlreadyBooked)
{
    throw new BadRequestException(
        "Vehicle is already booked for the selected dates.");
}

// Rule 6: Check whether employee already has
// an approved/allocated booking for these dates
var employeeAlreadyBooked =
    await _vehicleBookingRepository
        .HasEmployeeBookingOverlapAsync(
            employeeId,
            request.TravelDate,
            request.ReturnDate,
            cancellationToken);

if (employeeAlreadyBooked)
{
    throw new BadRequestException(
        "You already have a booking for the selected dates.");
}

// Create booking
var booking = new VehicleBooking
{
    Id = Guid.NewGuid(),

    VehicleId = request.VehicleId,

    EmployeeId = employeeId,

    BookingStatus = BookingStatus.Pending,

    TravelDate = request.TravelDate,

    ReturnDate = request.ReturnDate,

    Purpose = request.Purpose,

    Destination = request.Destination,

    CreatedDate = DateTime.UtcNow
};

await _vehicleBookingRepository.AddAsync(
    booking,
    cancellationToken);

// Create initial status history
var statusHistory = new BookingStatusHistory
{
    Id = Guid.NewGuid(),

    VehicleBookingId = booking.Id,

    Status = BookingStatus.Pending,

    UpdatedDate = DateTime.UtcNow
};

await _bookingStatusHistoryRepository.AddAsync(
    statusHistory,
    cancellationToken);

return new CreateBookingCommandResponse
{
    BookingId = booking.Id,

    VehicleId = booking.VehicleId,

    BookingStatus = booking.BookingStatus,

    TravelDate = booking.TravelDate,

    ReturnDate = booking.ReturnDate,

    Purpose = booking.Purpose,

    Destination = booking.Destination,

    Message = "Booking created successfully."
};
Enter fullscreen mode Exit fullscreen mode

}

public async Task HasVehicleBookingOverlapAsync(
Guid vehicleId,
DateTime travelDate,
DateTime returnDate,
CancellationToken cancellationToken)
{
return await _context.VehicleBookings.AnyAsync(
x =>
x.VehicleId == vehicleId
&&
(x.BookingStatus == BookingStatus.Approved
|| x.BookingStatus == BookingStatus.Allocated)
&&
x.TravelDate <= returnDate
&&
x.ReturnDate >= travelDate,
cancellationToken);
}

public async Task AllocateVehicleAsync(
AllocateVehicleCommand request,
CancellationToken cancellationToken)
{
// Get booking
var booking = await _vehicleBookingRepository.GetByIdAsync(
request.BookingId,
cancellationToken);

if (booking == null)
{
    throw new NotFoundException(
        "Booking not found.");
}

// Business Rule:
// Vehicle can be allocated only to a Pending booking.
if (booking.BookingStatus != BookingStatus.Pending)
{
    throw new BadRequestException(
        "Vehicle can be allocated only to a pending booking.");
}

// Get vehicle
var vehicle = await _vehicleRepository.GetByIdAsync(
    request.VehicleId,
    cancellationToken);

if (vehicle == null)
{
    throw new NotFoundException(
        "Vehicle not found.");
}

// Business Rule:
// Vehicle must be active.
if (!vehicle.IsActive)
{
    throw new BadRequestException(
        "This vehicle is not active.");
}

// Business Rule:
// Vehicle cannot be allocated if it already
// has an overlapping approved/allocated booking.
var vehicleAlreadyBooked =
    await _vehicleBookingRepository
        .HasVehicleActiveBookingOverlapAsync(
            request.VehicleId,
            booking.TravelDate,
            booking.ReturnDate,
            booking.Id,
            cancellationToken);

if (vehicleAlreadyBooked)
{
    throw new BadRequestException(
        "Vehicle is already allocated for the selected dates.");
}

// Allocate vehicle
booking.VehicleId = request.VehicleId;

booking.BookingStatus = BookingStatus.Allocated;

// Create status history
var statusHistory = new BookingStatusHistory
{
    Id = Guid.NewGuid(),

    VehicleBookingId = booking.Id,

    Status = BookingStatus.Allocated,

    UpdatedDate = DateTime.UtcNow
};

// Update booking
await _vehicleBookingRepository.UpdateAsync(
    booking,
    cancellationToken);

// Add history
await _bookingStatusHistoryRepository.AddAsync(
    statusHistory,
    cancellationToken);

return new AllocateVehicleCommandResponse
{
    BookingId = booking.Id,

    VehicleId = booking.VehicleId,

    Status = booking.BookingStatus,

    Message = "Vehicle allocated successfully."
};
Enter fullscreen mode Exit fullscreen mode

}

public async Task CompleteTripAsync(
CompleteTripCommand request,
CancellationToken cancellationToken)
{
// Get booking
var booking = await _vehicleBookingRepository.GetByIdAsync(
request.BookingId,
cancellationToken);

if (booking == null)
{
    throw new NotFoundException(
        "Booking not found.");
}

// Business Rule:
// Trip can be completed only after vehicle is allocated.
if (booking.BookingStatus != BookingStatus.Allocated)
{
    throw new BadRequestException(
        "Trip can be completed only after the vehicle is allocated.");
}

// Change status
booking.BookingStatus = BookingStatus.Completed;

// Create status history
var statusHistory = new BookingStatusHistory
{
    Id = Guid.NewGuid(),

    VehicleBookingId = booking.Id,

    Status = BookingStatus.Completed,

    UpdatedDate = DateTime.UtcNow
};

// Update booking
await _vehicleBookingRepository.UpdateAsync(
    booking,
    cancellationToken);

// Add history
await _bookingStatusHistoryRepository.AddAsync(
    statusHistory,
    cancellationToken);

return new CompleteTripCommandResponse
{
    BookingId = booking.Id,

    Status = booking.BookingStatus,

    Message = "Trip completed successfully."
};
Enter fullscreen mode Exit fullscreen mode

}

public async Task> ViewMyBookingsAsync(
ViewMyBookingsQuery request,
CancellationToken cancellationToken)
{
// Get current logged-in employee
var employeeId = _currentUserService.UserId;

var bookings = await _vehicleBookingRepository.ViewMyBookingsAsync(
    employeeId,
    request.PageNumber,
    request.PageSize,
    request.Search,
    request.SortOrder,
    request.SortBy,
    cancellationToken);

if (bookings == null || !bookings.Any())
{
    throw new BadRequestException(
        "Bookings not found.");
}

var bookingResponse = new List<ViewMyBookingsQueryResponse>();

foreach (var booking in bookings)
{
    bookingResponse.Add(new ViewMyBookingsQueryResponse
    {
        Id = booking.Id,
        VehicleId = booking.VehicleId,
        BookingStatus = booking.BookingStatus,
        TravelDate = booking.TravelDate,
        ReturnDate = booking.ReturnDate,
        Purpose = booking.Purpose,
        Destination = booking.Destination,
        CreatedDate = booking.CreatedDate
    });
}

return bookingResponse;
Enter fullscreen mode Exit fullscreen mode

}

public async Task> ViewMyBookingsAsync(
Guid employeeId,
int pageNumber,
int pageSize,
string? search,
string? sortOrder,
string? sortBy,
CancellationToken cancellationToken)
{
IQueryable query =
_context.VehicleBookings
.AsNoTracking()
.Where(x => x.EmployeeId == employeeId);

// Search
if (!string.IsNullOrWhiteSpace(search))
{
    search = search.Trim();

    query = query.Where(x =>
        x.Purpose.Contains(search) ||
        x.Destination.Contains(search));
}

// Sorting
query = sortBy?.ToLower() switch
{
    "traveldate" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.TravelDate)
        : query.OrderBy(x => x.TravelDate),

    "returndate" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.ReturnDate)
        : query.OrderBy(x => x.ReturnDate),

    "destination" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.Destination)
        : query.OrderBy(x => x.Destination),

    "purpose" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.Purpose)
        : query.OrderBy(x => x.Purpose),

    "status" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.BookingStatus)
        : query.OrderBy(x => x.BookingStatus),

    "createddate" => sortOrder?.ToLower() == "desc"
        ? query.OrderByDescending(x => x.CreatedDate)
        : query.OrderBy(x => x.CreatedDate),

    _ => query.OrderByDescending(x => x.CreatedDate)
};

// Pagination
pageNumber = pageNumber < 1 ? 1 : pageNumber;

pageSize = pageSize < 1
    ? 10
    : Math.Min(pageSize, 100);

query = query
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);

return await query.ToListAsync(cancellationToken);
Enter fullscreen mode Exit fullscreen mode

}

Top comments (0)