<!-- Generated by scripts/build-prompts.mjs — do not edit by hand. -->
# このツールを作ってください: Verpackungsregister

*以下がすべて機能要件です。書かれているとおりに実装してください。書かれていない点は自分で判断し、何を前提にしたかを述べてください。*

## 出発点

**openToolbox** テンプレートを使ってください：https://github.com/m-dohmen/openToolbox。まずそのリポジトリの `AGENTS.md` を読むこと — スキーマの形、フィールド型、単一ファイルビルドを壊す禁止事項について、そこが正典です。業務固有の内容はすべて `src/domain.js` 一つに収めます。

> openToolbox skill を導入済みなら（`claude plugin install opentoolbox@opentoolbox`）、このファイルを貼るだけで足ります。テンプレートは skill が自分で取得します。

## 背景にある問題

2026 年 8 月 12 日から、EU の包装規則（PPWR）は**市場に出すすべての包装**について適合宣言と技術文書を求めます。大企業には担当部署があります。品目 40 点の小さな製造者にとって、それをやるのは経営者本人です。そして気づきます。決め手になる数値は自社にはなく、段ボール業者・ラベル印刷業者・フィルムメーカーの側にあるのだと。これはソフトウェアの問題ではなく、情報収集の問題です。ツールの役目は、何がまだ足りず、それを誰から得るべきかを見えるようにすることです。

## 最終的に何が出来ていればよいか

ダブルクリックで開ける自己完結型の HTML ファイル 1 つ。サーバーもインストールも不要です。ファイル自体がデータベースでもあり、保存するとレコードを埋め込んだ新しい HTML ファイルが書き出されます。

## レコード種別

エンティティキー `packagings` — 1 件は **Verpackung**、複数は **Verpackungen**。

**フィールド**

| キー | ラベル | 型 | 補足 |
| --- | --- | --- | --- |
| `name` | Verpackung | text | 必須 |
| `category` | Art | enum | 次のいずれか：Verkaufsverpackung · Umverpackung · Transportverpackung · Serviceverpackung |
| `articles` | Betroffene Artikel | text | 表ヘッダー `Artikel` |
| `docStatus` | Stand der Unterlagen | enum | 次のいずれか：nicht begonnen · beim Lieferanten angefragt · Angaben liegen vor · Erklärung erstellt, 表ヘッダー `Stand` |
| `deadline` | Eigene Frist | date | 表ヘッダー `Frist` |
| `responsible` | Zuständig | text | — |
| `doc` | Konformitätserklärung | attachment | アップロードしたファイル。レコード内に格納, 表ヘッダー `KE` |
| `note` | Notiz | text | 複数行 |
| `weight` | Gewicht gesamt (g) | computed | 計算値。保存はしない, 表ヘッダー `g` |
| `recyclate` | Rezyklatanteil | computed | 計算値。保存はしない, 表ヘッダー `Rez.` |
| `worst` | Schlechteste Klasse | computed | 計算値。保存はしない, 表ヘッダー `Klasse` |

**表示**

- 主列：`name`
- 主列の下に置く 2 行目：`category`
- 表の列（この順）：`name`, `category`, `articles`, `weight`, `recyclate`, `docStatus`, `deadline`
- サイドバーの絞り込み：`category`, `docStatus`
- 未完了として数えなくなる条件：`r.docStatus === 'Erklärung erstellt'`
- 赤で強調する条件：`r.docStatus !== 'Erklärung erstellt' && r.deadline && r.deadline < today()`

**計算フィールド**

描画のたびに算出し、レコードには絶対に書き戻しません。保存した派生値は、入力のどれかが変わった瞬間に誤りになります。

- `weight` (Gewicht gesamt (g)) — `componentsOf(r.id).reduce((sum, c) => sum + (Number(c.weight) || 0), 0)`
- `recyclate` (Rezyklatanteil):

  ```js
  const parts = componentsOf(r.id)
  const total = parts.reduce((s, c) => s + (Number(c.weight) || 0), 0)
  if (!total) return ''
  const share = parts.reduce(
    (s, c) => s + ((Number(c.weight) || 0) * (Number(c.recycled) || 0)) / 100,
    0,
  )
  return Math.round((share / total) * 100) + ' %'
  ```
