diff --git a/src/SheetMe.Designer/Diagnostics/EditSmoke.cs b/src/SheetMe.Designer/Diagnostics/EditSmoke.cs index dd8b223..9f6b503 100644 --- a/src/SheetMe.Designer/Diagnostics/EditSmoke.cs +++ b/src/SheetMe.Designer/Diagnostics/EditSmoke.cs @@ -1112,6 +1112,40 @@ public static class EditSmoke $"ValueText='{choiceRow.ValueText}' IsMixed={choiceRow.IsMixed}"); } + // 15-a00000) 태그 행이 사실대로 말하는가. + { + // 카탈로그에 없는 이름은 표식이 붙어야 한다 — 그러나 막지는 않는다. + // 인라인으로 직접 치는 것이 주 경로인데 검증이 하나도 없어서, + // 한 글자 틀려도 조용히 커밋되고 미리보기에서 빈칸으로만 드러났다. + var tagRow2 = new ViewModels.Inspector.TagPickerRowViewModel("데이터 태그", + "데이터 태그 선택", SheetMe.Core.Catalog.LegacyTagCatalog.DataInterfaceTags, + new[] { "PAT_이름" }); + + tagRow2.Initialize("PAT_이름"); + Check("태그: 카탈로그에 있는 이름은 표식이 없다", !tagRow2.IsUnknownTag); + + tagRow2.ValueText = "PAT_이룸"; // 한 글자 오타 + Check("태그: 카탈로그에 없는 이름에 표식이 붙는다", tagRow2.IsUnknownTag); + Check("태그: 그래도 값은 그대로 둔다(사이트 커스텀 태그)", tagRow2.ValueText == "PAT_이룸", + tagRow2.ValueText); + + tagRow2.Initialize(string.Empty); + Check("태그: 빈 값은 표식 대상이 아니다", !tagRow2.IsUnknownTag); + tagRow2.Initialize(null, isMixed: true); + Check("태그: 값이 갈리면 표식을 내지 않는다", !tagRow2.IsUnknownTag); + + // 로그인 사용자 태그 — 피커가 쓰던 5-인자판은 면허·전문의·직종·부서·부서전화를 + // 빈 문자열로 채워, 종이에는 값이 찍히는데 피커에서만 "비어 있습니다"가 나왔다 + var withFields = SheetMe.Core.Catalog.TagPreviewCatalog.FromUser( + "ETC_로그인_의사면허번호", "011825", "홍길동", "010", "02", + "면허12345", "전문의678", "의사", "내과", "1234"); + var withoutFields = SheetMe.Core.Catalog.TagPreviewCatalog.FromUser( + "ETC_로그인_의사면허번호", "011825", "홍길동", "010", "02"); + Check("태그: 면허번호는 10-인자판에서만 값이 나온다", + withFields == "면허12345" && withoutFields.Length == 0, + $"10인자='{withFields}' 5인자='{withoutFields}'"); + } + // 15-a0000) 레이어 목록이 계층과 범위를 말하는가. // // 사용자가 콕 집어 "레이어 구분이 잘 안 된다"고 한 곳이다. 원인은 색이 아니라 산수였다 — diff --git a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs index c5118d9..e221c5b 100644 --- a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs +++ b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs @@ -918,9 +918,10 @@ public sealed class InspectorViewModel : ViewModelBase PropEditorKind.DataInterfaceTag => new TagPickerRowViewModel(def.Label, "데이터 태그 선택 — 자동 채움 원천(bzDataInterface)", LegacyTagCatalog.DataInterfaceTags, LegacyTagUsageCatalog.SuggestFor(def.Key, controlType)), + // 액션은 값이 아니라 동작이다 — 피커가 데이터 태그용 값·사유 칸을 그리면 안 된다 PropEditorKind.DataActionTag => new TagPickerRowViewModel(def.Label, "액션 태그 선택 — 더블클릭/버튼 액션(EN_DataActionTyp)", LegacyTagCatalog.DataActionTags, - LegacyTagUsageCatalog.SuggestFor(def.Key, controlType)), + LegacyTagUsageCatalog.SuggestFor(def.Key, controlType)) { IsActionTag = true }, PropEditorKind.SqlQuery => new QueryRowViewModel(def.Label), PropEditorKind.Mask => new MaskRowViewModel(def.Label), // 배선 대상 목록은 '지금 이 서식'의 데이터소스다 — 런타임도 같은 서식 안에서만 이름을 찾는다 diff --git a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs index 08d9b59..23c91e9 100644 --- a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs +++ b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs @@ -503,6 +503,29 @@ public sealed class TagPickerRowViewModel : PropertyRowViewModel /// 피커 대화상자 제목 public string PickerTitle { get; } + /// + /// 액션 태그 행인가 — 액션은 값이 아니라 동작이라 피커의 값·사유 칸 뜻이 다르다. + /// 데이터 태그용 문구를 그대로 쓰면 149종 전부에서 그 칸이 거짓이 된다. + /// + public bool IsActionTag { get; init; } + + /// + /// 카탈로그에 없는 이름인가 — 값은 건드리지 않고 표식만 낸다. + /// + /// 인라인 완성으로 직접 치는 것이 이 행의 주 경로인데 거기에는 검증이 하나도 없었다. + /// 'PAT_이룸' 처럼 한 글자 틀리면 아무 표시 없이 커밋되고, 그 사실은 미리보기에서 + /// 빈칸으로만 드러난다 — 빈칸의 원인이 오타인지 이 환자에게 자료가 없어서인지 가릴 수 없다. + /// + /// 막지는 않는다. 사이트마다 카탈로그에 없는 커스텀 태그를 쓰기 때문이다(글꼴 행과 같은 규칙). + /// + public bool IsUnknownTag => !IsMixed && ValueText.Length > 0 + && !string.Equals(ValueText, "None", StringComparison.Ordinal) + && !Choices.Contains(ValueText, StringComparer.Ordinal); + + /// 경고 문구 — 툴팁으로 보인다 + public string UnknownTagHint => $"'{ValueText}' 은(는) 카탈로그에 없는 이름입니다. " + + "사이트 전용 태그면 그대로 두고, 오타면 고치세요 — 값이 안 나오면 종이에서 빈칸이 됩니다."; + /// /// 한 줄에 하나꼴로 접히는 긴 한글 태그를 몇 개까지 깔지. /// 인스펙터 폭이 좁아 칩이 세로로 쌓인다 — 8개를 넘기면 다른 속성이 화면 밖으로 밀린다. @@ -676,13 +699,16 @@ public sealed class TagPickerRowViewModel : PropertyRowViewModel if (e.PropertyName == nameof(ValueText)) { OnPropertyChanged(nameof(ShowSuggestions)); + // 값이 바뀌면 '카탈로그에 없음' 표식도 다시 판정한다 + OnPropertyChanged(nameof(IsUnknownTag)); + OnPropertyChanged(nameof(UnknownTagHint)); } }; } private void OnBrowse() { - var dialog = new Views.TagPickerDialogView(PickerTitle, Choices, ValueText, preferredAll) + var dialog = new Views.TagPickerDialogView(PickerTitle, Choices, ValueText, preferredAll, IsActionTag) { Owner = System.Windows.Application.Current.MainWindow, }; diff --git a/src/SheetMe.Designer/Views/InspectorView.xaml b/src/SheetMe.Designer/Views/InspectorView.xaml index c9133cf..49654ac 100644 --- a/src/SheetMe.Designer/Views/InspectorView.xaml +++ b/src/SheetMe.Designer/Views/InspectorView.xaml @@ -622,6 +622,16 @@ private void ShowTagValue(string tag) { - if (previewStore is null || !previewStore.CanUseDb) - { - InfoValueBlock.Visibility = Visibility.Collapsed; - return; - } InfoValueBlock.Visibility = Visibility.Visible; + // 액션 태그는 값이 아니다. 더블클릭·버튼이 실행하는 동작이라 + // "환자 정보가 있어야 값이 나옵니다" 같은 데이터 태그용 사유를 붙이면 + // 149종 전부에서 이 칸이 통째로 거짓말을 한다. + if (isActionKind) + { + InfoValue.Text = "값이 아니라 동작입니다"; + InfoValueNote.Text = "더블클릭·버튼에서 실행됩니다 — 종이에 찍히는 값이 아닙니다."; + return; + } + + // 값을 만들 수 없는 이유는 DB 없이도 안다(정적 판정이다). + // 전에는 DB 가드가 이 위에 있어서, 접속이 없으면 사유까지 통째로 감춰졌다 — + // 정작 그 사유가 가장 필요한 상황(접속 안 된 단말에서 서식을 훑을 때)에 안 보였다. if (!TagPreviewCatalog.CanResolve(tag)) { InfoValue.Text = "값을 만들 수 없습니다"; @@ -309,12 +325,29 @@ public partial class TagPickerDialogView : Window return; } + // 여기서부터는 실제 값이 필요하다 — 그때만 DB 를 따진다. + // (진단 스크린샷은 DB 없이 이 창을 띄우므로 이 가드가 없으면 늘 "값을 읽지 못했습니다"가 찍힌다) + if (previewStore is null || !previewStore.CanUseDb) + { + InfoValue.Text = "값은 접속된 단말에서 확인됩니다"; + InfoValueNote.Text = TagPreviewCatalog.SourceText(tag); + return; + } + previewContext ??= previewStore.Read(UserSession.Current.UidCod, UserSession.Current.UidNam); var context = previewContext.Value; var value = TagPreviewCatalog.KindOf(tag) == TagPreviewKind.Clock ? TagPreviewCatalog.FromClock(tag, context.Minute12) + // 본판(10-인자)으로 부른다. 5-인자 위임판은 면허·전문의·직종·부서·부서전화를 + // 빈 문자열로 채운다 — 그래서 의사면허번호·전문의번호·직종·부서·부서전화 계열이 + // 피커에서는 "이 계정에는 값이 비어 있습니다"로 보이는데 종이에는 실제 값이 찍혔다. + // 값이 나오는 사용자 태그 중 다섯 갈래가 피커에서만 거짓말을 하고 있었다. + // 이 값들은 로그인 세션이 이미 들고 있다(HisUser 15컬럼) — 조회를 더 하지 않는다. : TagPreviewCatalog.FromUser(tag, context.UidCod, context.UidNam, - context.MobilePhone, context.OfficePhone); + context.MobilePhone, context.OfficePhone, + UserSession.Current.LicenseNumber, UserSession.Current.SpecialistNumber, + UserSession.Current.JobName, UserSession.Current.DepNam, + UserSession.Current.DepPhone); if (value.Length > 0) {