| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
- using Yitter.IdGenerator;
- namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
- /// <summary>
- /// S5 来料检验任务(报检分录)操作:认领 / 调配 / 优先级 / 检验完成 / 生成检验单。
- /// 对齐旧 DOP 任务列表(program 396052925665349)行为,数据落本库 qms_qcp_*。
- /// </summary>
- [ApiDescriptionSettings(Order = 311, Description = "来料检验任务操作")]
- [Route("api/S5IqcTaskEntry")]
- [NonUnify]
- public class IqcTaskEntryService : IDynamicApiController, ITransient
- {
- private const long MainTenantId = 1300000000001L;
- private readonly ISqlSugarClient _db;
- private readonly UserManager _userManager;
- public IqcTaskEntryService(ISqlSugarClient db, UserManager userManager)
- {
- _db = db;
- _userManager = userManager;
- }
- private long ResolveTenantOrThrow()
- {
- var tid = _userManager.TenantId;
- if (tid <= 0) throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
- if (_userManager.SuperAdmin && tid == MainTenantId)
- throw Oops.Oh("超级管理员操作前必须选择目标租户");
- return tid;
- }
- [DisplayName("来料检验任务列表(报检分录)")]
- [HttpGet("list")]
- public async Task<object> GetList([FromQuery] IqcTaskEntryListInput input)
- {
- var page = input.Page <= 0 ? 1 : input.Page;
- var pageSize = input.PageSize <= 0 ? 20 : (input.PageSize > 200 ? 200 : input.PageSize);
- var offset = (page - 1) * pageSize;
- var tenantId = ResolveTenantOrThrow();
- var where = new List<string> { "e.tenant_id = @TenantId" };
- var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
- if (!string.IsNullOrWhiteSpace(input.ApplyBillNo))
- {
- where.Add("a.FBILLNO LIKE @ApplyBillNo");
- pars.Add(new SugarParameter("@ApplyBillNo", $"%{input.ApplyBillNo.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.MaterialCode))
- {
- where.Add("e.FMATERIALCFG LIKE @MaterialCode");
- pars.Add(new SugarParameter("@MaterialCode", $"%{input.MaterialCode.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.LotNo))
- {
- where.Add("e.FLOTNUMBER LIKE @LotNo");
- pars.Add(new SugarParameter("@LotNo", $"%{input.LotNo.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.InspectStatus))
- {
- where.Add("e.FINSPECTSTATUS = @InspectStatus");
- pars.Add(new SugarParameter("@InspectStatus", input.InspectStatus.Trim()));
- }
- if (!string.IsNullOrWhiteSpace(input.Receiver))
- {
- where.Add("e.FSRCORDERNUM LIKE @Receiver");
- pars.Add(new SugarParameter("@Receiver", $"%{input.Receiver.Trim()}%"));
- }
- var whereSql = string.Join(" AND ", where);
- var total = await _db.Ado.GetIntAsync(
- $"""
- SELECT COUNT(1)
- FROM qms_qcp_insappnentry e
- LEFT JOIN qms_qcp_inspecapplyn a ON a.id = e.glid AND a.tenant_id = e.tenant_id
- WHERE {whereSql}
- """,
- pars);
- var list = await _db.Ado.SqlQueryAsync<IqcTaskEntryRow>(
- $"""
- SELECT
- e.id AS Id,
- e.glid AS Glid,
- a.FBILLNO AS ApplyBillNo,
- a.FBIZTYPE AS BizType,
- e.FMATERIALCFG AS MaterialCode,
- e.wlmc AS MaterialName,
- e.ggxh AS Spec,
- e.FLOTNUMBER AS LotNo,
- e.FSRCORDERNUM AS Receiver,
- e.FAPPLYQTY AS ApplyQty,
- e.FINSPECTSTATUS AS InspectStatus,
- e.jyfzr AS OwnerId,
- CAST(NULL AS CHAR) AS OwnerName,
- e.yxj AS Priority,
- e.FSUPPLIER AS SupplierCode,
- e.gysmc AS SupplierName,
- e.shdh AS ShipmentNo,
- b.id AS InspBillId,
- b.FBILLNO AS InspBillNo
- FROM qms_qcp_insappnentry e
- LEFT JOIN qms_qcp_inspecapplyn a ON a.id = e.glid AND a.tenant_id = e.tenant_id
- LEFT JOIN qms_qcp_inspbill b ON b.hid = e.id AND b.tenant_id = e.tenant_id
- WHERE {whereSql}
- ORDER BY IFNULL(e.yxj, 999999) ASC, e.id DESC
- LIMIT {pageSize} OFFSET {offset}
- """,
- pars);
- await FillOwnerNamesAsync(list);
- return new { total, page, pageSize, list };
- }
- private async Task FillOwnerNamesAsync(List<IqcTaskEntryRow> list)
- {
- var ids = list
- .Select(x => x.OwnerId)
- .Where(x => !string.IsNullOrWhiteSpace(x) && long.TryParse(x, out _))
- .Select(x => long.Parse(x!))
- .Distinct()
- .ToList();
- if (ids.Count == 0) return;
- var users = await _db.Queryable<SysUser>()
- .ClearFilter()
- .Where(u => ids.Contains(u.Id))
- .Select(u => new { u.Id, u.RealName, u.Account })
- .ToListAsync();
- var map = users.ToDictionary(u => u.Id.ToString(), u => u.RealName ?? u.Account ?? u.Id.ToString());
- foreach (var row in list)
- {
- if (!string.IsNullOrWhiteSpace(row.OwnerId) && map.TryGetValue(row.OwnerId!, out var name))
- row.OwnerName = name;
- }
- }
- [DisplayName("认领检验任务")]
- [HttpPost("claim")]
- public async Task<object> Claim([FromBody] IqcTaskEntryIdsInput input)
- {
- var ids = NormalizeIds(input?.Ids);
- var tenantId = ResolveTenantOrThrow();
- var owner = _userManager.UserId.ToString();
- var n = await _db.Ado.ExecuteCommandAsync(
- $"""
- UPDATE qms_qcp_insappnentry
- SET jyfzr = @Owner
- WHERE tenant_id = @TenantId AND id IN ({string.Join(",", ids)})
- """,
- new SugarParameter("@Owner", owner),
- new SugarParameter("@TenantId", tenantId));
- return new { updated = n, ownerId = owner };
- }
- [DisplayName("检验员调配")]
- [HttpPost("assign")]
- public async Task<object> Assign([FromBody] IqcTaskEntryAssignInput input)
- {
- var ids = NormalizeIds(input?.Ids);
- if (input == null || input.InspectorUserId <= 0) throw Oops.Oh("请指定检验负责人");
- var tenantId = ResolveTenantOrThrow();
- var owner = input.InspectorUserId.ToString();
- var n = await _db.Ado.ExecuteCommandAsync(
- $"""
- UPDATE qms_qcp_insappnentry
- SET jyfzr = @Owner
- WHERE tenant_id = @TenantId AND id IN ({string.Join(",", ids)})
- """,
- new SugarParameter("@Owner", owner),
- new SugarParameter("@TenantId", tenantId));
- return new { updated = n, ownerId = owner };
- }
- [DisplayName("优先级调整")]
- [HttpPost("priority")]
- public async Task<object> AdjustPriority([FromBody] IqcTaskEntryPriorityInput input)
- {
- var ids = NormalizeIds(input?.Ids);
- var tenantId = ResolveTenantOrThrow();
- var n = await _db.Ado.ExecuteCommandAsync(
- $"""
- UPDATE qms_qcp_insappnentry
- SET yxj = @Priority
- WHERE tenant_id = @TenantId AND id IN ({string.Join(",", ids)})
- """,
- new SugarParameter("@Priority", input!.Priority),
- new SugarParameter("@TenantId", tenantId));
- return new { updated = n };
- }
- [DisplayName("检验完成(手工兜底)")]
- [HttpPost("complete")]
- public async Task<object> MarkComplete([FromBody] IqcTaskEntryIdsInput input)
- {
- var ids = NormalizeIds(input?.Ids);
- var tenantId = ResolveTenantOrThrow();
- var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- var n = await _db.Ado.ExecuteCommandAsync(
- $"""
- UPDATE qms_qcp_insappnentry
- SET FINSPECTSTATUS = '检验完成', jywcsj = @Now
- WHERE tenant_id = @TenantId AND id IN ({string.Join(",", ids)})
- """,
- new SugarParameter("@Now", now),
- new SugarParameter("@TenantId", tenantId));
- return new { updated = n };
- }
- /// <summary>
- /// 「来料检验」:校验认领人 + 幂等 → 置检验中 → 本库生成检验单(IQC+yyyyMMdd+4 位)。
- /// </summary>
- [DisplayName("生成来料检验单")]
- [HttpPost("generate-inspbill")]
- public async Task<IqcGenerateInspBillOutput> GenerateInspBill([FromBody] IqcGenerateInspBillInput input)
- {
- if (input == null || input.EntryId <= 0) throw Oops.Oh("报检分录 id 非法");
- var tenantId = ResolveTenantOrThrow();
- var userId = _userManager.UserId.ToString();
- var entry = await _db.Ado.SqlQuerySingleAsync<EntryGenRow>(
- """
- SELECT
- e.id AS Id, e.glid AS Glid, e.jyfzr AS OwnerId, e.FINSPECTSTATUS AS InspectStatus,
- e.FMATERIALCFG AS MaterialCode, e.wlmc AS MaterialName, e.ggxh AS Spec,
- e.FLOTNUMBER AS LotNo, e.FAPPLYQTY AS ApplyQty, e.gysmc AS SupplierName,
- a.FBILLNO AS ApplyBillNo, a.FBIZTYPE AS BizType
- FROM qms_qcp_insappnentry e
- LEFT JOIN qms_qcp_inspecapplyn a ON a.id = e.glid AND a.tenant_id = e.tenant_id
- WHERE e.id = @Id AND e.tenant_id = @TenantId
- LIMIT 1
- """,
- new SugarParameter("@Id", input.EntryId),
- new SugarParameter("@TenantId", tenantId));
- if (entry == null) throw Oops.Oh("报检分录不存在");
- if (string.IsNullOrWhiteSpace(entry.ApplyBillNo)) throw Oops.Oh("报检单号缺失,无法生成检验单");
- if (!string.Equals(entry.OwnerId?.Trim(), userId, StringComparison.Ordinal))
- throw Oops.Oh("尚未认领或非认领人操作!");
- var exists = await _db.Ado.GetIntAsync(
- """
- SELECT COUNT(1)
- FROM qms_qcp_inspbill
- WHERE tenant_id = @TenantId AND lydjbh = @ApplyBillNo AND hid = @Hid
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@ApplyBillNo", entry.ApplyBillNo),
- new SugarParameter("@Hid", entry.Id));
- if (exists > 0) throw Oops.Oh("该检验任务已经有检验单,不能重新生成!");
- var billNo = await AllocateIqcBillNoAsync(tenantId);
- var billId = YitIdHelper.NextId();
- var now = DateTime.Now;
- var startText = now.ToString("yyyy-MM-dd HH:mm:ss");
- var tran = await _db.Ado.UseTranAsync(async () =>
- {
- await _db.Ado.ExecuteCommandAsync(
- """
- UPDATE qms_qcp_insappnentry
- SET jykssj = @Start, FINSPECTSTATUS = '检验中'
- WHERE id = @Id AND tenant_id = @TenantId
- """,
- new SugarParameter("@Start", startText),
- new SugarParameter("@Id", entry.Id),
- new SugarParameter("@TenantId", tenantId));
- await _db.Ado.ExecuteCommandAsync(
- """
- INSERT INTO qms_qcp_inspbill (
- id, tenant_id, FBILLNO, lydjbh, hid, FMATERIALCFG, wlmc, ggxh, gysmc, pch,
- FRINSQTY, jysl, dhsl, bhgsl, FBILLSTATUS, jyr, FINSPECTORID,
- FINSPESTARTDATE, FCREATETIME
- ) VALUES (
- @Id, @TenantId, @FBILLNO, @Lydjbh, @Hid, @FMATERIALCFG, @Wlmc, @Ggxh, @Gysmc, @Pch,
- @FRINSQTY, @Jysl, 0, 0, '检验中', @Jyr, @Inspector,
- @StartDate, @CreateTime
- )
- """,
- new SugarParameter("@Id", billId),
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@FBILLNO", billNo),
- new SugarParameter("@Lydjbh", entry.ApplyBillNo),
- new SugarParameter("@Hid", entry.Id),
- new SugarParameter("@FMATERIALCFG", entry.MaterialCode ?? (object)DBNull.Value),
- new SugarParameter("@Wlmc", entry.MaterialName ?? (object)DBNull.Value),
- new SugarParameter("@Ggxh", entry.Spec ?? (object)DBNull.Value),
- new SugarParameter("@Gysmc", entry.SupplierName ?? (object)DBNull.Value),
- new SugarParameter("@Pch", entry.LotNo ?? (object)DBNull.Value),
- new SugarParameter("@FRINSQTY", entry.ApplyQty ?? 0m),
- new SugarParameter("@Jysl", entry.ApplyQty ?? 0m),
- new SugarParameter("@Jyr", userId),
- new SugarParameter("@Inspector", _userManager.RealName ?? _userManager.Account ?? userId),
- new SugarParameter("@StartDate", now),
- new SugarParameter("@CreateTime", now));
- });
- if (!tran.IsSuccess) throw tran.ErrorException ?? Oops.Oh("生成检验单失败");
- return new IqcGenerateInspBillOutput { InspBillId = billId, InspBillNo = billNo };
- }
- private async Task<string> AllocateIqcBillNoAsync(long tenantId)
- {
- var today = DateTime.Now.ToString("yyyyMMdd");
- var prefix = $"IQC{today}";
- var max = await _db.Ado.SqlQuerySingleAsync<string>(
- """
- SELECT FBILLNO
- FROM qms_qcp_inspbill
- WHERE tenant_id = @TenantId AND FBILLNO LIKE @Prefix
- ORDER BY FBILLNO DESC
- LIMIT 1
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@Prefix", prefix + "%"));
- if (string.IsNullOrWhiteSpace(max) || max.Length < 4)
- return $"{prefix}0001";
- var last4 = max[^4..];
- if (!int.TryParse(last4, out var serial))
- return $"{prefix}0001";
- if (serial >= 9999)
- throw Oops.Oh($"当日检验单号已达上限 {prefix}9999");
- return $"{prefix}{(serial + 1).ToString().PadLeft(4, '0')}";
- }
- private static List<long> NormalizeIds(List<long>? ids)
- {
- var list = (ids ?? new List<long>()).Where(x => x > 0).Distinct().ToList();
- if (list.Count == 0) throw Oops.Oh("请至少选择一条任务");
- if (list.Count > 200) throw Oops.Oh("单次最多操作 200 条");
- return list;
- }
- private sealed class EntryGenRow
- {
- public long Id { get; set; }
- public long? Glid { get; set; }
- public string? OwnerId { get; set; }
- public string? InspectStatus { get; set; }
- public string? MaterialCode { get; set; }
- public string? MaterialName { get; set; }
- public string? Spec { get; set; }
- public string? LotNo { get; set; }
- public decimal? ApplyQty { get; set; }
- public string? SupplierName { get; set; }
- public string? ApplyBillNo { get; set; }
- public string? BizType { get; set; }
- }
- }
|