- `worst` (Schlechteste Klasse):

  ```js
  const grades = componentsOf(r.id)
    .map((c) => c.grade)
    .filter((g) => g && g !== 'noch offen')
  if (!grades.length) return 'noch offen'
  return grades.sort().at(-1)
  ```

**検証ルール**

フィールドをまたぐ条件です。入力フォーム、CSV 取り込み、AI の提案がすべて同じチェックを通るよう、一箇所で効かせてください。

- **条件** `r.docStatus === 'Erklärung erstellt'` → **要求** `responsible`
  **メッセージ:** „Eine ausgestellte Erklärung braucht eine verantwortliche Person.“
- **条件** `r.docStatus === 'Erklärung erstellt'` → **要求** `Boolean(r.doc?.data)`
  **メッセージ:** „Die erstellte Erklärung gehört als Datei an den Datensatz.“

---

エンティティキー `components` — 1 件は **Bestandteil**、複数は **Bestandteile**。

**フィールド**

| キー | ラベル | 型 | 補足 |
| --- | --- | --- | --- |
| `name` | Bestandteil | text | 必須 |
| `packagingId` | Gehört zu | reference | 必須, `packagings` への参照, 表ヘッダー `Verpackung` |
| `material` | Material | enum | 次のいずれか：Papier/Karton · Kunststoff PE · Kunststoff PET · Kunststoff PP · Glas · Aluminium · Weißblech · Verbund |
| `weight` | Gewicht (g) | number | 表ヘッダー `g` |
| `recycled` | Rezyklatanteil (%) | number | 表ヘッダー `% Rez.` |
| `grade` | Recyclingklasse | enum | 次のいずれか：A · B · C · D · E · noch offen, 表ヘッダー `Klasse` |
| `source` | Angabe von | enum | 次のいずれか：Lieferant schriftlich · Datenblatt · selbst geschätzt · fehlt noch, 表ヘッダー `Quelle` |
| `supplier` | Lieferant | text | — |
| `note` | Notiz | text | 複数行 |

**表示**

- 主列：`name`
- 表の列（この順）：`name`, `packagingId`, `material`, `weight`, `recycled`, `grade`, `source`
- サイドバーの絞り込み：`material`, `grade`
- 概要で合計する：`weight`
- 未完了として数えなくなる条件：`() => false`
- 赤で強調する条件：`r.source === 'fehlt noch'`

**検証ルール**

フィールドをまたぐ条件です。入力フォーム、CSV 取り込み、AI の提案がすべて同じチェックを通るよう、一箇所で効かせてください。

- **条件** `Number(r.recycled) > 0` → **要求** `source`
  **メッセージ:** „Zu einem Rezyklatanteil gehört, woher die Zahl stammt.“
- **条件** `r.source === 'Lieferant schriftlich'` → **要求** `supplier`
  **メッセージ:** „Wenn der Lieferant es schriftlich bestätigt hat, gehört sein Name dazu.“

---

## ダッシュボード

絞り込み後ではなく、レコード全体を対象とするタイルです。

- 数値 1 つ：**Verpackungen** — 件数 · „im Register“
- 数値 1 つ：**Noch offen** — 件数（フィルタに一致するレコードのみ）: `r.docStatus !== 'Erklärung erstellt'` · „ohne fertige Erklärung“
- 数値 1 つ：**Angaben fehlen** — 件数（フィルタに一致するレコードのみ）: `r.source === 'fehlt noch'` · „Bestandteile ohne Quelle“
- `docStatus` の値ごとのドーナツ
- `material` の値ごとの棒。指標は `weight` (Gewicht (g)) — **Gewicht je Material (g)**
- `grade` の値ごとの棒。指標は 件数 — **Bestandteile je Recyclingklasse**

## ガイド付き入力

1 件だけ報告すればよく、ツールに不慣れな人のための短い手順です。最後の手順を確定するまで何も書き込まれません。途中でやめても何も残らないこと。

タイトル：**Verpackung aufnehmen**

> Erst die Verpackung, dann ihre Bestandteile. Geschrieben wird nichts, bevor der letzte Schritt bestätigt ist — Abbrechen hinterlässt nichts.

