fix(awards): apply the reference Prefix where it can actually help

Prefix exists so a field holding a bare value counts for an award whose codes
carry a letter: a French operator writes "74" in STATE, DDFM's codes are "D74".
Def's own doc says exactly that. But searchOne applied the prefix AFTER looking
the token up in the reference list — after the step that had just failed — so
the bare form matched nothing and the prefix decorated an empty result. The only
way through was a regex, in a mode where the operator had chosen "code" and
explicitly not "pattern".

The token lookup now tries the prefixed form when the bare one is not a known
reference. The list stays the authority: an unknown number still matches
nothing, so the prefix completes references rather than inventing them.

Second bug in the same pass: the blanket prefix also hit codes that came
straight OUT of the list, so a field already holding "D74" produced "DD74" as
soon as a prefix was configured — for both the token lookup and the
description matcher. Branches that yield whole codes are now excluded from it;
the ones that yield a raw capture (regex, whole-field split) still get it.
This commit is contained in:
2026-08-09 09:43:27 +02:00
parent 4352b9aec5
commit 79427ccd18
3 changed files with 103 additions and 1 deletions
+20 -1
View File
@@ -793,6 +793,10 @@ func searchOne(field, matchBy string, re *regexp.Regexp, exact bool, leading, tr
byDesc := predefined && strings.EqualFold(strings.TrimSpace(matchBy), "description")
var found []string
// codesAreFinal: this branch produced references straight from the award's
// LIST, so they are already whole codes. The blanket prefix at the end must
// leave them alone — prefixing a code that is already "D74" yields "DD74".
codesAreFinal := false
switch {
case re != nil:
// Award-level regex: capture group 1 (or whole match) for each hit.
@@ -801,6 +805,7 @@ func searchOne(field, matchBy string, re *regexp.Regexp, exact bool, leading, tr
// Match references by their DESCRIPTION/name appearing in the field
// (e.g. WAJA finds the prefecture name inside the QTH). ExactMatch means
// the field equals the name; otherwise the name is a substring of it.
codesAreFinal = true
up := strings.ToUpper(raw)
for _, nc := range rl.names {
if exact {
@@ -823,9 +828,23 @@ func searchOne(field, matchBy string, re *regexp.Regexp, exact bool, leading, tr
// "Search reference inside the field": look up each token of the field in
// the list — O(tokens), not O(all references) — plus test the few
// references that declare a regex.
codesAreFinal = true
for _, tok := range tokenize(raw) {
if _, ok := rl.byCode[tok]; ok {
found = append(found, tok)
continue
}
// The field may carry the reference WITHOUT the award's letter: a French
// operator writes "74" in STATE while the DDFM codes are "D74". Prefix is
// exactly what that case is for, so try the prefixed form HERE — applying
// it after the lookup (as the blanket pass below used to) could never
// help, because the lookup is the step that failed. Without this, the
// only way to match a bare department was to write a regex, in a mode
// where the operator had chosen "code" and not "pattern".
if prefix != "" {
if _, ok := rl.byCode[prefix+tok]; ok {
found = append(found, prefix+tok)
}
}
}
for _, code := range rl.withPattern {
@@ -839,7 +858,7 @@ func searchOne(field, matchBy string, re *regexp.Regexp, exact bool, leading, tr
// counts each reference separately.
found = splitRefs(raw)
}
if prefix != "" {
if prefix != "" && !codesAreFinal {
for i := range found {
found[i] = prefix + found[i]
}