通勤認定エクセルツール対応13 M1 対応2
This commit is contained in:
41
AGENTS.md
41
AGENTS.md
@@ -25,13 +25,46 @@
|
||||
- ❌ Forbidden: hardcoded file paths or connection strings. Use config sheet or constants module
|
||||
- ❌ Forbidden: non-English comments (Chinese / Japanese / Korean)
|
||||
|
||||
## Project Structure
|
||||
### Code Review Checklist
|
||||
|
||||
**Before editing**
|
||||
- Read the entire function from first line to last line (not just the lines you plan to change)
|
||||
- Find all call sites that reference the function or data structure you are about to modify
|
||||
|
||||
**After editing**
|
||||
- Read the entire function from first line to last line again
|
||||
- Verify: are all variables declared, are scopes correct, is the logic complete
|
||||
- Go through every call site one by one to confirm compatibility
|
||||
|
||||
**When the user asks "Are you sure?"**
|
||||
- Actually re-read the relevant code instead of saying "looks fine"
|
||||
- grep all references and verify them one by one
|
||||
|
||||
**Key principle**
|
||||
- Never skip context — do not look at only the diff and call it done
|
||||
- The compiler cannot catch logic errors or missing variable declarations — only human review can
|
||||
- Read the whole function, read all call sites — no exceptions
|
||||
|
||||
## Design Document
|
||||
|
||||
**Primary reference**: `documents/Tukin_Design_Document.md`
|
||||
|
||||
Before editing any sheet class or cache logic, **read the design document first**. It contains:
|
||||
- Sheet column layouts with HeaderRow/StartRow/StartCol/EndCol per `RefreshSheetDict`
|
||||
- Cache key/value structures for every CACHE_* constant
|
||||
- Data flow diagrams for C1 editing cascade and CSV import
|
||||
- Column layout reference (C1 has 58 columns, C to BG)
|
||||
- All known issues and their fix status
|
||||
|
||||
**Rule**: Do not guess sheet layout or cache structure. Look it up in the design document.
|
||||
|
||||
vba/
|
||||
AGENTS.md, README.md, .gitignore, LICENSE
|
||||
通勤手当テンプレート2026xxxx.xlsm (latest date version)
|
||||
data/ CSV master data (14 files)
|
||||
documents/ design docs (3 files)
|
||||
documents/ design docs
|
||||
Tukin_Design_Document.md — master design doc: sheet layouts, cache architecture, column reference, data flow
|
||||
checklist-2026-05-27.md — audit checklist (historical)
|
||||
sql/ DB definitions (4 files)
|
||||
src/sh/
|
||||
juk/ address module
|
||||
@@ -55,13 +88,13 @@ vba/
|
||||
M2.cls (400 lines) - section detail master
|
||||
O1.cls (5 lines) - address master
|
||||
O2.cls (6 lines) - sender master (507)
|
||||
O3.cls (61 lines) - master 225
|
||||
O3.cls (61 lines) - (220) notification reason
|
||||
T1.cls (54 lines) - commutation pass master
|
||||
T2.cls (114 lines) - ticket master
|
||||
T3.cls (74 lines) - master 246
|
||||
Z1.cls (64 lines) - transport master (222)
|
||||
Z2.cls (54 lines) - decision master (223)
|
||||
Z3.cls (57 lines) - monthly amount decision master (224)
|
||||
Z4.cls - master Z4
|
||||
Z4.cls - (221) route station name
|
||||
|
||||
Sheet class prefixes: C=commuter editing, M=section master, O=other, T=commuter route, Z=master config
|
||||
@@ -1,81 +0,0 @@
|
||||
# Tukin_C1 ユーザーアクションドキュメント
|
||||
|
||||
## 列アクションのマッピング
|
||||
|
||||
### C列 (職員番号)
|
||||
- **トリガー条件**: C列 >= 第7行、内容変化
|
||||
- **アクション**:
|
||||
- 内容が空 → `ClearRowData` で一行クリア
|
||||
- 内容あり → `FillAddressFromO1` で住所ドロップダウン + 4区間の交通機関ドロップダウン生成
|
||||
|
||||
---
|
||||
|
||||
### 区間1
|
||||
|
||||
| 列 | アクション | トリガー条件 | 処理ロジック |
|
||||
|---|---|---|---|
|
||||
| **T** (交通機関) | 交通機関ドロップダウン変化 | Column=20 | `CreateZ1StationDropdown` → U列(発)ドロップダウン生成 |
|
||||
| **U** (利用区間発) | 発ドロップダウン変化 | Column=21 | `CreateM1KukanDDropdown` → V列(着)ドロップダウン生成 |
|
||||
| **S** (区間コード) | 区間コード入力 | Column=19 | T列ドロップダウン生成 → T列値ありの場合U,Vを填充 + W列(券種)ドロップダウン生成 |
|
||||
| **W** (券種) | 券種ドロップダウン変化 | Column=23 | `CreateM2CodeDropdown` → X列(コード)ドロップダウン生成 |
|
||||
|
||||
---
|
||||
|
||||
### 区間2
|
||||
|
||||
| 列 | アクション | トリガー条件 | 処理ロジック |
|
||||
|---|---|---|---|
|
||||
| **AA** (交通機関) | 交通機関ドロップダウン変化 | Column=27 | `CreateZ1StationDropdown` → AB列(発)ドロップダウン生成 |
|
||||
| **AB** (利用区間発) | 発ドロップダウン変化 | Column=28 | `CreateM1KukanDDropdown` → AC列(着)ドロップダウン生成 |
|
||||
| **Z** (区間コード) | 区間コード入力 | Column=26 | AA列ドロップダウン生成 → AA列値ありの場合AB,ACを填充 + AD列(券種)ドロップダウン生成 |
|
||||
| **AD** (券種) | 券種ドロップダウン変化 | Column=30 | `CreateM2CodeDropdown` → AE列(コード)ドロップダウン生成 |
|
||||
|
||||
---
|
||||
|
||||
### 区間3
|
||||
|
||||
| 列 | アクション | トリガー条件 | 処理ロジック |
|
||||
|---|---|---|---|
|
||||
| **AH** (交通機関) | 交通機関ドロップダウン変化 | Column=34 | `CreateZ1StationDropdown` → AI列(発)ドロップダウン生成 |
|
||||
| **AI** (利用区間発) | 発ドロップダウン変化 | Column=35 | `CreateM1KukanDDropdown` → AJ列(着)ドロップダウン生成 |
|
||||
| **AG** (区間コード) | 区間コード入力 | Column=33 | AH列ドロップダウン生成 → AH列値ありの場合AI,AJを填充 + AK列(券種)ドロップダウン生成 |
|
||||
| **AK** (券種) | 券種ドロップダウン変化 | Column=37 | `CreateM2CodeDropdown` → AL列(コード)ドロップダウン生成 |
|
||||
|
||||
---
|
||||
|
||||
### 区間4
|
||||
|
||||
| 列 | アクション | トリガー条件 | 処理ロジック |
|
||||
|---|---|---|---|
|
||||
| **AO** (交通機関) | 交通機関ドロップダウン変化 | Column=41 | `CreateZ1StationDropdown` → AP列(発)ドロップダウン生成 |
|
||||
| **AP** (利用区間発) | 発ドロップダウン変化 | Column=42 | `CreateM1KukanDDropdown` → AQ列(着)ドロップダウン生成 |
|
||||
| **AN** (区間コード) | 区間コード入力 | Column=40 | AO列ドロップダウン生成 → AO列値ありの場合AP,AQを填充 + AR列(券種)ドロップダウン生成 |
|
||||
| **AR** (券種) | 券種ドロップダウン変化 | Column=44 | `CreateM2CodeDropdown` → AS列(コード)ドロップダウン生成 |
|
||||
|
||||
---
|
||||
|
||||
## メソッド一覧
|
||||
|
||||
| メソッド名 | 機能 |
|
||||
|---|---|
|
||||
| `FillAddressFromO1` | 職員番号(C列)をキーとしてO1キャッシュから住所(I列)ドロップダウン生成 |
|
||||
| `CreateZ1TransportDropdown` | 交通機関ドロップダウン生成 |
|
||||
| `CreateZ1StationDropdown` | 交通機関をキーとしてZ1キャッシュから発ドロップダウン生成 |
|
||||
| `CreateM1KukanDDropdown` | 交通機関+発をキーとしてM1KukanDキャッシュから着ドロップダウン生成 |
|
||||
| `FillKukanFromM1` | 区間コードをキーとしてM1キャッシュから区間情報(T/U/V等)填充 |
|
||||
| `CreateM2Dropdown` | 区間コードをキーとして券種ドロップダウン生成 |
|
||||
| `CreateM2CodeDropdown` | 区間コード+券種をキーとしてコードドロップダウン生成 |
|
||||
| `ClearRowData` | 一行データクリア |
|
||||
| `ClearKukanValidation` | 指定列の検証ドロップダウンをクリア |
|
||||
|
||||
---
|
||||
|
||||
## キャッシュ依存
|
||||
|
||||
| キャッシュ | 用途 |
|
||||
|---|---|
|
||||
| `o1Cache` | 職員番号 → 住所 |
|
||||
| `z1Cache` | 交通機関 → 駅 |
|
||||
| `m1KukanDCache` | 交通機関+発 → 着 |
|
||||
| `m1Cache` | 区間コード → 区間情報 |
|
||||
| `m2Cache` | 区間コード+券種 → コード |
|
||||
@@ -1,71 +0,0 @@
|
||||
### 届出情報
|
||||
|列|C列|D列|E列|F列|G列|H列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|職員番号|事実発生年月日|提出年月日|受理年月日|届出の事由コード|届出の備考|
|
||||
|データ型|8|日付|日付|日付|Enum|文字列|
|
||||
|
||||
### 住所情報
|
||||
|列|I列|J列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|住所1|住所2|
|
||||
|データ型|文字列|文字列|
|
||||
|
||||
### 出勤情報
|
||||
|列|K列|L列|M列|N列|O列|
|
||||
|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|運賃改正・法改正年月日|出勤予定日数|往復区分|交替制|算出式|
|
||||
|データ型|日付|数字|Enum|Enum|文字列|
|
||||
|
||||
### 自動車等情報
|
||||
|列|P列|Q列|R列|
|
||||
|--------|--------|--------|--------|
|
||||
|ヘッダ|自動車等使用距離|自動車等支給額|自動車等駐車場代|
|
||||
|データ型|数字|数字|数字|
|
||||
|
||||
### 区間1情報
|
||||
|列|S列|T列|U列|V列|W列|X列|Y列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間1区間コード|区間1交通機関|区間1発|区間1着|区間1券種|区間1コード|区間1支給開始年月|
|
||||
|データ型|5|3|文字列|文字列|Enum|3|日付|
|
||||
|
||||
### 区間2情報
|
||||
|列|Z列|AA列|AB列|AC列|AD列|AE列|AF列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間2区間コード|区間2交通機関|区間2発|区間2着|区間2券種|区間2コード|区間2支給開始年月|
|
||||
|データ型|5|3|文字列|文字列|Enum|3|日付|
|
||||
|
||||
### 区間3情報
|
||||
|列|AG列|AH列|AI列|AJ列|AK列|AL列|AM列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間3区間コード|区間3交通機関|区間3発|区間3着|区間3券種|区間3コード|区間3支給開始年月|
|
||||
|データ型|5|3|文字列|文字列|Enum|3|日付|
|
||||
|
||||
### 区間4情報
|
||||
|列|AN列|AO列|AP列|AQ列|AR列|AS列|AT列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間4区間コード|区間4交通機関|区間4発|区間4着|区間4券種|区間4コード|区間4支給開始年月|
|
||||
|データ型|5|3|文字列|文字列|Enum|3|日付|
|
||||
|
||||
### 決定事項情報
|
||||
|列|AU列|AV列|AW列|AX列|
|
||||
|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|決定事項区分コード|非該当の理由|非該当者認定簿出力区分|手当月額の決定区分コード|
|
||||
|データ型|Enum|文字列|Enum|Enum|
|
||||
|
||||
### 備考情報
|
||||
|列|AY列|AZ列|BA列|
|
||||
|--------|--------|--------|--------|
|
||||
|ヘッダ|支給の始期|備考|所属コード|
|
||||
|データ型|日付|文字列|文字列|
|
||||
|
||||
### 認定情報
|
||||
|列|BB列|BC列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|認定年月日|(各庁の長)官職コード|
|
||||
|データ型|日付|ENUM|
|
||||
|
||||
### エラーメッセージ
|
||||
|列|BD列|
|
||||
|--------|--------|
|
||||
|ヘッダ|エラーメッセージ|
|
||||
|データ型|文字列|
|
||||
@@ -1,45 +0,0 @@
|
||||
# Tukin キャッシュ マッピング
|
||||
|
||||
## キャッシュ一覧
|
||||
|
||||
### m1Cache
|
||||
|列|C列|D列|E列|F列|G列|I列|L列|
|
||||
|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間コード|交通機関区分|交通機関名称|利用区間発名|利用区間着名|券種|運賃|
|
||||
|
||||
### m1KukanDCache
|
||||
|列|D列|F列|G列|
|
||||
|--------|--------|--------|--------|
|
||||
|ヘッダ|交通機関区分|利用区間発名|利用区間着名|
|
||||
|
||||
### m2Cache
|
||||
|列|C列|I列|J列|K列|
|
||||
|--------|--------|--------|--------|--------|
|
||||
|ヘッダ|区間コード|券種|コード|名称|
|
||||
|
||||
### z1Cache (222)交通機関マスタ
|
||||
|列|C列|D列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|区分|交通機関名称|
|
||||
|
||||
### z2Cache (223)通勤_決定事項区分一覧
|
||||
|列|C列|D列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|区分|決定事項|
|
||||
|
||||
### z3Cache (224)通勤_手当月額の決定区分一覧
|
||||
|列|C列|D列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|区分|手当月額の決定|
|
||||
|
||||
### t1Cache
|
||||
|
||||
### o1Cache 住所情報
|
||||
|列|C列|E列|F列|
|
||||
|--------|--------|--------|--------|
|
||||
|ヘッダ|職員番号|住所1|住所2|
|
||||
|
||||
### o2Cache (507)発信者一覧
|
||||
|列|C列|D列|
|
||||
|--------|--------|--------|
|
||||
|ヘッダ|区分|官職名称|
|
||||
373
documents/Tukin_Design_Document.md
Normal file
373
documents/Tukin_Design_Document.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# Commuter Allowance Editor — Design Document
|
||||
|
||||
## 1. Project Overview
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Application | Excel 2021 (.xlsm) |
|
||||
| Purpose | Edit commuter certification by referencing master data |
|
||||
| Module | tuk (通勤 = commutation) |
|
||||
| Entry Point | メインメニュー (Main Menu sheet) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Sheet Inventory
|
||||
|
||||
### 2.1 Editor Sheets
|
||||
|
||||
#### C1 — 通勤手当CSV編集 (Commuter Allowance Editor)
|
||||
- **HeaderRow**: 6 | **StartRow**: 8 | **StartCol**: C | **EndCol**: BC
|
||||
- **Encoding**: shift_jis | **HasHeader**: Yes
|
||||
- **Role**: Main editing sheet — direct cell editing only, no CSV import
|
||||
- **Key columns**:
|
||||
- C: 職員番号 (Employee ID) — triggers address + transport dropdowns
|
||||
- S/AA/AI/AQ: 区間N区間コード (Section N Route Code) — triggers fill from M1 + dropdown cascade
|
||||
- T/AB/AJ/AR: 区間N交通機関 (Section N Transport Type)
|
||||
- U/AC/AK/AS: 区間N発 (Section N Departure Station)
|
||||
- V/AD/AL/AT: 区間N着 (Section N Arrival Station)
|
||||
- W/AE/AM/AU: 区間N券種 (Section N Ticket Type)
|
||||
- X/AF/AN/AV: 区間Nコード (Section N Code)
|
||||
- AY: 決定事項区分コード (Determination Category)
|
||||
- BB: 手当月額の決定区分コード (Monthly Amount Decision Category)
|
||||
|
||||
#### M1 — 区間メンテナンス (Route Maintenance)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: N
|
||||
- **Encoding**: shift_jis | **HasHeader**: Yes
|
||||
- **Role**: Route master — defines routes (code → transport + departure + arrival + fare)
|
||||
- **Key columns**:
|
||||
- C: 利用区間コード (Route Code)
|
||||
- D: 交通機関区分 (Transport Category)
|
||||
- E: 交通機関名称 (Transport Name)
|
||||
- F: 利用区間発名 (Departure Station)
|
||||
- G: 利用区間着名 (Arrival Station)
|
||||
- I: 運賃 (Fare)
|
||||
- J: 現金の場合の1箇月運賃 (Monthly Fare Cash)
|
||||
- K: 連絡 (Connection)
|
||||
- L: 特別料金区分 (Special Fare Category)
|
||||
- M: 特別料金券種 (Special Fare Ticket Type)
|
||||
- N: 特別料金負担額 (Special Fare Burden Amount)
|
||||
|
||||
#### M2 — 区間詳細メンテナンス (Route Detail Maintenance)
|
||||
- **HeaderRow**: 6 | **StartRow**: 8 | **StartCol**: C | **EndCol**: R
|
||||
- **Encoding**: shift_jis | **HasHeader**: Yes
|
||||
- **Role**: Route detail master — ticket types and pricing per route code
|
||||
- **Key columns**:
|
||||
- C: 利用区間コード (Route Code) — join key with M1
|
||||
- I: 券種 (Ticket Type: 0=普通, 1=定期券, 2=回数券, 3=プリペイドカード)
|
||||
- J: コード (Ticket Code)
|
||||
- K: 名称 (Ticket Name)
|
||||
- L: 1箇月運賃/販売額 (Monthly Fare / Sales Price)
|
||||
- M: 定期額/券1(額)/利用額 (Monthly / Ticket1 Amount / Usage Amount)
|
||||
- N: 定期支給期間/券1(枚)/特別料金 (Monthly Issue Period / Ticket1 Qty / Special Fare)
|
||||
- O: 特別料金/券2(額) (Special Fare / Ticket2 Amount)
|
||||
- P: 券2(枚) (Ticket2 Quantity)
|
||||
- Q: 端数(額) (Fractional Amount)
|
||||
- R: 特別料金 (Special Fare)
|
||||
|
||||
### 2.2 Master / Config Sheets (Z-series)
|
||||
|
||||
#### Z1 — (222)交通機関マスタ (Transport Master)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: I
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Transport type catalog (dropdown source for C1 T/AB/AJ/AR)
|
||||
- **Columns**: C=区分 (code), D=交通機関名称 (name), E=ポップアップ名称, F=コメント, G=略式名称, H=表示しない, I=略称
|
||||
|
||||
#### Z2 — (223)通勤_決定事項区分一覧 (Determination Category List)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: G
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Determination categories for AY column dropdown
|
||||
- **Columns**: C=区分 (code), D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない
|
||||
|
||||
#### Z3 — (224)通勤_手当月額の決定区分一覧 (Monthly Amount Decision Category List)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: H
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Monthly amount decision categories for BB column dropdown
|
||||
- **Columns**: C=区分 (code), D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない, H=名称2
|
||||
|
||||
#### Z4 — (221)利用区間発着名区分 (Route Station Name Category)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: H
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Station name catalog grouped by line (rosen). Used for M1 F/G column dropdowns.
|
||||
- **Columns**: C=区分 (station code), D=画面用名称/駅名 (station display name), E=ポップアップ名称, F=コメント/路線名 (line name), G=正式名称, H=表示しない
|
||||
- **Note**: E column (ポップアップ名称) is populated but currently ignored by code. Code reads F (路線名) and D (駅名) only.
|
||||
|
||||
### 2.3 Ticket Master Sheets (T-series)
|
||||
|
||||
#### T1 — (244)通勤_定期券名称区分一覧 (Commuter Pass Name Category)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: G
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Periodic commuter pass types (dropdown source for W/AE/AM/AU)
|
||||
- **Columns**: C=区分 (code), D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない
|
||||
|
||||
#### T2 — (245)通勤_回数券名称区分一覧 (Fare Ticket Name Category)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: M
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Frequency ticket types with pricing breakdown
|
||||
- **Columns**: C=区分, D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない, H=販売額, I=券1(額), J=券1(枚), K=券2(額), L=券2(枚), M=端数額
|
||||
- **Feature**: ZeroFillCols = H,I,J,K,L,M — when C column (区分) is edited and H~M are empty, they are auto-filled with "0"
|
||||
|
||||
#### T3 — (246)通勤_プリペイドカード名称区分一覧 (Prepaid Card Name Category)
|
||||
- **HeaderRow**: 5 | **StartRow**: 7 | **StartCol**: C | **EndCol**: I
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Prepaid card types with pricing
|
||||
- **Columns**: C=区分, D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない, H=販売額, I=利用額
|
||||
|
||||
### 2.4 Other / Reference Sheets
|
||||
|
||||
#### O1 — 住所情報 (Address Information)
|
||||
- **HeaderRow**: 5 | **StartRow**: 6 | **StartCol**: C | **EndCol**: F
|
||||
- **Encoding**: shift_jis | **HasHeader**: Yes
|
||||
- **Role**: Employee address lookup (keyed by employee number)
|
||||
- **Columns**: C=社員番号, D=更新日, E=住所1, F=住所2
|
||||
|
||||
#### O2 — (507)発信者一覧 (Sender List)
|
||||
- **HeaderRow**: 5 | **StartRow**: 6 | **StartCol**: C | **EndCol**: O
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Sender/official name list for dropdown (O2Cache)
|
||||
- **Columns**: C=区分, D=画面用名称, E=ポップアップ用名称, F=コメント, G=表示しない, H=発信者名称, I=発信者氏名, J=所属区分(From), K=所属区分(To), L=研究科区分, M=ソート順(From), N=ソート順(To), O=諸手当認定者区分
|
||||
|
||||
#### O3 — (220)通勤手当届出事由区分 (Notification Reason Category)
|
||||
- **HeaderRow**: 5 | **StartRow**: 6 | **StartCol**: C | **EndCol**: I
|
||||
- **Encoding**: utf-8 | **HasHeader**: No
|
||||
- **Role**: Notification reason categories for C1 G column dropdown (Todoke)
|
||||
- **Columns**: C=区分, D=画面用名称, E=ポップアップ名称, F=コメント, G=表示しない, H=備考, I=からまで
|
||||
|
||||
#### Enum — Dropdown Enum Values
|
||||
- **Role**: Stores all enum values used in dropdown validation
|
||||
- **Sections** (each keyed by column position):
|
||||
- Col A (KeyCol=1): 特別料金区分 (tokubetuList: 普通=0, 定期券=1, 回数券=2, プリペイドカード=3)
|
||||
- Col C (KeyCol=3): 券種 (kenshuList)
|
||||
- Col F (KeyCol=6): 連絡 (renrakuList)
|
||||
- Col H (KeyCol=8): 往復区分 (oufukuList: 1=片道, 2=往復)
|
||||
- Col K (KeyCol=11): 交替制 (koutaiList: 0=非該当, 1=該当)
|
||||
- Col N (KeyCol=14): 非該当者認定簿出力区分 (higaitouList: 1=出力, 2=非出力)
|
||||
- Col Q (KeyCol=17): エラーメッセージ (errorList)
|
||||
|
||||
#### Caches — Pre-rendered Dropdown Cache
|
||||
- **Role**: Stores pre-built validation lists for named-range based dropdowns
|
||||
- **Layout**: Each row is a pre-formatted "code:name" string used as named-range source
|
||||
|
||||
---
|
||||
|
||||
## 3. Cache Architecture
|
||||
|
||||
### 3.1 Cache Dictionary Keys
|
||||
|
||||
| Constant | Sheet | Structure | Key | Value |
|
||||
|----------|-------|-----------|-----|-------|
|
||||
| `CACHE_Z1` | Z1 | Dict(code → Array(name)) | transport code | Array(transport name) |
|
||||
| `CACHE_Z2` | Z2 | Dict(code → Array(name)) | determination code | Array(display name) |
|
||||
| `CACHE_Z3` | Z3 | Dict(code → Array(name)) | decision code | Array(display name) |
|
||||
| `CACHE_Z4ROSEN` | Z4 | Dict(rosen → Dict(station → True)) | line name | Dict of all stations on that line |
|
||||
| `CACHE_T1` | T1 | Dict(code → Array(name)) | ticket type code | Array(display name) |
|
||||
| `CACHE_T2` | T2 | Dict(code → Array(all fields)) | ticket code | Array(C,D,E,F,G,H,I,J,K,L,M) |
|
||||
| `CACHE_T3` | T3 | Dict(code → Array(all fields)) | card code | Array(C,D,E,F,G,H,I) |
|
||||
| `CACHE_O1` | O1 | Dict(empNo → Array(addr1, addr2)) | employee number | Array(address1, address2) |
|
||||
| `CACHE_O2` | O2 | Dict(code → Array(name)) | sender code | Array(official name) |
|
||||
| `CACHE_O3` | O3 | Dict(code → Array(name)) | reason code | Array(display name) |
|
||||
| `CACHE_M1` | M1 | Dict(code → Array(all cols)) | route code | Array(C,D,E,F,G,H,I,J,K,L,M,N) |
|
||||
| `CACHE_M2` | M2 | Dict(code → Dict(ticketType → Array(detail))) | route code | Dict of ticket type → Array(I,J,K,L,M,N,O,P,Q,R) |
|
||||
|
||||
### 3.2 Special Enum Caches
|
||||
|
||||
| Constant | Source | Key | Value |
|
||||
|----------|--------|-----|-------|
|
||||
| `tokubetuList` | Enum sheet Col A | int (0-3) | name string |
|
||||
| `kenshuList` | Enum sheet Col C | int | name string |
|
||||
| `renrakuList` | Enum sheet Col F | int | name string |
|
||||
| `oufukuList` | Enum sheet Col H | code | name string |
|
||||
| `koutaiList` | Enum sheet Col K | code | name string |
|
||||
| `higaitouList` | Enum sheet Col N | code | name string |
|
||||
| `errorList` | Enum sheet Col Q | error code | error message |
|
||||
|
||||
### 3.3 m1KukanDCache (Special Derived Cache)
|
||||
|
||||
- **Type**: Dict(transportCode & "|" & fromStation → Dict(toStation → True))
|
||||
- **Purpose**: Fast lookup of valid arrival stations given transport + departure
|
||||
- **Derivation**: Built from M1 sheet at runtime: groups M1 rows by D(transport) + F(fromStation), keys are G(toStation)
|
||||
- **Used by**: `BuildZ4StationToDropdown` → C1 M1 KukanD cascade
|
||||
|
||||
---
|
||||
|
||||
## 4. Module Architecture
|
||||
|
||||
### 4.1 Module Summary
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|---------------|
|
||||
| `Common_Constants` | Error code constants (1001-1009, 2001+) |
|
||||
| `Common_Global_Cache` | All cache loading, refresh, and lookup. Sheet config definitions (RefreshSheetDict). Global Scripting.Dictionary objects. |
|
||||
| `Common_Functions` | CSV helpers (GetCSVHeader, CleanCSVField), validation helpers (CheckRequired, CheckAlphanumeric, etc.), utility functions (FormatDateInput, GetCode, MakeSelect) |
|
||||
| `Common_Selector` | Dropdown builder functions (BuildTransportList, BuildTodokeList, etc.) and Z4 cascade dropdowns (BuildZ4StationFromDropdown, BuildZ4StationToDropdown) |
|
||||
| `Common_File_Utils` | CSV file read/write (ReadCSV, WriteCSV), BOM handling |
|
||||
| `Common_Button` | Button action handlers: CSV_Import, Validation, CSV_Export, Sort, Filter, Fit. RunValidationSilent wrapper. |
|
||||
| `Common_Shape` | Icon/shape alignment utilities (AlignIconsByCenter) |
|
||||
|
||||
### 4.2 Sheet Class Summary
|
||||
|
||||
| Class | Sheet | Key Methods | Notes |
|
||||
|-------|-------|-------------|-------|
|
||||
| `C1` | C1 | Worksheet_Change, FillAddressFromO1, FillTransportFromM1KukanD, FillDepartureFromM1KukanD, FillArrivalFromM1KukanD, FillKukanFromM1, FillCodeFromM2, ValidateRow | Main editor. No CSV import. |
|
||||
| `M1` | M1 | Worksheet_Change, Validate, BuildM1StationDropdown | Route master with Z4 cascade dropdowns |
|
||||
| `M2` | M2 | Worksheet_Change, Validate | Route detail master, ticket pricing |
|
||||
| `T1` | T1 | Validate | Periodic pass name master |
|
||||
| `T2` | T2 | Worksheet_Change, Validate | Frequency ticket master with ZeroFillCols feature |
|
||||
| `T3` | T3 | Validate | Prepaid card master |
|
||||
| `Z1` | Z1 | Validate | Transport type master |
|
||||
| `Z2` | Z2 | Validate | Determination category master |
|
||||
| `Z3` | Z3 | Validate | Monthly amount decision master |
|
||||
| `Z4` | Z4 | Validate | Station name / line master |
|
||||
| `O1` | O1 | Validate | Employee address master (empty stub) |
|
||||
| `O2` | O2 | Validate | Sender list (empty stub) |
|
||||
| `O3` | O3 | Validate | Notification reason master (empty stub) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow
|
||||
|
||||
### 5.1 C1 Editing Flow (4 Kukan Sections)
|
||||
|
||||
```
|
||||
User edits C (職員番号)
|
||||
→ FillAddressFromO1: O1Cache lookup → populate I/J dropdowns
|
||||
→ BuildTransportList → T/AB/AJ/AR dropdown (Z1Cache)
|
||||
|
||||
User selects T (交通機関)
|
||||
→ BuildZ1StationDropdown → U/AC/AK/AS dropdown (Z1Cache)
|
||||
|
||||
User selects U (発)
|
||||
→ BuildZ4StationFromDropdown → F column (from station) dropdown (Z4RosenCache filtered by transport)
|
||||
OR: BuildM1KukanDDropdown → V/AD/AL/AT dropdown (m1KukanDCache: transport+from → to stations)
|
||||
|
||||
User selects S (区間コード)
|
||||
→ FillKukanFromM1: M1Cache lookup → fills T/U/V/W/Y with pre-defined values
|
||||
→ BuildM2Dropdown → W/AE/AM/AU dropdown (M2Cache by route code)
|
||||
→ BuildM2CodeDropdown → X/AF/AN/AV dropdown (M2Cache by route+券種)
|
||||
|
||||
User selects W (券種)
|
||||
→ BuildM2CodeDropdown → X/AF/AN/AV dropdown (M2Cache: route+券種 → code)
|
||||
|
||||
User edits X (コード)
|
||||
→ FillCodeFromM2: M2Cache lookup → fills AF/AG/AH with M2 details
|
||||
```
|
||||
|
||||
### 5.2 CSV Import Flow
|
||||
|
||||
```
|
||||
CSV_Import_Button clicked
|
||||
→ User selects CSV file
|
||||
→ ReadCSV (Common_File_Utils): shift_jis decode, BOM strip
|
||||
→ GetCSVHeader: extract header row
|
||||
→ LoadLookup: build/refresh cache for target sheet
|
||||
→ DO_CSV_Import: parse rows, write to sheet cells
|
||||
→ RunValidationSilent: call Sheet.Validate per row
|
||||
→ HandleError on validation failure
|
||||
```
|
||||
|
||||
### 5.3 Validation Flow
|
||||
|
||||
```
|
||||
RunValidationSilent(sheet) → for each data row
|
||||
→ Sheet.Validate(ws, rowNum, lastDataRow)
|
||||
→ On error: Err.Raise → caught by HandleError in caller
|
||||
→ Returns error count
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Key Constants
|
||||
|
||||
### 6.1 CACHE_ Constants (defined in Common_Global_Cache)
|
||||
|
||||
```
|
||||
CACHE_Z4ROSEN = "Z4Rosen"
|
||||
CACHE_T1 = "T1"
|
||||
CACHE_T2 = "T2"
|
||||
CACHE_T3 = "T3"
|
||||
CACHE_O1 = "O1"
|
||||
CACHE_O2 = "O2"
|
||||
CACHE_O3 = "O3"
|
||||
CACHE_M1 = "M1"
|
||||
CACHE_M2 = "M2"
|
||||
```
|
||||
|
||||
### 6.2 Validation Error Codes
|
||||
|
||||
```
|
||||
1001: ERR_CACHE_NOT_FOUND
|
||||
1002: ERR_CACHE_EMPTY
|
||||
1003: ERR_VALIDATION_FAILED
|
||||
1004: ERR_CONFIG_NOT_FOUND
|
||||
1005: ERR_CONFIG_INVALID
|
||||
1006: ERR_CONFIG_EMPTY_PARAM
|
||||
1007: ERR_SHEET_MISSING
|
||||
2001: ERR_VALIDATION
|
||||
5001-5009: File/CSV errors
|
||||
```
|
||||
|
||||
### 6.3 MakeSelect Format
|
||||
|
||||
All dropdown values use `code:name` format:
|
||||
- Display: `"001:JR 東北線"` → MakeSelect("001", "JR 東北線")
|
||||
- GetCode("001:JR 東北線") → "001"
|
||||
- GetDisplay("001:JR 東北線") → "JR 東北線" (via mid/InStr)
|
||||
|
||||
---
|
||||
|
||||
## 7. Column Layout Reference
|
||||
|
||||
### C1 — 58 columns (C to BG)
|
||||
|
||||
| Column | Header | Type |
|
||||
|--------|--------|------|
|
||||
| C | 職員番号 | string(8) |
|
||||
| D | 事実発生年月日 | date |
|
||||
| E | 提出年月日 | date |
|
||||
| F | 受理年月日 | date |
|
||||
| G | 届出の事由コード | enum(O3) |
|
||||
| H | 届出の備考 | string(40) |
|
||||
| I | 住所1 | string |
|
||||
| J | 住所2 | string |
|
||||
| K | 運賃改正・法改正年月日 | date |
|
||||
| L | 出勤予定日数 | number(2) |
|
||||
| M | 往復区分 | enum(oufukuList) |
|
||||
| N | 交替制 | enum(koutaiList) |
|
||||
| O | 算出式 | string(80) |
|
||||
| P | 自動車等使用距離 | number(3) |
|
||||
| Q | 自動車等支給額 | number(6) |
|
||||
| R | 自動車等駐車場代 | number(6) |
|
||||
| S~Y | 区間1 (コード/交通/発/着/券種/コード/期間) | mixed |
|
||||
| Z | 区間1支給開始年月 | date |
|
||||
| AA~AG | 区間2 | mixed |
|
||||
| AH | 区間2支給開始年月 | date |
|
||||
| AI~AM | 区間3 | mixed |
|
||||
| AN | 区間3支給開始年月 | date |
|
||||
| AO~AS | 区間4 | mixed |
|
||||
| AT | 区間4支給開始年月 | date |
|
||||
| AU | 決定事項区分コード | enum(Z2) |
|
||||
| AV | 非該当の理由 | string |
|
||||
| AW | 非該当者認定簿出力区分 | enum(higaitouList) |
|
||||
| AX | 非該当者認定簿出力区分 (actual) | enum |
|
||||
| BB | 手当月額の決定区分コード | enum(Z3) |
|
||||
| BC | 支給の始期 | date |
|
||||
| BD | 備考 | string |
|
||||
| BE | 所属コード | string |
|
||||
| BF | 認定年月日 | date |
|
||||
| BG | (各庁の長)官職コード | string |
|
||||
|
||||
---
|
||||
|
||||
## 8. Known Issues / Technical Debt
|
||||
|
||||
| # | Severity | Description | Status |
|
||||
|---|----------|-------------|--------|
|
||||
| 1 | High | M2.cls: `cacheVal` declared inside If block but used in Select Case outside it | Fixed |
|
||||
| 2 | High | C1.cls: `kukanCol`, `kukanCode`, `kukanLetter` undeclared in Validate | Fixed |
|
||||
| 3 | Medium | CJK comments in Common_Shape.bas and C1.cls | Fixed |
|
||||
| 4 | Medium | Hardcoded path `D:\Project\upds7\vba\` in Import_modules.bas | Fixed |
|
||||
| 5 | Medium | `sheetConfDict("Z4")` hardcoded key | Fixed |
|
||||
| 6 | Medium | `sheetName:="M1"` hardcoded in Common_Shape | Fixed |
|
||||
| 7 | Medium | M1.cls: `"M2"` hardcoded sheet reference | Fixed |
|
||||
| 8 | Medium | `On Error Resume Next` in 3 cache lookup functions (Common_Global_Cache.bas) | Fixed |
|
||||
| 9 | Low | T2.cls: CJK in comment `区分` | Fixed |
|
||||
| 10 | Medium | O1/O2/O3 Validate stubs are empty | Not fixed |
|
||||
184
src/sh/tuk/AUDIT_ISSUES_2026_05_28.md
Normal file
184
src/sh/tuk/AUDIT_ISSUES_2026_05_28.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# tuk Project Audit Issues
|
||||
|
||||
Status: Open
|
||||
Audit date: 2026-05-28
|
||||
|
||||
---
|
||||
|
||||
## Critical (Runtime Bugs)
|
||||
|
||||
### 1. M2.cls:341-356 — cacheVal used outside If block
|
||||
|
||||
**Severity:** 🔴 Critical
|
||||
**File:** `sheet/M2.cls`
|
||||
**Line:** 341-356
|
||||
|
||||
`cacheVal` is declared inside `If cache.Exists(code)` block but used in `Select Case` below regardless:
|
||||
|
||||
```vba
|
||||
If cache.Exists(code) Then
|
||||
Dim cacheVal As Variant: cacheVal = cache(code)
|
||||
...
|
||||
End If
|
||||
|
||||
Select Case kenshu
|
||||
Case "2"
|
||||
ws.Range("L" & rowNum).Value = Trim(cacheVal(1)) ' bug if code not in cache
|
||||
```
|
||||
|
||||
Fix: move `Dim cacheVal As Variant` before the `If cache.Exists(code)` check.
|
||||
|
||||
---
|
||||
|
||||
### 2. C1.cls:984-997 — undeclared variables in Validate
|
||||
|
||||
**Severity:** 🔴 Critical
|
||||
**File:** `sheet/C1.cls`
|
||||
**Line:** 984-997
|
||||
|
||||
Three variables used without `Dim` in the kukan duplicate-check loop:
|
||||
|
||||
```vba
|
||||
For kukanIdx = LBound(KUKAN_CODE_COLS) To UBound(KUKAN_CODE_COLS)
|
||||
kukanCol = KUKAN_CODE_COLS(kukanIdx) ' not declared
|
||||
kukanCode = Trim(Me.Cells(rowNum, kukanCol).Value)
|
||||
...
|
||||
kukanLetter = Split(Me.Cells(1, kukanCol).Address, "$")(1) ' not declared
|
||||
```
|
||||
|
||||
Fix: add `Dim kukanCol As Long, kukanCode As String, kukanLetter As String` at the top of `Validate`.
|
||||
|
||||
---
|
||||
|
||||
## AGENTS.md Violations
|
||||
|
||||
### 3. CJK Comments in Code
|
||||
|
||||
**Severity:** 🟡 AGENTS.md violation
|
||||
**Files:** `module/Common_Shape.bas`, `sheet/C1.cls`
|
||||
|
||||
| File | Line | Content |
|
||||
|------|------|---------|
|
||||
| `Common_Shape.bas` | 4 | `通用排版引擎(仅调整位置)` |
|
||||
| `Common_Shape.bas` | 22 | `第一个图标左边对齐B3左边` |
|
||||
| `Common_Shape.bas` | 44 | `你的专属调用入口` |
|
||||
| `C1.cls` | 675 | `vals(1) = D列, vals(3) = F列, vals(4) = G列` |
|
||||
|
||||
Fix: translate to English per AGENTS.md English-only comments rule.
|
||||
|
||||
---
|
||||
|
||||
### 4. Hardcoded File Path in Import_modules
|
||||
|
||||
**Severity:** 🟡 AGENTS.md violation
|
||||
**File:** `init_module/Import_modules.bas`
|
||||
**Line:** 8
|
||||
|
||||
```vba
|
||||
Const PROJECT_PATH As String = "D:\Project\upds7\vba\"
|
||||
```
|
||||
|
||||
Fix: use a config-based path or a constant defined in `Common_Constants.bas`.
|
||||
|
||||
---
|
||||
|
||||
### 5. Hardcoded Sheet Name in Z4.cls
|
||||
|
||||
**Severity:** 🟡 AGENTS.md violation
|
||||
**File:** `sheet/Z4.cls`
|
||||
**Line:** 14
|
||||
|
||||
```vba
|
||||
Set ws = ThisWorkbook.Worksheets("Z4")
|
||||
```
|
||||
|
||||
Fix: use `CACHE_Z4` constant instead of the string literal.
|
||||
|
||||
---
|
||||
|
||||
### 6. Hardcoded "M1" in Common_Shape
|
||||
|
||||
**Severity:** 🟡 AGENTS.md violation
|
||||
**File:** `module/Common_Shape.bas`
|
||||
**Line:** 47
|
||||
|
||||
```vba
|
||||
sheetName:="M1"
|
||||
```
|
||||
|
||||
Fix: pass as parameter or use constant.
|
||||
|
||||
---
|
||||
|
||||
## Code Quality
|
||||
|
||||
### 7. O1/O2/O3 Validate Stubs
|
||||
|
||||
**Severity:** 🟠 Quality
|
||||
**Files:** `sheet/O1.cls`, `sheet/O2.cls`, `sheet/O3.cls`
|
||||
|
||||
All three have `Validate` methods that are empty stubs — just `Exit Sub`. If validation runs on these sheets, all rows pass silently.
|
||||
|
||||
Fix: implement proper validation or confirm empty stubs are intentional.
|
||||
|
||||
---
|
||||
|
||||
### 8. On Error Resume Next — 7 instances
|
||||
|
||||
**Severity:** 🟠 Quality
|
||||
**Files:** `Common_Global_Cache.bas`, `Common_Functions.bas`, `Common_Shape.bas`, `Test_Cache.bas`, `Import_modules.bas`
|
||||
|
||||
These silently swallow errors (e.g., "subscript out of range" for missing worksheets):
|
||||
|
||||
| File | Line | Target |
|
||||
|------|------|--------|
|
||||
| `Common_Global_Cache.bas` | 91 | `ThisWorkbook.Worksheets("M1")` |
|
||||
| `Common_Global_Cache.bas` | 155 | `ThisWorkbook.Worksheets("M2")` |
|
||||
| `Common_Global_Cache.bas` | 232 | `ThisWorkbook.Worksheets("M1")` |
|
||||
| `Common_Functions.bas` | 101 | `ThisWorkbook.Worksheets(sheetName)` |
|
||||
| `Common_Shape.bas` | 14 | `ThisWorkbook.Worksheets(sheetName)` |
|
||||
| `Test_Cache.bas` | 25 | `ThisWorkbook.Worksheets("Test_Cache")` |
|
||||
| `Import_modules.bas` | 82, 126 | `VBProject.VBComponents` |
|
||||
|
||||
Fix: use `On Error GoTo ErrHandler` pattern with `Err.Raise` for missing objects, or explicitly check worksheet existence before access.
|
||||
|
||||
---
|
||||
|
||||
### 9. Fixed Cell Reference in C1 Validate
|
||||
|
||||
**Severity:** 🟠 Quality
|
||||
**File:** `sheet/C1.cls`
|
||||
**Line:** 1000
|
||||
|
||||
```vba
|
||||
Dim linkCellValue As String: linkCellValue = Me.Cells(3, "H").Value
|
||||
```
|
||||
|
||||
Fixed address `Cells(3, "H")` used as a config switch. No constant or comment explaining its purpose.
|
||||
|
||||
Fix: add a named constant or sheetConf entry for this cell.
|
||||
|
||||
---
|
||||
|
||||
## Resolved / Correct
|
||||
|
||||
- All modules have `Option Explicit` ✅
|
||||
- All `Worksheet_Change` handlers disable `EnableEvents` and use `Finally:` pattern ✅
|
||||
- All sheet classes have English module headers ✅
|
||||
- T2 `FillZeroIfEmpty` uses `sheetConf("ZeroFillCols")` with letter strings ✅
|
||||
- Z4 `LookupZ4RosenCache` uses F col for rosen, D col for station ✅
|
||||
- `Common_Selector.BuildZ4StationToDropdown` iterates `stationFromDict.Keys` to exclude `stationFrom` ✅
|
||||
- No `Select`/`Selection`/`ActiveCell` patterns in sheet classes ✅
|
||||
- All `Validate` methods in Z-series and T-series have proper checkResult pattern ✅
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| 🔴 Critical | 2 |
|
||||
| 🟡 AGENTS.md violation | 4 |
|
||||
| 🟠 Code quality | 3 |
|
||||
|
||||
Priority: Fix #1 → #2 → #3 → #4
|
||||
@@ -5,9 +5,11 @@ Sub ImportModulesAndSheets_Safe()
|
||||
Dim fso As Object
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
|
||||
Const PROJECT_PATH As String = "D:\Project\upds7\vba\"
|
||||
Const MODULE_PATH As String = PROJECT_PATH & "src\sh\tuk\module"
|
||||
Const SHEET_PATH As String = PROJECT_PATH & "src\sh\tuk\sheet"
|
||||
Dim basePath As String: basePath = ThisWorkbook.Path
|
||||
If Right(basePath, 1) <> "\" Then basePath = basePath & "\"
|
||||
Const PROJECT_PATH As String = basePath
|
||||
Const MODULE_PATH As String = basePath & "src\sh\tuk\module"
|
||||
Const SHEET_PATH As String = basePath & "src\sh\tuk\sheet"
|
||||
|
||||
' --- Phase 1: Validation ---
|
||||
Debug.Print "[LOG] Starting validation phase..."
|
||||
|
||||
@@ -98,12 +98,9 @@ Function LoadLookup(ByVal sheetName As String, ByVal cacheName As String) As Obj
|
||||
|
||||
' --- obtain worksheet ---
|
||||
Dim ws As Worksheet
|
||||
On Error Resume Next
|
||||
Set ws = ThisWorkbook.Worksheets(sheetName)
|
||||
On Error GoTo ErrHandler
|
||||
If ws Is Nothing Then
|
||||
Err.Raise ERR_SHEET_MISSING, "LoadLookup", "Worksheet '" & sheetName & "' not found."
|
||||
End If
|
||||
Set ws = ThisWorkbook.Worksheets(sheetName)
|
||||
' ws exists, continue
|
||||
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict(cacheName)
|
||||
Dim startRow As Long: startRow = sheetConf("StartRow")
|
||||
@@ -335,12 +332,8 @@ Public Function FormatDateInput(ByVal inputStr As String) As String
|
||||
Dim s As String: s = Trim(inputStr)
|
||||
If s = "" Then Exit Function
|
||||
|
||||
' Only process pure digit strings
|
||||
If Not IsNumeric(s) Then
|
||||
FormatDateInput = inputStr
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
' Handle pure digit strings (YYYYMMDD / YYMMDD)
|
||||
If IsNumeric(s) Then
|
||||
Dim yearPart As String, monthPart As String, dayPart As String
|
||||
Dim dateStr As String
|
||||
|
||||
@@ -359,14 +352,25 @@ Public Function FormatDateInput(ByVal inputStr As String) As String
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
' Build date string and validate
|
||||
dateStr = yearPart & "-" & monthPart & "-" & dayPart
|
||||
|
||||
If IsDate(dateStr) Then
|
||||
FormatDateInput = dateStr
|
||||
Else
|
||||
FormatDateInput = inputStr
|
||||
FormatDateInput = ""
|
||||
End If
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
' Handle non-numeric date strings (e.g. "2026/05", "2026/5/1", "2026-5-1")
|
||||
If IsDate(s) Then
|
||||
Dim d As Date: d = CDate(s)
|
||||
FormatDateInput = Year(d) & "-" & Right("0" & Month(d), 2) & "-" & Right("0" & Day(d), 2)
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
' Not a date - return empty string
|
||||
FormatDateInput = ""
|
||||
End Function
|
||||
|
||||
'Check header edit protection
|
||||
|
||||
@@ -19,7 +19,8 @@ Public Const CACHE_T3 As String = "T3"
|
||||
Public Const CACHE_O1 As String = "O1"
|
||||
Public Const CACHE_O2 As String = "O2"
|
||||
Public Const CACHE_O3 As String = "O3"
|
||||
|
||||
Public Const CACHE_M1 As String = "M1"
|
||||
Public Const CACHE_M2 As String = "M2"
|
||||
|
||||
|
||||
Private sheetConfDict As Object
|
||||
@@ -62,7 +63,7 @@ Public Sub RefreshCache(ByVal cacheName As String)
|
||||
Dim sheetConfDict As Object: Set sheetConfDict = GetSheetConfig()
|
||||
If cacheName = "M1KukanDCache" Then
|
||||
Set loadedData = LookupM1KukanCache()
|
||||
ElseIf cacheName = "M2" Then
|
||||
ElseIf cacheName = CACHE_M2 Then
|
||||
Set loadedData = LookupM2Cache()
|
||||
ElseIf cacheName = CACHE_O1 Then
|
||||
Set loadedData = LookupO1Cache()
|
||||
@@ -88,12 +89,9 @@ Private Function LookupM1KukanCache()
|
||||
On Error GoTo ErrHandler
|
||||
|
||||
Dim ws As Worksheet
|
||||
On Error Resume Next
|
||||
Set ws = ThisWorkbook.Worksheets("M1")
|
||||
On Error GoTo ErrHandler
|
||||
' ws exists, continue
|
||||
Set ws = ThisWorkbook.Worksheets(CACHE_M1)
|
||||
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict("M1")
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict(CACHE_M1)
|
||||
Dim startRow As Long: startRow = sheetConf("StartRow")
|
||||
Dim lastRow As Long: lastRow = GetLastDataRowInRange(ws)
|
||||
If lastRow < startRow Then
|
||||
@@ -152,12 +150,9 @@ Private Function LookupM2Cache() As Object
|
||||
On Error GoTo ErrHandler
|
||||
|
||||
Dim ws As Worksheet
|
||||
On Error Resume Next
|
||||
Set ws = ThisWorkbook.Worksheets("M2")
|
||||
On Error GoTo ErrHandler
|
||||
' ws exists, continue
|
||||
Set ws = ThisWorkbook.Worksheets(CACHE_M2)
|
||||
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict("M2")
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict(CACHE_M2)
|
||||
Dim startRow As Long: startRow = sheetConf("StartRow")
|
||||
Dim lastRow As Long: lastRow = GetLastDataRowInRange(ws)
|
||||
If lastRow < startRow Then
|
||||
@@ -229,10 +224,7 @@ Private Function LookupO1Cache() As Object
|
||||
On Error GoTo ErrHandler
|
||||
|
||||
Dim ws As Worksheet
|
||||
On Error Resume Next
|
||||
Set ws = ThisWorkbook.Worksheets(CACHE_O1)
|
||||
On Error GoTo ErrHandler
|
||||
' ws exists, continue
|
||||
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict(CACHE_O1)
|
||||
Dim startRow As Long: startRow = sheetConf("StartRow")
|
||||
@@ -289,7 +281,7 @@ End Function
|
||||
|
||||
' ============================================================
|
||||
' Z4 Rosen Cache - nested dict for M1 E/F/H cascade dropdown
|
||||
' Structure: { rosen [F]: { stationFrom [D]: [stationTo E, ...] } }
|
||||
' Structure: { rosen [F]: { station [D]: True } }
|
||||
' ============================================================
|
||||
Private Function LookupZ4RosenCache() As Object
|
||||
Dim resultCache As Object
|
||||
@@ -309,31 +301,25 @@ Private Function LookupZ4RosenCache() As Object
|
||||
End If
|
||||
|
||||
Dim r As Long
|
||||
For r = startRow To lastRow
|
||||
Dim rosen As String: rosen = Trim(ws.Cells(r, 6).Value)
|
||||
Dim stationFrom As String: stationFrom = Trim(ws.Cells(r, 4).Value)
|
||||
Dim stationTo As String: stationTo = Trim(ws.Cells(r, 5).Value)
|
||||
Dim station As String
|
||||
Dim rosen As String
|
||||
Dim innerDict As Object
|
||||
|
||||
If rosen = "" Or stationFrom = "" Then GoTo NextRow3
|
||||
For r = startRow To lastRow
|
||||
rosen = Trim(ws.Cells(r, 6).Value)
|
||||
station = Trim(ws.Cells(r, 4).Value)
|
||||
|
||||
If rosen = "" Or station = "" Then GoTo NextRow3
|
||||
|
||||
If Not resultCache.Exists(rosen) Then
|
||||
Dim innerDict As Object
|
||||
Set innerDict = CreateObject("Scripting.Dictionary")
|
||||
innerDict.CompareMode = vbTextCompare
|
||||
resultCache.Add rosen, innerDict
|
||||
End If
|
||||
|
||||
Set innerDict = resultCache(rosen)
|
||||
If Not innerDict.Exists(stationFrom) Then
|
||||
Dim arr As Object
|
||||
Set arr = CreateObject("Scripting.Dictionary")
|
||||
arr.CompareMode = vbTextCompare
|
||||
innerDict.Add stationFrom, arr
|
||||
End If
|
||||
|
||||
Set arr = innerDict(stationFrom)
|
||||
If stationTo <> "" And Not arr.Exists(stationTo) Then
|
||||
arr.Add stationTo, True
|
||||
If Not innerDict.Exists(station) Then
|
||||
innerDict.Add station, True
|
||||
End If
|
||||
|
||||
NextRow3:
|
||||
@@ -387,7 +373,7 @@ Private Sub RefreshSheetDict()
|
||||
sheetConf("FilterRow") = 6
|
||||
sheetConf("KeyCol") = 3
|
||||
sheetConf("ValueCols") = Array(3, 4, 5, 6, 7, 9, 12)
|
||||
Set sheetConfDict("M1") = sheetConf
|
||||
Set sheetConfDict(CACHE_M1) = sheetConf
|
||||
Debug.Print "RefreshSheetDict M1 ok."
|
||||
|
||||
' M2
|
||||
@@ -403,7 +389,7 @@ Private Sub RefreshSheetDict()
|
||||
sheetConf("HeaderColumns") = Array("C", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R")
|
||||
sheetConf("AlwaysQuote") = False
|
||||
sheetConf("FilterRow") = 7
|
||||
Set sheetConfDict("M2") = sheetConf
|
||||
Set sheetConfDict(CACHE_M2) = sheetConf
|
||||
Debug.Print "RefreshSheetDict M2 ok."
|
||||
|
||||
' Z1
|
||||
@@ -479,7 +465,7 @@ Private Sub RefreshSheetDict()
|
||||
sheetConf("FilterRow") = 6
|
||||
sheetConf("KeyCol") = 3
|
||||
sheetConf("ValueCols") = Array(4)
|
||||
Set sheetConfDict("Z4") = sheetConf
|
||||
Set sheetConfDict(CACHE_Z4) = sheetConf
|
||||
Debug.Print "RefreshSheetDict Z4 ok."
|
||||
|
||||
' T1
|
||||
@@ -517,6 +503,7 @@ Private Sub RefreshSheetDict()
|
||||
sheetConf("FilterRow") = 6
|
||||
sheetConf("KeyCol") = 3
|
||||
sheetConf("ValueCols") = Array(4, 8, 9, 10, 11, 12, 13)
|
||||
sheetConf("ZeroFillCols") = Array("H", "I", "J", "K", "L", "M")
|
||||
Set sheetConfDict(CACHE_T2) = sheetConf
|
||||
Debug.Print "RefreshSheetDict T2 ok."
|
||||
|
||||
@@ -695,15 +682,15 @@ Public Sub RefreshMasterCache()
|
||||
End Sub
|
||||
|
||||
Public Sub RefreshKukanCache(ByVal sheetName As String)
|
||||
If sheetName = "M1" Then
|
||||
Call RefreshCache("M1")
|
||||
If sheetName = CACHE_M1 Then
|
||||
Call RefreshCache(CACHE_M1)
|
||||
Call RefreshCache("M1KukanDCache")
|
||||
Call RefreshCache(CACHE_Z4ROSEN)
|
||||
Call WriteCachesSheet("M1")
|
||||
Call WriteCachesSheet(CACHE_M1)
|
||||
End If
|
||||
If sheetName = "M2" Then
|
||||
Call RefreshCache("M2")
|
||||
Call WriteCachesSheet("M2")
|
||||
If sheetName = CACHE_M2 Then
|
||||
Call RefreshCache(CACHE_M2)
|
||||
Call WriteCachesSheet(CACHE_M2)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -729,7 +716,7 @@ Public Sub WriteCachesSheet(ByVal cacheName As String)
|
||||
Case CACHE_T3: colLetter = "G"
|
||||
Case CACHE_O2: colLetter = "H"
|
||||
Case CACHE_O3: colLetter = "I"
|
||||
Case "M1": colLetter = "M"
|
||||
Case CACHE_M1: colLetter = "M"
|
||||
Case Else: Exit Sub
|
||||
End Select
|
||||
|
||||
|
||||
@@ -264,18 +264,17 @@ Public Sub BuildZ4StationToDropdown(ws As Worksheet, ByVal columnLetter As Strin
|
||||
If Not z4RosenCache.Exists(rosen) Then Exit Sub
|
||||
|
||||
Dim stationFromDict As Object: Set stationFromDict = z4RosenCache(rosen)
|
||||
If Not stationFromDict.Exists(stationFrom) Then Exit Sub
|
||||
|
||||
Dim stationToDict As Object: Set stationToDict = stationFromDict(stationFrom)
|
||||
Dim dropdownList As String: dropdownList = ""
|
||||
Dim stationTo As Variant
|
||||
For Each stationTo In stationToDict.Keys
|
||||
Dim s As Variant
|
||||
For Each s In stationFromDict.Keys
|
||||
If s <> stationFrom Then
|
||||
If dropdownList = "" Then
|
||||
dropdownList = stationTo
|
||||
dropdownList = s
|
||||
Else
|
||||
dropdownList = dropdownList & "," & stationTo
|
||||
dropdownList = dropdownList & "," & s
|
||||
End If
|
||||
Next stationTo
|
||||
End If
|
||||
Next s
|
||||
|
||||
If dropdownList = "" Then Exit Sub
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Attribute VB_Name = "Common_Shape"
|
||||
Option Explicit
|
||||
|
||||
' ================= 通用排版引擎(仅调整位置) =================
|
||||
' ================= Common Layout Engine (position only) =================
|
||||
|
||||
Public Sub AlignIconsByCenter(sheetName As String, anchorAddr As String, _
|
||||
iconArr As Variant, gapPt As Double)
|
||||
@@ -10,16 +10,14 @@ Public Sub AlignIconsByCenter(sheetName As String, anchorAddr As String, _
|
||||
Dim shp As Shape
|
||||
Dim i As Long
|
||||
Dim shapeCount As Long
|
||||
|
||||
On Error Resume Next
|
||||
On Error GoTo ErrHandler
|
||||
Set ws = ThisWorkbook.Worksheets(sheetName)
|
||||
On Error GoTo 0
|
||||
If ws Is Nothing Then Exit Sub
|
||||
' ws exists, continue
|
||||
|
||||
Set anchor = ws.Range(anchorAddr)
|
||||
shapeCount = UBound(iconArr) - LBound(iconArr) + 1
|
||||
|
||||
' 第一个图标左边对齐B3左边
|
||||
' First icon left-aligns to B3 left edge
|
||||
Dim curX As Double: curX = anchor.Left
|
||||
Dim prevX As Double: prevX = 0
|
||||
Dim cy As Double: cy = anchor.Top + anchor.Height / 2
|
||||
@@ -39,12 +37,17 @@ Public Sub AlignIconsByCenter(sheetName As String, anchorAddr As String, _
|
||||
curX = curX + shp.Width + gapPt
|
||||
Next i
|
||||
Application.ScreenUpdating = True
|
||||
Exit Sub
|
||||
|
||||
ErrHandler:
|
||||
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "AlignIconsByCenter"
|
||||
Application.ScreenUpdating = True
|
||||
End Sub
|
||||
|
||||
' ================= 你的专属调用入口 =================
|
||||
' ================= Entry point =================
|
||||
Sub RunAlignForMySheet()
|
||||
AlignIconsByCenter _
|
||||
sheetName:="M1", _
|
||||
sheetName:=CACHE_M1, _
|
||||
anchorAddr:="B3", _
|
||||
iconArr:=Array("input", "check", "output", "sort", "filter", "fit", "load"), _
|
||||
gapPt:=10
|
||||
|
||||
@@ -157,10 +157,16 @@ Private Sub Worksheet_Change(ByVal Target As Range)
|
||||
idx = GetIdx(Target.Column, DATE_COLS)
|
||||
If idx >= 0 Then
|
||||
Dim cellDate As Range
|
||||
Dim formattedDate As String
|
||||
For Each cellDate In Target
|
||||
If Trim(cellDate.Value) <> "" Then
|
||||
Dim formattedDate As String: formattedDate = FormatDateInput(cellDate.Value)
|
||||
cellDate.Value = FormatDateInput(formattedDate)
|
||||
formattedDate = FormatDateInput(cellDate.Value)
|
||||
If formattedDate = "" Then
|
||||
' Invalid date input - clear cell and show message
|
||||
cellDate.Value = ""
|
||||
MsgBox "Please enter a valid date (YYYY-MM-DD or YYYY/MM/DD)", vbExclamation, "Invalid Date"
|
||||
Else
|
||||
cellDate.Value = formattedDate
|
||||
If cellDate.Column = 5 Then
|
||||
Dim fCell As Range: Set fCell = Me.Cells(cellDate.Row, 6)
|
||||
If Trim(fCell.Value) = "" Then
|
||||
@@ -168,6 +174,7 @@ Private Sub Worksheet_Change(ByVal Target As Range)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
@@ -672,7 +679,7 @@ Private Function FindKukanCodeByStation(ByVal rowNum As Long, ByVal transportCol
|
||||
Dim code As Variant
|
||||
For Each code In m1Cache.Keys
|
||||
Dim vals As Variant: vals = m1Cache(code)
|
||||
' vals(1) = D列, vals(3) = F列, vals(4) = G列
|
||||
' vals(1) = D col, vals(3) = F col, vals(4) = G col
|
||||
If vals(1) = transportKbn And vals(3) = stationFrom And vals(4) = stationTo Then
|
||||
FindKukanCodeByStation = code
|
||||
Exit Function
|
||||
@@ -795,12 +802,15 @@ Public Sub Validate(ws As Worksheet, ByVal rowNum As Long, ByVal lastDataRow As
|
||||
Dim colIndex As Variant
|
||||
For Each colIndex In DATE_COLS()
|
||||
Dim cellDate As String: cellDate = Trim(Me.Cells(rowNum, colIndex).Value)
|
||||
If cellDate <> "" And Not IsDate(cellDate) Then
|
||||
If cellDate <> "" Then
|
||||
' Require full YYYY-MM-DD format (output of FormatDateInput)
|
||||
If Len(cellDate) <> 10 Or Mid(cellDate, 5, 1) <> "-" Or Mid(cellDate, 8, 1) <> "-" Then
|
||||
Dim letter As String: letter = Split(Me.Cells(1, colIndex).Address, "$")(1)
|
||||
Me.Cells(rowNum, errorCol).Value = GetErrorMsg("E001", letter & rowNum)
|
||||
Me.Range(letter & rowNum).Interior.Color = RGB(255, 0, 0)
|
||||
Exit Sub
|
||||
End If
|
||||
End If
|
||||
Next colIndex
|
||||
|
||||
' validate number
|
||||
|
||||
@@ -51,20 +51,19 @@ Private Sub Worksheet_Change(ByVal Target As Range)
|
||||
|
||||
If z4Rosen.Exists(kikanName) Then
|
||||
Call BuildZ4StationFromDropdown(Me, "F", cellD.Row, kikanName)
|
||||
Dim fromStations As Object: Set fromStations = z4Rosen(kikanName)
|
||||
Dim stations As Object: Set stations = z4Rosen(kikanName)
|
||||
Dim fromCell As Range: Set fromCell = Me.Cells(cellD.Row, 6)
|
||||
Dim fromStation As String: fromStation = Trim(fromCell.Value)
|
||||
If Not fromStations.Exists(fromStation) Or fromStation = "" Then
|
||||
If Not stations.Exists(fromStation) Or fromStation = "" Then
|
||||
fromCell.ClearContents
|
||||
Me.Cells(cellD.Row, 7).ClearContents
|
||||
Me.Cells(cellD.Row, 7).Validation.Delete
|
||||
Else
|
||||
Call BuildZ4StationToDropdown(Me, "G", cellD.Row, kikanName, fromStation)
|
||||
Dim toStations As Object: Set toStations = fromStations(fromStation)
|
||||
Dim toCell As Range: Set toCell = Me.Cells(cellD.Row, 7)
|
||||
Dim toStation As String: toStation = Trim(toCell.Value)
|
||||
|
||||
If Not toStations.Exists(fromStation) Or toStation = "" Then
|
||||
If Not stations.Exists(toStation) Or toStation = "" Then
|
||||
toCell.ClearContents
|
||||
End If
|
||||
End If
|
||||
@@ -264,9 +263,9 @@ Private Sub ValidateWarn(ws As Worksheet, ByVal lastDataRow As Long)
|
||||
|
||||
' Get M2 sheet kukan code list directly
|
||||
Dim sheetConfDict As Object: Set sheetConfDict = GetSheetConfig()
|
||||
Dim m2SheetConf As Object: Set m2SheetConf = sheetConfDict("M2")
|
||||
Dim m2SheetConf As Object: Set m2SheetConf = sheetConfDict(CACHE_M2)
|
||||
Dim m2StartRow As Long: m2StartRow = m2SheetConf("StartRow")
|
||||
Dim wsM2 As Worksheet: Set wsM2 = ThisWorkbook.Worksheets("M2")
|
||||
Dim wsM2 As Worksheet: Set wsM2 = ThisWorkbook.Worksheets(CACHE_M2)
|
||||
Dim lastRowM2 As Long: lastRowM2 = GetLastDataRowInRange(wsM2)
|
||||
If lastRowM2 < m2StartRow Then
|
||||
exitMsg = "M2 sheet has no data"
|
||||
|
||||
@@ -23,6 +23,7 @@ Private Sub Worksheet_Change(ByVal Target As Range)
|
||||
If Trim(cell.Value) = "" Then
|
||||
Call ClearDataRow(Me, cell.Row)
|
||||
Else
|
||||
Call FillZeroIfEmpty(Me, cell.Row)
|
||||
Call BuildDisplayDropdown(Me, cell.Row)
|
||||
End If
|
||||
Next
|
||||
@@ -47,6 +48,20 @@ Finally:
|
||||
Application.EnableEvents = True
|
||||
End Sub
|
||||
|
||||
' Fill H~M with "0" if they are empty when C column (kubun/category) is edited
|
||||
Private Sub FillZeroIfEmpty(ws As Worksheet, ByVal rowNum As Long)
|
||||
Dim sheetConfDict As Object: Set sheetConfDict = GetSheetConfig()
|
||||
Dim sheetConf As Object: Set sheetConf = sheetConfDict(ws.CodeName)
|
||||
|
||||
Dim zeroFillCols As Variant: zeroFillCols = sheetConf("ZeroFillCols")
|
||||
Dim colLetter As Variant
|
||||
For Each colLetter In zeroFillCols
|
||||
If Trim(ws.Range(colLetter & rowNum).Value) = "" Then
|
||||
ws.Range(colLetter & rowNum).Value = "0"
|
||||
End If
|
||||
Next colLetter
|
||||
End Sub
|
||||
|
||||
' Prevent insert/delete row in header area
|
||||
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
|
||||
Dim sheetConfDict As Object: Set sheetConfDict = GetSheetConfig()
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user