1. **ステップ 1 `packagings`** — Verpackung: フィールド：`name`, `category`, `articles`, `responsible`, `deadline`
2. **ステップ 2 `components`** — Erster Bestandteil: フィールド：`name`, `packagingId`, `material`, `weight`, `recycled`, `grade`, `source`, `supplier`
   次の場合のみ表示：`Boolean(drafts.packagings?.name)`
3. **ステップ 3 `components`** — Liste vom Lieferanten: CSV 取り込み。同じ入力セッションに合流します
   次の場合のみ表示：`Boolean(drafts.packagings?.name)`
4. **ステップ 4** — Prüfen: スキーマから生成される確認画面

完了画面：「Aufgenommen. Was noch fehlt, steht in der Liste unter „Angabe von: fehlt noch".」

## 既定値

`src/app.jsx` の `DEFAULT_SETTINGS`、`DEFAULT_COLORS`、`DEFAULT_HOME` に設定します。

- タイトル: **Verpackungsregister**
- サブタイトル: Konformität nach PPWR, Bestandteil für Bestandteil
- ファイル名: `verpackungsregister`
- バージョン: `1.0`
- 画面の言語: `de`
- 起動時の表示: 通常のツール
- 配色: `accent` #2f6b3a · `band` #17241a · `flag` #b4531c · `ok` #2f6b3a · `pending` #c08a12

## スタートページ

アプリはこの文章から始まります。Markdown の小さな部分集合（見出し、箇条書き、引用、太字、斜体、インラインコード、リンク）です。そのまま使ってください：

```markdown
# Verpackungsregister nach PPWR

Ab dem **12. August 2026** darf in der EU nur noch in Verkehr gebracht werden, was der neuen
Verpackungsverordnung entspricht — mit Konformitätserklärung und technischer Dokumentation
**je Verpackung**.

Für einen Konzern macht das eine Fachabteilung. Für die Manufaktur mit vierzig Artikeln macht das
die Inhaberin — und stellt fest, dass die entscheidenden Angaben gar nicht bei ihr liegen, sondern
bei Kartonlieferant, Etikettendruckerei und Folienhersteller.

## Was diese Demo zeigt

- **Zwei Datenarten**, weil die Recyclingfähigkeit nicht an der Verpackung hängt, sondern an ihren
  Bestandteilen. Ein Sichtfenster aus der falschen Folie kippt die ganze Einheit.
- **Gerechnet statt gepflegt**: Gesamtgewicht, Rezyklatanteil und die schlechteste Klasse ergeben
  sich aus den Bestandteilen.
- **Regeln, die Schätzungen sichtbar machen** — eine Zahl ohne Quelle lässt sich nicht speichern.
- **Anhänge** für die Erklärung selbst, mit hartem Speicherbudget.

> Erfundene Daten, Veranschaulichung der Struktur. Keine Rechtsberatung und kein Nachweis von
> Konformität.
```

## サンプルデータ

初回起動時に空にならないよう、現実味のあるサンプルレコードを Verpackungen: 8, Bestandteile: 15 件用意してください。上のフィールドに合わせて創作します。これは例示であってユーザーのデータではありません。本番データは **Import CSV → replace all** から入れる、と伝えてください。

## 完了条件

- `npm run build` が自己完結した `dist/index.html` を 1 つ出力する
- `npm test` が通る
- ダブルクリックで開き、サンプルレコードが表示される
- 計算フィールドに値が出て、ルールに反するレコードは保存を拒否される
- 設定・配色・スタートページが上の仕様どおりである

## 引き渡す前に

受け取る側に委ねず、自分で決めてください：`copyright` をツールの所有者に設定し、openToolbox リポジトリを指すヘッダーリンクを差し替え、相手がデータ入力だけなら `examplePrompts` を切ります。利用カウンター（設定 → セキュリティ）は引き渡し時に自分から伝えること。

---

*`examples/ppwr-packaging.domain.js` から生成 — [ライブデモ](https://m-dohmen.github.io/openToolbox/demos/ppwr-packaging/) の実際のソースです。再生成は `npm run prompts`。*

*デモのデータはすべて架空です。この種のツールの構造を示すものであり、法的助言でも適合性の証明でもありません。*
