using SVSim.Database.Enums; using SVSim.Database.Models; using SVSim.Database.Services; namespace SVSim.Database.Services.Inventory; /// /// Scoped builder returned by . Queue spend + /// grant operations; commit to save and assemble the . /// /// Dispose without committing rolls back the underlying DB transaction and detaches any /// in-memory mutations. Always wrap in await using. /// /// public interface IInventoryTransaction : IAsyncDisposable { Viewer Viewer { get; } bool IsFreeplay { get; } /// /// Debits one of the four scalar wallets. Freeplay-aware for Crystal/Rupee/RedEther /// (returns Success with the configured freeplay amount, balance unchanged); SpotPoint /// always real. Returns with current balance on /// failure; viewer state is not mutated on failure. /// Task TrySpendAsync(SpendCurrency currency, long cost, CancellationToken ct = default); /// /// Type-dispatched debit. Currencies (RedEther/Crystal/Rupy/SpotCardPoint) route to /// ; Item decrements OwnedItemEntry.Count. Returns /// whose PostStateTotal is the new wallet balance for /// currencies and the remaining item count for Item. /// Task TryDebitAsync(UserGoodsType type, long detailId, int num, CancellationToken ct = default); Task> GrantAsync(UserGoodsType type, long detailId, int num, CancellationToken ct = default); Task BackfillCardCosmeticsAsync(CancellationToken ct = default); /// /// Freeplay-aware balance read against the live viewer; reflects any spends queued in /// this transaction. Inside a transaction, use this; outside, use /// . /// long EffectiveBalance(SpendCurrency currency); bool OwnsCard(long cardId); bool OwnsCosmetic(CosmeticType type, int id); Task CommitAsync(CancellationToken ct = default); }