实现原料入场条码与批次号生成逻辑,离线时支持本地兜底;并在后续处理前补充入场记录必要标识校验。

This commit is contained in:
geht
2026-05-18 17:57:47 +08:00
parent a102ed46f2
commit 50b74f484f
3 changed files with 89 additions and 2 deletions

View File

@@ -21,7 +21,7 @@ public interface IRawMaterialEntryService
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
Task<bool> DeleteBatchAsync(string ids, CancellationToken ct = default);
/// <summary>调用后端接口生成条码/批次号格式QH+物料编码+yyMMdd+序号)</summary>
/// <summary>生成条码/批次号(优先后端,离线回退本地;格式QH+物料编码+yyMMdd+序号)</summary>
Task<string?> GenerateBarcodeAsync(string materialCode, CancellationToken ct = default);
/// <summary>按业务打印绑定准备模板 JSON 与 printData与后端 prepareNativePrint 一致,免密 anon。</summary>

View File

@@ -5,6 +5,7 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Globalization;
using System.Web;
using Prism.Events;
using YY.Admin.Core;
@@ -151,6 +152,7 @@ public class RawMaterialEntryService : IRawMaterialEntryService, ISingletonDepen
if (!entry.TenantId.HasValue || entry.TenantId.Value <= 0) entry.TenantId = DefaultTenantId;
var local = Clone(entry);
if (string.IsNullOrWhiteSpace(local.Id)) local.Id = $"local-{Guid.NewGuid():N}";
EnsureBarcodeAndBatchNo(local);
if (_networkMonitor.IsOnline)
{
@@ -228,6 +230,11 @@ public class RawMaterialEntryService : IRawMaterialEntryService, ISingletonDepen
public async Task<string?> GenerateBarcodeAsync(string materialCode, CancellationToken ct = default)
{
if (!_networkMonitor.IsOnline)
{
return GenerateLocalBarcode(materialCode);
}
var url = $"{BaseUrl}/xslmes/mesXslRawMaterialEntry/anon/generateBarcode?materialCode={Uri.EscapeDataString(materialCode ?? "")}";
try
{
@@ -243,7 +250,46 @@ public class RawMaterialEntryService : IRawMaterialEntryService, ISingletonDepen
{
_logger.Warning($"[原料入场] 生成条码失败: {ex.Message}");
}
return null;
return GenerateLocalBarcode(materialCode);
}
/// <summary>
/// 桌面端离线兜底产号:保持与服务端一致的格式 QH + 物料编码 + yyMMdd + 3位流水。
/// 流水号口径与服务端一致:按当前前缀匹配条码计数 + 1非 max+1
/// </summary>
private string GenerateLocalBarcode(string materialCode)
{
var normalizedCode = (materialCode ?? string.Empty).Trim();
var dateStr = DateTime.Now.ToString("yyMMdd", CultureInfo.InvariantCulture);
var prefix = $"QH{normalizedCode}{dateStr}";
int sequence;
lock (_cacheLock)
{
var snapshot = ApplyPendingOpsSnapshotUnsafe(_localCache.Select(Clone).ToList());
var count = snapshot.Count(x =>
!string.IsNullOrWhiteSpace(x.Barcode) &&
x.Barcode!.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
sequence = count + 1;
}
return $"{prefix}{sequence:000}";
}
/// <summary>
/// 新增入场记录时保证条码和批次号本地可用:
/// 条码为空时本地生成,批次号为空时回填为条码。
/// </summary>
private void EnsureBarcodeAndBatchNo(MesXslRawMaterialEntry entry)
{
if (string.IsNullOrWhiteSpace(entry.Barcode))
{
entry.Barcode = GenerateLocalBarcode(entry.MaterialCode ?? string.Empty);
}
if (string.IsNullOrWhiteSpace(entry.BatchNo))
{
entry.BatchNo = entry.Barcode;
}
}
public async Task<(string templateJson, string printDataJson, string? errorMessage)> PrepareNativePrintAsync(string id, CancellationToken ct = default)

View File

@@ -688,6 +688,11 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
return;
}
if (!await EnsureEntryBarcodeAndBatchNoAsync())
{
return;
}
// 库位必填仅校验本批待生成行;行号取在原集合的真实索引,避免编号偏移误导
foreach (var row in pendingRows)
{
@@ -865,6 +870,42 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
}
}
/// <summary>
/// 生成原材料卡片前确保入场记录已有条码/批次号。
/// 规则:条码优先沿用现有值;为空时调用入场服务生成(在线优先,离线本地兜底);
/// 批次号为空则回填为条码。
/// </summary>
private async Task<bool> EnsureEntryBarcodeAndBatchNoAsync()
{
if (Entry == null) return false;
if (string.IsNullOrWhiteSpace(Entry.Barcode))
{
if (string.IsNullOrWhiteSpace(Entry.MaterialCode))
{
HandyControl.Controls.MessageBox.Warning("当前记录缺少物料编码,无法生成原材料卡片条码。");
return false;
}
var code = await EntryService.GenerateBarcodeAsync(Entry.MaterialCode);
if (string.IsNullOrWhiteSpace(code))
{
HandyControl.Controls.MessageBox.Warning("未能生成入场条码,请检查网络或稍后重试。");
return false;
}
Entry.Barcode = code.Trim();
}
if (string.IsNullOrWhiteSpace(Entry.BatchNo))
{
Entry.BatchNo = Entry.Barcode;
}
RaisePropertyChanged(nameof(Entry));
return true;
}
private List<RawMaterialCardGeneratePlanItem> BuildPlannedRawMaterialCards(
MesXslRawMaterialEntry entry,
IReadOnlyList<RawMaterialSplitDetailItem> pendingRows,