Add Phase B: Inventory COGS auto-posting to GL on JobUsage transactions

When powder is consumed via a job (JobsController) or scan (InventoryController.LogUsage),
debit the item's CogsAccountId and credit its InventoryAccountId for the cost of the
quantity consumed (using AverageCost if available, else UnitCost). No-op when either
GL account is not configured on the InventoryItem.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 23:39:23 -04:00
parent 7e1676cfd7
commit 0afb474c3e
3 changed files with 26 additions and 3 deletions
@@ -30,6 +30,7 @@ public class InventoryController : Controller
private readonly IInventoryAiLookupService _aiLookupService;
private readonly ISubscriptionService _subscriptionService;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IAccountBalanceService _accountBalanceService;
public InventoryController(
IUnitOfWork unitOfWork,
@@ -39,7 +40,8 @@ public class InventoryController : Controller
IMeasurementConversionService measurementService,
IInventoryAiLookupService aiLookupService,
ISubscriptionService subscriptionService,
UserManager<ApplicationUser> userManager)
UserManager<ApplicationUser> userManager,
IAccountBalanceService accountBalanceService)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
@@ -49,6 +51,7 @@ public class InventoryController : Controller
_aiLookupService = aiLookupService;
_subscriptionService = subscriptionService;
_userManager = userManager;
_accountBalanceService = accountBalanceService;
}
/// <summary>
@@ -1552,6 +1555,14 @@ public class InventoryController : Controller
await _unitOfWork.InventoryTransactions.AddAsync(txn);
await _unitOfWork.SaveChangesAsync();
// GL: DR COGS, CR Inventory Asset — no-op if accounts not configured on the item
if (item.CogsAccountId.HasValue && item.InventoryAccountId.HasValue)
{
var cost = quantity * (item.AverageCost > 0 ? item.AverageCost : item.UnitCost);
await _accountBalanceService.DebitAsync(item.CogsAccountId, cost);
await _accountBalanceService.CreditAsync(item.InventoryAccountId, cost);
}
// PowderUsageLog requires a specific JobItem + Coat FK — scan-based logging
// doesn't have that context, so we rely on the InventoryTransaction alone
// for the audit trail. Coat-level PowderUsageLogs are created by the job workflow.