Files
cobol-tna-system/基本設計書/02_残業統計管理システム_設計書(サブシステムB).md

818 lines
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 残業統計管理システム 設計書(サブシステムB)
## システム概要
本サブシステムは、社員からの加班申請データを元に、振り分け・重複チェック・打刻照合・取消マッチング・キーブレイク集約を行い、月次の加班統計データをDBに保存する。
### サブシステム情報
| 項目 | 内容 |
|------|------|
| サブシステムID | ZAN(残業→ZANgyo |
| COBOLプログラム数 | 6 |
| JCL数 | 6 |
| DB | 1OVERTIME-DB / DB2、2テーブル) |
### システム定数
| 定数 | 値 | 説明 |
|------|-----|------|
| WORK-START | 0900 | 所定労働開始時刻 |
| WORK-END | 1800 | 所定労働終了時刻 |
| DINNER-START | 1830 | 食事時間開始(1830以前は加班不可) |
| MIN-OVT-HOURS | 0.5 | 加班最小単位 |
### 入力CSV形式(OVT-APPLY
各レコードはCSV形式。
| 項目 | 内容 |
|------|------|
| フォーマット | `申請番号,社員番号,日付(YYYYMMDD),開始時刻(HHMM),終了時刻(HHMM),ステータス` |
| ステータス 0 | 申請(新規) |
| ステータス 1 | 承認済(後続処理で状態更新) |
| ステータス 9 | 取消 |
---
## ファイル一覧
| # | ファイル名 | 編成 | RECM | サイズ | 用途 | 区分 |
|---|-----------|------|------|-------|------|------|
| 1 | OVT-APPLY | SEQUENTIAL | FB | 80 | 加班申請CSV(入力) | 新規 |
| 2 | OVT-VALID | SEQUENTIAL | FB | 80 | 振り分け通過(有効申請) | 新規 |
| 3 | OVT-CANCEL | SEQUENTIAL | FB | 80 | 振り分け通過(取消) | 新規 |
| 4 | OVT-NODUP | SEQUENTIAL | FB | 80 | 重複チェック通過データ | 新規 |
| 5 | OVT-CHECKED | SEQUENTIAL | FB | 80 | 打刻照合通過(OVT-TYPE付加) | 新規 |
| 6 | OVT-MATCHED | SEQUENTIAL | FB | 80 | マッチング通過(処理番号付加) | 新規 |
| 7 | OVT-DBCLEAN | SEQUENTIAL | FB | 80 | 孤立取消(DB削除対象) | 新規 |
| 8 | OVT-SUMMARY | SEQUENTIAL | FB | 80 | 集約結果 | 新規 |
| 9 | ERROR-LOG | SEQUENTIAL | VB | 200 | エラーレコード退避 | Aと共用 |
| 10 | EDITED-PUNCH | SEQUENTIAL | FB | 80 | 打刻データ(Aシステム出力) | Aから連携 |
| 11 | HOLIDAY-FILE | SEQUENTIAL | FB | 80 | 休日マスタファイル(Aシステム提供) | Aから連携 |
### DBテーブル管理方針
DB2インスタンスは全サブシステムで共有するが、テーブルはサブシステム別に機能分割する。
BサブシステムのテーブルはOVT-APPLICATIONS, OVT-MONTHLYの2つであり、これらの管理・更新はBのみが行う。
Aサブシステムのテーブル(DAILY_RECORDS, LEAVE_RECORDS等)は参照せず、A→Bの連携はEDITED-PUNCHファイル経由に限定する。
HOLIDAY_CALENDARテーブルについては、Aサブシステムによって提供されるHOLIDAY-FILEを使用することで参照を代替する。
### DB二重対応
本サブシステムのDB操作はDB2の埋め込みSQL(`EXEC SQL`)で直接記述する。
ただし、休日マスタに関するDBアクセスはファイルベースで処理するため、
ZAN06UPD以外の全プログラムはDBアクセスを伴わない。
各プログラム内でEXEC SQL INSERT/UPDATE/SELECTを発行する。
### 共通関数利用方針
Aサブシステムの共通関数のうち、安定した汎用APISUB01DAT, SUB02MSG, SUB03END, SUB04CHK, SUB05TIM)はBでも利用する。
SUB06DBUはサブシステムAで使用するDB更新用サブプログラムであり、Bでは使用しない(EXEC SQLを直接記述)。
---
## DB構成
### DB名称:残業統計データベース(OVERTIME-DB
#### テーブル1OVT-APPLICATIONS(個別加班申請テーブル)
| カラム | 型 | 内容 |
|--------|---|------|
| APPL-ID | CHAR(8) | 申請番号(PK |
| EMP-ID | CHAR(8) | 社員番号 |
| APPL-DATE | CHAR(8) | 申請日 YYYYMMDD |
| OVT-TYPE | CHAR(1) | W=平日 / H=休日 |
| START-TIME | CHAR(4) | 開始時刻 HHMM |
| END-TIME | CHAR(4) | 終了時刻 HHMM |
| OVT-HOURS | DECIMAL(4,1) | 加班時間 |
| STATUS | CHAR(1) | 0=有効 / 9=取消 |
| UPDATED-AT | TIMESTAMP | 更新日時 |
#### テーブル2OVT-MONTHLY(月次集計テーブル)
| カラム | 型 | 内容 |
|--------|---|------|
| EMP-ID | CHAR(8) | 社員番号(PK |
| YEAR-MONTH | CHAR(6) | 対象年月 YYYYMMPK |
| OVT-TYPE | CHAR(1) | W=平日 / H=休日(PK |
| OVT-HOURS | DECIMAL(6,1) | 加班時間合計 |
| OVT-COUNT | INTEGER | 加班回数 |
| UPDATED-AT | TIMESTAMP | 更新日時 |
---
## 処理フロー
```
OVT-APPLYCSV、任意順)
レコード: 申請番号,社員番号,日付,開始時刻,終了時刻,ステータス(0/1/9)
╞══ ZANJ010 ═══════════════════════════════════╡
│ ZAN01CHK(残業申請振分処理)— 振り分け
│ UNSTRINGでCSV分解 / EVALUATEでステータス判定
│ SUB04CHKで日付/時刻/数値チェック / 0.5h未満チェック
├── ステータス=0,1 → OVT-VALID
├── ステータス=9 → OVT-CANCEL
└── 異常 → ERROR-LOG
╞══ ZANJ012 ═══════════════════════════════════╡
│ SORT(OVT-VALID → 社員番号+日付+開始時刻昇順)
│ ZAN02CHK(時間帯重複チェック処理)— 項目チェック
│ 同社員+同日の前レコード終了時刻 > 現レコード開始時刻
│ → 重複(境界接続は重複としない)
├── 通過 → OVT-NODUP
└── 重複 → ERROR-LOG
╞══ ZANJ013 ═══════════════════════════════════╡
│ SORT(EDITED-PUNCH → 社員番号+日付昇順)
│ ZAN03CHK(打刻時間照合処理)— マッチング(N:1)
│ OVT-NODUP + PUNCH-SORTED を社員番号+日付で突合
│ 申請時間帯が出勤〜退勤の範囲内か確認
│ HOLIDAY_CALENDAR検索+曜日関数で休日判定→OVT-TYPE(W/H)設定
├── 通過 → OVT-CHECKED
└── 異常 → ERROR-LOG
╞══ ZANJ015 ═══════════════════════════════════╡
│ SORT(OVT-CHECKED → 申請番号昇順)
│ SORT(OVT-CANCEL → 申請番号昇順)
│ ZAN04MAT(取消マッチング処理)— マッチング1:1
│ キー=申請番号で VALID vs CANCEL を突合
├── VALID有 + CANCEL無 → OVT-MATCHED
├── VALID有 + CANCEL有 → 消滅
└── VALID無 + CANCEL有 → OVT-DBCLEAN
╞══ ZANJ020 ═══════════════════════════════════╡
│ SORT(OVT-MATCHED → 申請番号+処理番号昇順)
│ ZAN05CAL(残業時間集計処理)— キーブレイク集計
│ 同一申請番号内の最終レコードを集計
│ DIVIDE 分→時間変換 / SUB05TIMで丸め
│ COMPUTE ROUNDED ON SIZE ERROR
│ → OVT-SUMMARY
╞══ ZANJ025 ═══════════════════════════════════╡
│ ZAN06UPD(残業統計DB更新処理)— DB更新
├── OVT-SUMMARY → OVT-APPLICATIONS INSERT/UPDATE
│ → OVT-MONTHLY UPSERT(平日/休日別)
└── OVT-DBCLEAN → OVT-APPLICATIONS STATUS='9' UPDATE
→ OVT-MONTHLY 減算UPDATE
→ 該当なし → ERROR-LOG
```
---
## JCL
### ZANJ010 — 残業申請振分
```jcl
//ZANJ010 JOB (ACCT),'残業申請振分',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP010: 振り分け(SORT不要)
//* 注:YEARMONTH PARMは他プログラムとの一貫性のため渡すが、ZAN01CHK内では未使用
//*=====================================================================
//STEP010 EXEC PGM=ZAN01CHK
//ZAN01R01 DD DSN=OVT-APPLY.DAT,DISP=SHR
//ZAN01W01 DD DSN=OVT-VALID.DAT,DISP=(NEW,PASS)
//ZAN01W02 DD DSN=OVT-CANCEL.DAT,DISP=(NEW,PASS)
//ZAN01W03 DD DSN=ERROR-LOG.DAT,DISP=(NEW,PASS)
```
### ZANJ012 — 時間帯重複チェック
```jcl
//ZANJ012 JOB (ACCT),'時間帯重複チェック',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP012S: 有効申請 SORT
//*=====================================================================
//STEP012S EXEC PGM=SORT
//SORTIN DD DSN=OVT-VALID.DAT,DISP=(OLD,DELETE)
//SORTOUT DD DSN=OVT-VSORT.DAT,DISP=(NEW,PASS)
//SYSIN DD *
SORT FIELDS=(9,8,CH,A,17,8,CH,A,25,4,CH,A),EQUALS
/*
//*
//*=====================================================================
//* STEP012: 重複チェック
//*=====================================================================
//STEP012 EXEC PGM=ZAN02CHK,PARM='YEARMONTH=&YEARMONTH'
//ZAN02R01 DD DSN=OVT-VSORT.DAT,DISP=(OLD,DELETE)
//ZAN02W01 DD DSN=OVT-NODUP.DAT,DISP=(NEW,PASS)
//ZAN02W02 DD DSN=ERROR-LOG.DAT,DISP=(NEW,PASS)
```
### ZANJ013 — 打刻時間照合
```jcl
//ZANJ013 JOB (ACCT),'打刻時間照合',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP013S: 打刻データ SORT
//*=====================================================================
//STEP013S EXEC PGM=SORT
//SORTIN DD DSN=EDITED-PUNCH.DAT,DISP=SHR
//SORTOUT DD DSN=PUNCH-SORTED.DAT,DISP=(NEW,PASS)
//SYSIN DD *
SORT FIELDS=(1,8,CH,A,9,8,CH,A),EQUALS
/*
//*
//*=====================================================================
//* STEP013: 打刻照合 + 曜日判定
//*=====================================================================
//STEP013 EXEC PGM=ZAN03CHK,PARM='YEARMONTH=&YEARMONTH'
//ZAN03R01 DD DSN=OVT-NODUP.DAT,DISP=(OLD,DELETE)
//ZAN03R02 DD DSN=PUNCH-SORTED.DAT,DISP=(OLD,DELETE)
//ZAN03R03 DD DSN=HOLIDAY-FILE.DAT,DISP=SHR
//ZAN03W01 DD DSN=OVT-CHECKED.DAT,DISP=(NEW,PASS)
//ZAN03W02 DD DSN=ERROR-LOG.DAT,DISP=(NEW,PASS)
```
### ZANJ015 — 取消マッチング
```jcl
//ZANJ015 JOB (ACCT),'取消マッチング',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP015S1: チェック済データ SORT
//*=====================================================================
//STEP015SA EXEC PGM=SORT
//SORTIN DD DSN=OVT-CHECKED.DAT,DISP=(OLD,DELETE)
//SORTOUT DD DSN=OVT-SORTED.DAT,DISP=(NEW,PASS)
//SYSIN DD *
SORT FIELDS=(1,8,CH,A),EQUALS
/*
//*
//*=====================================================================
//* STEP015S2: 取消データ SORT
//*=====================================================================
//STEP015SB EXEC PGM=SORT
//SORTIN DD DSN=OVT-CANCEL.DAT,DISP=(OLD,DELETE)
//SORTOUT DD DSN=OVT-CSORT.DAT,DISP=(NEW,PASS)
//SYSIN DD *
SORT FIELDS=(1,8,CH,A),EQUALS
/*
//*
//*=====================================================================
//* STEP015: マッチング
//*=====================================================================
//STEP015 EXEC PGM=ZAN04MAT,PARM='YEARMONTH=&YEARMONTH'
//ZAN04R01 DD DSN=OVT-SORTED.DAT,DISP=(OLD,DELETE)
//ZAN04R02 DD DSN=OVT-CSORT.DAT,DISP=(OLD,DELETE)
//ZAN04W01 DD DSN=OVT-MATCHED.DAT,DISP=(NEW,PASS)
//ZAN04W02 DD DSN=OVT-DBCLEAN.DAT,DISP=(NEW,PASS)
//ZAN04W03 DD DSN=ERROR-LOG.DAT,DISP=(NEW,PASS)
```
### ZANJ020 — 残業時間集計
```jcl
//ZANJ020 JOB (ACCT),'残業時間集計',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP020S: マッチング済データ SORT
//*=====================================================================
//STEP020S EXEC PGM=SORT
//SORTIN DD DSN=OVT-MATCHED.DAT,DISP=(OLD,DELETE)
//SORTOUT DD DSN=OVT-SORTED2.DAT,DISP=(NEW,PASS)
//SYSIN DD *
SORT FIELDS=(1,8,CH,A,35,2,CH,A),EQUALS
/*
//*
//*=====================================================================
//* STEP020: キーブレイク集計
//*=====================================================================
//STEP020 EXEC PGM=ZAN05CAL,PARM='YEARMONTH=&YEARMONTH'
//ZAN05R01 DD DSN=OVT-SORTED2.DAT,DISP=(OLD,DELETE)
//ZAN05W01 DD DSN=OVT-SUMMARY.DAT,DISP=(NEW,PASS)
```
### ZANJ025 — 残業統計DB更新
```jcl
//ZANJ025 JOB (ACCT),'残業統計DB更新',CLASS=A
//*
//*--- パラメータ定義 ---
//SETPARM SET YEARMONTH=202605
//*
//*=====================================================================
//* STEP025: DB更新(SORT不要)
//*=====================================================================
//STEP025 EXEC PGM=ZAN06UPD,PARM='YEARMONTH=&YEARMONTH'
//ZAN06R01 DD DSN=OVT-SUMMARY.DAT,DISP=(OLD,DELETE)
//ZAN06R02 DD DSN=OVT-DBCLEAN.DAT,DISP=(OLD,DELETE)
//ZAN06W01 DD DSN=ERROR-LOG.DAT,DISP=(NEW,PASS)
```
### 実行順序
BサブシステムのJOBは、AサブシステムのKINJ020(打刻チェック)出力を入力とする。
そのためJCL起動時は以下の順序を保証すること。
1. Aサブシステム KINJ020KIN03PCK)→ EDITED-PUNCH生成
2. Bサブシステム ZANJ010〜ZANJ025(必要時順次実行)
KINJ030(日別計算)は各打刻確定後であればいつでも実行可能で、ZANJ系列との依存関係はない。
---
## プログラム詳細
### STEP010: ZAN01CHK(残業申請振分処理) — 振り分け
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 残業申請振分処理 |
| **PGMパターン** | 振り分け |
| **SORT前処理** | 不要 |
| **入力ファイル** | OVT-APPLY80B / CSV形式) |
| **出力ファイル** | OVT-VALID80B/ OVT-CANCEL80B/ ERROR-LOGVB |
| **件数変化** | N件 → 有効M件 + 取消K件 + 異常L件(M+K+L=N |
| **使用共通関数** | SUB01DAT(運用日付), SUB02MSG(メッセージ), SUB03ENDABEND, SUB04CHK(項目チェック) |
**入力CSVレコード例:** `A001,EMP001,20260610,1800,2000,0`
**機能:**
1. READ OVT-APPLY
2. **UNSTRING** でCSVレコードを各項目に分解
3. **UNSTRING TALLYING** でカンマ区切り数をカウント(項目数妥当性確認)
4. **EVALUATE** でステータスを判定
- 0 or 1 → 有効申請として次へ
- 9 → OVT-CANCEL に書出し
- その他 → エラーとして処理
5. SUB04CHK で日付(DATE8)・時刻(TIME4)・社員番号(NUMERIC)を検証
6. 加班時間計算(時刻はHHMM形式のため分単位に変換してから減算)
```
開始分 = (開始時刻 / 100) * 60 + (開始時刻 - (開始時刻 / 100) * 100)
終了分 = (終了時刻 / 100) * 60 + (終了時刻 - (終了時刻 / 100) * 100)
加班時間(分) = 終了分 - 開始分
```
- 開始時刻 < DINNER-START1830 → エラー(18:30以前は食事時間のため加班不可)
- 加班時間 < 30分 → エラー(0.5h未満)
- 開始時刻 ≧ 終了時刻 → エラー
7. 通過 → OVT-VALID に書出し
8. エラー → 該当レコードをERROR-LOG用01レコードにMOVEしてWRITE出力
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| UNSTRING | CSVレコードの項目分解(TALLYING句で区切り数カウント) |
| STRING | エラーレコード編集 |
---
### STEP012: ZAN02CHK(時間帯重複チェック処理) — 項目チェック
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 時間帯重複チェック処理 |
| **PGMパターン** | 項目チェック |
| **SORT前処理** | OVT-VALID → 社員番号+日付+開始時刻昇順 |
| **入力ファイル** | OVT-VSORT80B |
| **出力ファイル** | OVT-NODUP80B/ ERROR-LOGVB |
| **件数変化** | M件 → 通過K件 + 重複エラー(M-K)件 |
| **使用共通関数** | SUB01DAT(運用日付), SUB02MSG(メッセージ), SUB03ENDABEND |
**機能:**
OVT-VSORT は既に社員番号+日付+開始時刻でソート済。順次READしながら直前レコードと比較する。
1. READ OVT-VSORT → 現レコード取得成功
2. 直前レコードと同一社員番号+日付 かつ 前終了時刻 > 現開始時刻 の場合 → **時間帯重複**
- 境界接続(前終了時刻 = 現開始時刻)は重複としない
3. 重複 → ERROR-LOG へ
4. 重複なし → OVT-NODUP へ書出し
5. 社員番号 or 日付が変わったら直前レコードをリセット
6. **SET** で重複有無フラグを設定
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| SET | 重複有無フラグ設定 |
| CONTINUE | リセット時の空処理 |
---
### STEP013: ZAN03CHK(打刻時間照合処理) — マッチング(N:1)
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 打刻時間照合処理 |
| **PGMパターン** | マッチング(N:1) |
| **SORT前処理** | EDITED-PUNCH → 社員番号+日付昇順 |
| **入力ファイル** | OVT-NODUP80B/ PUNCH-SORTED80B |
| **出力ファイル** | OVT-CHECKED80B/ ERROR-LOGVB |
| **件数変化** | K件 → 通過P件 + エラー(K-P)件 |
| **使用共通関数** | SUB02MSG(メッセージ) |
**機能:**
#### Step1:打刻時間内チェック
OVT-NODUP と PUNCH-SORTED を社員番号+日付で突合(共にソート済のため順次READで処理可)。
1. READ OVT-NODUP → 現レコード
2. 該当社員番号+日付の打刻レコードを PUNCH-SORTED から READ
3. 打刻データあり かつ 以下を両方満たすことを確認:
- 申請開始時刻 ≧ 出勤時刻
- 申請終了時刻 ≦ 退勤時刻
4. 範囲外 または 打刻データなし → ERROR-LOG
#### Step2:曜日判定
**SEARCH ALL** で休日マスタファイル(WORKING-STORAGE)を検索し、申請日の種別を判定。
Aサブシステムと同一の休日定義に従う(HOLIDAY-FILEファイル相当のデータを保持)。
##### 休日ファイル初期化手順
初期処理(1000ITTSOR)でHOLIDAY-FILEを全件READし、WORKING-STORAGEのHOLIDAY-TABLEに格納する。
```
* 休日データ取得(ファイル読込み)
OPEN INPUT HOLIDAY-FILE
READ HOLIDAY-FILE
... 逐次処理
CLOSE HOLIDAY-FILE
```
```
01 HOLIDAY-TABLE.
05 HOLIDAY-CNT PIC 9(004).
05 HOLIDAY-DATA OCCURS 1 TO 366 TIMES
DEPENDING ON HOLIDAY-CNT
INDEXED BY HD-IDX.
10 HD-DATE PIC 9(008).
10 HD-TYPE PIC X(001).
88 HD-HOLIDAY VALUE 'H'.
```
```
SET HD-IDX TO 1
SEARCH ALL HOLIDAY-DATA
AT END
SET OVT-TYPE TO 'W'
WHEN HD-DATE(HD-IDX) = APPL-DATE
IF HD-HOLIDAY(HD-IDX)
SET OVT-TYPE TO 'H'
ELSE
SET OVT-TYPE TO 'W'
END-IF
END-SEARCH
```
- HOLIDAY-FILE該当 → OVT-TYPE='H'
- 該当なし → OVT-TYPE='W'
**MOVE** で OVT-TYPE を設定。
全通過レコードは OVT-TYPE を付加して OVT-CHECKED に書出し。
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| SEARCH ALL | 休日マスタテーブル二分探索 |
| SET | テーブル添字操作 |
---
### STEP015: ZAN04MAT(取消マッチング処理) — マッチング1:1
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 取消マッチング処理 |
| **PGMパターン** | マッチング(1:1) |
| **SORT前処理** | OVT-CHECKED → 申請番号昇順 |
| | OVT-CANCEL → 申請番号昇順 |
| **入力ファイル** | OVT-SORTED80B/ OVT-CSORT80B |
| **出力ファイル** | OVT-MATCHED80B/ OVT-DBCLEAN80B/ ERROR-LOGVB |
| **件数変化** | 有効P件 + 取消R件 → 通過Q件 + 消滅S件 + DB削除T件(P=Q+S, R=S+T |
| **使用共通関数** | SUB01DAT(運用日付), SUB02MSG(メッセージ), SUB03ENDABEND |
**機能:**
キー=**申請番号(APPL-ID** で OVT-SORTED(有効申請)と OVT-CSORT(取消)をマッチング1:1。
マッチング制御の分岐:
| 条件 | 結果 | 処理 |
|------|------|------|
| SORTED.APPL-ID < CSORT.APPL-ID | 申請のみ(取消なし) | OVT-MATCHEDへ |
| SORTED.APPL-ID = CSORT.APPL-ID | 両方あり(取消済) | 消滅(出力なし)。ただし監査のため取消情報をERROR-LOGに記録(エラー区分=03、取消申請番号+原本データ) |
| SORTED.APPL-ID > CSORT.APPL-ID | 取消のみ(既DB登録済) | OVT-DBCLEANへ |
同一申請番号グループ内の出力順に **処理番号(OA-PROC-SEQ** を1から付与する。
**STRING** で申請情報を編集して OVT-MATCHED に書出す。
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| STRING | 申請情報編集出力 |
---
### STEP020: ZAN05CAL(残業時間集計処理) — キーブレイク集計
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 残業時間集計処理 |
| **PGMパターン** | キーブレイク(集計) |
| **SORT前処理** | OVT-MATCHED → 申請番号+処理番号昇順 |
| **入力ファイル** | OVT-SORTED280B |
| **出力ファイル** | OVT-SUMMARY80B |
| **件数変化** | Q件 → 申請番号ユニーク数(L件、L≦Q) |
| **使用共通関数** | SUB01DAT(運用日付), SUB02MSG(メッセージ), SUB03ENDABEND, SUB05TIM(時刻丸め) |
**機能:**
#### Step1:キーブレイク制御
同一申請番号内に複数レコード(例:申請(0) + 承認済(1))がある場合、グループ内の**最終レコード**を集約結果として採用する。
```
入力(申請番号+処理番号昇順):
A001,EMP01,6/10,1800,2000,0 ← 申請(処理番号1
A001,EMP01,6/10,1800,2000,1 ← 承認済(処理番号2 → 最新)
集約結果: 承認済(1) を採用、申請(0) は捨てる
```
- READ → 申請番号が変わるまで読み進める
- ブレイク(申請番号切替)時に、**直前に保持していたレコード**を出力
- グループ内の途中レコードは **CONTINUE** で読み飛ばす
- **SET** で集約結果の有無フラグを設定
#### Step2:加班時間計算
終了時刻 - 開始時刻 を分数で計算 → **DIVIDE** で時間に変換。
```
DIVIDE 分 BY 60 GIVING 時間 REMAINDER 剰余
```
**SUB05TIMmode 20.1h単位切捨)** を呼び出して丸め処理。加班の丸めは0.1h単位切捨が正となる。
**COMPUTE ROUNDED ON SIZE ERROR** で内部計算のエラー処理。
```
COMPUTE WRK-OVT-HOURS ROUNDED = WRK-MINUTES / 60
ON SIZE ERROR
CONTINUE
MOVE ZERO TO WRK-OVT-HOURS
END-COMPUTE
```
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| COMPUTE ROUNDED ON SIZE ERROR | 丸め計算+エラー処理 |
| DIVIDE | 分→時間変換 |
| CONTINUE | キーブレイク制御の空分岐 |
| SET | 集計結果フラグ設定 |
| ACCEPT FROM DATE | 処理日付取得 |
---
### STEP025: ZAN06UPD(残業統計DB更新処理) — DB更新
| 項目 | 内容 |
|------|------|
| **プログラム名称** | 残業統計DB更新処理 |
| **PGMパターン** | DB更新 |
| **SORT前処理** | 不要 |
| **入力ファイル** | OVT-SUMMARY80B/ OVT-DBCLEAN80B |
| **出力ファイル** | OVERTIME-DBDB2/ ERROR-LOGVB |
| **使用共通関数** | SUB01DAT(運用日付), SUB02MSG(メッセージ), SUB03ENDABEND |
**機能:**
全DB操作は埋め込みSQL`EXEC SQL`)で直接記述する。
#### 処理1OVT-SUMMARY → DB登録
**EXEC SQL** でDB2にINSERT/UPDATEを発行する。
```
EXEC SQL
INSERT INTO OVT-APPLICATIONS
(APPL-ID, EMP-ID, APPL-DATE, OVT-TYPE,
START-TIME, END-TIME, OVT-HOURS, STATUS)
VALUES
(:WRK-APPL-ID, :WRK-EMP-ID, :WRK-APPL-DATE,
:WRK-OVT-TYPE, :WRK-START-TIME, :WRK-END-TIME,
:WRK-OVT-HOURS, :WRK-STATUS)
END-EXEC.
IF SQLCODE NOT = 0
* 同一APPL-ID存在 → UPDATEに変更
EXEC SQL
UPDATE OVT-APPLICATIONS SET
STATUS = :WRK-STATUS,
UPDATED-AT = CURRENT TIMESTAMP
WHERE APPL-ID = :WRK-APPL-ID
END-EXEC
END-IF.
```
OVT-MONTHLY のUPSERTも同様。
```
EXEC SQL
SELECT OVT-HOURS, OVT-COUNT
INTO :DB-OVT-HOURS, :DB-OVT-COUNT
FROM OVT-MONTHLY
WHERE EMP-ID = :WRK-EMP-ID
AND YEAR-MONTH = :WRK-YEAR-MONTH
AND OVT-TYPE = :WRK-OVT-TYPE
END-EXEC.
IF SQLCODE = 0
* UPDATE(加算)
EXEC SQL
UPDATE OVT-MONTHLY SET
OVT-HOURS = OVT-HOURS + :WRK-OVT-HOURS,
OVT-COUNT = OVT-COUNT + 1,
UPDATED-AT = CURRENT TIMESTAMP
WHERE EMP-ID = :WRK-EMP-ID
AND YEAR-MONTH = :WRK-YEAR-MONTH
AND OVT-TYPE = :WRK-OVT-TYPE
END-EXEC
ELSE
* INSERT(新規)
EXEC SQL
INSERT INTO OVT-MONTHLY
(EMP-ID, YEAR-MONTH, OVT-TYPE,
OVT-HOURS, OVT-COUNT, UPDATED-AT)
VALUES
(:WRK-EMP-ID, :WRK-YEAR-MONTH, :WRK-OVT-TYPE,
:WRK-OVT-HOURS, 1, CURRENT TIMESTAMP)
END-EXEC
END-IF.
```
#### 処理2OVT-DBCLEAN → DB取消
```
EXEC SQL
UPDATE OVT-APPLICATIONS SET
STATUS = '9',
UPDATED-AT = CURRENT TIMESTAMP
WHERE APPL-ID = :WRK-APPL-ID
END-EXEC.
IF SQLCODE NOT = 0
該当なし → ERROR-LOG に書出し
PERFORM 9999ABDSOR
END-IF.
```
OVT-MONTHLY の減算UPDATE
```
EXEC SQL
UPDATE OVT-MONTHLY SET
OVT-HOURS = OVT-HOURS - :WRK-OVT-HOURS,
OVT-COUNT = OVT-COUNT - 1,
UPDATED-AT = CURRENT TIMESTAMP
WHERE EMP-ID = :WRK-EMP-ID
AND YEAR-MONTH = :WRK-YEAR-MONTH
AND OVT-TYPE = :WRK-OVT-TYPE
END-EXEC.
IF SQLCODE NOT = 0
* OVT-MONTHLYレコード不存在 → データ不整合の可能性
該当エラー → ERROR-LOG に書出し
PERFORM 9999ABDSOR
END-IF.
```
**注意**: OVT-MONTHLY減算UPDATEの失敗時は、OVT-MONTHLYに対応レコードが存在しないことを意味する。
ファイルとDBの不整合としてエラーログに記録した上でABENDさせる。
**SQLCODE** でDB操作の成否を確認し、異常時は ERROR-LOG に出力。
**新規カバレッジ構文:**
| 構文 | 用途 |
|------|------|
| EXEC SQL | DB2埋め込みSQLによるDB操作 |
| IF | SQLCODE判定による分岐 |
| PERFORM VARYING | 月バリデーションループ(1〜12月) |
| PERFORM TEST AFTER | OVT-MONTHLY減算リトライ制御 |
| MULTIPLY | 時間→分変換(MULTIPLY BY 60 GIVING |
| SUBTRACT | 残容量検証(SUBTRACT FROM GIVING |
---
## レコード構成
### OVT-VALID / OVT-CANCEL / OVT-NODUP / OVT-CHECKED80B
| # | 項目 | 名称 | 開始 | 長さ | 属性 | 備考 |
|---|------|------|------|------|------|------|
| 1 | 申請番号 | OA-APPL-ID | 1 | 8 | X(8) | |
| 2 | 社員番号 | OA-EMP-ID | 9 | 8 | 9(8) | |
| 3 | 申請日 | OA-APPL-DATE | 17 | 8 | 9(8) | YYYYMMDD |
| 4 | 開始時刻 | OA-START-TIME | 25 | 4 | 9(4) | HHMM |
| 5 | 終了時刻 | OA-END-TIME | 29 | 4 | 9(4) | HHMM |
| 6 | ステータス | OA-STATUS | 33 | 1 | X(1) | 0/1/9 |
| 7 | OVT-TYPE | OA-OVT-TYPE | 34 | 1 | X(1) | W/HOVT-CHECKED以降のみ有効) |
| 8 | 予備 | OA-FILLER | 35 | 46 | X(46) | |
### OVT-MATCHED80B
| # | 項目 | 名称 | 開始 | 長さ | 属性 | 備考 |
|---|------|------|------|------|------|------|
| 1 | 申請番号 | OA-APPL-ID | 1 | 8 | X(8) | |
| 2 | 社員番号 | OA-EMP-ID | 9 | 8 | 9(8) | |
| 3 | 申請日 | OA-APPL-DATE | 17 | 8 | 9(8) | YYYYMMDD |
| 4 | 開始時刻 | OA-START-TIME | 25 | 4 | 9(4) | HHMM |
| 5 | 終了時刻 | OA-END-TIME | 29 | 4 | 9(4) | HHMM |
| 6 | ステータス | OA-STATUS | 33 | 1 | X(1) | 0/1 |
| 7 | OVT-TYPE | OA-OVT-TYPE | 34 | 1 | X(1) | W/H |
| 8 | 処理番号 | OA-PROC-SEQ | 35 | 2 | 9(2) | 同申請内連番 |
| 9 | 予備 | OA-FILLER | 37 | 44 | X(44) | |
社員名・部署コードは DB2OVT-APPLICATIONSテーブル)側で保持する。
### OVT-SUMMARY80B
| # | 項目 | 名称 | 開始 | 長さ | 属性 | 備考 |
|---|------|------|------|------|------|------|
| 1 | 申請番号 | OS-APPL-ID | 1 | 8 | X(8) | |
| 2 | 社員番号 | OS-EMP-ID | 9 | 8 | 9(8) | |
| 3 | 申請日 | OS-APPL-DATE | 17 | 8 | 9(8) | YYYYMMDD |
| 4 | 開始時刻 | OS-START-TIME | 25 | 4 | 9(4) | HHMM |
| 5 | 終了時刻 | OS-END-TIME | 29 | 4 | 9(4) | HHMM |
| 6 | 加班時間 | OS-OVT-HOURS | 33 | 5 | 9(4)V9(1) | SUB05TIM丸め後 |
| 7 | OVT-TYPE | OS-OVT-TYPE | 38 | 1 | X(1) | W/H |
| 8 | 予備 | OS-FILLER | 39 | 42 | X(42) | |
### OVT-DBCLEAN80B
| # | 項目 | 名称 | 開始 | 長さ | 属性 |
|---|------|------|------|------|------|
| 1 | 申請番号 | OD-APPL-ID | 1 | 8 | X(8) |
| 2 | 予備 | OD-FILLER | 9 | 72 | X(72) |
---
## 新規COBOL構文カバレッジ一覧(14種)
| # | 構文 | ZAN01CHK | ZAN02CHK | ZAN03CHK | ZAN04MAT | ZAN05CAL | ZAN06UPD |
|---|------|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:|
| 1 | UNSTRING | ● | | | | | |
| 2 | STRING | ● | | | ● | | |
| 3 | SEARCH ALL | | | ● | | | |
| 4 | SET | | ● | ● | | ● | |
| 5 | COMPUTE ROUNDED ON SIZE ERROR | | | | | ● | |
| 6 | DIVIDE | ● | | | | ● | |
| 7 | CONTINUE | | ● | | | ● | |
| 8 | ACCEPT FROM DATE | | | | | ● | |
| 9 | EXEC SQLINSERT/UPDATE/SELECT/COMMIT/ROLLBACK | | | | | | ● |
|10 | IFSQLCODE分岐) | | | | | | ● |
|11 | PERFORM VARYING | | | | | | ● |
|12 | PERFORM TEST AFTER | | | | | | ● |
|13 | MULTIPLY | | | | | | ● |
|14 | SUBTRACT | | | | | | ● |
---
## 丸めルール
| 条件 | ルール |
|------|--------|
| 加班開始時刻 < DINNER-START1830 | エラー(申請不可、食事時間のため) |
| 加班時間 < 30分 | エラー(申請不可) |
| 加班時間 ≧ 30分 かつ < 0.5h換算 | 0.5hに切上 |
| 加班時間 ≧ 0.5h | 0.1h単位切捨(SUB05TIM mode 2 |
例:0.5h→0.5, 0.7h→0.7, 1.55h→1.5(切捨のため1.5、切上なら1.6)