From 56135623748810ef1c438fc9bf80b96decf854d9 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:21:03 +0900 Subject: [PATCH 1/9] =?UTF-8?q?=E5=AE=9F=E8=B7=B5=E7=9A=84MES=E5=B0=8E?= =?UTF-8?q?=E5=85=A5=E3=82=AC=E3=82=A4=E3=83=89=E3=81=AE=E7=AC=AC3?= =?UTF-8?q?=E5=9B=9E=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=83=AD=E3=82=B0=EF=BC=88GR3-01=E3=80=9C06?= =?UTF-8?q?=EF=BC=89=E3=82=92=E8=B5=B7=E7=A5=A8=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs-dev/GuideReview3.md | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 docs-dev/GuideReview3.md diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md new file mode 100644 index 0000000..61b47db --- /dev/null +++ b/docs-dev/GuideReview3.md @@ -0,0 +1,68 @@ +# 実践的MES導入ガイド 反映バックログ(第3回・業界別/導入/データ活用の章) + +`ref/実践的MES導入ガイド-1.0.0.pdf`(ENAA・331頁)のうち、前2回(`mes8-backlog`=第4章・第8章、`GuideReview2.md`=指標と未実装の業務No)で +当てていなかった章——**5.1 組立・5.4 自動車部品・6.3.2 中堅・中小企業・7.2.2 エネルギー管理・8.3.2 データドリブンマネジメントの実践**——を実装と突き合わせた結果。 + +**提案であり、決定ではない。** 着手する場合は1項目=1セッションを基本とする(`Orchestration.md` §4.1)。 + +| ID | 項目 | 区分 | 状態 | +|:--|:--|:--|:--| +| GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | 未着手 | +| GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | 未着手 | +| GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | 未着手 | +| GR3-04 | 製造リードタイムの分布 | B. 指標 | 未着手 | +| GR3-05 | 標準時間の見直し候補 | B. 指標 | 未着手 | +| GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | 未着手 | + +## 総評 + +第4章の機能面は前2回で埋まっている。今回残ったのは **`Spec.md` が照合・表示を宣言しているのに経路の一部にしか実装が無いもの**(A群)と、 +ガイド 8.3.2 が代表例に挙げる **「ばらつき」と「業務パラメータの定期見直し」の見える化**(B群)である。 +シリアル番号による全数トレーサビリティ(5.1.2(2)・5.4.2)は既に[将来拡張]と判断済みで、エンティティからの設計になるため本バックログに含めない。 +SaaS型提供・オフライン同期(6.3.2)は配布方式(`Spec.md` 7.8)の方針外として扱わない。 + +--- + +## A. `Spec.md` に書いたのに実装が無い/経路の一部にしか無い + +### GR3-01 着手時のスキル・資格照合(F-20-30-01) + +- 該当: `src/MesApp.Api/Services/WorkOrderExecutionService.cs`(`StartAsync`)、`WorkOrderDispatchService.cs` +- 現状: スキル照合は差立(作業員割当)にしか無い。着手は「未配布でも着手可能(差立を省略する小規模運用を許容)」であり、照合を通らない。 + 差立を省略した運用・割当者と異なる者の着手・差立後の有効期限切れのいずれでも、必要スキルを持たない者が作業できる。 +- ガイド: 5.1.2(3)「作業者の認定資格のレベルと作業内容を照合し、適切なスキルを持つ作業者のみが特定作業を実施できるように確認する機能も重要である」。 +- 対応: 照合を `Api/Policies/` に切り出し、差立と着手の両方で通す。着手者のスキルを判定する(割当者ではなく実際に作業する者)。 + +### GR3-02 検査・保全のスキル照合 + +- 現状: `Spec.md` 3.6 は「差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合」と書くが、検査基準・保全計画に必要スキルの項目自体が無い。 +- 対応: 項目を足して照合するか、`Spec.md` の記述を実装に合わせるかを決める。 + +### GR3-03 基本的な管理図と工程能力指数(C-50-10-01〜02) + +- 現状: `Spec.md` 3.3 は「初期は基本的な管理図表示」を実装対象([将来拡張]の印なし)とし、6.1 の品質分析画面にも「管理図」がある。 + 実装は `QualityAnalysisController` のコメントが「SPC・管理図は将来拡張」としたままで、画面にも無い。 +- ガイド: 4.4.3(QC七つ道具・工程能力指数)、5.1.2(4)「SPC の機能を組み込み、リアルタイムで工程の異常を検知」。 +- 対応: 検査実績の測定値と検査基準の上下限は既にあるため、新規エンティティなしで X̄-R 管理図と Cp/Cpk を出す。 + +## B. 指標 + +### GR3-04 製造リードタイムの分布 + +- 現状: リードタイムの集計が無い。指図の納期超過フラグ(`IsOverdue`)だけではばらつきが見えない。 +- ガイド: 8.3.2(3)。受注(着手)からの経過日数ごとの完成数量のヒストグラムで、実績リードタイムのばらつきと異常値を見る。 +- 対応: 指図の着手〜完了を状態・作業指示の履歴から求め、分布と納期との関係を返す集計API+画面。 + +### GR3-05 標準時間の見直し候補 + +- 現状: 標準時間の予実(MESG-02)はあるが、マスタを見直すきっかけにはつながっていない。 +- ガイド: 8.3.2(4)。標準時間などの業務パラメータは影響が顕在化してからではなく、定期的に再設計する。 +- 対応: 実績が標準から一定以上ずれている工順を一覧にする。マスタの自動書き換えはしない(決めるのは業務)。 + +## C. 書くだけ + +### GR3-06 エネルギー管理(FEMS)との範囲を明記する + +- 現状: `Spec.md` にエネルギーの記述が無い。 +- ガイド: 3.4.2・7.2.2・5.1.2(5)。MES は FEMS に稼働・生産量を渡し、原単位(kWh/個)の分母になる役割とされる。 +- 対応: 対象外か[将来拡張]かを明記する。 From 365b66006e9c883223ec9cd68f8f27380f866f99 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:26:07 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=E7=9D=80=E6=89=8B=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=82=82=E7=9D=80=E6=89=8B=E3=81=99=E3=82=8B=E8=80=85=E3=81=AE?= =?UTF-8?q?=E3=82=B9=E3=82=AD=E3=83=AB=E3=83=BB=E8=B3=87=E6=A0=BC=E3=82=92?= =?UTF-8?q?=E7=85=A7=E5=90=88=E3=81=99=E3=82=8B=EF=BC=88GR3-01=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 差立を省略した運用・割当者以外の着手・差立後の有効期限切れで、 必要スキルを持たない者が作業できていた。判定を SkillQualificationPolicy に集約し、 差立と着手で同じ条件を使う。Spec.md 改訂129(125〜128は inspection-fixes で使用中)。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 7 +-- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 2 +- docs/execution.md | 2 + src/MesApp.Api/Localization/ApiText.en.resx | 3 ++ .../Policies/SkillQualificationPolicy.cs | 35 +++++++++++++ .../Services/WorkOrderDispatchService.cs | 17 ++----- .../Services/WorkOrderExecutionService.cs | 24 ++++++++- tests/MesApp.Api.Tests/ProductionTests.cs | 50 +++++++++++++++++++ 9 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 src/MesApp.Api/Policies/SkillQualificationPolicy.cs diff --git a/Spec.md b/Spec.md index cd5262d..b04153c 100644 --- a/Spec.md +++ b/Spec.md @@ -123,6 +123,7 @@ 改訂122: 2026-09-23(5.7節「指図展開時のマスタ固定」に、予定材料の標準不良率による割り増しと消費工程の固定(改訂117・118)を追記した。あわせてマスタCSV・実績CSVの列の案内(`AlternativeGroup`・`IsAlternative`・`Backflush`)、サンプルの説明、利用者ガイド(帳票・用語・マスタのCSV・部材投入)を改訂117〜121に合わせた) 改訂123: 2026-09-23(工程管理項目の自由記述(`Routing.ControlItems`)の長さの上限を、工順・作業指示・API・マスタCSVで1000文字に揃えた。工順が1000文字、展開先の作業指示が500文字だったため、501文字以上を書いた工順は PostgreSQL / SQL Server で指図の展開に失敗していた(SQLiteは長さを守らせないため表に出なかった)。上限を超える記述は工順の登録で拒否する) 改訂124: 2026-09-23(工程管理項目を工順(BOP)の工程ごとに紐付けるようにした(`RoutingControlItem`。I-30-20-10)。これまでは項目側の対象品目/対象工程に「または」で合致する項目を展開時に自動選択していたため、品目を指定した項目がその品目の全工程の作業指示に載り、品目と工程の組合せや、同じ工程が2回ある工順の工程ごとの条件を表せなかった。改訂48の「工順に明示的な紐付けを持たせない」方針を改め、`ControlItem` から対象品目・対象工程を削除した(作業手順書と同じく紐付けは工順側だけに持つ)。紐付けられるのは有効な項目だけで、紐付け中の項目は単票APIでもCSV取込でも無効化できない(`MasterDeactivationPolicy`)。API は `RoutingStepRequest.ControlItemIds`、CSV は工程管理項目から `TargetProductCode`・`TargetProcessCode` を外し、工順に `ControlItemCodes`(セミコロン区切り)を追加。`Routing.ControlItems` の自由記述は補足として残す。5.1節・5.7節に反映) +改訂129: 2026-09-23(**着手時にもスキル・資格を照合する**ようにした(F-20-30-01)。照合は差立(作業員割当)にしか無く、着手は差立を省略できる(小規模運用)ため、差立を省いた運用・割当者と異なる者の着手・差立後の有効期限切れのいずれでも、必要スキルを持たない者が作業できていた。照合するのは**割当者ではなく実際に着手する者**。判定は `Api/Policies/SkillQualificationPolicy` に集約し、差立と着手で同じ条件を使う。3.2節・3.6節に反映。改訂125〜128は別ブランチ(検査の見直し)で使用中のため欠番にしない) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -243,12 +244,12 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 ### 3.2 製造実行〔B〕 - **製造指図の工程展開・作業指示化**:製造指図を工順(BOP)に基づき工程単位の作業指示に展開(B-10-10-01)、受領・内容確認(B-10-10-02〜03)、産出ロットNo採番(B-10-10-05。自動採番:品目コード+日付+連番、または手入力。シリアルNo単位は将来拡張) -- **差立(ディスパッチング)**:作業指示への作業員割当(B-10-20-01。スキル・資格照合 F-20-30-01 を伴う)、工程・設備割当(B-10-20-02。新しく割り当てられる設備は状態が稼働可能のものだけ。割当済みの設備のまま着手順だけを変える差立は通す)、着手順割当(B-10-20-03) +- **差立(ディスパッチング)**:作業指示への作業員割当(B-10-20-01。スキル・資格照合 F-20-30-01 を伴う。照合は着手時にも着手する者に対して行う)、工程・設備割当(B-10-20-02。新しく割り当てられる設備は状態が稼働可能のものだけ。割当済みの設備のまま着手順だけを変える差立は通す)、着手順割当(B-10-20-03) - **作業指示の配布・確認**:作業指示の承認・配布(B-10-30-01〜02)、指示内容(構成部品・手順等)の閲覧(B-10-30-03)、作業指示書・現品票等の帳票出力(B-10-30-04) - **前段取り・セッティング**:始業前点検・自主保全記録(B-20-10-01〜02)、部材引当・受領・構成確認(B-20-20-01、B-20-20-03〜04)、**治工具引当・払出指示・受領確認(B-20-30-01〜03。引当→払出(受領確認)→返却を `ToolIssue` で追う。無効・廃棄・メンテ中・寿命到達・他の作業指示に引当中の治工具は引き当てられず、判定は引当時と払出時の両方で行う)**、製造条件/設備プログラム展開・設備作業前点検(B-20-40-01〜02)、段取り指示確認〜着手〜完了の記録(B-20-50-01〜05。開始/終了時刻・実施者・異常報告) - **チェックリスト**:共通事項・工程/品目別制約・作業資格のチェック実施記録(B-30-10-01〜03)。チェックリストマスタにHSEの健康衛生・安全項目を組み込み可能(G-20-20-02、G-30-20-03) - **部材投入**:投入部材の数量・ロットNo・投入時刻の記録(B-30-20-01)。**投入部材はMBOM照合を行い、作業指示の品目のMBOMに定義されていない品目は投入不可**とする(誤投入防止。3.9節「部材投入の照合」)。秤量機等との自動連携(B-30-20-02)は[将来拡張]とし、初期はCSV取込で代替 -- **製造実施**:製造着手(B-30-30-01)、作業時間記録(B-30-30-02、F-30-20-01)、製造条件データの記録(B-30-30-04。手入力/CSV取込。設備自動収集は[将来拡張])、作業完了報告(B-30-30-06) +- **製造実施**:製造着手(B-30-30-01。差立を省略しても着手できるが、必要スキルのある作業指示は着手する者のスキル・資格を照合する)、作業時間記録(B-30-30-02、F-30-20-01)、製造条件データの記録(B-30-30-04。手入力/CSV取込。設備自動収集は[将来拡張])、作業完了報告(B-30-30-06) - **製造実績報告**:出来高(良品数・不良数、および不良数の内訳としての**廃棄数・再作業待ち数**・**不良理由別の内訳**)報告(B-40-10-01)、半製品・製品の在庫計上(B-40-10-02)、進捗・時間報告(B-40-10-03〜04)、完了時の製造データ・トラブル履歴・部材欠品報告(B-40-10-05〜07)、部材投入実績の事後報告(B-40-10-08)、**バックフラッシュ**(MBOMと完了数量から部材消費を自動算出し在庫引落し。B-40-10-09)、製造完了承認(B-40-10-10) - **設備稼働報告**:稼働時間・停止時間・停止原因の記録(B-40-20-01〜02。手入力。自動収集は[将来拡張]) - **品質実績報告**:逸脱/品質不具合の記録(B-40-30-01)、不良原因の選択式記録+詳細(文章・写真)(B-40-30-02)、不良部材報告(B-40-30-03)、不良品在庫の計上・引落し(B-40-30-04) @@ -307,7 +308,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - **工場従業員情報管理**:従業員データの登録・変更・削除(F-10-10-01〜02。MESユーザーアカウントと統合管理)。**所属(部署・課)・作業場所(作業区)・所属する直**を持つ - **勤務シフト(直)の定義**:昼勤・夜勤などの時間帯を定義する(F-10-10-01)。3.9節の製造日(業務日付)が夜勤を前提にしているのに対し、「その実績がどの直か」を表す。**誰がいつどの直に入るかの勤務計画は持たない**(勤怠管理 F-30-10-01〜02 は本システムの対象外。ガイドでも△) - **スキル・資格管理**:スキル・資格の定義(F-20-10-01)、従業員への資格・スキル登録(使用可能設備・製造可能品・有効期限等。F-20-10-02〜03)、有効期限接近の警告(F-20-10-04)、有効期間の管理(F-20-10-05) -- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03) +- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03)。製造では差立(割当者)と着手(着手する者)の両方で照合する(`SkillQualificationPolicy`) - **間接時間管理**:段取り・部材準備・設備メンテ等、製造に直接紐づかない作業時間の記録(F-30-20-02) - **システム管理者を失わない**:**有効なシステム管理者が0人になる変更は行えない**(無効化・システム管理者ロールの剥奪のいずれも)。管理者がいなくなるとユーザー管理も権限付与もできず、初期セットアップAPIはUserが0件のときしか動かない(2.2節E)ため、DBを直接操作する以外に復旧手段がなくなる。単独PC配布(7.8節)では管理者が1人だけの運用が普通で、現実に起こりうる - 判定は1か所(`Api/Policies/LastAdminPolicy`)に置き、ユーザー管理画面(`PUT api/users/{id}`)とマスタCSV取込(users)の双方から呼ぶ diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index e0a63eb..527590f 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -30,7 +30,7 @@ | 業務 | MES No | API | エンティティ | 画面 | テスト | |:--|:--|:--|:--|:--|:--| -| 作業指示・差立(作業員/設備割当・着手順) | B-10-20 / F-20-30-01 | `WorkOrdersController.cs` `api/work-orders`
差立の検証・反映は `Api/Services/WorkOrderDispatchService.cs`
候補設備は `api/work-orders/{id}/equipment-candidates`(**候補があればその中からしか割り当てられない**。候補は工順マスタの現在値。**新しく割り当てる設備は `Status`=稼働可能のみ**) | Production.cs: WorkOrder
Masters.cs: **RoutingEquipment**(工順の候補設備) | `/work-orders` `WorkOrders.razor`
`/dispatch` `Dispatch.razor` | `Tests/ExecutionTests.cs` | +| 作業指示・差立(作業員/設備割当・着手順) | B-10-20 / F-20-30-01 | `WorkOrdersController.cs` `api/work-orders`
差立の検証・反映は `Api/Services/WorkOrderDispatchService.cs`
スキル照合は `Api/Policies/SkillQualificationPolicy.cs`(**差立=割当者と着手=着手する者の両方**で通す。着手は `WorkOrderExecutionService.StartAsync`)
候補設備は `api/work-orders/{id}/equipment-candidates`(**候補があればその中からしか割り当てられない**。候補は工順マスタの現在値。**新しく割り当てる設備は `Status`=稼働可能のみ**) | Production.cs: WorkOrder
Masters.cs: **RoutingEquipment**(工順の候補設備) | `/work-orders` `WorkOrders.razor`
`/dispatch` `Dispatch.razor` | `Tests/ExecutionTests.cs`・`ProductionTests.cs`(スキル照合) | | 実行系(着手・段取り・チェックリスト・部材投入・実績報告・製造条件データ・状態履歴) | B-30-30-01 / B-20-50 / B-40-40 | `WorkOrderExecutionController.cs`
`api/work-orders/{id:int}`
着手・承認・判定・在庫計上は `Api/Services/WorkOrderExecutionService.cs`(CSV取込と共有) | `Core/Entities/Execution.cs`
SetupRecord / ChecklistRecord / ChecklistResultItem / MaterialConsumption / ProductionRecord / ProductionDataRecord | `/work-orders/{id}/setup` `WorkOrderSetup.razor`
`/work-orders/{id}/record` `ProductionRecordEntry.razor`(部材投入・製造条件データ・訂正・状態履歴もここ)
`/process-progress` `ProcessProgress.razor` | `Tests/ExecutionTests.cs` | | 製造履歴訂正(訂正履歴+監査ログ) | B-70-30-01 | `ProductionRecordsController.cs` `api/production-records`(数量だけを訂正する。**直は記録時に固定した値なので動かない**) | Execution.cs: ProductionRecord / **ProductionRecordCorrection**(訂正前の値。Spec.md 5.7) | `ProductionRecordEntry.razor` の実績一覧から訂正
`/traceability` の履歴タブに訂正履歴を表示 | `Tests/ExecutionTests.cs` / `QualityTests.cs` | | 作業時間記録(直接/間接) | B-30-30-02 / F-30-20-02 | `WorkTimeRecordsController.cs` `api/work-time-records` | Execution.cs: WorkTimeRecord | `/work-time` `WorkTime.razor` | `Tests/ExecutionTests.cs` | diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index 61b47db..e03ce4c 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -7,7 +7,7 @@ | ID | 項目 | 区分 | 状態 | |:--|:--|:--|:--| -| GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | 未着手 | +| GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂129) | | GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | 未着手 | | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | 未着手 | | GR3-04 | 製造リードタイムの分布 | B. 指標 | 未着手 | diff --git a/docs/execution.md b/docs/execution.md index e9a9d1a..54379f4 100644 --- a/docs/execution.md +++ b/docs/execution.md @@ -45,6 +45,8 @@ next_title: 物流・在庫管理 作業者を割り当てると、その工程の工順に設定された **必要スキル・資格を持っているか自動チェック** されます。 資格がない場合や、**資格の有効期限が切れている場合**はエラーになり、割当されません。 +同じ照合は **着手のとき、着手する人に対しても** 行われます。差立を省略した場合や、割り当てた人と別の人が着手する場合、差立のあとで資格の期限が切れた場合も、資格のない人は着手できません。 + > 例:`作業者 '山田 太郎' のスキル '溶接技能' は有効期限切れです。` マスタ管理 → ユーザー のスキル・資格から、資格の追加や有効期限の更新を行ってください。 diff --git a/src/MesApp.Api/Localization/ApiText.en.resx b/src/MesApp.Api/Localization/ApiText.en.resx index aed7dcb..2387e5a 100644 --- a/src/MesApp.Api/Localization/ApiText.en.resx +++ b/src/MesApp.Api/Localization/ApiText.en.resx @@ -2016,4 +2016,7 @@ Control item '{0}' cannot be deactivated because it is linked from the routing of item {1}. + + Cannot identify the operator starting the work. + diff --git a/src/MesApp.Api/Policies/SkillQualificationPolicy.cs b/src/MesApp.Api/Policies/SkillQualificationPolicy.cs new file mode 100644 index 0000000..ab7acbb --- /dev/null +++ b/src/MesApp.Api/Policies/SkillQualificationPolicy.cs @@ -0,0 +1,35 @@ +using MesApp.Api.Localization; +using MesApp.Core.Entities; + +namespace MesApp.Api.Policies; + +/// +/// 作業者のスキル・資格照合(Spec.md 3.6。F-20-30-01)。 +/// +/// 差立(作業員割当)と着手の両方から同じ条件で判定する。差立は省略できる(小規模運用)うえ、 +/// 割り当てた者と実際に着手する者が違うことも、差立から着手までに有効期限が切れることもある。 +/// 差立にだけ置くと、必要スキルを持たない者がそのまま作業できてしまう。 +/// +/// +public static class SkillQualificationPolicy +{ + /// + /// 必要スキルを満たすか。満たさない理由を日本語で返し、問題なければ null を返す。 + /// + /// 作業者の表示名(メッセージ用) + /// 必要スキル + /// 作業者の保有スキル(未保有は null) + /// 判定する製造日(業務日付) + public static string? Check(string userDisplayName, SkillMaster skill, UserSkill? userSkill, DateOnly businessDate) + { + if (userSkill is null) + { + return ApiText.T("作業者 '{0}' は必要スキル '{1}' を保有していません。", userDisplayName, skill.Name); + } + if (skill.RequiresExpiry && (userSkill.ExpiresOn is null || userSkill.ExpiresOn < businessDate)) + { + return ApiText.T("作業者 '{0}' のスキル '{1}' は有効期限切れです。", userDisplayName, skill.Name); + } + return null; + } +} diff --git a/src/MesApp.Api/Services/WorkOrderDispatchService.cs b/src/MesApp.Api/Services/WorkOrderDispatchService.cs index 7abbdf9..513f12d 100644 --- a/src/MesApp.Api/Services/WorkOrderDispatchService.cs +++ b/src/MesApp.Api/Services/WorkOrderDispatchService.cs @@ -1,4 +1,5 @@ using MesApp.Api.Localization; +using MesApp.Api.Policies; using MesApp.Core.Localization; using MesApp.Core.Abstractions; using MesApp.Core.Contracts.Production; @@ -49,20 +50,12 @@ public async Task> DispatchAsync( // 必要スキルは工順マスタの現在値ではなく、展開時点のスナップショットを使う(Spec.md 5.7) if (workOrder.RequiredSkillId is int skillId) { - var today = businessDate.Today; - var userSkill = await db.UserSkills.Include(s => s.Skill) + var skill = await db.Skills.FirstAsync(s => s.Id == skillId, ct); + var userSkill = await db.UserSkills .FirstOrDefaultAsync(s => s.UserId == user.Id && s.SkillId == skillId, ct); - var skillName = userSkill?.Skill?.Name - ?? await db.Skills.Where(s => s.Id == skillId).Select(s => s.Name).FirstAsync(ct); - if (userSkill is null) + if (SkillQualificationPolicy.Check(user.DisplayName, skill, userSkill, businessDate.Today) is { } error) { - return Outcome.Invalid( - ApiText.T("作業者 '{0}' は必要スキル '{1}' を保有していません。", user.DisplayName, skillName)); - } - if (userSkill.Skill!.RequiresExpiry && (userSkill.ExpiresOn is null || userSkill.ExpiresOn < today)) - { - return Outcome.Invalid( - ApiText.T("作業者 '{0}' のスキル '{1}' は有効期限切れです。", user.DisplayName, skillName)); + return Outcome.Invalid(error); } } } diff --git a/src/MesApp.Api/Services/WorkOrderExecutionService.cs b/src/MesApp.Api/Services/WorkOrderExecutionService.cs index f9f9346..389c029 100644 --- a/src/MesApp.Api/Services/WorkOrderExecutionService.cs +++ b/src/MesApp.Api/Services/WorkOrderExecutionService.cs @@ -50,7 +50,10 @@ public sealed class WorkOrderExecutionService( IBusinessDateService businessDate, IAuditLogger auditLogger) { - /// 着手(B-30-30-01)。未配布でも着手可能(差立を省略する小規模運用を許容) + /// + /// 着手(B-30-30-01)。未配布でも着手可能(差立を省略する小規模運用を許容)。 + /// 必要スキルのある作業指示は、着手する者のスキル・資格を照合する(F-20-30-01) + /// public async Task> StartAsync(int workOrderId, string? userId, CancellationToken ct) { var workOrder = await db.WorkOrders.FindAsync([workOrderId], ct); @@ -62,6 +65,25 @@ public async Task> StartAsync(int workOrderId, string? userId { return Outcome.Conflict(ApiText.T("状態 '{0}' の作業指示は着手できません。", EnumLabels.Of(workOrder.Status))); } + + // スキル・資格照合(F-20-30-01)。照合するのは割当者ではなく実際に着手する者。 + // 差立を省略した運用・割当者以外の着手・差立後の期限切れを、差立の照合だけでは防げないため + if (workOrder.RequiredSkillId is int skillId) + { + var user = userId is null ? null : await db.Users.FirstOrDefaultAsync(u => u.Id == userId, ct); + if (user is null) + { + return Outcome.Invalid(ApiText.T("着手する作業者を特定できません。")); + } + var skill = await db.Skills.FirstAsync(s => s.Id == skillId, ct); + var userSkill = await db.UserSkills + .FirstOrDefaultAsync(s => s.UserId == user.Id && s.SkillId == skillId, ct); + if (SkillQualificationPolicy.Check(user.DisplayName, skill, userSkill, businessDate.Today) is { } error) + { + return Outcome.Invalid(error); + } + } + workOrderStatus.ChangeStatus(workOrder, WorkOrderStatus.Started, WorkOrderStatusChangeSource.Start, userId); await db.SaveChangesAsync(ct); diff --git a/tests/MesApp.Api.Tests/ProductionTests.cs b/tests/MesApp.Api.Tests/ProductionTests.cs index 916fa50..d471b93 100644 --- a/tests/MesApp.Api.Tests/ProductionTests.cs +++ b/tests/MesApp.Api.Tests/ProductionTests.cs @@ -527,6 +527,56 @@ await admin.PutAsJsonAsync($"/api/users/{worker.Id}/skills", Assert.Equal(HttpStatusCode.OK, second.StatusCode); } + [Fact] + public async Task 着手は着手する者のスキルを照合し差立を省略しても通さない() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var skillCreated = await admin.PostAsJsonAsync("/api/skills", + new SkillRequest("SK-01", "組立資格", SkillType.Certification, true)); + var skill = await skillCreated.Content.ReadFromJsonAsync(); + var (productId, _) = await SetupMastersAsync(admin, requiredSkillId: skill!.Id); + + using var qualified = await TestAuth.CreateUserClientAsync(factory, admin, "worker1", "Passw0rd123", MesRoles.Operator); + using var unqualified = await TestAuth.CreateUserClientAsync(factory, admin, "worker2", "Passw0rd123", MesRoles.Operator); + var users = await admin.GetFromJsonAsync>("/api/users"); + var worker1 = users!.Single(u => u.UserName == "worker1"); + var worker2 = users!.Single(u => u.UserName == "worker2"); + var today = DateOnly.FromDateTime(DateTime.Today); + await admin.PutAsJsonAsync($"/api/users/{worker1.Id}/skills", + new List { new(skill.Id, null, today.AddYears(1)) }); + // worker2 は期限切れの資格だけを持つ + await admin.PutAsJsonAsync($"/api/users/{worker2.Id}/skills", + new List { new(skill.Id, null, today.AddDays(-10)) }); + + var order = await CreateOrderAsync(admin, 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)); + var detail = await expanded.Content.ReadFromJsonAsync(); + var skilledStep = detail!.WorkOrders[0].Id; // 工順1(必要スキルあり) + + // 差立を省略して、スキルを持たない管理者が着手 → 400 + var byAdmin = await admin.PostAsync($"/api/work-orders/{skilledStep}/start", null); + Assert.Equal(HttpStatusCode.BadRequest, byAdmin.StatusCode); + + // worker1 に差立しても、期限切れの worker2 が着手すれば 400 + (await admin.PutAsJsonAsync($"/api/work-orders/{skilledStep}/dispatch", + new DispatchRequest(worker1.Id, null, 1))).EnsureSuccessStatusCode(); + var byExpired = await unqualified.PostAsync($"/api/work-orders/{skilledStep}/start", null); + Assert.Equal(HttpStatusCode.BadRequest, byExpired.StatusCode); + var problem = await byExpired.Content.ReadFromJsonAsync(); + Assert.Contains("有効期限切れ", problem!.Title); + + // 資格のある worker1 は着手できる + var byQualified = await qualified.PostAsync($"/api/work-orders/{skilledStep}/start", null); + Assert.Equal(HttpStatusCode.NoContent, byQualified.StatusCode); + + // 必要スキルのない工順2は誰でも着手できる + var free = await admin.PostAsync($"/api/work-orders/{detail.WorkOrders[1].Id}/start", null); + Assert.Equal(HttpStatusCode.NoContent, free.StatusCode); + } + [Fact] public async Task 進捗一覧で納期遅延を検出できる() { From b4bf25e2837c66cfa594a4196d3a922e6a8c94bf Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:33:03 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=E4=BF=9D=E5=85=A8=E6=89=8B=E9=A0=86?= =?UTF-8?q?=E6=9B=B8=E3=81=AB=E5=BF=85=E8=A6=81=E3=82=B9=E3=82=AD=E3=83=AB?= =?UTF-8?q?=E3=82=92=E6=8C=81=E3=81=9F=E3=81=9B=E3=80=81=E4=BF=9D=E5=85=A8?= =?UTF-8?q?=E5=AE=9F=E7=B8=BE=E3=81=AE=E7=99=BB=E9=8C=B2=E8=80=85=E3=82=92?= =?UTF-8?q?=E7=85=A7=E5=90=88=E3=81=99=E3=82=8B=EF=BC=88GR3-02=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec.md 3.6 は検査・保全でも照合すると書いていたが、必要スキルの項目自体が無かった。 保全は手順書の現在値で照合する。検査は inspection-fixes 合流後に扱い、それまでは 未実装と明記した。Spec.md 改訂130。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 5 +- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 4 +- docs/maintenance.md | 1 + .../MaintenanceProceduresController.cs | 10 +- src/MesApp.Api/Localization/ApiText.en.resx | 3 + .../Services/MaintenanceOrderService.cs | 17 + .../MaintenanceProceduresTab.razor | 21 +- .../Maintenance/MaintenanceContracts.cs | 6 +- src/MesApp.Core/Entities/Maintenance.cs | 7 + .../MaintenanceConfigurations.cs | 2 + ...ntenanceProcedureRequiredSkill.Designer.cs | 4783 ++++++++++++++++ ...12_AddMaintenanceProcedureRequiredSkill.cs | 49 + .../MesAppDbContextModelSnapshot.cs | 12 + ...ntenanceProcedureRequiredSkill.Designer.cs | 4982 +++++++++++++++++ ...14_AddMaintenanceProcedureRequiredSkill.cs | 49 + .../MesAppDbContextModelSnapshot.cs | 12 + ...ntenanceProcedureRequiredSkill.Designer.cs | 4961 ++++++++++++++++ ...17_AddMaintenanceProcedureRequiredSkill.cs | 49 + .../MesAppDbContextModelSnapshot.cs | 12 + tests/MesApp.Api.Tests/MaintenanceTests.cs | 36 + 21 files changed, 15013 insertions(+), 10 deletions(-) create mode 100644 src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.Designer.cs create mode 100644 src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.cs create mode 100644 src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.Designer.cs create mode 100644 src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.cs create mode 100644 src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.Designer.cs create mode 100644 src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.cs diff --git a/Spec.md b/Spec.md index b04153c..9a86b33 100644 --- a/Spec.md +++ b/Spec.md @@ -124,6 +124,7 @@ 改訂123: 2026-09-23(工程管理項目の自由記述(`Routing.ControlItems`)の長さの上限を、工順・作業指示・API・マスタCSVで1000文字に揃えた。工順が1000文字、展開先の作業指示が500文字だったため、501文字以上を書いた工順は PostgreSQL / SQL Server で指図の展開に失敗していた(SQLiteは長さを守らせないため表に出なかった)。上限を超える記述は工順の登録で拒否する) 改訂124: 2026-09-23(工程管理項目を工順(BOP)の工程ごとに紐付けるようにした(`RoutingControlItem`。I-30-20-10)。これまでは項目側の対象品目/対象工程に「または」で合致する項目を展開時に自動選択していたため、品目を指定した項目がその品目の全工程の作業指示に載り、品目と工程の組合せや、同じ工程が2回ある工順の工程ごとの条件を表せなかった。改訂48の「工順に明示的な紐付けを持たせない」方針を改め、`ControlItem` から対象品目・対象工程を削除した(作業手順書と同じく紐付けは工順側だけに持つ)。紐付けられるのは有効な項目だけで、紐付け中の項目は単票APIでもCSV取込でも無効化できない(`MasterDeactivationPolicy`)。API は `RoutingStepRequest.ControlItemIds`、CSV は工程管理項目から `TargetProductCode`・`TargetProcessCode` を外し、工順に `ControlItemCodes`(セミコロン区切り)を追加。`Routing.ControlItems` の自由記述は補足として残す。5.1節・5.7節に反映) 改訂129: 2026-09-23(**着手時にもスキル・資格を照合する**ようにした(F-20-30-01)。照合は差立(作業員割当)にしか無く、着手は差立を省略できる(小規模運用)ため、差立を省いた運用・割当者と異なる者の着手・差立後の有効期限切れのいずれでも、必要スキルを持たない者が作業できていた。照合するのは**割当者ではなく実際に着手する者**。判定は `Api/Policies/SkillQualificationPolicy` に集約し、差立と着手で同じ条件を使う。3.2節・3.6節に反映。改訂125〜128は別ブランチ(検査の見直し)で使用中のため欠番にしない) +改訂130: 2026-09-23(**保全手順書に必要スキル・資格を持たせ、保全実績の登録時に実施者と照合する**ようにした(`MaintenanceProcedure.RequiredSkillId`。F-20-30-01)。3.6節は「差立・検査・保全の各業務で照合」と書いていたが、保全・検査には必要スキルの項目自体が無かった。保全指示は手順書を版数ごと固定しないため、照合は手順書の現在値を使う。判定は差立・着手と同じ `SkillQualificationPolicy`。**検査側は未実装のまま3.6節にそう明記した**(検査基準の見直しが別ブランチで進行中のため、合流後に扱う)。5.1節・3.6節に反映) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -308,7 +309,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - **工場従業員情報管理**:従業員データの登録・変更・削除(F-10-10-01〜02。MESユーザーアカウントと統合管理)。**所属(部署・課)・作業場所(作業区)・所属する直**を持つ - **勤務シフト(直)の定義**:昼勤・夜勤などの時間帯を定義する(F-10-10-01)。3.9節の製造日(業務日付)が夜勤を前提にしているのに対し、「その実績がどの直か」を表す。**誰がいつどの直に入るかの勤務計画は持たない**(勤怠管理 F-30-10-01〜02 は本システムの対象外。ガイドでも△) - **スキル・資格管理**:スキル・資格の定義(F-20-10-01)、従業員への資格・スキル登録(使用可能設備・製造可能品・有効期限等。F-20-10-02〜03)、有効期限接近の警告(F-20-10-04)、有効期間の管理(F-20-10-05) -- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03)。製造では差立(割当者)と着手(着手する者)の両方で照合する(`SkillQualificationPolicy`) +- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03)。製造では差立(割当者)と着手(着手する者)の両方で、保全では保全手順書の必要スキルを保全実績の登録者(実施者)に対して照合する(`SkillQualificationPolicy`)。**検査の照合は未実装**(検査基準に必要スキルを持たない) - **間接時間管理**:段取り・部材準備・設備メンテ等、製造に直接紐づかない作業時間の記録(F-30-20-02) - **システム管理者を失わない**:**有効なシステム管理者が0人になる変更は行えない**(無効化・システム管理者ロールの剥奪のいずれも)。管理者がいなくなるとユーザー管理も権限付与もできず、初期セットアップAPIはUserが0件のときしか動かない(2.2節E)ため、DBを直接操作する以外に復旧手段がなくなる。単独PC配布(7.8節)では管理者が1人だけの運用が普通で、現実に起こりうる - 判定は1か所(`Api/Policies/LastAdminPolicy`)に置き、ユーザー管理画面(`PUT api/users/{id}`)とマスタCSV取込(users)の双方から呼ぶ @@ -425,7 +426,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと | Checklist(チェックリストマスタ) | チェックリストコード、名称、適用区分(共通/工程/品目/段取り/保全/HSE)、項目一覧(B-30-10、G-20/G-30) | | WorkProcedure(作業手順書/SOP) | 手順書番号、表題、手順ステップ、手順書の所在(文書番号・URL)、版数(I-30-40-01〜02、B-10-30-03)。対象品目・対象工程は持たず、工順(BOP)側から紐付ける(I-30-20-12) | | DefectReason(不良理由マスタ) | 不良理由コード、不良理由名、区分(材質・部材/加工・作業/設備/人的要因/その他)、有効フラグ。粒度は「現場が迷わず選べて、集計にも使える」範囲で定義する(C-40-10-01) | -| MaintenanceProcedure(保全手順書) | 手順書番号、対象設備/治工具、手順ステップ、版数(E-10-20) | +| MaintenanceProcedure(保全手順書) | 手順書番号、対象設備/治工具、手順ステップ、版数(E-10-20)、**必要スキルID**(任意。保全実績の登録時に実施者と照合する。F-20-30-01) | ### 5.2 指図・実績系 diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index 527590f..48bd598 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -73,7 +73,7 @@ |:--|:--|:--|:--|:--|:--| | 作業区/資源階層(BOR。工場/ライン/エリア/作業区) | I-10-20-02 | `WorkCentersController.cs` `api/work-centers`
`Api/Policies/WorkCenterHierarchyPolicy.cs`(段の妥当性・循環。単票APIとCSV取込の**両方**から呼ぶ) | Masters.cs: WorkCenter(自己参照。`Level` は文字列保存のため**DB側で並べると段の順にならない**。取得後に並べ直す) | `/masters` `Masters/WorkCentersTab.razor` | `Tests/MasterTests.cs` / `MasterCsvTests.cs` | | 設備台帳/BOE・保全部品 | E-10-10 / I-10-20 | `EquipmentsController.cs` `api/equipments`
保全部品は `api/equipments/{id}/parts`(**設備ごとの一括置換**) | Masters.cs: Equipment(`WorkCenterId`=設置場所の正。**作業区(最下段)のみ**。`Site` は移行用の旧項目)
Masters.cs: **EquipmentPart**(品目参照。資産管理部品/消耗品の区分。`MaintenanceParts` の自由記述は移行元) | `/masters` `Masters/EquipmentsTab.razor` | `Tests/MasterTests.cs` | -| 保全手順書(版数管理) | E-10-20 / E-20-30 | `MaintenanceProceduresController.cs` `api/maintenance-procedures` | Maintenance.cs: MaintenanceProcedure | `/maintenance` `Maintenance/MaintenanceProceduresTab.razor` | `Tests/MaintenanceTests.cs` | +| 保全手順書(版数管理) | E-10-20 / E-20-30 | `MaintenanceProceduresController.cs` `api/maintenance-procedures`(**必要スキル**は保全実績の登録時に実施者と照合:`MaintenanceOrderService.AddRecordAsync` → `SkillQualificationPolicy`) | Maintenance.cs: MaintenanceProcedure | `/maintenance` `Maintenance/MaintenanceProceduresTab.razor` | `Tests/MaintenanceTests.cs` | | 保全計画(中長期・年次) | E-30-10 | `MaintenancePlansController.cs` `api/maintenance-plans`
取消(状態変更)は `Api/Services/MaintenanceOrderService.CancelPlanAsync` | Maintenance.cs: MaintenancePlan | `/maintenance` `Maintenance/MaintenancePlansTab.razor` | `Tests/MaintenanceTests.cs` | | 保全指示・実績・突発依頼 | E-30-20 / E-30-30 / E-40 | `MaintenanceOrdersController.cs` `api/maintenance-orders`
作成・実績登録・取消(指示と元計画の状態変更)は `Api/Services/MaintenanceOrderService.cs`
消費部材の在庫引落しは `POST {id}/record` の `parts`(**`InventoryService.RemoveAsync` 経由**。区分 `MaintenanceIssue`) | Maintenance.cs: MaintenanceOrder / MaintenanceRecord / **MaintenanceRecordPart**(ロット単位の消費明細。`PartsUsed` の自由記述は補足) | `/maintenance` `Maintenance/MaintenanceOrdersTab.razor` | `Tests/MaintenanceTests.cs` | | 設備総合効率(OEE) | E-20-30-03 | `OeeController.cs` `api/oee`(**稼働サマリとは別**。あちらは稼働監視の素の時間区分、こちらは製造実績と突き合わせた評価指標)
負荷時間=記録済み総時間(**計画休止の区分は無い**)。理論CTは作業指示の標準作業時間(1個あたり) | Maintenance.cs: EquipmentLog(`WorkOrderId` が無い稼働は**対象外**=カバー率で開示)
Execution.cs: ProductionRecord | `/maintenance` `Maintenance/EquipmentLogsTab.razor`(稼働監視タブ) | `Tests/MaintenanceTests.cs` | diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index e03ce4c..ffa5e8d 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -8,7 +8,7 @@ | ID | 項目 | 区分 | 状態 | |:--|:--|:--|:--| | GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂129) | -| GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | 未着手 | +| GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **保全は対応済**(Spec.md 改訂130)。検査は `inspection-fixes` 合流後 | | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | 未着手 | | GR3-04 | 製造リードタイムの分布 | B. 指標 | 未着手 | | GR3-05 | 標準時間の見直し候補 | B. 指標 | 未着手 | @@ -37,6 +37,8 @@ SaaS型提供・オフライン同期(6.3.2)は配布方式(`Spec.md` 7.8 - 現状: `Spec.md` 3.6 は「差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合」と書くが、検査基準・保全計画に必要スキルの項目自体が無い。 - 対応: 項目を足して照合するか、`Spec.md` の記述を実装に合わせるかを決める。 +- **保全は対応済**(改訂130)。保全手順書に必要スキルを持たせ、保全実績の登録者を照合する。 + **検査は保留**:`inspection-fixes` ブランチ(未合流)が検査基準の選び方と `InspectionService` を大きく書き換えているため、合流後に検査基準(または検査項目)へ必要スキルを足し、検査実績の登録者を照合する。それまでは `Spec.md` 3.6 に未実装と明記した。 ### GR3-03 基本的な管理図と工程能力指数(C-50-10-01〜02) diff --git a/docs/maintenance.md b/docs/maintenance.md index 44bcb1a..757bc4a 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -112,6 +112,7 @@ next_title: 帳票・ラベル・スキャン
  • 手順書番号と表題を入力します。
  • 対象設備または対象治工具を選びます。
  • 手順ステップに作業手順を記入します(改行がそのまま保持されます)。
  • +
  • 実施に資格が要る手順(電気工事など)は 必要スキル・資格 を選びます。この手順書を使う保全指示では、実績を登録する人がその資格を持っていないか、期限が切れているとエラーになります。
  • 手順書を登録を押します。
  • diff --git a/src/MesApp.Api/Controllers/MaintenanceProceduresController.cs b/src/MesApp.Api/Controllers/MaintenanceProceduresController.cs index eb937f4..0890dc9 100644 --- a/src/MesApp.Api/Controllers/MaintenanceProceduresController.cs +++ b/src/MesApp.Api/Controllers/MaintenanceProceduresController.cs @@ -11,6 +11,7 @@ namespace MesApp.Api.Controllers; /// /// 保全手順書(E-10-20 作成・管理、E-20-30-04〜06 見直し)。更新のたびに版数を上げる。 +/// 必要スキル・資格を持たせると、保全実績の登録時に実施者と照合する(F-20-30-01)。 /// [ApiController] [Route("api/maintenance-procedures")] @@ -70,6 +71,7 @@ public async Task> Create( TargetEquipmentId = request.TargetEquipmentId, TargetToolId = request.TargetToolId, Steps = request.Steps, + RequiredSkillId = request.RequiredSkillId, }; db.MaintenanceProcedures.Add(procedure); await db.SaveChangesAsync(ct); @@ -103,6 +105,7 @@ public async Task> Update( procedure.TargetEquipmentId = request.TargetEquipmentId; procedure.TargetToolId = request.TargetToolId; procedure.Steps = request.Steps; + procedure.RequiredSkillId = request.RequiredSkillId; procedure.Version++; // 見直しの版数管理(E-20-30-06) await db.SaveChangesAsync(ct); await auditLogger.LogAsync("Maintenance", "ProcedureUpdate", nameof(MaintenanceProcedure), @@ -128,6 +131,10 @@ public Task Deactivate(int id, CancellationToken ct) => { return ApiText.T("存在しない対象治工具IDです。"); } + if (request.RequiredSkillId is int skillId && !await db.Skills.AnyAsync(s => s.Id == skillId, ct)) + { + return ApiText.T("存在しない必要スキルIDです。"); + } return null; } @@ -141,5 +148,6 @@ private static readonly System.Linq.Expressions.Expression< p.Id, p.ProcedureNo, p.Title, p.TargetEquipmentId, p.TargetEquipment!.Name, p.TargetToolId, p.TargetTool!.Name, - p.Steps, p.Version, p.IsActive); + p.Steps, p.Version, p.IsActive, + p.RequiredSkillId, p.RequiredSkill!.Name); } diff --git a/src/MesApp.Api/Localization/ApiText.en.resx b/src/MesApp.Api/Localization/ApiText.en.resx index 2387e5a..027c36d 100644 --- a/src/MesApp.Api/Localization/ApiText.en.resx +++ b/src/MesApp.Api/Localization/ApiText.en.resx @@ -2019,4 +2019,7 @@ Cannot identify the operator starting the work. + + The required skill ID does not exist. + diff --git a/src/MesApp.Api/Services/MaintenanceOrderService.cs b/src/MesApp.Api/Services/MaintenanceOrderService.cs index 13f43ec..1b7d5e2 100644 --- a/src/MesApp.Api/Services/MaintenanceOrderService.cs +++ b/src/MesApp.Api/Services/MaintenanceOrderService.cs @@ -86,6 +86,7 @@ await auditLogger.LogAsync("Maintenance", "OrderCreate", nameof(MaintenanceOrder /// /// 保全実績の登録(E-40-30-01)。登録と同時に指示は完了。元計画は完了、 /// 治工具メンテでresetToolLife指定時は寿命カウンタをリセットする(E-60-30)。 + /// 手順書に必要スキルがあれば実施者(登録する者)を照合する(F-20-30-01)。 /// public async Task> AddRecordAsync( int orderId, MaintenanceRecordRequest request, string userId, CancellationToken ct) @@ -108,6 +109,22 @@ public async Task> AddRecordAsync( return Outcome.Invalid(ApiText.T("寿命リセットは治工具メンテナンスの指示でのみ指定できます。")); } + // 手順書の必要スキル・資格を実施者と照合する(F-20-30-01)。差立・着手と同じ判定を使う + var requiredSkill = order.ProcedureId is int procedureId + ? await db.MaintenanceProcedures.Where(p => p.Id == procedureId) + .Select(p => p.RequiredSkill).FirstOrDefaultAsync(ct) + : null; + if (requiredSkill is not null) + { + var performer = await db.Users.FirstAsync(u => u.Id == userId, ct); + var userSkill = await db.UserSkills + .FirstOrDefaultAsync(s => s.UserId == userId && s.SkillId == requiredSkill.Id, ct); + if (SkillQualificationPolicy.Check(performer.DisplayName, requiredSkill, userSkill, businessDate.Today) is { } skillError) + { + return Outcome.Invalid(skillError); + } + } + var record = new MaintenanceRecord { MaintenanceOrderId = orderId, diff --git a/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceProceduresTab.razor b/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceProceduresTab.razor index f43fb18..5f092f6 100644 --- a/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceProceduresTab.razor +++ b/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceProceduresTab.razor @@ -1,6 +1,6 @@ @inject HttpClient Http -@* 保全手順書(Spec.md 3.1。E-10-20)。更新で版数が上がる *@ +@* 保全手順書(Spec.md 3.1。E-10-20)。更新で版数が上がる。必要スキルは保全実績の登録時に実施者と照合する(F-20-30-01) *@ @@ -35,6 +35,16 @@ } +
    + + +
    @@ -52,7 +62,7 @@ else { - + @foreach (var procedure in _procedures) { @@ -60,6 +70,7 @@ + @@ -76,11 +87,13 @@ public string Title { get; set; } = string.Empty; public int EquipmentId { get; set; } public int ToolId { get; set; } + public int SkillId { get; set; } public string Steps { get; set; } = string.Empty; } private List _equipments = []; private List _tools = []; + private List _skills = []; private List _procedures = []; private readonly ProcedureForm _procedure = new(); @@ -93,6 +106,7 @@ { _equipments = await Http.GetFromJsonAsync>("api/equipments") ?? []; _tools = await Http.GetFromJsonAsync>("api/tools") ?? []; + _skills = await Http.GetFromJsonAsync>("api/skills") ?? []; await LoadProceduresAsync(); } @@ -116,7 +130,8 @@ new MaintenanceProcedureRequest(_procedure.ProcedureNo, _procedure.Title, _procedure.EquipmentId == 0 ? null : _procedure.EquipmentId, _procedure.ToolId == 0 ? null : _procedure.ToolId, - _procedure.Steps)); + _procedure.Steps, + _procedure.SkillId == 0 ? null : _procedure.SkillId)); if (await response.ReadErrorAsync(L["登録に失敗しました。"]) is { } error) { _error = error; diff --git a/src/MesApp.Core/Contracts/Maintenance/MaintenanceContracts.cs b/src/MesApp.Core/Contracts/Maintenance/MaintenanceContracts.cs index a5020b8..4418e1a 100644 --- a/src/MesApp.Core/Contracts/Maintenance/MaintenanceContracts.cs +++ b/src/MesApp.Core/Contracts/Maintenance/MaintenanceContracts.cs @@ -10,13 +10,15 @@ public record MaintenanceProcedureRequest( [Required, MaxLength(200)] string Title, int? TargetEquipmentId, int? TargetToolId, - [Required, MaxLength(4000)] string Steps); + [Required, MaxLength(4000)] string Steps, + int? RequiredSkillId = null); public record MaintenanceProcedureResponse( int Id, string ProcedureNo, string Title, int? TargetEquipmentId, string? TargetEquipmentName, int? TargetToolId, string? TargetToolName, - string Steps, int Version, bool IsActive); + string Steps, int Version, bool IsActive, + int? RequiredSkillId = null, string? RequiredSkillName = null); // ---- 設備稼働履歴(B-40-20、E-20-10)---- diff --git a/src/MesApp.Core/Entities/Maintenance.cs b/src/MesApp.Core/Entities/Maintenance.cs index 4a3ff13..6265af9 100644 --- a/src/MesApp.Core/Entities/Maintenance.cs +++ b/src/MesApp.Core/Entities/Maintenance.cs @@ -20,6 +20,13 @@ public class MaintenanceProcedure : IDeactivatableMaster public int? TargetToolId { get; set; } public Tool? TargetTool { get; set; } + /// + /// 実施に必要なスキル・資格(F-20-30-01)。保全実績の登録時に実施者と照合する。 + /// 保全指示は手順書を版数ごと固定しないため、照合は手順書の現在値を使う + /// + public int? RequiredSkillId { get; set; } + public SkillMaster? RequiredSkill { get; set; } + /// 手順ステップ(テキスト。1行1ステップ等の自由書式) public string Steps { get; set; } = string.Empty; diff --git a/src/MesApp.Infrastructure/Configurations/MaintenanceConfigurations.cs b/src/MesApp.Infrastructure/Configurations/MaintenanceConfigurations.cs index 5596c54..8b1210b 100644 --- a/src/MesApp.Infrastructure/Configurations/MaintenanceConfigurations.cs +++ b/src/MesApp.Infrastructure/Configurations/MaintenanceConfigurations.cs @@ -32,6 +32,8 @@ public void Configure(EntityTypeBuilder e) e.Property(x => x.Steps).HasMaxLength(4000); e.HasOne(x => x.TargetEquipment).WithMany().HasForeignKey(x => x.TargetEquipmentId) .OnDelete(DeleteBehavior.Restrict); + e.HasOne(x => x.RequiredSkill).WithMany().HasForeignKey(x => x.RequiredSkillId) + .OnDelete(DeleteBehavior.Restrict); e.HasOne(x => x.TargetTool).WithMany().HasForeignKey(x => x.TargetToolId) .OnDelete(DeleteBehavior.Restrict); } diff --git a/src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.Designer.cs b/src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.Designer.cs new file mode 100644 index 0000000..06e7d12 --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.Designer.cs @@ -0,0 +1,4783 @@ +// +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("20260923112712_AddMaintenanceProcedureRequiredSkill")] + partial class AddMaintenanceProcedureRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("INTEGER"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ControlItemId") + .HasColumnType("INTEGER"); + + b.Property("RoutingId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923112712_AddMaintenanceProcedureRequiredSkill.cs b/src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.cs new file mode 100644 index 0000000..ad28320 --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260923112712_AddMaintenanceProcedureRequiredSkill.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Infrastructure.Migrations +{ + /// + public partial class AddMaintenanceProcedureRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures"); + } + } +} diff --git a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs index 19d0494..b4b2c65 100644 --- a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs @@ -1315,6 +1315,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(50) .HasColumnType("TEXT"); + b.Property("RequiredSkillId") + .HasColumnType("INTEGER"); + b.Property("Steps") .IsRequired() .HasMaxLength(4000) @@ -1339,6 +1342,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProcedureNo") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetEquipmentId"); b.HasIndex("TargetToolId"); @@ -3786,6 +3791,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") .WithMany() .HasForeignKey("TargetEquipmentId") @@ -3796,6 +3806,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("TargetToolId") .OnDelete(DeleteBehavior.Restrict); + b.Navigation("RequiredSkill"); + b.Navigation("TargetEquipment"); b.Navigation("TargetTool"); diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.Designer.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.Designer.cs new file mode 100644 index 0000000..3e3a9aa --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.Designer.cs @@ -0,0 +1,4982 @@ +// +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("20260923112714_AddMaintenanceProcedureRequiredSkill")] + partial class AddMaintenanceProcedureRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("integer"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("integer"); + + b.Property("RoutingId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .HasColumnType("character varying(1000)"); + + 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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923112714_AddMaintenanceProcedureRequiredSkill.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.cs new file mode 100644 index 0000000..8c2aadc --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260923112714_AddMaintenanceProcedureRequiredSkill.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.PostgreSql.Migrations +{ + /// + public partial class AddMaintenanceProcedureRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures"); + } + } +} diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs index e6882d5..d649da3 100644 --- a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs @@ -1392,6 +1392,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(50) .HasColumnType("character varying(50)"); + b.Property("RequiredSkillId") + .HasColumnType("integer"); + b.Property("Steps") .IsRequired() .HasMaxLength(4000) @@ -1416,6 +1419,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProcedureNo") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetEquipmentId"); b.HasIndex("TargetToolId"); @@ -3985,6 +3990,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") .WithMany() .HasForeignKey("TargetEquipmentId") @@ -3995,6 +4005,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("TargetToolId") .OnDelete(DeleteBehavior.Restrict); + b.Navigation("RequiredSkill"); + b.Navigation("TargetEquipment"); b.Navigation("TargetTool"); diff --git a/src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.Designer.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.Designer.cs new file mode 100644 index 0000000..2848178 --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.Designer.cs @@ -0,0 +1,4961 @@ +// +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("20260923112717_AddMaintenanceProcedureRequiredSkill")] + partial class AddMaintenanceProcedureRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("int"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("int"); + + b.Property("RoutingId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .HasColumnType("nvarchar(1000)"); + + 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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923112717_AddMaintenanceProcedureRequiredSkill.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.cs new file mode 100644 index 0000000..c0566f7 --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260923112717_AddMaintenanceProcedureRequiredSkill.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.SqlServer.Migrations +{ + /// + public partial class AddMaintenanceProcedureRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MaintenanceProcedures_Skills_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropIndex( + name: "IX_MaintenanceProcedures_RequiredSkillId", + table: "MaintenanceProcedures"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "MaintenanceProcedures"); + } + } +} diff --git a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs index 0bdd2e6..b0dfa19 100644 --- a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs @@ -1393,6 +1393,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(50) .HasColumnType("nvarchar(50)"); + b.Property("RequiredSkillId") + .HasColumnType("int"); + b.Property("Steps") .IsRequired() .HasMaxLength(4000) @@ -1417,6 +1420,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProcedureNo") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetEquipmentId"); b.HasIndex("TargetToolId"); @@ -3981,6 +3986,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.MaintenanceProcedure", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.Equipment", "TargetEquipment") .WithMany() .HasForeignKey("TargetEquipmentId") @@ -3991,6 +4001,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("TargetToolId") .OnDelete(DeleteBehavior.Restrict); + b.Navigation("RequiredSkill"); + b.Navigation("TargetEquipment"); b.Navigation("TargetTool"); diff --git a/tests/MesApp.Api.Tests/MaintenanceTests.cs b/tests/MesApp.Api.Tests/MaintenanceTests.cs index f95be01..3d7d07e 100644 --- a/tests/MesApp.Api.Tests/MaintenanceTests.cs +++ b/tests/MesApp.Api.Tests/MaintenanceTests.cs @@ -8,6 +8,7 @@ using MesApp.Core.Contracts.Masters; using MesApp.Core.Contracts.Production; using MesApp.Core.Contracts.Quality; +using MesApp.Core.Contracts.Users; using MesApp.Core.Entities; namespace MesApp.Api.Tests; @@ -169,6 +170,41 @@ public async Task 保全手順書のCRUDと版数管理ができる() Assert.Equal(2, updatedBody!.Version); } + [Fact] + public async Task 手順書の必要スキルを持たない者は保全実績を登録できない() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var equipment = await CreateEquipmentAsync(admin); + var skillCreated = await admin.PostAsJsonAsync("/api/skills", + new SkillRequest("SK-EL", "電気工事士", SkillType.Certification, true)); + var skill = (await skillCreated.Content.ReadFromJsonAsync())!; + + var created = await admin.PostAsJsonAsync("/api/maintenance-procedures", + new MaintenanceProcedureRequest("PROC-EL", "制御盤点検", equipment.Id, null, "1. 遮断\n2. 端子増し締め", skill.Id)); + var procedure = (await created.Content.ReadFromJsonAsync())!; + Assert.Equal("電気工事士", procedure.RequiredSkillName); + + var orderCreated = await admin.PostAsJsonAsync("/api/maintenance-orders", + new MaintenanceOrderCreateRequest(equipment.Id, null, null, procedure.Id, null, + MaintenanceRequestType.Spot, "制御盤の異音")); + var order = (await orderCreated.Content.ReadFromJsonAsync())!; + var record = new MaintenanceRecordRequest(DateTimeOffset.Now.AddHours(-1), DateTimeOffset.Now, null, "点検完了", null); + + // 資格なし → 400、指示は完了しない + var rejected = await admin.PostAsJsonAsync($"/api/maintenance-orders/{order.Id}/record", record); + Assert.Equal(HttpStatusCode.BadRequest, rejected.StatusCode); + + // 資格を付与すると登録できる + var users = await admin.GetFromJsonAsync>("/api/users"); + var me = users!.Single(u => u.UserName == TestAuth.AdminUser); + (await admin.PutAsJsonAsync($"/api/users/{me.Id}/skills", + new List { new(skill.Id, null, DateOnly.FromDateTime(DateTime.Today).AddYears(1)) })) + .EnsureSuccessStatusCode(); + var recorded = await admin.PostAsJsonAsync($"/api/maintenance-orders/{order.Id}/record", record); + Assert.Equal(HttpStatusCode.OK, recorded.StatusCode); + } + /// 消耗品を1品目登録した設備と、その在庫ロットを用意する private static async Task<(EquipmentResponse Equipment, ProductResponse Part, LotResponse Lot, int LocationId)> SetupConsumablePartAsync(HttpClient admin, decimal stockQuantity) From 154c07db74f5eb0fb45ca09023c6c61c2468ba69 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:48:12 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=9B=B3=EF=BC=88X=CC=84?= =?UTF-8?q?-R=EF=BC=8FX-Rs=EF=BC=89=E3=81=A8=E5=B7=A5=E7=A8=8B=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E6=8C=87=E6=95=B0=E3=82=92=E5=93=81=E8=B3=AA=E5=88=86?= =?UTF-8?q?=E6=9E=90=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=EF=BC=88?= =?UTF-8?q?GR3-03=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec.md 3.3・6.1 は基本的な管理図を実装対象としていたが実装が無かった。 検査実績の測定値を検査指示ごとの群にまとめ、管理限界外と9点連続を印し、 Cp・Cpk を群内のばらつき(R̄/d2)から求める。新規エンティティなし。Spec.md 改訂131。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 6 +- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 2 +- docs/quality.md | 15 ++ .../Controllers/ControlChartController.cs | 75 ++++++++ .../Controllers/QualityAnalysisController.cs | 2 +- .../Services/ControlChartCalculator.cs | 143 ++++++++++++++++ .../Pages/QualityAnalysis.razor | 121 ++++++++++++- .../Shared/Charts/ControlLineChart.razor | 160 ++++++++++++++++++ src/MesApp.Client.Web/Shared/UiText.en.resx | 66 +++++++- .../Contracts/Quality/QualityContracts.cs | 48 ++++++ .../ControlChartCalculatorTests.cs | 96 +++++++++++ tests/MesApp.Api.Tests/QualityTests.cs | 47 +++++ 13 files changed, 771 insertions(+), 12 deletions(-) create mode 100644 src/MesApp.Api/Controllers/ControlChartController.cs create mode 100644 src/MesApp.Api/Services/ControlChartCalculator.cs create mode 100644 src/MesApp.Client.Web/Shared/Charts/ControlLineChart.razor create mode 100644 tests/MesApp.Api.Tests/ControlChartCalculatorTests.cs diff --git a/Spec.md b/Spec.md index 9a86b33..19800d5 100644 --- a/Spec.md +++ b/Spec.md @@ -125,6 +125,7 @@ 改訂124: 2026-09-23(工程管理項目を工順(BOP)の工程ごとに紐付けるようにした(`RoutingControlItem`。I-30-20-10)。これまでは項目側の対象品目/対象工程に「または」で合致する項目を展開時に自動選択していたため、品目を指定した項目がその品目の全工程の作業指示に載り、品目と工程の組合せや、同じ工程が2回ある工順の工程ごとの条件を表せなかった。改訂48の「工順に明示的な紐付けを持たせない」方針を改め、`ControlItem` から対象品目・対象工程を削除した(作業手順書と同じく紐付けは工順側だけに持つ)。紐付けられるのは有効な項目だけで、紐付け中の項目は単票APIでもCSV取込でも無効化できない(`MasterDeactivationPolicy`)。API は `RoutingStepRequest.ControlItemIds`、CSV は工程管理項目から `TargetProductCode`・`TargetProcessCode` を外し、工順に `ControlItemCodes`(セミコロン区切り)を追加。`Routing.ControlItems` の自由記述は補足として残す。5.1節・5.7節に反映) 改訂129: 2026-09-23(**着手時にもスキル・資格を照合する**ようにした(F-20-30-01)。照合は差立(作業員割当)にしか無く、着手は差立を省略できる(小規模運用)ため、差立を省いた運用・割当者と異なる者の着手・差立後の有効期限切れのいずれでも、必要スキルを持たない者が作業できていた。照合するのは**割当者ではなく実際に着手する者**。判定は `Api/Policies/SkillQualificationPolicy` に集約し、差立と着手で同じ条件を使う。3.2節・3.6節に反映。改訂125〜128は別ブランチ(検査の見直し)で使用中のため欠番にしない) 改訂130: 2026-09-23(**保全手順書に必要スキル・資格を持たせ、保全実績の登録時に実施者と照合する**ようにした(`MaintenanceProcedure.RequiredSkillId`。F-20-30-01)。3.6節は「差立・検査・保全の各業務で照合」と書いていたが、保全・検査には必要スキルの項目自体が無かった。保全指示は手順書を版数ごと固定しないため、照合は手順書の現在値を使う。判定は差立・着手と同じ `SkillQualificationPolicy`。**検査側は未実装のまま3.6節にそう明記した**(検査基準の見直しが別ブランチで進行中のため、合流後に扱う)。5.1節・3.6節に反映) +改訂131: 2026-09-23(**管理図と工程能力指数を実装した**(`api/quality/control-chart`。C-50-10-01〜02)。3.3節は「初期は基本的な管理図表示」を実装対象とし、6.1節の品質分析画面にも管理図があったが、実装は無かった。検査実績の測定値を検査指示ごとの群にまとめ、X̄-R(群の大きさ1なら X-Rs)管理図と Cp・Cpk を返す。新規エンティティは無い。計算は `Api/Services/ControlChartCalculator`(係数表は JIS Z 9020-2)。品質分析画面に検査項目を選ぶ管理図の欄を追加した。3.3節・6.1節に反映) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -272,6 +273,9 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 - **品質分析**:品質異常検知(C-40-10-01)、品質記録参照(C-40-10-02)、不良項目別・工程別・**直別**・期間別の集計分析(C-40-10-03。直別は3.9節の製造日が夜勤を前提にしているため、昼勤と夜勤の不良率を比べられるようにする)、不適合・逸脱発生状況のモニタリング(C-40-10-05) - **不良項目別分析**:不良理由マスタ(C-10)に基づく不良理由別の不良数・構成比・**累積構成比**の集計(C-40-10-01)。数量の多い順に並べ**パレート図として読める**ようにし、改善対象の選定に使う - **品質傾向管理**:管理図等による傾向分析に基づく検査判定(SPC。C-50-10-01〜02。初期は基本的な管理図表示、本格的SPC・予兆管理(C-50-10-03)は[将来拡張]) + - **管理図**(`api/quality/control-chart`):検査項目ごとに、検査実績の測定値を**検査指示ごとの群**にまとめて X̄-R 管理図を描く。群の大きさが1なら個別値-移動範囲(X-Rs)管理図にする。群の大きさは最も多い大きさに揃え、**違う大きさの群は除外して件数を示す**(係数の違う群を混ぜると管理限界が意味を失うため)。大きさ11以上(X̄-s 管理図)は扱わず点だけを示す。取消した検査の測定値は使わない。期間は測定日時が属する製造日 + - 異常の判定は**管理限界の外**と、**中心線の同じ側に9点以上続く並び**(JIS Z 9020-2 のルール2)の2つだけ。群が2つ未満なら管理限界を出さない + - **工程能力指数**(Cp・Cpk)は**群内のばらつき(R̄/d2)**から推定する。全測定値の標準偏差を使うと群間の変動(工程の異常)までばらつきに入り、管理状態でない工程の Cpk が良く見えるため。規格値は検査指示が発行時に写した値(5.7節)で、期間内に改訂があれば最新の群の規格で求めてその旨を示す。規格が片側だけなら Cp は出さず Cpk を片側で求める - **グレード管理**:検査結果により出荷先・品目が変わる製品の管理(C-60-10-01。ロットのグレード属性で表現) ### 3.4 物流/在庫管理〔D〕 @@ -598,7 +602,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと 14. 棚卸画面(棚卸指示、実棚入力、差異一覧・調整・確定) 15. 検査管理画面(受入/工程内/完成品/サンプル検査の進捗一覧、検査指示発行、実績入力、判定、成績書発行、承認、取消) 16. 不適合管理画面(逸脱/不具合一覧、対応指示・実行記録、特別採用承認) -17. 品質分析画面(不良集計、品質モニタリング、管理図) +17. 品質分析画面(不良集計、品質モニタリング、管理図と工程能力指数) 18. 出荷判定画面(判定一覧、判定・承認、判定書発行) 19. トレーサビリティ画面(ロット番号によるトレースバック/フォワード、製造・設備・検査履歴閲覧) 20. 設備保全画面(設備台帳、稼働履歴、**稼働サマリ・OEE**、保全計画・指示・実績(消費部材の在庫引落しを含む)、保全手順書、消耗材モニタリング) diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index 48bd598..3168167 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -49,7 +49,7 @@ | 検査項目・基準マスタ | C-10-10 | `InspectionItemsController.cs` `api/inspection-items` | Masters.cs: InspectionItem | `/masters` `Masters/InspectionItemsTab.razor` | `Tests/MasterTests.cs` | | 検査指示・実績・判定・成績書 | C-20 | `InspectionOrdersController.cs` `api/inspection-orders`(発行・実績・判定の処理は `Api/Services/InspectionService.cs`。実績登録時に**検査機の校正期限を判定**:`InspectionDeviceCalibrationPolicy`) | `Core/Entities/Quality.cs`
    InspectionOrder / InspectionOrderItem(**発行時点の基準スナップショット**。判定・成績書はこちらを使い、マスタ現在値を参照しない:Spec.md 5.7) / InspectionResult / **InspectionResultCorrection**(訂正前の記録。詳細画面・成績書に表示) | `/inspections` `Inspections.razor`
    `/inspections/{id}` `InspectionDetail.razor`
    `/print/inspection/{id}` `Print/InspectionCertificate.razor` | `Tests/QualityTests.cs` | | 不適合・逸脱管理(特採・廃棄・保留) | C-30 / B-40-30 | `NonconformanceController.cs` `api/nonconformances`
    記録・対応指示・承認と不適合の連鎖は `Api/Services/NonconformanceService.cs` | Quality.cs: NonconformanceReport | `/nonconformances` `Nonconformances.razor` | `Tests/QualityTests.cs` | -| 品質分析(不良理由別・品目別・工程別・**直別**・期間別) | C-40-10 | `QualityAnalysisController.cs` `api/quality/summary` | Execution.cs: ProductionDefect(不良理由別の内訳)
    ProductionRecord.`ShiftId`(**記録時に固定**。集計で引き直さない) | `/quality-analysis` `QualityAnalysis.razor`
    `Web/Shared/BarMeter.razor` | `Tests/QualityTests.cs` | +| 品質分析(不良理由別・品目別・工程別・**直別**・期間別) | C-40-10 | `QualityAnalysisController.cs` `api/quality/summary`
    管理図・工程能力(C-50-10-01〜02)は `ControlChartController.cs` `api/quality/control-chart` → 計算は `Api/Services/ControlChartCalculator.cs`(**群=検査指示**。係数表・群の揃え方・9点連続の判定はここだけ)。描画は `Web/Shared/Charts/ControlLineChart.razor` | Execution.cs: ProductionDefect(不良理由別の内訳)
    ProductionRecord.`ShiftId`(**記録時に固定**。集計で引き直さない) | `/quality-analysis` `QualityAnalysis.razor`
    `Web/Shared/BarMeter.razor` | `Tests/QualityTests.cs`・`ControlChartCalculatorTests.cs` | | 不良理由マスタ | C-40-10-01 | `DefectReasonsController.cs` `api/defect-reasons` | Masters.cs: DefectReason | `/masters` `Masters/DefectReasonsTab.razor` | `Tests/MasterTests.cs` / `MasterCsvTests.cs` | | チェックリストマスタ(HSE含む) | B-30-10 / G-20-20-02 | `ChecklistsController.cs` `api/checklists` | Masters.cs: Checklist / ChecklistItem | `/masters` `Masters/ChecklistsTab.razor` | `Tests/MasterTests.cs` | diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index ffa5e8d..25f82ae 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -9,7 +9,7 @@ |:--|:--|:--|:--| | GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂129) | | GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **保全は対応済**(Spec.md 改訂130)。検査は `inspection-fixes` 合流後 | -| GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | 未着手 | +| GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂131) | | GR3-04 | 製造リードタイムの分布 | B. 指標 | 未着手 | | GR3-05 | 標準時間の見直し候補 | B. 指標 | 未着手 | | GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | 未着手 | diff --git a/docs/quality.md b/docs/quality.md index 285c3c4..aecd100 100644 --- a/docs/quality.md +++ b/docs/quality.md @@ -199,6 +199,21 @@ next_title: 品質保証 | 品目別の不良集計 | 品目ごとの良品数・不良数・不良率 | | 工程別の不良集計 | 工程ごとの良品数・不良数・不良率 | | 不適合の原因区分別 発生件数 | 起票時に入力した原因区分ごとの件数 | +| 管理図と工程能力 | 選んだ検査項目の測定値の管理図と、工程能力指数(Cp・Cpk) | 開始日・終了日を入れて **集計** を押すと再計算されます。日付を空欄にすると全期間が対象です。 未クローズの不適合がある場合は「要対応」バッジが表示されます。 + +### 管理図と工程能力 + +**検査項目(定量)** を選ぶと、その項目の測定値の管理図が表示されます。 + +- 1つの検査指示で測った値を1つの点(群)にまとめます。1回の検査で2個以上測る項目は X̄-R 管理図(平均値と範囲)、1個だけ測る項目は X-Rs 管理図(個別値と移動範囲)になります +- **赤い点**は管理限界(UCL・LCL)の外、**黄色い点**は中心線の同じ側に9点以上続いている並びです。どちらも工程に何かが起きているサインです +- 規格値(規格上限・規格下限)は、検査指示を出したときの検査基準の値です +- **Cp・Cpk** は工程能力指数です。1.33 未満のときは印が付きます。群が2つ以上ないと計算できません + +
    +

    1回の検査で測る個数(サンプル数)はそろえてください。 +個数の違う群は同じ管理図に混ぜられないため、いちばん多い個数に合わない群は除外され、除外した数が表示されます。

    +
    diff --git a/src/MesApp.Api/Controllers/ControlChartController.cs b/src/MesApp.Api/Controllers/ControlChartController.cs new file mode 100644 index 0000000..fdbd782 --- /dev/null +++ b/src/MesApp.Api/Controllers/ControlChartController.cs @@ -0,0 +1,75 @@ +using MesApp.Api.Services; +using MesApp.Core.Abstractions; +using MesApp.Core.Contracts.Quality; +using MesApp.Core.Entities; +using MesApp.Infrastructure; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace MesApp.Api.Controllers; + +/// +/// 管理図と工程能力指数(Spec.md 3.3 品質傾向管理。C-50-10-01〜02)。 +/// 検査実績の測定値を検査指示ごとの群にまとめ、X̄-R(群の大きさ1なら X-Rs)管理図を返す。 +/// 期間は測定日時が属する製造日(業務日付)。計算は 。 +/// 本格的なSPC・予兆管理(C-50-10-03)は将来拡張。 +/// +[ApiController] +[Route("api/quality/control-chart")] +[Authorize] +public class ControlChartController(MesAppDbContext db, IBusinessDateService businessDate) : ControllerBase +{ + [HttpGet] + public async Task> Get( + [FromQuery] int inspectionItemId, + [FromQuery] DateOnly? from = null, + [FromQuery] DateOnly? to = null, + CancellationToken ct = default) + { + var item = await db.InspectionItems.AsNoTracking() + .Where(i => i.Id == inspectionItemId) + .Select(i => new { i.Id, i.Code, i.Name }) + .FirstOrDefaultAsync(ct); + if (item is null) + { + return NotFound(); + } + + // SQLiteはDateTimeOffsetの比較を翻訳できないため、期間フィルタはクライアント側で行う + var fromStart = from is null ? (DateTimeOffset?)null : businessDate.GetRange(from.Value).Start; + var toEnd = to is null ? (DateTimeOffset?)null : businessDate.GetRange(to.Value).End; + + // 取消した検査の測定値は使わない。規格値は指示発行時のスナップショット(Spec.md 5.7) + var results = (await db.InspectionResults.AsNoTracking() + .Where(r => r.InspectionItemId == inspectionItemId && r.MeasuredValue != null) + .Join(db.InspectionOrders.Where(o => o.Status != InspectionOrderStatus.Canceled), + r => r.InspectionOrderId, o => o.Id, + (r, o) => new { r.InspectionOrderId, o.OrderNo, r.InspectedAt, Value = r.MeasuredValue!.Value }) + .ToListAsync(ct)) + .Where(r => (fromStart is null || r.InspectedAt >= fromStart) + && (toEnd is null || r.InspectedAt < toEnd)) + .ToList(); + + var orderIds = results.Select(r => r.InspectionOrderId).Distinct().ToList(); + var specs = await db.InspectionOrders.AsNoTracking() + .Where(o => orderIds.Contains(o.Id)) + .SelectMany(o => o.Items.Where(i => i.InspectionItemId == inspectionItemId)) + .Select(i => new { i.InspectionOrderId, i.LowerLimit, i.UpperLimit }) + .ToListAsync(ct); + var specByOrder = specs.GroupBy(s => s.InspectionOrderId).ToDictionary(g => g.Key, g => g.First()); + + var subgroups = results + .GroupBy(r => (r.InspectionOrderId, r.OrderNo)) + .Select(g => + { + var first = g.Min(r => r.InspectedAt); + specByOrder.TryGetValue(g.Key.InspectionOrderId, out var spec); + return new ControlChartSubgroup(g.Key.OrderNo, first, businessDate.GetBusinessDate(first), + g.Select(r => r.Value).ToList(), spec?.LowerLimit, spec?.UpperLimit); + }) + .ToList(); + + return ControlChartCalculator.Calculate(item.Id, item.Code, item.Name, subgroups); + } +} diff --git a/src/MesApp.Api/Controllers/QualityAnalysisController.cs b/src/MesApp.Api/Controllers/QualityAnalysisController.cs index 50ee040..7e023bb 100644 --- a/src/MesApp.Api/Controllers/QualityAnalysisController.cs +++ b/src/MesApp.Api/Controllers/QualityAnalysisController.cs @@ -11,7 +11,7 @@ namespace MesApp.Api.Controllers; /// /// 品質分析(Spec.md 3.3:C-40-10 不良項目別・工程別・期間別の集計、不適合発生状況のモニタリング)。 -/// 期間は製造日(業務日付)基準。SPC・管理図(C-50-10)は将来拡張。 +/// 期間は製造日(業務日付)基準。管理図(C-50-10-01〜02)は `ControlChartController`。 /// [ApiController] [Route("api/quality/summary")] diff --git a/src/MesApp.Api/Services/ControlChartCalculator.cs b/src/MesApp.Api/Services/ControlChartCalculator.cs new file mode 100644 index 0000000..be10b52 --- /dev/null +++ b/src/MesApp.Api/Services/ControlChartCalculator.cs @@ -0,0 +1,143 @@ +using MesApp.Core.Contracts.Quality; + +namespace MesApp.Api.Services; + +/// 管理図の1群の入力(検査指示ごとの測定値と、その指示が発行時に写した規格値) +public sealed record ControlChartSubgroup( + string OrderNo, DateTimeOffset InspectedAt, DateOnly BusinessDate, + IReadOnlyList Values, decimal? LowerSpecLimit, decimal? UpperSpecLimit); + +/// +/// 管理図と工程能力指数の計算(Spec.md 3.3。C-50-10-01〜02。JIS Z 9020-2 のシューハート管理図)。 +/// +/// 群の大きさが1なら個別値-移動範囲管理図(X-Rs)、2〜10なら X̄-R 管理図にする。 +/// 群の大きさは最も多い大きさに揃え、違う大きさの群は除外して件数を返す +/// (大きさが違う群を同じ係数で混ぜると管理限界が意味を失う。黙って捨てずに除外数を見せる)。 +/// 群の大きさが11以上の X̄-s 管理図は扱わず、点だけを返す。 +/// +/// +/// 標準偏差は群内のばらつき(R̄/d2)から推定する。全測定値の標準偏差を使うと群間の変動(工程の異常)まで +/// ばらつきに含まれ、管理状態でない工程の Cpk が実際より良く見えるため。 +/// +/// +public static class ControlChartCalculator +{ + /// 中心線の同じ側に続くと異常とみなす点の数(JIS Z 9020-2 のルール2) + public const int RunLength = 9; + + /// X̄-R 管理図の係数(群の大きさ n → A2・D3・D4・d2)。n=2 の値は移動範囲(X-Rs)にも使う + private static readonly Dictionary Factors = new() + { + [2] = (1.880m, 0m, 3.267m, 1.128m), + [3] = (1.023m, 0m, 2.574m, 1.693m), + [4] = (0.729m, 0m, 2.282m, 2.059m), + [5] = (0.577m, 0m, 2.114m, 2.326m), + [6] = (0.483m, 0m, 2.004m, 2.534m), + [7] = (0.419m, 0.076m, 1.924m, 2.704m), + [8] = (0.373m, 0.136m, 1.864m, 2.847m), + [9] = (0.337m, 0.184m, 1.816m, 2.970m), + [10] = (0.308m, 0.223m, 1.777m, 3.078m), + }; + + /// 個別値管理図の係数 E2(= 3 / d2(n=2)) + private const decimal E2 = 2.660m; + + public static ControlChartResponse Calculate( + int itemId, string itemCode, string itemName, IReadOnlyList subgroups) + { + var ordered = subgroups.Where(s => s.Values.Count > 0).OrderBy(s => s.InspectedAt).ToList(); + var size = ordered.Count == 0 ? 1 + : ordered.GroupBy(s => s.Values.Count) + .OrderByDescending(g => g.Count()).ThenBy(g => g.Key).First().Key; + var used = ordered.Where(s => s.Values.Count == size).ToList(); + var excluded = ordered.Count - used.Count; + var individuals = size == 1; + + var means = used.Select(s => s.Values.Average()).ToList(); + var ranges = individuals + ? means.Select((m, i) => i == 0 ? (decimal?)null : Math.Abs(m - means[i - 1])).ToList() + : used.Select(s => (decimal?)(s.Values.Max() - s.Values.Min())).ToList(); + + var latest = used.Count > 0 ? used[^1] : null; + var lsl = latest?.LowerSpecLimit; + var usl = latest?.UpperSpecLimit; + var specChanged = used.Any(s => s.LowerSpecLimit != lsl || s.UpperSpecLimit != usl); + + decimal? cl = null, ucl = null, lcl = null, rcl = null, rucl = null, rlcl = null, sigma = null, cp = null, cpk = null; + var factorKey = individuals ? 2 : size; + if (used.Count >= 2 && Factors.TryGetValue(factorKey, out var f)) + { + var xbar = means.Average(); + var rbar = ranges.Where(r => r is not null).Average(r => r!.Value); + var width = (individuals ? E2 : f.A2) * rbar; + cl = Round(xbar); + ucl = Round(xbar + width); + lcl = Round(xbar - width); + rcl = Round(rbar); + rucl = Round(f.D4 * rbar); + rlcl = Round(f.D3 * rbar); + if (rbar > 0) + { + var s = rbar / f.D2; + sigma = Round(s); + if (lsl is { } lo && usl is { } hi) + { + cp = Math.Round((hi - lo) / (6 * s), 2); + } + var sides = new List(); + if (usl is { } upper) + { + sides.Add((upper - xbar) / (3 * s)); + } + if (lsl is { } lower) + { + sides.Add((xbar - lower) / (3 * s)); + } + cpk = sides.Count == 0 ? null : Math.Round(sides.Min(), 2); + } + } + + var inRun = RunFlags(means, cl); + var points = used.Select((s, i) => new ControlChartPoint( + s.OrderNo, s.InspectedAt, s.BusinessDate, s.Values.Count, + Round(means[i]), ranges[i] is { } r ? Round(r) : null, + MeanOutOfControl: ucl is not null && (means[i] > ucl || means[i] < lcl), + RangeOutOfControl: rucl is not null && ranges[i] is { } range && (range > rucl || range < rlcl), + InRun: inRun[i])) + .ToList(); + + return new ControlChartResponse(itemId, itemCode, itemName, individuals, size, + cl, ucl, lcl, rcl, rucl, rlcl, lsl, usl, specChanged, sigma, cp, cpk, excluded, points); + } + + /// 中心線の同じ側に 点以上続く並びに属する点に印を付ける(中心線上の点は並びを切る) + private static bool[] RunFlags(IReadOnlyList means, decimal? center) + { + var flags = new bool[means.Count]; + if (center is not { } c) + { + return flags; + } + var start = 0; + for (var i = 0; i <= means.Count; i++) + { + var continues = i < means.Count && i > start + && Math.Sign(means[i] - c) == Math.Sign(means[start] - c) && means[i] != c; + if (continues) + { + continue; + } + if (i - start >= RunLength && means[start] != c) + { + for (var k = start; k < i; k++) + { + flags[k] = true; + } + } + start = i; + } + return flags; + } + + private static decimal Round(decimal value) => Math.Round(value, 4); +} diff --git a/src/MesApp.Client.Web/Pages/QualityAnalysis.razor b/src/MesApp.Client.Web/Pages/QualityAnalysis.razor index 9cc8320..9422e8d 100644 --- a/src/MesApp.Client.Web/Pages/QualityAnalysis.razor +++ b/src/MesApp.Client.Web/Pages/QualityAnalysis.razor @@ -5,7 +5,7 @@ @L["品質分析 - Parallel Factory MES"]

    @L["品質分析"]

    -

    @L["不良項目別・工程別・直別・期間別の集計(C-40-10-03)、不適合・逸脱発生状況のモニタリング(C-40-10-05)。期間は製造日(業務日付)基準。"]

    +

    @L["不良項目別・工程別・直別・期間別の集計(C-40-10-03)、不適合・逸脱発生状況のモニタリング(C-40-10-05)、管理図と工程能力(C-50-10-01〜02)。期間は製造日(業務日付)基準。"]

    @@ -194,10 +194,92 @@ else
    @L["手順書番号"]@L["表題"]@L["対象"]@L["版数"]@L["手順"]
    @L["手順書番号"]@L["表題"]@L["対象"]@L["必要スキル・資格"]@L["版数"]@L["手順"]
    @procedure.ProcedureNo @procedure.Title @(procedure.TargetEquipmentName ?? procedure.TargetToolName ?? "-")@(procedure.RequiredSkillName ?? "-") v@(procedure.Version) @procedure.Steps
    }
    + + @* 管理図と工程能力指数(C-50-10-01〜02)。群は検査指示ごと、期間は上の製造日 *@ +
    +

    @L["管理図と工程能力"]

    +

    + @L["検査実績の測定値を検査指示ごとの群にまとめ、X̄-R 管理図(群の大きさが1なら X-Rs 管理図)を描く。赤は管理限界の外、黄は中心線の同じ側に9点以上続く並び。工程能力指数は群内のばらつきから求める。"] +

    +
    + + +
    + @if (_chart is not null) + { + @if (_chart.Points.Count == 0) + { +

    @L["対象期間の測定値はありません。"]

    + } + else + { + + + + + + + + + + + + + + + +
    @L["管理図"]@(_chart.IsIndividuals ? L["X-Rs 管理図"] : L["X̄-R 管理図(群の大きさ {0})", _chart.SubgroupSize])@L["群の数"] + @_chart.Points.Count + @if (_chart.ExcludedSubgroupCount > 0) + { + @L["(大きさの違う {0} 群を除外)", _chart.ExcludedSubgroupCount] + } +
    Cp@(_chart.Cp?.ToString("0.00") ?? "-")Cpk + @(_chart.Cpk?.ToString("0.00") ?? "-") + @if (_chart.Cpk is { } cpk && cpk < 1.33m) + { + @L["1.33 未満"] + } +
    + @if (_chart.CenterLine is null) + { +

    @L["群が2つ以上ないと管理限界と工程能力は求められません。"]

    + } + @if (_chart.SpecLimitsChanged) + { +

    @L["期間内に規格値が改訂されています。工程能力は最新の規格値で求めています。"]

    + } +

    @(_chart.IsIndividuals ? L["個別値(X)"] : L["平均値(X̄)"])

    + +

    @(_chart.IsIndividuals ? L["移動範囲(Rs)"] : L["範囲(R)"])

    + + } + } +
    } @code { private QualitySummaryResponse? _summary; + private List _chartItems = []; + private int _chartItemId; + private ControlChartResponse? _chart; + + private List ChartLabels => _chart?.Points.Select(p => p.BusinessDate.ToString("MM/dd")).ToList() ?? []; private DateTime? _from; private DateTime? _to; @@ -205,9 +287,33 @@ else ? _summary.NonconformanceByCause.Values.Max() : 0; - protected override async Task OnInitializedAsync() => await LoadAsync(); + protected override async Task OnInitializedAsync() + { + // 管理図は測定値を持つ(規格値のある)項目だけを選べるようにする + _chartItems = (await Http.GetFromJsonAsync>("api/inspection-items") ?? []) + .Where(i => i.LowerLimit is not null || i.UpperLimit is not null) + .ToList(); + await LoadAsync(); + } - private async Task LoadAsync() + private async Task OnChartItemChangedAsync(ChangeEventArgs e) + { + _chartItemId = int.TryParse(e.Value?.ToString(), out var id) ? id : 0; + await LoadChartAsync(); + } + + private async Task LoadChartAsync() + { + if (_chartItemId == 0) + { + _chart = null; + return; + } + _chart = await Http.GetFromJsonAsync( + $"api/quality/control-chart?inspectionItemId={_chartItemId}" + PeriodQuery("&")); + } + + private string PeriodQuery(string prefix) { var query = new List(); if (_from is not null) @@ -218,7 +324,12 @@ else { query.Add($"to={DateOnly.FromDateTime(_to.Value):yyyy-MM-dd}"); } - var url = "api/quality/summary" + (query.Count > 0 ? "?" + string.Join("&", query) : string.Empty); - _summary = await Http.GetFromJsonAsync(url); + return query.Count > 0 ? prefix + string.Join("&", query) : string.Empty; + } + + private async Task LoadAsync() + { + _summary = await Http.GetFromJsonAsync("api/quality/summary" + PeriodQuery("?")); + await LoadChartAsync(); } } diff --git a/src/MesApp.Client.Web/Shared/Charts/ControlLineChart.razor b/src/MesApp.Client.Web/Shared/Charts/ControlLineChart.razor new file mode 100644 index 0000000..daa981b --- /dev/null +++ b/src/MesApp.Client.Web/Shared/Charts/ControlLineChart.razor @@ -0,0 +1,160 @@ +@* 管理図の1枚(Spec.md 3.3。C-50-10-01〜02)。点を折れ線で結び、中心線・管理限界・規格値を横線で引く。 + 管理限界の外の点と、中心線の同じ側に続く並びの点を色で示す。値の計算はせず、渡された値を描くだけ *@ + +
    + + @foreach (var tick in _ticks) + { + var y = Y(tick); + + @ChartScale.Text(Left - 6, y + 3, ChartScale.Number(tick), "end") + } + + @foreach (var (value, name, color, dash) in Guides()) + { + var y = Y(value); + + @name @ChartScale.Number(value) + + @ChartScale.Text(Left + PlotWidth + 4, y + 3, name, "start", $"font-size:10px; fill:{color};") + } + + @foreach (var segment in Segments()) + { + @if (segment.Count > 1) + { + + } + } + @for (var i = 0; i < Labels.Count; i++) + { + @if (Values[i] is { } value) + { + var color = OutOfControl.Count > i && OutOfControl[i] ? "var(--danger)" + : InRun.Count > i && InRun[i] ? "var(--warning)" : "var(--primary)"; + + @Labels[i] @ChartScale.Number(value) + + } + @if (i % LabelEvery == 0) + { + @ChartScale.Text(X(i), Height - 8, Labels[i]) + } + } + +
    + +@code { + /// 横軸のラベル(点の数だけ) + [Parameter, EditorRequired] public IReadOnlyList Labels { get; set; } = []; + + /// 点の値。null の点は描かず、線もつなげない + [Parameter, EditorRequired] public IReadOnlyList Values { get; set; } = []; + + /// 管理限界の外の点(赤で示す) + [Parameter] public IReadOnlyList OutOfControl { get; set; } = []; + + /// 中心線の同じ側に続く並びの点(黄で示す) + [Parameter] public IReadOnlyList InRun { get; set; } = []; + + [Parameter] public decimal? Center { get; set; } + [Parameter] public decimal? Upper { get; set; } + [Parameter] public decimal? Lower { get; set; } + [Parameter] public decimal? SpecUpper { get; set; } + [Parameter] public decimal? SpecLower { get; set; } + + [Parameter] public string? AriaLabel { get; set; } + + private const decimal Height = 212m; + private const decimal Top = 16m; + private const decimal Bottom = 188m; + private const decimal Left = 56m; + private const decimal Right = 52m; + + private IReadOnlyList _ticks = [0m, 1m]; + private decimal _min; + private decimal _max = 1m; + + private decimal PlotWidth => Math.Max(Labels.Count * 28m, 480m); + private decimal Width => Left + PlotWidth + Right; + private decimal Slot => Labels.Count == 0 ? PlotWidth : PlotWidth / Labels.Count; + private int LabelEvery => Math.Max(1, (int)Math.Ceiling(Labels.Count / 12m)); + + protected override void OnParametersSet() + { + // 管理図は0から描くと変動が潰れるので、点・限界・規格の範囲に合わせて縦軸を取る + var all = Values.Where(v => v is not null).Select(v => v!.Value) + .Concat(new[] { Center, Upper, Lower, SpecUpper, SpecLower }.Where(v => v is not null).Select(v => v!.Value)) + .ToList(); + var min = all.Count == 0 ? 0m : all.Min(); + var max = all.Count == 0 ? 1m : all.Max(); + if (max == min) + { + max += 1m; + min -= 1m; + } + var step = ChartScale.NiceStep((max - min) / 5); + _min = Math.Floor(min / step) * step; + _max = Math.Ceiling(max / step) * step; + var ticks = new List(); + for (var t = _min; t <= _max; t += step) + { + ticks.Add(t); + } + _ticks = ticks; + } + + private decimal Y(decimal value) => Bottom - (value - _min) / (_max - _min) * (Bottom - Top); + + private decimal X(int i) => Left + Slot * i + Slot / 2; + + private IEnumerable<(decimal Value, string Name, string Color, string Dash)> Guides() + { + if (SpecUpper is { } su) + { + yield return (su, L["規格上限"], "var(--danger)", "6 3"); + } + if (SpecLower is { } sl) + { + yield return (sl, L["規格下限"], "var(--danger)", "6 3"); + } + if (Upper is { } u) + { + yield return (u, "UCL", "var(--warning)", "4 2"); + } + if (Lower is { } l) + { + yield return (l, "LCL", "var(--warning)", "4 2"); + } + if (Center is { } c) + { + yield return (c, "CL", "var(--text-muted)", "none"); + } + } + + private IEnumerable> Segments() + { + var segment = new List<(decimal X, decimal Y)>(); + for (var i = 0; i < Labels.Count; i++) + { + if (Values[i] is { } value) + { + segment.Add((X(i), Y(value))); + } + else if (segment.Count > 0) + { + yield return segment; + segment = []; + } + } + if (segment.Count > 0) + { + yield return segment; + } + } +} diff --git a/src/MesApp.Client.Web/Shared/UiText.en.resx b/src/MesApp.Client.Web/Shared/UiText.en.resx index cea695f..52c7f4f 100644 --- a/src/MesApp.Client.Web/Shared/UiText.en.resx +++ b/src/MesApp.Client.Web/Shared/UiText.en.resx @@ -3000,9 +3000,6 @@ Quality Analysis - Parallel Factory MES - - Defects by item, process, shift and period (C-40-10-03) and monitoring of nonconformances and deviations (C-40-10-05). Periods are based on the production (business) date. - Summary @@ -4179,4 +4176,67 @@ Checks without target values, etc. + + Totals by defect item, process, shift and period (C-40-10-03), monitoring of nonconformances and deviations (C-40-10-05), and control charts with process capability (C-50-10-01 to 02). Periods are based on the production date (business date). + + + Control chart and process capability + + + Measured values are grouped into one subgroup per inspection order and plotted as an X̄-R chart (an X-Rs chart when the subgroup size is 1). Red points are outside the control limits; yellow points belong to a run of 9 or more on the same side of the center line. Process capability indices are calculated from within-subgroup variation. + + + Inspection item (quantitative) + + + No measured values in the selected period. + + + Control chart + + + X-Rs chart + + + X̄-R chart (subgroup size {0}) + + + Subgroups + + + ({0} subgroups of a different size excluded) + + + Below 1.33 + + + At least 2 subgroups are needed to calculate control limits and process capability. + + + The specification limits were revised within the period. Process capability is calculated with the latest limits. + + + Individual values (X) + + + Means (X̄) + + + Control chart of means + + + Moving ranges (Rs) + + + Ranges (R) + + + Control chart of ranges + + + USL + + + LSL + diff --git a/src/MesApp.Core/Contracts/Quality/QualityContracts.cs b/src/MesApp.Core/Contracts/Quality/QualityContracts.cs index 68abdb8..9f1e88f 100644 --- a/src/MesApp.Core/Contracts/Quality/QualityContracts.cs +++ b/src/MesApp.Core/Contracts/Quality/QualityContracts.cs @@ -206,3 +206,51 @@ public record QualitySummaryResponse( int InspectionFailCount, /// 未クローズの不適合件数(C-40-10-05) int OpenNonconformanceCount); + +// ---- 管理図・工程能力(C-50-10-01〜02)---- + +/// +/// 管理図の1点(1群)。群は「1つの検査指示で同じ検査項目を測った測定値の集まり」。 +/// 個別値管理図(群の大きさ1)では は直前の点との移動範囲で、先頭の点は null +/// +public record ControlChartPoint( + string OrderNo, + /// 群の最初の測定日時 + DateTimeOffset InspectedAt, + /// 測定日時が属する製造日 + DateOnly BusinessDate, + int SampleCount, + /// 群の平均(個別値管理図では測定値そのもの) + decimal Mean, + /// 範囲 R(個別値管理図では移動範囲 Rs) + decimal? Range, + /// 平均が管理限界の外 + bool MeanOutOfControl, + /// 範囲が管理限界の外 + bool RangeOutOfControl, + /// 中心線の同じ側に9点以上続く並びの中にある(JIS Z 9020-2 のルール2) + bool InRun); + +/// +/// 管理図と工程能力指数(C-50-10-01〜02。Spec.md 3.3)。 +/// 群の数が2未満のときは管理限界・工程能力を null で返す(点だけ描く)。 +/// 規格の片側しか無い項目は Cp を null にし、Cpk は片側で求める +/// +public record ControlChartResponse( + int InspectionItemId, string ItemCode, string ItemName, + /// 個別値-移動範囲管理図(X-Rs)か。false なら X̄-R 管理図 + bool IsIndividuals, + /// 群の大きさ(使った群の大きさ。最も多い大きさに揃え、違う大きさの群は除外する) + int SubgroupSize, + decimal? CenterLine, decimal? UpperControlLimit, decimal? LowerControlLimit, + decimal? RangeCenterLine, decimal? RangeUpperControlLimit, decimal? RangeLowerControlLimit, + /// 規格の下限・上限(最も新しい群の検査指示が発行時に写した値) + decimal? LowerSpecLimit, decimal? UpperSpecLimit, + /// 期間内で規格値が改訂されている(工程能力は最新の規格で求めている) + bool SpecLimitsChanged, + /// 群内のばらつきから推定した標準偏差(R̄/d2) + decimal? Sigma, + decimal? Cp, decimal? Cpk, + /// 群の大きさが揃わないため除外した群の数 + int ExcludedSubgroupCount, + List Points); diff --git a/tests/MesApp.Api.Tests/ControlChartCalculatorTests.cs b/tests/MesApp.Api.Tests/ControlChartCalculatorTests.cs new file mode 100644 index 0000000..1f6f417 --- /dev/null +++ b/tests/MesApp.Api.Tests/ControlChartCalculatorTests.cs @@ -0,0 +1,96 @@ +using MesApp.Api.Services; + +namespace MesApp.Api.Tests; + +/// +/// 管理図と工程能力指数の計算(Spec.md 3.3。C-50-10-01〜02)。 +/// 係数表と群の揃え方を手計算の値で固定する。 +/// +public class ControlChartCalculatorTests +{ + private static int _seq; + + private static ControlChartSubgroup Group(decimal? lsl, decimal? usl, params decimal[] values) + { + var n = ++_seq; + var at = new DateTimeOffset(2026, 9, 1, 8, 0, 0, TimeSpan.Zero).AddHours(n); + return new ControlChartSubgroup($"IN-{n:000}", at, DateOnly.FromDateTime(at.DateTime), values, lsl, usl); + } + + [Fact] + public void XバーR管理図の管理限界と工程能力を係数表どおりに求め大きさの違う群は除外する() + { + var chart = ControlChartCalculator.Calculate(1, "INS-01", "外径", + [ + Group(5m, 15m, 10m, 12m), + Group(5m, 15m, 11m, 13m), + Group(5m, 15m, 9m, 11m), + Group(5m, 15m, 10m, 10m), + Group(5m, 15m, 10m, 10m, 10m), // 群の大きさ3は揃わないので除外 + ]); + + Assert.False(chart.IsIndividuals); + Assert.Equal(2, chart.SubgroupSize); + Assert.Equal(1, chart.ExcludedSubgroupCount); + Assert.Equal(4, chart.Points.Count); + // X̄ = 10.75、R̄ = 1.5、A2 = 1.880、D4 = 3.267、d2 = 1.128 + Assert.Equal(10.75m, chart.CenterLine); + Assert.Equal(13.57m, chart.UpperControlLimit); + Assert.Equal(7.93m, chart.LowerControlLimit); + Assert.Equal(1.5m, chart.RangeCenterLine); + Assert.Equal(4.9005m, chart.RangeUpperControlLimit); + Assert.Equal(0m, chart.RangeLowerControlLimit); + Assert.Equal(1.3298m, chart.Sigma); + Assert.Equal(1.25m, chart.Cp); // 10 / (6 × 1.3298) + Assert.Equal(1.07m, chart.Cpk); // (15 − 10.75) / (3 × 1.3298) + Assert.False(chart.SpecLimitsChanged); + Assert.DoesNotContain(chart.Points, p => p.MeanOutOfControl || p.RangeOutOfControl); + } + + [Fact] + public void 群の大きさ1なら個別値と移動範囲で管理限界を求め外れた点に印を付ける() + { + decimal[] values = [10m, 11m, 10m, 11m, 10m, 11m, 10m, 11m, 10m, 20m]; + var chart = ControlChartCalculator.Calculate(1, "INS-01", "外径", + [.. values.Select(v => Group(null, 25m, v))]); + + Assert.True(chart.IsIndividuals); + Assert.Equal(1, chart.SubgroupSize); + Assert.Null(chart.Points[0].Range); // 先頭の点には移動範囲が無い + // X̄ = 114 / 10 = 11.4、Rs̄ = 18 / 9 = 2、E2 = 2.660 + Assert.Equal(11.4m, chart.CenterLine); + Assert.Equal(16.72m, chart.UpperControlLimit); + Assert.True(chart.Points[^1].MeanOutOfControl); + Assert.True(chart.Points[^1].RangeOutOfControl); + Assert.Equal(9, chart.Points.Count(p => !p.MeanOutOfControl)); + // 規格が片側だけなら Cp は出さず、Cpk は片側で求める + Assert.Null(chart.Cp); + Assert.NotNull(chart.Cpk); + } + + [Fact] + public void 中心線の同じ側に9点続くと並びに印を付け8点なら付けない() + { + var nine = ControlChartCalculator.Calculate(1, "INS-01", "外径", + [.. Enumerable.Repeat(11m, 9).Concat(Enumerable.Repeat(9m, 9)).Select(v => Group(null, null, v))]); + Assert.All(nine.Points, p => Assert.True(p.InRun)); + + var eight = ControlChartCalculator.Calculate(1, "INS-01", "外径", + [.. Enumerable.Repeat(11m, 8).Concat(Enumerable.Repeat(9m, 8)).Select(v => Group(null, null, v))]); + Assert.All(eight.Points, p => Assert.False(p.InRun)); + } + + [Fact] + public void 群が1つでは管理限界を出さず規格の改訂を知らせる() + { + var single = ControlChartCalculator.Calculate(1, "INS-01", "外径", [Group(9m, 11m, 10m, 10.2m)]); + Assert.Single(single.Points); + Assert.Null(single.CenterLine); + Assert.Null(single.Cpk); + + var revised = ControlChartCalculator.Calculate(1, "INS-01", "外径", + [Group(9m, 11m, 10m, 10.2m), Group(9.5m, 10.5m, 10.1m, 10.3m)]); + Assert.True(revised.SpecLimitsChanged); + Assert.Equal(9.5m, revised.LowerSpecLimit); // 工程能力は最新の規格で求める + } +} diff --git a/tests/MesApp.Api.Tests/QualityTests.cs b/tests/MesApp.Api.Tests/QualityTests.cs index 7ed40a7..634042e 100644 --- a/tests/MesApp.Api.Tests/QualityTests.cs +++ b/tests/MesApp.Api.Tests/QualityTests.cs @@ -232,6 +232,53 @@ public async Task 検査指示から実績登録判定承認まで通しで動 Assert.Equal(HttpStatusCode.OK, approved.StatusCode); } + [Fact] + public async Task 管理図は検査指示ごとに群を作り取消した検査の測定値を使わない() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + var item = await CreateFinalInspectionItemAsync(admin, ctx.ProductId); + + async Task InspectAsync(params decimal[] values) + { + var lot = await Phase3TestData.ReceiveAsync(admin, ctx.ProductId, 10m, ctx.ProductLocationId); + var created = await admin.PostAsJsonAsync("/api/inspection-orders", + new InspectionOrderCreateRequest(InspectionOrderType.FinalProduct, lot.Id, null, null, null)); + var order = (await created.Content.ReadFromJsonAsync())!; + (await admin.PostAsJsonAsync($"/api/inspection-orders/{order.Id}/results", + values.Select((v, i) => new InspectionResultRequest(item.Id, i + 1, v, null, null)).ToList())) + .EnsureSuccessStatusCode(); + return order; + } + + await InspectAsync(10.0m, 10.2m); + await InspectAsync(9.9m, 10.1m); + var canceled = await InspectAsync(12.0m, 12.4m); + (await admin.PostAsync($"/api/inspection-orders/{canceled.Id}/cancel", null)).EnsureSuccessStatusCode(); + + var chart = await admin.GetFromJsonAsync( + $"/api/quality/control-chart?inspectionItemId={item.Id}"); + + Assert.Equal(2, chart!.Points.Count); + Assert.DoesNotContain(chart.Points, p => p.OrderNo == canceled.OrderNo); + Assert.False(chart.IsIndividuals); + Assert.Equal(2, chart.SubgroupSize); + Assert.Equal(10.05m, chart.CenterLine); // (10.1 + 10.0) / 2 + Assert.Equal(9.5m, chart.LowerSpecLimit); // 規格は検査指示のスナップショット + Assert.Equal(10.5m, chart.UpperSpecLimit); + Assert.NotNull(chart.Cpk); + + // 期間外を指定すると点は無く、管理限界も出さない + var empty = await admin.GetFromJsonAsync( + $"/api/quality/control-chart?inspectionItemId={item.Id}&from=2020-01-01&to=2020-01-31"); + Assert.Empty(empty!.Points); + Assert.Null(empty.CenterLine); + + Assert.Equal(HttpStatusCode.NotFound, + (await admin.GetAsync("/api/quality/control-chart?inspectionItemId=99999")).StatusCode); + } + [Fact] public async Task 検査基準を改訂しても発行済みの検査は当時の規格値で判定され成績書も変わらない() { From 2540812324ca8a45224f669fff6936902d41d100 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:54:27 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=E8=A3=BD=E9=80=A0=E3=83=AA=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=82=BF=E3=82=A4=E3=83=A0=E3=81=AE=E5=88=86=E5=B8=83?= =?UTF-8?q?=E3=82=92=E7=94=9F=E7=94=A3=E6=80=A7=E3=83=A2=E3=83=8B=E3=82=BF?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=99?= =?UTF-8?q?=E3=82=8B=EF=BC=88GR3-04=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 納期の管理が指図の納期超過フラグだけで、ばらつきと異常値が見えなかった。 着手・完了は作業の記録の時刻で決め(状態履歴は実績CSV取込で取込時刻になる)、 製造日の差で数える。度数分布・中央値・90%点と四分位からの異常値を返す。Spec.md 改訂132。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 6 +- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 2 +- .../Controllers/ProductivityController.cs | 112 +++++++++++++++++- .../Pages/Productivity.razor | 93 ++++++++++++++- src/MesApp.Client.Web/Shared/UiText.en.resx | 57 +++++++++ .../Production/ProductionContracts.cs | 36 ++++++ tests/MesApp.Api.Tests/ProductionTests.cs | 80 +++++++++++++ 8 files changed, 380 insertions(+), 8 deletions(-) diff --git a/Spec.md b/Spec.md index 19800d5..c2ced57 100644 --- a/Spec.md +++ b/Spec.md @@ -126,6 +126,7 @@ 改訂129: 2026-09-23(**着手時にもスキル・資格を照合する**ようにした(F-20-30-01)。照合は差立(作業員割当)にしか無く、着手は差立を省略できる(小規模運用)ため、差立を省いた運用・割当者と異なる者の着手・差立後の有効期限切れのいずれでも、必要スキルを持たない者が作業できていた。照合するのは**割当者ではなく実際に着手する者**。判定は `Api/Policies/SkillQualificationPolicy` に集約し、差立と着手で同じ条件を使う。3.2節・3.6節に反映。改訂125〜128は別ブランチ(検査の見直し)で使用中のため欠番にしない) 改訂130: 2026-09-23(**保全手順書に必要スキル・資格を持たせ、保全実績の登録時に実施者と照合する**ようにした(`MaintenanceProcedure.RequiredSkillId`。F-20-30-01)。3.6節は「差立・検査・保全の各業務で照合」と書いていたが、保全・検査には必要スキルの項目自体が無かった。保全指示は手順書を版数ごと固定しないため、照合は手順書の現在値を使う。判定は差立・着手と同じ `SkillQualificationPolicy`。**検査側は未実装のまま3.6節にそう明記した**(検査基準の見直しが別ブランチで進行中のため、合流後に扱う)。5.1節・3.6節に反映) 改訂131: 2026-09-23(**管理図と工程能力指数を実装した**(`api/quality/control-chart`。C-50-10-01〜02)。3.3節は「初期は基本的な管理図表示」を実装対象とし、6.1節の品質分析画面にも管理図があったが、実装は無かった。検査実績の測定値を検査指示ごとの群にまとめ、X̄-R(群の大きさ1なら X-Rs)管理図と Cp・Cpk を返す。新規エンティティは無い。計算は `Api/Services/ControlChartCalculator`(係数表は JIS Z 9020-2)。品質分析画面に検査項目を選ぶ管理図の欄を追加した。3.3節・6.1節に反映) +改訂132: 2026-09-23(**製造リードタイムの分布を生産性モニタリングに追加した**(`api/productivity/lead-time`。B-60-10-05)。納期の管理が指図の納期超過フラグだけで、実績リードタイムのばらつきと異常値が見えなかった(実践的MES導入ガイド 8.3.2 の納期実績の見える化)。着手・完了は状態履歴ではなく作業の記録の時刻で決め、製造日の差で数える。新規エンティティは無い。3.2節・6.1節に反映) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -257,9 +258,10 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 - **品質実績報告**:逸脱/品質不具合の記録(B-40-30-01)、不良原因の選択式記録+詳細(文章・写真)(B-40-30-02)、不良部材報告(B-40-30-03)、不良品在庫の計上・引落し(B-40-30-04) - **後段取り**:指示確認〜着手〜実行〜完了の記録(B-40-40-01〜05) - **工程間搬送**:半製品の搬送・移動指示と移動実行(B-50-10-01〜02)、製品の梱包・包装(B-30-30-05、B-50-20-02)、完成品の倉庫・出荷場への入庫(B-50-20-03) -- **工程進捗管理**:工程毎の完了状況把握(B-60-10-01)、製造トラブルの発生報告・対応履歴記録(B-60-10-02〜04。状態は発生→対応中→完了の前進のみとし、完了からは理由を対応履歴に残したときだけ対応中へ戻せる。発生へは戻さない)、生産性モニタリング(歩留まり・直行率・標準時間予実。B-60-10-05) +- **工程進捗管理**:工程毎の完了状況把握(B-60-10-01)、製造トラブルの発生報告・対応履歴記録(B-60-10-02〜04。状態は発生→対応中→完了の前進のみとし、完了からは理由を対応履歴に残したときだけ対応中へ戻せる。発生へは戻さない)、生産性モニタリング(歩留まり・直行率・標準時間予実・製造リードタイム。B-60-10-05) - **直行率**=良品数÷(良品数+不良数)。**歩留まり**=(良品数+リワーク良品数)÷(良品数+不良数)。リワーク指図の産出は分母に入れない(同じ材料を二度数えるため)、救済された良品として歩留まりの分子にだけ足す - **標準時間の予実**:予定=標準段取り時間+標準作業時間×計画数量(指図展開時に固定した工順の値。5.7節)。実績=直接作業時間+段取り実績時間(いずれも終了済みの記録のみ)。作業時間を記録していない作業指示は実績0分として並ぶ + - **製造リードタイム**(`api/productivity/lead-time`):取消以外の作業指示がすべて完了・承認済みの製造指図ごとに、**最初の作業(段取り・生産実績の開始)から最後の生産実績の終了まで**を製造日の差で数える(同じ製造日なら0日)。**状態履歴の時刻は使わない**——実績CSVで後から取り込むと状態の変わった時刻が取込の時刻になり、実際の作業日から外れるため。期間は完了の製造日。リワーク指図は含めない(歩留まりと同じ扱い)。平均だけではばらつきと異常値が見えないため、**日数ごとの度数分布**(うち納期遅れの件数)・中央値・90%点と、**第3四分位+1.5×四分位範囲を超える異常値**(4件以上のとき)を返す。納期に対する遅れは完了の製造日−納期 - **リワーク・リペア**:リワーク指図の作成・実施・実績記録(B-70-10-01〜03。製造指図の指図区分で表現) - **外注管理**[将来拡張]:工程外注・庫内外注の管理(B-70-20-01〜02。初期は工程マスタの内外製区分と実績記録まで) - **製造履歴訂正**:出来高・品質・稼働時間等の変更・削除(B-70-30-01。権限制御+監査ログによる改ざん防止付き)。**訂正前の値は`ProductionRecordCorrection`に業務履歴として残し**、トレース画面の履歴から参照できるようにする(5.7節「実績訂正の扱い」) @@ -593,7 +595,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと 9. 工程進捗管理画面(工程別進捗、**遅れている作業指示**、トラブル対応履歴) 9.1 作業時間画面(直接作業=作業指示に紐づく時間、間接作業=区分を指定する時間。一覧は本人分、管理系ロールは全員分) 9.1.1 生産計画・予実画面(計画登録タブ=製造日×品目×工程(作業区は任意)の計画数量の登録・変更・削除、予実タブ=製造日×品目×工程の計画と出来高・達成率の表と、1品目・1工程に絞ったときの日ごとのグラフ(計画は棒・実績は折れ線、左右の軸は同じ目盛り)。集計はAPIが行う。A-30-10-01) -9.2 生産性モニタリング画面(歩留まり・直行率の品目別/工程別集計、標準時間の予実。期間は製造日基準) +9.2 生産性モニタリング画面(歩留まり・直行率の品目別/工程別集計、標準時間の予実、製造リードタイムの分布。期間は製造日基準) 10. 受入画面(受入登録・取消・返品、ラベル/現品票印刷) 11. 在庫照会・在庫オペレーション画面(在庫移動、数量調整、ロット分割/統合/振替、在庫ステータス変更、期限アラート) 12. 出庫・払出画面(出庫指示、ピッキング指示・実行、払出戻し) diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index 3168167..1375997 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -38,7 +38,7 @@ | 工程間搬送・移動指示 | B-50-10 / D-30-10-04 | `TransferOrdersController.cs` `api/transfer-orders`(更新系は在庫権限) | Execution.cs: TransferOrder | `/transfer-orders` `TransferOrders.razor`(`Inventory.razor` の「振替」は別機能の `api/inventory/transfer`) | `Tests/InventoryTests.cs` | | 設備稼働報告・稼働監視 | B-40-20 / E-20-10 | `EquipmentLogsController.cs` `api/equipment-logs`(`?workOrderId=` で絞り込み。登録の判定は `ShopFloorReportService.AddEquipmentLogAsync` で実績CSVと共通)
    稼働サマリは `GET summary?from=&to=`(**製造日基準**。稼働の開始時刻が属する製造日で振り分ける。省略時は全期間) | `Core/Entities/Maintenance.cs`: EquipmentLog(`WorkOrderId` 任意=**PQC×EQCの交差点**。ロット履歴 H-30-10-04 はこれを辿る。`EquipmentLogStatus` への追加は**末尾のみ**:JSONが数値) | `/maintenance` `Maintenance/EquipmentLogsTab.razor` | `Tests/MaintenanceTests.cs` | -| 生産性モニタリング(歩留まり・直行率・標準時間予実) | B-60-10-05 | `ProductivityController.cs` `api/productivity`(製造日で期間指定。**リワーク指図の産出は分母に入れない**=救済分は歩留まりの分子だけ) | Execution.cs: ProductionRecord / WorkTimeRecord / SetupRecord
    Production.cs: WorkOrder(標準時間は**展開時の工順スナップショット**。マスタ現在値で引き直さない) | `/productivity` `Web/Pages/Productivity.razor`
    `Web/Shared/ProductivityTable.razor` | `Tests/ProductionTests.cs` | +| 生産性モニタリング(歩留まり・直行率・標準時間予実) | B-60-10-05 | `ProductivityController.cs` `api/productivity`(製造日で期間指定。**リワーク指図の産出は分母に入れない**=救済分は歩留まりの分子だけ)
    製造リードタイム `api/productivity/lead-time`(**着手・完了は作業の記録の時刻**。状態履歴は実績CSV取込で取込時刻になるため使わない。異常値は四分位から) | Execution.cs: ProductionRecord / WorkTimeRecord / SetupRecord
    Production.cs: WorkOrder(標準時間は**展開時の工順スナップショット**。マスタ現在値で引き直さない) | `/productivity` `Web/Pages/Productivity.razor`
    `Web/Shared/ProductivityTable.razor` | `Tests/ProductionTests.cs` | ## C. 品質管理 diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index 25f82ae..13c11c8 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -10,7 +10,7 @@ | GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂129) | | GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **保全は対応済**(Spec.md 改訂130)。検査は `inspection-fixes` 合流後 | | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂131) | -| GR3-04 | 製造リードタイムの分布 | B. 指標 | 未着手 | +| GR3-04 | 製造リードタイムの分布 | B. 指標 | **対応済**(Spec.md 改訂132) | | GR3-05 | 標準時間の見直し候補 | B. 指標 | 未着手 | | GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | 未着手 | diff --git a/src/MesApp.Api/Controllers/ProductivityController.cs b/src/MesApp.Api/Controllers/ProductivityController.cs index 5daa7b5..2bc8b7d 100644 --- a/src/MesApp.Api/Controllers/ProductivityController.cs +++ b/src/MesApp.Api/Controllers/ProductivityController.cs @@ -10,7 +10,7 @@ namespace MesApp.Api.Controllers; /// -/// 生産性モニタリング(Spec.md 3.2:B-60-10-05 歩留まり・直行率・標準時間予実)。 +/// 生産性モニタリング(Spec.md 3.2:B-60-10-05 歩留まり・直行率・標準時間予実・製造リードタイム)。 /// 期間は製造日(業務日付)基準。 /// [ApiController] @@ -93,6 +93,116 @@ ProductivityRow Aggregate(string key, IReadOnlyCollection< return new ProductivitySummaryResponse(total, byProduct, byProcess, timeVariances); } + /// + /// 製造リードタイムの分布(B-60-10-05。ガイド 8.3.2 の納期実績の見える化)。 + /// + /// 着手・完了は状態履歴ではなく作業の記録の時刻で決める(実績CSVで後から取り込むと、 + /// 状態が変わった時刻は取込の時刻になり、実際の作業日から外れるため)。 + /// 着手=最初の段取り・生産実績の開始、完了=最後の生産実績の終了(終了が無ければ開始)。 + /// 対象は取消以外の作業指示がすべて完了・承認済みの指図で、完了の製造日で期間を絞る。 + /// リワーク指図は元の指図と同じ品物を2度数えることになるため含めない(歩留まりと同じ扱い)。 + /// + /// + [HttpGet("lead-time")] + public async Task> LeadTime( + [FromQuery] DateOnly? from = null, + [FromQuery] DateOnly? to = null, + [FromQuery] int? productId = null, + CancellationToken ct = default) + { + var orders = await db.ManufacturingOrders.AsNoTracking() + .Where(o => o.OrderType != ManufacturingOrderType.Rework + && (productId == null || o.ProductId == productId) + && o.WorkOrders.Any(w => w.Status != WorkOrderStatus.Canceled) + && o.WorkOrders.All(w => w.Status == WorkOrderStatus.Canceled + || w.Status == WorkOrderStatus.Completed + || w.Status == WorkOrderStatus.Approved)) + .Select(o => new + { + o.Id, o.OrderNo, ProductCode = o.Product!.Code, ProductName = o.Product!.Name, + o.Quantity, o.DueDate, + }) + .ToListAsync(ct); + var orderIds = orders.Select(o => o.Id).ToList(); + + // 時刻の最小・最大はSQLiteでDateTimeOffsetの集計が翻訳できないため、取り出してから求める + var records = await db.ProductionRecords.AsNoTracking() + .Where(r => orderIds.Contains(r.WorkOrder!.ManufacturingOrderId)) + .Select(r => new { r.WorkOrder!.ManufacturingOrderId, r.StartedAt, r.EndedAt }) + .ToListAsync(ct); + var setups = await db.SetupRecords.AsNoTracking() + .Where(s => orderIds.Contains(s.WorkOrder!.ManufacturingOrderId)) + .Select(s => new { s.WorkOrder!.ManufacturingOrderId, s.StartedAt }) + .ToListAsync(ct); + var recordsByOrder = records.ToLookup(r => r.ManufacturingOrderId); + var setupStartByOrder = setups.GroupBy(s => s.ManufacturingOrderId) + .ToDictionary(g => g.Key, g => g.Min(s => s.StartedAt)); + + var rows = new List(); + foreach (var order in orders) + { + var orderRecords = recordsByOrder[order.Id].ToList(); + if (orderRecords.Count == 0) + { + continue; // 実績の無い指図(数量0で完了扱いにしたもの等)は日数を決められない + } + var started = orderRecords.Min(r => r.StartedAt); + if (setupStartByOrder.TryGetValue(order.Id, out var setupStart) && setupStart < started) + { + started = setupStart; + } + var completed = orderRecords.Max(r => r.EndedAt ?? r.StartedAt); + var startedOn = businessDate.GetBusinessDate(started); + var completedOn = businessDate.GetBusinessDate(completed); + if ((from is not null && completedOn < from) || (to is not null && completedOn > to)) + { + continue; + } + rows.Add(new LeadTimeOrderRow(order.Id, order.OrderNo, order.ProductCode, order.ProductName, + order.Quantity, started, completed, startedOn, completedOn, + completedOn.DayNumber - startedOn.DayNumber, + Math.Round((decimal)(completed - started).TotalHours, 1), + order.DueDate, + order.DueDate is { } due ? completedOn.DayNumber - due.DayNumber : null, + IsOutlier: false)); + } + + return LeadTimeStatistics(rows); + } + + /// リードタイムの要約・度数分布・異常値(四分位は最近順位法。4件未満では異常値を判定しない) + private static LeadTimeResponse LeadTimeStatistics(List rows) + { + if (rows.Count == 0) + { + return new LeadTimeResponse(0, null, null, null, null, null, 0, 0, 0, [], []); + } + var days = rows.Select(r => r.LeadTimeDays).Order().ToList(); + int Rank(decimal p) => days[Math.Max(0, (int)Math.Ceiling(p * days.Count) - 1)]; + var median = days.Count % 2 == 1 + ? days[days.Count / 2] + : (days[days.Count / 2 - 1] + days[days.Count / 2]) / 2m; + decimal? threshold = days.Count >= 4 ? Rank(0.75m) + 1.5m * (Rank(0.75m) - Rank(0.25m)) : null; + + var marked = rows + .Select(r => r with { IsOutlier = threshold is { } t && r.LeadTimeDays > t }) + .OrderByDescending(r => r.LeadTimeDays).ThenByDescending(r => r.LeadTimeHours).ThenBy(r => r.OrderNo) + .ToList(); + // 0日から最大日数まで、件数0の日も並べる(分布の切れ目が見えるように) + var distribution = Enumerable.Range(0, days[^1] + 1) + .Select(d => new LeadTimeBucket(d, + rows.Count(r => r.LeadTimeDays == d), + rows.Count(r => r.LeadTimeDays == d && r.DelayDays > 0))) + .ToList(); + + return new LeadTimeResponse(rows.Count, + Math.Round((decimal)days.Average(), 2), median, Rank(0.9m), days[^1], threshold, + OnTimeCount: rows.Count(r => r.DelayDays <= 0), + LateCount: rows.Count(r => r.DelayDays > 0), + NoDueDateCount: rows.Count(r => r.DelayDays is null), + distribution, marked); + } + /// /// 標準時間の予実(B-60-10-05)。予定は指図展開時に固定した工順の値で、 /// 実績は直接作業時間(F-30-20-01)と段取り実績(B-20-50)の合計。 diff --git a/src/MesApp.Client.Web/Pages/Productivity.razor b/src/MesApp.Client.Web/Pages/Productivity.razor index 2ee64c5..57ab06f 100644 --- a/src/MesApp.Client.Web/Pages/Productivity.razor +++ b/src/MesApp.Client.Web/Pages/Productivity.razor @@ -1,4 +1,4 @@ -@* 生産性モニタリング(Spec.md 3.2 工程進捗管理。歩留まり・直行率・標準時間予実。B-60-10-05) *@ +@* 生産性モニタリング(Spec.md 3.2 工程進捗管理。歩留まり・直行率・標準時間予実・製造リードタイム。B-60-10-05) *@ @page "/productivity" @attribute [Authorize] @inject HttpClient Http @@ -120,8 +120,94 @@ else } +@* 製造リードタイムの分布(ガイド 8.3.2)。平均では見えないばらつきと異常値を、完了した指図の分布で見る *@ +
    +

    @L["製造リードタイム"]

    +

    + @L["完了した製造指図ごとに、最初の作業(段取り・実績の開始)から最後の実績の終了までを製造日で数える。期間は完了した製造日。リワーク指図は含めない。"] +

    + @if (_leadTime is null) + { +

    @L["読み込み中..."]

    + } + else if (_leadTime.OrderCount == 0) + { +

    @L["対象期間に完了した製造指図はありません。"]

    + } + else + { + + + + + + + + + + + + + + + + + + + +
    @L["完了した指図"]@L["{0} 件", _leadTime.OrderCount]@L["平均"]@L["{0} 日", _leadTime.AverageDays!.Value.ToString("0.##")]@L["中央値"]@L["{0} 日", _leadTime.MedianDays!.Value.ToString("0.#")]
    @L["90%点"]@L["{0} 日", _leadTime.Percentile90Days!.Value]@L["最大"]@L["{0} 日", _leadTime.MaxDays!.Value]@L["納期内 / 遅れ / 納期なし"]@L["{0} 件 / {1} 件 / {2} 件", _leadTime.OnTimeCount, _leadTime.LateCount, _leadTime.NoDueDateCount]
    + +

    @L["リードタイムの長い指図"]

    + + + + + + + + + @foreach (var row in _leadTime.Orders.Take(20)) + { + + + + + + + + + + + } + +
    @L["製造指図"]@L["品目"]@L["数量"]@L["着手"]@L["完了"]@L["リードタイム"]@L["納期"]@L["遅れ"]
    @row.OrderNo@row.ProductCode @row.ProductName@row.Quantity@row.StartedOn.ToString("yyyy-MM-dd")@row.CompletedOn.ToString("yyyy-MM-dd") + @L["{0} 日", row.LeadTimeDays] + @if (row.IsOutlier) + { + @L["異常値"] + } + @(row.DueDate?.ToString("yyyy-MM-dd") ?? "-") + @(row.DelayDays is { } delay && delay > 0 ? L["{0} 日", delay].Value : "-") +
    + @if (_leadTime.OutlierThresholdDays is { } threshold) + { +

    @L["異常値は第3四分位+1.5×四分位範囲({0} 日)を超えるもの。平均を押し上げている原因を個別に調べる対象。", threshold.ToString("0.#")]

    + } + } +
    + @code { private ProductivitySummaryResponse? _summary; + private LeadTimeResponse? _leadTime; + + /// 分布の棒(納期内・納期なしと、納期遅れを積み上げる) + private List LeadTimeBars => _leadTime is null ? [] : + [ + new(L["納期内・納期なし"], "var(--primary)", [.. _leadTime.Distribution.Select(d => (decimal?)(d.Count - d.LateCount))]), + new(L["納期遅れ"], "var(--danger)", [.. _leadTime.Distribution.Select(d => (decimal?)d.LateCount)]), + ]; private DateTime? _from; private DateTime? _to; @@ -138,7 +224,8 @@ else { query.Add($"to={DateOnly.FromDateTime(_to.Value):yyyy-MM-dd}"); } - var url = "api/productivity" + (query.Count > 0 ? "?" + string.Join("&", query) : string.Empty); - _summary = await Http.GetFromJsonAsync(url); + var suffix = query.Count > 0 ? "?" + string.Join("&", query) : string.Empty; + _summary = await Http.GetFromJsonAsync("api/productivity" + suffix); + _leadTime = await Http.GetFromJsonAsync("api/productivity/lead-time" + suffix); } } diff --git a/src/MesApp.Client.Web/Shared/UiText.en.resx b/src/MesApp.Client.Web/Shared/UiText.en.resx index 52c7f4f..09ac50f 100644 --- a/src/MesApp.Client.Web/Shared/UiText.en.resx +++ b/src/MesApp.Client.Web/Shared/UiText.en.resx @@ -4239,4 +4239,61 @@ LSL + + Manufacturing lead time + + + For each completed manufacturing order, counted in production days from the first activity (start of setup or production) to the end of the last production record. The period is the production date of completion. Rework orders are excluded. + + + No manufacturing orders were completed in the selected period. + + + Completed orders + + + Average + + + {0} days + + + Median + + + 90th percentile + + + Maximum + + + On time / Late / No due date + + + {0} / {1} / {2} + + + Distribution of manufacturing lead time + + + Orders with the longest lead time + + + Lead time + + + Delay + + + Outlier + + + Outliers exceed the third quartile + 1.5 × interquartile range ({0} days). Investigate each one individually, as they push up the average. + + + On time / no due date + + + Late + diff --git a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs index ad62071..5493dc0 100644 --- a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs +++ b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs @@ -160,6 +160,42 @@ public record ProductivitySummaryResponse( /// 期間内に実績のあった作業指示の予実(超過率の大きい順) List TimeVariances); +// ---- 製造リードタイム(B-60-10-05。ガイド 8.3.2 の納期実績の見える化)---- + +/// +/// 完了した製造指図1件のリードタイム。着手は最初の作業記録(段取り・生産実績の開始時刻)、 +/// 完了は最後の生産実績の終了時刻。日数は製造日の差(同じ製造日に着手・完了すれば0日) +/// +public record LeadTimeOrderRow( + int ManufacturingOrderId, string OrderNo, string ProductCode, string ProductName, decimal Quantity, + DateTimeOffset StartedAt, DateTimeOffset CompletedAt, + DateOnly StartedOn, DateOnly CompletedOn, + int LeadTimeDays, + /// 着手から完了までの経過時間(時間) + decimal LeadTimeHours, + DateOnly? DueDate, + /// 納期に対する遅れ(日)。完了の製造日−納期。0以下は納期内。納期なしはnull + int? DelayDays, + /// 異常値(第3四分位+1.5×四分位範囲を超える)。個別に原因を調べる対象 + bool IsOutlier); + +/// リードタイムの度数分布の1区切り(日数ごとの完了件数。うち納期遅れの件数) +public record LeadTimeBucket(int Days, int Count, int LateCount); + +/// +/// 製造リードタイムの分布(期間は完了の製造日。リワーク指図は含めない)。 +/// 平均だけではばらつきと異常値が見えないため、分布と四分位から見た異常値を返す +/// +public record LeadTimeResponse( + int OrderCount, + decimal? AverageDays, decimal? MedianDays, int? Percentile90Days, int? MaxDays, + /// 異常値とみなすしきい値(日)。4件未満では求めない + decimal? OutlierThresholdDays, + int OnTimeCount, int LateCount, int NoDueDateCount, + List Distribution, + /// リードタイムの長い順 + List Orders); + // ---- 遅延検知(A-30-20-01)---- /// 遅れの種類 diff --git a/tests/MesApp.Api.Tests/ProductionTests.cs b/tests/MesApp.Api.Tests/ProductionTests.cs index d471b93..c8728a2 100644 --- a/tests/MesApp.Api.Tests/ProductionTests.cs +++ b/tests/MesApp.Api.Tests/ProductionTests.cs @@ -747,6 +747,86 @@ public async Task 生産性モニタリングで歩留まり直行率と標準 Assert.Empty(empty.TimeVariances); } + [Fact] + public async Task 製造リードタイムは作業の記録の時刻から製造日で数え完了した指図だけを分布にする() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + // 製造日の境界に掛からないよう正午で組む(工場のタイムゾーンは既定でOSのローカル) + static DateTimeOffset Noon(int day) => new(new DateTime(2026, 6, day, 12, 0, 0, DateTimeKind.Local)); + + async Task OrderAsync(DateOnly? due) + { + var created = await admin.PostAsJsonAsync("/api/manufacturing-orders", + new CreateManufacturingOrderRequest(ctx.ProductId, 10m, due, ManufacturingOrderType.Normal, null, null)); + var order = (await created.Content.ReadFromJsonAsync())!; + (await admin.PostAsync($"/api/manufacturing-orders/{order.Id}/approve", null)).EnsureSuccessStatusCode(); + var expanded = await admin.PostAsJsonAsync($"/api/manufacturing-orders/{order.Id}/expand", new ExpandRequest(null)); + return (await expanded.Content.ReadFromJsonAsync())!; + } + async Task RecordAsync(int workOrderId, DateTimeOffset start, int? locationId = null) => + (await admin.PostAsJsonAsync($"/api/work-orders/{workOrderId}/production-records", + new ProductionRecordRequest(10m, 0m, start, start.AddHours(1), locationId, false))) + .EnsureSuccessStatusCode(); + + // A:6/1着手 → 6/3完了(2日)。納期6/2なので1日遅れ + var a = await OrderAsync(new DateOnly(2026, 6, 2)); + await RecordAsync(a.WorkOrders[0].Id, Noon(1)); + await RecordAsync(a.WorkOrders[1].Id, Noon(3), ctx.ProductLocationId); + // B:前段取りを6/4に始め、実績は6/5(段取りの開始から数えて1日)。納期なし + var b = await OrderAsync(null); + (await admin.PostAsJsonAsync($"/api/work-orders/{b.WorkOrders[0].Id}/setup-records", + new SetupRecordRequest(SetupType.Pre, Noon(4), Noon(4).AddHours(1), null))).EnsureSuccessStatusCode(); + await RecordAsync(b.WorkOrders[0].Id, Noon(5)); + await RecordAsync(b.WorkOrders[1].Id, Noon(5), ctx.ProductLocationId); + // C:2工程目が未完了なので対象外 + var c = await OrderAsync(null); + await RecordAsync(c.WorkOrders[0].Id, Noon(1)); + + var result = await admin.GetFromJsonAsync("/api/productivity/lead-time"); + + Assert.Equal(2, result!.OrderCount); + Assert.DoesNotContain(result.Orders, o => o.OrderNo == c.Order.OrderNo); + var rowA = Assert.Single(result.Orders, o => o.OrderNo == a.Order.OrderNo); + Assert.Equal(2, rowA.LeadTimeDays); + Assert.Equal(1, rowA.DelayDays); + Assert.Equal(new DateOnly(2026, 6, 3), rowA.CompletedOn); + var rowB = Assert.Single(result.Orders, o => o.OrderNo == b.Order.OrderNo); + Assert.Equal(1, rowB.LeadTimeDays); + Assert.Null(rowB.DelayDays); + Assert.Equal(a.Order.OrderNo, result.Orders[0].OrderNo); // 長い順 + Assert.Equal(1, result.LateCount); + Assert.Equal(1, result.NoDueDateCount); + Assert.Equal(1.5m, result.MedianDays); + Assert.Null(result.OutlierThresholdDays); // 4件未満では異常値を判定しない + // 0日〜最大2日を件数0の日も含めて並べる + Assert.Equal([0, 1, 2], result.Distribution.Select(d => d.Days)); + Assert.Equal([0, 1, 1], result.Distribution.Select(d => d.Count)); + Assert.Equal(1, result.Distribution[2].LateCount); + + // 期間は完了の製造日で絞る(Aは6/3完了、Bは6/5完了) + var onlyA = await admin.GetFromJsonAsync("/api/productivity/lead-time?from=2026-06-01&to=2026-06-04"); + Assert.Equal(a.Order.OrderNo, Assert.Single(onlyA!.Orders).OrderNo); + + // 0日を3件と10日を1件足すと [0,0,0,1,2,10]。Q1=0・Q3=2 からしきい値 2+1.5×2=5 日を超える10日が異常値 + for (var i = 0; i < 3; i++) + { + var same = await OrderAsync(null); + await RecordAsync(same.WorkOrders[0].Id, Noon(10)); + await RecordAsync(same.WorkOrders[1].Id, Noon(10), ctx.ProductLocationId); + } + var slow = await OrderAsync(null); + await RecordAsync(slow.WorkOrders[0].Id, Noon(11)); + await RecordAsync(slow.WorkOrders[1].Id, Noon(21), ctx.ProductLocationId); + + var withOutlier = await admin.GetFromJsonAsync("/api/productivity/lead-time"); + Assert.Equal(5m, withOutlier!.OutlierThresholdDays); + var outlier = Assert.Single(withOutlier.Orders, o => o.IsOutlier); + Assert.Equal(slow.Order.OrderNo, outlier.OrderNo); + Assert.Equal(10, withOutlier.MaxDays); + } + [Fact] public async Task 納期超過と標準時間超過の作業指示を遅延として拾える() { From bcbd344d72e09a5aa6117b8c9c1372df1fb1b11c Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:58:43 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=E6=A8=99=E6=BA=96=E6=99=82=E9=96=93?= =?UTF-8?q?=E3=81=AE=E8=A6=8B=E7=9B=B4=E3=81=97=E5=80=99=E8=A3=9C=E3=82=92?= =?UTF-8?q?=E7=94=9F=E7=94=A3=E6=80=A7=E3=83=A2=E3=83=8B=E3=82=BF=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= =?UTF-8?q?=EF=BC=88GR3-05=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにならなかった。 工程ごとに実績の中央値を工順マスタの現在値と比べ、件数としきい値で候補を挙げる。 マスタは書き換えない。Spec.md 改訂133。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 6 +- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 2 +- .../Controllers/ProductivityController.cs | 121 ++++++++++++++++++ src/MesApp.Api/Localization/ApiText.en.resx | 3 + .../Pages/Productivity.razor | 54 ++++++++ src/MesApp.Client.Web/Shared/UiText.en.resx | 30 +++++ .../Production/ProductionContracts.cs | 33 +++++ tests/MesApp.Api.Tests/ProductionTests.cs | 51 ++++++++ 9 files changed, 298 insertions(+), 4 deletions(-) diff --git a/Spec.md b/Spec.md index c2ced57..2c55522 100644 --- a/Spec.md +++ b/Spec.md @@ -127,6 +127,7 @@ 改訂130: 2026-09-23(**保全手順書に必要スキル・資格を持たせ、保全実績の登録時に実施者と照合する**ようにした(`MaintenanceProcedure.RequiredSkillId`。F-20-30-01)。3.6節は「差立・検査・保全の各業務で照合」と書いていたが、保全・検査には必要スキルの項目自体が無かった。保全指示は手順書を版数ごと固定しないため、照合は手順書の現在値を使う。判定は差立・着手と同じ `SkillQualificationPolicy`。**検査側は未実装のまま3.6節にそう明記した**(検査基準の見直しが別ブランチで進行中のため、合流後に扱う)。5.1節・3.6節に反映) 改訂131: 2026-09-23(**管理図と工程能力指数を実装した**(`api/quality/control-chart`。C-50-10-01〜02)。3.3節は「初期は基本的な管理図表示」を実装対象とし、6.1節の品質分析画面にも管理図があったが、実装は無かった。検査実績の測定値を検査指示ごとの群にまとめ、X̄-R(群の大きさ1なら X-Rs)管理図と Cp・Cpk を返す。新規エンティティは無い。計算は `Api/Services/ControlChartCalculator`(係数表は JIS Z 9020-2)。品質分析画面に検査項目を選ぶ管理図の欄を追加した。3.3節・6.1節に反映) 改訂132: 2026-09-23(**製造リードタイムの分布を生産性モニタリングに追加した**(`api/productivity/lead-time`。B-60-10-05)。納期の管理が指図の納期超過フラグだけで、実績リードタイムのばらつきと異常値が見えなかった(実践的MES導入ガイド 8.3.2 の納期実績の見える化)。着手・完了は状態履歴ではなく作業の記録の時刻で決め、製造日の差で数える。新規エンティティは無い。3.2節・6.1節に反映) +改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -258,10 +259,11 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 - **品質実績報告**:逸脱/品質不具合の記録(B-40-30-01)、不良原因の選択式記録+詳細(文章・写真)(B-40-30-02)、不良部材報告(B-40-30-03)、不良品在庫の計上・引落し(B-40-30-04) - **後段取り**:指示確認〜着手〜実行〜完了の記録(B-40-40-01〜05) - **工程間搬送**:半製品の搬送・移動指示と移動実行(B-50-10-01〜02)、製品の梱包・包装(B-30-30-05、B-50-20-02)、完成品の倉庫・出荷場への入庫(B-50-20-03) -- **工程進捗管理**:工程毎の完了状況把握(B-60-10-01)、製造トラブルの発生報告・対応履歴記録(B-60-10-02〜04。状態は発生→対応中→完了の前進のみとし、完了からは理由を対応履歴に残したときだけ対応中へ戻せる。発生へは戻さない)、生産性モニタリング(歩留まり・直行率・標準時間予実・製造リードタイム。B-60-10-05) +- **工程進捗管理**:工程毎の完了状況把握(B-60-10-01)、製造トラブルの発生報告・対応履歴記録(B-60-10-02〜04。状態は発生→対応中→完了の前進のみとし、完了からは理由を対応履歴に残したときだけ対応中へ戻せる。発生へは戻さない)、生産性モニタリング(歩留まり・直行率・標準時間予実・製造リードタイム・標準時間の見直し候補。B-60-10-05) - **直行率**=良品数÷(良品数+不良数)。**歩留まり**=(良品数+リワーク良品数)÷(良品数+不良数)。リワーク指図の産出は分母に入れない(同じ材料を二度数えるため)、救済された良品として歩留まりの分子にだけ足す - **標準時間の予実**:予定=標準段取り時間+標準作業時間×計画数量(指図展開時に固定した工順の値。5.7節)。実績=直接作業時間+段取り実績時間(いずれも終了済みの記録のみ)。作業時間を記録していない作業指示は実績0分として並ぶ - **製造リードタイム**(`api/productivity/lead-time`):取消以外の作業指示がすべて完了・承認済みの製造指図ごとに、**最初の作業(段取り・生産実績の開始)から最後の生産実績の終了まで**を製造日の差で数える(同じ製造日なら0日)。**状態履歴の時刻は使わない**——実績CSVで後から取り込むと状態の変わった時刻が取込の時刻になり、実際の作業日から外れるため。期間は完了の製造日。リワーク指図は含めない(歩留まりと同じ扱い)。平均だけではばらつきと異常値が見えないため、**日数ごとの度数分布**(うち納期遅れの件数)・中央値・90%点と、**第3四分位+1.5×四分位範囲を超える異常値**(4件以上のとき)を返す。納期に対する遅れは完了の製造日−納期 + - **標準時間の見直し候補**(`api/productivity/standard-time-review`):工順の工程ごとに、期間内の作業指示の**1個あたり実作業時間**(直接作業時間÷(良品数+不良数))と**1回あたり段取り時間**の**中央値**を、**工順マスタの現在値**と比べる(作業指示のスナップショットではない。見直す対象そのものであり、改訂後もまだずれているかを見たいため)。作業時間を記録していない作業指示は0分として混ぜず件数から外す。件数が既定3件以上でずれが既定20%以上なら候補にする(標準0分で実績があるものも候補)。**マスタは書き換えない**——標準を実績に寄せるか、作業を改めるかは業務の判断。工順の工程順序と工程が作業指示と一致しないもの(工順改訂で並びが変わったもの)は数えない。リワーク指図は含めない - **リワーク・リペア**:リワーク指図の作成・実施・実績記録(B-70-10-01〜03。製造指図の指図区分で表現) - **外注管理**[将来拡張]:工程外注・庫内外注の管理(B-70-20-01〜02。初期は工程マスタの内外製区分と実績記録まで) - **製造履歴訂正**:出来高・品質・稼働時間等の変更・削除(B-70-30-01。権限制御+監査ログによる改ざん防止付き)。**訂正前の値は`ProductionRecordCorrection`に業務履歴として残し**、トレース画面の履歴から参照できるようにする(5.7節「実績訂正の扱い」) @@ -595,7 +597,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと 9. 工程進捗管理画面(工程別進捗、**遅れている作業指示**、トラブル対応履歴) 9.1 作業時間画面(直接作業=作業指示に紐づく時間、間接作業=区分を指定する時間。一覧は本人分、管理系ロールは全員分) 9.1.1 生産計画・予実画面(計画登録タブ=製造日×品目×工程(作業区は任意)の計画数量の登録・変更・削除、予実タブ=製造日×品目×工程の計画と出来高・達成率の表と、1品目・1工程に絞ったときの日ごとのグラフ(計画は棒・実績は折れ線、左右の軸は同じ目盛り)。集計はAPIが行う。A-30-10-01) -9.2 生産性モニタリング画面(歩留まり・直行率の品目別/工程別集計、標準時間の予実、製造リードタイムの分布。期間は製造日基準) +9.2 生産性モニタリング画面(歩留まり・直行率の品目別/工程別集計、標準時間の予実と見直し候補、製造リードタイムの分布。期間は製造日基準) 10. 受入画面(受入登録・取消・返品、ラベル/現品票印刷) 11. 在庫照会・在庫オペレーション画面(在庫移動、数量調整、ロット分割/統合/振替、在庫ステータス変更、期限アラート) 12. 出庫・払出画面(出庫指示、ピッキング指示・実行、払出戻し) diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index 1375997..06c7255 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -38,7 +38,7 @@ | 工程間搬送・移動指示 | B-50-10 / D-30-10-04 | `TransferOrdersController.cs` `api/transfer-orders`(更新系は在庫権限) | Execution.cs: TransferOrder | `/transfer-orders` `TransferOrders.razor`(`Inventory.razor` の「振替」は別機能の `api/inventory/transfer`) | `Tests/InventoryTests.cs` | | 設備稼働報告・稼働監視 | B-40-20 / E-20-10 | `EquipmentLogsController.cs` `api/equipment-logs`(`?workOrderId=` で絞り込み。登録の判定は `ShopFloorReportService.AddEquipmentLogAsync` で実績CSVと共通)
    稼働サマリは `GET summary?from=&to=`(**製造日基準**。稼働の開始時刻が属する製造日で振り分ける。省略時は全期間) | `Core/Entities/Maintenance.cs`: EquipmentLog(`WorkOrderId` 任意=**PQC×EQCの交差点**。ロット履歴 H-30-10-04 はこれを辿る。`EquipmentLogStatus` への追加は**末尾のみ**:JSONが数値) | `/maintenance` `Maintenance/EquipmentLogsTab.razor` | `Tests/MaintenanceTests.cs` | -| 生産性モニタリング(歩留まり・直行率・標準時間予実) | B-60-10-05 | `ProductivityController.cs` `api/productivity`(製造日で期間指定。**リワーク指図の産出は分母に入れない**=救済分は歩留まりの分子だけ)
    製造リードタイム `api/productivity/lead-time`(**着手・完了は作業の記録の時刻**。状態履歴は実績CSV取込で取込時刻になるため使わない。異常値は四分位から) | Execution.cs: ProductionRecord / WorkTimeRecord / SetupRecord
    Production.cs: WorkOrder(標準時間は**展開時の工順スナップショット**。マスタ現在値で引き直さない) | `/productivity` `Web/Pages/Productivity.razor`
    `Web/Shared/ProductivityTable.razor` | `Tests/ProductionTests.cs` | +| 生産性モニタリング(歩留まり・直行率・標準時間予実) | B-60-10-05 | `ProductivityController.cs` `api/productivity`(製造日で期間指定。**リワーク指図の産出は分母に入れない**=救済分は歩留まりの分子だけ)
    標準時間の見直し候補 `api/productivity/standard-time-review`(**比べる相手は工順マスタの現在値**。実績は中央値。時間の記録が無い作業指示は件数から外す)
    製造リードタイム `api/productivity/lead-time`(**着手・完了は作業の記録の時刻**。状態履歴は実績CSV取込で取込時刻になるため使わない。異常値は四分位から) | Execution.cs: ProductionRecord / WorkTimeRecord / SetupRecord
    Production.cs: WorkOrder(標準時間は**展開時の工順スナップショット**。マスタ現在値で引き直さない) | `/productivity` `Web/Pages/Productivity.razor`
    `Web/Shared/ProductivityTable.razor` | `Tests/ProductionTests.cs` | ## C. 品質管理 diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index 13c11c8..b530032 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -11,7 +11,7 @@ | GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **保全は対応済**(Spec.md 改訂130)。検査は `inspection-fixes` 合流後 | | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂131) | | GR3-04 | 製造リードタイムの分布 | B. 指標 | **対応済**(Spec.md 改訂132) | -| GR3-05 | 標準時間の見直し候補 | B. 指標 | 未着手 | +| GR3-05 | 標準時間の見直し候補 | B. 指標 | **対応済**(Spec.md 改訂133) | | GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | 未着手 | ## 総評 diff --git a/src/MesApp.Api/Controllers/ProductivityController.cs b/src/MesApp.Api/Controllers/ProductivityController.cs index 2bc8b7d..749f894 100644 --- a/src/MesApp.Api/Controllers/ProductivityController.cs +++ b/src/MesApp.Api/Controllers/ProductivityController.cs @@ -170,6 +170,127 @@ public async Task> LeadTime( return LeadTimeStatistics(rows); } + /// + /// 標準時間の見直し候補(B-60-10-05。ガイド 8.3.2(4))。 + /// + /// 工順の工程ごとに、期間内の作業指示の実績(1個あたり実作業時間・1回あたり段取り時間)の中央値を + /// 工順マスタの現在値と比べ、件数が 以上でずれが % 以上なら候補にする。 + /// 比べる相手は作業指示のスナップショットではなくマスタの現在値(見直す対象そのものであり、 + /// 期間中に改訂済みなら、改訂後の値に対してまだずれているかを見たい)。 + /// マスタは書き換えない——標準を実績に寄せるか、作業のほうを直すかは業務の判断。 + /// + /// + /// 作業時間を記録していない作業指示は0分として混ぜず、件数から外す(標準時間の予実と違い、 + /// ここで0分を混ぜると標準が過大に見えてしまう)。期間は生産実績の開始時刻の製造日。リワーク指図は含めない。 + /// + /// + [HttpGet("standard-time-review")] + public async Task> StandardTimeReview( + [FromQuery] DateOnly? from = null, + [FromQuery] DateOnly? to = null, + [FromQuery] decimal threshold = 20m, + [FromQuery] int minSamples = 3, + CancellationToken ct = default) + { + if (threshold <= 0 || minSamples < 1) + { + return this.BadRequestProblem(ApiText.T("しきい値は0より大きく、件数は1以上で指定してください。")); + } + var fromStart = from is null ? (DateTimeOffset?)null : businessDate.GetRange(from.Value).Start; + var toEnd = to is null ? (DateTimeOffset?)null : businessDate.GetRange(to.Value).End; + + var produced = (await db.ProductionRecords.AsNoTracking() + .Where(r => r.WorkOrder!.ManufacturingOrder!.OrderType != ManufacturingOrderType.Rework) + .Select(r => new { r.WorkOrderId, r.StartedAt, Quantity = r.GoodQuantity + r.DefectQuantity }) + .ToListAsync(ct)) + .Where(r => (fromStart is null || r.StartedAt >= fromStart) + && (toEnd is null || r.StartedAt < toEnd)) + .GroupBy(r => r.WorkOrderId) + .ToDictionary(g => g.Key, g => g.Sum(r => r.Quantity)); + var workOrderIds = produced.Keys.ToList(); + + var workOrders = await db.WorkOrders.AsNoTracking() + .Where(w => workOrderIds.Contains(w.Id)) + .Select(w => new { w.Id, w.ProductId, w.ProcessId, w.RoutingSequence }) + .ToListAsync(ct); + // 所要時間の算出はSQLiteに載らないため、時刻のまま取り出してから分に直す + var directMinutes = (await db.WorkTimeRecords.AsNoTracking() + .Where(t => t.Type == WorkTimeType.Direct && t.EndedAt != null + && t.WorkOrderId != null && workOrderIds.Contains(t.WorkOrderId.Value)) + .Select(t => new { WorkOrderId = t.WorkOrderId!.Value, t.StartedAt, EndedAt = t.EndedAt!.Value }) + .ToListAsync(ct)) + .GroupBy(t => t.WorkOrderId) + .ToDictionary(g => g.Key, g => g.Sum(t => (decimal)(t.EndedAt - t.StartedAt).TotalMinutes)); + var setupMinutes = (await db.SetupRecords.AsNoTracking() + .Where(s => s.EndedAt != null && workOrderIds.Contains(s.WorkOrderId)) + .Select(s => new { s.WorkOrderId, s.StartedAt, EndedAt = s.EndedAt!.Value }) + .ToListAsync(ct)) + .GroupBy(s => s.WorkOrderId) + .ToDictionary(g => g.Key, g => g.Sum(s => (decimal)(s.EndedAt - s.StartedAt).TotalMinutes)); + + var productIds = workOrders.Select(w => w.ProductId).Distinct().ToList(); + var routings = await db.Routings.AsNoTracking() + .Where(r => productIds.Contains(r.ProductId)) + .Select(r => new + { + r.Id, r.ProductId, r.Sequence, r.ProcessId, + ProductCode = r.Product!.Code, ProductName = r.Product!.Name, + ProcessCode = r.Process!.Code, ProcessName = r.Process!.Name, + r.StandardWorkMinutes, r.StandardSetupMinutes, + }) + .ToListAsync(ct); + + static decimal? Median(List values) + { + if (values.Count == 0) + { + return null; + } + values.Sort(); + var mid = values.Count / 2; + return values.Count % 2 == 1 ? values[mid] : (values[mid - 1] + values[mid]) / 2; + } + static decimal? Deviation(decimal? median, decimal standard) => + median is { } m && standard > 0 ? Math.Round((m - standard) / standard * 100, 1) : null; + bool Off(int samples, decimal? median, decimal? deviation, decimal standard) => + samples >= minSamples && median is { } m + && (deviation is { } d ? Math.Abs(d) >= threshold : standard == 0 && m > 0); + + var rows = routings + .Select(r => + { + // 工順が変わって工程順序と工程が一致しない作業指示は、この工程の実績として数えない + var mine = workOrders + .Where(w => w.ProductId == r.ProductId && w.RoutingSequence == r.Sequence && w.ProcessId == r.ProcessId) + .ToList(); + var work = mine + .Where(w => directMinutes.GetValueOrDefault(w.Id) > 0 && produced[w.Id] > 0) + .Select(w => directMinutes[w.Id] / produced[w.Id]) + .ToList(); + var setup = mine + .Where(w => setupMinutes.ContainsKey(w.Id)) + .Select(w => setupMinutes[w.Id]) + .ToList(); + var workMedian = Median(work); + var setupMedian = Median(setup); + var workDeviation = Deviation(workMedian, r.StandardWorkMinutes); + var setupDeviation = Deviation(setupMedian, r.StandardSetupMinutes); + return new StandardTimeReviewRow(r.Id, r.ProductCode, r.ProductName, r.Sequence, + r.ProcessCode, r.ProcessName, + r.StandardWorkMinutes, work.Count, workMedian is { } wm ? Math.Round(wm, 2) : null, workDeviation, + r.StandardSetupMinutes, setup.Count, setupMedian is { } sm ? Math.Round(sm, 2) : null, setupDeviation, + Off(work.Count, workMedian, workDeviation, r.StandardWorkMinutes) + || Off(setup.Count, setupMedian, setupDeviation, r.StandardSetupMinutes)); + }) + .Where(r => r.WorkSampleCount + r.SetupSampleCount > 0) + .OrderByDescending(r => r.IsCandidate) + .ThenByDescending(r => Math.Max(Math.Abs(r.WorkDeviationRate ?? 0), Math.Abs(r.SetupDeviationRate ?? 0))) + .ThenBy(r => r.ProductCode).ThenBy(r => r.Sequence) + .ToList(); + + return new StandardTimeReviewResponse(threshold, minSamples, rows); + } + /// リードタイムの要約・度数分布・異常値(四分位は最近順位法。4件未満では異常値を判定しない) private static LeadTimeResponse LeadTimeStatistics(List rows) { diff --git a/src/MesApp.Api/Localization/ApiText.en.resx b/src/MesApp.Api/Localization/ApiText.en.resx index 027c36d..0b2dc84 100644 --- a/src/MesApp.Api/Localization/ApiText.en.resx +++ b/src/MesApp.Api/Localization/ApiText.en.resx @@ -2022,4 +2022,7 @@ The required skill ID does not exist. + + Specify a threshold greater than 0 and a count of at least 1. + diff --git a/src/MesApp.Client.Web/Pages/Productivity.razor b/src/MesApp.Client.Web/Pages/Productivity.razor index 57ab06f..7516f0f 100644 --- a/src/MesApp.Client.Web/Pages/Productivity.razor +++ b/src/MesApp.Client.Web/Pages/Productivity.razor @@ -120,6 +120,56 @@ else } +@* 標準時間の見直し候補(ガイド 8.3.2(4))。マスタは書き換えず、見直すきっかけだけを示す *@ +
    +

    @L["標準時間の見直し候補"]

    +

    + @L["工順の工程ごとに、実績の中央値(1個あたり作業時間・1回あたり段取り時間)を工順マスタの現在値と比べる。作業時間を記録していない作業指示は数えない。標準を実績に合わせるか、作業のほうを改めるかは現場で判断する。"] +

    + @if (_review is null) + { +

    @L["読み込み中..."]

    + } + else if (_review.Rows.Count == 0) + { +

    @L["対象期間に作業時間・段取りの記録がある作業指示はありません。"]

    + } + else + { + + + + + + + + + + @foreach (var row in _review.Rows) + { + + + + + + + + + + + + } + +
    @L["品目"]@L["工程"]@L["標準作業(分/個)"]@L["実績の中央値"]@L["ずれ"]@L["標準段取り(分/回)"]@L["実績の中央値"]@L["ずれ"]
    @row.ProductCode @row.ProductName@row.Sequence. @row.ProcessCode @row.ProcessName@row.StandardWorkMinutes.ToString("0.##")@(row.MedianWorkMinutes is { } w ? L["{0}({1} 件)", w.ToString("0.##"), row.WorkSampleCount].Value : "-")@Deviation(row.WorkDeviationRate)@row.StandardSetupMinutes.ToString("0.##")@(row.MedianSetupMinutes is { } s ? L["{0}({1} 件)", s.ToString("0.##"), row.SetupSampleCount].Value : "-")@Deviation(row.SetupDeviationRate) + @if (row.IsCandidate) + { + @L["見直し候補"] + } +
    +

    @L["ずれが {0}% 以上で、作業指示が {1} 件以上あるものを見直し候補とする。標準の変更はマスタ管理の工順で行う。", _review.ThresholdPercent.ToString("0.#"), _review.MinSamples]

    + } +
    + @* 製造リードタイムの分布(ガイド 8.3.2)。平均では見えないばらつきと異常値を、完了した指図の分布で見る *@

    @L["製造リードタイム"]

    @@ -201,6 +251,9 @@ else @code { private ProductivitySummaryResponse? _summary; private LeadTimeResponse? _leadTime; + private StandardTimeReviewResponse? _review; + + private static string Deviation(decimal? rate) => rate is { } r ? $"{r:+0.#;-0.#;0} %" : "-"; /// 分布の棒(納期内・納期なしと、納期遅れを積み上げる) private List LeadTimeBars => _leadTime is null ? [] : @@ -227,5 +280,6 @@ else var suffix = query.Count > 0 ? "?" + string.Join("&", query) : string.Empty; _summary = await Http.GetFromJsonAsync("api/productivity" + suffix); _leadTime = await Http.GetFromJsonAsync("api/productivity/lead-time" + suffix); + _review = await Http.GetFromJsonAsync("api/productivity/standard-time-review" + suffix); } } diff --git a/src/MesApp.Client.Web/Shared/UiText.en.resx b/src/MesApp.Client.Web/Shared/UiText.en.resx index 09ac50f..dd40863 100644 --- a/src/MesApp.Client.Web/Shared/UiText.en.resx +++ b/src/MesApp.Client.Web/Shared/UiText.en.resx @@ -4296,4 +4296,34 @@ Late + + Standard time review candidates + + + For each routing step, the median of the actuals (work time per unit and setup time per setup) is compared with the current routing master. Work orders without recorded work time are not counted. Whether to align the standard with the actuals or to change the work itself is decided on the shop floor. + + + No work orders with work time or setup records in the selected period. + + + Standard work (min/unit) + + + Actual median + + + Deviation + + + Standard setup (min/setup) + + + {0} ({1}) + + + Review candidate + + + Steps with a deviation of {0}% or more and at least {1} work orders are review candidates. Change the standards in the routing under master management. + diff --git a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs index 5493dc0..12c441d 100644 --- a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs +++ b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs @@ -196,6 +196,39 @@ public record LeadTimeResponse( /// リードタイムの長い順 List Orders); +// ---- 標準時間の見直し候補(B-60-10-05。ガイド 8.3.2(4) 業務ルールの定期的な再設計)---- + +/// +/// 工順の工程1つ分の標準時間と実績の比較。標準は工順マスタの現在値(見直す対象そのもの)、 +/// 実績は期間内の作業指示ごとの値の中央値(1件の極端な記録に引きずられないように) +/// +public record StandardTimeReviewRow( + int RoutingId, string ProductCode, string ProductName, int Sequence, string ProcessCode, string ProcessName, + /// 標準作業時間(分/個。工順マスタの現在値) + decimal StandardWorkMinutes, + /// 作業時間の実績がある作業指示の数(直接作業時間と産出数の両方があるもの) + int WorkSampleCount, + /// 1個あたり実作業時間の中央値(分)= 直接作業時間 ÷(良品数+不良数) + decimal? MedianWorkMinutes, + /// 標準に対するずれ(%)。標準が0分ならnull + decimal? WorkDeviationRate, + /// 標準段取り時間(分/回。工順マスタの現在値) + decimal StandardSetupMinutes, + int SetupSampleCount, + /// 作業指示1件あたり段取り時間の中央値(分) + decimal? MedianSetupMinutes, + decimal? SetupDeviationRate, + /// 見直し候補か(件数が足り、作業か段取りのずれがしきい値以上。標準0分で実績があるものも含む) + bool IsCandidate); + +public record StandardTimeReviewResponse( + /// 候補とみなすずれ(%) + decimal ThresholdPercent, + /// 候補とみなすのに必要な作業指示の数 + int MinSamples, + /// 候補を先に、ずれの大きい順 + List Rows); + // ---- 遅延検知(A-30-20-01)---- /// 遅れの種類 diff --git a/tests/MesApp.Api.Tests/ProductionTests.cs b/tests/MesApp.Api.Tests/ProductionTests.cs index c8728a2..79a48b6 100644 --- a/tests/MesApp.Api.Tests/ProductionTests.cs +++ b/tests/MesApp.Api.Tests/ProductionTests.cs @@ -827,6 +827,57 @@ async Task RecordAsync(int workOrderId, DateTimeOffset start, int? locationId = Assert.Equal(10, withOutlier.MaxDays); } + [Fact] + public async Task 標準時間の見直し候補は実績の中央値が工順マスタから外れた工程を件数が足りるときだけ挙げる() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + var started = DateTimeOffset.Now.AddHours(-12); + + // 工順1段目(標準 作業30分/個)を3指図で、1個あたり60分・60分・66分で行う → 中央値60分(+100%) + // 工順2段目は1指図だけ時間を記録する(件数不足で候補にしない)。時間を記録しない作業指示は件数に入れない + var minutes = new[] { 600, 600, 660 }; + for (var i = 0; i < 3; i++) + { + var order = await Phase3TestData.CreateReleasedOrderAsync(admin, ctx.ProductId, 10m); + var first = order.WorkOrders[0].Id; + (await admin.PostAsJsonAsync($"/api/work-orders/{first}/production-records", + new ProductionRecordRequest(10m, 0m, started, started.AddHours(1), null, false))).EnsureSuccessStatusCode(); + (await admin.PostAsJsonAsync("/api/work-time-records", + new WorkTimeRequest(WorkTimeType.Direct, null, first, started, started.AddMinutes(minutes[i]), null))) + .EnsureSuccessStatusCode(); + var second = order.WorkOrders[1].Id; + (await admin.PostAsJsonAsync($"/api/work-orders/{second}/production-records", + new ProductionRecordRequest(10m, 0m, started, started.AddHours(1), ctx.ProductLocationId, false))).EnsureSuccessStatusCode(); + if (i == 0) + { + (await admin.PostAsJsonAsync("/api/work-time-records", + new WorkTimeRequest(WorkTimeType.Direct, null, second, started, started.AddMinutes(500), null))) + .EnsureSuccessStatusCode(); + } + } + + var review = await admin.GetFromJsonAsync("/api/productivity/standard-time-review"); + + var step1 = Assert.Single(review!.Rows, r => r.Sequence == 1); + Assert.True(step1.IsCandidate); + Assert.Equal(3, step1.WorkSampleCount); + Assert.Equal(60m, step1.MedianWorkMinutes); + Assert.Equal(100m, step1.WorkDeviationRate); + var step2 = Assert.Single(review.Rows, r => r.Sequence == 2); + Assert.False(step2.IsCandidate); + Assert.Equal(1, step2.WorkSampleCount); + Assert.Equal(step1.RoutingId, review.Rows[0].RoutingId); // 候補が先 + + // しきい値を上げれば候補から外れる。マスタは書き換えない + var loose = await admin.GetFromJsonAsync( + "/api/productivity/standard-time-review?threshold=150"); + Assert.DoesNotContain(loose!.Rows, r => r.IsCandidate); + Assert.Equal(HttpStatusCode.BadRequest, + (await admin.GetAsync("/api/productivity/standard-time-review?threshold=0")).StatusCode); + } + [Fact] public async Task 納期超過と標準時間超過の作業指示を遅延として拾える() { From 01619abf665d6e2e94020aaf30ba006996e96168 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:59:17 +0900 Subject: [PATCH 7/9] =?UTF-8?q?=E3=82=A8=E3=83=8D=E3=83=AB=E3=82=AE?= =?UTF-8?q?=E3=83=BC=E7=AE=A1=E7=90=86=E3=82=92=EF=BC=BB=E5=B0=86=E6=9D=A5?= =?UTF-8?q?=E6=8B=A1=E5=BC=B5=EF=BC=BD=E3=81=A8=E3=81=97=E3=81=A6=E4=BB=95?= =?UTF-8?q?=E6=A7=98=E3=81=AE=E7=AF=84=E5=9B=B2=E3=81=AB=E6=98=8E=E8=A8=98?= =?UTF-8?q?=E3=81=99=E3=82=8B=EF=BC=88GR3-06=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec.md にエネルギーの記述が無く、対象かどうかが読めなかった。計測・総量監視は FEMS の領域とし、MES は製造指図別の分母を渡す役割に限る形を記録した。Spec.md 改訂134。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 3 +++ docs-dev/GuideReview3.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Spec.md b/Spec.md index 2c55522..ecbbc22 100644 --- a/Spec.md +++ b/Spec.md @@ -128,6 +128,7 @@ 改訂131: 2026-09-23(**管理図と工程能力指数を実装した**(`api/quality/control-chart`。C-50-10-01〜02)。3.3節は「初期は基本的な管理図表示」を実装対象とし、6.1節の品質分析画面にも管理図があったが、実装は無かった。検査実績の測定値を検査指示ごとの群にまとめ、X̄-R(群の大きさ1なら X-Rs)管理図と Cp・Cpk を返す。新規エンティティは無い。計算は `Api/Services/ControlChartCalculator`(係数表は JIS Z 9020-2)。品質分析画面に検査項目を選ぶ管理図の欄を追加した。3.3節・6.1節に反映) 改訂132: 2026-09-23(**製造リードタイムの分布を生産性モニタリングに追加した**(`api/productivity/lead-time`。B-60-10-05)。納期の管理が指図の納期超過フラグだけで、実績リードタイムのばらつきと異常値が見えなかった(実践的MES導入ガイド 8.3.2 の納期実績の見える化)。着手・完了は状態履歴ではなく作業の記録の時刻で決め、製造日の差で数える。新規エンティティは無い。3.2節・6.1節に反映) 改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) +改訂134: 2026-09-23(1.3節に**エネルギー管理を[将来拡張]とする**ことを明記した。仕様にエネルギーの記述が無く、対象かどうかが読めなかった(実践的MES導入ガイド 3.4.2・4.4.7・7.2.2 は、MES の役割を FEMS へ製造オーダ別の分母を渡すことと位置づける)。計測・総量監視・ユーティリティ制御は FEMS/FMCS の領域として持たず、実装する場合は設備×期間の使用量をCSVで受け取り稼働区間の時間比で作業指示へ按分する形とした。機能の追加・変更は無い) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -163,6 +164,8 @@ MES機能のクライアントは**Webブラウザ(Blazorクライアント) HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組み込み(G-20-20-02、G-30-20-03)で対応する。生産技術〔I〕はBOP/BOE情報のMES登録(I-10-20-02、I-30-20-02/13)・保全台帳整備(I-10-20-05)・工程変更反映(I-50-30-01/03)、設計〔J〕は設計変更反映時の在庫・生産状況確認(J-40-40-01/03)として各モジュールに包含する。購買・資材〔K〕・工場経理〔L〕は対象外。 +**エネルギー管理(HSE〔G〕の環境)は[将来拡張]とする。** 電力・ガス・圧縮空気などの計測と総量の監視、ユーティリティ設備の制御は FEMS/FMCS の領域であり、本システムは持たない。MES の役割は、FEMS が持たない**「どの製造指図・品目・工程で使ったか」の分母**(設備ごとの稼働区間と、そこに紐づく作業指示・産出数。`EquipmentLog.WorkOrderId`)を渡すことにある。実装する場合は、設備×期間の使用量を FEMS からCSVで受け取り、同じ設備の稼働区間の時間比で作業指示へ按分して、品目別・指図別の原単位(kWh/個など)を出す形とする(OEEの按分と同じ考え方。3.5節)。計測値を直接収集する経路(センサー・PLC)は作らない。 + **業界固有の規制対応も対象外とする。** とくに医薬・医療機器で求められる電子記録・電子署名規制(21 CFR Part 11 / EU Annex 11)には対応しない(7.6節)。監査ログを持つことと規制に適合することは別である。 --- diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index b530032..8e1b909 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -12,7 +12,7 @@ | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂131) | | GR3-04 | 製造リードタイムの分布 | B. 指標 | **対応済**(Spec.md 改訂132) | | GR3-05 | 標準時間の見直し候補 | B. 指標 | **対応済**(Spec.md 改訂133) | -| GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | 未着手 | +| GR3-06 | エネルギー管理(FEMS)との範囲を明記する | C. 書くだけ | **対応済**(Spec.md 改訂134。[将来拡張]) | ## 総評 From 36d69abae58095759106eeadf642b5234c021630 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:01:57 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=E6=A4=9C=E6=9F=BB=E9=A0=85=E7=9B=AE?= =?UTF-8?q?=E3=81=AB=E5=BF=85=E8=A6=81=E3=82=B9=E3=82=AD=E3=83=AB=E3=82=92?= =?UTF-8?q?=E6=8C=81=E3=81=9F=E3=81=9B=E3=80=81=E6=A4=9C=E6=9F=BB=E5=AE=9F?= =?UTF-8?q?=E7=B8=BE=E3=81=AE=E7=99=BB=E9=8C=B2=E8=80=85=E3=82=92=E7=85=A7?= =?UTF-8?q?=E5=90=88=E3=81=99=E3=82=8B=EF=BC=88GR3-02=20=E6=A4=9C=E6=9F=BB?= =?UTF-8?q?=E5=81=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 必要スキルは発行時に検査指示の対象項目へ写し、発行後のマスタ変更の影響を受けない。 判定は差立・着手・保全と同じ SkillQualificationPolicy。マスタCSVに RequiredSkillCode 列を追加。 Spec.md 改訂135。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 7 +- docs-dev/CodeMap.md | 2 +- docs-dev/GuideReview3.md | 4 +- docs/masters.md | 1 + docs/quality.md | 5 + .../Controllers/InspectionItemsController.cs | 12 +- src/MesApp.Api/Services/InspectionService.cs | 27 +- src/MesApp.Api/Services/MasterCsvKinds.cs | 1 + .../Services/MasterCsvService.Import.cs | 7 +- src/MesApp.Api/Services/MasterCsvService.cs | 4 +- .../Pages/Masters/InspectionItemsTab.razor | 21 +- .../Contracts/Masters/MasterContracts.cs | 7 +- src/MesApp.Core/Entities/Masters.cs | 7 + src/MesApp.Core/Entities/Quality.cs | 7 + .../Configurations/MasterConfigurations.cs | 2 + .../Configurations/QualityConfigurations.cs | 2 + ...505_AddInspectionRequiredSkill.Designer.cs | 4807 ++++++++++++++++ ...260923125505_AddInspectionRequiredSkill.cs | 80 + .../MesAppDbContextModelSnapshot.cs | 24 + ...508_AddInspectionRequiredSkill.Designer.cs | 5006 +++++++++++++++++ ...260923125508_AddInspectionRequiredSkill.cs | 80 + .../MesAppDbContextModelSnapshot.cs | 24 + ...510_AddInspectionRequiredSkill.Designer.cs | 4985 ++++++++++++++++ ...260923125510_AddInspectionRequiredSkill.cs | 80 + .../MesAppDbContextModelSnapshot.cs | 24 + tests/MesApp.Api.Tests/MasterCsvTests.cs | 18 + tests/MesApp.Api.Tests/QualityTests.cs | 42 + 27 files changed, 15270 insertions(+), 16 deletions(-) create mode 100644 src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.Designer.cs create mode 100644 src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.cs create mode 100644 src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.Designer.cs create mode 100644 src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.cs create mode 100644 src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.Designer.cs create mode 100644 src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.cs diff --git a/Spec.md b/Spec.md index e096447..f4cc0bc 100644 --- a/Spec.md +++ b/Spec.md @@ -133,6 +133,7 @@ 改訂132: 2026-09-23(**製造リードタイムの分布を生産性モニタリングに追加した**(`api/productivity/lead-time`。B-60-10-05)。納期の管理が指図の納期超過フラグだけで、実績リードタイムのばらつきと異常値が見えなかった(実践的MES導入ガイド 8.3.2 の納期実績の見える化)。着手・完了は状態履歴ではなく作業の記録の時刻で決め、製造日の差で数える。新規エンティティは無い。3.2節・6.1節に反映) 改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) 改訂134: 2026-09-23(1.3節に**エネルギー管理を[将来拡張]とする**ことを明記した。仕様にエネルギーの記述が無く、対象かどうかが読めなかった(実践的MES導入ガイド 3.4.2・4.4.7・7.2.2 は、MES の役割を FEMS へ製造オーダ別の分母を渡すことと位置づける)。計測・総量監視・ユーティリティ制御は FEMS/FMCS の領域として持たず、実装する場合は設備×期間の使用量をCSVで受け取り稼働区間の時間比で作業指示へ按分する形とした。機能の追加・変更は無い) +改訂135: 2026-09-23(**検査項目・基準に必要スキル・資格を持たせ、検査実績の登録時に検査員と照合する**ようにした(`InspectionItem.RequiredSkillId`。F-20-30-01)。改訂130で保全を実装した際、検査の見直し(改訂125〜128)と衝突するため後回しにしていたもの。必要スキルは発行時に `InspectionOrderItem` へ写し(作業指示の必要スキルと同じ)、発行後にマスタを変えても発行済みの検査の照合条件は変わらない。写す値なので、CSV取込で変えると版数が上がる。判定は差立・着手・保全と同じ `SkillQualificationPolicy`。実績の訂正では照合しない。マスタCSVの検査項目に `RequiredSkillCode` 列を追加(列が無ければ現在値のまま)。3.6節・5.1節・5.4節に反映) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -324,7 +325,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - **工場従業員情報管理**:従業員データの登録・変更・削除(F-10-10-01〜02。MESユーザーアカウントと統合管理)。**所属(部署・課)・作業場所(作業区)・所属する直**を持つ - **勤務シフト(直)の定義**:昼勤・夜勤などの時間帯を定義する(F-10-10-01)。3.9節の製造日(業務日付)が夜勤を前提にしているのに対し、「その実績がどの直か」を表す。**誰がいつどの直に入るかの勤務計画は持たない**(勤怠管理 F-30-10-01〜02 は本システムの対象外。ガイドでも△) - **スキル・資格管理**:スキル・資格の定義(F-20-10-01)、従業員への資格・スキル登録(使用可能設備・製造可能品・有効期限等。F-20-10-02〜03)、有効期限接近の警告(F-20-10-04)、有効期間の管理(F-20-10-05) -- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03)。製造では差立(割当者)と着手(着手する者)の両方で、保全では保全手順書の必要スキルを保全実績の登録者(実施者)に対して照合する(`SkillQualificationPolicy`)。**検査の照合は未実装**(検査基準に必要スキルを持たない) +- **資格・スキル照合**:差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合(F-20-30-01、B-30-10-03)。製造では差立(割当者)と着手(着手する者)の両方で、保全では保全手順書の必要スキルを保全実績の登録者(実施者)に対して、検査では検査項目・基準の必要スキル(発行時点のスナップショット)を検査実績の登録者(検査員)に対して照合する(`SkillQualificationPolicy`)。検査員の割当は持たないため、検査の照合は実績の登録時だけ。実績の訂正(責任者の操作)では照合しない - **間接時間管理**:段取り・部材準備・設備メンテ等、製造に直接紐づかない作業時間の記録(F-30-20-02) - **システム管理者を失わない**:**有効なシステム管理者が0人になる変更は行えない**(無効化・システム管理者ロールの剥奪のいずれも)。管理者がいなくなるとユーザー管理も権限付与もできず、初期セットアップAPIはUserが0件のときしか動かない(2.2節E)ため、DBを直接操作する以外に復旧手段がなくなる。単独PC配布(7.8節)では管理者が1人だけの運用が普通で、現実に起こりうる - 判定は1か所(`Api/Policies/LastAdminPolicy`)に置き、ユーザー管理画面(`PUT api/users/{id}`)とマスタCSV取込(users)の双方から呼ぶ @@ -435,7 +436,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと | Tool(治工具マスタ) | 治工具コード、名称、種別(型/切削工具/検査工具等)、寿命閾値(使用回数/時間)、状態(使用可能/使用中/メンテナンス中/廃棄。**使用中は引当(`ToolIssue`)から決まり、マスタ編集・CSV取込では付けも外しもできない**。引当中の治工具は使用可能にできず、メンテナンス中・廃棄への変更はできる)(E-60) | | Location(ロケーション) | ロケーションコード、倉庫/エリア区分(部材倉庫/工程内/製品倉庫/出荷場)、**所属する作業区**、棚番(D-50-20-01) | | ControlItem(工程管理項目) | 工程管理項目コード、名称、単位、**指示値**、許容下限/上限、版数、有効フラグ。対象品目・対象工程は持たず、工順(BOP)の工程から紐付ける(`RoutingControlItem`)。温度・回転数など製造時に記録すべき条件の定義(B-30-30-04)。検査項目と同じ形にしているのは、「指示値と実績値」の関係が「検査パラメータと検査結果」と同じ構造で、判定の考え方も揃うため | -| InspectionItem(検査項目・基準) | 検査項目コード、対象品目/工程、検査種別(受入/工程内/完成品/サンプル)、規格値(上限/下限/基準値)、検査方法、サンプリング数、版数(C-10-10)。対象品目/工程は**両方を「かつ」で組み合わせ**、空の方は問わない(5.7節「検査基準の対象」) | +| InspectionItem(検査項目・基準) | 検査項目コード、対象品目/工程、検査種別(受入/工程内/完成品/サンプル)、規格値(上限/下限/基準値)、検査方法、サンプリング数、**必要スキルID**(任意。検査実績の登録者と照合。F-20-30-01)、版数(C-10-10)。対象品目/工程は**両方を「かつ」で組み合わせ**、空の方は問わない(5.7節「検査基準の対象」) | | InspectionDevice(検査機・測定器) | 検査機コード、名称、製造番号、設置場所、最終校正日、**次回校正期限**、校正周期(日)、備考、状態(C-20-50-03)。校正期限切れ・無効化された機器は検査実績に使えない | | InspectionDeviceCalibration(校正実施記録) | 検査機ID、校正日、次回期限、結果・所見、実施者(C-20-50-03)。現在値の上書きだけでは「いつ誰がどの結果で校正したか」が残らないため履歴に残す | | Checklist(チェックリストマスタ) | チェックリストコード、名称、適用区分(共通/工程/品目/段取り/保全/HSE)、項目一覧(B-30-10、G-20/G-30) | @@ -483,7 +484,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと | エンティティ | 主な項目 | |---|---| | InspectionOrder(検査指示) | 検査指示番号、検査種別(受入/工程内/完成品/サンプル/再検査)、対象(ロットID/作業指示ID)、検査項目セット、依頼者、状態(指示/実施中/判定済/承認済)(C-20) | -| InspectionOrderItem(検査指示の対象項目) | 検査指示ID、検査項目ID(参照)、**発行時点の基準のスナップショット**(検査項目コード・名称・版数・規格値上限/下限・基準値・検査方法・サンプリング数)。自動判定・画面表示・検査成績書はこのスナップショットを使い、マスタは新規指示の作成時にのみ参照する(C-20、C-10-10-03) | +| InspectionOrderItem(検査指示の対象項目) | 検査指示ID、検査項目ID(参照)、**発行時点の基準のスナップショット**(検査項目コード・名称・版数・規格値上限/下限・基準値・検査方法・サンプリング数・**必要スキル**)。自動判定・検査員のスキル照合・画面表示・検査成績書はこのスナップショットを使い、マスタは新規指示の作成時にのみ参照する(C-20、C-10-10-03) | | InspectionResult(検査実績) | 検査指示ID、検査項目ID、測定値、判定(合格/不合格)、検査者、検査日時、承認者、**使用した検査機ID**(任意。C-20-50-03。校正期限切れの機器は登録時に弾くため、残るのは当時有効だった機器に限られる)(C-20) | | InspectionResultCorrection(検査実績の訂正履歴) | 検査実績ID、検査指示ID、訂正前(測定値・記録・判定)、訂正後(測定値・記録・判定)、訂正理由、訂正者、訂正日時。訂正のたびに1レコード追加(C-20-50-07) | | NonconformanceReport(不適合・逸脱) | 発生元(生産実績/検査/受入)、対象ロットID、逸脱・不具合内容、原因区分+詳細(文章・写真添付)、対応指示(リワーク/保留/廃棄/特採)、対応実績、承認者(特採時)、状態(B-40-30、C-30) | diff --git a/docs-dev/CodeMap.md b/docs-dev/CodeMap.md index c01cc72..d2d376c 100644 --- a/docs-dev/CodeMap.md +++ b/docs-dev/CodeMap.md @@ -47,7 +47,7 @@ | 工程管理項目マスタ(指示値・許容範囲。版数付き) | B-30-30-04 | `ControlItemsController.cs` `api/control-items`
    展開時のスナップショット取得は `WorkOrdersController.ControlItems` `api/work-orders/{id}/control-items` | Masters.cs: ControlItem(対象品目・工程は持たない)/ **RoutingControlItem**(工順の工程への紐付け。展開はこの紐付けどおり。紐付け中は無効化できない:`MasterDeactivationPolicy`)
    Production.cs: **WorkOrderControlItem**(展開時点の指示値。判定・表示はこちらを使い、マスタ現在値を参照しない) | `/masters` `Masters/ControlItemsTab.razor`
    工順への紐付けは `Masters/Products/ProductRoutingEditor.razor` | `Tests/MasterTests.cs` / `MasterCsvTests.cs` / `ProductionTests.cs` | | 検査機・測定器マスタ/校正管理 | C-20-50-03 | `InspectionDevicesController.cs` `api/inspection-devices`
    校正の記録は `POST {id}/calibrations`(次回期限は校正周期から自動)/期限接近は `GET expiring?withinDays=` | Masters.cs: InspectionDevice(現在の校正状態)/InspectionDeviceCalibration(実施履歴) | `/masters` `Masters/InspectionDevicesTab.razor` | `Tests/MasterTests.cs` / `MasterCsvTests.cs` | | 検査項目・基準マスタ | C-10-10 | `InspectionItemsController.cs` `api/inspection-items`
    対象品目/工程の意味(**「かつ」で組み合わせ、空は問わない**)と登録時の制約は `Api/Policies/InspectionItemPolicy.cs`(単票API・マスタCSV・検査指示の発行で共通) | Masters.cs: InspectionItem | `/masters` `Masters/InspectionItemsTab.razor` | `Tests/MasterTests.cs` | -| 検査指示・実績・判定・成績書 | C-20 | `InspectionOrdersController.cs` `api/inspection-orders`(発行・実績・判定の処理は `Api/Services/InspectionService.cs`。実績登録時に**検査機の校正期限を判定**:`InspectionDeviceCalibrationPolicy`。発行できるロットの状態・判定前の指示の重複は `InspectionLotPolicy`。合否(手の指定は自動判定と食い違えば拒否。訂正も同じ)とサンプリング数(判定の下限)は `InspectionJudgmentPolicy`。取消はロットを**発行前のステータス**へ戻す) | `Core/Entities/Quality.cs`
    InspectionOrder / InspectionOrderItem(**発行時点の基準スナップショット**。判定・成績書はこちらを使い、マスタ現在値を参照しない:Spec.md 5.7) / InspectionResult / **InspectionResultCorrection**(訂正前の記録。詳細画面・成績書に表示) | `/inspections` `Inspections.razor`
    `/inspections/{id}` `InspectionDetail.razor`
    `/print/inspection/{id}` `Print/InspectionCertificate.razor` | `Tests/QualityTests.cs` | +| 検査指示・実績・判定・成績書 | C-20 | `InspectionOrdersController.cs` `api/inspection-orders`(発行・実績・判定の処理は `Api/Services/InspectionService.cs`。実績登録時に**検査機の校正期限を判定**:`InspectionDeviceCalibrationPolicy`。**検査員のスキル照合**も実績登録時(発行時に写した `InspectionOrderItem.RequiredSkillId` → `SkillQualificationPolicy`)。発行できるロットの状態・判定前の指示の重複は `InspectionLotPolicy`。合否(手の指定は自動判定と食い違えば拒否。訂正も同じ)とサンプリング数(判定の下限)は `InspectionJudgmentPolicy`。取消はロットを**発行前のステータス**へ戻す) | `Core/Entities/Quality.cs`
    InspectionOrder / InspectionOrderItem(**発行時点の基準スナップショット**。判定・成績書はこちらを使い、マスタ現在値を参照しない:Spec.md 5.7) / InspectionResult / **InspectionResultCorrection**(訂正前の記録。詳細画面・成績書に表示) | `/inspections` `Inspections.razor`
    `/inspections/{id}` `InspectionDetail.razor`
    `/print/inspection/{id}` `Print/InspectionCertificate.razor` | `Tests/QualityTests.cs` | | 不適合・逸脱管理(特採・廃棄・保留) | C-30 / B-40-30 | `NonconformanceController.cs` `api/nonconformances`
    記録・対応指示・承認と不適合の連鎖は `Api/Services/NonconformanceService.cs` | Quality.cs: NonconformanceReport | `/nonconformances` `Nonconformances.razor` | `Tests/QualityTests.cs` | | 品質分析(不良理由別・品目別・工程別・**直別**・期間別) | C-40-10 | `QualityAnalysisController.cs` `api/quality/summary`
    管理図・工程能力(C-50-10-01〜02)は `ControlChartController.cs` `api/quality/control-chart` → 計算は `Api/Services/ControlChartCalculator.cs`(**群=検査指示**。係数表・群の揃え方・9点連続の判定はここだけ)。描画は `Web/Shared/Charts/ControlLineChart.razor` | Execution.cs: ProductionDefect(不良理由別の内訳)
    ProductionRecord.`ShiftId`(**記録時に固定**。集計で引き直さない) | `/quality-analysis` `QualityAnalysis.razor`
    `Web/Shared/BarMeter.razor` | `Tests/QualityTests.cs`・`ControlChartCalculatorTests.cs` | | 不良理由マスタ | C-40-10-01 | `DefectReasonsController.cs` `api/defect-reasons` | Masters.cs: DefectReason | `/masters` `Masters/DefectReasonsTab.razor` | `Tests/MasterTests.cs` / `MasterCsvTests.cs` | diff --git a/docs-dev/GuideReview3.md b/docs-dev/GuideReview3.md index 8e1b909..096a685 100644 --- a/docs-dev/GuideReview3.md +++ b/docs-dev/GuideReview3.md @@ -8,7 +8,7 @@ | ID | 項目 | 区分 | 状態 | |:--|:--|:--|:--| | GR3-01 | 着手時のスキル・資格照合 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂129) | -| GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **保全は対応済**(Spec.md 改訂130)。検査は `inspection-fixes` 合流後 | +| GR3-02 | 検査・保全のスキル照合(仕様と実装のずれ) | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂130=保全、改訂135=検査) | | GR3-03 | 基本的な管理図(X̄-R)と工程能力指数 | A. 仕様にあるが未実装 | **対応済**(Spec.md 改訂131) | | GR3-04 | 製造リードタイムの分布 | B. 指標 | **対応済**(Spec.md 改訂132) | | GR3-05 | 標準時間の見直し候補 | B. 指標 | **対応済**(Spec.md 改訂133) | @@ -38,7 +38,7 @@ SaaS型提供・オフライン同期(6.3.2)は配布方式(`Spec.md` 7.8 - 現状: `Spec.md` 3.6 は「差立・検査・保全の各業務で担当者に必要スキル・資格があるかを照合」と書くが、検査基準・保全計画に必要スキルの項目自体が無い。 - 対応: 項目を足して照合するか、`Spec.md` の記述を実装に合わせるかを決める。 - **保全は対応済**(改訂130)。保全手順書に必要スキルを持たせ、保全実績の登録者を照合する。 - **検査は保留**:`inspection-fixes` ブランチ(未合流)が検査基準の選び方と `InspectionService` を大きく書き換えているため、合流後に検査基準(または検査項目)へ必要スキルを足し、検査実績の登録者を照合する。それまでは `Spec.md` 3.6 に未実装と明記した。 +- **検査も対応済**(改訂135)。`inspection-fixes` を合流させてから、検査項目・基準に必要スキルを持たせ、発行時に検査指示へ写して検査実績の登録者を照合する。 ### GR3-03 基本的な管理図と工程能力指数(C-50-10-01〜02) diff --git a/docs/masters.md b/docs/masters.md index f3181d1..1263aae 100644 --- a/docs/masters.md +++ b/docs/masters.md @@ -185,6 +185,7 @@ MBOMは、実績入力の**バックフラッシュ**(部材の自動消費) | 基準値 | 目標値(表示用) | | 検査方法 | 目視、ノギスなどの記述 | | サンプリング数 | 判定に必要な最低サンプル数。この数だけサンプル番号の違う実績がそろうまで総合判定できません(多く測るのは構いません) | +| 必要スキル・資格 | 検査に資格が要る項目で指定します。検査実績を登録する人がその資格を持っていないか、期限が切れているとエラーになります(CSVでは `RequiredSkillCode` 列) |

    上限・下限のどちらも設定していない項目は自動判定できません。 diff --git a/docs/quality.md b/docs/quality.md index 978d27a..e40d4eb 100644 --- a/docs/quality.md +++ b/docs/quality.md @@ -98,6 +98,11 @@ next_title: 品質保証 その項目の「判定(手動)」で合格/不合格を選んでください。

    +
    +

    必要スキル・資格が設定された検査項目は、その資格を持つ人しか実績を登録できません。 +照合するのは検査指示を出したときの検査基準の設定です(あとで検査基準を変えても、発行済みの検査には効きません)。

    +
    + ### 総合判定する **全検査項目の実績を登録してから** 判定します。サンプリング数が決まっている項目は、その数だけサンプル番号の違う実績がそろっている必要があります。 diff --git a/src/MesApp.Api/Controllers/InspectionItemsController.cs b/src/MesApp.Api/Controllers/InspectionItemsController.cs index b1d51d2..f2c9501 100644 --- a/src/MesApp.Api/Controllers/InspectionItemsController.cs +++ b/src/MesApp.Api/Controllers/InspectionItemsController.cs @@ -76,6 +76,7 @@ public async Task> Create(InspectionItemReq StandardValue = request.StandardValue, Method = request.Method, SamplingCount = request.SamplingCount, + RequiredSkillId = request.RequiredSkillId, }; db.InspectionItems.Add(i); await db.SaveChangesAsync(ct); @@ -114,6 +115,7 @@ public async Task> Update(int id, Inspectio i.StandardValue = request.StandardValue; i.Method = request.Method; i.SamplingCount = request.SamplingCount; + i.RequiredSkillId = request.RequiredSkillId; i.Version++; // 基準改訂(C-10-10-03) await db.SaveChangesAsync(ct); await auditLogger.LogAsync("Master", "Update", nameof(InspectionItem), id.ToString(), @@ -136,6 +138,10 @@ public Task Deactivate(int id, CancellationToken ct) => { return ApiText.T("存在しない対象工程IDです。"); } + if (request.RequiredSkillId is int skillId && !await db.Skills.AnyAsync(s => s.Id == skillId, ct)) + { + return ApiText.T("存在しない必要スキルIDです。"); + } // 判定条件はマスタCSV取込と共通(片方だけ通る状態を作らない。Spec.md 7.4) return InspectionItemPolicy.CheckDefinition(request.Type, request.TargetProductId, request.TargetProcessId, request.LowerLimit, request.UpperLimit, request.StandardValue); @@ -149,7 +155,8 @@ public Task Deactivate(int id, CancellationToken ct) => private IQueryable BaseQuery() => db.InspectionItems.AsNoTracking() .Include(i => i.TargetProduct) - .Include(i => i.TargetProcess); + .Include(i => i.TargetProcess) + .Include(i => i.RequiredSkill); /// 保存後の応答。対象マスタを読み込み直してコードまで返す private async Task GetResponseAsync(int id, CancellationToken ct) => @@ -160,5 +167,6 @@ private static InspectionItemResponse ToResponse(InspectionItem i) => i.TargetProductId, i.TargetProduct?.Code, i.TargetProcessId, i.TargetProcess?.Code, i.Type, i.LowerLimit, i.UpperLimit, i.StandardValue, - i.Method, i.SamplingCount, i.Version, i.IsActive); + i.Method, i.SamplingCount, i.Version, i.IsActive, + i.RequiredSkillId, i.RequiredSkill?.Code, i.RequiredSkill?.Name); } diff --git a/src/MesApp.Api/Services/InspectionService.cs b/src/MesApp.Api/Services/InspectionService.cs index 0f52afb..c7cef26 100644 --- a/src/MesApp.Api/Services/InspectionService.cs +++ b/src/MesApp.Api/Services/InspectionService.cs @@ -164,6 +164,7 @@ public async Task> CreateAsync( StandardValue = i.StandardValue, Method = i.Method, SamplingCount = i.SamplingCount, + RequiredSkillId = i.RequiredSkillId, }).ToList(), }; db.InspectionOrders.Add(order); @@ -182,7 +183,10 @@ await auditLogger.LogAsync("Quality", "InspectionCreate", nameof(InspectionOrder return Outcome.Ok(order); } - /// 検査実績の登録(C-20-10-03 ほか)。測定値があり規格値が定義されていれば自動判定する + /// + /// 検査実績の登録(C-20-10-03 ほか)。測定値があり規格値が定義されていれば自動判定する。 + /// 検査項目に必要スキル(発行時点)があれば、登録する者を照合する(F-20-30-01) + /// public async Task> AddResultsAsync( int orderId, List requests, string userId, CancellationToken ct) { @@ -203,6 +207,27 @@ public async Task> AddResultsAsync( var itemById = order.Items.ToDictionary(i => i.InspectionItemId); + // 検査員のスキル・資格照合(F-20-30-01)。条件は発行時に写した必要スキルで、登録する者に対して行う + // (差立・着手・保全と同じ SkillQualificationPolicy。検査員の割当は持たないため、照合は登録時だけ) + var requiredSkillIds = requests + .Select(r => itemById.TryGetValue(r.InspectionItemId, out var i) ? i.RequiredSkillId : null) + .Where(id => id is not null).Select(id => id!.Value).Distinct().ToList(); + if (requiredSkillIds.Count > 0) + { + var inspector = await db.Users.FirstAsync(u => u.Id == userId, ct); + var skills = await db.Skills.Where(s => requiredSkillIds.Contains(s.Id)).ToListAsync(ct); + var held = await db.UserSkills.Where(s => s.UserId == userId && requiredSkillIds.Contains(s.SkillId)) + .ToDictionaryAsync(s => s.SkillId, ct); + foreach (var skill in skills.OrderBy(s => s.Code, StringComparer.Ordinal)) + { + if (SkillQualificationPolicy.Check(inspector.DisplayName, skill, held.GetValueOrDefault(skill.Id), + businessDate.Today) is { } skillError) + { + return Outcome.Invalid(skillError); + } + } + } + // 校正期限を過ぎた検査機で測った結果は品質保証の根拠にならないため、記録させない // (判定は InspectionDeviceCalibrationPolicy。期限接近の一覧と同じ条件を使う) var deviceIds = requests.Where(r => r.InspectionDeviceId is not null) diff --git a/src/MesApp.Api/Services/MasterCsvKinds.cs b/src/MesApp.Api/Services/MasterCsvKinds.cs index 758e7b8..9ca18e6 100644 --- a/src/MesApp.Api/Services/MasterCsvKinds.cs +++ b/src/MesApp.Api/Services/MasterCsvKinds.cs @@ -111,6 +111,7 @@ public static class MasterCsvKinds new("StandardValue", "基準値", false, null), new("Method", "検査方法", false, null), new("SamplingCount", "サンプリング数", false, null), + new("RequiredSkillCode", "必要スキル・資格コード", false, "登録済みのスキル・資格コード。検査実績を登録する人と照合する"), new("IsActive", "有効", false, "true / false"), new("Version", "版数", false, "出力のみ。基準値が変わる更新で自動採番"), ]), diff --git a/src/MesApp.Api/Services/MasterCsvService.Import.cs b/src/MesApp.Api/Services/MasterCsvService.Import.cs index 01ce040..7148069 100644 --- a/src/MesApp.Api/Services/MasterCsvService.Import.cs +++ b/src/MesApp.Api/Services/MasterCsvService.Import.cs @@ -656,6 +656,8 @@ private async Task ImportInspectionItemsAsync( var productIds = await ProductIdsAsync(ct); var processIds = await db.Processes.AsNoTracking() .ToDictionaryAsync(p => p.Code, p => p.Id, StringComparer.Ordinal, ct); + var skillIds = await db.Skills.AsNoTracking() + .ToDictionaryAsync(s => s.Code, s => s.Id, StringComparer.Ordinal, ct); var seen = new HashSet(StringComparer.Ordinal); foreach (var row in table.Rows) @@ -679,6 +681,7 @@ private async Task ImportInspectionItemsAsync( var standard = reader.NumberOrNull("StandardValue", item.StandardValue); var method = reader.Text("Method", item.Method, 200); var sampling = reader.IntOrNull("SamplingCount", item.SamplingCount, 0); + var requiredSkillId = reader.Reference("RequiredSkillCode", item.RequiredSkillId, skillIds, "スキル・資格"); var isActive = reader.Bool("IsActive", item.IsActive); // 判定条件は単票APIと共通(片方だけ通る状態を作らない。Spec.md 7.4) if (!reader.Failed @@ -694,7 +697,8 @@ private async Task ImportInspectionItemsAsync( // 基準そのものが変わる更新は版数を上げる(C-10-10-03) var criteriaChanged = !isNew && (item.Type != type || item.LowerLimit != lower || item.UpperLimit != upper - || item.StandardValue != standard || item.Method != method || item.SamplingCount != sampling); + || item.StandardValue != standard || item.Method != method || item.SamplingCount != sampling + || item.RequiredSkillId != requiredSkillId); item.Name = name; item.TargetProductId = productId; @@ -705,6 +709,7 @@ private async Task ImportInspectionItemsAsync( item.StandardValue = standard; item.Method = method; item.SamplingCount = sampling; + item.RequiredSkillId = requiredSkillId; item.IsActive = isActive; if (criteriaChanged) { diff --git a/src/MesApp.Api/Services/MasterCsvService.cs b/src/MesApp.Api/Services/MasterCsvService.cs index 3c24007..64c6570 100644 --- a/src/MesApp.Api/Services/MasterCsvService.cs +++ b/src/MesApp.Api/Services/MasterCsvService.cs @@ -148,14 +148,14 @@ public async Task ExportAsync(CsvKindInfo kind, bool includeInactive, Ca private async Task> ExportInspectionItemsAsync(bool includeInactive, CancellationToken ct) { var items = await db.InspectionItems.AsNoTracking() - .Include(i => i.TargetProduct).Include(i => i.TargetProcess) + .Include(i => i.TargetProduct).Include(i => i.TargetProcess).Include(i => i.RequiredSkill) .Where(i => includeInactive || i.IsActive) .OrderBy(i => i.Code).ToListAsync(ct); return [.. items.Select(i => new string?[] { i.Code, i.Name, i.TargetProduct?.Code, i.TargetProcess?.Code, i.Type.ToString(), Num(i.LowerLimit), Num(i.UpperLimit), Num(i.StandardValue), i.Method, Num(i.SamplingCount), - Bool(i.IsActive), i.Version.ToString(CultureInfo.InvariantCulture), + i.RequiredSkill?.Code, Bool(i.IsActive), i.Version.ToString(CultureInfo.InvariantCulture), })]; } diff --git a/src/MesApp.Client.Web/Pages/Masters/InspectionItemsTab.razor b/src/MesApp.Client.Web/Pages/Masters/InspectionItemsTab.razor index f9fbb59..36dabbb 100644 --- a/src/MesApp.Client.Web/Pages/Masters/InspectionItemsTab.razor +++ b/src/MesApp.Client.Web/Pages/Masters/InspectionItemsTab.razor @@ -62,6 +62,16 @@
    +
    + + +
    @@ -84,7 +94,7 @@ { - + @foreach (var item in _items) @@ -99,6 +109,7 @@ +
    @L["コード"]@L["名称"]@L["種別"]@L["対象"]@L["規格値"]@L["方法"]@L["サンプリング"]@L["版数"]
    @L["コード"]@L["名称"]@L["種別"]@L["対象"]@L["規格値"]@L["方法"]@L["サンプリング"]@L["必要スキル・資格"]@L["版数"]
    @(item.LowerLimit?.ToString() ?? "-") 〜 @(item.UpperLimit?.ToString() ?? "-") @(item.Method ?? "-") @(item.SamplingCount?.ToString() ?? "-")@(item.RequiredSkillName ?? "-") v@(item.Version) @@ -120,6 +131,7 @@ @code { private List? _items; private List _processes = []; + private List _skills = []; private int? _editingId; private string _code = string.Empty; @@ -132,6 +144,7 @@ private decimal? _standard; private string? _method; private int? _sampling; + private int _skillId; private string? _error; private string? _message; private bool _busy; @@ -139,6 +152,7 @@ protected override async Task OnInitializedAsync() { _processes = await Http.GetFromJsonAsync>("api/processes") ?? []; + _skills = await Http.GetFromJsonAsync>("api/skills") ?? []; await LoadAsync(); } @@ -158,6 +172,7 @@ _standard = item.StandardValue; _method = item.Method; _sampling = item.SamplingCount; + _skillId = item.RequiredSkillId ?? 0; } private void ResetForm() @@ -173,6 +188,7 @@ _standard = null; _method = null; _sampling = null; + _skillId = 0; } private async Task SaveAsync() @@ -190,7 +206,8 @@ var request = new InspectionItemRequest(_code, _name, _productId == 0 ? null : _productId, _processId == 0 ? null : _processId, - _type, _lower, _upper, _standard, _method, _sampling); + _type, _lower, _upper, _standard, _method, _sampling, + _skillId == 0 ? null : _skillId); var response = _editingId is null ? await Http.PostAsJsonAsync("api/inspection-items", request) : await Http.PutAsJsonAsync($"api/inspection-items/{_editingId}", request); diff --git a/src/MesApp.Core/Contracts/Masters/MasterContracts.cs b/src/MesApp.Core/Contracts/Masters/MasterContracts.cs index a8d0ab2..9d8da7c 100644 --- a/src/MesApp.Core/Contracts/Masters/MasterContracts.cs +++ b/src/MesApp.Core/Contracts/Masters/MasterContracts.cs @@ -238,14 +238,17 @@ public record InspectionItemRequest( decimal? UpperLimit, decimal? StandardValue, string? Method, - int? SamplingCount); + int? SamplingCount, + /// 検査の実施に必要なスキル・資格(検査実績の登録者と照合する。F-20-30-01) + int? RequiredSkillId = null); public record InspectionItemResponse( int Id, string Code, string Name, int? TargetProductId, string? TargetProductCode, int? TargetProcessId, string? TargetProcessCode, InspectionType Type, decimal? LowerLimit, decimal? UpperLimit, decimal? StandardValue, - string? Method, int? SamplingCount, int Version, bool IsActive); + string? Method, int? SamplingCount, int Version, bool IsActive, + int? RequiredSkillId = null, string? RequiredSkillCode = null, string? RequiredSkillName = null); // ---- チェックリスト(Checklist)---- diff --git a/src/MesApp.Core/Entities/Masters.cs b/src/MesApp.Core/Entities/Masters.cs index 85d982a..b20a2f0 100644 --- a/src/MesApp.Core/Entities/Masters.cs +++ b/src/MesApp.Core/Entities/Masters.cs @@ -508,6 +508,13 @@ public class InspectionItem : IDeactivatableMaster /// サンプリング数 public int? SamplingCount { get; set; } + /// + /// 検査の実施に必要なスキル・資格(F-20-30-01)。検査指示の発行時に写し、 + /// 検査実績の登録時に登録者(検査員)と照合する + /// + public int? RequiredSkillId { get; set; } + public SkillMaster? RequiredSkill { get; set; } + /// 版数(基準改訂の管理) public int Version { get; set; } = 1; diff --git a/src/MesApp.Core/Entities/Quality.cs b/src/MesApp.Core/Entities/Quality.cs index bf3b6f4..9ebc408 100644 --- a/src/MesApp.Core/Entities/Quality.cs +++ b/src/MesApp.Core/Entities/Quality.cs @@ -82,6 +82,13 @@ public class InspectionOrderItem public string? Method { get; set; } public int? SamplingCount { get; set; } + + /// + /// 必要スキル・資格(発行時点)。検査実績の登録者と照合する(F-20-30-01)。 + /// 発行後に基準の必要スキルが変わっても、この検査の照合条件は変わらない(作業指示の必要スキルと同じ) + /// + public int? RequiredSkillId { get; set; } + public SkillMaster? RequiredSkill { get; set; } } /// 検査実績(Spec.md 5.4 InspectionResult。C-20) diff --git a/src/MesApp.Infrastructure/Configurations/MasterConfigurations.cs b/src/MesApp.Infrastructure/Configurations/MasterConfigurations.cs index a4c085d..4c38973 100644 --- a/src/MesApp.Infrastructure/Configurations/MasterConfigurations.cs +++ b/src/MesApp.Infrastructure/Configurations/MasterConfigurations.cs @@ -247,6 +247,8 @@ public void Configure(EntityTypeBuilder e) e.Property(x => x.Code).HasMaxLength(50); e.Property(x => x.Name).HasMaxLength(200); e.Property(x => x.Method).HasMaxLength(500); + e.HasOne(x => x.RequiredSkill).WithMany().HasForeignKey(x => x.RequiredSkillId) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/src/MesApp.Infrastructure/Configurations/QualityConfigurations.cs b/src/MesApp.Infrastructure/Configurations/QualityConfigurations.cs index 1862ead..3cd7c32 100644 --- a/src/MesApp.Infrastructure/Configurations/QualityConfigurations.cs +++ b/src/MesApp.Infrastructure/Configurations/QualityConfigurations.cs @@ -55,6 +55,8 @@ public void Configure(EntityTypeBuilder e) e.Property(x => x.Method).HasMaxLength(500); e.HasOne(x => x.InspectionItem).WithMany().HasForeignKey(x => x.InspectionItemId) .OnDelete(DeleteBehavior.Restrict); + e.HasOne(x => x.RequiredSkill).WithMany().HasForeignKey(x => x.RequiredSkillId) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.Designer.cs b/src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.Designer.cs new file mode 100644 index 0000000..351b7b5 --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.Designer.cs @@ -0,0 +1,4807 @@ +// +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("20260923125505_AddInspectionRequiredSkill")] + partial class AddInspectionRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("INTEGER"); + + 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("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("INTEGER"); + + b.Property("SamplingCount") + .HasColumnType("INTEGER"); + + b.Property("StandardValue") + .HasColumnType("TEXT"); + + b.Property("UpperLimit") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("InspectionItemId"); + + b.HasIndex("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("INTEGER"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ControlItemId") + .HasColumnType("INTEGER"); + + b.Property("RoutingId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("RequiredSkill"); + + 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.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); + }); + + 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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923125505_AddInspectionRequiredSkill.cs b/src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.cs new file mode 100644 index 0000000..c93bb83 --- /dev/null +++ b/src/MesApp.Infrastructure/Migrations/20260923125505_AddInspectionRequiredSkill.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Infrastructure.Migrations +{ + /// + public partial class AddInspectionRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionItems", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId"); + + migrationBuilder.CreateIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionItems"); + } + } +} diff --git a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs index b4b2c65..40d2cff 100644 --- a/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Infrastructure/Migrations/MesAppDbContextModelSnapshot.cs @@ -644,6 +644,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(200) .HasColumnType("TEXT"); + b.Property("RequiredSkillId") + .HasColumnType("INTEGER"); + b.Property("SamplingCount") .HasColumnType("INTEGER"); @@ -672,6 +675,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Code") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetProcessId"); b.HasIndex("TargetProductId"); @@ -780,6 +785,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(500) .HasColumnType("TEXT"); + b.Property("RequiredSkillId") + .HasColumnType("INTEGER"); + b.Property("SamplingCount") .HasColumnType("INTEGER"); @@ -793,6 +801,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("InspectionItemId"); + b.HasIndex("RequiredSkillId"); + b.HasIndex("InspectionOrderId", "InspectionItemId") .IsUnique(); @@ -3504,6 +3514,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") .WithMany() .HasForeignKey("TargetProcessId"); @@ -3512,6 +3527,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .WithMany() .HasForeignKey("TargetProductId"); + b.Navigation("RequiredSkill"); + b.Navigation("TargetProcess"); b.Navigation("TargetProduct"); @@ -3555,7 +3572,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); }); modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.Designer.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.Designer.cs new file mode 100644 index 0000000..3f12e60 --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.Designer.cs @@ -0,0 +1,5006 @@ +// +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("20260923125508_AddInspectionRequiredSkill")] + partial class AddInspectionRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("integer"); + + 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("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("integer"); + + 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("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("integer"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("integer"); + + b.Property("RoutingId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .HasColumnType("character varying(1000)"); + + 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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("RequiredSkill"); + + 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.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); + }); + + 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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923125508_AddInspectionRequiredSkill.cs b/src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.cs new file mode 100644 index 0000000..65ba44e --- /dev/null +++ b/src/MesApp.Migrations.PostgreSql/Migrations/20260923125508_AddInspectionRequiredSkill.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.PostgreSql.Migrations +{ + /// + public partial class AddInspectionRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionItems", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId"); + + migrationBuilder.CreateIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionItems"); + } + } +} diff --git a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs index d649da3..ee54bcc 100644 --- a/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.PostgreSql/Migrations/MesAppDbContextModelSnapshot.cs @@ -683,6 +683,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(200) .HasColumnType("character varying(200)"); + b.Property("RequiredSkillId") + .HasColumnType("integer"); + b.Property("SamplingCount") .HasColumnType("integer"); @@ -713,6 +716,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Code") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetProcessId"); b.HasIndex("TargetProductId"); @@ -826,6 +831,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(500) .HasColumnType("character varying(500)"); + b.Property("RequiredSkillId") + .HasColumnType("integer"); + b.Property("SamplingCount") .HasColumnType("integer"); @@ -841,6 +849,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("InspectionItemId"); + b.HasIndex("RequiredSkillId"); + b.HasIndex("InspectionOrderId", "InspectionItemId") .IsUnique(); @@ -3703,6 +3713,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") .WithMany() .HasForeignKey("TargetProcessId"); @@ -3711,6 +3726,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .WithMany() .HasForeignKey("TargetProductId"); + b.Navigation("RequiredSkill"); + b.Navigation("TargetProcess"); b.Navigation("TargetProduct"); @@ -3754,7 +3771,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); }); modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => diff --git a/src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.Designer.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.Designer.cs new file mode 100644 index 0000000..7a8467c --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.Designer.cs @@ -0,0 +1,4985 @@ +// +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("20260923125510_AddInspectionRequiredSkill")] + partial class AddInspectionRequiredSkill + { + /// + 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("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.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("RequiredSkillId") + .HasColumnType("int"); + + 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("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("int"); + + 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("RequiredSkillId"); + + 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("RequiredSkillId") + .HasColumnType("int"); + + 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("RequiredSkillId"); + + 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.RoutingControlItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ControlItemId") + .HasColumnType("int"); + + b.Property("RoutingId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ControlItemId"); + + b.HasIndex("RoutingId", "ControlItemId") + .IsUnique(); + + b.ToTable("RoutingControlItems"); + }); + + 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(1000) + .HasColumnType("nvarchar(1000)"); + + 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.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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") + .WithMany() + .HasForeignKey("TargetProcessId"); + + b.HasOne("MesApp.Core.Entities.Product", "TargetProduct") + .WithMany() + .HasForeignKey("TargetProductId"); + + b.Navigation("RequiredSkill"); + + 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.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); + }); + + 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.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + + 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("RequiredSkill"); + + 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.RoutingControlItem", b => + { + b.HasOne("MesApp.Core.Entities.ControlItem", "ControlItem") + .WithMany() + .HasForeignKey("ControlItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("MesApp.Core.Entities.Routing", "Routing") + .WithMany("ControlItemLinks") + .HasForeignKey("RoutingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ControlItem"); + + b.Navigation("Routing"); + }); + + 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("ControlItemLinks"); + + 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/20260923125510_AddInspectionRequiredSkill.cs b/src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.cs new file mode 100644 index 0000000..8638ff9 --- /dev/null +++ b/src/MesApp.Migrations.SqlServer/Migrations/20260923125510_AddInspectionRequiredSkill.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MesApp.Migrations.SqlServer.Migrations +{ + /// + public partial class AddInspectionRequiredSkill : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequiredSkillId", + table: "InspectionItems", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId"); + + migrationBuilder.CreateIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId"); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem", + column: "RequiredSkillId", + principalTable: "Skills", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_InspectionItems_Skills_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropForeignKey( + name: "FK_InspectionOrderItem_Skills_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionOrderItem_RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropIndex( + name: "IX_InspectionItems_RequiredSkillId", + table: "InspectionItems"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionOrderItem"); + + migrationBuilder.DropColumn( + name: "RequiredSkillId", + table: "InspectionItems"); + } + } +} diff --git a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs index b0dfa19..f04f0d8 100644 --- a/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs +++ b/src/MesApp.Migrations.SqlServer/Migrations/MesAppDbContextModelSnapshot.cs @@ -684,6 +684,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(200) .HasColumnType("nvarchar(200)"); + b.Property("RequiredSkillId") + .HasColumnType("int"); + b.Property("SamplingCount") .HasColumnType("int"); @@ -714,6 +717,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Code") .IsUnique(); + b.HasIndex("RequiredSkillId"); + b.HasIndex("TargetProcessId"); b.HasIndex("TargetProductId"); @@ -827,6 +832,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(500) .HasColumnType("nvarchar(500)"); + b.Property("RequiredSkillId") + .HasColumnType("int"); + b.Property("SamplingCount") .HasColumnType("int"); @@ -842,6 +850,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("InspectionItemId"); + b.HasIndex("RequiredSkillId"); + b.HasIndex("InspectionOrderId", "InspectionItemId") .IsUnique(); @@ -3706,6 +3716,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MesApp.Core.Entities.InspectionItem", b => { + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("MesApp.Core.Entities.ProcessMaster", "TargetProcess") .WithMany() .HasForeignKey("TargetProcessId"); @@ -3714,6 +3729,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .WithMany() .HasForeignKey("TargetProductId"); + b.Navigation("RequiredSkill"); + b.Navigation("TargetProcess"); b.Navigation("TargetProduct"); @@ -3756,7 +3773,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("MesApp.Core.Entities.SkillMaster", "RequiredSkill") + .WithMany() + .HasForeignKey("RequiredSkillId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("InspectionItem"); + + b.Navigation("RequiredSkill"); }); modelBuilder.Entity("MesApp.Core.Entities.InspectionResult", b => diff --git a/tests/MesApp.Api.Tests/MasterCsvTests.cs b/tests/MesApp.Api.Tests/MasterCsvTests.cs index fe90ee9..ea69bb5 100644 --- a/tests/MesApp.Api.Tests/MasterCsvTests.cs +++ b/tests/MesApp.Api.Tests/MasterCsvTests.cs @@ -371,6 +371,24 @@ public async Task 検査項目は基準が変わる更新で版数が上がる() Assert.Contains(invalid.Errors, e => e.Line == 3 && e.Message.Contains("基準値")); Assert.Contains(invalid.Errors, e => e.Line == 4 && e.Message.Contains("工程内検査の基準だけ")); Assert.Contains(invalid.Errors, e => e.Line == 5 && e.Message.Contains("対象品目か対象工程")); + + // 必要スキルはコードで指定する。発行時に写す値なので、変えると版数が上がる + Assert.True((await ImportAsync(client, "skills", """ + Code,Name,Type,RequiresExpiry + SK-QC,検査員認定,Certification,false + """)).Succeeded); + Assert.True((await ImportAsync(client, "inspection-items", """ + Code,Name,Type,LowerLimit,UpperLimit,StandardValue,Method,SamplingCount,RequiredSkillCode + INS-01,外径測定(改称),InProcess,9,11,10,マイクロメータ,10,SK-QC + """)).Succeeded); + var withSkill = (await client.GetFromJsonAsync>("/api/inspection-items"))!.Single(); + Assert.Equal("SK-QC", withSkill.RequiredSkillCode); + Assert.Equal(3, withSkill.Version); + var unknown = await ImportAsync(client, "inspection-items", """ + Code,Name,Type,RequiredSkillCode + INS-01,外径測定(改称),InProcess,SK-NONE + """); + Assert.False(unknown.Succeeded); } [Fact] diff --git a/tests/MesApp.Api.Tests/QualityTests.cs b/tests/MesApp.Api.Tests/QualityTests.cs index 79af946..94986e8 100644 --- a/tests/MesApp.Api.Tests/QualityTests.cs +++ b/tests/MesApp.Api.Tests/QualityTests.cs @@ -5,6 +5,7 @@ using MesApp.Core.Contracts.Masters; using MesApp.Core.Contracts.Production; using MesApp.Core.Contracts.Quality; +using MesApp.Core.Contracts.Users; using MesApp.Core.Entities; namespace MesApp.Api.Tests; @@ -395,6 +396,47 @@ public async Task 検査指示から実績登録判定承認まで通しで動 Assert.Equal(HttpStatusCode.OK, approved.StatusCode); } + [Fact] + public async Task 検査項目の必要スキルは発行時に写り検査実績の登録者と照合される() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + var ctx = await Phase3TestData.SetupAsync(admin); + var skill = (await (await admin.PostAsJsonAsync("/api/skills", + new SkillRequest("SK-QC", "検査員認定", SkillType.Certification, true))) + .Content.ReadFromJsonAsync())!; + var created = await admin.PostAsJsonAsync("/api/inspection-items", + new InspectionItemRequest("INS-01", "外径測定", ctx.ProductId, null, InspectionType.FinalProduct, + 9.5m, 10.5m, 10m, "ノギス", 1, skill.Id)); + var item = (await created.Content.ReadFromJsonAsync())!; + Assert.Equal("検査員認定", item.RequiredSkillName); + + var lot = await Phase3TestData.ReceiveAsync(admin, ctx.ProductId, 10m, ctx.ProductLocationId); + var order = (await (await admin.PostAsJsonAsync("/api/inspection-orders", + new InspectionOrderCreateRequest(InspectionOrderType.FinalProduct, lot.Id, null, null, null))) + .Content.ReadFromJsonAsync())!; + + // 発行後にマスタから必要スキルを外しても、発行済みの検査の照合条件は変わらない(スナップショット) + (await admin.PutAsJsonAsync($"/api/inspection-items/{item.Id}", + new InspectionItemRequest("INS-01", "外径測定", ctx.ProductId, null, InspectionType.FinalProduct, + 9.5m, 10.5m, 10m, "ノギス", 1))).EnsureSuccessStatusCode(); + + List results = [new(item.Id, 1, 10.0m, null, null)]; + var rejected = await admin.PostAsJsonAsync($"/api/inspection-orders/{order.Id}/results", results); + Assert.Equal(HttpStatusCode.BadRequest, rejected.StatusCode); + var problem = await rejected.Content.ReadFromJsonAsync(); + Assert.Contains("検査員認定", problem!.Title); + + // 有効期限内の資格を付与すると登録できる + var me = (await admin.GetFromJsonAsync>("/api/users"))! + .Single(u => u.UserName == TestAuth.AdminUser); + (await admin.PutAsJsonAsync($"/api/users/{me.Id}/skills", + new List { new(skill.Id, null, DateOnly.FromDateTime(DateTime.Today).AddYears(1)) })) + .EnsureSuccessStatusCode(); + var accepted = await admin.PostAsJsonAsync($"/api/inspection-orders/{order.Id}/results", results); + Assert.Equal(HttpStatusCode.OK, accepted.StatusCode); + } + [Fact] public async Task 管理図は検査指示ごとに群を作り取消した検査の測定値を使わない() { From e8f66e108022a353bae93ffa023da3fa432a4f14 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:27:39 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=E7=94=BB=E9=9D=A2=E7=A2=BA=E8=AA=8D?= =?UTF-8?q?=E3=81=A7=E8=A6=8B=E3=81=A4=E3=81=8B=E3=81=A3=E3=81=9F=E7=82=B9?= =?UTF-8?q?=E3=82=92=E7=9B=B4=E3=81=99=EF=BC=9A=E3=83=AA=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=81=AE=E7=95=B0=E5=B8=B8=E5=80=A4?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E3=81=A8=E3=82=B9=E3=82=AD=E3=83=AB=E7=85=A7?= =?UTF-8?q?=E5=90=88=E3=81=AE=E6=96=87=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 多くの指図が同じ日数だと四分位範囲が0になり、1日長いだけの指図が異常値に出ていた。 範囲の下限を1日にする。スキル照合の文面は保全・検査でも出るため「担当者」に改める。 Spec.md 改訂136。 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 3 ++- docs/execution.md | 2 +- docs/troubleshooting.md | 2 +- .../Controllers/ProductivityController.cs | 11 +++++++++-- src/MesApp.Api/Localization/ApiText.en.resx | 8 ++++---- src/MesApp.Api/Policies/SkillQualificationPolicy.cs | 8 ++++---- .../Contracts/Production/ProductionContracts.cs | 2 +- tests/MesApp.Api.Tests/ProductionTests.cs | 13 +++++++++++++ 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Spec.md b/Spec.md index f4cc0bc..19a62e9 100644 --- a/Spec.md +++ b/Spec.md @@ -134,6 +134,7 @@ 改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) 改訂134: 2026-09-23(1.3節に**エネルギー管理を[将来拡張]とする**ことを明記した。仕様にエネルギーの記述が無く、対象かどうかが読めなかった(実践的MES導入ガイド 3.4.2・4.4.7・7.2.2 は、MES の役割を FEMS へ製造オーダ別の分母を渡すことと位置づける)。計測・総量監視・ユーティリティ制御は FEMS/FMCS の領域として持たず、実装する場合は設備×期間の使用量をCSVで受け取り稼働区間の時間比で作業指示へ按分する形とした。機能の追加・変更は無い) 改訂135: 2026-09-23(**検査項目・基準に必要スキル・資格を持たせ、検査実績の登録時に検査員と照合する**ようにした(`InspectionItem.RequiredSkillId`。F-20-30-01)。改訂130で保全を実装した際、検査の見直し(改訂125〜128)と衝突するため後回しにしていたもの。必要スキルは発行時に `InspectionOrderItem` へ写し(作業指示の必要スキルと同じ)、発行後にマスタを変えても発行済みの検査の照合条件は変わらない。写す値なので、CSV取込で変えると版数が上がる。判定は差立・着手・保全と同じ `SkillQualificationPolicy`。実績の訂正では照合しない。マスタCSVの検査項目に `RequiredSkillCode` 列を追加(列が無ければ現在値のまま)。3.6節・5.1節・5.4節に反映) +改訂136: 2026-09-23(画面の目視確認で見つかった2点を直した。①製造リードタイムの異常値判定で**四分位範囲を1日を下限**にした(多くの指図が同じ日数だと範囲が0になり、1日長いだけの指図まで異常値に出ていた。3.2節)。②スキル照合の文面を「作業者」から「**担当者**」に改めた(差立・着手に加え、保全の実施者・検査員にも同じ文面が出るため。利用者ガイドの例とトラブルシューティングも合わせた)) 参考: みんなのMES(min-MES) https://min-mes.com/ / OSS: https://github.com/mihatama/open-mes-project --- @@ -270,7 +271,7 @@ HSE〔G〕はチェックリスト項目への健康衛生・安全項目の組 - **工程進捗管理**:工程毎の完了状況把握(B-60-10-01)、製造トラブルの発生報告・対応履歴記録(B-60-10-02〜04。状態は発生→対応中→完了の前進のみとし、完了からは理由を対応履歴に残したときだけ対応中へ戻せる。発生へは戻さない)、生産性モニタリング(歩留まり・直行率・標準時間予実・製造リードタイム・標準時間の見直し候補。B-60-10-05) - **直行率**=良品数÷(良品数+不良数)。**歩留まり**=(良品数+リワーク良品数)÷(良品数+不良数)。リワーク指図の産出は分母に入れない(同じ材料を二度数えるため)、救済された良品として歩留まりの分子にだけ足す - **標準時間の予実**:予定=標準段取り時間+標準作業時間×計画数量(指図展開時に固定した工順の値。5.7節)。実績=直接作業時間+段取り実績時間(いずれも終了済みの記録のみ)。作業時間を記録していない作業指示は実績0分として並ぶ - - **製造リードタイム**(`api/productivity/lead-time`):取消以外の作業指示がすべて完了・承認済みの製造指図ごとに、**最初の作業(段取り・生産実績の開始)から最後の生産実績の終了まで**を製造日の差で数える(同じ製造日なら0日)。**状態履歴の時刻は使わない**——実績CSVで後から取り込むと状態の変わった時刻が取込の時刻になり、実際の作業日から外れるため。期間は完了の製造日。リワーク指図は含めない(歩留まりと同じ扱い)。平均だけではばらつきと異常値が見えないため、**日数ごとの度数分布**(うち納期遅れの件数)・中央値・90%点と、**第3四分位+1.5×四分位範囲を超える異常値**(4件以上のとき)を返す。納期に対する遅れは完了の製造日−納期 + - **製造リードタイム**(`api/productivity/lead-time`):取消以外の作業指示がすべて完了・承認済みの製造指図ごとに、**最初の作業(段取り・生産実績の開始)から最後の生産実績の終了まで**を製造日の差で数える(同じ製造日なら0日)。**状態履歴の時刻は使わない**——実績CSVで後から取り込むと状態の変わった時刻が取込の時刻になり、実際の作業日から外れるため。期間は完了の製造日。リワーク指図は含めない(歩留まりと同じ扱い)。平均だけではばらつきと異常値が見えないため、**日数ごとの度数分布**(うち納期遅れの件数)・中央値・90%点と、**第3四分位+1.5×四分位範囲を超える異常値**(4件以上のとき)を返す。四分位範囲は**1日を下限**にする(日数は整数なので、多くの指図が同じ日数だと範囲が0になり、1日長いだけの指図まで異常値になるため)。納期に対する遅れは完了の製造日−納期 - **標準時間の見直し候補**(`api/productivity/standard-time-review`):工順の工程ごとに、期間内の作業指示の**1個あたり実作業時間**(直接作業時間÷(良品数+不良数))と**1回あたり段取り時間**の**中央値**を、**工順マスタの現在値**と比べる(作業指示のスナップショットではない。見直す対象そのものであり、改訂後もまだずれているかを見たいため)。作業時間を記録していない作業指示は0分として混ぜず件数から外す。件数が既定3件以上でずれが既定20%以上なら候補にする(標準0分で実績があるものも候補)。**マスタは書き換えない**——標準を実績に寄せるか、作業を改めるかは業務の判断。工順の工程順序と工程が作業指示と一致しないもの(工順改訂で並びが変わったもの)は数えない。リワーク指図は含めない - **リワーク・リペア**:リワーク指図の作成・実施・実績記録(B-70-10-01〜03。製造指図の指図区分で表現) - **外注管理**[将来拡張]:工程外注・庫内外注の管理(B-70-20-01〜02。初期は工程マスタの内外製区分と実績記録まで) diff --git a/docs/execution.md b/docs/execution.md index 54379f4..44940e7 100644 --- a/docs/execution.md +++ b/docs/execution.md @@ -47,7 +47,7 @@ next_title: 物流・在庫管理 同じ照合は **着手のとき、着手する人に対しても** 行われます。差立を省略した場合や、割り当てた人と別の人が着手する場合、差立のあとで資格の期限が切れた場合も、資格のない人は着手できません。 -> 例:`作業者 '山田 太郎' のスキル '溶接技能' は有効期限切れです。` +> 例:`担当者 '山田 太郎' のスキル '溶接技能' は有効期限切れです。` マスタ管理 → ユーザー のスキル・資格から、資格の追加や有効期限の更新を行ってください。 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index e27369b..e8dbf07 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -35,7 +35,7 @@ next_title: 管理者向け運用 | メッセージ | 対処 | |---|---| -| 作業者 '○○' のスキル '△△' は有効期限切れです。 | マスタ管理 → ユーザー → スキル・資格 で有効期限を更新してください | +| 担当者 '○○' のスキル '△△' は有効期限切れです。 | マスタ管理 → ユーザー → スキル・資格 で有効期限を更新してください(差立・着手・保全実績・検査実績の登録で出ます) | | 割当作業者が存在しないか無効です。 | ユーザーが無効化されています | | 状態 '○○' の作業指示は差立できません。 | 着手済み以降の作業指示は差立できません | | 作業者のプルダウンが「未割当」しか出ない | ユーザー一覧の参照権限(システム管理者)がありません。設備・着手順だけの割当は可能です | diff --git a/src/MesApp.Api/Controllers/ProductivityController.cs b/src/MesApp.Api/Controllers/ProductivityController.cs index 749f894..9087c5b 100644 --- a/src/MesApp.Api/Controllers/ProductivityController.cs +++ b/src/MesApp.Api/Controllers/ProductivityController.cs @@ -291,7 +291,10 @@ bool Off(int samples, decimal? median, decimal? deviation, decimal standard) => return new StandardTimeReviewResponse(threshold, minSamples, rows); } - /// リードタイムの要約・度数分布・異常値(四分位は最近順位法。4件未満では異常値を判定しない) + /// + /// リードタイムの要約・度数分布・異常値(四分位は最近順位法。4件未満では異常値を判定しない。 + /// 四分位範囲は1日を下限にする) + /// private static LeadTimeResponse LeadTimeStatistics(List rows) { if (rows.Count == 0) @@ -303,7 +306,11 @@ private static LeadTimeResponse LeadTimeStatistics(List rows) var median = days.Count % 2 == 1 ? days[days.Count / 2] : (days[days.Count / 2 - 1] + days[days.Count / 2]) / 2m; - decimal? threshold = days.Count >= 4 ? Rank(0.75m) + 1.5m * (Rank(0.75m) - Rank(0.25m)) : null; + // 日数は整数なので、四分位範囲が1日未満(多くの指図が同じ日数)でも1日として扱う。 + // 0のままだと、そろった日数から1日長いだけの指図まで異常値になる + decimal? threshold = days.Count >= 4 + ? Rank(0.75m) + 1.5m * Math.Max(Rank(0.75m) - Rank(0.25m), 1) + : null; var marked = rows .Select(r => r with { IsOutlier = threshold is { } t && r.LeadTimeDays > t }) diff --git a/src/MesApp.Api/Localization/ApiText.en.resx b/src/MesApp.Api/Localization/ApiText.en.resx index d8beef1..ca8e205 100644 --- a/src/MesApp.Api/Localization/ApiText.en.resx +++ b/src/MesApp.Api/Localization/ApiText.en.resx @@ -174,11 +174,11 @@ The work order (ID {0}) was not found. - - Skill '{1}' of operator '{0}' has expired. + + Skill '{1}' of user '{0}' has expired. - - Operator '{0}' does not have the required skill '{1}'. + + User '{0}' does not have the required skill '{1}'. Record either the usage count or the usage time. diff --git a/src/MesApp.Api/Policies/SkillQualificationPolicy.cs b/src/MesApp.Api/Policies/SkillQualificationPolicy.cs index ab7acbb..1a05cf7 100644 --- a/src/MesApp.Api/Policies/SkillQualificationPolicy.cs +++ b/src/MesApp.Api/Policies/SkillQualificationPolicy.cs @@ -16,19 +16,19 @@ public static class SkillQualificationPolicy /// /// 必要スキルを満たすか。満たさない理由を日本語で返し、問題なければ null を返す。 /// - /// 作業者の表示名(メッセージ用) + /// 照合する担当者(作業者・保全の実施者・検査員)の表示名(メッセージ用) /// 必要スキル - /// 作業者の保有スキル(未保有は null) + /// 担当者の保有スキル(未保有は null) /// 判定する製造日(業務日付) public static string? Check(string userDisplayName, SkillMaster skill, UserSkill? userSkill, DateOnly businessDate) { if (userSkill is null) { - return ApiText.T("作業者 '{0}' は必要スキル '{1}' を保有していません。", userDisplayName, skill.Name); + return ApiText.T("担当者 '{0}' は必要スキル '{1}' を保有していません。", userDisplayName, skill.Name); } if (skill.RequiresExpiry && (userSkill.ExpiresOn is null || userSkill.ExpiresOn < businessDate)) { - return ApiText.T("作業者 '{0}' のスキル '{1}' は有効期限切れです。", userDisplayName, skill.Name); + return ApiText.T("担当者 '{0}' のスキル '{1}' は有効期限切れです。", userDisplayName, skill.Name); } return null; } diff --git a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs index 12c441d..7ff04e1 100644 --- a/src/MesApp.Core/Contracts/Production/ProductionContracts.cs +++ b/src/MesApp.Core/Contracts/Production/ProductionContracts.cs @@ -176,7 +176,7 @@ public record LeadTimeOrderRow( DateOnly? DueDate, /// 納期に対する遅れ(日)。完了の製造日−納期。0以下は納期内。納期なしはnull int? DelayDays, - /// 異常値(第3四分位+1.5×四分位範囲を超える)。個別に原因を調べる対象 + /// 異常値(第3四分位+1.5×四分位範囲を超える。四分位範囲は1日を下限にする)。個別に原因を調べる対象 bool IsOutlier); /// リードタイムの度数分布の1区切り(日数ごとの完了件数。うち納期遅れの件数) diff --git a/tests/MesApp.Api.Tests/ProductionTests.cs b/tests/MesApp.Api.Tests/ProductionTests.cs index 79a48b6..ed91d50 100644 --- a/tests/MesApp.Api.Tests/ProductionTests.cs +++ b/tests/MesApp.Api.Tests/ProductionTests.cs @@ -825,6 +825,19 @@ async Task RecordAsync(int workOrderId, DateTimeOffset start, int? locationId = var outlier = Assert.Single(withOutlier.Orders, o => o.IsOutlier); Assert.Equal(slow.Order.OrderNo, outlier.OrderNo); Assert.Equal(10, withOutlier.MaxDays); + + // 0日を6件足して [0×9,1,2,10] にすると Q1=Q3=0。四分位範囲は1日を下限にするので + // しきい値は 0+1.5×1=1.5 日で、1日のBは異常値にならない(範囲0のままだと1日でも異常値になる) + for (var i = 0; i < 6; i++) + { + var same = await OrderAsync(null); + await RecordAsync(same.WorkOrders[0].Id, Noon(12)); + await RecordAsync(same.WorkOrders[1].Id, Noon(12), ctx.ProductLocationId); + } + var concentrated = await admin.GetFromJsonAsync("/api/productivity/lead-time"); + Assert.Equal(1.5m, concentrated!.OutlierThresholdDays); + Assert.False(Assert.Single(concentrated.Orders, o => o.OrderNo == b.Order.OrderNo).IsOutlier); + Assert.True(Assert.Single(concentrated.Orders, o => o.OrderNo == a.Order.OrderNo).IsOutlier); } [Fact]