diff --git a/.claude/commands/task-card.md b/.claude/commands/task-card.md index fc751ec..311a5c1 100644 --- a/.claude/commands/task-card.md +++ b/.claude/commands/task-card.md @@ -25,10 +25,11 @@ allowed-tools: Read, Grep, Glob, Bash - 受入条件(DoD): <機械的に検証できる形で。ビルド・テスト以外の固有条件を書く> - 参照: Spec.md §x.y / MES.md No.X-XX-XX-XX / docs-dev/CodeMap.md の該当行 - 想定層: L1(設計判断あり) / L2(仕様確定済の実装) / L3(機械的) +- effort: xhigh / high / medium / low(Orchestration.md §3.4 の表から選ぶ) - 想定変更ファイル数: N ``` ## 自己チェック(出力の最後に1行で) - 変更対象が3ファイルを超える、または「調査で確定」が2項目以上 → **粒度が粗い。分割案を併記する。** -- 認証/在庫更新/実績・原価計算/CSV取込/データモデル変更を含む → 想定層は L1 とし、その理由を書く。 +- 認証/在庫更新/実績・原価計算/CSV取込/データモデル変更を含む → 想定層は L1・effort は xhigh とし、その理由を書く。 diff --git a/CLAUDE.md b/CLAUDE.md index c2e8c98..d0b367b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -125,6 +125,7 @@ Entity(`Core/Entities`)→ `MesAppDbContext` の `DbSet` と `Configurations ## 進め方の要点(詳細は Orchestration.md) - 1セッション=1タスク。無関係な作業に移るときは `/clear`、同じファイル群を触るなら継続 +- モデルは Opus 5.5 で統一し、層は effort で分ける(L1 設計=high、高リスク領域=xhigh、L2 実装=medium、定型=low)。モデル・effort はセッション途中で変えない - 探索は Glob → Grep(`-n`・パス限定)→ 該当行±40行の Read。1タスクあたり読込1,500行・Grep 8回まで - **同じエラーで2回失敗したら3回目を同じやり方で試さない**。前提(変更対象・受入条件)を疑う - 機械的に確認できることは build / test / grep で確認する。推測で「直った」と言わない diff --git a/Orchestration.md b/Orchestration.md index 5003ed6..a642fea 100644 --- a/Orchestration.md +++ b/Orchestration.md @@ -1,7 +1,12 @@ # Claude Code オーケストレーション運用仕様書 プロダクト名:Parallel Factory MES -版: 1.0(2026-08-22 制定) +版: 1.1(2026-09-23 改訂。1.0 は 2026-08-22 制定) + +| 版 | 日付 | 内容 | +|:--|:--|:--| +| 1.1 | 2026-09-23 | Claude Opus 5.5 の提供開始に合わせて見直し。単価表を更新(Sonnet 5 の誤記も訂正)。L2 の既定を「別モデル」から「同じ Opus 5.5 を低い effort で」に変更し、effort を層ごとの主レバーにした(§2.1・§2.4・§3.1・§3.3・§3.4・§4.2・§8) | +| 1.0 | 2026-08-22 | 制定 | 対象: 本リポジトリにおける Claude Code を用いた開発作業すべて --- @@ -36,13 +41,19 @@ Claude Code の使用量(トークン/使用枠)を削減しつつ、成 ## 2. コスト構造(なぜ節約できるのか) ### 2.1 モデル単価 -| モデル | モデルID | 入力 $/1M | 出力 $/1M | -|:--|:--|--:|--:| -| Claude Opus 5 | `claude-opus-5` | $5.00 | $25.00 | -| Claude Sonnet 5 | `claude-sonnet-5` | $3.00 | $15.00 | -| Claude Haiku 4.5 | `claude-haiku-4-5` | $1.00 | $5.00 | +| モデル | モデルID | 入力 $/1M | 出力 $/1M | 文脈長 | 本書での扱い | +|:--|:--|--:|--:|--:|:--| +| **Claude Opus 5.5** | `claude-opus-5-5` | $4.00 | $20.00 | 1M | **L1・L2 の既定** | +| Claude Opus 5 | `claude-opus-5` | $5.00 | $25.00 | 1M | 使わない(5.5 の方が安く新しい) | +| Claude Sonnet 5 | `claude-sonnet-5` | $2.00 | $10.00 | 1M | L2 の選択肢(§3.4 の条件つき) | +| Claude Haiku 4.5 | `claude-haiku-4-5` | $1.00 | $5.00 | 200K | 広域の走査だけ(§4.5) | + +キャッシュ書込は入力単価の約 1.25 倍。キャッシュ読取は Opus 5.5 で $0.20/1M(入力の 1/20)。**先頭文脈を変えずにセッションを継続すると、入力コストは 1/10 以下に落ちる**。キャッシュは**モデルごと**に持たれるため、途中でモデルを切り替えると次のターンは全量が書込扱いになる(§4.2)。 -キャッシュ読取は入力単価の約 0.1 倍、キャッシュ書込は約 1.25 倍。**先頭文脈を変えずにセッションを継続すると、入力コストは概ね 1/10 に落ちる**。 +Fast mode(Opus 5.5 で $8 / $40、標準の2倍)は速度を買う機能で節約にはならない。待ち時間が問題になる場面以外では使わない。 + +#### effort(思考の深さ) +Opus 5.5 は思考を切れない。深さとトークン消費は **effort**(`low` / `medium` / `high` / `xhigh` / `max`)で調整する。API の既定は `medium` で、Opus 5 の既定(`high`)より1段低い。**既定に任せず、タスクの層に合わせて明示する**(§3.1・§3.4)。 ### 2.2 会話は毎ターン全量が再送される N ターン目の入力は「それまでの全履歴」である。したがって 1 セッションの総入力トークンは概ね**ターン数の二乗**に比例する。 @@ -59,7 +70,7 @@ N ターン目の入力は「それまでの全履歴」である。したがっ | MES.md | 518行 | 該当業務Noのみ grep | | docs/*.md | 3,321行 | 該当ファイルのみ | -コードベース全体を1回舐めるだけで Opus 入力 $2.5 相当が飛ぶ。**探索の規律が最大の効き目を持つ**。 +コードベース全体を1回舐めるだけで Opus 5.5 の入力 $2 相当が飛び、以降の毎ターンの再送にも乗る。**探索の規律が最大の効き目を持つ**。 ### 2.4 レバー別の期待効果(推定値・実測で更新すること) | レバー | 削減率 | 品質への影響 | @@ -68,19 +79,22 @@ N ターン目の入力は「それまでの全履歴」である。したがっ | 探索規律(§4.3) | 20〜40% | ↑ 無関係な情報に引きずられない | | 出力フィルタ(§4.4) | 5〜15% | → 変化なし | | L3(非LLM)優先(§3.2) | 5〜10% | ↑ 決定論的で確実 | -| L2 ルーティング(§3.3) | 10〜20% | 条件付き(§5.1 DoD と §5.2 エスカレーションで担保) | +| effort の層別指定(§3.4) | 10〜30% | → 設計判断は下げないので変化なし。L2 を下げすぎると手戻りで逆転(§5.2 で担保) | +| L2 の別モデル化(§3.4) | 0〜10% | 条件付き。Opus 5.5 と Sonnet 5 の単価差は2倍しかなく、キャッシュ分断と手戻りで消えやすい | --- ## 3. 役割モデル(オーケストレーション) ### 3.1 3層構造 -| 層 | 担当 | 使用モデル | 責務 | やらないこと | +| 層 | 担当 | 使用モデル / effort | 責務 | やらないこと | |:--|:--|:--|:--|:--| -| **L1 オーケストレーター** | メインセッション | Opus 5 | 分解・設計判断・タスクカード発行・受入判定・統合 | 大量のコードを自分で書かない/全文を読まない | -| **L2 ワーカー** | 別セッション or サブエージェント | Sonnet 5 | 仕様が確定した実装、定型追加、文書追記 | 設計判断・仕様変更の独断 | +| **L1 オーケストレーター** | メインセッション | Opus 5.5 / `high`(高リスク領域は `xhigh`) | 分解・設計判断・タスクカード発行・受入判定・統合 | 大量のコードを自分で書かない/全文を読まない | +| **L2 ワーカー** | 別セッション | Opus 5.5 / `medium`(定型は `low`) | 仕様が確定した実装、定型追加、文書追記 | 設計判断・仕様変更の独断 | | **L3 メカニカル** | ツール実行のみ | なし(**トークン0**) | ビルド、テスト、grep、置換、EF migration 生成、整形 | 判断 | +1.0 では L1 と L2 を**モデル**で分けていた。1.1 からは**同じ Opus 5.5 の effort** で分ける。Opus 5.5 は Opus 5 より2割安く、Sonnet 5 との差は2倍まで縮んだ。新しい上位モデルを低い effort で使う方が、下位モデルを高い effort で使うより品質あたりの消費が少ないことが多い。モデルが1つならキャッシュも分断されない。 + ### 3.2 大原則 > **L3 で済むことを LLM にさせない。L2 で済むことを L1 にさせない。** > ただし「済むかどうか」の判定は L1 が行う。判定を誤ると手戻りが発生し、節約分は消える。 @@ -100,6 +114,21 @@ N ターン目の入力は「それまでの全履歴」である。したがっ | 「どこで使われているか」調査 | L3 → 不足時のみ L2 | まず grep。§4.5 の条件を満たす場合のみサブエージェント | | ビルドエラー修正 | L2(2回失敗で L1 へ) | §5.2 | +### 3.4 effort とモデルの選び方 +| 状況 | 設定 | +|:--|:--| +| §3.3 で L1 に振った類型(データモデル・認証・在庫・実績/原価・CSV取込) | Opus 5.5 / `xhigh` | +| 上記以外の設計判断・タスクカード作成・受入判定 | Opus 5.5 / `high` | +| 仕様確定済みの実装(Controller + テスト、画面タブ追加) | Opus 5.5 / `medium` | +| 定型追加・文書追記・`/add-master` 等の手順どおりの作業 | Opus 5.5 / `low` | +| L2 で同じエラーに2回失敗した | effort を1段上げて L1 として扱う(§5.2)。同じ effort で3回目を試さない | + +**規則** +- effort は**セッションの最初に決め、途中で変えない**(途中の変更はキャッシュを捨てる)。段が変わるなら新しいセッションにする。 +- `max` は使わない。`xhigh` で2回失敗したときは effort ではなくタスクカードの前提を疑う(§5.2)。 +- **Sonnet 5 を L2 に使ってよいのは**、仕様・変更対象・受入条件がカードで確定し、既存の見本ファイルを真似るだけの作業に限る。L1 と同じセッションで切り替えない(キャッシュがモデルごとのため)。手戻りが1回でも出た類型は Opus 5.5 に戻す。 +- 効果は §7 の記録で確かめる。「安いモデル・低い effort にしたのに手戻りで総量が増えた」類型は1段戻す。 + --- ## 4. 実行プロトコル @@ -114,7 +143,7 @@ N ターン目の入力は「それまでの全履歴」である。したがっ - 変更禁止: <例: Migrations/ 配下、既存API契約、DbContext のスキーマ> - 受入条件(DoD): <機械的に検証できる形で箇条書き> - 参照: Spec.md §x.y / MES.md No.X-XX-XX-XX / docs/xxx.md -- 想定層: L1 / L2 / L3 +- 想定層: L1 / L2 / L3(effort: xhigh / high / medium / low。§3.4) - 想定変更ファイル数: N ``` @@ -130,6 +159,7 @@ N ターン目の入力は「それまでの全履歴」である。したがっ | 次のタスクが同じファイル群を触る | 継続(キャッシュ読取 0.1倍の恩恵を受ける) | | 30ターンを超えた | 受入条件を確認して打ち切り、残りを新カードに切り出す | | 作業を続けたいが文脈が長い | `/compact`(継続に文脈が本当に必要な場合のみ) | +| モデルか effort を変えたい | 新しいセッション(同じセッションで変えるとキャッシュが無駄になる) | `/clear` はキャッシュを捨てる操作でもある。**「無関係なら clear、関係あるなら継続」**が判断基準であり、機械的な頻繁 clear は逆効果。 @@ -178,6 +208,8 @@ dotnet test tests/MesApp.Api.Tests -v q --nologo 2>&1 | Select-String -Pattern " - すでに文脈がある作業の続き - 実装の分担(統合コストがトークン・品質の両面で上回る) +**起動するときのモデル**: 結論だけを返す走査(Explore)は Haiku 4.5 か Sonnet 5 を指定してよい(走査の生データを高い単価で読まない)。判断を含む調査は親と同じ Opus 5.5 のままにする。 + ### 4.6 計画と実装の分離 1. L1 が計画を立て、タスクカードに落とす 2. 人間が承認する(唯一かつ最も安い品質関門) @@ -280,7 +312,7 @@ dotnet test tests/MesApp.Api.Tests -v q --nologo 2>&1 | Select-String -Pattern " ## 8. アンチパターン(禁止事項) -1. **節約目的でモデルを一律ダウングレードする** — 手戻りが増え、総消費量はむしろ増える +1. **節約目的でモデルや effort を一律に下げる** — 手戻りが増え、総消費量はむしろ増える。下げるのは §3.4 の表に従い、層ごとに行う 2. **「まず全体を把握して」から始める** — 全文読込は 50万トークン級(§2.3) 3. **1セッションで無関係なタスクを連続実行する** — 履歴再送が二乗で効く(§2.2) 4. **同じ失敗を3回試す** — 2回で必ずエスカレーション(§5.2) diff --git a/Spec.md b/Spec.md index 7e576fb..5a9718e 100644 --- a/Spec.md +++ b/Spec.md @@ -115,6 +115,12 @@ 改訂114: 2026-09-23(3.8節・5.2節:同じ製造日・品目・工程で、作業区の範囲が重なる生産計画(作業区なしと作業区あり、上位の段と配下の段)を併存させないようにした。予実は作業区違いの計画を合算するため、全体の計画と内訳の計画が並ぶと二重に数えていた。兄弟の作業区どうしは併存できる。判定は `ProductionPlanPolicy` に置き、単票API(409)とCSV取込(既存との間・ファイル内とも行エラー)の両方から呼ぶ。既に重なって登録されている計画はそのまま残る) 改訂115: 2026-09-23(3.8節・7.2節:生産計画をマスタの一括ZIPの取込・出力から外した(改訂113で含めていた)。生産計画はマスタではなく日々増える業務データで、全期間がZIPに入り、マスタを別環境へ移すと過去の計画まで付いていったため。取込の仕組み(キーで上書き)は引き続きマスタCSVのものを使う。計画のCSV出力は全件をやめ、一覧と同じ絞り込みの `api/production-plans/csv` を追加した。あわせて、3か月分の実績サンプルと同じ期間の生産計画のサンプル `samples/production-plans/` を追加し(`scripts/New-SampleBulkActuals.ps1` が乱数を使わず生成。実績の内容は変えない)、リリースに `samples-production-plans.csv` として添付する。利用者向けガイド(生産管理・ロールと権限など)に生産計画・予実の操作を追記) 改訂116: 2026-09-23(3.8節:実績CSVに出荷指示 `shipping-orders`・出荷判定 `shipment-judgments`・出荷実行 `shipments` を追加した。サンプルの実績で出荷だけが画面操作で、製造から出荷までをCSVだけではたどれなかった。判定と出荷で取込の権限が違うため3種別に分ける。出荷判定の登録・承認を `ShipmentJudgmentsController` から `ShipmentJudgmentService` へ移し、単票APIとCSV取込の両方から呼ぶ。出荷指示は後続のCSVから指すため出荷番号の手入力を許す(`SH〜` は自動採番用として拒否)。サンプル `samples/actual-csv/13〜15` を追加) +改訂117: 2026-09-23(3.9節・5.1節・5.2節:MBOMの行に消費工程(`BomItem.RoutingSequence`。親品目の工順の工程順序)を追加した。バックフラッシュが作業指示の工程にかかわらず指図の予定材料をすべて引いていたため、複数工程の品目で工程ごとにバックフラッシュすると部材が工程数ぶん減っていた。バックフラッシュはその作業指示の工程で消費する部材だけを引き、消費する部材の無い工程では拒否する。未指定の行は最終工程で消費し、展開時に予定材料へ最終工程の工程順序を入れて固定する。工順は一括置換で行が作り直されるためIDではなく工程順序で指し、工順に無い工程順序は展開時に拒否する。MBOMのCSVに `RoutingSequence` 列を追加し、サンプル `samples/master-csv/15_bom.csv` は実績サンプルの部材投入に合わせて工程10とした) +改訂118: 2026-09-23(5.1節・5.2節:品目の標準不良率を指図の予定材料に反映するようにした。A-40-10-04(不良率などのMBOM付加情報)として入力できたが、どこにも使われていなかった。予定数量を 原単位×指図数量÷(1-親品目の標準不良率) とする。不良品も部材を消費する(バックフラッシュは良品+不良の数で引く)ため、良品を指図数量だけ得るのに要る部材を見込む。割り切れない値は列の精度(小数6桁)で切り上げる(切り捨てると欠品側にずれる)。÷(1-率) のため標準不良率は100%を受け付けず 0〜99.99 とした(単票API・CSV)。バックフラッシュの消費量は実績(良品+不良)基準のままで変えない) +改訂119: 2026-09-23(5.1節:MBOMの循環(A→B→…→A)を登録時に拒否するようにした。これまでは品目自身を子品目にする行だけを拒否しており、2段以上で一周する構成は単票APIでもCSVでも登録できた。今の展開は1段だけなので処理は止まらないが、作れない品目の構成になり、多段の所要量展開や逆展開を足したときに無限ループになる。判定は `ProductStructurePolicy.FindBomCycle` に置き、単票APIとマスタCSV取込の両方から呼ぶ。置き換える親品目自身の既存明細は判定に入れない。CSVでは取り込んだ親品目の明細をその都度判定用に置き換え、同じファイルの後の行が前の行と循環するのも捕まえる。エラーには一周する経路を品目コードで示す) +改訂120: 2026-09-23(5.1節:MBOMの代替部品グループの整合を登録時に確かめるようにした。グループの無い代替部品の行、主材料の無いグループ、主材料が2つ以上あるグループを登録できた。主材料が無いとバックフラッシュ(代替部品の行を消費しない。3.9節)がそのグループの部材を何も引かず、主材料が2つ以上だと代わりになるはずの部材を両方とも原単位ぶん引く。グループの無い代替部品はどの主材料の代わりかが決まらない。判定は `ProductStructurePolicy.CheckAlternativeGroups` に置き、単票APIとマスタCSV取込の両方から呼ぶ。グループ名は前後の空白を除いて比べ、その形で保存する(空白違いで別グループになるのを防ぐ)。既存のデータは直さない(次にそのMBOMを保存するときに判定される)) +改訂121: 2026-09-23(3.1節:MBOMの改訂(A-40-10-05 設計変更)の監査ログに変更前後の明細を残すようにした。これまでは `items=件数` だけで、一括置換のため何を変えたか追えなかった。明細は子品目をIDではなくコードで残す(品目を無効化・改名しても読めるように)。マスタCSV取込では種別ごとの取込ログに加えて親品目ごとに同じ形のログを書く(CSVが追跡の抜け道にならないように)。監査ログは書くたびに保存するため、取込の途中では書かず、全行の検証が通って保存した後にまとめて書く) +改訂122: 2026-09-23(5.7節「指図展開時のマスタ固定」に、予定材料の標準不良率による割り増しと消費工程の固定(改訂117・118)を追記した。あわせてマスタCSV・実績CSVの列の案内(`AlternativeGroup`・`IsAlternative`・`Backflush`)、サンプルの説明、利用者ガイド(帳票・用語・マスタのCSV・部材投入)を改訂117〜121に合わせた) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -225,6 +231,7 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 - **生産計画変更**[将来拡張]:スケジューラーを介さないMES上での計画変更(A-10-30-02) - **MBOM管理**:MBOM登録(A-40-10-01)、中間品(追加品目)登録(A-40-10-02)、内外製区分登録(A-40-10-03)、不良率・代替部品等の付加情報(A-40-10-04)、設計変更対応(A-40-10-05) - **BOP/BOE管理**:工順(BOP)・設備情報(BOE)の登録・付加情報管理(A-40-20-01〜02、I-10-20-02、I-30-20-02、I-30-20-13)、工程変更対応(A-40-20-03、I-50-30-01/03)、設計変更反映時の在庫・生産状況確認(J-40-40-01/03) + - MBOMの改訂は監査ログに**変更前後の明細**(子品目コード・原単位・内外製・代替部品グループ・代替部品フラグ・消費工程)を残す。単票APIでもマスタCSV取込でも同じ形で、CSVでは親品目ごとに1件書く - 設計変更(MBOM・工順の改訂)の影響は品目マスタ画面で確認する。進行中の指図と、部材の在庫・進行中指図の予定数量を並べる - **展開済みの指図には改訂が届かない**(5.7節のスナップショット)。改訂後の条件で作り直すなら指図の取消と再作成が要る。未展開(未承認・承認済)の指図は展開時に改訂後の値を使う - 部材は現行MBOMと進行中指図のスナップショットの**和集合**で並べる。改訂で外したのに進行中指図がまだ必要としている部材と、その在庫が見えるようにするため @@ -351,6 +358,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - 代替材料は**MBOMの代替部品グループへ登録することで表現する**。同一グループ内で主材料でない行には代替部品フラグを立て、**その行の部材を投入するときは代替理由の入力を必須**とする(誰がどの理由で代替を認めたかを実績に残すため。A-40-10-04)。主材料の投入は理由不要 - 代替理由は投入実績(`MaterialConsumption`)へ代替フラグとともに保存し、監査ログにも記録する。代替そのものへの承認フロー(別の承認者による事前承認)は初期リリースでは行わない[将来拡張] - バックフラッシュ(B-40-10-09)はMBOMから投入部材を導くため、この照合は元から満たしている。ただし**代替部品の行はバックフラッシュで消費しない**(主材料の代わりに使う部材なので、主材料と両方を原単位ぶん引くと二重に減る。また代替の投入には理由が要るが、バックフラッシュは理由を受け取らない)。代替部品を使った場合は手動の部材投入で理由とともに記録する + - **バックフラッシュは、その作業指示の工程で消費する部材だけを引く**。MBOMの行に消費工程(親品目の工順の工程順序)を持たせ、未指定の行は最終工程で消費する。消費工程は展開時に予定材料へ固定し、未指定の行はその時点の最終工程に解決して入れる。工順に無い工程順序を指すMBOMの品目は指図を展開できない(MBOMと工順は別々に改訂でき、どちらを先に登録してもよいため、登録時ではなく展開時に照合する)。消費する部材の無い工程でバックフラッシュを指定したときは拒否する(黙って何も引かないと、引いたつもりの在庫がずれる) - **採番**:指図番号・ロット番号等(`MOyyyyMMdd-0001`、`品目コード-yyyyMMdd-001`)は、**採番テーブル(`NumberSequence`)のプレフィックス1行を更新してから読む**ことで払い出す。既存番号の最大値+1では、同時に採番したリクエストが同じ番号を得て一意制約違反になるため - 採番列が無い場合(採番テーブル導入前のデータ)は、既存番号の最大値の次から始める - **採番列ができたあとに、同じ形式の番号を手入力で登録すると衝突しうる**(採番側はその番号を知らない)。衝突は409で返るため、操作をやり直せば次の番号で成立する。供給者のロット番号など、自動採番と別形式の手入力は影響を受けない @@ -396,8 +404,8 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと | Shift(勤務シフト/直) | シフトコード、名称、開始時刻、終了時刻(開始時刻以下なら翌日にまたぐ夜勤)、状態(F-10-10-01)。3.9節の製造日が夜勤を前提にしているのに対し「その実績がどの直か」を表す | | SkillMaster(スキル・資格マスタ) | コード、名称、種別(スキル/資格)、有効期限管理要否(F-20-10-01) | | UserSkill(従業員スキル・資格) | ユーザーID、スキル/資格ID、取得日、有効期限、状態(F-20-10-02〜05) | -| Product(品目マスタ) | 品目コード、品目名、単位、規格、品目区分(製品/半製品・中間品/部材)、標準不良率、**既定ロケーション**(推奨ロケーション指示の第一候補。D-10-30-03) | -| BomItem(MBOM明細) | 親品目ID、子品目ID(部材)、必要数量(親1単位あたり)、内外製区分、代替部品グループ、**代替部品フラグ**(同一グループ内の主材料でない行。A-40-10) | +| Product(品目マスタ) | 品目コード、品目名、単位、規格、品目区分(製品/半製品・中間品/部材)、標準不良率(0〜99.99%。指図の予定材料を割り増す。A-40-10-04)、**既定ロケーション**(推奨ロケーション指示の第一候補。D-10-30-03) | +| BomItem(MBOM明細) | 親品目ID、子品目ID(部材)、必要数量(親1単位あたり)、内外製区分、代替部品グループ、**代替部品フラグ**(同一グループ内の主材料でない行。A-40-10)、**消費工程**(工順の工程順序。未指定なら最終工程。B-40-10-09)。品目自身を子品目にする行と、明細をたどって親品目へ戻る循環は登録できない(単票API・CSVで同じ判定。`ProductStructurePolicy.FindBomCycle`)。代替部品グループは主材料(代替部品フラグの無い行)をちょうど1つ持ち、代替部品フラグの行はグループを必須とする(`CheckAlternativeGroups`。グループ名は前後の空白を除いて保存・比較) | | Process(工程マスタ) | 工程コード、工程名、工程区分(内製/外注) | | Routing(工順/BOP) | 品目ID、工程順序、工程ID、標準作業時間(**1個あたり**)、標準段取り時間(**1回あたり**)、必要スキルID、**候補設備**・治工具、**作業区**、工程管理項目、チェックリストID、**作業手順書ID**(A-40-20、I-30-20)
標準時間の予実(3.2節 B-60-10-05)は 段取り時間+作業時間×数量 で予定を出す | | RoutingEquipment(工順の候補設備) | 工順ID、設備ID(同一工順で一意)。同じ工程を複数の装置で実行できる場合に列挙し、差立ではこの中から1台を選ぶ(B-10-20-02) | @@ -421,7 +429,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと |---|---| | ProductionPlan(生産計画) | 製造日、品目ID、工程ID、作業区ID(任意)、計画数量(0以上)、備考。一意キーは製造日・品目・工程・作業区(作業区なしの重複はAPIで止める)。同じ製造日・品目・工程で作業区の範囲が重なる計画(作業区なしと作業区あり、上位の段と配下の段)は併存させない(予実で二重に数えるため。兄弟の作業区は可)。既存の指図・実績からは参照しない(A-30-10-01) | | ManufacturingOrder(製造指図) | 指図番号、品目ID、数量、納期、指図区分(通常/リワーク/突発)、状態(未承認/承認済/展開済/完了/取消)、承認者、元指図ID(リワーク時)(A-20、B-70-10) | -| ManufacturingOrderMaterial(指図の予定材料) | 指図ID、部材品目ID、原単位(親1単位あたり)、予定数量(=原単位×指図数量)、代替部品グループ。展開時にMBOMから固定し、部材投入の照合とバックフラッシュの基準にする(A-40-10、B-30-20) | +| ManufacturingOrderMaterial(指図の予定材料) | 指図ID、部材品目ID、原単位(親1単位あたり)、予定数量(=原単位×指図数量÷(1-親品目の標準不良率)。小数6桁で切り上げ)、代替部品グループ、消費工程(未指定のMBOM行は展開時の最終工程に解決。本項目の追加前に展開した指図は空で、最終工程として扱う)。展開時にMBOMから固定し、部材投入の照合とバックフラッシュの基準にする(A-40-10、B-30-20) | | WorkOrderControlItem(作業指示の工程管理項目) | 作業指示ID、工程管理項目ID、**展開時点のスナップショット**(項目コード・名称・単位・版数・指示値・許容下限/上限)。実績の逸脱判定と画面表示はこれを使い、マスタの現在値を参照しない(B-30-30-04) | | WorkOrderStatusHistory(作業指示の状態履歴) | 作業指示ID、変更前状態、変更後状態、契機(差立/着手/実績入力/承認/指図取消)、備考、変更者、変更日時。配布・着手・完了・承認・取消の遷移を業務履歴として残す(B-10、B-30-30、B-40-10-10) | | WorkOrder(作業指示) | 指示番号、製造指図ID、品目ID、工程ID、計画数、着手順、割当作業者ID、割当設備ID、**作業区(展開時の工順スナップショット)**、**作業手順書IDと展開時点の版数**、状態(未配布/配布済/着手/完了/承認済)(B-10) | @@ -532,6 +540,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと - **指図展開時のマスタ固定**:工順(BOP)とMBOMは改訂され上書きされるため、**製造指図の工程展開時点の値を実績側へ固定**する。以降にマスタが改訂されても、既に展開された指図の製造条件と照合条件は変わらない - 工順 → `WorkOrder`(標準作業時間・標準段取り時間・必要スキル・工程管理項目・チェックリスト)。差立のスキル照合(F-20-30-01)はこのスナップショットを使う - MBOM → `ManufacturingOrderMaterial`(予定材料)。部材投入の照合(B-30-20-01)とバックフラッシュ(B-40-10-09)はこの予定材料を基準にする + - 予定数量は親品目の標準不良率で割り増し(原単位×指図数量÷(1-率))、消費工程は未指定なら展開時の最終工程に解決して固定する。バックフラッシュはその作業指示の工程を消費工程とする行だけを引き、作業指示書にもその行だけを載せる - 改訂を仕掛中の指図へ反映したい場合は、指図を展開し直す(マスタ側に有効期間や版を持たせる方式は採らない。実績側スナップショットのほうが「当時どの条件で作れと指示されたか」を直接説明できるため) - **MBOMの部品切替を有効日/失効日で持つ方式は採らない**(切替前後の両方の行を保持し、失効日・有効日で切り替える一般的な方式)。理由は上と同じで、マスタ側に期間を持たせても「その指図が展開された時点でどちらの部品が有効だったか」は**展開時刻と有効日の突き合わせを毎回やり直さないと分からない**のに対し、予定材料(`ManufacturingOrderMaterial`)へ固定すれば当時の指示がそのまま残るため。部材投入の照合(3.9節)も日付の解釈を持たずに済む - ただしこの方式では、**切替日を事前に登録しておく運用(設計変更の予告。A-40-10-05)ができない**。当面は切替日にMBOMを差し替え、仕掛中の指図は必要に応じて展開し直す運用とする。予告運用が必要になった場合は、マスタに有効期間を持たせるのではなく**「切替予定」を別に持って切替日にMBOMへ適用する**形で[将来拡張]する(実績側スナップショットの方針を崩さないため) diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index 7f92e40..aeb6db1 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -119,7 +119,7 @@ | ロット使用可否(投入・引当・出荷の共通判定) | `Api/Policies/LotUsabilityPolicy.cs` | Spec.md 3.9。ステータス・有効期限の条件は**ここだけ**に置く。呼び先は `WorkOrderExecutionService.AddConsumptionAsync`(投入)/`InventoryService.AllocateFefoAsync`(FEFO)/`ShippingService.ShipAsync`(出荷) | | 製造条件の逸脱判定 | `Api/Policies/ControlItemDeviationPolicy.cs` | Spec.md 5.7。基準は**マスタ現在値ではなく作業指示のスナップショット**(`WorkOrderControlItem`)。数値なし・上下限なしは判定せず`null`のまま(`false`にしない)。呼び先は `WorkOrderExecutionService.AddDataRecordsAsync` | | 直(シフト)の時間帯判定 | `Api/Policies/ShiftSchedulePolicy.cs` | Spec.md 5.7。日跨ぎ(`EndTime <= StartTime`)・重なり判定・時刻→直の解決・**製造日の境界またぎの警告**(`CheckBusinessDateBoundary`。拒否ではなく `ShiftResponse.BoundaryWarning` で返す)。単票APIとCSV取込の両方から通す。重なり判定は**0時で切って区間に開いてから突き合わせる**(開始・終了の大小比較だと 22:00〜06:00 と 05:00〜09:00 の重なりを見落とす)。判定の単体テストは `Tests/ShiftSchedulePolicyTests.cs`(1440分の総当たりを期待値にした網羅テストで等価性を固定) | -| 工順・品目から参照できるマスタ | `Api/Policies/ProductStructurePolicy.cs` | Spec.md 5.7。工順の作業区(最下段かつ有効)・作業手順書(有効)・品目の既定ロケーション(有効)・候補設備(代表設備を含める)。呼び先は `ProductStructureService` / `ProductsController` と `MasterCsvService.Import` の `ImportProductsAsync`・`ImportRoutingAsync`。`Tests/MasterTests.cs` / `MasterCsvTests.cs` | +| 工順・品目から参照できるマスタ | `Api/Policies/ProductStructurePolicy.cs` | Spec.md 5.7。工順の作業区(最下段かつ有効)・作業手順書(有効)・品目の既定ロケーション(有効)・候補設備(代表設備を含める)・**MBOMの循環(`FindBomCycle`)と代替部品グループの整合(`CheckAlternativeGroups`)**。呼び先は `ProductStructureService` / `ProductsController` と `MasterCsvService.Import` の `ImportProductsAsync`・`ImportRoutingAsync`・`ImportBomAsync`。`Tests/MasterTests.cs` / `MasterCsvTests.cs` | | 部材投入の照合(予定材料) | `Api/Policies/MaterialIssuePolicy.cs` | Spec.md 3.9・5.7。基準はMBOMの現在値ではなく**指図の予定材料**。呼び先は `WorkOrderExecutionService.AddConsumptionAsync` | | 作業指示ステータス変更(+状態履歴) | `Api/Services/WorkOrderStatusService.cs`
`Core/Entities/Production.cs`: WorkOrderStatusHistory | Spec.md 5.2。`WorkOrder.Status` を**直接代入しない**。履歴は `GET api/work-orders/{id}/status-history` | | システム管理者を失わない | `Api/Policies/LastAdminPolicy.cs` | Spec.md 3.6。有効なシステム管理者が0人になる無効化・降格を拒否する。呼び先は `UsersController.Update`(1件ずつ判定)/`MasterCsvService.Import.ImportUsersAsync`(**全行の適用後**に判定。行順で引き継ぎを弾かないため)。`Tests/MasterTests.cs` / `MasterCsvTests.cs` | diff --git a/docs/assets/intro/print-wo.webp b/docs/assets/intro/print-wo.webp index ddfb304..eb02a89 100644 Binary files a/docs/assets/intro/print-wo.webp and b/docs/assets/intro/print-wo.webp differ diff --git a/docs/assets/walkthrough/order-detail.webp b/docs/assets/walkthrough/order-detail.webp index 4649f64..0ac5092 100644 Binary files a/docs/assets/walkthrough/order-detail.webp and b/docs/assets/walkthrough/order-detail.webp differ diff --git a/docs/execution.md b/docs/execution.md index 95ba93c..e9a9d1a 100644 --- a/docs/execution.md +++ b/docs/execution.md @@ -64,7 +64,7 @@ next_title: 物流・在庫管理 - **段取り**:段取り実績とチェックリストの記録 - **実績入力**:着手・出来高報告・完了承認 -- **指示書**:作業指示書の印刷(別タブ)。構成部品(MBOM)と工程情報が印刷されます +- **指示書**:作業指示書の印刷(別タブ)。この工程で使う部材(指図を展開したときのMBOMのうち、この工程を消費工程とするもの)と工程情報が印刷されます --- @@ -131,6 +131,8 @@ next_title: 物流・在庫管理 チェックを入れると、**MBOM × (良品数 + 不良数)** で必要量を計算し、 在庫から **先入れ先出し** で自動的に引き落とします。 +引き落とすのは、MBOMで**この工程を消費工程にした部材だけ**です(消費工程を指定していない部材は最終工程で引きます)。 +各工程でチェックを入れても、部材が二重に減ることはありません。

