| | | 1 | | using Microsoft.EntityFrameworkCore; |
| | | 2 | | using Microsoft.EntityFrameworkCore.Metadata.Builders; |
| | | 3 | | using MechanicsSoftware.Domain.Entities; |
| | | 4 | | using MechanicsSoftware.Domain.ValueObjects; |
| | | 5 | | using MechanicsSoftware.Domain.Enums; |
| | | 6 | | using MechanicsSoftware.Domain.Exceptions; |
| | | 7 | | |
| | | 8 | | namespace MechanicsSoftware.Infrastructure.Persistence.Configurations; |
| | | 9 | | |
| | | 10 | | public sealed class ServiceOrderConfiguration : IEntityTypeConfiguration<ServiceOrder> |
| | | 11 | | { |
| | | 12 | | public void Configure(EntityTypeBuilder<ServiceOrder> builder) |
| | 3 | 13 | | { |
| | 3 | 14 | | builder.ToTable("service_orders"); |
| | | 15 | | |
| | 3 | 16 | | builder.HasKey(so => so.Id); |
| | 3 | 17 | | builder.Property(so => so.Id).HasColumnName("id"); |
| | | 18 | | |
| | 3 | 19 | | builder.Property(so => so.CustomerId) |
| | 3 | 20 | | .HasColumnName("customer_id") |
| | 3 | 21 | | .IsRequired(); |
| | | 22 | | |
| | 3 | 23 | | builder.Property(so => so.VehicleId) |
| | 3 | 24 | | .HasColumnName("vehicle_id") |
| | 3 | 25 | | .IsRequired(); |
| | | 26 | | |
| | 3 | 27 | | builder.Property(so => so.Status) |
| | 3 | 28 | | .HasConversion( |
| | 3 | 29 | | v => v.ToString(), |
| | 3 | 30 | | v => ParseOrderStatus(v)) |
| | 3 | 31 | | .HasColumnName("status") |
| | 3 | 32 | | .HasMaxLength(30) |
| | 3 | 33 | | .IsRequired(); |
| | | 34 | | |
| | 3 | 35 | | builder.Property(so => so.CreatedAt) |
| | 3 | 36 | | .HasColumnName("created_at") |
| | 3 | 37 | | .IsRequired(); |
| | | 38 | | |
| | 3 | 39 | | builder.Property(so => so.CompletedAt) |
| | 3 | 40 | | .HasColumnName("completed_at"); |
| | | 41 | | |
| | 3 | 42 | | builder.Property(so => so.DeliveredAt) |
| | 3 | 43 | | .HasColumnName("delivered_at"); |
| | 1 | 44 | | |
| | 3 | 45 | | builder.Navigation(so => so.ServiceItems) |
| | 3 | 46 | | .HasField("_serviceItems") |
| | 3 | 47 | | .UsePropertyAccessMode(PropertyAccessMode.Field); |
| | 1 | 48 | | |
| | 3 | 49 | | builder.Navigation(so => so.PartItems) |
| | 3 | 50 | | .HasField("_partItems") |
| | 3 | 51 | | .UsePropertyAccessMode(PropertyAccessMode.Field); |
| | 1 | 52 | | |
| | 3 | 53 | | builder.OwnsMany(so => so.ServiceItems, si => |
| | 3 | 54 | | { |
| | 3 | 55 | | si.ToTable("service_items"); |
| | 3 | 56 | | si.HasKey(s => s.Id); |
| | 3 | 57 | | si.Property(s => s.Id).HasColumnName("id"); |
| | 3 | 58 | | si.WithOwner().HasForeignKey(s => s.ServiceOrderId); |
| | 3 | 59 | | si.Property(s => s.ServiceOrderId).HasColumnName("service_order_id"); |
| | 3 | 60 | | si.HasIndex(s => s.ServiceOrderId).HasDatabaseName("ix_service_items_service_order_id"); |
| | 3 | 61 | | si.Property(s => s.ServiceId).HasColumnName("service_id").IsRequired(); |
| | 2 | 62 | | si.Property(s => s.ServiceName) |
| | 3 | 63 | | .HasColumnName("service_name") |
| | 3 | 64 | | .HasMaxLength(100) |
| | 3 | 65 | | .IsRequired(); |
| | 3 | 66 | | si.Property(s => s.UnitPrice) |
| | 3 | 67 | | .HasConversion(m => m.Cents, m => new Money(m)) |
| | 3 | 68 | | .HasColumnName("unit_price") |
| | 3 | 69 | | .IsRequired(); |
| | 3 | 70 | | si.Property(s => s.Quantity).HasColumnName("quantity").IsRequired(); |
| | 3 | 71 | | si.Ignore(s => s.Total); |
| | 3 | 72 | | }); |
| | 1 | 73 | | |
| | 3 | 74 | | builder.OwnsMany(so => so.PartItems, pi => |
| | 3 | 75 | | { |
| | 3 | 76 | | pi.ToTable("part_items"); |
| | 3 | 77 | | pi.HasKey(p => p.Id); |
| | 3 | 78 | | pi.Property(p => p.Id).HasColumnName("id"); |
| | 3 | 79 | | pi.WithOwner().HasForeignKey(p => p.ServiceOrderId); |
| | 3 | 80 | | pi.Property(p => p.ServiceOrderId).HasColumnName("service_order_id"); |
| | 3 | 81 | | pi.HasIndex(p => p.ServiceOrderId).HasDatabaseName("ix_part_items_service_order_id"); |
| | 3 | 82 | | pi.Property(p => p.PartId).HasColumnName("part_id").IsRequired(); |
| | 3 | 83 | | pi.Property(p => p.PartName) |
| | 3 | 84 | | .HasColumnName("part_name") |
| | 3 | 85 | | .HasMaxLength(100) |
| | 3 | 86 | | .IsRequired(); |
| | 2 | 87 | | pi.Property(p => p.UnitPrice) |
| | 3 | 88 | | .HasConversion(m => m.Cents, m => new Money(m)) |
| | 3 | 89 | | .HasColumnName("unit_price") |
| | 3 | 90 | | .IsRequired(); |
| | 3 | 91 | | pi.Property(p => p.Quantity).HasColumnName("quantity").IsRequired(); |
| | 3 | 92 | | pi.Property(p => p.Availability) |
| | 3 | 93 | | .HasConversion<string>() |
| | 3 | 94 | | .HasColumnName("availability") |
| | 3 | 95 | | .IsRequired(); |
| | 3 | 96 | | pi.Ignore(p => p.Total); |
| | 3 | 97 | | }); |
| | 1 | 98 | | |
| | 3 | 99 | | builder.OwnsOne(so => so.Budget, budget => |
| | 3 | 100 | | { |
| | 3 | 101 | | budget.ToTable("budgets"); |
| | 3 | 102 | | budget.HasKey(b => b.Id); |
| | 3 | 103 | | budget.Property(b => b.Id).HasColumnName("id"); |
| | 3 | 104 | | budget.WithOwner().HasForeignKey(b => b.ServiceOrderId); |
| | 3 | 105 | | budget.Property(b => b.ServiceOrderId).HasColumnName("service_order_id"); |
| | 3 | 106 | | budget.HasIndex(b => b.ServiceOrderId).IsUnique().HasDatabaseName("ix_budgets_service_order_id"); |
| | 3 | 107 | | budget.Property(b => b.Total) |
| | 3 | 108 | | .HasConversion(m => m.Cents, m => new Money(m)) |
| | 2 | 109 | | .HasColumnName("total") |
| | 3 | 110 | | .IsRequired(); |
| | 3 | 111 | | budget.Property(b => b.Status) |
| | 2 | 112 | | .HasConversion( |
| | 3 | 113 | | v => v.ToString(), |
| | 3 | 114 | | v => ParseBudgetStatus(v)) |
| | 3 | 115 | | .HasColumnName("status") |
| | 3 | 116 | | .HasMaxLength(20) |
| | 2 | 117 | | .IsRequired(); |
| | 3 | 118 | | budget.Property(b => b.CreatedAt).HasColumnName("created_at").IsRequired(); |
| | 3 | 119 | | }); |
| | 1 | 120 | | |
| | 3 | 121 | | builder.HasIndex(so => so.CustomerId).HasDatabaseName("ix_service_orders_customer_id"); |
| | 3 | 122 | | builder.HasIndex(so => so.VehicleId).HasDatabaseName("ix_service_orders_vehicle_id"); |
| | | 123 | | |
| | 2 | 124 | | builder.HasOne<Customer>() |
| | 13 | 125 | | .WithMany() |
| | 13 | 126 | | .HasForeignKey(so => so.CustomerId) |
| | 3 | 127 | | .OnDelete(DeleteBehavior.Restrict); |
| | 2 | 128 | | |
| | 3 | 129 | | builder.HasOne<Vehicle>() |
| | 4 | 130 | | .WithMany() |
| | 3 | 131 | | .HasForeignKey(so => so.VehicleId) |
| | 3 | 132 | | .OnDelete(DeleteBehavior.Restrict); |
| | 4 | 133 | | } |
| | 1 | 134 | | |
| | 13 | 135 | | private static readonly Dictionary<string, ServiceOrderStatus.Status> _orderStatusMap = new() |
| | 2 | 136 | | { |
| | 2 | 137 | | ["RECEIVED"] = ServiceOrderStatus.Status.Received, |
| | 11 | 138 | | ["IN_DIAGNOSIS"] = ServiceOrderStatus.Status.InDiagnosis, |
| | 11 | 139 | | ["AWAITING_APPROVAL"] = ServiceOrderStatus.Status.AwaitingApproval, |
| | 4 | 140 | | ["IN_EXECUTION"] = ServiceOrderStatus.Status.InExecution, |
| | 6 | 141 | | ["COMPLETED"] = ServiceOrderStatus.Status.Completed, |
| | 4 | 142 | | ["DELIVERED"] = ServiceOrderStatus.Status.Delivered, |
| | 3 | 143 | | ["CANCELLED"] = ServiceOrderStatus.Status.Cancelled, |
| | 11 | 144 | | }; |
| | | 145 | | |
| | 2 | 146 | | private static readonly Dictionary<string, BudgetStatus.Status> _budgetStatusMap = new() |
| | 2 | 147 | | { |
| | 2 | 148 | | ["PENDING"] = BudgetStatus.Status.Pending, |
| | 2 | 149 | | ["APPROVED"] = BudgetStatus.Status.Approved, |
| | 2 | 150 | | ["REJECTED"] = BudgetStatus.Status.Rejected, |
| | 2 | 151 | | }; |
| | | 152 | | |
| | | 153 | | internal static ServiceOrderStatus ParseOrderStatus(string value) => |
| | 116 | 154 | | _orderStatusMap.TryGetValue(value, out var status) |
| | 116 | 155 | | ? new ServiceOrderStatus(status) |
| | 116 | 156 | | : throw new InvalidOperationException($"Unknown service order status: '{value}'"); |
| | | 157 | | |
| | | 158 | | internal static BudgetStatus ParseBudgetStatus(string value) => |
| | 56 | 159 | | _budgetStatusMap.TryGetValue(value, out var status) |
| | 56 | 160 | | ? new BudgetStatus(status) |
| | 56 | 161 | | : throw new InvalidOperationException($"Unknown budget status: '{value}'"); |
| | | 162 | | } |