< Summary - MechanicsSoftware — Coverage Report

Information
Class: MechanicsSoftware.Domain.Entities.ServiceOrder
Assembly: MechanicsSoftware.Domain
File(s): /home/runner/work/mechanics-software/mechanics-software/src/MechanicsSoftware.Domain/Entities/ServiceOrder.cs
Line coverage
100%
Covered lines: 87
Uncovered lines: 0
Coverable lines: 87
Total lines: 152
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_ServiceItems()100%11100%
get_PartItems()100%11100%
Create(...)100%44100%
StartDiagnosis()100%11100%
SendBudget()100%22100%
Approve()100%22100%
Reject()100%22100%
StartExecution()100%22100%
Complete()100%11100%
Deliver()100%11100%
AddServiceItem(...)100%11100%
AddPartItem(...)100%11100%
GenerateBudget()100%44100%
EnsureInDiagnosis()100%22100%

File(s)

/home/runner/work/mechanics-software/mechanics-software/src/MechanicsSoftware.Domain/Entities/ServiceOrder.cs

#LineLine coverage
 1using MechanicsSoftware.Domain.Enums;
 2using MechanicsSoftware.Domain.Exceptions;
 3using MechanicsSoftware.Domain.ValueObjects;
 4
 5namespace MechanicsSoftware.Domain.Entities;
 6
 7public sealed class ServiceOrder : Entity<Guid>
 8{
 2369    private readonly List<ServiceItem> _serviceItems = [];
 23610    private readonly List<PartItem> _partItems = [];
 11
 12    public Guid CustomerId { get; private set; }
 13    public Guid VehicleId { get; private set; }
 14    public ServiceOrderStatus Status { get; private set; } = null!;
 15    public Budget? Budget { get; private set; }
 16    public DateTime CreatedAt { get; private set; }
 17    public DateTime? CompletedAt { get; private set; }
 18    public DateTime? DeliveredAt { get; private set; }
 19
 4220    public IReadOnlyCollection<ServiceItem> ServiceItems => _serviceItems.AsReadOnly();
 5021    public IReadOnlyCollection<PartItem> PartItems => _partItems.AsReadOnly();
 22
 70823    private ServiceOrder() { }
 24
 25    public static ServiceOrder Create(Guid id, Guid customerId, Guid vehicleId)
 22026    {
 22027        if (customerId == Guid.Empty)
 228            throw new DomainException("CustomerId is required.");
 29
 21830        if (vehicleId == Guid.Empty)
 231            throw new DomainException("VehicleId is required.");
 32
 21633        return new ServiceOrder
 21634        {
 21635            Id = id,
 21636            CustomerId = customerId,
 21637            VehicleId = vehicleId,
 21638            Status = ServiceOrderStatus.CreateReceived(),
 21639            CreatedAt = DateTime.UtcNow
 21640        };
 21641    }
 42
 43    public void StartDiagnosis()
 17244    {
 17245        Status = Status.TransitionTo(ServiceOrderStatus.Status.InDiagnosis);
 16646    }
 47
 48    public void SendBudget()
 8649    {
 8650        if (Budget is null)
 651            throw new DomainException("A budget must be generated before it can be sent.");
 52
 8053        Status = Status.TransitionTo(ServiceOrderStatus.Status.AwaitingApproval);
 8054    }
 55
 56    public void Approve()
 5457    {
 5458        if (Budget is null)
 659            throw new DomainException("Cannot approve an order without a budget.");
 60
 4861        Status = Status.TransitionTo(ServiceOrderStatus.Status.InExecution);
 4862        Budget.Approve();
 4863    }
 64
 65    public void Reject()
 2066    {
 2067        if (Budget is null)
 468            throw new DomainException("Cannot reject an order without a budget.");
 69
 1670        Status = Status.TransitionTo(ServiceOrderStatus.Status.Cancelled);
 1671        Budget.Reject();
 1672    }
 73
 74    public void StartExecution()
 1075    {
 1076        if (!Status.Is(ServiceOrderStatus.Status.InExecution))
 477            throw new DomainException(
 478                $"Order must be IN_EXECUTION to start work. Current status: {Status}.");
 679    }
 80
 81    public void Complete()
 2882    {
 2883        Status = Status.TransitionTo(ServiceOrderStatus.Status.Completed);
 2484        CompletedAt = DateTime.UtcNow;
 2485    }
 86
 87    public void Deliver()
 1688    {
 1689        Status = Status.TransitionTo(ServiceOrderStatus.Status.Delivered);
 1290        DeliveredAt = DateTime.UtcNow;
 1291    }
 92
 93    public ServiceItem AddServiceItem(
 94        Guid serviceId,
 95        string serviceName,
 96        Money unitPrice,
 97        int quantity)
 11298    {
 11299        EnsureInDiagnosis();
 100
 110101        var item = ServiceItem.Create(Id, serviceId, serviceName, unitPrice, quantity);
 100102        _serviceItems.Add(item);
 100103        return item;
 100104    }
 105
 106    public PartItem AddPartItem(
 107        Guid partId,
 108        string partName,
 109        Money unitPrice,
 110        int quantity,
 111        PartAvailability availability)
 34112    {
 34113        EnsureInDiagnosis();
 114
 32115        var item = PartItem.Create(Id, partId, partName, unitPrice, quantity, availability);
 22116        _partItems.Add(item);
 22117        return item;
 22118    }
 119
 120    public Budget GenerateBudget()
 104121    {
 104122        if (Budget is not null)
 4123            throw new DomainException("Budget already generated. Cannot overwrite an existing budget.");
 124
 100125        EnsureInDiagnosis();
 126
 98127        var serviceItems = _serviceItems;
 98128        if (serviceItems.Count == 0)
 4129            throw new DomainException(
 4130                "Cannot generate a budget: at least one service item is required.");
 131
 94132        var servicesTotal = serviceItems
 94133            .Aggregate(new Money(0), (acc, item) => acc.Add(item.Total));
 134
 94135        var partsTotal = _partItems
 94136            .Where(p => p.Availability == PartAvailability.Available)
 94137            .Aggregate(new Money(0), (acc, item) => acc.Add(item.Total));
 138
 94139        var total = servicesTotal.Add(partsTotal);
 140
 94141        Budget = Budget.Create(Id, total);
 94142        return Budget;
 94143    }
 144
 145    private void EnsureInDiagnosis()
 246146    {
 246147        if (!Status.Is(ServiceOrderStatus.Status.InDiagnosis))
 6148            throw new DomainException(
 6149                $"Items can only be added or a budget generated when the order is IN_DIAGNOSIS. " +
 6150                $"Current status: {Status}.");
 240151    }
 152}