対象品目のMBOMが未登録だとエラーになります(MBOMが未登録のためバックフラッシュできません。)。 @@ -150,7 +152,7 @@ next_title: 物流・在庫管理

  1. 投入ロットを選びます。ロット番号はスキャンして検索できます。棚は選んだ在庫のものが使われます。
  2. 数量を入力します。
  3. -
  4. 指図の予定材料にない品目(代替部品)を投入するときは代替理由が必須です。
  5. +
  6. 予定材料のうち代替部品の行(MBOMで代替部品にした部材)を投入するときは代替理由が必須です。
  7. 投入を記録を押します。在庫が引き落とされ、投入実績に残ります。
diff --git a/docs/intro.html b/docs/intro.html index 7468e20..650f8cf 100644 --- a/docs/intro.html +++ b/docs/intro.html @@ -321,7 +321,7 @@

紙の指示書と現品票も、
そのまま印刷して現場へ

作業指示書印刷 / PDF
- 作業指示書の印刷プレビュー。指示番号・品目・工程、構成部品(MBOM)の表、作業実施記録欄、指示番号のバーコード + 作業指示書の印刷プレビュー。指示番号・品目・工程、この工程で使う部材(予定材料)の表、作業実施記録欄、指示番号のバーコード
現品票
diff --git a/docs/masters.md b/docs/masters.md index 7ede9e7..74aad8f 100644 --- a/docs/masters.md +++ b/docs/masters.md @@ -61,7 +61,7 @@ next_title: 生産管理 | 単位 | 個、kg など | | 品目区分 | 製品/半製品・中間品/部材 | | 規格 | 任意の規格情報 | -| 標準不良率(%) | 参考値として保持 | +| 標準不良率(%) | 製造指図を展開したときの予定材料をこの率ぶん割り増します(0〜99.99) | 登録済みの品目は一覧に表示され、**編集**で内容を変更、**無効化**で以後の選択肢から外せます (過去の実績は残ります)。 @@ -76,6 +76,7 @@ next_title: 生産管理
  • 子品目(使用する部材)を選びます。
  • 必要数量を入力します。これは親1単位あたりの数量です。
  • 内外製区分と、必要なら代替部品グループを指定します。
  • +
  • 消費工程で、その部材を使う工程(工順の工程順序)を選びます。選ばなければ最終工程で使う扱いになります。
  • 明細に追加を押して行を積み上げます。
  • すべて追加したら MBOMを保存(一括置換) を押します。
  • @@ -83,11 +84,19 @@ next_title: 生産管理

    保存は一括置換です。画面に表示されている明細で既存の構成が丸ごと置き換わります。 1行だけ足したい場合も、既存行を残したまま追加して保存してください。

    -

    品目自身を子品目にすることはできません。

    +

    品目自身を子品目にすることはできません。構成が一周する登録(例:製品の子に半製品、半製品の子に製品)もできません。エラーには一周する経路が表示されます。

    +

    代替部品グループには、主材料(代替部品にチェックしない行)をちょうど1つ入れ、残りを代替部品にします。代替部品にチェックした行にはグループ名が必要です。

    MBOMは、実績入力の**バックフラッシュ**(部材の自動消費)で使われます。 バックフラッシュを使う予定があれば必ず登録してください。 +製造指図を展開すると、MBOMから予定材料が作られます。予定数量は **必要数量 × 指図数量 ÷ (1 − 標準不良率)** です。 +不良になる分も部材を使うため、親品目の標準不良率ぶん多めに見込みます(例:必要数量2・指図数量10・標準不良率20%なら予定数量25)。 +バックフラッシュは各部材を**消費工程の作業指示でだけ**引くので、組立の工程で使う部材は消費工程に組立の工程を選んでください。 + +
    +

    消費工程に工順に無い工程順序を選んだままだと、製造指図を展開できません。工順を組み直したときはMBOMの消費工程も見直してください(一覧に「(工順に無し)」と表示されます)。

    +
    --- @@ -293,6 +302,7 @@ MBOMは、実績入力の**バックフラッシュ**(部材の自動消費) 明細を持つマスタは、**同じキーの行をまとめて1件**として扱い、**その明細を丸ごと置き換えます**。 - **MBOM**:`ParentProductCode`(親品目コード)が同じ行が、その親のMBOMになります。 + `RoutingSequence` に部材を使う工程の工程順序を書きます(空なら最終工程)。代替部品グループのルールと循環の禁止は、画面から登録したときと同じです。 - **工順**:`ProductCode`(品目コード)が同じ行が、その品目の工順になります。 - **チェックリスト**:`Code` が同じ行が、1つのチェックリストの項目になります。 diff --git a/docs/reference.md b/docs/reference.md index 11d3364..16766e5 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -116,7 +116,7 @@ next_title: 困ったときは | 承認しないと展開できない | 製造指図は「承認済」でのみ工程展開できます | | 工程の順序は強制しない | 前工程が終わっていなくても後工程に着手できます。着手順は差立で管理します | | 最終工程で在庫計上 | 工順の最後の工程の実績登録時に、良品数が産出ロットとして入庫されます | -| バックフラッシュはMBOM基準 | MBOM × (良品数 + 不良数) を先入れ先出しで自動消費します | +| バックフラッシュは予定材料基準 | 指図の予定材料のうち、その工程で使う部材を 原単位 × (良品数 + 不良数) だけ先入れ先出しで自動消費します(代替部品は引きません) | | ピッキングは先入れ先出し | 有効期限を優先して自動引当されます | | 出荷には出荷判定が必要 | 承認済みの「可」または「特採」判定がないと出荷実行できません | | 承認は単段階 | 各種承認は1回の承認操作で確定します(多段階ワークフローはありません) | diff --git a/docs/reports.md b/docs/reports.md index dfa5434..6d42e4e 100644 --- a/docs/reports.md +++ b/docs/reports.md @@ -16,7 +16,7 @@ next_title: ロールと権限 | 帳票 | どこから出すか | 内容 | |---|---|---| -| 作業指示書 | 作業指示 一覧/製造指図 詳細 の **指示書** | 指示番号・指図番号・品目・工程・計画数・担当者・設備・構成部品(MBOM) | +| 作業指示書 | 作業指示 一覧/製造指図 詳細 の **指示書** | 指示番号・指図番号・品目・工程・計画数・担当者・設備・この工程で使う部材(指図の予定材料) | | 現品票(ラベル) | 受入/在庫照会/トレーサビリティ の **現品票** | 品目・数量・製造日/受入日・有効期限・ステータス・グレードと、**ロット番号のバーコード** | | 検査成績書 | 検査詳細の **検査成績書** | 検査項目ごとの規格値・測定値・判定 | | 出荷伝票 | 出荷管理の **出荷伝票** | 出荷先・予定日・明細 | @@ -75,4 +75,4 @@ next_title: ロールと権限 - **受入したらすぐ現品票を印刷して貼る。** 以降のピッキング照合・検査・トレースがすべてスキャンで進みます。 - **ピッキングは必ず照合してから実行する。** 一致/不一致の表示は目視確認用で、不一致でも実行ボタンは押せてしまいます。 -- **作業指示書を工程に配る。** 構成部品(MBOM)が載っているので、部材の取り違え防止になります。 +- **作業指示書を工程に配る。** その工程で使う部材(代替部品を含む)が載っているので、部材の取り違え防止になります。 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 65fde13..e27369b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -46,6 +46,8 @@ next_title: 管理者向け運用 |---|---| | 最終工程の実績には入庫先ロケーション(outputLocationId)が必要です。 | **入庫先ロケーション**を選んでから登録してください | | MBOMが未登録のためバックフラッシュできません。 | [マスタ管理](masters.html#mbom)でMBOMを登録するか、バックフラッシュのチェックを外してください | +| この工程(工程順序 ○○)で消費する予定材料が無いためバックフラッシュできません。 | この工程で使う部材はありません。バックフラッシュのチェックを外してください。部材を使う工程なら[MBOM](masters.html#mbom)の消費工程を直し、指図を作り直してください | +| MBOMの部材 '○○' の消費工程(工程順序 ○○)が品目 '○○' の工順にありません。 | [MBOM](masters.html#mbom)の消費工程か工順を直してから展開してください | | 良品数と不良数の合計は0より大きい必要があります。 | どちらかに数量を入力してください | | 状態 '○○' の作業指示には実績を記録できません。 | すでに承認済み、または取消済みです | | 状態 '○○' の作業指示は承認できません(完了済みのみ)。 | 実績を登録して「完了」にしてから承認してください | diff --git a/docs/walkthrough.md b/docs/walkthrough.md index 239a7d6..8e96acf 100644 --- a/docs/walkthrough.md +++ b/docs/walkthrough.md @@ -240,9 +240,9 @@ MBOMと工順の登録方法は、[マスタ管理の品目・MBOM・工順](mas SMP-FG-001-20PR-40 最終検査 10台 SMP-FG-001-30PR-50 梱包 10台 産出ロット FG1000-S001最終工程の良品が、このロットで入庫する -予定材料(MBOM×10台で固定) -SF-2000 10・ギアセット 10・シャフト 10 -Oリング 20(代替可)・ボルト 40・潤滑油 0.5 L +予定材料(MBOM×10台÷(1−1.5%)で固定) +SF-2000 10.15・ギアセット 10.15・シャフト 10.15 +Oリング 20.30(代替可)・ボルト 40.61・潤滑油 0.51 L 予定にない品目は投入できない 製造指図の状態 @@ -270,8 +270,8 @@ MBOMと工順の登録方法は、[マスタ管理の品目・MBOM・工順](mas
    -製造指図 SMP-FG-001 の詳細画面。予定材料7品目と、工程展開でできた作業指示3件が表示されている -
    製造指図の詳細。予定材料と、工程展開でできた作業指示が並ぶ。各行から段取り・実績入力・指示書の印刷へ進める
    +製造指図 SMP-FG-001 の詳細画面。予定材料7品目(予定数量は標準不良率ぶん割り増し、消費工程はすべて10)と、工程展開でできた作業指示3件が表示されている +
    製造指図の詳細。予定材料と、工程展開でできた作業指示が並ぶ。予定数量はギアポンプの標準不良率 1.5% ぶん割り増され、部材はどれも組立(工程10)で使う。各行から段取り・実績入力・指示書の印刷へ進める
    **差立**では、作業指示ごとに作業者・設備・着手順を割り当てます。作業者には工順の必要スキルが照合され、 diff --git a/samples/actual-csv/README.md b/samples/actual-csv/README.md index c649ed0..62e3bde 100644 --- a/samples/actual-csv/README.md +++ b/samples/actual-csv/README.md @@ -57,5 +57,7 @@ GET /api/actuals/csv/receiving/template … ヘッダーだけのCSV - 出荷実行には、その出荷指示を対象にした**承認済みの出荷判定(可・特採)**が要ります。判定の無い指示・保留の指示を 出荷実行CSVに書くと、そのファイルは取り込めません。 - `DV-03`(校正期限切れ)を `DeviceCode` に書くと、その検査は取り込めません。 -- 部材はバックフラッシュ(`Backflush=true`)でも消費できます。ただし代替部品は消費されない - (主材料だけを引く)ので、代替部品を使ったときは投入CSVで理由とともに投入してください。 +- 部材はバックフラッシュ(`Backflush=true`)でも消費できます。引くのは**その工程で使う部材だけ**で、 + サンプルのMBOMでは部材をすべて最初の工程(`Sequence=10`)で使います。部材を使わない工程(20・30)の行を + `Backflush=true` にすると、そのファイルは取り込めません。また代替部品は消費されない(主材料だけを引く)ので、 + 代替部品を使ったときは投入CSVで理由とともに投入してください。 diff --git a/samples/master-csv/15_bom.csv b/samples/master-csv/15_bom.csv index 9ff4baf..158a42a 100644 --- a/samples/master-csv/15_bom.csv +++ b/samples/master-csv/15_bom.csv @@ -1,9 +1,9 @@ -ParentProductCode,ChildProductCode,QuantityPer,MakeOrBuy,AlternativeGroup,IsAlternative -FG-1000,SF-2000,1,InHouse,,false -FG-1000,RM-3002,1,InHouse,,false -FG-1000,RM-3003,1,InHouse,,false -FG-1000,RM-3004,2,InHouse,ORING-P20,false -FG-1000,RM-3007,2,InHouse,ORING-P20,true -FG-1000,RM-3005,4,InHouse,,false -FG-1000,RM-3006,0.05,InHouse,,false -SF-2000,RM-3001,1,Outsourced,,false +ParentProductCode,ChildProductCode,QuantityPer,MakeOrBuy,AlternativeGroup,IsAlternative,RoutingSequence +FG-1000,SF-2000,1,InHouse,,false,10 +FG-1000,RM-3002,1,InHouse,,false,10 +FG-1000,RM-3003,1,InHouse,,false,10 +FG-1000,RM-3004,2,InHouse,ORING-P20,false,10 +FG-1000,RM-3007,2,InHouse,ORING-P20,true,10 +FG-1000,RM-3005,4,InHouse,,false,10 +FG-1000,RM-3006,0.05,InHouse,,false,10 +SF-2000,RM-3001,1,Outsourced,,false,10 diff --git a/samples/master-csv/README.md b/samples/master-csv/README.md index a2d9d30..d84b836 100644 --- a/samples/master-csv/README.md +++ b/samples/master-csv/README.md @@ -38,7 +38,7 @@ RM-3001 ハウジング鋳造品 | 12 | `12_inspection-items.csv` | 検査項目・基準 | 規格値あり/なし、受入・工程内・完成品・サンプル検査 | | 13 | `13_control-items.csv` | 工程管理項目 | 指示値と許容範囲(製造条件の逸脱判定に使う) | | 14 | `14_inspection-devices.csv` | 検査機・測定器 | 校正期限切れの機器を1台含む(システム管理者のみ取込可) | -| 15 | `15_bom.csv` | 品目・MBOM・工順 | FG-1000 と SF-2000 のMBOM。Oリングに代替部品あり | +| 15 | `15_bom.csv` | 品目・MBOM・工順 | FG-1000 と SF-2000 のMBOM。Oリングに代替部品あり。部材はどれも最初の工程(工程10)で消費する | | 16 | `16_work-procedures.csv` | 作業手順書 | 手順を本文に書く例と、所在だけを書く例(システム管理者のみ取込可) | | 17 | `17_routing.csv` | 品目・MBOM・工順 | 候補設備・作業区・治工具・スキル・チェックリスト・手順書を紐づけた工順 | | 18 | `18_users.csv` | ユーザー | 各ロールの担当者。作業場所・所属・直つき(システム管理者のみ取込可) | @@ -66,6 +66,8 @@ RM-3001 ハウジング鋳造品 ## 試せる業務のポイント - `RM-3007`(耐油Oリング)は `RM-3004` の代替部品です。投入するときは理由の入力が求められます +- `FG-1000` の標準不良率は 1.5%、`SF-2000` は 2% です。製造指図を展開すると、予定材料がこの率ぶん割り増されます + (例:ギアポンプ10台の指図で、ギアセットの予定数量は 10 ÷ (1 − 0.015) = 10.152285) - `14_inspection-devices.csv` の `DV-03` は校正期限切れです。検査実績の測定器に選ぶと登録できません - `SF-2000` の機械加工は `EQ-01` と `EQ-05` のどちらでも作業できます(候補設備) - `op003` は夜勤(20:00〜翌05:00)の作業者です。直別の集計を試せます diff --git a/src/MesApp.Api/Controllers/ManufacturingOrdersController.cs b/src/MesApp.Api/Controllers/ManufacturingOrdersController.cs index 59aed62..60e3f63 100644 --- a/src/MesApp.Api/Controllers/ManufacturingOrdersController.cs +++ b/src/MesApp.Api/Controllers/ManufacturingOrdersController.cs @@ -86,7 +86,7 @@ public async Task> Get(int id, Ca order.Materials.OrderBy(m => m.ChildProduct!.Code) .Select(m => new OrderMaterialResponse( m.ChildProductId, m.ChildProduct!.Code, m.ChildProduct!.Name, - m.QuantityPer, m.PlannedQuantity, m.AlternativeGroup)).ToList()); + m.QuantityPer, m.PlannedQuantity, m.AlternativeGroup, m.RoutingSequence)).ToList()); } /// 指図登録(A-20-10-01 手動登録、B-10-10-04 突発、B-70-10-01 リワーク) diff --git a/src/MesApp.Api/Controllers/ProductsController.cs b/src/MesApp.Api/Controllers/ProductsController.cs index 64b4392..fb708f5 100644 --- a/src/MesApp.Api/Controllers/ProductsController.cs +++ b/src/MesApp.Api/Controllers/ProductsController.cs @@ -147,7 +147,7 @@ public async Task>> GetBom(int id, Cancellati .OrderBy(b => b.ChildProduct!.Code) .Select(b => new BomItemResponse( b.Id, b.ChildProductId, b.ChildProduct!.Code, b.ChildProduct!.Name, - b.QuantityPer, b.MakeOrBuy, b.AlternativeGroup, b.IsAlternative)) + b.QuantityPer, b.MakeOrBuy, b.AlternativeGroup, b.IsAlternative, b.RoutingSequence)) .ToListAsync(ct); } diff --git a/src/MesApp.Api/Localization/ApiText.en.resx b/src/MesApp.Api/Localization/ApiText.en.resx index 7cca82f..73ff611 100644 --- a/src/MesApp.Api/Localization/ApiText.en.resx +++ b/src/MesApp.Api/Localization/ApiText.en.resx @@ -1380,8 +1380,8 @@ Substitute part - - true / false. Consuming a true row requires recording a reason + + true / false. A true row requires AlternativeGroup, and consuming it requires recording a reason Routing (BOP) @@ -1704,8 +1704,8 @@ Backflush - - true consumes planned materials × (good + defect) automatically by FIFO + + true consumes the planned materials used at this step (quantity per × (good + defect)) first-in first-out (substitutes are not consumed) Defects by reason @@ -1983,4 +1983,37 @@ A number greater than 0. Together with the quantity already shipped, it cannot exceed the instructed quantity. + + The consuming step (sequence {1}) of MBOM material '{0}' is not in the routing of item '{2}'. Correct the MBOM or the routing. + + + Cannot backflush because no planned materials are consumed at this step (sequence {0}). + + + Consuming step (sequence) + + + Sequence in the parent item's routing. Blank means the final step. Backflush consumes the material at this step + + + 0 to 99.99. Planned materials of an order are increased by this rate + + + The MBOM is circular ({0}). + + + A substitute row needs a substitute group (child item '{0}'). + + + Substitute group '{0}' has no primary material (a row that is not a substitute). + + + Substitute group '{0}' has more than one primary material ({1}). Keep one primary and mark the others as substitutes. + + + Parent item '{0}': {1} + + + Exactly one primary material (IsAlternative=false) per group + diff --git a/src/MesApp.Api/Policies/ProductStructurePolicy.cs b/src/MesApp.Api/Policies/ProductStructurePolicy.cs index 199d484..9e5e843 100644 --- a/src/MesApp.Api/Policies/ProductStructurePolicy.cs +++ b/src/MesApp.Api/Policies/ProductStructurePolicy.cs @@ -1,3 +1,4 @@ +using MesApp.Api.Localization; using MesApp.Core.Entities; namespace MesApp.Api.Policies; @@ -35,4 +36,95 @@ public static IEnumerable CandidateEquipmentIds(IEnumerable? candidate (candidates ?? []) .Concat(representative is { } id ? [id] : []) .Distinct(); + + /// + /// MBOMの代替部品グループの整合(A-40-10-04)。問題があれば日本語の理由を返す。 + /// + /// 代替部品の行は代替部品グループを持つ(どの主材料の代わりかが決まらないと、投入の照合で意味を持たない) + /// グループの主材料(代替部品でない行)はちょうど1つ。無いとバックフラッシュがそのグループの部材を何も引かず、 + /// 2つ以上だと代わりになるはずの部材を両方とも原単位ぶん引く(Spec.md 3.9) + /// + /// グループ名は前後の空白を除いて比べる() + /// + public static string? CheckAlternativeGroups(IEnumerable<(string ChildCode, string? Group, bool IsAlternative)> lines) + { + var normalized = lines.Select(l => (l.ChildCode, Group: NormalizeAlternativeGroup(l.Group), l.IsAlternative)).ToList(); + if (normalized.FirstOrDefault(l => l.IsAlternative && l.Group is null) is { ChildCode: { } orphan }) + { + return ApiText.T("代替部品の行には代替部品グループが必要です(子品目 '{0}')。", orphan); + } + foreach (var group in normalized.Where(l => l.Group is not null).GroupBy(l => l.Group!, StringComparer.Ordinal)) + { + var primaries = group.Where(l => !l.IsAlternative).Select(l => l.ChildCode).ToList(); + if (primaries.Count == 0) + { + return ApiText.T("代替部品グループ '{0}' に主材料(代替部品でない行)がありません。", group.Key); + } + if (primaries.Count > 1) + { + return ApiText.T("代替部品グループ '{0}' に主材料が複数あります({1})。主材料は1つにし、他は代替部品にしてください。", + group.Key, string.Join(", ", primaries)); + } + } + return null; + } + + /// 代替部品グループ名の正規化(前後の空白を除き、空なら未指定)。保存とグループの比較で同じ形にする + public static string? NormalizeAlternativeGroup(string? group) => + string.IsNullOrWhiteSpace(group) ? null : group.Trim(); + + /// + /// MBOMの循環(A→B→…→A)を探す(A-40-10-01)。親品目の明細を に置き換えたとき、 + /// 子品目から既存の明細をたどって親品目へ戻れるなら、その経路(親品目で始まり親品目で終わる品目IDの並び)を返す。 + /// 循環がなければ null。 + /// + /// 今の展開は1段だけなので循環があっても止まらないが、構成として作れない品目になる。 + /// 多段の所要量展開や逆展開を足したときに無限ループになるため、登録の時点で止める。 + /// の親品目自身の明細は、置き換えられるので見ない + /// + /// + public static IReadOnlyList? FindBomCycle( + IReadOnlyDictionary> childrenByParent, int parentId, IEnumerable childIds) + { + // 幅優先でたどり、見つけた品目の手前を覚えておいて経路を組み立てる + var previous = new Dictionary(); + var queue = new Queue(); + foreach (var child in childIds.Distinct()) + { + if (child == parentId) + { + return [parentId, parentId]; + } + previous[child] = parentId; + queue.Enqueue(child); + } + while (queue.Count > 0) + { + var current = queue.Dequeue(); + if (current == parentId || !childrenByParent.TryGetValue(current, out var next)) + { + continue; + } + foreach (var child in next) + { + if (child == parentId) + { + var path = new List { parentId, current }; + for (var at = current; previous[at] != parentId; at = previous[at]) + { + path.Add(previous[at]); + } + path.Add(parentId); + // 末尾から組んだので、親品目→子品目→…→親品目の順に並べ直す + path.Reverse(1, path.Count - 2); + return path; + } + if (previous.TryAdd(child, current)) + { + queue.Enqueue(child); + } + } + } + return null; + } } diff --git a/src/MesApp.Api/Services/ActualCsvKinds.cs b/src/MesApp.Api/Services/ActualCsvKinds.cs index e5bf0f5..dca7088 100644 --- a/src/MesApp.Api/Services/ActualCsvKinds.cs +++ b/src/MesApp.Api/Services/ActualCsvKinds.cs @@ -101,7 +101,7 @@ public static class ActualCsvKinds new("StartedAt", "開始日時", true, DateTimeNote + "。この時刻で直を決める"), new("EndedAt", "終了日時", false, DateTimeNote), new("OutputLocationCode", "入庫先ロケーションコード", false, "最終工程で良品があるときは必須"), - new("Backflush", "バックフラッシュ", false, "true で予定材料×(良品+不良)を先入れ先出しで自動消費"), + new("Backflush", "バックフラッシュ", false, "true でこの工程で使う予定材料を 原単位×(良品+不良) だけ先入れ先出しで自動消費(代替部品は引かない)"), new("Defects", "不良理由別の内訳", false, "不良理由コード=数量 をセミコロン区切り(DR-02=2;DR-03=1)。合計は不良数以下"), ]), MesRoleGroups.ShopFloorRecord), new(new CsvKindInfo(DataRecords, "製造条件データ", false, diff --git a/src/MesApp.Api/Services/ManufacturingOrderService.cs b/src/MesApp.Api/Services/ManufacturingOrderService.cs index bfacd10..d0d07e6 100644 --- a/src/MesApp.Api/Services/ManufacturingOrderService.cs +++ b/src/MesApp.Api/Services/ManufacturingOrderService.cs @@ -213,6 +213,21 @@ public async Task ExpandAsync(ManufacturingOrder order, string? lo return OrderOutcome.Invalid(ApiText.T("品目 '{0}' に工順(BOP)が登録されていません。", order.Product!.Code)); } + // MBOMの消費工程は工順の工程順序で指す。MBOMと工順は別々に改訂できるため、ずれはここで止める + // (見過ごすと、どの作業指示のバックフラッシュでも引かれない部材ができる) + var bom = await db.BomItems + .Include(b => b.ChildProduct) + .Where(b => b.ParentProductId == order.ProductId) + .ToListAsync(ct); + var sequences = routing.Select(r => r.Sequence).ToHashSet(); + if (bom.FirstOrDefault(b => b.RoutingSequence is { } s && !sequences.Contains(s)) is { } orphan) + { + return OrderOutcome.Invalid(ApiText.T( + "MBOMの部材 '{0}' の消費工程(工程順序 {1})が品目 '{2}' の工順にありません。MBOMか工順を直してください。", + orphan.ChildProduct!.Code, orphan.RoutingSequence!.Value, order.Product!.Code)); + } + var finalSequence = routing[^1].Sequence; + // 産出ロット採番(手入力があれば一意性を確認して使用) if (string.IsNullOrWhiteSpace(lotNumber)) { @@ -289,10 +304,8 @@ .. controlItems } // MBOMも展開時点で予定材料として固定する。以降の投入照合(B-30-20-01)と - // バックフラッシュ(B-40-10-09)はこの予定材料を基準にする - var bom = await db.BomItems - .Where(b => b.ParentProductId == order.ProductId) - .ToListAsync(ct); + // バックフラッシュ(B-40-10-09)はこの予定材料を基準にする。 + // 消費工程が未指定の行は最終工程に解決して入れ、以降は工順の改訂に左右されないようにする foreach (var item in bom) { db.ManufacturingOrderMaterials.Add(new ManufacturingOrderMaterial @@ -300,9 +313,10 @@ .. controlItems ManufacturingOrder = order, ChildProductId = item.ChildProductId, QuantityPer = item.QuantityPer, - PlannedQuantity = item.QuantityPer * order.Quantity, + PlannedQuantity = PlannedMaterialQuantity(item.QuantityPer, order.Quantity, order.Product!.StandardDefectRate), AlternativeGroup = item.AlternativeGroup, IsAlternative = item.IsAlternative, + RoutingSequence = item.RoutingSequence ?? finalSequence, }); } @@ -320,4 +334,15 @@ await auditLogger.LogAsync("Production", "Expand", nameof(ManufacturingOrder), o }, ct: ct); return OrderOutcome.Ok(order); } + + /// + /// 予定材料の数量(A-40-10-04)。親品目の標準不良率ぶん割り増す:原単位×指図数量÷(1-率)。 + /// 不良品も部材を消費するため、良品を指図数量だけ得るには不良になる分の部材も要る。 + /// 割り切れない値は列の精度(小数6桁)で切り上げる(切り捨てると欠品側にずれる) + /// + internal static decimal PlannedMaterialQuantity(decimal quantityPer, decimal orderQuantity, decimal defectRatePercent) + { + var quantity = quantityPer * orderQuantity / (1m - defectRatePercent / 100m); + return Math.Ceiling(quantity * 1_000_000m) / 1_000_000m; + } } diff --git a/src/MesApp.Api/Services/MasterCsvKinds.cs b/src/MesApp.Api/Services/MasterCsvKinds.cs index cf9e31f..2d7fe83 100644 --- a/src/MesApp.Api/Services/MasterCsvKinds.cs +++ b/src/MesApp.Api/Services/MasterCsvKinds.cs @@ -40,7 +40,7 @@ public static class MasterCsvKinds new("Unit", "単位", true, "個・kg・m など"), new("Specification", "規格", false, null), new("Type", "品目区分", false, "Product(製品)/ SemiFinished(半製品)/ Material(部材)"), - new("StandardDefectRate", "標準不良率(%)", false, "0〜100"), + new("StandardDefectRate", "標準不良率(%)", false, "0〜99.99。指図の予定材料をこの率ぶん割り増す"), new("DefaultLocationCode", "既定ロケーション", false, "推奨ロケーションの第一候補。空欄で解除"), new("IsActive", "有効", false, "true / false。falseで無効化"), ]), @@ -161,8 +161,9 @@ public static class MasterCsvKinds new("ChildProductCode", "子品目コード", true, null), new("QuantityPer", "必要数量(親1単位)", true, "0より大きい数値"), new("MakeOrBuy", "内外製区分", false, "InHouse(内製)/ Outsourced(外注)"), - new("AlternativeGroup", "代替部品グループ", false, null), - new("IsAlternative", "代替部品", false, "true / false。trueの行を投入するには理由の記録が必要"), + new("AlternativeGroup", "代替部品グループ", false, "グループの主材料(IsAlternative=false)はちょうど1つ"), + new("IsAlternative", "代替部品", false, "true / false。trueの行はAlternativeGroupが必須で、投入するには理由の記録が必要"), + new("RoutingSequence", "消費工程(工程順序)", false, "親品目の工順の工程順序。空なら最終工程。バックフラッシュはこの工程で部材を引く"), ]), new(Routing, "工順(BOP)", false, [ diff --git a/src/MesApp.Api/Services/MasterCsvService.Import.cs b/src/MesApp.Api/Services/MasterCsvService.Import.cs index bba9cb7..ef2b417 100644 --- a/src/MesApp.Api/Services/MasterCsvService.Import.cs +++ b/src/MesApp.Api/Services/MasterCsvService.Import.cs @@ -20,6 +20,12 @@ private sealed class ImportCounter /// 取り込めたが伝えたいこと(エラーと違いロールバックしない。CsvImportResult.Warnings) public List Warnings { get; } = []; + + /// + /// 保存の後に書く個別の監査ログ(対象の種類・ID・内容)。監査ログは書くたびに保存するため、 + /// 取込の途中で書くと検証前の変更まで保存してしまう。全行の検証が通って保存した後にまとめて書く + /// + public List<(string TargetType, string TargetId, object Detail)> Audits { get; } = []; } public async Task ImportAsync( @@ -113,6 +119,10 @@ private async Task ImportOneAsync( await db.SaveChangesAsync(ct); await auditLogger.LogAsync("Master", "CsvImport", kind.Kind, null, detail: $"rows={table.Rows.Count}, created={counter.Created}, updated={counter.Updated}", ct: ct); + foreach (var (targetType, targetId, detail) in counter.Audits) + { + await auditLogger.LogAsync("Master", "Update", targetType, targetId, detail: detail, ct: ct); + } } return new CsvFileImportCount(counter.Created, counter.Updated) { Warnings = counter.Warnings }; } @@ -144,7 +154,7 @@ private async Task ImportProductsAsync( var unit = reader.RequiredText("Unit", 20); var specification = reader.Text("Specification", product.Specification); var type = reader.Enum("Type", product.Type, CsvEnumLabels.ProductTypes); - var defectRate = reader.Number("StandardDefectRate", product.StandardDefectRate, 0, 100); + var defectRate = reader.Number("StandardDefectRate", product.StandardDefectRate, 0, 99.99m); var defaultLocationId = reader.Reference( "DefaultLocationCode", product.DefaultLocationId, locationIds, "ロケーション"); var isActive = reader.Bool("IsActive", product.IsActive); @@ -905,6 +915,10 @@ private async Task ImportBomAsync( { var productIds = await ProductIdsAsync(ct); var existing = await db.BomItems.ToListAsync(ct); + // 循環の判定に使う明細。取り込んだ親品目の分はその都度置き換え、後の行が前の行と循環するのも捕まえる + var edges = existing.GroupBy(b => b.ParentProductId) + .ToDictionary(g => g.Key, g => g.Select(b => b.ChildProductId).ToList()); + var codeById = productIds.ToDictionary(p => p.Value, p => p.Key); foreach (var group in GroupRows(table, "ParentProductCode", errors)) { @@ -928,6 +942,7 @@ private async Task ImportBomAsync( var makeOrBuy = reader.Enum("MakeOrBuy", MakeOrBuy.InHouse, CsvEnumLabels.MakeOrBuys); var alternativeGroup = reader.Text("AlternativeGroup", null, 50); var isAlternative = reader.Bool("IsAlternative", false); + var routingSequence = reader.IntOrNull("RoutingSequence", null, 1); if (quantity is null && !reader.Failed) { reader.Fail(ApiText.T("QuantityPer(必要数量)は必須です。")); @@ -951,16 +966,31 @@ private async Task ImportBomAsync( ChildProductId = childId!.Value, QuantityPer = quantity!.Value, MakeOrBuy = makeOrBuy, - AlternativeGroup = alternativeGroup, + AlternativeGroup = ProductStructurePolicy.NormalizeAlternativeGroup(alternativeGroup), IsAlternative = isAlternative, + RoutingSequence = routingSequence, }); } if (failed) { continue; } + if (ProductStructurePolicy.CheckAlternativeGroups( + lines.Select(l => (codeById[l.ChildProductId], l.AlternativeGroup, l.IsAlternative))) is { } alternativeError) + { + parentReader.Fail(ApiText.T("親品目 '{0}':{1}", parentCode, alternativeError)); + continue; + } + if (ProductStructurePolicy.FindBomCycle(edges, parentId, lines.Select(l => l.ChildProductId)) is { } cycle) + { + parentReader.Fail(ApiText.T("MBOMが循環します({0})。", string.Join(" → ", cycle.Select(id => codeById[id])))); + continue; + } + edges[parentId] = [.. lines.Select(l => l.ChildProductId)]; var current = existing.Where(b => b.ParentProductId == parentId).ToList(); + // 設計変更の追跡(A-40-10-05)は単票APIと同じ形で残す。CSVから改訂すると中身が追えない、にしない + counter.Audits.Add(("Bom", parentId.ToString(), ProductStructureService.BomChangeDetail(current, lines, codeById))); db.BomItems.RemoveRange(current); db.BomItems.AddRange(lines); if (current.Count > 0) diff --git a/src/MesApp.Api/Services/MasterCsvService.cs b/src/MesApp.Api/Services/MasterCsvService.cs index fdb44d9..ba6ac34 100644 --- a/src/MesApp.Api/Services/MasterCsvService.cs +++ b/src/MesApp.Api/Services/MasterCsvService.cs @@ -217,6 +217,7 @@ public async Task ExportAsync(CsvKindInfo kind, bool includeInactive, Ca { b.ParentProduct!.Code, b.ChildProduct!.Code, Num(b.QuantityPer), b.MakeOrBuy.ToString(), b.AlternativeGroup, Bool(b.IsAlternative), + b.RoutingSequence?.ToString(CultureInfo.InvariantCulture), })]; } diff --git a/src/MesApp.Api/Services/ProductStructureService.cs b/src/MesApp.Api/Services/ProductStructureService.cs index ffad404..63ba9e8 100644 --- a/src/MesApp.Api/Services/ProductStructureService.cs +++ b/src/MesApp.Api/Services/ProductStructureService.cs @@ -28,30 +28,76 @@ public sealed class ProductStructureService(MesAppDbContext db, IAuditLogger aud } var childIds = items.Select(i => i.ChildProductId).ToList(); - var validChildIds = await db.Products - .Where(p => childIds.Contains(p.Id)).Select(p => p.Id).ToListAsync(ct); - if (childIds.Except(validChildIds).Any()) + var childCodes = await db.Products + .Where(p => childIds.Contains(p.Id)).ToDictionaryAsync(p => p.Id, p => p.Code, ct); + if (childIds.Except(childCodes.Keys).Any()) { return ApiText.T("存在しない子品目IDが含まれています。"); } + if (ProductStructurePolicy.CheckAlternativeGroups( + items.Select(i => (childCodes[i.ChildProductId], i.AlternativeGroup, i.IsAlternative))) is { } alternativeError) + { + return alternativeError; + } + + var edges = (await db.BomItems.AsNoTracking() + .Select(b => new { b.ParentProductId, b.ChildProductId }) + .ToListAsync(ct)) + .GroupBy(b => b.ParentProductId) + .ToDictionary(g => g.Key, g => g.Select(b => b.ChildProductId).ToList()); + if (ProductStructurePolicy.FindBomCycle(edges, productId, childIds) is { } cycle) + { + var cycleCodes = await db.Products.AsNoTracking() + .Where(p => cycle.Contains(p.Id)).ToDictionaryAsync(p => p.Id, p => p.Code, ct); + return ApiText.T("MBOMが循環します({0})。", string.Join(" → ", cycle.Select(id => cycleCodes[id]))); + } var existing = await db.BomItems.Where(b => b.ParentProductId == productId).ToListAsync(ct); - db.BomItems.RemoveRange(existing); - db.BomItems.AddRange(items.Select(i => new BomItem + var lines = items.Select(i => new BomItem { ParentProductId = productId, ChildProductId = i.ChildProductId, QuantityPer = i.QuantityPer, MakeOrBuy = i.MakeOrBuy, - AlternativeGroup = i.AlternativeGroup, + AlternativeGroup = ProductStructurePolicy.NormalizeAlternativeGroup(i.AlternativeGroup), IsAlternative = i.IsAlternative, - })); + RoutingSequence = i.RoutingSequence, + }).ToList(); + var codeIds = existing.Select(b => b.ChildProductId).Except(childCodes.Keys).ToList(); + var codes = childCodes.Concat(await db.Products.AsNoTracking() + .Where(p => codeIds.Contains(p.Id)).ToDictionaryAsync(p => p.Id, p => p.Code, ct)) + .ToDictionary(); + var detail = BomChangeDetail(existing, lines, codes); + db.BomItems.RemoveRange(existing); + db.BomItems.AddRange(lines); await db.SaveChangesAsync(ct); - await auditLogger.LogAsync("Master", "Update", "Bom", productId.ToString(), - detail: $"items={items.Count}", ct: ct); + await auditLogger.LogAsync("Master", "Update", "Bom", productId.ToString(), detail: detail, ct: ct); return null; } + /// + /// MBOM改訂の監査ログの内容(変更前後の明細。A-40-10-05 設計変更)。 + /// 一括置換なので件数だけでは何を変えたか追えない。子品目はIDでなくコードで残す(品目を無効化・改名しても読めるように)。 + /// 単票APIとマスタCSV取込の両方で同じ形にする + /// + internal static object BomChangeDetail( + IEnumerable before, IEnumerable after, IReadOnlyDictionary codeById) + { + object[] Snapshot(IEnumerable lines) => + [ + .. lines.OrderBy(b => codeById[b.ChildProductId], StringComparer.Ordinal).Select(b => new + { + child = codeById[b.ChildProductId], + quantityPer = b.QuantityPer, + makeOrBuy = b.MakeOrBuy.ToString(), + alternativeGroup = b.AlternativeGroup, + isAlternative = b.IsAlternative, + routingSequence = b.RoutingSequence, + }), + ]; + return new { before = Snapshot(before), after = Snapshot(after), reason = (string?)null }; + } + /// 工順(BOP)の一括置換(工程変更 A-40-20-03、I-50-30 も本処理で反映) public async Task ReplaceRoutingAsync(int productId, List steps, CancellationToken ct) { diff --git a/src/MesApp.Api/Services/WorkOrderExecutionService.cs b/src/MesApp.Api/Services/WorkOrderExecutionService.cs index 1827705..f9f9346 100644 --- a/src/MesApp.Api/Services/WorkOrderExecutionService.cs +++ b/src/MesApp.Api/Services/WorkOrderExecutionService.cs @@ -343,13 +343,20 @@ public async Task> AddProductionRecordAsync( { // 消費量の基準は展開時に固定した予定材料の原単位(Spec.md 5.7)。 // 代替部品の行は除く:主材料の代わりに使う部材なので、両方を原単位ぶん引くと二重に減る。 - // 代替の投入には理由が要る(Spec.md 3.9)ため、代替部品は手動の部材投入で理由とともに記録する + // 代替の投入には理由が要る(Spec.md 3.9)ため、代替部品は手動の部材投入で理由とともに記録する。 + // 引くのはこの作業指示の工程で消費する部材だけ。指図の全部材を引くと、工程ごとに + // バックフラッシュしたとき工程数ぶん二重に減る。消費工程が null の行(項目追加前の指図)は最終工程で引く + var sequence = workOrder.RoutingSequence; var bom = await db.ManufacturingOrderMaterials - .Where(m => m.ManufacturingOrderId == workOrder.ManufacturingOrderId && !m.IsAlternative) + .Where(m => m.ManufacturingOrderId == workOrder.ManufacturingOrderId && !m.IsAlternative + && (m.RoutingSequence == sequence || (m.RoutingSequence == null && isFinalStep))) .ToListAsync(ct); if (bom.Count == 0) { - return Outcome.Invalid(ApiText.T("予定材料が未登録(展開時にMBOMが未登録)のためバックフラッシュできません。")); + return Outcome.Invalid( + await db.ManufacturingOrderMaterials.AnyAsync(m => m.ManufacturingOrderId == workOrder.ManufacturingOrderId, ct) + ? ApiText.T("この工程(工程順序 {0})で消費する予定材料が無いためバックフラッシュできません。", sequence) + : ApiText.T("予定材料が未登録(展開時にMBOMが未登録)のためバックフラッシュできません。")); } var totalProduced = request.GoodQuantity + request.DefectQuantity; foreach (var bomItem in bom) diff --git a/src/MesApp.Client.Web/Pages/ManufacturingOrderDetail.razor b/src/MesApp.Client.Web/Pages/ManufacturingOrderDetail.razor index 0d22415..1afcaea 100644 --- a/src/MesApp.Client.Web/Pages/ManufacturingOrderDetail.razor +++ b/src/MesApp.Client.Web/Pages/ManufacturingOrderDetail.razor @@ -105,7 +105,7 @@ else

    @L["予定材料(展開時のMBOMで確定)"]

    - + @foreach (var material in _detail.Materials) @@ -115,6 +115,7 @@ else + } diff --git a/src/MesApp.Client.Web/Pages/Masters/Products/ProductBomEditor.razor b/src/MesApp.Client.Web/Pages/Masters/Products/ProductBomEditor.razor index 3deccf5..efddcd1 100644 --- a/src/MesApp.Client.Web/Pages/Masters/Products/ProductBomEditor.razor +++ b/src/MesApp.Client.Web/Pages/Masters/Products/ProductBomEditor.razor @@ -5,7 +5,7 @@

    @L["{0} {1} の MBOM(A-40-10)", Product.Code, Product.Name]

    @L["品目"]@L["原単位"]@L["予定数量"]@L["代替部品グループ"]
    @L["品目"]@L["原単位"]@L["予定数量"]@L["代替部品グループ"]@L["消費工程"]
    @material.QuantityPer @material.PlannedQuantity @(material.AlternativeGroup ?? "-")@(material.RoutingSequence?.ToString() ?? L["最終工程"].Value)
    - + @foreach (var line in _lines) { @@ -15,6 +15,7 @@ + } @@ -54,6 +55,17 @@ @L["代替部品(主材料でない)"] + @* バックフラッシュはこの工程の作業指示で部材を引く(B-40-10-09) *@ +
    + + +
    @@ -73,6 +85,7 @@ [Parameter] public EventCallback OnSaved { get; set; } private readonly List _lines = []; + private List _routing = []; private int _loadedProductId; private int _childId; @@ -80,6 +93,7 @@ private MakeOrBuy _makeOrBuy = MakeOrBuy.InHouse; private string? _altGroup; private bool _isAlternative; + private int _routingSequence; private string? _error; private string? _message; @@ -101,7 +115,9 @@ var bom = await Http.GetFromJsonAsync>($"api/products/{Product.Id}/bom") ?? []; _lines.AddRange(bom.Select(b => new BomItemRequest(b.ChildProductId, b.QuantityPer, b.MakeOrBuy, b.AlternativeGroup, - b.IsAlternative))); + b.IsAlternative, b.RoutingSequence))); + // 消費工程の選択肢。工順は隣の編集欄で直せるため、MBOMを読み直すたびに取り直す + _routing = await Http.GetFromJsonAsync>($"api/products/{Product.Id}/routing") ?? []; // 親から呼ばれたときはパラメータが変わらないため、自分で再描画を要求する StateHasChanged(); } @@ -115,11 +131,20 @@ } _error = null; _lines.Add(new BomItemRequest(_childId, _quantity, _makeOrBuy, - string.IsNullOrWhiteSpace(_altGroup) ? null : _altGroup, _isAlternative)); + string.IsNullOrWhiteSpace(_altGroup) ? null : _altGroup, _isAlternative, + _routingSequence == 0 ? null : _routingSequence)); _childId = 0; _isAlternative = false; } + /// 消費工程の表示。工順に無い工程順序は指図の展開で拒否されるため、ここで目立たせる + private string StepLabel(int? sequence) => sequence switch + { + null => L["最終工程"], + { } s when _routing.FirstOrDefault(r => r.Sequence == s) is { } step => $"{s} {step.ProcessName}", + { } s => L["{0}(工順に無し)", s], + }; + private async Task SaveAsync() { _busy = true; diff --git a/src/MesApp.Client.Web/Pages/Masters/ProductsTab.razor b/src/MesApp.Client.Web/Pages/Masters/ProductsTab.razor index 6dcb774..7d73590 100644 --- a/src/MesApp.Client.Web/Pages/Masters/ProductsTab.razor +++ b/src/MesApp.Client.Web/Pages/Masters/ProductsTab.razor @@ -34,7 +34,7 @@
    - +
    diff --git a/src/MesApp.Client.Web/Pages/Print/WorkInstruction.razor b/src/MesApp.Client.Web/Pages/Print/WorkInstruction.razor index 653859e..44a657d 100644 --- a/src/MesApp.Client.Web/Pages/Print/WorkInstruction.razor +++ b/src/MesApp.Client.Web/Pages/Print/WorkInstruction.razor @@ -2,7 +2,8 @@ @attribute [Authorize] @inject HttpClient Http -@* 作業指示書(B-10-30-04)。構成部品(MBOM)・工程情報を含む *@ +@* 作業指示書(B-10-30-04)。この工程で使う部材と工程情報を含む。 + 部材は現行のMBOMではなく指図の予定材料(展開時に固定。Spec.md 5.7)から、この工程を消費工程とする行だけを出す *@ @L["作業指示書 - Parallel Factory MES"] @if (_workOrder is null) @@ -36,23 +37,23 @@ else
    @L["子品目"]@L["必要数量(親1単位)"]@L["内外製"]@L["代替グループ"]@L["代替部品"]
    @L["子品目"]@L["必要数量(親1単位)"]@L["内外製"]@L["代替グループ"]@L["代替部品"]@L["消費工程"]
    @EnumLabels.Of(line.MakeOrBuy) @(line.AlternativeGroup ?? "-") @(line.IsAlternative ? L["代替"] : L["主"])@StepLabel(line.RoutingSequence)
    -

    @L["構成部品(MBOM)"]

    - @if (_bom.Count == 0) +

    @L["この工程で使う部材(予定材料)"]

    + @if (_materials.Count == 0) { -

    @L["MBOMは登録されていません。"]

    +

    @L["この工程で使う部材はありません。"]

    } else { - + - @foreach (var line in _bom) + @foreach (var line in _materials) { - - - - + + + + } @@ -76,15 +77,32 @@ else [Parameter] public int Id { get; set; } private WorkOrderResponse? _workOrder; - private List _bom = []; + private List _materials = []; + private decimal _orderQuantity; protected override async Task OnParametersSetAsync() { _workOrder = await Http.GetFromJsonAsync($"api/work-orders/{Id}"); if (_workOrder is not null) { - _bom = await Http.GetFromJsonAsync>( - $"api/products/{_workOrder.ProductId}/bom") ?? []; + var order = await Http.GetFromJsonAsync( + $"api/manufacturing-orders/{_workOrder.ManufacturingOrderId}"); + // 消費工程が空の行は消費工程の追加前に展開した指図のもので、最終工程で使う扱い(バックフラッシュと同じ) + var finalSequence = order?.WorkOrders + .Where(w => w.Status != WorkOrderStatus.Canceled) + .Max(w => (int?)w.RoutingSequence); + _orderQuantity = order?.Order.Quantity ?? 0; + var sequence = _workOrder.RoutingSequence; + _materials = [.. (order?.Materials ?? []) + .Where(m => m.RoutingSequence == sequence || (m.RoutingSequence is null && sequence == finalSequence))]; } } + + /// + /// 計画数分の必要数量。予定材料の予定数量(標準不良率で割り増し済み。Spec.md 5.2)を、 + /// 指図数量に対するこの作業指示の計画数の割合で按分する。原単位×計画数で出すと割り増しが抜け、指図の予定材料と食い違う + /// + private decimal RequiredQuantity(OrderMaterialResponse line) => + _orderQuantity == 0 ? line.QuantityPer * _workOrder!.PlannedQuantity + : line.PlannedQuantity * _workOrder!.PlannedQuantity / _orderQuantity; } diff --git a/src/MesApp.Client.Web/Shared/UiText.en.resx b/src/MesApp.Client.Web/Shared/UiText.en.resx index e307d34..54ce73d 100644 --- a/src/MesApp.Client.Web/Shared/UiText.en.resx +++ b/src/MesApp.Client.Web/Shared/UiText.en.resx @@ -2127,11 +2127,11 @@ WORK INSTRUCTION - - Components (MBOM) + + Materials used in this step (planned materials) - - No MBOM registered. + + No materials are used in this step. Required Qty (for planned qty) @@ -4155,4 +4155,16 @@ production date + product code + process code + work center code + + Consuming step + + + Final step + + + Final step (not specified) + + + {0} (not in routing) + diff --git a/src/MesApp.Core/Contracts/Masters/MasterContracts.cs b/src/MesApp.Core/Contracts/Masters/MasterContracts.cs index 83d5416..5cb58bc 100644 --- a/src/MesApp.Core/Contracts/Masters/MasterContracts.cs +++ b/src/MesApp.Core/Contracts/Masters/MasterContracts.cs @@ -11,7 +11,8 @@ public record ProductRequest( [Required, MaxLength(20)] string Unit, string? Specification, ProductType Type, - [Range(0, 100)] decimal StandardDefectRate, + /// 標準不良率(%)。予定材料の数量を割り増す(÷(1-率))ため100%は受け付けない + [Range(0, 99.99)] decimal StandardDefectRate, /// 既定の入庫先ロケーション(推奨ロケーション指示の第一候補。D-10-30-03、D-40-40-03) int? DefaultLocationId = null); @@ -28,12 +29,14 @@ public record BomItemRequest( MakeOrBuy MakeOrBuy, string? AlternativeGroup, /// 代替部品か(同一グループ内の主材料でない行。投入時に理由の記録を求める) - bool IsAlternative = false); + bool IsAlternative = false, + /// 消費する工程の工程順序(未指定なら最終工程。バックフラッシュはこの工程で引く) + [Range(1, int.MaxValue)] int? RoutingSequence = null); public record BomItemResponse( int Id, int ChildProductId, string ChildProductCode, string ChildProductName, decimal QuantityPer, MakeOrBuy MakeOrBuy, string? AlternativeGroup, - bool IsAlternative = false); + bool IsAlternative = false, int? RoutingSequence = null); // ---- 工程(Process)---- diff --git a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs index 187efd4..ad62071 100644 --- a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs +++ b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs @@ -28,7 +28,8 @@ public record ManufacturingOrderResponse( /// 指図の予定材料(展開時にMBOMから固定。Spec.md 5.7) public record OrderMaterialResponse( int ChildProductId, string ProductCode, string ProductName, - decimal QuantityPer, decimal PlannedQuantity, string? AlternativeGroup); + decimal QuantityPer, decimal PlannedQuantity, string? AlternativeGroup, + int? RoutingSequence = null); public record ManufacturingOrderDetailResponse( ManufacturingOrderResponse Order, diff --git a/src/MesApp.Core/Entities/Masters.cs b/src/MesApp.Core/Entities/Masters.cs index b7292a4..a8ed796 100644 --- a/src/MesApp.Core/Entities/Masters.cs +++ b/src/MesApp.Core/Entities/Masters.cs @@ -20,7 +20,10 @@ public class Product : IDeactivatableMaster public ProductType Type { get; set; } - /// 標準不良率(%。A-40-10-04) + /// + /// 標準不良率(%。A-40-10-04)。指図の展開で予定材料の数量を 原単位×指図数量÷(1-率) に割り増す。 + /// 不良品も部材を消費する(バックフラッシュは良品+不良の数で引く)ため、良品を指図数量だけ得るのに要る部材を見込む + /// public decimal StandardDefectRate { get; set; } /// @@ -61,6 +64,13 @@ public class BomItem /// 代替部品の投入には理由の記録を求める(Spec.md 3.9 部材投入の照合) /// public bool IsAlternative { get; set; } + + /// + /// この部材を消費する工程(親品目の工順の工程順序 。B-40-10-09)。 + /// 未設定なら最終工程で消費する。工順は一括置換で行が作り直されるため、IDではなく工程順序で指す。 + /// 工順に無い工程順序は指図の展開時に拒否する(MBOMと工順はどちらを先に登録してもよいため、登録時には照合しない) + /// + public int? RoutingSequence { get; set; } } /// 工程マスタ(Spec.md 5.1 Process) diff --git a/src/MesApp.Core/Entities/Production.cs b/src/MesApp.Core/Entities/Production.cs index b0fb782..fcabb75 100644 --- a/src/MesApp.Core/Entities/Production.cs +++ b/src/MesApp.Core/Entities/Production.cs @@ -75,6 +75,13 @@ public class ManufacturingOrderMaterial /// 代替部品か(展開時点のMBOMの値。投入時に理由の記録を求める) public bool IsAlternative { get; set; } + + /// + /// この部材を消費する工程順序(展開時点のMBOMの値。未設定の行は展開時に最終工程へ解決して入れる)。 + /// バックフラッシュはこの工程の作業指示でだけ消費する。 + /// null は本項目の追加前に展開した指図で、最終工程で消費したものとして扱う + /// + public int? RoutingSequence { get; set; } } /// 作業指示(Spec.md 5.2 WorkOrder。製造指図×工程。B-10) diff --git a/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.Designer.cs b/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.Designer.cs new file mode 100644 index 0000000..7cf7afa --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.Designer.cs @@ -0,0 +1,4753 @@ +// +using System; +using MesApp.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MesApp.Infrastructure.Migrations +{ + [DbContext(typeof(MesAppDbContext))] + [Migration("20260922224608_AddBomItemRoutingSequence")] + partial class AddBomItemRoutingSequence + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("MustChangePassword") + .HasColumnType("INTEGER"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("ShiftId") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkCenterId"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Detail") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("RecordedOn") + .HasColumnType("TEXT"); + + b.Property("TargetId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("TargetType") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RecordedOn"); + + b.HasIndex("Timestamp"); + + b.HasIndex("UserId"); + + b.HasIndex("Category", "Action"); + + b.HasIndex("TargetType", "TargetId"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ChildProductId") + .HasColumnType("INTEGER"); + + b.Property("IsAlternative") + .HasColumnType("INTEGER"); + + b.Property("MakeOrBuy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ParentProductId") + .HasColumnType("INTEGER"); + + b.Property("QuantityPer") + .HasColumnType("TEXT"); + + b.Property("RoutingSequence") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ParentProductId", "ChildProductId") + .IsUnique(); + + b.ToTable("BomItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Checklists"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChecklistId") + .HasColumnType("INTEGER"); + + b.Property("IsRequired") + .HasColumnType("INTEGER"); + + b.Property("Sequence") + .HasColumnType("INTEGER"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.ToTable("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChecklistId") + .HasColumnType("INTEGER"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("PerformedAt") + .HasColumnType("TEXT"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ChecklistRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChecklistItemId") + .HasColumnType("INTEGER"); + + b.Property("ChecklistRecordId") + .HasColumnType("INTEGER"); + + b.Property("IsChecked") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistItemId"); + + b.HasIndex("ChecklistRecordId"); + + b.ToTable("ChecklistResultItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LowerLimit") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("TargetProcessId") + .HasColumnType("INTEGER"); + + b.Property("TargetProductId") + .HasColumnType("INTEGER"); + + b.Property("TargetValue") + .HasColumnType("TEXT"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpperLimit") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("ControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.DefectReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("DefectReasons"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AssetNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("MaintenanceParts") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("MaintenanceThreshold") + .HasColumnType("TEXT"); + + b.Property("MaintenanceType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Site") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AssetNo") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Equipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndedAt") + .HasColumnType("TEXT"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RecordedByUserId") + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("StopCause") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("EquipmentLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("QuantityPer") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("EquipmentId", "ProductId") + .IsUnique(); + + b.ToTable("EquipmentParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDevice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CalibratedOn") + .HasColumnType("TEXT"); + + b.Property("CalibrationCycleDays") + .HasColumnType("INTEGER"); + + b.Property("CalibrationDueOn") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Location") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("SerialNo") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("InspectionDevices"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CalibratedOn") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InspectionDeviceId") + .HasColumnType("INTEGER"); + + b.Property("NextDueOn") + .HasColumnType("TEXT"); + + b.Property("PerformedByUserId") + .HasColumnType("TEXT"); + + b.Property("Result") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("InspectionDeviceId", "CalibratedOn"); + + b.ToTable("InspectionDeviceCalibrations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LowerLimit") + .HasColumnType("TEXT"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("SamplingCount") + .HasColumnType("INTEGER"); + + b.Property("StandardValue") + .HasColumnType("TEXT"); + + b.Property("TargetProcessId") + .HasColumnType("INTEGER"); + + b.Property("TargetProductId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpperLimit") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("InspectionItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("JudgedAt") + .HasColumnType("TEXT"); + + b.Property("JudgedByUserId") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OverallJudgment") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("RequestedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("TargetLotId") + .HasColumnType("INTEGER"); + + b.Property("TargetWorkOrderId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("TargetLotId"); + + b.HasIndex("TargetWorkOrderId"); + + b.ToTable("InspectionOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("InspectionItemId") + .HasColumnType("INTEGER"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ItemVersion") + .HasColumnType("INTEGER"); + + b.Property("LowerLimit") + .HasColumnType("TEXT"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("SamplingCount") + .HasColumnType("INTEGER"); + + b.Property("StandardValue") + .HasColumnType("TEXT"); + + b.Property("UpperLimit") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId", "InspectionItemId") + .IsUnique(); + + b.ToTable("InspectionOrderItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionNote") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("InspectedAt") + .HasColumnType("TEXT"); + + b.Property("InspectedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InspectionDeviceId") + .HasColumnType("INTEGER"); + + b.Property("InspectionItemId") + .HasColumnType("INTEGER"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("Judgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("MeasuredValue") + .HasColumnType("TEXT"); + + b.Property("SampleNo") + .HasColumnType("INTEGER"); + + b.Property("TextValue") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("InspectedByUserId"); + + b.HasIndex("InspectionDeviceId"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId"); + + b.ToTable("InspectionResults"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AfterJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AfterMeasuredValue") + .HasColumnType("TEXT"); + + b.Property("AfterTextValue") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("BeforeJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("BeforeMeasuredValue") + .HasColumnType("TEXT"); + + b.Property("BeforeTextValue") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("CorrectedAt") + .HasColumnType("TEXT"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("TEXT"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("InspectionResultId") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("InspectionResultId"); + + b.HasIndex("InspectionOrderId", "Id"); + + b.ToTable("InspectionResultCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId", "LocationId") + .IsUnique(); + + b.HasIndex("ProductId", "LocationId"); + + b.ToTable("InventoryStocks"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FromLocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PerformedByUserId") + .HasColumnType("TEXT"); + + b.Property("PickingOrderId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("ShippingOrderId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("ToLocationId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("Timestamp"); + + b.HasIndex("ToLocationId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("InventoryTransactions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AreaType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("ShelfNo") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ExpiresOn") + .HasColumnType("TEXT"); + + b.Property("Grade") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("InitialQuantity") + .HasColumnType("TEXT"); + + b.Property("LotNumber") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("ManufacturedOn") + .HasColumnType("TEXT"); + + b.Property("OriginType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ParentLotId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("SourceWorkOrderId") + .HasColumnType("INTEGER"); + + b.Property("StockStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LotNumber") + .IsUnique(); + + b.HasIndex("ParentLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceWorkOrderId"); + + b.ToTable("Lots"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChildLotId") + .HasColumnType("INTEGER"); + + b.Property("OccurredAt") + .HasColumnType("TEXT"); + + b.Property("ParentLotId") + .HasColumnType("INTEGER"); + + b.Property("PerformedByUserId") + .HasMaxLength(450) + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("RelationType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChildLotId"); + + b.HasIndex("ParentLotId"); + + b.ToTable("LotGenealogies"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChangedAt") + .HasColumnType("TEXT"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("TEXT"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("NonconformanceReportId") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LotId", "Id"); + + b.ToTable("LotStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("MaintenancePlanId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ProcedureId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ScheduledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ToolId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("MaintenancePlanId"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ProcedureId"); + + b.HasIndex("Status"); + + b.HasIndex("ToolId"); + + b.ToTable("MaintenanceOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("CycleDays") + .HasColumnType("INTEGER"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("PlanYear") + .HasColumnType("INTEGER"); + + b.Property("ScheduledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId", "PlanYear"); + + b.ToTable("MaintenancePlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("TargetEquipmentId") + .HasColumnType("INTEGER"); + + b.Property("TargetToolId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.HasIndex("TargetEquipmentId"); + + b.HasIndex("TargetToolId"); + + b.ToTable("MaintenanceProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndedAt") + .HasColumnType("TEXT"); + + b.Property("MaintenanceOrderId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("PartsUsed") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Result") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MaintenanceOrderId"); + + b.HasIndex("PerformedByUserId"); + + b.ToTable("MaintenanceRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("MaintenanceRecordId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("MaintenanceRecordId"); + + b.HasIndex("ProductId"); + + b.ToTable("MaintenanceRecordParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("DueDate") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrderType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OutputLotId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("SourceOrderId") + .HasColumnType("INTEGER"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("OutputLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceOrderId"); + + b.HasIndex("Status"); + + b.ToTable("ManufacturingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ChildProductId") + .HasColumnType("INTEGER"); + + b.Property("IsAlternative") + .HasColumnType("INTEGER"); + + b.Property("ManufacturingOrderId") + .HasColumnType("INTEGER"); + + b.Property("PlannedQuantity") + .HasColumnType("TEXT"); + + b.Property("QuantityPer") + .HasColumnType("TEXT"); + + b.Property("RoutingSequence") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ManufacturingOrderId", "ChildProductId") + .IsUnique(); + + b.ToTable("ManufacturingOrderMaterials"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConsumedAt") + .HasColumnType("TEXT"); + + b.Property("IsSubstitute") + .HasColumnType("INTEGER"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("Method") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("RecordedByUserId") + .HasColumnType("TEXT"); + + b.Property("SubstituteReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("MaterialConsumptions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ActionCompletedAt") + .HasColumnType("TEXT"); + + b.Property("ActionCompletedByUserId") + .HasColumnType("TEXT"); + + b.Property("ActionInstructedAt") + .HasColumnType("TEXT"); + + b.Property("ActionInstructedByUserId") + .HasColumnType("TEXT"); + + b.Property("ActionInstruction") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("ActionRecord") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("CauseCategory") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CauseDetail") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("ReportNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ReportedByUserId") + .HasColumnType("TEXT"); + + b.Property("ReworkOrderId") + .HasColumnType("INTEGER"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ReportNo") + .IsUnique(); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("ReworkOrderId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("NonconformanceReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NumberSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastValue") + .HasColumnType("INTEGER"); + + b.Property("Prefix") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Prefix") + .IsUnique(); + + b.ToTable("NumberSequences"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("PickingOrderId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("PickingOrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("PickingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("ExecutedAt") + .HasColumnType("TEXT"); + + b.Property("ExecutedByUserId") + .HasColumnType("TEXT"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ShippingOrderId") + .HasColumnType("INTEGER"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ShippingOrderId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("PickingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProcessMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Processes", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DefaultLocationId") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Specification") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("StandardDefectRate") + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("DefaultLocationId"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsDeviation") + .HasColumnType("INTEGER"); + + b.Property("Item") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("NumericValue") + .HasColumnType("TEXT"); + + b.Property("RecordedAt") + .HasColumnType("TEXT"); + + b.Property("RecordedByUserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("WorkOrderControlItemId") + .HasColumnType("INTEGER"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionDataRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DefectReasonId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ProductionRecordId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DefectReasonId"); + + b.HasIndex("ProductionRecordId"); + + b.ToTable("ProductionDefects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BusinessDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PlannedQuantity") + .HasColumnType("TEXT"); + + b.Property("ProcessId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("BusinessDate", "ProductId", "ProcessId", "WorkCenterId") + .IsUnique(); + + b.ToTable("ProductionPlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DefectQuantity") + .HasColumnType("TEXT"); + + b.Property("EndedAt") + .HasColumnType("TEXT"); + + b.Property("GoodQuantity") + .HasColumnType("TEXT"); + + b.Property("OutputLocationId") + .HasColumnType("INTEGER"); + + b.Property("OutputLotId") + .HasColumnType("INTEGER"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ReworkQuantity") + .HasColumnType("TEXT"); + + b.Property("ScrapQuantity") + .HasColumnType("TEXT"); + + b.Property("ShiftId") + .HasColumnType("INTEGER"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OutputLotId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AfterDefectQuantity") + .HasColumnType("TEXT"); + + b.Property("AfterGoodQuantity") + .HasColumnType("TEXT"); + + b.Property("AfterReworkQuantity") + .HasColumnType("TEXT"); + + b.Property("AfterScrapQuantity") + .HasColumnType("TEXT"); + + b.Property("BeforeDefectQuantity") + .HasColumnType("TEXT"); + + b.Property("BeforeGoodQuantity") + .HasColumnType("TEXT"); + + b.Property("BeforeReworkQuantity") + .HasColumnType("TEXT"); + + b.Property("BeforeScrapQuantity") + .HasColumnType("TEXT"); + + b.Property("CorrectedAt") + .HasColumnType("TEXT"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("TEXT"); + + b.Property("ProductionRecordId") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("ProductionRecordId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("ProductionRecordCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("RefreshTokens"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChecklistId") + .HasColumnType("INTEGER"); + + b.Property("ControlItems") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("ProcessId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("RequiredSkillId") + .HasColumnType("INTEGER"); + + b.Property("Sequence") + .HasColumnType("INTEGER"); + + b.Property("StandardSetupMinutes") + .HasColumnType("TEXT"); + + b.Property("StandardWorkMinutes") + .HasColumnType("TEXT"); + + b.Property("ToolId") + .HasColumnType("INTEGER"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.Property("WorkProcedureId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkProcedureId"); + + b.HasIndex("ProductId", "Sequence") + .IsUnique(); + + b.ToTable("Routings"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("RoutingId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("RoutingId", "EquipmentId") + .IsUnique(); + + b.ToTable("RoutingEquipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClosedByUserId") + .HasColumnType("TEXT"); + + b.Property("ClosedOn") + .HasColumnType("TEXT"); + + b.Property("CollectedByUserId") + .HasColumnType("TEXT"); + + b.Property("CollectedOn") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InspectionOrderId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("RetainUntil") + .HasColumnType("TEXT"); + + b.Property("SampleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StorageLocationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SampleNo") + .IsUnique(); + + b.HasIndex("StorageLocationId"); + + b.HasIndex("Status", "RetainUntil"); + + b.ToTable("SampleStorages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AbnormalityNote") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndedAt") + .HasColumnType("TEXT"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("SetupRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Shift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("EndTime") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Shifts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("JudgedAt") + .HasColumnType("TEXT"); + + b.Property("JudgedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("JudgmentNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ShippingOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("JudgedByUserId"); + + b.HasIndex("JudgmentNo") + .IsUnique(); + + b.HasIndex("LotId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShipmentJudgments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("ShippedQuantity") + .HasColumnType("TEXT"); + + b.Property("ShippingOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShippingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("PlannedDate") + .HasColumnType("TEXT"); + + b.Property("ShippedAt") + .HasColumnType("TEXT"); + + b.Property("ShippedByUserId") + .HasColumnType("TEXT"); + + b.Property("ShippingNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ShippingNo") + .IsUnique(); + + b.ToTable("ShippingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SkillMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RequiresExpiry") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Skills", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("FinalizedAt") + .HasColumnType("TEXT"); + + b.Property("FinalizedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("StocktakeNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("TargetLocationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("StocktakeNo") + .IsUnique(); + + b.HasIndex("TargetLocationId"); + + b.ToTable("Stocktakes"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CountedQuantity") + .HasColumnType("TEXT"); + + b.Property("IsAdjusted") + .HasColumnType("INTEGER"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("StocktakeId") + .HasColumnType("INTEGER"); + + b.Property("TheoreticalQuantity") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("StocktakeId"); + + b.ToTable("StocktakeLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Tool", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LifeResetAt") + .HasColumnType("TEXT"); + + b.Property("LifeThresholdCount") + .HasColumnType("INTEGER"); + + b.Property("LifeThresholdHours") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ToolType") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Tools"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllocatedAt") + .HasColumnType("TEXT"); + + b.Property("AllocatedByUserId") + .HasColumnType("TEXT"); + + b.Property("IssuedAt") + .HasColumnType("TEXT"); + + b.Property("IssuedToUserId") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReturnedAt") + .HasColumnType("TEXT"); + + b.Property("ReturnedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("ToolId") + .HasColumnType("INTEGER"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("IssuedToUserId"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("ToolId", "Status"); + + b.ToTable("ToolIssues"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RecordedAt") + .HasColumnType("TEXT"); + + b.Property("RecordedByUserId") + .HasColumnType("TEXT"); + + b.Property("ToolId") + .HasColumnType("INTEGER"); + + b.Property("UsageCount") + .HasColumnType("INTEGER"); + + b.Property("UsageHours") + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ToolUsages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("ExecutedAt") + .HasColumnType("TEXT"); + + b.Property("ExecutedByUserId") + .HasColumnType("TEXT"); + + b.Property("FromLocationId") + .HasColumnType("INTEGER"); + + b.Property("LotId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ToLocationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("Status"); + + b.HasIndex("ToLocationId"); + + b.ToTable("TransferOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EquipmentId") + .HasColumnType("INTEGER"); + + b.Property("OccurredAt") + .HasColumnType("TEXT"); + + b.Property("ReportedByUserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ResponseHistory") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("TroubleReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AcquiredOn") + .HasColumnType("TEXT"); + + b.Property("ExpiresOn") + .HasColumnType("TEXT"); + + b.Property("SkillId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.HasIndex("UserId", "SkillId") + .IsUnique(); + + b.ToTable("UserSkills"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("WorkCenters"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AssignedEquipmentId") + .HasColumnType("INTEGER"); + + b.Property("AssignedUserId") + .HasColumnType("TEXT"); + + b.Property("ControlItems") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DispatchOrder") + .HasColumnType("INTEGER"); + + b.Property("ManufacturingOrderId") + .HasColumnType("INTEGER"); + + b.Property("PlannedQuantity") + .HasColumnType("TEXT"); + + b.Property("ProcessId") + .HasColumnType("INTEGER"); + + b.Property("ProductId") + .HasColumnType("INTEGER"); + + b.Property("RequiredSkillId") + .HasColumnType("INTEGER"); + + b.Property("RoutingChecklistId") + .HasColumnType("INTEGER"); + + b.Property("RoutingSequence") + .HasColumnType("INTEGER"); + + b.Property("StandardSetupMinutes") + .HasColumnType("TEXT"); + + b.Property("StandardWorkMinutes") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.Property("WorkOrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("WorkProcedureId") + .HasColumnType("INTEGER"); + + b.Property("WorkProcedureVersion") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AssignedEquipmentId"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("ManufacturingOrderId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("RoutingChecklistId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkOrderNo") + .IsUnique(); + + b.HasIndex("WorkProcedureId"); + + b.ToTable("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ControlItemId") + .HasColumnType("INTEGER"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ItemVersion") + .HasColumnType("INTEGER"); + + b.Property("LowerLimit") + .HasColumnType("TEXT"); + + b.Property("TargetValue") + .HasColumnType("TEXT"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpperLimit") + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("WorkOrderControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChangedAt") + .HasColumnType("TEXT"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("TEXT"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChangedByUserId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("WorkOrderStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Reference") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.ToTable("WorkProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndedAt") + .HasColumnType("TEXT"); + + b.Property("IndirectCategory") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkOrderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("UserId", "StartedAt"); + + b.ToTable("WorkTimeRecords"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Shift"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "ParentProduct") + .WithMany() + .HasForeignKey("ParentProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ParentProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", null) + .WithMany("Items") + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.HasOne("MesApp.Core.Entities.ChecklistItem", "ChecklistItem") + .WithMany() + .HasForeignKey("ChecklistItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ChecklistRecord", null) + .WithMany("Results") + .HasForeignKey("ChecklistRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Equipment"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany("Parts") + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionDevice"); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "RequestedBy") + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "TargetLot") + .WithMany() + .HasForeignKey("TargetLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "TargetWorkOrder") + .WithMany() + .HasForeignKey("TargetWorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RequestedBy"); + + b.Navigation("TargetLot"); + + b.Navigation("TargetWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Items") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "InspectedBy") + .WithMany() + .HasForeignKey("InspectedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Results") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectedBy"); + + b.Navigation("InspectionDevice"); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.InspectionResult", "InspectionResult") + .WithMany() + .HasForeignKey("InspectionResultId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("InspectionResult"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("ToLocation"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "SourceWorkOrder") + .WithMany() + .HasForeignKey("SourceWorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("ParentLot"); + + b.Navigation("Product"); + + b.Navigation("SourceWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ChildLot") + .WithMany() + .HasForeignKey("ChildLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ChildLot"); + + b.Navigation("ParentLot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.MaintenancePlan", "MaintenancePlan") + .WithMany() + .HasForeignKey("MaintenancePlanId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.MaintenanceProcedure", "Procedure") + .WithMany() + .HasForeignKey("ProcedureId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CreatedBy"); + + b.Navigation("Equipment"); + + b.Navigation("MaintenancePlan"); + + b.Navigation("Procedure"); + + b.Navigation("Tool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") + .WithMany() + .HasForeignKey("TargetEquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "TargetTool") + .WithMany() + .HasForeignKey("TargetToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetEquipment"); + + b.Navigation("TargetTool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.HasOne("MesApp.Core.Entities.MaintenanceOrder", null) + .WithMany("Records") + .HasForeignKey("MaintenanceOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.MaintenanceRecord", null) + .WithMany("Parts") + .HasForeignKey("MaintenanceRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "SourceOrder") + .WithMany() + .HasForeignKey("SourceOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("OutputLot"); + + b.Navigation("Product"); + + b.Navigation("SourceOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("Materials") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ManufacturingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ReworkOrder") + .WithMany() + .HasForeignKey("ReworkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("ReportedBy"); + + b.Navigation("ReworkOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.PickingOrder", null) + .WithMany("Lines") + .HasForeignKey("PickingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("ShippingOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.HasOne("MesApp.Core.Entities.Location", "DefaultLocation") + .WithMany() + .HasForeignKey("DefaultLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.HasOne("MesApp.Core.Entities.WorkOrderControlItem", "WorkOrderControlItem") + .WithMany() + .HasForeignKey("WorkOrderControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WorkOrder"); + + b.Navigation("WorkOrderControlItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.HasOne("MesApp.Core.Entities.DefectReason", "DefectReason") + .WithMany() + .HasForeignKey("DefectReasonId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany("Defects") + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DefectReason"); + + b.Navigation("ProductionRecord"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutputLot"); + + b.Navigation("PerformedBy"); + + b.Navigation("Shift"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany() + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("ProductionRecord"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId"); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId"); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId"); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId"); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("Tool"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("EquipmentCandidates") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Routing"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "StorageLocation") + .WithMany() + .HasForeignKey("StorageLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("StorageLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "JudgedBy") + .WithMany() + .HasForeignKey("JudgedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("JudgedBy"); + + b.Navigation("Lot"); + + b.Navigation("ShippingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", null) + .WithMany("Lines") + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.HasOne("MesApp.Core.Entities.Location", "TargetLocation") + .WithMany() + .HasForeignKey("TargetLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Stocktake", null) + .WithMany("Lines") + .HasForeignKey("StocktakeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "IssuedTo") + .WithMany() + .HasForeignKey("IssuedToUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IssuedTo"); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("ToLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Equipment"); + + b.Navigation("ReportedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.HasOne("MesApp.Core.Entities.SkillMaster", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "Parent") + .WithMany() + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "AssignedEquipment") + .WithMany() + .HasForeignKey("AssignedEquipmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "AssignedUser") + .WithMany() + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("WorkOrders") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Checklist", "RoutingChecklist") + .WithMany() + .HasForeignKey("RoutingChecklistId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedEquipment"); + + b.Navigation("AssignedUser"); + + b.Navigation("ManufacturingOrder"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("RoutingChecklist"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany("ControlItemSnapshots") + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "ChangedBy") + .WithMany() + .HasForeignKey("ChangedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChangedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Navigation("Items"); + + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Navigation("Records"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Navigation("Materials"); + + b.Navigation("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Navigation("Defects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Navigation("EquipmentCandidates"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Navigation("ControlItemSnapshots"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.cs b/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.cs new file mode 100644 index 0000000..ed57f29 --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260922224608_AddBomItemRoutingSequence.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Infrastructure.Migrations +{ + /// + public partial class AddBomItemRoutingSequence : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "BomItems", + type: "INTEGER", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials"); + + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "BomItems"); + } + } +} diff --git a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs index c47826f..2b78802 100644 --- a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs @@ -192,6 +192,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("QuantityPer") .HasColumnType("TEXT"); + b.Property("RoutingSequence") + .HasColumnType("INTEGER"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); @@ -1531,6 +1534,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("QuantityPer") .HasColumnType("TEXT"); + b.Property("RoutingSequence") + .HasColumnType("INTEGER"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.Designer.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.Designer.cs new file mode 100644 index 0000000..75dac58 --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.Designer.cs @@ -0,0 +1,4950 @@ +// +using System; +using MesApp.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MesApp.Migrations.PostgreSql.Migrations +{ + [DbContext(typeof(MesAppDbContext))] + [Migration("20260922224610_AddBomItemRoutingSequence")] + partial class AddBomItemRoutingSequence + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("ShiftId") + .HasColumnType("integer"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkCenterId"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Detail") + .HasColumnType("text"); + + b.Property("IpAddress") + .HasColumnType("text"); + + b.Property("RecordedOn") + .HasColumnType("date"); + + b.Property("TargetId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TargetType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("text"); + + b.Property("UserName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RecordedOn"); + + b.HasIndex("Timestamp"); + + b.HasIndex("UserId"); + + b.HasIndex("Category", "Action"); + + b.HasIndex("TargetType", "TargetId"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ChildProductId") + .HasColumnType("integer"); + + b.Property("IsAlternative") + .HasColumnType("boolean"); + + b.Property("MakeOrBuy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ParentProductId") + .HasColumnType("integer"); + + b.Property("QuantityPer") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("RoutingSequence") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ParentProductId", "ChildProductId") + .IsUnique(); + + b.ToTable("BomItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Checklists"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("integer"); + + b.Property("IsRequired") + .HasColumnType("boolean"); + + b.Property("Sequence") + .HasColumnType("integer"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.ToTable("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("integer"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("PerformedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ChecklistRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChecklistItemId") + .HasColumnType("integer"); + + b.Property("ChecklistRecordId") + .HasColumnType("integer"); + + b.Property("IsChecked") + .HasColumnType("boolean"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistItemId"); + + b.HasIndex("ChecklistRecordId"); + + b.ToTable("ChecklistResultItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TargetProcessId") + .HasColumnType("integer"); + + b.Property("TargetProductId") + .HasColumnType("integer"); + + b.Property("TargetValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("ControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.DefectReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("DefectReasons"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssetNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MaintenanceParts") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("MaintenanceThreshold") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("MaintenanceType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Site") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AssetNo") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Equipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("RecordedByUserId") + .HasColumnType("text"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("StopCause") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("EquipmentLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("QuantityPer") + .HasPrecision(18, 4) + .HasColumnType("numeric(18,4)"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("EquipmentId", "ProductId") + .IsUnique(); + + b.ToTable("EquipmentParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDevice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CalibratedOn") + .HasColumnType("date"); + + b.Property("CalibrationCycleDays") + .HasColumnType("integer"); + + b.Property("CalibrationDueOn") + .HasColumnType("date"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Location") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("SerialNo") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("InspectionDevices"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CalibratedOn") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectionDeviceId") + .HasColumnType("integer"); + + b.Property("NextDueOn") + .HasColumnType("date"); + + b.Property("PerformedByUserId") + .HasColumnType("text"); + + b.Property("Result") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("InspectionDeviceId", "CalibratedOn"); + + b.ToTable("InspectionDeviceCalibrations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SamplingCount") + .HasColumnType("integer"); + + b.Property("StandardValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("TargetProcessId") + .HasColumnType("integer"); + + b.Property("TargetProductId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("InspectionItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByUserId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("JudgedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("JudgedByUserId") + .HasColumnType("text"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OverallJudgment") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("RequestedByUserId") + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("TargetLotId") + .HasColumnType("integer"); + + b.Property("TargetWorkOrderId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("TargetLotId"); + + b.HasIndex("TargetWorkOrderId"); + + b.ToTable("InspectionOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("InspectionItemId") + .HasColumnType("integer"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ItemVersion") + .HasColumnType("integer"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("SamplingCount") + .HasColumnType("integer"); + + b.Property("StandardValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId", "InspectionItemId") + .IsUnique(); + + b.ToTable("InspectionOrderItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CorrectionNote") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("InspectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("InspectionDeviceId") + .HasColumnType("integer"); + + b.Property("InspectionItemId") + .HasColumnType("integer"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("Judgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("MeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("SampleNo") + .HasColumnType("integer"); + + b.Property("TextValue") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("InspectedByUserId"); + + b.HasIndex("InspectionDeviceId"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId"); + + b.ToTable("InspectionResults"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AfterJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AfterMeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("AfterTextValue") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("BeforeJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("BeforeMeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("BeforeTextValue") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CorrectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("InspectionResultId") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("InspectionResultId"); + + b.HasIndex("InspectionOrderId", "Id"); + + b.ToTable("InspectionResultCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("LocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId", "LocationId") + .IsUnique(); + + b.HasIndex("ProductId", "LocationId"); + + b.ToTable("InventoryStocks"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FromLocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("PerformedByUserId") + .HasColumnType("text"); + + b.Property("PickingOrderId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ShippingOrderId") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("ToLocationId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("Timestamp"); + + b.HasIndex("ToLocationId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("InventoryTransactions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AreaType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("ShelfNo") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresOn") + .HasColumnType("date"); + + b.Property("Grade") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("InitialQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("LotNumber") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.Property("ManufacturedOn") + .HasColumnType("date"); + + b.Property("OriginType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ParentLotId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("SourceWorkOrderId") + .HasColumnType("integer"); + + b.Property("StockStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("LotNumber") + .IsUnique(); + + b.HasIndex("ParentLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceWorkOrderId"); + + b.ToTable("Lots"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChildLotId") + .HasColumnType("integer"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ParentLotId") + .HasColumnType("integer"); + + b.Property("PerformedByUserId") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("RelationType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("ChildLotId"); + + b.HasIndex("ParentLotId"); + + b.ToTable("LotGenealogies"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("NonconformanceReportId") + .HasColumnType("integer"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("LotId", "Id"); + + b.ToTable("LotStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("MaintenancePlanId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ProcedureId") + .HasColumnType("integer"); + + b.Property("RequestType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ScheduledDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ToolId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("MaintenancePlanId"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ProcedureId"); + + b.HasIndex("Status"); + + b.HasIndex("ToolId"); + + b.ToTable("MaintenanceOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("CycleDays") + .HasColumnType("integer"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PlanYear") + .HasColumnType("integer"); + + b.Property("ScheduledDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId", "PlanYear"); + + b.ToTable("MaintenancePlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("TargetEquipmentId") + .HasColumnType("integer"); + + b.Property("TargetToolId") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.HasIndex("TargetEquipmentId"); + + b.HasIndex("TargetToolId"); + + b.ToTable("MaintenanceProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MaintenanceOrderId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PartsUsed") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Result") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MaintenanceOrderId"); + + b.HasIndex("PerformedByUserId"); + + b.ToTable("MaintenanceRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("MaintenanceRecordId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("numeric(18,4)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("MaintenanceRecordId"); + + b.HasIndex("ProductId"); + + b.ToTable("MaintenanceRecordParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByUserId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrderType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OutputLotId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("SourceOrderId") + .HasColumnType("integer"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("OutputLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceOrderId"); + + b.HasIndex("Status"); + + b.ToTable("ManufacturingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ChildProductId") + .HasColumnType("integer"); + + b.Property("IsAlternative") + .HasColumnType("boolean"); + + b.Property("ManufacturingOrderId") + .HasColumnType("integer"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("QuantityPer") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("RoutingSequence") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ManufacturingOrderId", "ChildProductId") + .IsUnique(); + + b.ToTable("ManufacturingOrderMaterials"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsSubstitute") + .HasColumnType("boolean"); + + b.Property("LocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("Method") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("RecordedByUserId") + .HasColumnType("text"); + + b.Property("SubstituteReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("MaterialConsumptions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ActionCompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ActionCompletedByUserId") + .HasColumnType("text"); + + b.Property("ActionInstructedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ActionInstructedByUserId") + .HasColumnType("text"); + + b.Property("ActionInstruction") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("ActionRecord") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByUserId") + .HasColumnType("text"); + + b.Property("CauseCategory") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CauseDetail") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("ReportNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ReportedByUserId") + .HasColumnType("text"); + + b.Property("ReworkOrderId") + .HasColumnType("integer"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ReportNo") + .IsUnique(); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("ReworkOrderId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("NonconformanceReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NumberSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LastValue") + .HasColumnType("integer"); + + b.Property("Prefix") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.HasKey("Id"); + + b.HasIndex("Prefix") + .IsUnique(); + + b.ToTable("NumberSequences"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("PickingOrderId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("PickingOrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("PickingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutedByUserId") + .HasColumnType("text"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ShippingOrderId") + .HasColumnType("integer"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ShippingOrderId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("PickingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProcessMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Processes", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultLocationId") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Specification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("StandardDefectRate") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("DefaultLocationId"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeviation") + .HasColumnType("boolean"); + + b.Property("Item") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NumericValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("RecordedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RecordedByUserId") + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("WorkOrderControlItemId") + .HasColumnType("integer"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionDataRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DefectReasonId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProductionRecordId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("DefectReasonId"); + + b.HasIndex("ProductionRecordId"); + + b.ToTable("ProductionDefects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("BusinessDate", "ProductId", "ProcessId", "WorkCenterId") + .IsUnique(); + + b.ToTable("ProductionPlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByUserId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("OutputLocationId") + .HasColumnType("integer"); + + b.Property("OutputLotId") + .HasColumnType("integer"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ShiftId") + .HasColumnType("integer"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OutputLotId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AfterDefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("AfterGoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("AfterReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("AfterScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("BeforeDefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("BeforeGoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("BeforeReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("BeforeScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("CorrectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("ProductionRecordId") + .HasColumnType("integer"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("ProductionRecordId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("ProductionRecordCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("RefreshTokens"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("integer"); + + b.Property("ControlItems") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("RequiredSkillId") + .HasColumnType("integer"); + + b.Property("Sequence") + .HasColumnType("integer"); + + b.Property("StandardSetupMinutes") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("StandardWorkMinutes") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ToolId") + .HasColumnType("integer"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.Property("WorkProcedureId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkProcedureId"); + + b.HasIndex("ProductId", "Sequence") + .IsUnique(); + + b.ToTable("Routings"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("RoutingId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("RoutingId", "EquipmentId") + .IsUnique(); + + b.ToTable("RoutingEquipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClosedByUserId") + .HasColumnType("text"); + + b.Property("ClosedOn") + .HasColumnType("date"); + + b.Property("CollectedByUserId") + .HasColumnType("text"); + + b.Property("CollectedOn") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectionOrderId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("numeric(18,4)"); + + b.Property("RetainUntil") + .HasColumnType("date"); + + b.Property("SampleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StorageLocationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SampleNo") + .IsUnique(); + + b.HasIndex("StorageLocationId"); + + b.HasIndex("Status", "RetainUntil"); + + b.ToTable("SampleStorages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AbnormalityNote") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("SetupRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Shift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("EndTime") + .HasColumnType("time without time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartTime") + .HasColumnType("time without time zone"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Shifts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByUserId") + .HasColumnType("text"); + + b.Property("JudgedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("JudgedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("JudgmentNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ShippingOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("JudgedByUserId"); + + b.HasIndex("JudgmentNo") + .IsUnique(); + + b.HasIndex("LotId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShipmentJudgments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ShippedQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ShippingOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShippingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PlannedDate") + .HasColumnType("date"); + + b.Property("ShippedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ShippedByUserId") + .HasColumnType("text"); + + b.Property("ShippingNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("ShippingNo") + .IsUnique(); + + b.ToTable("ShippingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SkillMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RequiresExpiry") + .HasColumnType("boolean"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Skills", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("FinalizedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FinalizedByUserId") + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("StocktakeNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TargetLocationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("StocktakeNo") + .IsUnique(); + + b.HasIndex("TargetLocationId"); + + b.ToTable("Stocktakes"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountedQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("IsAdjusted") + .HasColumnType("boolean"); + + b.Property("LocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("StocktakeId") + .HasColumnType("integer"); + + b.Property("TheoreticalQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("StocktakeId"); + + b.ToTable("StocktakeLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Tool", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LifeResetAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LifeThresholdCount") + .HasColumnType("integer"); + + b.Property("LifeThresholdHours") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ToolType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Tools"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllocatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("AllocatedByUserId") + .HasColumnType("text"); + + b.Property("IssuedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IssuedToUserId") + .HasColumnType("text"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ReturnedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReturnedByUserId") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("ToolId") + .HasColumnType("integer"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IssuedToUserId"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("ToolId", "Status"); + + b.ToTable("ToolIssues"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("RecordedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RecordedByUserId") + .HasColumnType("text"); + + b.Property("ToolId") + .HasColumnType("integer"); + + b.Property("UsageCount") + .HasColumnType("integer"); + + b.Property("UsageHours") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ToolUsages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutedByUserId") + .HasColumnType("text"); + + b.Property("FromLocationId") + .HasColumnType("integer"); + + b.Property("LotId") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ToLocationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("Status"); + + b.HasIndex("ToLocationId"); + + b.ToTable("TransferOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EquipmentId") + .HasColumnType("integer"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReportedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ResponseHistory") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("TroubleReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AcquiredOn") + .HasColumnType("date"); + + b.Property("ExpiresOn") + .HasColumnType("date"); + + b.Property("SkillId") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.HasIndex("UserId", "SkillId") + .IsUnique(); + + b.ToTable("UserSkills"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ParentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("WorkCenters"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedEquipmentId") + .HasColumnType("integer"); + + b.Property("AssignedUserId") + .HasColumnType("text"); + + b.Property("ControlItems") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DispatchOrder") + .HasColumnType("integer"); + + b.Property("ManufacturingOrderId") + .HasColumnType("integer"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("integer"); + + b.Property("RequiredSkillId") + .HasColumnType("integer"); + + b.Property("RoutingChecklistId") + .HasColumnType("integer"); + + b.Property("RoutingSequence") + .HasColumnType("integer"); + + b.Property("StandardSetupMinutes") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("StandardWorkMinutes") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkCenterId") + .HasColumnType("integer"); + + b.Property("WorkOrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("WorkProcedureId") + .HasColumnType("integer"); + + b.Property("WorkProcedureVersion") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AssignedEquipmentId"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("ManufacturingOrderId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("RoutingChecklistId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkOrderNo") + .IsUnique(); + + b.HasIndex("WorkProcedureId"); + + b.ToTable("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("integer"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ItemVersion") + .HasColumnType("integer"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("TargetValue") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("WorkOrderControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChangedByUserId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("WorkOrderStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reference") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.ToTable("WorkProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IndirectCategory") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkOrderId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("UserId", "StartedAt"); + + b.ToTable("WorkTimeRecords"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Shift"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "ParentProduct") + .WithMany() + .HasForeignKey("ParentProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ParentProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", null) + .WithMany("Items") + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.HasOne("MesApp.Core.Entities.ChecklistItem", "ChecklistItem") + .WithMany() + .HasForeignKey("ChecklistItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ChecklistRecord", null) + .WithMany("Results") + .HasForeignKey("ChecklistRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Equipment"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany("Parts") + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionDevice"); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "RequestedBy") + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "TargetLot") + .WithMany() + .HasForeignKey("TargetLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "TargetWorkOrder") + .WithMany() + .HasForeignKey("TargetWorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RequestedBy"); + + b.Navigation("TargetLot"); + + b.Navigation("TargetWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Items") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "InspectedBy") + .WithMany() + .HasForeignKey("InspectedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Results") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectedBy"); + + b.Navigation("InspectionDevice"); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.InspectionResult", "InspectionResult") + .WithMany() + .HasForeignKey("InspectionResultId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("InspectionResult"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("ToLocation"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "SourceWorkOrder") + .WithMany() + .HasForeignKey("SourceWorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("ParentLot"); + + b.Navigation("Product"); + + b.Navigation("SourceWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ChildLot") + .WithMany() + .HasForeignKey("ChildLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ChildLot"); + + b.Navigation("ParentLot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.MaintenancePlan", "MaintenancePlan") + .WithMany() + .HasForeignKey("MaintenancePlanId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.MaintenanceProcedure", "Procedure") + .WithMany() + .HasForeignKey("ProcedureId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CreatedBy"); + + b.Navigation("Equipment"); + + b.Navigation("MaintenancePlan"); + + b.Navigation("Procedure"); + + b.Navigation("Tool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") + .WithMany() + .HasForeignKey("TargetEquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "TargetTool") + .WithMany() + .HasForeignKey("TargetToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetEquipment"); + + b.Navigation("TargetTool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.HasOne("MesApp.Core.Entities.MaintenanceOrder", null) + .WithMany("Records") + .HasForeignKey("MaintenanceOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.MaintenanceRecord", null) + .WithMany("Parts") + .HasForeignKey("MaintenanceRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "SourceOrder") + .WithMany() + .HasForeignKey("SourceOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("OutputLot"); + + b.Navigation("Product"); + + b.Navigation("SourceOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("Materials") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ManufacturingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ReworkOrder") + .WithMany() + .HasForeignKey("ReworkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("ReportedBy"); + + b.Navigation("ReworkOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.PickingOrder", null) + .WithMany("Lines") + .HasForeignKey("PickingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("ShippingOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.HasOne("MesApp.Core.Entities.Location", "DefaultLocation") + .WithMany() + .HasForeignKey("DefaultLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.HasOne("MesApp.Core.Entities.WorkOrderControlItem", "WorkOrderControlItem") + .WithMany() + .HasForeignKey("WorkOrderControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WorkOrder"); + + b.Navigation("WorkOrderControlItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.HasOne("MesApp.Core.Entities.DefectReason", "DefectReason") + .WithMany() + .HasForeignKey("DefectReasonId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany("Defects") + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DefectReason"); + + b.Navigation("ProductionRecord"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutputLot"); + + b.Navigation("PerformedBy"); + + b.Navigation("Shift"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany() + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("ProductionRecord"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId"); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId"); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId"); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId"); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("Tool"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("EquipmentCandidates") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Routing"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "StorageLocation") + .WithMany() + .HasForeignKey("StorageLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("StorageLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "JudgedBy") + .WithMany() + .HasForeignKey("JudgedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("JudgedBy"); + + b.Navigation("Lot"); + + b.Navigation("ShippingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", null) + .WithMany("Lines") + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.HasOne("MesApp.Core.Entities.Location", "TargetLocation") + .WithMany() + .HasForeignKey("TargetLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Stocktake", null) + .WithMany("Lines") + .HasForeignKey("StocktakeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "IssuedTo") + .WithMany() + .HasForeignKey("IssuedToUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IssuedTo"); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("ToLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Equipment"); + + b.Navigation("ReportedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.HasOne("MesApp.Core.Entities.SkillMaster", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "Parent") + .WithMany() + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "AssignedEquipment") + .WithMany() + .HasForeignKey("AssignedEquipmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.AppUser", "AssignedUser") + .WithMany() + .HasForeignKey("AssignedUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("WorkOrders") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Checklist", "RoutingChecklist") + .WithMany() + .HasForeignKey("RoutingChecklistId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedEquipment"); + + b.Navigation("AssignedUser"); + + b.Navigation("ManufacturingOrder"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("RoutingChecklist"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany("ControlItemSnapshots") + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "ChangedBy") + .WithMany() + .HasForeignKey("ChangedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChangedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Navigation("Items"); + + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Navigation("Records"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Navigation("Materials"); + + b.Navigation("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Navigation("Defects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Navigation("EquipmentCandidates"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Navigation("ControlItemSnapshots"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.cs new file mode 100644 index 0000000..56c6847 --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260922224610_AddBomItemRoutingSequence.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.PostgreSql.Migrations +{ + /// + public partial class AddBomItemRoutingSequence : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "BomItems", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials"); + + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "BomItems"); + } + } +} diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs index 3bc815a..6d6b506 100644 --- a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs @@ -202,6 +202,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasPrecision(18, 6) .HasColumnType("numeric(18,6)"); + b.Property("RoutingSequence") + .HasColumnType("integer"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); @@ -1619,6 +1622,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasPrecision(18, 6) .HasColumnType("numeric(18,6)"); + b.Property("RoutingSequence") + .HasColumnType("integer"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); diff --git a/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.Designer.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.Designer.cs new file mode 100644 index 0000000..4d32381 --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.Designer.cs @@ -0,0 +1,4929 @@ +// +using System; +using MesApp.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MesApp.Migrations.SqlServer.Migrations +{ + [DbContext(typeof(MesAppDbContext))] + [Migration("20260922224613_AddBomItemRoutingSequence")] + partial class AddBomItemRoutingSequence + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("MustChangePassword") + .HasColumnType("bit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkCenterId"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Detail") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("RecordedOn") + .HasColumnType("date"); + + b.Property("TargetId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TargetType") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("UserName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RecordedOn"); + + b.HasIndex("Timestamp"); + + b.HasIndex("UserId"); + + b.HasIndex("Category", "Action"); + + b.HasIndex("TargetType", "TargetId"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ChildProductId") + .HasColumnType("int"); + + b.Property("IsAlternative") + .HasColumnType("bit"); + + b.Property("MakeOrBuy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ParentProductId") + .HasColumnType("int"); + + b.Property("QuantityPer") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RoutingSequence") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ParentProductId", "ChildProductId") + .IsUnique(); + + b.ToTable("BomItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Checklists"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("int"); + + b.Property("IsRequired") + .HasColumnType("bit"); + + b.Property("Sequence") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.ToTable("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("int"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("PerformedAt") + .HasColumnType("datetimeoffset"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ChecklistRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChecklistItemId") + .HasColumnType("int"); + + b.Property("ChecklistRecordId") + .HasColumnType("int"); + + b.Property("IsChecked") + .HasColumnType("bit"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistItemId"); + + b.HasIndex("ChecklistRecordId"); + + b.ToTable("ChecklistResultItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("TargetProcessId") + .HasColumnType("int"); + + b.Property("TargetProductId") + .HasColumnType("int"); + + b.Property("TargetValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("ControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.DefectReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("DefectReasons"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AssetNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("MaintenanceParts") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("MaintenanceThreshold") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("MaintenanceType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Site") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AssetNo") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Equipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("RecordedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("StopCause") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("EquipmentLogs"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("QuantityPer") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("EquipmentId", "ProductId") + .IsUnique(); + + b.ToTable("EquipmentParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDevice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalibratedOn") + .HasColumnType("date"); + + b.Property("CalibrationCycleDays") + .HasColumnType("int"); + + b.Property("CalibrationDueOn") + .HasColumnType("date"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Location") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("SerialNo") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("InspectionDevices"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalibratedOn") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InspectionDeviceId") + .HasColumnType("int"); + + b.Property("NextDueOn") + .HasColumnType("date"); + + b.Property("PerformedByUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("Result") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("InspectionDeviceId", "CalibratedOn"); + + b.ToTable("InspectionDeviceCalibrations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("SamplingCount") + .HasColumnType("int"); + + b.Property("StandardValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("TargetProcessId") + .HasColumnType("int"); + + b.Property("TargetProductId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("TargetProcessId"); + + b.HasIndex("TargetProductId"); + + b.ToTable("InspectionItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ApprovedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("JudgedAt") + .HasColumnType("datetimeoffset"); + + b.Property("JudgedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("OverallJudgment") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("RequestedByUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("TargetLotId") + .HasColumnType("int"); + + b.Property("TargetWorkOrderId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("TargetLotId"); + + b.HasIndex("TargetWorkOrderId"); + + b.ToTable("InspectionOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("InspectionItemId") + .HasColumnType("int"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ItemVersion") + .HasColumnType("int"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Method") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("SamplingCount") + .HasColumnType("int"); + + b.Property("StandardValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId", "InspectionItemId") + .IsUnique(); + + b.ToTable("InspectionOrderItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CorrectionNote") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("InspectedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InspectedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("InspectionDeviceId") + .HasColumnType("int"); + + b.Property("InspectionItemId") + .HasColumnType("int"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("Judgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("MeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("SampleNo") + .HasColumnType("int"); + + b.Property("TextValue") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("InspectedByUserId"); + + b.HasIndex("InspectionDeviceId"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("InspectionOrderId"); + + b.ToTable("InspectionResults"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AfterJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("AfterMeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("AfterTextValue") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("BeforeJudgment") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("BeforeMeasuredValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("BeforeTextValue") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CorrectedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("InspectionResultId") + .HasColumnType("int"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("InspectionResultId"); + + b.HasIndex("InspectionOrderId", "Id"); + + b.ToTable("InspectionResultCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId", "LocationId") + .IsUnique(); + + b.HasIndex("ProductId", "LocationId"); + + b.ToTable("InventoryStocks"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FromLocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PerformedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("PickingOrderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ShippingOrderId") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.Property("ToLocationId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("Timestamp"); + + b.HasIndex("ToLocationId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("InventoryTransactions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AreaType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ShelfNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("WorkCenterId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ExpiresOn") + .HasColumnType("date"); + + b.Property("Grade") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("InitialQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("LotNumber") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("ManufacturedOn") + .HasColumnType("date"); + + b.Property("OriginType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ParentLotId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("SourceWorkOrderId") + .HasColumnType("int"); + + b.Property("StockStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("LotNumber") + .IsUnique(); + + b.HasIndex("ParentLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceWorkOrderId"); + + b.ToTable("Lots"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChildLotId") + .HasColumnType("int"); + + b.Property("OccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("ParentLotId") + .HasColumnType("int"); + + b.Property("PerformedByUserId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RelationType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("ChildLotId"); + + b.HasIndex("ParentLotId"); + + b.ToTable("LotGenealogies"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChangedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("NonconformanceReportId") + .HasColumnType("int"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("LotId", "Id"); + + b.ToTable("LotStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("MaintenancePlanId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ProcedureId") + .HasColumnType("int"); + + b.Property("RequestType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ScheduledDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ToolId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("MaintenancePlanId"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ProcedureId"); + + b.HasIndex("Status"); + + b.HasIndex("ToolId"); + + b.ToTable("MaintenanceOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CycleDays") + .HasColumnType("int"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("PlanYear") + .HasColumnType("int"); + + b.Property("ScheduledDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId", "PlanYear"); + + b.ToTable("MaintenancePlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("TargetEquipmentId") + .HasColumnType("int"); + + b.Property("TargetToolId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.HasIndex("TargetEquipmentId"); + + b.HasIndex("TargetToolId"); + + b.ToTable("MaintenanceProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MaintenanceOrderId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("PartsUsed") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Result") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("MaintenanceOrderId"); + + b.HasIndex("PerformedByUserId"); + + b.ToTable("MaintenanceRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("MaintenanceRecordId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("MaintenanceRecordId"); + + b.HasIndex("ProductId"); + + b.ToTable("MaintenanceRecordParts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ApprovedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("OrderType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("OutputLotId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("SourceOrderId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("OutputLotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SourceOrderId"); + + b.HasIndex("Status"); + + b.ToTable("ManufacturingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AlternativeGroup") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ChildProductId") + .HasColumnType("int"); + + b.Property("IsAlternative") + .HasColumnType("bit"); + + b.Property("ManufacturingOrderId") + .HasColumnType("int"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("QuantityPer") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RoutingSequence") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChildProductId"); + + b.HasIndex("ManufacturingOrderId", "ChildProductId") + .IsUnique(); + + b.ToTable("ManufacturingOrderMaterials"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ConsumedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsSubstitute") + .HasColumnType("bit"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("Method") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RecordedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstituteReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("MaterialConsumptions"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ActionCompletedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ActionCompletedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ActionInstructedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ActionInstructedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ActionInstruction") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ActionRecord") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ApprovedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ApprovedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CauseCategory") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CauseDetail") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("ReportNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ReportedByUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("ReworkOrderId") + .HasColumnType("int"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ReportNo") + .IsUnique(); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("ReworkOrderId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("NonconformanceReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NumberSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("LastValue") + .HasColumnType("int"); + + b.Property("Prefix") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.HasKey("Id"); + + b.HasIndex("Prefix") + .IsUnique(); + + b.ToTable("NumberSequences"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("PickingOrderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("PickingOrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("PickingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ExecutedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ShippingOrderId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderNo") + .IsUnique(); + + b.HasIndex("ShippingOrderId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("PickingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProcessMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Processes", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("DefaultLocationId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Specification") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("StandardDefectRate") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("DefaultLocationId"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsDeviation") + .HasColumnType("bit"); + + b.Property("Item") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NumericValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RecordedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RecordedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("WorkOrderControlItemId") + .HasColumnType("int"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionDataRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DefectReasonId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProductionRecordId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("DefectReasonId"); + + b.HasIndex("ProductionRecordId"); + + b.ToTable("ProductionDefects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BusinessDate") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("BusinessDate", "ProductId", "ProcessId", "WorkCenterId") + .IsUnique() + .HasFilter("[WorkCenterId] IS NOT NULL"); + + b.ToTable("ProductionPlans"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ApprovedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("DefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("GoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("OutputLocationId") + .HasColumnType("int"); + + b.Property("OutputLotId") + .HasColumnType("int"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OutputLotId"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("ShiftId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ProductionRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AfterDefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("AfterGoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("AfterReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("AfterScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("BeforeDefectQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("BeforeGoodQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("BeforeReworkQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("BeforeScrapQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("CorrectedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CorrectedByUserId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ProductionRecordId") + .HasColumnType("int"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CorrectedByUserId"); + + b.HasIndex("ProductionRecordId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("ProductionRecordCorrections"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("RefreshTokens"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChecklistId") + .HasColumnType("int"); + + b.Property("ControlItems") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("RequiredSkillId") + .HasColumnType("int"); + + b.Property("Sequence") + .HasColumnType("int"); + + b.Property("StandardSetupMinutes") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("StandardWorkMinutes") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ToolId") + .HasColumnType("int"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.Property("WorkProcedureId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChecklistId"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkProcedureId"); + + b.HasIndex("ProductId", "Sequence") + .IsUnique(); + + b.ToTable("Routings"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("RoutingId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("RoutingId", "EquipmentId") + .IsUnique(); + + b.ToTable("RoutingEquipments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClosedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ClosedOn") + .HasColumnType("date"); + + b.Property("CollectedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("CollectedOn") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InspectionOrderId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("RetainUntil") + .HasColumnType("date"); + + b.Property("SampleNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StorageLocationId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InspectionOrderId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SampleNo") + .IsUnique(); + + b.HasIndex("StorageLocationId"); + + b.HasIndex("Status", "RetainUntil"); + + b.ToTable("SampleStorages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AbnormalityNote") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("PerformedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PerformedByUserId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("SetupRecords"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Shift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("EndTime") + .HasColumnType("time"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("StartTime") + .HasColumnType("time"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Shifts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ApprovedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ApprovedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("JudgedAt") + .HasColumnType("datetimeoffset"); + + b.Property("JudgedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("JudgmentNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ShippingOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("JudgedByUserId"); + + b.HasIndex("JudgmentNo") + .IsUnique(); + + b.HasIndex("LotId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShipmentJudgments"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ShippedQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ShippingOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("ShippingOrderId"); + + b.ToTable("ShippingLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PlannedDate") + .HasColumnType("date"); + + b.Property("ShippedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ShippedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ShippingNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("ShippingNo") + .IsUnique(); + + b.ToTable("ShippingOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SkillMaster", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RequiresExpiry") + .HasColumnType("bit"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Skills", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("FinalizedAt") + .HasColumnType("datetimeoffset"); + + b.Property("FinalizedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("StocktakeNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TargetLocationId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StocktakeNo") + .IsUnique(); + + b.HasIndex("TargetLocationId"); + + b.ToTable("Stocktakes"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CountedQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("IsAdjusted") + .HasColumnType("bit"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("StocktakeId") + .HasColumnType("int"); + + b.Property("TheoreticalQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("LocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("ProductId"); + + b.HasIndex("StocktakeId"); + + b.ToTable("StocktakeLine"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Tool", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LifeResetAt") + .HasColumnType("datetimeoffset"); + + b.Property("LifeThresholdCount") + .HasColumnType("int"); + + b.Property("LifeThresholdHours") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ToolType") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Tools"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllocatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("AllocatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("IssuedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IssuedToUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ReturnedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ReturnedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("ToolId") + .HasColumnType("int"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IssuedToUserId"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("ToolId", "Status"); + + b.ToTable("ToolIssues"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("RecordedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RecordedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ToolId") + .HasColumnType("int"); + + b.Property("UsageCount") + .HasColumnType("int"); + + b.Property("UsageHours") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ToolId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("ToolUsages"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ExecutedByUserId") + .HasColumnType("nvarchar(max)"); + + b.Property("FromLocationId") + .HasColumnType("int"); + + b.Property("LotId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ToLocationId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FromLocationId"); + + b.HasIndex("LotId"); + + b.HasIndex("Status"); + + b.HasIndex("ToLocationId"); + + b.ToTable("TransferOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("OccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("ReportedByUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ResponseHistory") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("EquipmentId"); + + b.HasIndex("ReportedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("TroubleReports"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AcquiredOn") + .HasColumnType("date"); + + b.Property("ExpiresOn") + .HasColumnType("date"); + + b.Property("SkillId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("SkillId"); + + b.HasIndex("UserId", "SkillId") + .IsUnique(); + + b.ToTable("UserSkills"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ParentId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("WorkCenters"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AssignedEquipmentId") + .HasColumnType("int"); + + b.Property("AssignedUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("ControlItems") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("DispatchOrder") + .HasColumnType("int"); + + b.Property("ManufacturingOrderId") + .HasColumnType("int"); + + b.Property("PlannedQuantity") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("RequiredSkillId") + .HasColumnType("int"); + + b.Property("RoutingChecklistId") + .HasColumnType("int"); + + b.Property("RoutingSequence") + .HasColumnType("int"); + + b.Property("StandardSetupMinutes") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("StandardWorkMinutes") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkCenterId") + .HasColumnType("int"); + + b.Property("WorkOrderNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("WorkProcedureId") + .HasColumnType("int"); + + b.Property("WorkProcedureVersion") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AssignedEquipmentId"); + + b.HasIndex("AssignedUserId"); + + b.HasIndex("ManufacturingOrderId"); + + b.HasIndex("ProcessId"); + + b.HasIndex("ProductId"); + + b.HasIndex("RequiredSkillId"); + + b.HasIndex("RoutingChecklistId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkCenterId"); + + b.HasIndex("WorkOrderNo") + .IsUnique(); + + b.HasIndex("WorkProcedureId"); + + b.ToTable("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("int"); + + b.Property("ItemCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ItemName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ItemVersion") + .HasColumnType("int"); + + b.Property("LowerLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("TargetValue") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Unit") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpperLimit") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("WorkOrderId"); + + b.ToTable("WorkOrderControlItems"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChangedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ChangedByUserId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("FromStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("ToStatus") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChangedByUserId"); + + b.HasIndex("WorkOrderId", "Id"); + + b.ToTable("WorkOrderStatusHistories"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ProcedureNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Reference") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Steps") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProcedureNo") + .IsUnique(); + + b.ToTable("WorkProcedures"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IndirectCategory") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("WorkOrderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WorkOrderId"); + + b.HasIndex("UserId", "StartedAt"); + + b.ToTable("WorkTimeRecords"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("MesApp.Core.Entities.AppUser", b => + { + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Shift"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.BomItem", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "ParentProduct") + .WithMany() + .HasForeignKey("ParentProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ParentProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistItem", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", null) + .WithMany("Items") + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistResultItem", b => + { + b.HasOne("MesApp.Core.Entities.ChecklistItem", "ChecklistItem") + .WithMany() + .HasForeignKey("ChecklistItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ChecklistRecord", null) + .WithMany("Results") + .HasForeignKey("ChecklistRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChecklistItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentLog", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Equipment"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.EquipmentPart", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany("Parts") + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionDeviceCalibration", b => + { + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionDevice"); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("TargetProcess"); + + b.Navigation("TargetProduct"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "RequestedBy") + .WithMany() + .HasForeignKey("RequestedByUserId"); + + b.HasOne("MesApp.Core.Entities.Lot", "TargetLot") + .WithMany() + .HasForeignKey("TargetLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "TargetWorkOrder") + .WithMany() + .HasForeignKey("TargetWorkOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RequestedBy"); + + b.Navigation("TargetLot"); + + b.Navigation("TargetWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrderItem", b => + { + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Items") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "InspectedBy") + .WithMany() + .HasForeignKey("InspectedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionDevice", "InspectionDevice") + .WithMany() + .HasForeignKey("InspectionDeviceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.InspectionItem", "InspectionItem") + .WithMany() + .HasForeignKey("InspectionItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.InspectionOrder", null) + .WithMany("Results") + .HasForeignKey("InspectionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectedBy"); + + b.Navigation("InspectionDevice"); + + b.Navigation("InspectionItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionResultCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId"); + + b.HasOne("MesApp.Core.Entities.InspectionResult", "InspectionResult") + .WithMany() + .HasForeignKey("InspectionResultId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("InspectionResult"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryStock", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InventoryTransaction", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("ToLocation"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Location", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Lot", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "SourceWorkOrder") + .WithMany() + .HasForeignKey("SourceWorkOrderId"); + + b.Navigation("ParentLot"); + + b.Navigation("Product"); + + b.Navigation("SourceWorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotGenealogy", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "ChildLot") + .WithMany() + .HasForeignKey("ChildLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "ParentLot") + .WithMany() + .HasForeignKey("ParentLotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ChildLot"); + + b.Navigation("ParentLot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.LotStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lot"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedByUserId"); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.MaintenancePlan", "MaintenancePlan") + .WithMany() + .HasForeignKey("MaintenancePlanId"); + + b.HasOne("MesApp.Core.Entities.MaintenanceProcedure", "Procedure") + .WithMany() + .HasForeignKey("ProcedureId"); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CreatedBy"); + + b.Navigation("Equipment"); + + b.Navigation("MaintenancePlan"); + + b.Navigation("Procedure"); + + b.Navigation("Tool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenancePlan", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") + .WithMany() + .HasForeignKey("TargetEquipmentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "TargetTool") + .WithMany() + .HasForeignKey("TargetToolId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetEquipment"); + + b.Navigation("TargetTool"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.HasOne("MesApp.Core.Entities.MaintenanceOrder", null) + .WithMany("Records") + .HasForeignKey("MaintenanceOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PerformedBy"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecordPart", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.MaintenanceRecord", null) + .WithMany("Parts") + .HasForeignKey("MaintenanceRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId"); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "SourceOrder") + .WithMany() + .HasForeignKey("SourceOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("OutputLot"); + + b.Navigation("Product"); + + b.Navigation("SourceOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrderMaterial", b => + { + b.HasOne("MesApp.Core.Entities.Product", "ChildProduct") + .WithMany() + .HasForeignKey("ChildProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("Materials") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildProduct"); + + b.Navigation("ManufacturingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaterialConsumption", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.NonconformanceReport", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId"); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId"); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ReworkOrder") + .WithMany() + .HasForeignKey("ReworkOrderId"); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("ReportedBy"); + + b.Navigation("ReworkOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.PickingOrder", null) + .WithMany("Lines") + .HasForeignKey("PickingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId"); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("ShippingOrder"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Product", b => + { + b.HasOne("MesApp.Core.Entities.Location", "DefaultLocation") + .WithMany() + .HasForeignKey("DefaultLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DefaultLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDataRecord", b => + { + b.HasOne("MesApp.Core.Entities.WorkOrderControlItem", "WorkOrderControlItem") + .WithMany() + .HasForeignKey("WorkOrderControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WorkOrder"); + + b.Navigation("WorkOrderControlItem"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionDefect", b => + { + b.HasOne("MesApp.Core.Entities.DefectReason", "DefectReason") + .WithMany() + .HasForeignKey("DefectReasonId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany("Defects") + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DefectReason"); + + b.Navigation("ProductionRecord"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionPlan", b => + { + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.HasOne("MesApp.Core.Entities.Lot", "OutputLot") + .WithMany() + .HasForeignKey("OutputLotId"); + + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutputLot"); + + b.Navigation("PerformedBy"); + + b.Navigation("Shift"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecordCorrection", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "CorrectedBy") + .WithMany() + .HasForeignKey("CorrectedByUserId"); + + b.HasOne("MesApp.Core.Entities.ProductionRecord", "ProductionRecord") + .WithMany() + .HasForeignKey("ProductionRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CorrectedBy"); + + b.Navigation("ProductionRecord"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RefreshToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.HasOne("MesApp.Core.Entities.Checklist", "Checklist") + .WithMany() + .HasForeignKey("ChecklistId"); + + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId"); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId"); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId"); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Checklist"); + + b.Navigation("Equipment"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("Tool"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.RoutingEquipment", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("EquipmentCandidates") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Equipment"); + + b.Navigation("Routing"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SampleStorage", b => + { + b.HasOne("MesApp.Core.Entities.InspectionOrder", "InspectionOrder") + .WithMany() + .HasForeignKey("InspectionOrderId"); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "StorageLocation") + .WithMany() + .HasForeignKey("StorageLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("InspectionOrder"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + + b.Navigation("StorageLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.SetupRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PerformedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShipmentJudgment", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "JudgedBy") + .WithMany() + .HasForeignKey("JudgedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", "ShippingOrder") + .WithMany() + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("JudgedBy"); + + b.Navigation("Lot"); + + b.Navigation("ShippingOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingLine", b => + { + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ShippingOrder", null) + .WithMany("Lines") + .HasForeignKey("ShippingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.HasOne("MesApp.Core.Entities.Location", "TargetLocation") + .WithMany() + .HasForeignKey("TargetLocationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("TargetLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.StocktakeLine", b => + { + b.HasOne("MesApp.Core.Entities.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Stocktake", null) + .WithMany("Lines") + .HasForeignKey("StocktakeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + + b.Navigation("Lot"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolIssue", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "IssuedTo") + .WithMany() + .HasForeignKey("IssuedToUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IssuedTo"); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ToolUsage", b => + { + b.HasOne("MesApp.Core.Entities.Tool", "Tool") + .WithMany() + .HasForeignKey("ToolId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("Tool"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TransferOrder", b => + { + b.HasOne("MesApp.Core.Entities.Location", "FromLocation") + .WithMany() + .HasForeignKey("FromLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Lot", "Lot") + .WithMany() + .HasForeignKey("LotId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Location", "ToLocation") + .WithMany() + .HasForeignKey("ToLocationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromLocation"); + + b.Navigation("Lot"); + + b.Navigation("ToLocation"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.TroubleReport", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId"); + + b.HasOne("MesApp.Core.Entities.AppUser", "ReportedBy") + .WithMany() + .HasForeignKey("ReportedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("Equipment"); + + b.Navigation("ReportedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.UserSkill", b => + { + b.HasOne("MesApp.Core.Entities.SkillMaster", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkCenter", b => + { + b.HasOne("MesApp.Core.Entities.WorkCenter", "Parent") + .WithMany() + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.HasOne("MesApp.Core.Entities.Equipment", "AssignedEquipment") + .WithMany() + .HasForeignKey("AssignedEquipmentId"); + + b.HasOne("MesApp.Core.Entities.AppUser", "AssignedUser") + .WithMany() + .HasForeignKey("AssignedUserId"); + + b.HasOne("MesApp.Core.Entities.ManufacturingOrder", "ManufacturingOrder") + .WithMany("WorkOrders") + .HasForeignKey("ManufacturingOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "Process") + .WithMany() + .HasForeignKey("ProcessId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.Checklist", "RoutingChecklist") + .WithMany() + .HasForeignKey("RoutingChecklistId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkProcedure", "WorkProcedure") + .WithMany() + .HasForeignKey("WorkProcedureId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AssignedEquipment"); + + b.Navigation("AssignedUser"); + + b.Navigation("ManufacturingOrder"); + + b.Navigation("Process"); + + b.Navigation("Product"); + + b.Navigation("RequiredSkill"); + + b.Navigation("RoutingChecklist"); + + b.Navigation("WorkCenter"); + + b.Navigation("WorkProcedure"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany("ControlItemSnapshots") + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrderStatusHistory", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "ChangedBy") + .WithMany() + .HasForeignKey("ChangedByUserId"); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChangedBy"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkTimeRecord", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.WorkOrder", "WorkOrder") + .WithMany() + .HasForeignKey("WorkOrderId"); + + b.Navigation("User"); + + b.Navigation("WorkOrder"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("MesApp.Core.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Checklist", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ChecklistRecord", b => + { + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Equipment", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.InspectionOrder", b => + { + b.Navigation("Items"); + + b.Navigation("Results"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceOrder", b => + { + b.Navigation("Records"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.MaintenanceRecord", b => + { + b.Navigation("Parts"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ManufacturingOrder", b => + { + b.Navigation("Materials"); + + b.Navigation("WorkOrders"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.PickingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ProductionRecord", b => + { + b.Navigation("Defects"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Routing", b => + { + b.Navigation("EquipmentCandidates"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.ShippingOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.Stocktake", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("MesApp.Core.Entities.WorkOrder", b => + { + b.Navigation("ControlItemSnapshots"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.cs new file mode 100644 index 0000000..0dd8cd4 --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260922224613_AddBomItemRoutingSequence.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.SqlServer.Migrations +{ + /// + public partial class AddBomItemRoutingSequence : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "RoutingSequence", + table: "BomItems", + type: "int", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "ManufacturingOrderMaterials"); + + migrationBuilder.DropColumn( + name: "RoutingSequence", + table: "BomItems"); + } + } +} diff --git a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs index 1230726..d589440 100644 --- a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs @@ -203,6 +203,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasPrecision(18, 6) .HasColumnType("decimal(18,6)"); + b.Property("RoutingSequence") + .HasColumnType("int"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); @@ -1620,6 +1623,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasPrecision(18, 6) .HasColumnType("decimal(18,6)"); + b.Property("RoutingSequence") + .HasColumnType("int"); + b.HasKey("Id"); b.HasIndex("ChildProductId"); diff --git a/tests/MesApp.Api.Tests/AuditLogTests.cs b/tests/MesApp.Api.Tests/AuditLogTests.cs index 33a4466..826a141 100644 --- a/tests/MesApp.Api.Tests/AuditLogTests.cs +++ b/tests/MesApp.Api.Tests/AuditLogTests.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using System.Net; using System.Net.Http.Json; +using System.Text.Json; using MesApp.Core.Constants; using MesApp.Core.Contracts.Audit; using MesApp.Core.Contracts.Common; @@ -157,6 +158,56 @@ await admin.PutAsJsonAsync($"/api/stocktakes/{stocktake.Id}/counts", Assert.Contains("90", logs.Items[0].Detail); } + [Fact] + public async Task MBOMの改訂は変更前後の明細が監査ログに残る() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); // FG-01 の MBOM:RM-01 × 2 + var rm2 = await MasterTests.CreateProductAsync(admin, "RM-02", "梱包材", ProductType.Material); + + // 設計変更(A-40-10-05)は一括置換なので、件数だけでは何を変えたか追えない + (await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}/bom", new List + { + new(ctx.MaterialId, 3m, MakeOrBuy.InHouse, null), + new(rm2.Id, 1m, MakeOrBuy.InHouse, null, RoutingSequence: 2), + })).EnsureSuccessStatusCode(); + var api = await LatestBomLogAsync(); + using (var detail = JsonDocument.Parse(api.Detail!)) + { + var before = detail.RootElement.GetProperty("before"); + var after = detail.RootElement.GetProperty("after"); + Assert.Equal(1, before.GetArrayLength()); + Assert.Equal("RM-01", before[0].GetProperty("child").GetString()); + Assert.Equal(2m, before[0].GetProperty("quantityPer").GetDecimal()); + Assert.Equal(["RM-01", "RM-02"], after.EnumerateArray().Select(l => l.GetProperty("child").GetString())); + Assert.Equal(3m, after[0].GetProperty("quantityPer").GetDecimal()); + Assert.Equal(2, after[1].GetProperty("routingSequence").GetInt32()); + } + + // CSVから改訂しても同じ形で残る(CSVが追跡の抜け道にならない) + Assert.True((await Phase3TestData.ImportCsvAsync(admin, "masters/csv/bom", """ + ParentProductCode,ChildProductCode,QuantityPer + FG-01,RM-02,4 + """)).Succeeded); + var csv = await LatestBomLogAsync(); + Assert.NotEqual(api.Id, csv.Id); + using (var detail = JsonDocument.Parse(csv.Detail!)) + { + Assert.Equal(2, detail.RootElement.GetProperty("before").GetArrayLength()); + var after = detail.RootElement.GetProperty("after"); + Assert.Equal(1, after.GetArrayLength()); + Assert.Equal(4m, after[0].GetProperty("quantityPer").GetDecimal()); + } + + async Task LatestBomLogAsync() + { + var logs = await admin.GetFromJsonAsync>( + $"/api/audit-logs?targetType=Bom&targetId={ctx.ProductId}&action=Update"); + return logs!.Items[0]; // 新しい順 + } + } + /// /// 埋め戻しはSQLiteの既存DBだけが対象(他のプロバイダーは列の追加後に作られるため過去行が無い)。 /// 実DB(MESAPP_TEST_PROVIDER)で流したときは何も検査せずに通る(xUnit 2 は実行時にスキップできない) diff --git a/tests/MesApp.Api.Tests/ExecutionTests.cs b/tests/MesApp.Api.Tests/ExecutionTests.cs index 94f65e9..c391e4d 100644 --- a/tests/MesApp.Api.Tests/ExecutionTests.cs +++ b/tests/MesApp.Api.Tests/ExecutionTests.cs @@ -311,7 +311,7 @@ await admin.PostAsJsonAsync("/api/inventory/status", new List { new(ctx.MaterialId, Phase3TestData.BomQuantityPer, MakeOrBuy.InHouse, "G1"), - new(other.Id, Phase3TestData.BomQuantityPer, MakeOrBuy.InHouse, "G1"), + new(other.Id, Phase3TestData.BomQuantityPer, MakeOrBuy.InHouse, "G1", IsAlternative: true), })).EnsureSuccessStatusCode(); var stillRejected = await admin.PostAsJsonAsync($"/api/work-orders/{workOrderId}/consumptions", new ConsumptionRequest(otherLot.Id, ctx.MaterialLocationId, 5m)); @@ -321,7 +321,7 @@ await admin.PostAsJsonAsync("/api/inventory/status", var newOrder = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); var accepted2 = await admin.PostAsJsonAsync( $"/api/work-orders/{newOrder.WorkOrders[0].Id}/consumptions", - new ConsumptionRequest(otherLot.Id, ctx.MaterialLocationId, 5m)); + new ConsumptionRequest(otherLot.Id, ctx.MaterialLocationId, 5m, "主材料の欠品")); Assert.Equal(HttpStatusCode.OK, accepted2.StatusCode); Assert.Equal(95m, await Phase3TestData.GetStockQuantityAsync(admin, otherLot.Id)); } @@ -409,6 +409,58 @@ await admin.PostAsJsonAsync("/api/inventory/status", Assert.Equal(20m, consumptions[0].Quantity); } + [Fact] + public async Task バックフラッシュは部材を消費工程の作業指示でだけ引く() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + // RM-01 は工程1で消費、RM-02 は消費工程を指定しない(=最終工程の工程2) + var packing = await MasterTests.CreateProductAsync(admin, "RM-02", "梱包材", ProductType.Material); + (await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}/bom", new List + { + new(ctx.MaterialId, Phase3TestData.BomQuantityPer, MakeOrBuy.InHouse, null, RoutingSequence: 1), + new(packing.Id, 1m, MakeOrBuy.InHouse, null), + })).EnsureSuccessStatusCode(); + var materialLot = await Phase3TestData.ReceiveAsync(admin, ctx.MaterialId, 100m, ctx.MaterialLocationId); + var packingLot = await Phase3TestData.ReceiveAsync(admin, packing.Id, 100m, ctx.MaterialLocationId); + var order = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); + + // 未指定の行は展開時に最終工程へ解決して固定する(以降の工順改訂に左右されない) + Assert.Equal(1, order.Materials!.Single(m => m.ChildProductId == ctx.MaterialId).RoutingSequence); + Assert.Equal(2, order.Materials!.Single(m => m.ChildProductId == packing.Id).RoutingSequence); + + // 工程ごとにバックフラッシュしても、各部材は自分の工程で1回だけ引かれる(工程数ぶん二重に減らない) + (await admin.PostAsJsonAsync($"/api/work-orders/{order.WorkOrders[0].Id}/production-records", + new ProductionRecordRequest(10m, 0m, DateTimeOffset.Now.AddHours(-2), DateTimeOffset.Now.AddHours(-1), + null, Backflush: true))).EnsureSuccessStatusCode(); + Assert.Equal(80m, await Phase3TestData.GetStockQuantityAsync(admin, materialLot.Id)); + Assert.Equal(100m, await Phase3TestData.GetStockQuantityAsync(admin, packingLot.Id)); + + (await admin.PostAsJsonAsync($"/api/work-orders/{order.WorkOrders[1].Id}/production-records", + new ProductionRecordRequest(10m, 0m, DateTimeOffset.Now.AddHours(-1), DateTimeOffset.Now, + ctx.ProductLocationId, Backflush: true))).EnsureSuccessStatusCode(); + Assert.Equal(80m, await Phase3TestData.GetStockQuantityAsync(admin, materialLot.Id)); + Assert.Equal(90m, await Phase3TestData.GetStockQuantityAsync(admin, packingLot.Id)); + } + + [Fact] + public async Task 消費する部材の無い工程ではバックフラッシュを拒否する() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + var materialLot = await Phase3TestData.ReceiveAsync(admin, ctx.MaterialId, 100m, ctx.MaterialLocationId); + var order = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); + + // 部材は消費工程が未指定=最終工程(工程2)。工程1で引こうとしたら、黙って何もしないのではなく知らせる + var record = await admin.PostAsJsonAsync($"/api/work-orders/{order.WorkOrders[0].Id}/production-records", + new ProductionRecordRequest(10m, 0m, DateTimeOffset.Now.AddHours(-1), DateTimeOffset.Now, + null, Backflush: true)); + Assert.Equal(HttpStatusCode.BadRequest, record.StatusCode); + Assert.Equal(100m, await Phase3TestData.GetStockQuantityAsync(admin, materialLot.Id)); + } + [Fact] public async Task バックフラッシュは代替部品を消費しない() { diff --git a/tests/MesApp.Api.Tests/MasterCsvTests.cs b/tests/MesApp.Api.Tests/MasterCsvTests.cs index 7c44af5..cd60e40 100644 --- a/tests/MesApp.Api.Tests/MasterCsvTests.cs +++ b/tests/MesApp.Api.Tests/MasterCsvTests.cs @@ -161,7 +161,7 @@ public class MasterCsvTests Assert.False(result.Succeeded); Assert.Contains(result.Errors, e => e.Line == 2 && e.Message == "Name is required."); - Assert.Contains(result.Errors, e => e.Line == 3 && e.Message == "StandardDefectRate must be 100 or less ('200')."); + Assert.Contains(result.Errors, e => e.Line == 3 && e.Message == "StandardDefectRate must be 99.99 or less ('200')."); Assert.Contains(result.Errors, e => e.Line == 5 && e.Message == "Item code 'P-003' appears on multiple rows."); } @@ -186,6 +186,49 @@ public class MasterCsvTests Assert.Equal(ProductType.Material, product.Type); } + [Fact] + public async Task 循環するMBOMはCSVでも取り込めない() + { + using var factory = new ApiFactory(); + using var client = await TestAuth.CreateAdminClientAsync(factory); + Assert.True((await ImportAsync(client, "products", """ + Code,Name,Unit,Type + FG-01,完成品,個,Product + SF-01,半製品,個,SemiFinished + """)).Succeeded); + + // 同じファイルの後の行が前の行と循環するのも捕まえる(単票APIと同じ判定。Spec.md 7.4) + var result = await ImportAsync(client, "bom", """ + ParentProductCode,ChildProductCode,QuantityPer + FG-01,SF-01,1 + SF-01,FG-01,1 + """); + Assert.False(result.Succeeded); + Assert.Contains(result.Errors, e => e.Line == 3 && e.Message.Contains("SF-01 → FG-01 → SF-01")); + } + + [Fact] + public async Task 代替部品グループの整合はCSVでも確かめる() + { + using var factory = new ApiFactory(); + using var client = await TestAuth.CreateAdminClientAsync(factory); + Assert.True((await ImportAsync(client, "products", """ + Code,Name,Unit,Type + FG-01,完成品,個,Product + RM-01,主材料,個,Material + RM-02,代替部材,個,Material + """)).Succeeded); + + // 単票APIと同じ判定(ProductStructurePolicy)。主材料の無いグループは取り込めない + var result = await ImportAsync(client, "bom", """ + ParentProductCode,ChildProductCode,QuantityPer,AlternativeGroup,IsAlternative + FG-01,RM-01,1,GRP-1,true + FG-01,RM-02,1,GRP-1,true + """); + Assert.False(result.Succeeded); + Assert.Contains(result.Errors, e => e.Line == 2 && e.Message.Contains("GRP-1") && e.Message.Contains("FG-01")); + } + [Fact] public async Task MBOMと工順をコード指定のCSVで一括登録できる() { @@ -206,9 +249,9 @@ public class MasterCsvTests """)).Succeeded); var bom = await ImportAsync(client, "bom", """ - ParentProductCode,ChildProductCode,QuantityPer,MakeOrBuy,AlternativeGroup - FG-01,RM-01,2,InHouse, - FG-01,RM-02,1.5,Outsourced,ALT-1 + ParentProductCode,ChildProductCode,QuantityPer,MakeOrBuy,AlternativeGroup,RoutingSequence + FG-01,RM-01,2,InHouse,,1 + FG-01,RM-02,1.5,Outsourced,ALT-1, """); Assert.True(bom.Succeeded, string.Join(" / ", bom.Errors.Select(e => e.Message))); Assert.Equal(1, bom.Created); @@ -218,6 +261,9 @@ public class MasterCsvTests var bomLines = await client.GetFromJsonAsync>($"/api/products/{parentId}/bom"); Assert.Equal(2, bomLines!.Count); Assert.Equal(1.5m, bomLines.Single(b => b.ChildProductCode == "RM-02").QuantityPer); + // 消費工程は空なら未指定(最終工程で消費) + Assert.Equal(1, bomLines.Single(b => b.ChildProductCode == "RM-01").RoutingSequence); + Assert.Null(bomLines.Single(b => b.ChildProductCode == "RM-02").RoutingSequence); var routing = await ImportAsync(client, "routing", """ ProductCode,Sequence,ProcessCode,StandardWorkMinutes,StandardSetupMinutes,ControlItems diff --git a/tests/MesApp.Api.Tests/MasterTests.cs b/tests/MesApp.Api.Tests/MasterTests.cs index ef6d7fc..7000e30 100644 --- a/tests/MesApp.Api.Tests/MasterTests.cs +++ b/tests/MesApp.Api.Tests/MasterTests.cs @@ -75,6 +75,69 @@ public class MasterTests Assert.Equal(HttpStatusCode.Forbidden, write.StatusCode); } + [Fact] + public async Task 循環するMBOMは登録できない() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var fg = await CreateProductAsync(admin, "FG-01", "完成品", ProductType.Product); + var sf = await CreateProductAsync(admin, "SF-01", "半製品", ProductType.SemiFinished); + var rm = await CreateProductAsync(admin, "RM-01", "部材", ProductType.Material); + (await admin.PutAsJsonAsync($"/api/products/{fg.Id}/bom", + new List { new(sf.Id, 1m, MakeOrBuy.InHouse, null) })).EnsureSuccessStatusCode(); + (await admin.PutAsJsonAsync($"/api/products/{sf.Id}/bom", + new List { new(rm.Id, 1m, MakeOrBuy.InHouse, null) })).EnsureSuccessStatusCode(); + + // RM-01 → FG-01 を足すと FG-01 → SF-01 → RM-01 → FG-01 と一周する。経路をそのまま示す + var cyclic = await admin.PutAsJsonAsync($"/api/products/{rm.Id}/bom", + new List { new(fg.Id, 1m, MakeOrBuy.InHouse, null) }); + Assert.Equal(HttpStatusCode.BadRequest, cyclic.StatusCode); + var problem = await cyclic.Content.ReadFromJsonAsync(); + Assert.Contains("RM-01 → FG-01 → SF-01 → RM-01", problem!.Title); + + // 親品目自身の既存明細は置き換えられるので、循環の判定に入れない(SF-01 の子を差し替えるのは循環ではない) + (await admin.PutAsJsonAsync($"/api/products/{sf.Id}/bom", + new List { new(rm.Id, 2m, MakeOrBuy.InHouse, null) })).EnsureSuccessStatusCode(); + } + + [Fact] + public async Task 代替部品グループは主材料をちょうど1つ持つ() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var fg = await CreateProductAsync(admin, "FG-01", "完成品", ProductType.Product); + var main = await CreateProductAsync(admin, "RM-01", "主材料", ProductType.Material); + var sub = await CreateProductAsync(admin, "RM-02", "代替部材", ProductType.Material); + + async Task PutAsync(params BomItemRequest[] items) + { + var response = await admin.PutAsJsonAsync($"/api/products/{fg.Id}/bom", items.ToList()); + return response.IsSuccessStatusCode + ? null + : (await response.Content.ReadFromJsonAsync())!.Title; + } + + // グループの無い代替部品は、どの主材料の代わりか決まらない + Assert.Contains("RM-02", await PutAsync( + new(main.Id, 1m, MakeOrBuy.InHouse, null), + new(sub.Id, 1m, MakeOrBuy.InHouse, null, IsAlternative: true))); + // 主材料が無いと、バックフラッシュがグループの部材を何も引かない + Assert.Contains("GRP-1", await PutAsync( + new(main.Id, 1m, MakeOrBuy.InHouse, "GRP-1", IsAlternative: true), + new(sub.Id, 1m, MakeOrBuy.InHouse, "GRP-1", IsAlternative: true))); + // 主材料が2つだと、代わりになるはずの部材を両方引く + Assert.Contains("RM-01, RM-02", await PutAsync( + new(main.Id, 1m, MakeOrBuy.InHouse, "GRP-1"), + new(sub.Id, 1m, MakeOrBuy.InHouse, "GRP-1"))); + + // グループ名は前後の空白を除いて比べ、その形で保存する + Assert.Null(await PutAsync( + new(main.Id, 1m, MakeOrBuy.InHouse, "GRP-1"), + new(sub.Id, 1m, MakeOrBuy.InHouse, " GRP-1 ", IsAlternative: true))); + var bom = await admin.GetFromJsonAsync>($"/api/products/{fg.Id}/bom"); + Assert.All(bom!, b => Assert.Equal("GRP-1", b.AlternativeGroup)); + } + [Fact] public async Task MBOMと工順を一括登録できる() { diff --git a/tests/MesApp.Api.Tests/ProductionTests.cs b/tests/MesApp.Api.Tests/ProductionTests.cs index 9f873cf..b631df6 100644 --- a/tests/MesApp.Api.Tests/ProductionTests.cs +++ b/tests/MesApp.Api.Tests/ProductionTests.cs @@ -413,6 +413,51 @@ private static async Task CreateOrderAsync( Assert.Equal(HttpStatusCode.BadRequest, expanded.StatusCode); } + [Fact] + public async Task 予定材料は親品目の標準不良率ぶん割り増される() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + + // 不良品も部材を使うので、良品10を得るには 2×10÷(1-0.2)=25 の部材が要る(A-40-10-04) + (await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}", + new ProductRequest("FG-01", "完成品", "個", null, ProductType.Product, 20m))).EnsureSuccessStatusCode(); + var order = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); + Assert.Equal(25m, order.Materials!.Single().PlannedQuantity); + + // 割り切れないときは小数6桁で切り上げる(2×10÷0.7=28.5714285…) + (await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}", + new ProductRequest("FG-01", "完成品", "個", null, ProductType.Product, 30m))).EnsureSuccessStatusCode(); + var rounded = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); + Assert.Equal(28.571429m, rounded.Materials!.Single().PlannedQuantity); + + // ÷(1-率) なので100%は受け付けない + var invalid = await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}", + new ProductRequest("FG-01", "完成品", "個", null, ProductType.Product, 100m)); + Assert.Equal(HttpStatusCode.BadRequest, invalid.StatusCode); + } + + [Fact] + public async Task MBOMの消費工程が工順に無い品目の指図は展開できない() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + // 工順は工程順序 1・2。MBOMと工順は別々に改訂できるため、ずれは展開時に止める + (await admin.PutAsJsonAsync($"/api/products/{ctx.ProductId}/bom", new List + { + new(ctx.MaterialId, Phase3TestData.BomQuantityPer, MakeOrBuy.InHouse, null, RoutingSequence: 3), + })).EnsureSuccessStatusCode(); + var order = await CreateOrderAsync(admin, ctx.ProductId); + await admin.PostAsync($"/api/manufacturing-orders/{order.Id}/approve", null); + + var expanded = await admin.PostAsJsonAsync( + $"/api/manufacturing-orders/{order.Id}/expand", new ExpandRequest(null)); + Assert.Equal(HttpStatusCode.BadRequest, expanded.StatusCode); + Assert.Contains("RM-01", await expanded.Content.ReadAsStringAsync()); + } + [Fact] public async Task 承認済みの指図を変更すると未承認に戻る() {
    @L["子品目"]@L["品目名"]@L["必要数量(計画数分)"]@L["内外製"]
    @L["子品目"]@L["品目名"]@L["必要数量(計画数分)"]@L["代替部品グループ"]
    @line.ChildProductCode@line.ChildProductName@(line.QuantityPer * _workOrder.PlannedQuantity)@EnumLabels.Of(line.MakeOrBuy)@line.ProductCode@line.ProductName@RequiredQuantity(line)@(line.AlternativeGroup ?? "-")