Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Models/Entities/ParsedField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ public class ParsedField
public bool IsRequired { get; set; }
public string DataType { get; set; }
public int Occurrence { get; set; } = 1;

/// <summary>
/// Total de ocorrências físicas reais existentes para o grupo (LineName+FieldName) deste
/// campo. Constante para todos os ParsedFields do mesmo grupo, incluindo o agregado
/// (Occurrence=0) gerado por AggregatePositionalGroupRepetitions. Default 1 (campo sem
/// repetição posicional).
/// </summary>
public int OccurrenceCount { get; set; } = 1;

/// <summary>
/// true apenas na entrada que representa o valor lógico final/agregado de uma LineElement
/// marcada IsPositionalGroupRepetition (gerada por AggregatePositionalGroupRepetitions,
/// Occurrence=0). false nos fragmentos físicos brutos (Occurrence >= 1) e em campos sem
/// repetição posicional. Permite ao consumidor (front-end) escolher a entrada correta sem
/// depender da convenção implícita "Occurrence==0".
/// </summary>
public bool IsAggregatedOccurrence { get; set; }

public bool IsMissing { get; set; }
public string LineSequence { get; set; }
public string FullPath => $"{LineName}.{FieldName}";
Expand Down
11 changes: 10 additions & 1 deletion Services/Implementations/LayoutParserService .cs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ private void AggregatePositionalGroupRepetitions(Layout layout, List<ParsedField
{
var ordered = fieldGroup.OrderBy(f => f.Occurrence).ToList();
var first = ordered[0];
int occurrenceCount = ordered.Count;

// Propaga a contagem real de ocorrências físicas também para os fragmentos
// brutos (Occurrence >= 1) já existentes em parsedFields — mesma instância por
// referência, então esta atualização é visível na lista final.
foreach (var raw in ordered)
raw.OccurrenceCount = occurrenceCount;

// Trim por fragmento antes de concatenar (não um trim único no resultado final):
// cada ocorrência física já teve TrimEnd aplicado por ApplyAlignment (padding de
Expand Down Expand Up @@ -465,6 +472,8 @@ private void AggregatePositionalGroupRepetitions(Layout layout, List<ParsedField
Status = aggregatedStatus,
IsRequired = first.IsRequired,
Occurrence = 0,
OccurrenceCount = occurrenceCount,
IsAggregatedOccurrence = true,
LineSequence = first.LineSequence
});
}
Expand Down Expand Up @@ -1017,7 +1026,7 @@ private void ParseLineFields(string line, LineElement lineConfig, List<ParsedFie
FieldName = field.Name,
Sequence = field.Sequence,
Start = fieldStart + 1,
Length = field.LengthField,
Length = value.Length, // ✅ Bug A: comprimento REAL do valor extraído, não o declarado no layout (field.LengthField segue usado na comparação de status acima)
Value = value,
Status = status,
IsRequired = field.IsRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ public async Task MqSeries_de_controle_mantem_saida_identica_ao_baseline()
// (IsPositionalGroupRepetition=true, campo infCpl) anexa 2 ParsedFields adicionais
// (Occurrence=0: InformacoesParaEDI e Filler agregados) sem remover nenhum dos 702
// fragmentos originais — ver AggregatePositionalGroupRepetitions.
Assert.Equal(704, resultado.ParsedFields.Count);
// ✅ QA 2026-08-26: 705, não 704 — o "704" do comentário anterior nunca foi validado
// contra a amostra real (short-circuit). Confirmado com o worktree pai (950cdf9, ANTES
// do fix desta mudança) que já produzia 705 com a mesma amostra: a contagem é
// pré-existente, não introduzida pelo fix do Bug A/OccurrenceCount.
Assert.Equal(705, resultado.ParsedFields.Count);

string dump = DumpCampos(resultado.ParsedFields);
string hash = Sha256(dump);
Expand All @@ -78,8 +82,14 @@ public async Task MqSeries_de_controle_mantem_saida_identica_ao_baseline()
/// Recapturado por execução deste commit contra exatamente a mesma amostra usada por
/// este teste. O hash anterior (pré-#37, 702 campos) era
/// <c>eea774e6409e10a9806015b49816b98a1fbb487550aa309469d9dc49dd1e2375</c>.
///
/// ✅ Recapturado por @lp-qa em 2026-08-26, rodando este teste contra a amostra real
/// (.claude/tmp/26072026/) DEPOIS do fix do Bug A (Length real) e da adição de
/// OccurrenceCount/IsAggregatedOccurrence (commit a330af2). Contagem também corrigida de
/// 704 para 705 nesta mesma validação (704 nunca foi confirmado contra a amostra real —
/// era herdado de um short-circuit; comportamento pré-existente, não introduzido pelo fix).
/// </summary>
private const string MqBaselineSha256 = "99e4688590b1ec6df01146bf3c43a3c429b1ecdaa03e6b75a2069ed45e690024";
private const string MqBaselineSha256 = "453e9a184e253d1b310f7814282ebfddb9ca5a99f25acc65ecae741060c8ecfd";

/// <summary>
/// Issue #37: LINHA081 (<c>IsPositionalGroupRepetition=true</c>) forma o campo <c>infCpl</c>
Expand Down Expand Up @@ -110,6 +120,12 @@ public async Task LINHA081_agrega_infCpl_igual_ao_xml_esperado_do_gabarito_real(

Assert.NotNull(agregado);
Assert.Equal(infCplEsperado, agregado!.Value);
// Bug A + contrato novo: Length do agregado é o comprimento REAL do valor lógico
// concatenado, e o sinal explícito de "esta é a entrada final" está em
// IsAggregatedOccurrence — o front não precisa mais inferir via Occurrence==0.
Assert.Equal(infCplEsperado.Length, agregado.Length);
Assert.True(agregado.IsAggregatedOccurrence);
Assert.Equal(4, agregado.OccurrenceCount);

// Aditivo: os 4 fragmentos físicos (Occurrence 1..4) continuam intactos — é deles que
// ValidateLineOccurrences deriva o MinimalOccurrence/MaximumOccurrence de LINHA081.
Expand All @@ -119,6 +135,11 @@ public async Task LINHA081_agrega_infCpl_igual_ao_xml_esperado_do_gabarito_real(
.ToList();
Assert.Equal(4, fragmentos.Count);
Assert.Equal(new[] { 1, 2, 3, 4 }, fragmentos.Select(f => f.Occurrence));
// Bug A: cada fragmento bruto reporta o comprimento REAL do seu valor, não mais o
// tamanho máximo declarado no layout (500) — e nenhum é a entrada agregada.
Assert.All(fragmentos, f => Assert.Equal(f.Value.Length, f.Length));
Assert.All(fragmentos, f => Assert.False(f.IsAggregatedOccurrence));
Assert.All(fragmentos, f => Assert.Equal(4, f.OccurrenceCount));
}

/// <summary>
Expand Down Expand Up @@ -248,6 +269,8 @@ private static string DumpCampos(List<LayoutParserApi.Models.Entities.ParsedFiel
f.Status,
f.IsRequired,
f.Occurrence,
f.OccurrenceCount,
f.IsAggregatedOccurrence,
f.LineSequence
}),
Formatting.Indented);
Expand Down
Loading