Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 76ca9f874d |
@@ -60,6 +60,9 @@ public class AppDBContext(DbContextOptions<AppDBContext> options) : DbContext(op
|
|||||||
public DbSet<RegionalBudget> RegionalBudgets { get; set; }
|
public DbSet<RegionalBudget> RegionalBudgets { get; set; }
|
||||||
public DbSet<RegionAgencyMap> RegionAgencyMaps { get; set; }
|
public DbSet<RegionAgencyMap> RegionAgencyMaps { get; set; }
|
||||||
public DbSet<RegionalTaxRule> RegionalTaxRules { get; set; }
|
public DbSet<RegionalTaxRule> RegionalTaxRules { get; set; }
|
||||||
|
public DbSet<RideItemCount> RideItemCounts { get; set; }
|
||||||
|
public DbSet<RideVariance> RideVariances { get; set; }
|
||||||
|
public DbSet<RideCloseSummary> RideCloseSummaries { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@@ -399,6 +402,23 @@ public class AppDBContext(DbContextOptions<AppDBContext> options) : DbContext(op
|
|||||||
.HasIndex(r => r.FK_RegionId)
|
.HasIndex(r => r.FK_RegionId)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
|
modelBuilder.Entity<RideVariance>()
|
||||||
|
.HasOne(v => v.ResolvedBy)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(v => v.FK_ResolvedByUserId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired(false);
|
||||||
|
|
||||||
|
modelBuilder.Entity<RideCloseSummary>()
|
||||||
|
.HasIndex(s => s.FK_RideId)
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
modelBuilder.Entity<RideCloseSummary>()
|
||||||
|
.HasOne(s => s.ClosedByUser)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(s => s.FK_ClosedByUserId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
Seed.RoleAndPermissionSeed.Apply(modelBuilder);
|
Seed.RoleAndPermissionSeed.Apply(modelBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Distribution.Features.DriverComplianceConfig;
|
|
||||||
|
|
||||||
public record DriverComplianceConfigResponse(
|
|
||||||
Guid Id,
|
|
||||||
bool EnforceLicenseExpiry,
|
|
||||||
bool EnforceMedicalClearance,
|
|
||||||
DateTime UpdatedAt,
|
|
||||||
Guid UpdatedBy);
|
|
||||||
|
|
||||||
public record UpdateDriverComplianceConfigRequest(
|
|
||||||
bool EnforceLicenseExpiry,
|
|
||||||
bool EnforceMedicalClearance);
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using Distribution.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Distribution.Features.DriverComplianceConfig;
|
|
||||||
|
|
||||||
public class DriverComplianceConfigRepository(AppDBContext context) : IDriverComplianceConfigRepository
|
|
||||||
{
|
|
||||||
public async Task<Models.DriverComplianceConfig?> GetAsync() =>
|
|
||||||
await context.DriverComplianceConfigs.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
public async Task AddAsync(Models.DriverComplianceConfig config) =>
|
|
||||||
await context.DriverComplianceConfigs.AddAsync(config);
|
|
||||||
|
|
||||||
public void Update(Models.DriverComplianceConfig config) =>
|
|
||||||
context.DriverComplianceConfigs.Update(config);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
using Distribution.Infra.UoW;
|
|
||||||
|
|
||||||
namespace Distribution.Features.DriverComplianceConfig;
|
|
||||||
|
|
||||||
public class DriverComplianceConfigService(IDriverComplianceConfigRepository repo, IUnitOfWork uow)
|
|
||||||
{
|
|
||||||
public async Task<DriverComplianceConfigResponse?> GetAsync()
|
|
||||||
{
|
|
||||||
var config = await repo.GetAsync();
|
|
||||||
return config is null
|
|
||||||
? null
|
|
||||||
: new DriverComplianceConfigResponse(
|
|
||||||
config.Id,
|
|
||||||
config.EnforceLicenseExpiry,
|
|
||||||
config.EnforceMedicalClearance,
|
|
||||||
config.UpdatedAt,
|
|
||||||
config.UpdatedBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<DriverComplianceConfigResponse> UpsertAsync(UpdateDriverComplianceConfigRequest request, Guid updatedBy, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
var config = await repo.GetAsync();
|
|
||||||
|
|
||||||
await uow.BeginAsync(ct);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (config is null)
|
|
||||||
{
|
|
||||||
config = new global::Distribution.Models.DriverComplianceConfig
|
|
||||||
{
|
|
||||||
EnforceLicenseExpiry = request.EnforceLicenseExpiry,
|
|
||||||
EnforceMedicalClearance = request.EnforceMedicalClearance,
|
|
||||||
UpdatedBy = updatedBy,
|
|
||||||
UpdatedAt = DateTime.Now
|
|
||||||
};
|
|
||||||
await repo.AddAsync(config);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
config.EnforceLicenseExpiry = request.EnforceLicenseExpiry;
|
|
||||||
config.EnforceMedicalClearance = request.EnforceMedicalClearance;
|
|
||||||
config.UpdatedBy = updatedBy;
|
|
||||||
config.UpdatedAt = DateTime.Now;
|
|
||||||
repo.Update(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
await uow.CommitAsync(ct);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
await uow.RollbackAsync(ct);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DriverComplianceConfigResponse(
|
|
||||||
config.Id,
|
|
||||||
config.EnforceLicenseExpiry,
|
|
||||||
config.EnforceMedicalClearance,
|
|
||||||
config.UpdatedAt,
|
|
||||||
config.UpdatedBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Distribution.Features.DriverComplianceConfig;
|
|
||||||
|
|
||||||
public interface IDriverComplianceConfigRepository
|
|
||||||
{
|
|
||||||
Task<Models.DriverComplianceConfig?> GetAsync();
|
|
||||||
Task AddAsync(Models.DriverComplianceConfig config);
|
|
||||||
void Update(Models.DriverComplianceConfig config);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using Distribution.Features.DriverComplianceConfig;
|
|
||||||
using Distribution.Infra.Authorization;
|
using Distribution.Infra.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@@ -8,7 +7,6 @@ namespace Distribution.Features.DriverProfile;
|
|||||||
[Route("api/driver-profiles")]
|
[Route("api/driver-profiles")]
|
||||||
public class DriverProfileController(
|
public class DriverProfileController(
|
||||||
DriverProfileService service,
|
DriverProfileService service,
|
||||||
DriverComplianceConfigService complianceService,
|
|
||||||
ICurrentUserService currentUser) : ControllerBase
|
ICurrentUserService currentUser) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -60,7 +58,7 @@ public class DriverProfileController(
|
|||||||
[HttpGet("compliance")]
|
[HttpGet("compliance")]
|
||||||
public async Task<IActionResult> GetCompliance()
|
public async Task<IActionResult> GetCompliance()
|
||||||
{
|
{
|
||||||
var result = await complianceService.GetAsync();
|
var result = await service.GetComplianceConfigAsync();
|
||||||
return result is null ? NoContent() : Ok(result);
|
return result is null ? NoContent() : Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ public class DriverProfileController(
|
|||||||
public async Task<IActionResult> UpsertCompliance(UpdateDriverComplianceConfigRequest request, CancellationToken ct)
|
public async Task<IActionResult> UpsertCompliance(UpdateDriverComplianceConfigRequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
||||||
var result = await complianceService.UpsertAsync(request, userId, ct);
|
var result = await service.UpsertComplianceConfigAsync(request, userId, ct);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,14 @@ public record UpdateDriverProfileRequest(
|
|||||||
string LicenseNumber,
|
string LicenseNumber,
|
||||||
DateTime LicenseExpiry,
|
DateTime LicenseExpiry,
|
||||||
DateTime MedicalClearanceExpiry);
|
DateTime MedicalClearanceExpiry);
|
||||||
|
|
||||||
|
public record DriverComplianceConfigResponse(
|
||||||
|
Guid Id,
|
||||||
|
bool EnforceLicenseExpiry,
|
||||||
|
bool EnforceMedicalClearance,
|
||||||
|
DateTime UpdatedAt,
|
||||||
|
Guid UpdatedBy);
|
||||||
|
|
||||||
|
public record UpdateDriverComplianceConfigRequest(
|
||||||
|
bool EnforceLicenseExpiry,
|
||||||
|
bool EnforceMedicalClearance);
|
||||||
|
|||||||
@@ -37,4 +37,13 @@ public class DriverProfileRepository(AppDBContext context) : IDriverProfileRepos
|
|||||||
profile.IsActive = false;
|
profile.IsActive = false;
|
||||||
context.DriverProfiles.Update(profile);
|
context.DriverProfiles.Update(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<global::Distribution.Models.DriverComplianceConfig?> GetComplianceConfigAsync() =>
|
||||||
|
await context.DriverComplianceConfigs.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
public async Task AddComplianceConfigAsync(global::Distribution.Models.DriverComplianceConfig config) =>
|
||||||
|
await context.DriverComplianceConfigs.AddAsync(config);
|
||||||
|
|
||||||
|
public void UpdateComplianceConfig(global::Distribution.Models.DriverComplianceConfig config) =>
|
||||||
|
context.DriverComplianceConfigs.Update(config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,56 @@ public class DriverProfileService(IDriverProfileRepository repo, IUnitOfWork uow
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<DriverComplianceConfigResponse?> GetComplianceConfigAsync()
|
||||||
|
{
|
||||||
|
var config = await repo.GetComplianceConfigAsync();
|
||||||
|
return config is null ? null
|
||||||
|
: new DriverComplianceConfigResponse(
|
||||||
|
config.Id, config.EnforceLicenseExpiry, config.EnforceMedicalClearance,
|
||||||
|
config.UpdatedAt, config.UpdatedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DriverComplianceConfigResponse> UpsertComplianceConfigAsync(
|
||||||
|
UpdateDriverComplianceConfigRequest request, Guid updatedBy, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var config = await repo.GetComplianceConfigAsync();
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (config is null)
|
||||||
|
{
|
||||||
|
config = new global::Distribution.Models.DriverComplianceConfig
|
||||||
|
{
|
||||||
|
EnforceLicenseExpiry = request.EnforceLicenseExpiry,
|
||||||
|
EnforceMedicalClearance = request.EnforceMedicalClearance,
|
||||||
|
UpdatedBy = updatedBy,
|
||||||
|
UpdatedAt = DateTime.Now
|
||||||
|
};
|
||||||
|
await repo.AddComplianceConfigAsync(config);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.EnforceLicenseExpiry = request.EnforceLicenseExpiry;
|
||||||
|
config.EnforceMedicalClearance = request.EnforceMedicalClearance;
|
||||||
|
config.UpdatedBy = updatedBy;
|
||||||
|
config.UpdatedAt = DateTime.Now;
|
||||||
|
repo.UpdateComplianceConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DriverComplianceConfigResponse(
|
||||||
|
config.Id, config.EnforceLicenseExpiry, config.EnforceMedicalClearance,
|
||||||
|
config.UpdatedAt, config.UpdatedBy);
|
||||||
|
}
|
||||||
|
|
||||||
private static DriverProfileResponse ToResponse(global::Distribution.Models.DriverProfile p) =>
|
private static DriverProfileResponse ToResponse(global::Distribution.Models.DriverProfile p) =>
|
||||||
new(p.Id, p.FK_UserId, p.User?.FullName ?? string.Empty,
|
new(p.Id, p.FK_UserId, p.User?.FullName ?? string.Empty,
|
||||||
p.LicenseNumber, p.LicenseExpiry, p.MedicalClearanceExpiry,
|
p.LicenseNumber, p.LicenseExpiry, p.MedicalClearanceExpiry,
|
||||||
|
|||||||
@@ -9,4 +9,8 @@ public interface IDriverProfileRepository
|
|||||||
Task AddAsync(global::Distribution.Models.DriverProfile profile);
|
Task AddAsync(global::Distribution.Models.DriverProfile profile);
|
||||||
void Update(global::Distribution.Models.DriverProfile profile);
|
void Update(global::Distribution.Models.DriverProfile profile);
|
||||||
void Deactivate(global::Distribution.Models.DriverProfile profile);
|
void Deactivate(global::Distribution.Models.DriverProfile profile);
|
||||||
|
|
||||||
|
Task<global::Distribution.Models.DriverComplianceConfig?> GetComplianceConfigAsync();
|
||||||
|
Task AddComplianceConfigAsync(global::Distribution.Models.DriverComplianceConfig config);
|
||||||
|
void UpdateComplianceConfig(global::Distribution.Models.DriverComplianceConfig config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Distribution.Models;
|
||||||
|
|
||||||
|
namespace Distribution.Features.Reconciliation;
|
||||||
|
|
||||||
|
public interface IReconciliationRepository
|
||||||
|
{
|
||||||
|
Task AddItemCountAsync(RideItemCount count);
|
||||||
|
Task<List<RideItemCount>> GetItemCountsByRideAsync(Guid rideId);
|
||||||
|
|
||||||
|
Task AddVarianceAsync(RideVariance variance);
|
||||||
|
Task<List<RideVariance>> GetVariancesByRideAsync(Guid rideId);
|
||||||
|
Task<RideVariance?> GetVarianceByIdAsync(Guid id);
|
||||||
|
void UpdateVariance(RideVariance variance);
|
||||||
|
Task<bool> HasPendingVariancesAsync(Guid rideId);
|
||||||
|
|
||||||
|
Task AddCloseSummaryAsync(RideCloseSummary summary);
|
||||||
|
Task<RideCloseSummary?> GetCloseSummaryByRideAsync(Guid rideId);
|
||||||
|
|
||||||
|
/// <summary>Sum of Cash payments on sales belonging to the given ride.</summary>
|
||||||
|
Task<decimal> GetCashCollectedByRideAsync(Guid rideId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Distribution.Infra.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Distribution.Features.Reconciliation;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/rides/{rideId:guid}")]
|
||||||
|
public class ReconciliationController(ReconciliationService service, ICurrentUserService currentUser) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpPost("cycle-count")]
|
||||||
|
public async Task<IActionResult> SubmitCycleCount(Guid rideId, SubmitCycleCountRequest request, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await service.SubmitCycleCountAsync(rideId, request, userId, ct);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("variances")]
|
||||||
|
public async Task<IActionResult> GetVariances(Guid rideId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await service.GetVariancesAsync(rideId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("variances/{id:guid}/approve")]
|
||||||
|
public async Task<IActionResult> ApproveVariance(Guid rideId, Guid id, ResolveVarianceRequest request, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await service.ApproveVarianceAsync(id, request, userId, ct);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("variances/{id:guid}/reject")]
|
||||||
|
public async Task<IActionResult> RejectVariance(Guid rideId, Guid id, ResolveVarianceRequest request, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await service.RejectVarianceAsync(id, request, userId, ct);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("close-ride")]
|
||||||
|
public async Task<IActionResult> CloseRide(Guid rideId, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await service.CloseRideAsync(rideId, userId, ct);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Distribution.Models;
|
||||||
|
|
||||||
|
namespace Distribution.Features.Reconciliation;
|
||||||
|
|
||||||
|
public record CycleCountItemLine(Guid ItemId, decimal PhysicalQuantity);
|
||||||
|
public record SubmitCycleCountRequest(List<CycleCountItemLine> Items, decimal CashOnHand);
|
||||||
|
|
||||||
|
public record RideVarianceResponse(
|
||||||
|
Guid Id, Guid RideId, Guid? ItemId, string? ItemName,
|
||||||
|
bool IsCash, decimal ExpectedQuantity, decimal ActualQuantity, decimal VarianceQuantity,
|
||||||
|
string? ReasonCode, VarianceStatus Status, Guid? ResolvedByUserId, DateTime? ResolvedAt);
|
||||||
|
|
||||||
|
public record ResolveVarianceRequest(string ReasonCode);
|
||||||
|
|
||||||
|
public record CycleCountResult(bool HasVariances, List<RideVarianceResponse> Variances);
|
||||||
|
|
||||||
|
public record RideCloseSummaryResponse(
|
||||||
|
Guid Id, Guid RideId, decimal ExpectedCash, decimal ActualCash, decimal CashVariance,
|
||||||
|
Guid ClosedByUserId, DateTime ClosedAt);
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using Distribution.Data;
|
||||||
|
using Distribution.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Distribution.Features.Reconciliation;
|
||||||
|
|
||||||
|
public class ReconciliationRepository(AppDBContext db) : IReconciliationRepository
|
||||||
|
{
|
||||||
|
public async Task AddItemCountAsync(RideItemCount count)
|
||||||
|
=> await db.RideItemCounts.AddAsync(count);
|
||||||
|
|
||||||
|
public async Task<List<RideItemCount>> GetItemCountsByRideAsync(Guid rideId)
|
||||||
|
=> await db.RideItemCounts
|
||||||
|
.Where(c => c.FK_RideId == rideId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
public async Task AddVarianceAsync(RideVariance variance)
|
||||||
|
=> await db.RideVariances.AddAsync(variance);
|
||||||
|
|
||||||
|
public async Task<List<RideVariance>> GetVariancesByRideAsync(Guid rideId)
|
||||||
|
=> await db.RideVariances
|
||||||
|
.Include(v => v.Item)
|
||||||
|
.Include(v => v.ResolvedBy)
|
||||||
|
.Where(v => v.FK_RideId == rideId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
public async Task<RideVariance?> GetVarianceByIdAsync(Guid id)
|
||||||
|
=> await db.RideVariances
|
||||||
|
.Include(v => v.Item)
|
||||||
|
.Include(v => v.ResolvedBy)
|
||||||
|
.FirstOrDefaultAsync(v => v.Id == id);
|
||||||
|
|
||||||
|
public void UpdateVariance(RideVariance variance)
|
||||||
|
=> db.RideVariances.Update(variance);
|
||||||
|
|
||||||
|
public async Task<bool> HasPendingVariancesAsync(Guid rideId)
|
||||||
|
=> await db.RideVariances
|
||||||
|
.AnyAsync(v => v.FK_RideId == rideId && v.Status == VarianceStatus.Pending);
|
||||||
|
|
||||||
|
public async Task AddCloseSummaryAsync(RideCloseSummary summary)
|
||||||
|
=> await db.RideCloseSummaries.AddAsync(summary);
|
||||||
|
|
||||||
|
public async Task<RideCloseSummary?> GetCloseSummaryByRideAsync(Guid rideId)
|
||||||
|
=> await db.RideCloseSummaries
|
||||||
|
.FirstOrDefaultAsync(s => s.FK_RideId == rideId);
|
||||||
|
|
||||||
|
public async Task<decimal> GetCashCollectedByRideAsync(Guid rideId)
|
||||||
|
=> await db.Payments
|
||||||
|
.Where(p => p.Sale.FK_RideId == rideId && p.Method == PaymentMethod.Cash)
|
||||||
|
.SumAsync(p => p.Amount);
|
||||||
|
}
|
||||||
@@ -0,0 +1,396 @@
|
|||||||
|
using Distribution.Features.Ride;
|
||||||
|
using Distribution.Features.Stock;
|
||||||
|
using Distribution.Infra.UoW;
|
||||||
|
using Distribution.Models;
|
||||||
|
|
||||||
|
namespace Distribution.Features.Reconciliation;
|
||||||
|
|
||||||
|
public class ReconciliationService(
|
||||||
|
IReconciliationRepository reconciliationRepo,
|
||||||
|
IRideRepository rideRepo,
|
||||||
|
IStockRepository stockRepo,
|
||||||
|
IUnitOfWork uow)
|
||||||
|
{
|
||||||
|
public async Task<CycleCountResult> SubmitCycleCountAsync(
|
||||||
|
Guid rideId, SubmitCycleCountRequest request, Guid userId, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var ride = await rideRepo.GetByIdAsync(rideId)
|
||||||
|
?? throw new KeyNotFoundException("Ride not found");
|
||||||
|
|
||||||
|
if (ride.Status != RideStatus.Active)
|
||||||
|
throw new InvalidOperationException($"Cannot submit cycle count for a ride in {ride.Status} status");
|
||||||
|
|
||||||
|
var existingCounts = await reconciliationRepo.GetItemCountsByRideAsync(rideId);
|
||||||
|
if (existingCounts.Count > 0)
|
||||||
|
throw new InvalidOperationException("Cycle count has already been submitted for this ride");
|
||||||
|
|
||||||
|
// Build submitted counts lookup
|
||||||
|
var submittedCounts = request.Items.ToDictionary(i => i.ItemId, i => i.PhysicalQuantity);
|
||||||
|
|
||||||
|
// Save all item count records
|
||||||
|
var itemCounts = request.Items.Select(line => new RideItemCount
|
||||||
|
{
|
||||||
|
FK_RideId = rideId,
|
||||||
|
FK_ItemId = line.ItemId,
|
||||||
|
PhysicalQuantity = line.PhysicalQuantity
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
// Get current vehicle stock balances
|
||||||
|
var vehicleBalances = await stockRepo.GetBalancesByLocationAsync(ride.FK_VehicleId, LocationType.Vehicle);
|
||||||
|
|
||||||
|
var variances = new List<RideVariance>();
|
||||||
|
|
||||||
|
// Compare each stock balance against the physical count
|
||||||
|
foreach (var balance in vehicleBalances)
|
||||||
|
{
|
||||||
|
var physicalQty = submittedCounts.TryGetValue(balance.FK_ItemId, out var qty) ? qty : 0m;
|
||||||
|
|
||||||
|
if (balance.Quantity != physicalQty)
|
||||||
|
{
|
||||||
|
variances.Add(new RideVariance
|
||||||
|
{
|
||||||
|
FK_RideId = rideId,
|
||||||
|
FK_ItemId = balance.FK_ItemId,
|
||||||
|
IsCash = false,
|
||||||
|
ExpectedQuantity = balance.Quantity,
|
||||||
|
ActualQuantity = physicalQty,
|
||||||
|
VarianceQuantity = balance.Quantity - physicalQty,
|
||||||
|
Status = VarianceStatus.Pending
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cash variance check
|
||||||
|
var expectedCash = await reconciliationRepo.GetCashCollectedByRideAsync(rideId);
|
||||||
|
if (expectedCash != request.CashOnHand)
|
||||||
|
{
|
||||||
|
variances.Add(new RideVariance
|
||||||
|
{
|
||||||
|
FK_RideId = rideId,
|
||||||
|
FK_ItemId = null,
|
||||||
|
IsCash = true,
|
||||||
|
ExpectedQuantity = expectedCash,
|
||||||
|
ActualQuantity = request.CashOnHand,
|
||||||
|
VarianceQuantity = expectedCash - request.CashOnHand,
|
||||||
|
Status = VarianceStatus.Pending
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ride.Status = RideStatus.PendingClose;
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var count in itemCounts)
|
||||||
|
await reconciliationRepo.AddItemCountAsync(count);
|
||||||
|
|
||||||
|
foreach (var variance in variances)
|
||||||
|
await reconciliationRepo.AddVarianceAsync(variance);
|
||||||
|
|
||||||
|
rideRepo.Update(ride);
|
||||||
|
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
var varianceResponses = variances.Select(ToVarianceResponse).ToList();
|
||||||
|
return new CycleCountResult(varianceResponses.Count > 0, varianceResponses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<RideVarianceResponse>> GetVariancesAsync(Guid rideId)
|
||||||
|
{
|
||||||
|
var variances = await reconciliationRepo.GetVariancesByRideAsync(rideId);
|
||||||
|
return variances.Select(ToVarianceResponse).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RideVarianceResponse> ApproveVarianceAsync(
|
||||||
|
Guid varianceId, ResolveVarianceRequest request, Guid userId, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var variance = await reconciliationRepo.GetVarianceByIdAsync(varianceId)
|
||||||
|
?? throw new KeyNotFoundException("Variance not found");
|
||||||
|
|
||||||
|
if (variance.Status != VarianceStatus.Pending)
|
||||||
|
throw new InvalidOperationException($"Variance is not in Pending status");
|
||||||
|
|
||||||
|
var ride = await rideRepo.GetByIdAsync(variance.FK_RideId)
|
||||||
|
?? throw new KeyNotFoundException("Ride not found");
|
||||||
|
|
||||||
|
if (ride.Status != RideStatus.PendingClose)
|
||||||
|
throw new InvalidOperationException($"Ride is not in PendingClose status");
|
||||||
|
|
||||||
|
StockMovement? movement = null;
|
||||||
|
StockBalance? balance = null;
|
||||||
|
|
||||||
|
if (!variance.IsCash)
|
||||||
|
{
|
||||||
|
// adjustmentQty = -variance.VarianceQuantity
|
||||||
|
// positive variance = short → debit vehicle (reduce stock)
|
||||||
|
// negative variance = surplus → credit vehicle (add stock)
|
||||||
|
var adjustmentQty = -variance.VarianceQuantity;
|
||||||
|
|
||||||
|
balance = await stockRepo.GetBalanceAsync(variance.FK_ItemId!.Value, ride.FK_VehicleId);
|
||||||
|
|
||||||
|
if (adjustmentQty < 0)
|
||||||
|
{
|
||||||
|
// Debit vehicle: FromLocationId=vehicleId, Quantity=abs(adjustmentQty)
|
||||||
|
var absQty = Math.Abs(adjustmentQty);
|
||||||
|
movement = new StockMovement
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
FromLocationId = ride.FK_VehicleId,
|
||||||
|
FromLocationType = LocationType.Vehicle,
|
||||||
|
ToLocationId = null,
|
||||||
|
ToLocationType = null,
|
||||||
|
Quantity = absQty,
|
||||||
|
Type = MovementType.Adjustment,
|
||||||
|
ReasonCode = request.ReasonCode,
|
||||||
|
ReferenceId = variance.Id,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
CreatedBy = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
{
|
||||||
|
balance.Quantity -= absQty;
|
||||||
|
balance.UpdatedAt = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (adjustmentQty > 0)
|
||||||
|
{
|
||||||
|
// Credit vehicle: ToLocationId=vehicleId, Quantity=adjustmentQty
|
||||||
|
movement = new StockMovement
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
FromLocationId = null,
|
||||||
|
FromLocationType = null,
|
||||||
|
ToLocationId = ride.FK_VehicleId,
|
||||||
|
ToLocationType = LocationType.Vehicle,
|
||||||
|
Quantity = adjustmentQty,
|
||||||
|
Type = MovementType.Adjustment,
|
||||||
|
ReasonCode = request.ReasonCode,
|
||||||
|
ReferenceId = variance.Id,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
CreatedBy = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
{
|
||||||
|
balance.Quantity += adjustmentQty;
|
||||||
|
balance.UpdatedAt = DateTime.Now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
balance = new StockBalance
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
LocationId = ride.FK_VehicleId,
|
||||||
|
LocationType = LocationType.Vehicle,
|
||||||
|
Quantity = adjustmentQty,
|
||||||
|
UpdatedAt = DateTime.Now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variance.Status = VarianceStatus.Approved;
|
||||||
|
variance.ReasonCode = request.ReasonCode;
|
||||||
|
variance.FK_ResolvedByUserId = userId;
|
||||||
|
variance.ResolvedAt = DateTime.Now;
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reconciliationRepo.UpdateVariance(variance);
|
||||||
|
|
||||||
|
if (movement is not null)
|
||||||
|
await stockRepo.AddMovementAsync(movement);
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
stockRepo.UpdateBalance(balance);
|
||||||
|
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToVarianceResponse(variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RideVarianceResponse> RejectVarianceAsync(
|
||||||
|
Guid varianceId, ResolveVarianceRequest request, Guid userId, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var variance = await reconciliationRepo.GetVarianceByIdAsync(varianceId)
|
||||||
|
?? throw new KeyNotFoundException("Variance not found");
|
||||||
|
|
||||||
|
if (variance.Status != VarianceStatus.Pending)
|
||||||
|
throw new InvalidOperationException($"Variance is not in Pending status");
|
||||||
|
|
||||||
|
var ride = await rideRepo.GetByIdAsync(variance.FK_RideId)
|
||||||
|
?? throw new KeyNotFoundException("Ride not found");
|
||||||
|
|
||||||
|
if (ride.Status != RideStatus.PendingClose)
|
||||||
|
throw new InvalidOperationException($"Ride is not in PendingClose status");
|
||||||
|
|
||||||
|
StockMovement? movement = null;
|
||||||
|
StockBalance? balance = null;
|
||||||
|
|
||||||
|
if (!variance.IsCash)
|
||||||
|
{
|
||||||
|
var adjustmentQty = -variance.VarianceQuantity;
|
||||||
|
|
||||||
|
balance = await stockRepo.GetBalanceAsync(variance.FK_ItemId!.Value, ride.FK_VehicleId);
|
||||||
|
|
||||||
|
if (adjustmentQty < 0)
|
||||||
|
{
|
||||||
|
var absQty = Math.Abs(adjustmentQty);
|
||||||
|
movement = new StockMovement
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
FromLocationId = ride.FK_VehicleId,
|
||||||
|
FromLocationType = LocationType.Vehicle,
|
||||||
|
ToLocationId = null,
|
||||||
|
ToLocationType = null,
|
||||||
|
Quantity = absQty,
|
||||||
|
Type = MovementType.Adjustment,
|
||||||
|
ReasonCode = request.ReasonCode,
|
||||||
|
ReferenceId = variance.Id,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
CreatedBy = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
{
|
||||||
|
balance.Quantity -= absQty;
|
||||||
|
balance.UpdatedAt = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (adjustmentQty > 0)
|
||||||
|
{
|
||||||
|
movement = new StockMovement
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
FromLocationId = null,
|
||||||
|
FromLocationType = null,
|
||||||
|
ToLocationId = ride.FK_VehicleId,
|
||||||
|
ToLocationType = LocationType.Vehicle,
|
||||||
|
Quantity = adjustmentQty,
|
||||||
|
Type = MovementType.Adjustment,
|
||||||
|
ReasonCode = request.ReasonCode,
|
||||||
|
ReferenceId = variance.Id,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
CreatedBy = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
{
|
||||||
|
balance.Quantity += adjustmentQty;
|
||||||
|
balance.UpdatedAt = DateTime.Now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
balance = new StockBalance
|
||||||
|
{
|
||||||
|
FK_ItemId = variance.FK_ItemId!.Value,
|
||||||
|
LocationId = ride.FK_VehicleId,
|
||||||
|
LocationType = LocationType.Vehicle,
|
||||||
|
Quantity = adjustmentQty,
|
||||||
|
UpdatedAt = DateTime.Now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variance.Status = VarianceStatus.Rejected;
|
||||||
|
variance.ReasonCode = request.ReasonCode;
|
||||||
|
variance.FK_ResolvedByUserId = userId;
|
||||||
|
variance.ResolvedAt = DateTime.Now;
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reconciliationRepo.UpdateVariance(variance);
|
||||||
|
|
||||||
|
if (movement is not null)
|
||||||
|
await stockRepo.AddMovementAsync(movement);
|
||||||
|
|
||||||
|
if (balance is not null)
|
||||||
|
stockRepo.UpdateBalance(balance);
|
||||||
|
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToVarianceResponse(variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RideCloseSummaryResponse> CloseRideAsync(
|
||||||
|
Guid rideId, Guid userId, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var ride = await rideRepo.GetByIdAsync(rideId)
|
||||||
|
?? throw new KeyNotFoundException("Ride not found");
|
||||||
|
|
||||||
|
if (ride.Status != RideStatus.PendingClose)
|
||||||
|
throw new InvalidOperationException($"Cannot close a ride in {ride.Status} status");
|
||||||
|
|
||||||
|
if (await reconciliationRepo.HasPendingVariancesAsync(rideId))
|
||||||
|
throw new InvalidOperationException("All variances must be resolved before the ride can be closed");
|
||||||
|
|
||||||
|
var allVariances = await reconciliationRepo.GetVariancesByRideAsync(rideId);
|
||||||
|
var cashVariance = allVariances.FirstOrDefault(v => v.IsCash);
|
||||||
|
|
||||||
|
var expectedCash = cashVariance?.ExpectedQuantity ?? 0m;
|
||||||
|
var actualCash = cashVariance?.ActualQuantity ?? 0m;
|
||||||
|
var cashVarianceAmount = cashVariance?.VarianceQuantity ?? 0m;
|
||||||
|
|
||||||
|
var summary = new RideCloseSummary
|
||||||
|
{
|
||||||
|
FK_RideId = rideId,
|
||||||
|
ExpectedCash = expectedCash,
|
||||||
|
ActualCash = actualCash,
|
||||||
|
CashVariance = cashVarianceAmount,
|
||||||
|
FK_ClosedByUserId = userId,
|
||||||
|
ClosedAt = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
ride.Status = RideStatus.Closed;
|
||||||
|
ride.ClosedAt = DateTime.Now;
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await reconciliationRepo.AddCloseSummaryAsync(summary);
|
||||||
|
rideRepo.Update(ride);
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RideCloseSummaryResponse(
|
||||||
|
summary.Id,
|
||||||
|
summary.FK_RideId,
|
||||||
|
summary.ExpectedCash,
|
||||||
|
summary.ActualCash,
|
||||||
|
summary.CashVariance,
|
||||||
|
summary.FK_ClosedByUserId,
|
||||||
|
summary.ClosedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RideVarianceResponse ToVarianceResponse(RideVariance v) =>
|
||||||
|
new(v.Id, v.FK_RideId, v.FK_ItemId, v.Item?.Name,
|
||||||
|
v.IsCash, v.ExpectedQuantity, v.ActualQuantity, v.VarianceQuantity,
|
||||||
|
v.ReasonCode, v.Status, v.FK_ResolvedByUserId, v.ResolvedAt);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Distribution.Features.Stock;
|
using Distribution.Features.Stock;
|
||||||
using Distribution.Features.DriverComplianceConfig;
|
|
||||||
using Distribution.Features.DriverProfile;
|
using Distribution.Features.DriverProfile;
|
||||||
using Distribution.Infra.UoW;
|
using Distribution.Infra.UoW;
|
||||||
using Distribution.Models;
|
using Distribution.Models;
|
||||||
@@ -9,7 +8,6 @@ namespace Distribution.Features.Ride;
|
|||||||
public class RideService(
|
public class RideService(
|
||||||
IRideRepository rideRepo,
|
IRideRepository rideRepo,
|
||||||
IDriverProfileRepository driverRepo,
|
IDriverProfileRepository driverRepo,
|
||||||
IDriverComplianceConfigRepository complianceRepo,
|
|
||||||
IRideCancelRequestRepository cancelRepo,
|
IRideCancelRequestRepository cancelRepo,
|
||||||
Features.Vehicle.IVehicleRepository vehicleRepo,
|
Features.Vehicle.IVehicleRepository vehicleRepo,
|
||||||
IStockRepository stockRepo,
|
IStockRepository stockRepo,
|
||||||
@@ -138,11 +136,9 @@ public class RideService(
|
|||||||
var ride = await rideRepo.GetByIdAsync(rideId)
|
var ride = await rideRepo.GetByIdAsync(rideId)
|
||||||
?? throw new KeyNotFoundException("Ride not found");
|
?? throw new KeyNotFoundException("Ride not found");
|
||||||
|
|
||||||
if (ride.Status != RideStatus.Active)
|
if (ride.Status != RideStatus.PendingClose)
|
||||||
throw new InvalidOperationException($"Cannot close a ride in {ride.Status} status");
|
throw new InvalidOperationException($"Cannot close a ride in {ride.Status} status");
|
||||||
|
|
||||||
await EnsureVehicleStockClearedAsync(ride.FK_VehicleId);
|
|
||||||
|
|
||||||
ride.Status = RideStatus.Closed;
|
ride.Status = RideStatus.Closed;
|
||||||
ride.ClosedAt = DateTime.Now;
|
ride.ClosedAt = DateTime.Now;
|
||||||
ride.Meterend = request.Meterend;
|
ride.Meterend = request.Meterend;
|
||||||
@@ -319,7 +315,7 @@ public class RideService(
|
|||||||
if (!driver.IsActive)
|
if (!driver.IsActive)
|
||||||
throw new InvalidOperationException("Driver profile is inactive");
|
throw new InvalidOperationException("Driver profile is inactive");
|
||||||
|
|
||||||
var config = await complianceRepo.GetAsync();
|
var config = await driverRepo.GetComplianceConfigAsync();
|
||||||
if (config is null) return;
|
if (config is null) return;
|
||||||
|
|
||||||
var today = DateTime.Now.Date;
|
var today = DateTime.Now.Date;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Distribution.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddRideReconciliation : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RideCloseSummaries",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FK_RideId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
ExpectedCash = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
ActualCash = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
CashVariance = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
FK_ClosedByUserId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
ClosedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RideCloseSummaries", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideCloseSummaries_DeliveryRuns_FK_RideId",
|
||||||
|
column: x => x.FK_RideId,
|
||||||
|
principalTable: "DeliveryRuns",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideCloseSummaries_Users_FK_ClosedByUserId",
|
||||||
|
column: x => x.FK_ClosedByUserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RideItemCounts",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FK_RideId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FK_ItemId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
PhysicalQuantity = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RideItemCounts", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideItemCounts_DeliveryRuns_FK_RideId",
|
||||||
|
column: x => x.FK_RideId,
|
||||||
|
principalTable: "DeliveryRuns",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideItemCounts_Items_FK_ItemId",
|
||||||
|
column: x => x.FK_ItemId,
|
||||||
|
principalTable: "Items",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RideVariances",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FK_RideId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FK_ItemId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
IsCash = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||||
|
ExpectedQuantity = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
ActualQuantity = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
VarianceQuantity = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||||
|
ReasonCode = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
Status = table.Column<int>(type: "int", nullable: false),
|
||||||
|
FK_ResolvedByUserId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
ResolvedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RideVariances", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideVariances_DeliveryRuns_FK_RideId",
|
||||||
|
column: x => x.FK_RideId,
|
||||||
|
principalTable: "DeliveryRuns",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideVariances_Items_FK_ItemId",
|
||||||
|
column: x => x.FK_ItemId,
|
||||||
|
principalTable: "Items",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RideVariances_Users_FK_ResolvedByUserId",
|
||||||
|
column: x => x.FK_ResolvedByUserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideCloseSummaries_FK_ClosedByUserId",
|
||||||
|
table: "RideCloseSummaries",
|
||||||
|
column: "FK_ClosedByUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideCloseSummaries_FK_RideId",
|
||||||
|
table: "RideCloseSummaries",
|
||||||
|
column: "FK_RideId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideItemCounts_FK_ItemId",
|
||||||
|
table: "RideItemCounts",
|
||||||
|
column: "FK_ItemId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideItemCounts_FK_RideId",
|
||||||
|
table: "RideItemCounts",
|
||||||
|
column: "FK_RideId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideVariances_FK_ItemId",
|
||||||
|
table: "RideVariances",
|
||||||
|
column: "FK_ItemId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideVariances_FK_ResolvedByUserId",
|
||||||
|
table: "RideVariances",
|
||||||
|
column: "FK_ResolvedByUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RideVariances_FK_RideId",
|
||||||
|
table: "RideVariances",
|
||||||
|
column: "FK_RideId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RideCloseSummaries");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RideItemCounts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RideVariances");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1004,6 +1004,129 @@ namespace Distribution.Migrations
|
|||||||
b.ToTable("RideCancelRequests");
|
b.ToTable("RideCancelRequests");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideCloseSummary", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ActualCash")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<decimal>("CashVariance")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ClosedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ExpectedCash")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<Guid>("FK_ClosedByUserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("FK_RideId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("FK_ClosedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("FK_RideId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("RideCloseSummaries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideItemCount", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("FK_ItemId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("FK_RideId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<decimal>("PhysicalQuantity")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("FK_ItemId");
|
||||||
|
|
||||||
|
b.HasIndex("FK_RideId");
|
||||||
|
|
||||||
|
b.ToTable("RideItemCounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideVariance", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ActualQuantity")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ExpectedQuantity")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("FK_ItemId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("FK_ResolvedByUserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("FK_RideId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsCash")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("ReasonCode")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ResolvedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("VarianceQuantity")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("FK_ItemId");
|
||||||
|
|
||||||
|
b.HasIndex("FK_ResolvedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("FK_RideId");
|
||||||
|
|
||||||
|
b.ToTable("RideVariances");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Distribution.Models.Role", b =>
|
modelBuilder.Entity("Distribution.Models.Role", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -2363,6 +2486,68 @@ namespace Distribution.Migrations
|
|||||||
b.Navigation("Ride");
|
b.Navigation("Ride");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideCloseSummary", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Distribution.Models.User", "ClosedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_ClosedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Distribution.Models.DeliveryRun", "Ride")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_RideId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ClosedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Ride");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideItemCount", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Distribution.Models.Item", "Item")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_ItemId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Distribution.Models.DeliveryRun", "Ride")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_RideId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Item");
|
||||||
|
|
||||||
|
b.Navigation("Ride");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Distribution.Models.RideVariance", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Distribution.Models.Item", "Item")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_ItemId");
|
||||||
|
|
||||||
|
b.HasOne("Distribution.Models.User", "ResolvedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_ResolvedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("Distribution.Models.DeliveryRun", "Ride")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("FK_RideId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Item");
|
||||||
|
|
||||||
|
b.Navigation("ResolvedBy");
|
||||||
|
|
||||||
|
b.Navigation("Ride");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Distribution.Models.RolePermission", b =>
|
modelBuilder.Entity("Distribution.Models.RolePermission", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Distribution.Models.Permission", "Permission")
|
b.HasOne("Distribution.Models.Permission", "Permission")
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ namespace Distribution.Models
|
|||||||
|
|
||||||
public enum RideType { Delivery, Sale }
|
public enum RideType { Delivery, Sale }
|
||||||
|
|
||||||
public enum RideStatus { Draft, Open, Active, CancelRequested, Cancelled, Closed }
|
public enum RideStatus { Draft, Open, Active, PendingClose, CancelRequested, Cancelled, Closed }
|
||||||
|
|
||||||
|
public enum VarianceStatus { Pending, Approved, Rejected }
|
||||||
|
|
||||||
public enum CancelRequestType { VehicleBreakdown, DriverUnavailable, RouteIssue, Other }
|
public enum CancelRequestType { VehicleBreakdown, DriverUnavailable, RouteIssue, Other }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Distribution.Models
|
||||||
|
{
|
||||||
|
public class RideCloseSummary : Template
|
||||||
|
{
|
||||||
|
public Guid FK_RideId { get; set; }
|
||||||
|
[ForeignKey("FK_RideId")]
|
||||||
|
public DeliveryRun Ride { get; set; }
|
||||||
|
|
||||||
|
public decimal ExpectedCash { get; set; }
|
||||||
|
public decimal ActualCash { get; set; }
|
||||||
|
public decimal CashVariance { get; set; }
|
||||||
|
|
||||||
|
public Guid FK_ClosedByUserId { get; set; }
|
||||||
|
[ForeignKey("FK_ClosedByUserId")]
|
||||||
|
public User ClosedByUser { get; set; }
|
||||||
|
|
||||||
|
public DateTime ClosedAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Distribution.Models
|
||||||
|
{
|
||||||
|
public class RideItemCount : Template
|
||||||
|
{
|
||||||
|
public Guid FK_RideId { get; set; }
|
||||||
|
[ForeignKey("FK_RideId")]
|
||||||
|
public DeliveryRun Ride { get; set; }
|
||||||
|
|
||||||
|
public Guid FK_ItemId { get; set; }
|
||||||
|
[ForeignKey("FK_ItemId")]
|
||||||
|
public Item Item { get; set; }
|
||||||
|
|
||||||
|
public decimal PhysicalQuantity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Distribution.Models
|
||||||
|
{
|
||||||
|
public class RideVariance : Template
|
||||||
|
{
|
||||||
|
public Guid FK_RideId { get; set; }
|
||||||
|
[ForeignKey("FK_RideId")]
|
||||||
|
public DeliveryRun Ride { get; set; }
|
||||||
|
|
||||||
|
public Guid? FK_ItemId { get; set; }
|
||||||
|
[ForeignKey("FK_ItemId")]
|
||||||
|
public Item? Item { get; set; }
|
||||||
|
|
||||||
|
public bool IsCash { get; set; }
|
||||||
|
|
||||||
|
public decimal ExpectedQuantity { get; set; }
|
||||||
|
public decimal ActualQuantity { get; set; }
|
||||||
|
public decimal VarianceQuantity { get; set; }
|
||||||
|
|
||||||
|
public string? ReasonCode { get; set; }
|
||||||
|
public VarianceStatus Status { get; set; } = VarianceStatus.Pending;
|
||||||
|
|
||||||
|
public Guid? FK_ResolvedByUserId { get; set; }
|
||||||
|
[ForeignKey("FK_ResolvedByUserId")]
|
||||||
|
public User? ResolvedBy { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ResolvedAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ using Distribution.Features.Brand;
|
|||||||
using Distribution.Features.Category;
|
using Distribution.Features.Category;
|
||||||
using Distribution.Features.DeliveryRide;
|
using Distribution.Features.DeliveryRide;
|
||||||
using Distribution.Features.Payment;
|
using Distribution.Features.Payment;
|
||||||
using Distribution.Features.DriverComplianceConfig;
|
|
||||||
using Distribution.Features.DriverProfile;
|
using Distribution.Features.DriverProfile;
|
||||||
using Distribution.Features.Grn;
|
using Distribution.Features.Grn;
|
||||||
using Distribution.Features.Item;
|
using Distribution.Features.Item;
|
||||||
@@ -30,6 +29,7 @@ using Distribution.Features.Sync;
|
|||||||
using Distribution.Features.Uom;
|
using Distribution.Features.Uom;
|
||||||
using Distribution.Features.User;
|
using Distribution.Features.User;
|
||||||
using Distribution.Features.Vehicle;
|
using Distribution.Features.Vehicle;
|
||||||
|
using Distribution.Features.Reconciliation;
|
||||||
using Distribution.Features.Stock;
|
using Distribution.Features.Stock;
|
||||||
using Distribution.Features.Warehouse;
|
using Distribution.Features.Warehouse;
|
||||||
using Distribution.Infra.Authorization;
|
using Distribution.Infra.Authorization;
|
||||||
@@ -62,7 +62,6 @@ builder.Services.AddScoped<IUomRepository, UomRepository>();
|
|||||||
builder.Services.AddScoped<IItemRepository, ItemRepository>();
|
builder.Services.AddScoped<IItemRepository, ItemRepository>();
|
||||||
builder.Services.AddScoped<IStockRepository, StockRepository>();
|
builder.Services.AddScoped<IStockRepository, StockRepository>();
|
||||||
builder.Services.AddScoped<IDriverProfileRepository, DriverProfileRepository>();
|
builder.Services.AddScoped<IDriverProfileRepository, DriverProfileRepository>();
|
||||||
builder.Services.AddScoped<IDriverComplianceConfigRepository, DriverComplianceConfigRepository>();
|
|
||||||
builder.Services.AddScoped<IRideRepository, RideRepository>();
|
builder.Services.AddScoped<IRideRepository, RideRepository>();
|
||||||
builder.Services.AddScoped<IRideCancelRequestRepository, RideCancelRequestRepository>();
|
builder.Services.AddScoped<IRideCancelRequestRepository, RideCancelRequestRepository>();
|
||||||
builder.Services.AddScoped<ILoadingSheetRepository, LoadingSheetRepository>();
|
builder.Services.AddScoped<ILoadingSheetRepository, LoadingSheetRepository>();
|
||||||
@@ -78,6 +77,7 @@ builder.Services.AddScoped<IClaimRepository, ClaimRepository>();
|
|||||||
builder.Services.AddScoped<ISupplierClaimRepository, SupplierClaimRepository>();
|
builder.Services.AddScoped<ISupplierClaimRepository, SupplierClaimRepository>();
|
||||||
builder.Services.AddScoped<IDeliveryTaskRepository, DeliveryTaskRepository>();
|
builder.Services.AddScoped<IDeliveryTaskRepository, DeliveryTaskRepository>();
|
||||||
builder.Services.AddScoped<IPaymentRepository, PaymentRepository>();
|
builder.Services.AddScoped<IPaymentRepository, PaymentRepository>();
|
||||||
|
builder.Services.AddScoped<IReconciliationRepository, ReconciliationRepository>();
|
||||||
|
|
||||||
builder.Services.AddScoped<RegionService>();
|
builder.Services.AddScoped<RegionService>();
|
||||||
builder.Services.AddScoped<RouteService>();
|
builder.Services.AddScoped<RouteService>();
|
||||||
@@ -99,7 +99,6 @@ builder.Services.AddScoped<StoreKycDocumentService>();
|
|||||||
builder.Services.AddScoped<PriceListService>();
|
builder.Services.AddScoped<PriceListService>();
|
||||||
builder.Services.AddScoped<SupplierService>();
|
builder.Services.AddScoped<SupplierService>();
|
||||||
builder.Services.AddScoped<DriverProfileService>();
|
builder.Services.AddScoped<DriverProfileService>();
|
||||||
builder.Services.AddScoped<DriverComplianceConfigService>();
|
|
||||||
builder.Services.AddScoped<RideService>();
|
builder.Services.AddScoped<RideService>();
|
||||||
builder.Services.AddScoped<LoadingSheetService>();
|
builder.Services.AddScoped<LoadingSheetService>();
|
||||||
builder.Services.AddScoped<SchemeService>();
|
builder.Services.AddScoped<SchemeService>();
|
||||||
@@ -111,6 +110,7 @@ builder.Services.AddScoped<ClaimService>();
|
|||||||
builder.Services.AddScoped<SupplierClaimService>();
|
builder.Services.AddScoped<SupplierClaimService>();
|
||||||
builder.Services.AddScoped<DeliveryTaskService>();
|
builder.Services.AddScoped<DeliveryTaskService>();
|
||||||
builder.Services.AddScoped<PaymentService>();
|
builder.Services.AddScoped<PaymentService>();
|
||||||
|
builder.Services.AddScoped<ReconciliationService>();
|
||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
|
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||||
|
|||||||
Reference in New Issue
Block a user