Craft ERP 週定義・週次集計仕様 — 週番号を企業ごとに設定可能にする
週次売上集計と前年同週比較のための「週」の定義(週を何曜日から始めるか・その年の第1週をどう決めるか)を整理した記録です。
結論:方式コードではなく、week_start_day(週開始曜日)と week_min_days(第一週最小日数)の2項目で週定義を表現する。ISO 8601 もこの組み合わせの1つとして扱い、システム全体で固定せず企業単位で設定可能とする。
本記事の前提となる Craft ERP(生成AI × 仕様駆動開発による小売業向け基幹業務システムの開発プロジェクト)の概要・目的・技術選定方針は Craft ERP Overview にまとめています。
1. はじめに
Craft ERPでは、売上を日次・週次・月次などの単位で集計し、前年同週売上との比較を行うことを想定します。週次集計では、単純に7日間をまとめるだけではなく、次の2点を明確に定義する必要があります。
- 週を何曜日から開始するか
- その年の第1週をどのように決めるか
これらのルールは企業や業務運用によって異なる可能性があります。そのため、Craft ERPでは週定義をシステム全体で固定せず、企業単位で設定可能とする方針を採用します。
2. 週番号が必要となる理由
週次売上は、週開始日と週終了日だけでも集計できます。しかし、前年同週比較を行う場合、週番号または同等の週識別情報がある方が扱いやすくなります。
2026年第27週 売上 1,500,000円
2025年第27週 売上 1,300,000円
前年差 +200,000円
前年比 115.4%
週番号を持つことで、2026-W27 → 2025-W27 という前年同週の対応を明確に取れます。そのため、Craft ERPでは週次集計において週番号を利用します。
3. 週には複数の定義が存在する
「第1週」の決め方には複数の考え方があります。
| 方式 | 第1週の考え方 |
|---|---|
| 1月1日基準 | 1月1日を含む週を第1週とする |
| ISO 8601 | 新年の日を4日以上含む週を第1週とする |
| 完全週方式 | 新年に7日すべて含まれる最初の週を第1週とする |
| 業務独自方式 | 企業独自の週定義を使用する |
ISO 8601では、週は月曜日から始まり、新しい年の日を4日以上含む週がその年の第1週となります。同じ日付であっても、採用する週定義によって所属する週年・週番号が変わる可能性があります。
2025/12/29 ~ 2026/01/04
ある定義では → 2026年第1週
別の定義では → 2025年最終週
したがって、週番号を利用する場合は、週開始曜日だけでなく、第1週の決定方法も定義する必要があります。
4. 企業ごとに週開始曜日が異なる可能性
企業や業務運用によって、1週間の開始曜日が異なる可能性があります。
【月曜日始まり】 【日曜日始まり】 【木曜日始まり】
月 火 水 木 金 土 日 日 月 火 水 木 金 土 木 金 土 日 月 火 水
Craft ERPは、小売、卸売、EC、メーカー直販、物流拠点など、商品を扱う複数の業態を対象としています。そのため、週開始曜日をCraft ERP全体で固定せず、企業単位で設定可能とします。
5. 採用する週定義モデル
Craft ERPでは、企業ごとの週定義を次の2項目で表現します。
| フィールド | 意味 | 型・制約 |
|---|---|---|
week_start_day | 週の開始曜日(1=月曜日 ~ 7=日曜日) | SMALLINT NOT NULL / CHECK (BETWEEN 1 AND 7) |
week_min_days | その年の第1週として成立するために、新しい年の日が最低何日含まれている必要があるか(1~7) | SMALLINT NOT NULL / CHECK (BETWEEN 1 AND 7) |
この方式では、「週の開始曜日」と「第一週に必要な最小日数」の組み合わせで週番号を定義します。なお、この2項目による週定義は、Unicode CLDR の週データや Java の WeekFields(firstDayOfWeek + minimalDaysInFirstWeek)と同じ、確立されたモデルです。
実装上の注意(PostgreSQL):PostgreSQL の EXTRACT(WEEK FROM ...) および to_char(..., 'IW') は ISO 8601 週固定であり、企業ごとの週定義には利用できません。週番号の算出は、week_start_day と week_min_days に基づく自前の週計算ロジック(アプリケーション側、またはユーザー定義関数)として実装します。
6. week_min_days の具体例
week_min_days = 1(1月1日を含む週を第1週)
月曜日始まりで、新しい年の日が1日以上含まれていれば第1週とします。
2025/12/29 月
2025/12/30 火
2025/12/31 水
2026/01/01 木
2026/01/02 金
2026/01/03 土
2026/01/04 日
この週には2026年の日が4日含まれるため、2026-W01 となります。
week_min_days = 4(ISO 8601 と同じ)
月曜日始まりで、新しい年の日を4日以上含む週を第1週とします。week_start_day = 1 + week_min_days = 4 は ISO 8601 と同じ組み合わせです。
week_min_days = 7(完全週方式)
月曜日始まりで、新しい年の日を7日すべて含む最初の完全な週を第1週とします。上記の 2025/12/29 ~ 2026/01/04 は2026年の日が4日しか含まれないため第1週にはならず、次の 2026/01/05 ~ 2026/01/11 が 2026-W01 となります。
7. 方式コードを採用しない理由
別案として、JANUARY_1 / ISO / FIRST_FULL_WEEK のような方式コードを保持する設計も考えられます。しかし、この方式ではアプリケーションロジックが方式ごとに分岐します。
if rule == "JANUARY_1":
...
elif rule == "ISO":
...
elif rule == "FIRST_FULL_WEEK":
...
一方、week_start_day と week_min_days で定義すれば、同じ週計算ロジックで複数の週定義を表現できます(月曜始まり+1日、月曜始まり+4日、日曜始まり+1日、土曜始まり+7日 …)。このため、Craft ERPでは方式コードではなく、週計算の構成要素そのものを保持します。
8. 企業設定画面での表示
データベース上は数値で保持しますが、ユーザーへ数値を直接意識させる必要はありません。
週の開始曜日
[ 月曜日 ▼ ]
第一週の決め方
[ 1月1日を含む週 ▼ ] → week_min_days = 1
[ 新年を4日以上含む週(ISO方式) ▼ ] → week_min_days = 4
[ 新年に完全に含まれる最初の週 ▼ ] → week_min_days = 7
画面では業務ユーザーが理解しやすい説明を表示し、内部では week_min_days として統一的に管理します。
9. 売上日が所属する週は企業設定によって変わる
同じ売上日でも、企業の週開始曜日によって所属週が異なります。
売上日 = 2026-07-05(日曜日)
月曜日始まり(week_start_day = 1)
→ 所属週: 2026/06/29 ~ 2026/07/05
日曜日始まり(week_start_day = 7)
→ 所属週: 2026/07/05 ~ 2026/07/11
このため、週次売上集計では必ず対象企業の週設定を考慮します。
10. ISO週を固定採用しない理由
ISO週は国際標準として明確な定義(月曜始まり・第一週最小日数4日)を持ちます。しかし、Craft ERPの対象業務では、企業独自の売上管理や週次比較ルールを利用する可能性があります。ISO週を固定すると、「週は日曜日始まり」「1月1日を含む週を第1週としたい」「完全な7日間から第1週を始めたい」といった企業要件へ対応できません。
そのため、ISO方式は選択可能な週定義の1つとして扱い、Craft ERP全体の固定ルールにはしません。
11. 週次売上の識別情報
企業独自の週定義を採用するため、iso_year や iso_week という名称は使用しません。週次集計では次の項目を利用します。
| week_year | week_no | week_start_date | week_end_date |
|---|---|---|---|
| 2026 | 1 | 2025-12-29 | 2026-01-04 |
| 2026 | 2 | 2026-01-05 | 2026-01-11 |
| 2026 | 27 | 2026-06-29 | 2026-07-05 |
前年同週比較の検索概念:
WHERE week_year = :week_year - 1
AND week_no = :week_no
53週を持つ年の前年比較
週定義によっては、1年が52週の年と53週の年が存在します(おおむね5~6年に1回、53週の年が発生)。このため、week_no = 53 の前年同週比較では、前年に第53週が存在しないケースがあります。53週の扱いは、次のいずれかを業務要件として決定します。
- 前年に第53週が存在しない場合は「比較対象なし」とする
- 前年の第52週と比較する
- 前年の同期間(週開始日ベース)と比較する
Craft ERPでは、まず「比較対象なし」を基本とし、必要に応じて業務要件で見直します。
12. 週開始日・週終了日も保持する理由
week_year と week_no だけでも週を識別できます。しかし、運用時の可読性を考慮し、週開始日と週終了日も保持することを推奨します。テーブルを直接確認した担当者は、週計算ロジックを知らなくても対象期間を理解できます。
これはCraft ERPの設計原則である、
開発時の構造美より、運用時の可読性を優先する。テーブルを開いた人が、業務データの状態を初見で理解できる設計を目指す。
という方針にも合致します。
13. 企業設定変更時の注意
企業が week_start_day または week_min_days を変更すると、同じ日付でも所属週が変わる可能性があります。
売上日 2026-07-05
変更前(月曜日始まり)→ 2026/06/29 ~ 2026/07/05 の週
変更後(日曜日始まり)→ 2026/07/05 ~ 2026/07/11 の週
このため、過去の週次集計結果を企業設定の現在値だけから毎回再解釈すると、過去集計の所属週が変化する可能性があります。週次集計結果を保存する場合は、集計時点の week_year / week_no / week_start_date / week_end_date を保持することを推奨します。必要に応じて、週定義設定の変更履歴管理も検討します。
14. 推奨企業設定
| 日本語名 | フィールド | 型 | 説明 |
|---|---|---|---|
| 週開始曜日 | week_start_day | SMALLINT | 1=月曜日 ~ 7=日曜日 |
| 第一週最小日数 | week_min_days | SMALLINT | 第1週に必要な新年の日数。1~7 |
CHECK (week_start_day BETWEEN 1 AND 7)
CHECK (week_min_days BETWEEN 1 AND 7)
15. 最終方針
Craft ERPでは、週次売上の週定義をシステム全体で固定せず、企業ごとに week_start_day(週開始曜日)と week_min_days(第一週最小日数)を設定可能とします。この2項目の組み合わせにより、次の週定義を同じモデルで表現できます。
- 1月1日を含む週を第1週
- ISO 8601方式
- 完全週方式
- 企業独自の週定義
週次売上では、企業設定に基づいて week_year / week_no / week_start_date / week_end_date を決定し、前年同週比較は同一企業の週定義に基づく week_year と week_no を利用します。この設計により、企業ごとの業務運用へ柔軟に対応しながら、週計算ロジックを統一的に実装できます。
参考資料
- Unicode CLDR Week Data — unicode.org
- Java
WeekFields— docs.oracle.com - PostgreSQL Date/Time Functions and Operators — postgresql.org
- ISO 8601 week date overview — en.wikipedia.org
まとめ
- 週次集計・前年同週比較には「週開始曜日」と「第1週の決定方法」の定義が必要
- 方式コードではなく week_start_day + week_min_days の2項目で表現(CLDR / Java WeekFields と同じ確立されたモデル)。同じ計算ロジックで ISO 8601 を含む複数の週定義を扱える
- 週定義は企業単位で設定可能とし、集計結果には week_year / week_no / 週開始日・終了日を保持して運用時の可読性と設定変更への耐性を確保
- PostgreSQL の EXTRACT(WEEK) は ISO 固定のため使わず、自前の週計算ロジックとして実装
- 53週の年の前年比較は「比較対象なし」を基本とし、業務要件で見直す