|
|
@@ -25,20 +25,38 @@ public class AdoS0MfgProductionLinesController : ControllerBase
|
|
|
/// <summary>
|
|
|
/// B1-9:LineMaster 6 个 Location 变体的统一 existence check。
|
|
|
/// 全部复用 AdoS0ReferenceChecker.LocationExistsAsync,空值放行。
|
|
|
+ ///
|
|
|
+ /// LEGACY REFERENCE PRESERVATION(与 D-03 并列的独立兼容合同,不改 D-03 语义):
|
|
|
+ /// UPDATE 时,若某个库位字段的值与库中原值**完全未变化**,且该引用的校验作用域
|
|
|
+ /// (LocationExistsAsync 只取 tenantId,UPDATE 中租户不可变)也未变化,
|
|
|
+ /// 则允许原值 preserve,跳过这一个字段的存在性校验。
|
|
|
+ /// 任何被**修改过**的值仍走严格校验;CREATE 传 existing = null,永远严格。
|
|
|
+ ///
|
|
|
+ /// 动机:T01 的主库位是历史描述文本「脱包车间周转仓」,不在 LocationMaster 中。
|
|
|
+ /// 在此之前,用户即使什么都不改也无法保存该行。
|
|
|
+ /// 本豁免只允许「旧值 → 同一个旧值」,绝不放行任何新的非法值。
|
|
|
/// </summary>
|
|
|
- private async Task<IActionResult?> ValidateLocationReferencesAsync(AdoS0LineMasterUpsertDto dto, long tenantId)
|
|
|
+ private async Task<IActionResult?> ValidateLocationReferencesAsync(
|
|
|
+ AdoS0LineMasterUpsertDto dto, long tenantId, AdoS0LineMaster? existing = null)
|
|
|
{
|
|
|
- var checks = new (string? Value, string Label)[]
|
|
|
+ var checks = new (string? Value, string? Original, string Label)[]
|
|
|
{
|
|
|
- (dto.Location, "主库位"),
|
|
|
- (dto.VLocation, "虚拟库位"),
|
|
|
- (dto.Location2, "辅助库位2"),
|
|
|
- (dto.Location3, "辅助库位3"),
|
|
|
- (dto.PickingLocation, "拣料库位"),
|
|
|
- (dto.MidLocation, "中间库位"),
|
|
|
+ (dto.Location, existing?.Location, "主库位"),
|
|
|
+ (dto.VLocation, existing?.VLocation, "虚拟库位"),
|
|
|
+ (dto.Location2, existing?.Location2, "辅助库位2"),
|
|
|
+ (dto.Location3, existing?.Location3, "辅助库位3"),
|
|
|
+ (dto.PickingLocation, existing?.PickingLocation, "拣料库位"),
|
|
|
+ (dto.MidLocation, existing?.MidLocation, "中间库位"),
|
|
|
};
|
|
|
- foreach (var (value, label) in checks)
|
|
|
+ // 与落库归一化保持一致(Trim + 空串视为 null),避免仅因空白差异被判为「已修改」
|
|
|
+ static string? Normalize(string? v) => string.IsNullOrWhiteSpace(v) ? null : v.Trim();
|
|
|
+
|
|
|
+ foreach (var (value, original, label) in checks)
|
|
|
{
|
|
|
+ // 值未变化(UPDATE 且与原值逐字相同)→ preserve,不重新校验
|
|
|
+ if (existing != null && string.Equals(Normalize(value), Normalize(original), StringComparison.Ordinal))
|
|
|
+ continue;
|
|
|
+
|
|
|
if (!await _refChecker.LocationExistsAsync(tenantId, value))
|
|
|
return AdoS0ApiErrors.InvalidReference(AdoS0ErrorCodes.ReferenceNotFound,
|
|
|
$"{label}编码 '{value}' 不存在于库位主数据");
|
|
|
@@ -148,12 +166,13 @@ public class AdoS0MfgProductionLinesController : ControllerBase
|
|
|
public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0LineMasterUpsertDto dto)
|
|
|
{
|
|
|
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
|
|
|
- var locErr = await ValidateLocationReferencesAsync(dto, tenantId);
|
|
|
- if (locErr != null) return locErr;
|
|
|
-
|
|
|
var entity = await _rep.ByIdScopedAsync(id, tenantId);
|
|
|
if (entity == null) return NotFound();
|
|
|
|
|
|
+ // 传入库中原记录 → 未变化的历史库位值可 preserve;被修改的值仍严格校验
|
|
|
+ var locErr = await ValidateLocationReferencesAsync(dto, tenantId, entity);
|
|
|
+ if (locErr != null) return locErr;
|
|
|
+
|
|
|
entity.CompanyRefId = dto.CompanyRefId;
|
|
|
entity.FactoryRefId = dto.FactoryRefId;
|
|
|
entity.Domain = dto.Domain.Trim();
|