using Admin.NET.Plugin.AiDOP.Infrastructure; using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto; using Microsoft.Extensions.Options; namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse; /// /// S5 库存查询:经 IInventoryBalanceReader,优先 LIVE_165,回落 mdp_std_inventory。 /// Domain 由 SourceDomainTenantResolver 反查,禁止回落 8010。 /// [ApiDescriptionSettings(Order = 300, Description = "库存查询")] [Route("api/S5StockQuery")] [NonUnify] public class StockQueryService : IDynamicApiController, ITransient { private const long MainTenantId = 1300000000001L; private readonly ISqlSugarClient _db; private readonly UserManager _userManager; private readonly IInventoryBalanceReader _balanceReader; private readonly SourceDomainTenantResolver _domainTenant; private readonly AidopInventoryOptions _opt; public StockQueryService( ISqlSugarClient db, UserManager userManager, IInventoryBalanceReader balanceReader, SourceDomainTenantResolver domainTenant, IOptions opt) { _db = db; _userManager = userManager; _balanceReader = balanceReader; _domainTenant = domainTenant; _opt = opt.Value; } private long ResolveTenantOrThrow() { var tid = _userManager.TenantId; if (tid <= 0) throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户"); if (_userManager.SuperAdmin && tid == MainTenantId) throw Oops.Oh("超级管理员操作前必须选择目标租户"); return tid; } private async Task ResolveDomainOrThrowAsync(long tenantId, CancellationToken ct = default) { var sourceCode = string.IsNullOrWhiteSpace(_opt.SourceCode) ? "DOPDEMORQ_SQLSERVER" : _opt.SourceCode.Trim(); try { return await _domainTenant.ResolveDomainAsync(sourceCode, tenantId, ct); } catch (Exception ex) { throw Oops.Oh($"无法解析当前租户 Domain 映射:{ex.Message}"); } } private const string LocTypeExpr = "CASE WHEN t.typed='VMI' THEN 'VMI' WHEN t.typed='Supp' THEN 'O库' ELSE '' END"; [DisplayName("库存查询列表")] [HttpGet("list")] public async Task GetList([FromQuery] StockQueryListInput input, CancellationToken ct) { var tenantId = ResolveTenantOrThrow(); var domain = await ResolveDomainOrThrowAsync(tenantId, ct); var page = await _balanceReader.QueryPageAsync(new InventoryBalanceQuery { TenantId = tenantId, Domain = domain, Type = input.Type, Location = input.Location, MaterialCode = input.MaterialCode, BatchNo = input.BatchNo, StockGt = input.StockGt, UnrestrictedGt = input.UnrestrictedGt, QcStockGt = input.QcStockGt, FrozenGt = input.FrozenGt, Page = input.Page, PageSize = input.PageSize }, ct); return new { total = page.Total, page = page.Page, pageSize = page.PageSize, list = page.List.Select(x => new StockQueryListRow { Type = x.Type, Location = x.Location, LocationName = x.LocationName, MaterialCode = x.MaterialCode, MaterialName = x.MaterialName, BatchNo = x.BatchNo, Shelf = x.Shelf, Version = x.Version, UnrestrictedStock = x.UnrestrictedStock, QcStock = x.QcStock, FrozenStock = x.FrozenStock, QtyOnHand = x.QtyOnHand, Unit = x.Unit, Supplier = x.Supplier }).ToList(), grandTotalQtyOnHand = page.GrandTotalQtyOnHand, grandUnrestricted = page.GrandUnrestricted, grandQc = page.GrandQc, grandFrozen = page.GrandFrozen, dataSource = page.DataSource, asOf = page.AsOf, isRealtime = page.IsRealtime }; } [DisplayName("库存查询类型选项")] [HttpGet("type-options")] public async Task GetTypeOptions(CancellationToken ct) { var tenantId = ResolveTenantOrThrow(); var domain = await ResolveDomainOrThrowAsync(tenantId, ct); var list = await _db.Ado.SqlQueryAsync( $""" SELECT DISTINCT {LocTypeExpr.Replace("t.typed", "typed")} AS Val FROM LocationMaster WHERE Domain = @Domain AND tenant_id = @TenantId AND {LocTypeExpr.Replace("t.typed", "typed")} <> '' ORDER BY Val """, new List { new("@Domain", domain), new("@TenantId", tenantId) }); return list; } [DisplayName("库存查询库位选项")] [HttpGet("location-options")] public async Task GetLocationOptions(CancellationToken ct) { var tenantId = ResolveTenantOrThrow(); var domain = await ResolveDomainOrThrowAsync(tenantId, ct); var list = await _db.Ado.SqlQueryAsync( """ SELECT location AS Val, descr AS Label FROM LocationMaster WHERE Domain = @Domain AND tenant_id = @TenantId AND IFNULL(location,'') <> '' ORDER BY location """, new List { new("@Domain", domain), new("@TenantId", tenantId) }); return list; } }