ui bug fixers
This commit is contained in:
@@ -65,4 +65,15 @@ public sealed class VendorsController : ApiControllerBase
|
||||
await _vendors.SetStatusAsync(vendorId, request.Status, ct);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{vendorId:int}")]
|
||||
[Authorize(Policy = NavPolicies.Prefix + "vendors" + NavPolicies.ActionSeparator + NavPolicies.DeleteAction)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<IActionResult> Delete(int vendorId, CancellationToken ct)
|
||||
{
|
||||
await _vendors.DeleteAsync(vendorId, ct);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ public sealed class CreatePurchaseInvoicePaymentRequest
|
||||
[Range(0.01, double.MaxValue)] public decimal Amount { get; set; }
|
||||
[Required] public long GlBankAccountId { get; set; }
|
||||
[StringLength(100)] public string? Reference { get; set; }
|
||||
/// <summary>When the vendor was paid; also the GL entry date. Omitted ⇒ today. Never in the future.</summary>
|
||||
public DateOnly? PaymentDate { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CreatePurchaseInvoiceRequest
|
||||
|
||||
@@ -13,4 +13,5 @@ public interface IVendorService
|
||||
Task<ETagged<VendorDto>> CreateAsync(CreateVendorRequest request, CancellationToken ct = default);
|
||||
Task<ETagged<VendorDto>> UpdateAsync(int vendorId, UpdateVendorRequest request, uint expectedRowVersion, CancellationToken ct = default);
|
||||
Task SetStatusAsync(int vendorId, EntityStatus status, CancellationToken ct = default);
|
||||
Task DeleteAsync(int vendorId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public sealed class PurchaseInvoicePaymentService : IPurchaseInvoicePaymentServi
|
||||
|
||||
var actor = _currentUser.AuditUserId;
|
||||
var now = DateTime.UtcNow;
|
||||
var (paymentDay, paymentDate) = ResolvePaymentDate(request.PaymentDate, now);
|
||||
|
||||
var payment = await _uow.ExecuteInTransactionAsync(async token =>
|
||||
{
|
||||
@@ -82,7 +83,7 @@ public sealed class PurchaseInvoicePaymentService : IPurchaseInvoicePaymentServi
|
||||
{
|
||||
PurchaseInvoiceId = invoice.PurchaseInvoiceId,
|
||||
Amount = request.Amount,
|
||||
PaymentDate = now,
|
||||
PaymentDate = paymentDate,
|
||||
GlBankAccountId = account.AccountId,
|
||||
BankAccountName = account.AccountName,
|
||||
Reference = request.Reference,
|
||||
@@ -101,6 +102,21 @@ public sealed class PurchaseInvoicePaymentService : IPurchaseInvoicePaymentServi
|
||||
return Map(payment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The payment's calendar day (also the GL entry date) and the timestamp stored on it — same
|
||||
/// rule as <see cref="SalesInvoicePaymentService"/>. Omitted ⇒ today. One day of slack on
|
||||
/// "future": the client sends its local date, which runs ahead of UTC for users east of it.
|
||||
/// Today keeps the actual time of entry; a back-dated payment is stamped at that day's start.
|
||||
/// </summary>
|
||||
private static (DateOnly Day, DateTime Stamp) ResolvePaymentDate(DateOnly? requested, DateTime now)
|
||||
{
|
||||
var today = DateOnly.FromDateTime(now);
|
||||
var day = requested ?? today;
|
||||
if (day > today.AddDays(1))
|
||||
throw new DomainException(ErrorCodes.Validation, "Payment date cannot be in the future.", 400);
|
||||
return (day, day >= today ? now : day.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc));
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<PurchaseInvoicePaymentDto>> ListAsync(int purchaseInvoiceId, CancellationToken ct = default)
|
||||
{
|
||||
if (!await _invoices.Query().AnyAsync(i => i.PurchaseInvoiceId == purchaseInvoiceId, ct))
|
||||
|
||||
@@ -66,7 +66,11 @@ public sealed class RequisitionService : IRequisitionService
|
||||
{
|
||||
await EnsureItemsExistAsync(request.Lines.Select(l => l.ItemId), ct);
|
||||
var actor = _currentUser.AuditUserId;
|
||||
var branchId = await _branchConfig.ResolveBranchIdAsync(request.BranchId, ct);
|
||||
// Branch is optional on a requisition: an unset one falls back to the default branch
|
||||
// rather than being rejected (this also covers reorder-alert requisitions, which carry none).
|
||||
var branchId = request.BranchId > 0
|
||||
? await _branchConfig.ResolveBranchIdAsync(request.BranchId, ct)
|
||||
: await _branchConfig.ResolveDefaultBranchIdAsync(ct);
|
||||
|
||||
var req = await _uow.ExecuteInTransactionAsync(async token =>
|
||||
{
|
||||
|
||||
@@ -120,6 +120,26 @@ public sealed class VendorService : IVendorService
|
||||
await _uow.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hard delete. Every FK to vendors (POs, GRNs, invoices, returns, quotations, item
|
||||
/// default vendor) is Restrict, so a vendor in use is refused with 409 — deactivate it instead.
|
||||
/// </summary>
|
||||
public async Task DeleteAsync(int vendorId, CancellationToken ct = default)
|
||||
{
|
||||
var vendor = await _vendors.GetByIdAsync(vendorId, ct)
|
||||
?? throw new NotFoundException($"Vendor {vendorId} was not found.");
|
||||
|
||||
_vendors.Remove(vendor);
|
||||
try
|
||||
{
|
||||
await _uow.SaveChangesAsync(ct);
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
throw new DomainException(ErrorCodes.MasterInUse, "This vendor is used by purchase documents or items and cannot be deleted. Deactivate it instead.", 409);
|
||||
}
|
||||
}
|
||||
|
||||
private static VendorDto Map(Vendor v) => new(
|
||||
v.VendorId, v.Code, v.Name, v.Terms, v.TaxReg, v.Currency, v.Status, v.BranchId, v.CreatedAt, v.UpdatedAt);
|
||||
}
|
||||
|
||||
@@ -10,5 +10,8 @@
|
||||
},
|
||||
"AuthHex": {
|
||||
"BaseUrl": "http://localhost:5011"
|
||||
},
|
||||
"GeneralLedgerService": {
|
||||
"BaseUrl": "http://localhost:5200/api/v1/"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user