GitHub(nextgroup2706/ken_nogi)は今後使わず自社Gitea運用に切替え。 NodeSrvは旧リポジトリの履歴を破棄しファイルのみ統合(Dokploy用サービスアカウントは 別途mygit-admin/NodeSrv.gitに履歴あり)。notepmエクスポート(12GB)とPleasanter インストーラzip(208MB)はサイズが大きいため.gitignoreで除外。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
219 KiB
仕様書:積算問合せシステム(SiteId: 335410 / 335411)
対象2サイト構成システム。AI学習用途、網羅性優先、可読性より情報欠落ゼロを優先する。
- ①物件基本情報 Ver0.3(SiteId: 335410) — 取得元: site-335410_latest.json
- ②問い合わせ内容管理 Ver0.3(SiteId: 335411) — 取得元: site-335411_latest.json
- 取得日時(=両サイトの最終反映日時):335410=2026-08-09T14:39:26 / 335411=2026-08-09T18:42:09(いずれもJST、latest.json取得時点で最新の反映内容を含む)
- 設定バージョン:両サイトとも
SiteSettings.Version: 1.017 - 両サイトとも
ParentId: 74663(同一親サイト配下の兄弟サイト)
0. システム概要
新築物件の「営業積算問い合わせ」業務を扱う2サイト構成システム。
- ①物件基本情報(335410):施主・物件の基本情報(施主名、エリア、契約ブランド、担当者等)を登録するマスター的サイト。登録完了後、この画面から「新規見積依頼」ボタンで②サイトへレコードを作成し依頼を開始する。
- ②問い合わせ内容管理(335411):①から作成された個別の見積問い合わせ(積算依頼)を管理するサイト。依頼者⇔営業積算課(Dept25)間の質疑応答、見積回答、遅延連絡、積算発注課(Dept26等)への引継ぎまでを1レコードのライフサイクルとして扱う。
2サイトはClassZ(②→①のLinks参照列)で1:多(①1件に対し②は複数の問い合わせ=子番ClassB)の関係にある。詳細は7章「他サイト連携」参照。
外部マスタとして以下のサイトを参照する(詳細後述):
- SiteId 189112:★マスターシート(施主・物件属性のマスタ)
- SiteId 210699:エリアマスタ
- SiteId 109100:契約ブランドマスタ
- SiteId 96243:契約営業所マスタ
- SiteId 192267:祝日テーブル(営業日計算用)
- SiteId 335450:遅延理由マスタ(②の遅延連絡機能で参照)
1. サイト基本情報
| 項目 | ①物件基本情報(335410) | ②問い合わせ内容管理(335411) |
|---|---|---|
| SiteId | 335410 | 335411 |
| TenantId | 1 | 1 |
| Title | ①物件基本情報 Ver0.3 | ②問い合わせ内容管理 Ver0.3 |
| ReferenceType | Results | Results |
| ParentId | 74663 | 74663 |
| InheritPermission | 335410(自サイト=権限は自サイト直接設定、継承なし) | 335411(自サイト=権限は自サイト直接設定、継承なし) |
| Publish | false | false |
| DisableCrossSearch | false | false |
| Creator(UserId) | 737 | 737 |
| Updator(UserId) | 995 | 995 |
| CreatedTime | 2026-03-10T13:41:04 | 2026-03-10T13:41:38 |
| UpdatedTime | 2026-08-09T14:39:26 | 2026-08-09T18:42:09 |
| SiteSettings.Version | 1.017 | 1.017 |
| Body(サイト説明) | ・2026.3.10作成 | ・2026.3.10作成 |
一覧・編集画面ガイド文(GridGuide/EditorGuide、両者同一内容)
335410:
新規物件は、一覧画面の右上「+新規作成」ボタンから登録してください。
見積依頼は「新規見積依頼」ボタンから依頼を進めてください。
■LINEWORKS:[営業積算相談窓口](https://line.worksmobile.com/message/send?userNoList=800300000001353&version=25&type=counselor) ※窓口の対応内容や対応時間の確認は[こちら](https://board.worksmobile.com/main/article/4080000000785782853?t=41276&boardNo=my&isShowFilesArea=false)
■LINEWORKS掲示板:[営業積算関連情報ポータル](https://board.worksmobile.com/main/article/4080000000813091905?t=70969&boardNo=my&isShowFilesArea=false)
335411:
新規依頼は【①物件登録情報】の各施主ページ内から行ってください!
※②問い合わせ内容管理ページの「+新規作成」から依頼するとエラーが出ます
■LINEWORKS:[営業積算相談窓口](https://line.worksmobile.com/message/send?userNoList=800300000001353&version=25&type=counselor) ※窓口の対応内容や対応時間の確認は[こちら](https://board.worksmobile.com/main/article/4080000000785782853?t=41276&boardNo=my&isShowFilesArea=false)
※不明点がある場合や期日前倒し希望の場合は、窓口までご連絡ください
※期日前倒し連絡がない場合は、対応できかねます
■LINEWORKS掲示板:[営業積算関連情報ポータル](https://board.worksmobile.com/main/article/4080000000813091905?t=70969&boardNo=my&isShowFilesArea=false)
2. アクセス権限(Permissions)
両サイトともInheritPermissionが自サイトを指しており、権限は継承ではなく自サイト直接設定。権限値はビットフラグの加算方式のため、正確な意味は環境のロール定義(511=フル権限相当、7/3等は限定権限)に依存する。以下はresolve-names.jsによるマスターデータ名前解決結果。
335410(①物件基本情報)
| 種別 | 対象ID | 対象名 | PermissionType |
|---|---|---|---|
| Dept | 25 | 営業積算課 | 511 |
| Group | 27 | ネクストグループ全社員 | 3 |
| User | 460 | 野木 健一郎 | 511 |
| User | 678 | 高橋 光晴 | 511 |
| User | 995 | システムユーザ | 511 |
335411(②問い合わせ内容管理)
| 種別 | 対象ID | 対象名 | PermissionType |
|---|---|---|---|
| Dept | 25 | 営業積算課 | 511 |
| Dept | 26 | 積算発注管理課 | 7 |
| Group | 27 | ネクストグループ全社員 | 3 |
| User | 460 | 野木 健一郎 | 511 |
| User | 678 | 高橋 光晴 | 511 |
| User | 995 | システムユーザ | 511 |
335411のみ「積算発注管理課(Dept26)」がPermissionType 7(限定権限)で追加参加している点が335410との差分。これは③積算発注タブ(_Tab-3)の閲覧・入力対象部署と一致する(3.2節・11章参照)。
3. 画面構成
3.1〜3.3 ①物件基本情報(335410)
一覧画面列(GridColumns) — Site 335410
| 順 | 列名(内部) | 表示ラベル | 種別 |
|---|---|---|---|
| 1 | ResultId | (標準ラベル) | 自動採番ID |
| 2 | ClassA | 管理番号 | 文字列(分類・1文字) |
| 3 | ClassS | 案件状況 | 文字列(分類・1文字) |
| 4 | ClassB | 登録者 | 文字列(分類・1文字) |
| 5 | Class001 | 施主名 | 文字列(拡張分類・3桁連番) |
| 6 | Class021 | 建上地 | 文字列(拡張分類・3桁連番) |
| 7 | Class022 | エリア | 文字列(拡張分類・3桁連番) |
| 8 | Class023 | 契約ブランド | 文字列(拡張分類・3桁連番) |
| 9 | Class040 | 物件担当者 | 文字列(拡張分類・3桁連番) |
| 10 | UpdatedTime | (標準ラベル) | 更新日時(システム項目) |
Class001のGridDesign:[Class001][Class002]\n[Class003][Class004](施主1姓名+カナを2行組版表示)。Class040のGridDesign:営業:[Class032] 設計:[Class034] \nIC:[Class036] 監督:[Class037](項目自体はChoicesTextを持たず、一覧表示専用の合成列)。
タイトル表示 — Site 335410
- TitleColumns:
ClassA+Class022+Class023 - TitleSeparator:
■ - 結合式: [ClassA/管理番号] ■ [Class022/エリア] ■ [Class023/契約ブランド]
検索・フィルタ設定 — Site 335410
- FilterColumns(一覧の絞り込み対象列): Status, ClassB, Class022, Class031〜037
- LinkColumns(Links参照時に他サイトから見える列): ResultId, Title, ClassA, ClassB, ClassK, ClassS, ClassY, ClassZ, Class021〜023, Class031, Class032
編集画面レイアウト — Site 335410
タブはGeneral(物件情報)1本のみ(GeneralTabLabelText: 物件情報、TabLatestId: 1=追加タブなし)。
タブ: General
| 順 | 項目名(内部) | ラベル名 | 種別/備考 |
|---|---|---|---|
| 1 | ResultId | ID(自動採番) | 自動採番ID |
| 2 | Ver | Ver(バージョン) | バージョン(システム項目) |
| 3 | Title | タイトル | タイトル |
| 4 | Comments | 変更履歴 | コメント欄 |
| 5 | ClassA | 管理番号 | 文字列(分類・1文字) |
| 6 | ClassB | 登録者 | 文字列(分類・1文字) |
| 7 | Status | 登録状況 | 状態(Status) [Hide] |
| 8 | ClassF | 年 | 文字列(分類・1文字) [Hide] |
| 9 | _Section-1 | — | セクション開始「(無題セクション)」 |
| 10 | ClassY | マスターシート | 文字列(分類・1文字) |
| 11 | ClassZ | 契約コード | 文字列(分類・1文字) |
| 12 | ClassS | 案件状況 | 文字列(分類・1文字) |
| 13 | _Section-2 | — | セクション開始「物件情報」 |
| 14 | Class001 | 施主1・姓 | 文字列(拡張分類・3桁連番) |
| 15 | Class002 | 施主1・名 | 文字列(拡張分類・3桁連番) |
| 16 | Class003 | 施主1・姓(カナ) | 文字列(拡張分類・3桁連番) |
| 17 | Class004 | 施主1・名(カナ) | 文字列(拡張分類・3桁連番) |
| 18 | _Section-7 | — | セクション開始「(無題セクション)」 |
| 19 | Class005 | 施主2・姓 | 文字列(拡張分類・3桁連番) |
| 20 | Class006 | 施主2・名 | 文字列(拡張分類・3桁連番) |
| 21 | Class007 | 施主2・姓(カナ) | 文字列(拡張分類・3桁連番) |
| 22 | Class008 | 施主2・名(カナ) | 文字列(拡張分類・3桁連番) |
| 23 | _Section-8 | — | セクション開始「(無題セクション)」 |
| 24 | Class009 | 施主3・姓 | 文字列(拡張分類・3桁連番) |
| 25 | Class010 | 施主3・名 | 文字列(拡張分類・3桁連番) |
| 26 | Class011 | 施主3・姓(カナ) | 文字列(拡張分類・3桁連番) |
| 27 | Class012 | 施主3・名(カナ) | 文字列(拡張分類・3桁連番) |
| 28 | Class015 | 施主名マージ | 文字列(拡張分類・3桁連番) [Hide] |
| 29 | _Section-9 | — | セクション開始「(無題セクション)」 |
| 30 | Class021 | 建上地 | 文字列(拡張分類・3桁連番) |
| 31 | Class022 | エリア | 文字列(拡張分類・3桁連番) |
| 32 | Class023 | 契約ブランド | 文字列(拡張分類・3桁連番) |
| 33 | Class024 | 防火指定 | 文字列(拡張分類・3桁連番) |
| 34 | Class025 | 建物種類 | 文字列(拡張分類・3桁連番) |
| 35 | ClassM | エリア記号 | 文字列(分類・1文字) [Hide] |
| 36 | ClassN | ブランド記号 | 文字列(分類・1文字) [Hide] |
| 37 | _Section-4 | — | セクション開始「スケジュール ※商談中の場合は、仮予定日を選択してください(後から変更可)」 |
| 38 | Date001 | 承認図予定日 | 日付(拡張・3桁連番) |
| 39 | Date002 | 着工予定日 | 日付(拡張・3桁連番) |
| 40 | _Section-3 | — | セクション開始「物件担当者」 |
| 41 | Class031 | 契約営業所 | 文字列(拡張分類・3桁連番) |
| 42 | Class032 | 主担当営業 | 文字列(拡張分類・3桁連番) |
| 43 | Class033 | 副担当営業 | 文字列(拡張分類・3桁連番) |
| 44 | Class034 | 営業設計 | 文字列(拡張分類・3桁連番) |
| 45 | Class035 | 管理設計 | 文字列(拡張分類・3桁連番) |
| 46 | Class036 | 担当IC | 文字列(拡張分類・3桁連番) |
| 47 | Class037 | 担当監督 | 文字列(拡張分類・3桁連番) |
| 48 | Class038 | 所属長 | 文字列(拡張分類・3桁連番) |
| 49 | Class151 | 担当者一覧 | 文字列(拡張分類・3桁連番) [Hide] |
| 50 | _Section-6 | — | セクション開始「CCで送ってほしい社員・パートがいる場合は、名前を検索・選択してください」 |
| 51 | Class061 | CC① | 文字列(拡張分類・3桁連番) |
| 52 | Class062 | CC② | 文字列(拡張分類・3桁連番) |
| 53 | Class063 | CC③ | 文字列(拡張分類・3桁連番) |
| 54 | Class064 | CC④ | 文字列(拡張分類・3桁連番) |
| 55 | Class152 | CC一覧 | 文字列(拡張分類・3桁連番) [Hide] |
| 56 | Class153 | 担当者・CC一覧 | 文字列(拡張分類・3桁連番) [Hide] |
| 57 | Class154 | 依頼者・担当者・CC一覧 | 文字列(拡張分類・3桁連番) [Hide] |
| 58 | Class199 | 重複許容 | 文字列(拡張分類・3桁連番) |
| 59 | Class200 | 重複チェック | 文字列(拡張分類・3桁連番) [Hide] |
| 60 | _Links-335411 | — | 他サイトLinksウィジェット表示(SiteId 335411)=この物件に紐づく②問い合わせ内容管理レコード一覧を編集画面下部に表示 |
Sections定義(Id/ラベル/AllowExpand/Expand): Id1(無題,false,true) / Id2(物件情報,false,true) / Id7(無題,false,true) / Id8(無題,false,true) / Id9(無題,false,true) / Id4(スケジュール ※商談中の場合は、仮予定日を選択してください(後から変更可),false,true) / Id3(物件担当者,false,true) / Id6(CCで送ってほしい社員・パートがいる場合は、名前を検索・選択してください,false,true)。SectionLatestId: 9。全セクション折りたたみ不可(AllowExpand:false)。
3.1〜3.3 ②問い合わせ内容管理(335411)
一覧画面列(GridColumns) — Site 335411
| 順 | 列名(内部) | 表示ラベル | 種別 |
|---|---|---|---|
| 1 | ClassA | 親番 | 文字列(分類・1文字) |
| 2 | ClassB | 子番 | 文字列(分類・1文字) |
| 3 | Status | (標準ラベル) | 状態(Status) |
| 4 | ClassO | 施主情報 | 文字列(分類・1文字) |
| 5 | CheckA | 事前相談済み | チェックボックス(1文字) |
| 6 | ClassE | アップ希望日時 | 文字列(分類・1文字) |
| 7 | ClassC | 担当者 | 文字列(分類・1文字) |
| 8 | DescriptionC | 依頼内容 | 説明(複数行文字列・1文字) |
| 9 | Attachments031 | 依頼ファイル | 添付ファイル(拡張・3桁連番) |
| 10 | Check071 | 質疑あり | チェックボックス(拡張・3桁連番) |
| 11 | Check072 | 回答あり | チェックボックス(拡張・3桁連番) |
| 12 | DescriptionF | 見積回答 | 説明(複数行文字列・1文字) |
| 13 | Attachments061 | (標準ラベル) | 添付ファイル(拡張・3桁連番) |
| 14 | Comments | (標準ラベル) | コメント欄 |
| 15 | UpdatedTime | (標準ラベル) | 更新日時(システム項目) |
ClassOのGridDesign:[Class001]\n[Class002]\n[Class004](施主名・建上地・ブランドを一覧セル内に合成表示、項目自体はGridLabelText「施主情報」のみで独自ChoicesTextなし)。DescriptionFのGridDesign:[Description091]\n[Description092](見積回答+別途費用を合成表示)。ClassEのGridDesign:[DateA]\n[ClassE](アップ希望日+時間を合成)。UpdatedTimeのGridDesign:[UpdatedTime]\n[Updator]。ClassCのGridDesign:依頼者:[ClassC]\n営業担当:[ClassD](GridLabelTextは「担当者」)。
タイトル表示 — Site 335411
- TitleColumns:
ClassA+ClassB+Class003+Class004 - TitleSeparator:
- - 結合式: [ClassA/親番] - [ClassB/子番] - [Class003/エリア] - [Class004/ブランド]
検索・フィルタ設定 — Site 335411
- FilterColumns: Status, Owner, Class003, Class004, Class011, Class062, Class064, Class101〜103, DateA, Date081
- LinkColumns: ClassA, ClassB, ClassC, Class062, DateA, Class091, Description061
編集画面レイアウト — Site 335411
タブ構成:GeneralTabLabelText: 問い合わせ情報(既定タブ)+独自タブ3本。TabLatestId: 4。
| EditorColumnHashキー | タブ表示名 | 表示制御 |
|---|---|---|
| General | 問い合わせ情報 | 常時表示 |
| _Tab-1(Tabs配列Id:1) | 質疑回答 | 常時表示(タブ色は質疑応答ステータスに応じ3_質疑回答テーブル関連スクリプトが動的変更) |
| _Tab-2(Tabs配列Id:2) | 営業積算 | 既定非表示。営業積算課(Dept25)のみ01_基本スクリプト.jsがbodyへshow-sekisan-tabクラス付与→CSS#EditorTabs li[aria-controls="FieldSetTab2"]等が表示化 |
| _Tab-3(Tabs配列Id:3) | 積算発注 | 既定非表示。Dept25/26/28/66/36所属者、またはUserId 913/987/1153のみbodyへshow-sekisan-hachu-tabクラス付与で表示化 |
タブ: General(問い合わせ情報)
| 順 | 項目名(内部) | ラベル名 | 種別/備考 |
|---|---|---|---|
| 1 | ResultId | ID(自動採番) | 自動採番ID |
| 2 | Ver | Ver(バージョン) | バージョン(システム項目) |
| 3 | Title | レコード名 | タイトル |
| 4 | ClassZ | テスト・リンク用 | 文字列(分類・1文字) [Hide](①335410へのLinks参照キー、実体は「①物件基本情報からのリンク元ID」用途) |
| 5 | Comments | 変更履歴 | コメント欄 |
| 6 | ClassA | 親番 | 文字列(分類・1文字) |
| 7 | ClassB | 子番 | 文字列(分類・1文字) |
| 8 | ClassX | 連番処理 | 文字列(分類・1文字) [Hide]([ClassA]-[nnn]自動採番、子番ClassBの算出元) |
| 9 | ClassC | 依頼者 | 文字列(分類・1文字) |
| 10 | ClassD | 主担当営業 | 文字列(分類・1文字) |
| 11 | _Section-1 | — | セクション開始「案件情報」(折りたたみ可) |
| 12 | DateA | アップ希望日 | 日付(1文字) |
| 13 | DateJ | 選択可最短日 | 日付(1文字) |
| 14 | ClassE | アップ希望時間 | 文字列(分類・1文字) |
| 15 | CheckA | 事前相談済み | チェックボックス(1文字) |
| 16 | ClassF | 事前相談者 | 文字列(分類・1文字) |
| 17 | ClassG | 現在の進捗 | 文字列(分類・1文字) |
| 18 | ClassT | 現在の進捗(控え) | 文字列(分類・1文字) [Hide] |
| 19 | ClassH | 現場の進捗 | 文字列(分類・1文字) |
| 20 | ClassI | 必要な見積金額 | 文字列(分類・1文字) |
| 21 | DescriptionA | 問い合わせ経緯 | 説明(複数行文字列・1文字) |
| 22 | DateB | 承認図予定日 | 日付(1文字) |
| 23 | DateC | 着工予定日 | 日付(1文字) |
| 24 | _Section-2 | — | セクション開始「物件情報」 |
| 25 | Class001 | 施主名 | 文字列(拡張分類・3桁連番) |
| 26 | Class002 | 建上地 | 文字列(拡張分類・3桁連番) |
| 27 | Class003 | エリア | 文字列(拡張分類・3桁連番) |
| 28 | Class004 | ブランド | 文字列(拡張分類・3桁連番) |
| 29 | Class005 | 建物種類 | 文字列(拡張分類・3桁連番) |
| 30 | Class006 | 防火指定 | 文字列(拡張分類・3桁連番) |
| 31 | _Section-3 | — | セクション開始「問い合わせ内容 ※入力不備があった場合は差し戻します※」 |
| 32 | Class007 | 問合せ種別 | 文字列(拡張分類・3桁連番)(ラジオ、選択肢「マトリックス以外」「マトリックス」、初期未選択、必須。2026-08-09追加。下に赤字「※最初に問い合わせの種類を選択してください※」。選択に応じて33〜39行目(メーカー〜前回の管理番号)と42〜44行目(積算Ver.〜nexthouseシリーズ)の表示・必須・DescriptionCマージ対象を切替。10章スクリプト参照) |
| 33 | Class031 | メーカー | 文字列(拡張分類・3桁連番) |
| 34 | Class032 | 商品名 | 文字列(拡張分類・3桁連番) |
| 35 | Class033 | 品番 | 文字列(拡張分類・3桁連番) |
| 36 | Class034 | 施工範囲・箇所 | 文字列(拡張分類・3桁連番) |
| 37 | Description031 | 依頼内容補足 | 説明(拡張・3桁連番) |
| 38 | Class045 | 標準仕様 | 文字列(拡張分類・3桁連番) |
| 39 | Class050 | 前回の管理番号 | 文字列(拡張分類・3桁連番) |
| 40 | _Section-17 | — | セクション開始「(無題セクション)」 |
| 41 | Class038 | 積算Ver. | 文字列(拡張分類・3桁連番) |
| 42 | Class039 | マトリックスサイズ | 文字列(拡張分類・3桁連番) |
| 43 | Class040 | nexthouseシリーズ | 文字列(拡張分類・3桁連番)(2026-08-09、下に赤字「※nexthouseの場合のみ選択してください※」+選択解除ボタン追加。必須は種別=マトリックスかつブランド(Class004)=109101/268122のときのみ、10章スクリプト参照) |
| 44 | DescriptionC | 依頼内容 | 説明(複数行文字列・1文字) [Hide](問合せ種別により、マトリックス以外はClass031〜034,Description031,Class045,Class050の7項目、マトリックスはClass038〜040の3項目を自動マージ集約。一覧のDescriptionC列表示用。10章スクリプト参照) |
| 45 | _Section-18 | — | セクション開始「(無題セクション)」 |
| 46 | Attachments031 | 依頼ファイル | 添付ファイル(拡張・3桁連番)(あらゆる状況で必須対象外、2026-08-09確定) |
| 47 | Class052 | 回答希望送付先 | 文字列(拡張分類・3桁連番)(あらゆる状況で必須対象外、2026-08-09確定) |
| 48 | _Section-6 | — | セクション開始「営業積算 ※※読み取り専用※※」 |
| 49 | Date061 | 受付日 | 日付(拡張・3桁連番) |
| 50 | Class061 | 受付者 | 文字列(拡張分類・3桁連番) |
| 51 | Class062 | 状況 | 文字列(拡張分類・3桁連番)(Status列と相互同期。11章参照) |
| 52 | Date062 | 見積依頼日 | 日付(拡張・3桁連番) |
| 53 | Class063 | 見積依頼先 | 文字列(拡張分類・3桁連番) |
| 54 | _Section-7 | — | セクション開始「見積回答結果」 |
| 55 | Date063 | 見積受信日 | 日付(拡張・3桁連番) |
| 56 | Class065 | 見積種別 | 文字列(拡張分類・3桁連番) |
| 57 | Description061 | 見積結果 | 説明(拡張・3桁連番) |
| 58 | Description062 | 見積回答ファイル | 説明(拡張・3桁連番) |
| 59 | _Links-335410 | — | 他サイトLinksウィジェット表示(SiteId 335410)=紐づく①物件基本情報レコードへのリンク表示。ClassZのChoicesTextにNotReturnParentRecordオプション設定済み(2026-08-09、①からの新規依頼作成後、遷移元に戻らず作成した②自身のレコードに留まる。12章参照) |
| 60 | Class153 | 担当者・CC一覧 | 文字列(拡張分類・3桁連番) [Hide] |
| 61 | ClassY | 依頼者・担当者・CC一覧 | 文字列(分類・1文字) [Hide] |
タブ: _Tab-2(営業積算、Dept25限定表示)
| 順 | 項目名(内部) | ラベル名 | 種別/備考 |
|---|---|---|---|
| 1 | _Section-11 | — | セクション開始「受付処理」 |
| 2 | Date081 | 受付日 | 日付(拡張・3桁連番)(プロセス「受付」実行時、$p.set()で本日日付を設定。旧「データの変更」から移設、2026-08-09。受付者欄の右横に独自配置「受付」ボタン) |
| 3 | Class081 | 受付者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート。プロセス「受付」実行時にログインユーザーを設定) |
| 4 | Date082 | 見積依頼日 | 日付(拡張・3桁連番)(プロセス「外部見積待ちへ」実行時、$p.set()で本日日付を設定。旧「データの変更」から移設、2026-08-09) |
| 5 | Class082 | 見積依頼者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート。プロセス「外部見積待ちへ」実行時にログインユーザーを設定。見積依頼者欄の右横に独自配置「外部見積待ちへ」ボタン) |
| 6 | Class083 | 見積依頼先 | 文字列(拡張分類・3桁連番) |
| 7 | Class084 | 見積備考 | 文字列(拡張分類・3桁連番) |
| 8 | Description051 | 受付処理備考 | 説明(拡張・3桁連番)(2026-08-09追加。受付処理セクション末尾) |
| 9 | Status | (標準ラベル) | 状態(Status)本体(14種類の選択肢。8.節参照)。右横に「社内確認中へ」「問い合わせ不要」「差戻し」を独自配置ボタンとして一列配置(2026-08-09) |
| 10 | Class080 | 問い合わせジャンル | 文字列(拡張分類・3桁連番) |
| 11 | _Section-12 | — | セクション開始「遅延連絡」 |
| 12 | Class085 | 遅延送付先 | 文字列(拡張分類・3桁連番) |
| 13 | Class086 | 遅延理由 | 文字列(拡張分類・3桁連番)(SiteId 335450参照Lookup) |
| 14 | Date084 | 遅延連絡日 | 日付(拡張・3桁連番)(プロセス「遅延連絡」実行時、$p.set()で本日日付を設定。旧「データの変更」から移設、2026-08-09) |
| 15 | Class087 | 遅延連絡者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート。プロセス「遅延連絡」実行時にログインユーザーを設定) |
| 16 | Date085 | 返信予定日 | 日付(拡張・3桁連番) |
| 17 | Description081 | 遅延連絡内容 | 説明(拡張・3桁連番) |
| 18 | Description082 | 遅延連絡内容(控え) | 説明(拡張・3桁連番) [Hide] |
| 19 | Description083 | 遅延連絡ログ | 説明(拡張・3桁連番) |
| 20 | _Section-13 | — | セクション開始「算出回答結果」 |
| 21 | Date090 | 見積受信日 | 日付(拡張・3桁連番) |
| 22 | Class091 | 見積種別 | 文字列(拡張分類・3桁連番) |
| 23 | Description091 | 見積回答 | 説明(拡張・3桁連番) |
| 24 | Description092 | 別途費用・伝達事項 | 説明(拡張・3桁連番) |
| 25 | Description093 | (標準ラベル) | 説明(拡張・3桁連番) [Hide扱い相当。EditorColumnHash非掲載・091+092の自動マージ先=Description061転記元] |
| 26 | Attachments101 | 見積回答ファイル | 添付ファイル(拡張・3桁連番)(対応する一括DLボタンestimateAttachmentsDLは、1件も添付が無い場合は非表示。2026-08-09) |
| 27 | Num101 | 算出回答回数 | 数値(拡張・3桁連番) |
| 28 | Class101 | 算出者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート) |
| 29 | Class102 | チェック者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート) |
| 30 | Date103 | 返信日 | 日付(拡張・3桁連番)(プロセス「一部回答」「見積回答」実行時、$p.set()で本日日付を設定。旧「データの変更」から移設、2026-08-09) |
| 31 | Class103 | 返信者 | 文字列(拡張分類・3桁連番)(2026-08-09、Usersテーブル検索型に変更。DeptId=25限定・UserIdソート。プロセス「一部回答」「見積回答」実行時にログインユーザーを設定。返信者欄の右横に「一部回答」(上段)「見積回答」(下段)を独自配置ボタンとして縦並び配置) |
| 32 | Class095 | 回答送付先 | 文字列(拡張分類・3桁連番)(「一部回答」「見積回答」通知メールのCC先[Class095]として使用中のため存置、2026-08-09使用箇所確認) |
| 33 | Description101 | 見積回答ログ | 説明(拡張・3桁連番) |
(2026-08-09時点でDate101「算出日」・Date102「チェック日」・Description095「回答送付先一覧」を削除。前2者は依頼になかったため、後者はスクリプト・通知メールいずれからも参照が無いことを確認した上で削除。詳細は12章参照)
(注:Description093はEditorColumnHash配列に含まれないが、Columns定義には存在しないため実質未使用列の可能性あり。詳細は12章「気になる点」参照)
タブ: _Tab-1(質疑回答、常時表示)
| 順 | 項目名(内部) | ラベル名 | 種別/備考 |
|---|---|---|---|
| 1 | Class071 | 質疑回答ステータス | 文字列(拡張分類・3桁連番) [Hide](0=質疑なし/1=質疑あり/2=回答あり/3=回答確認済み) |
| 2 | Description071 | 質疑回答データ保存用 | 説明(拡張・3桁連番) [Hide](質疑応答メッセージ履歴のJSON配列を保管) |
| 3 | Class075 | 質疑回答送信者 | 文字列(拡張分類・3桁連番) [Hide] |
| 4 | Date071 | 質疑回答送信日時 | 日付(拡張・3桁連番) [Hide] |
| 5 | _Section-8 | — | セクション開始「質疑回答ログ」(折りたたみ可) |
| 6 | _Section-9 | — | セクション開始「営業積算課からの質疑」 |
| 7 | Description075 | 質疑内容 | 説明(拡張・3桁連番) |
| 8 | Attachments071 | 質疑ファイル | 添付ファイル(拡張・3桁連番) |
| 9 | Date073 | 質疑回答期日 | 日付(拡張・3桁連番)(送信時に土日祝を除く5営業日後を自動計算) |
| 10 | Class077 | 質疑送付先 | 文字列(拡張分類・3桁連番) |
| 11 | Check071 | 質疑あり | チェックボックス(拡張・3桁連番) |
| 12 | Num071 | 質疑回数 | 数値(拡張・3桁連番) |
| 13 | _Section-10 | — | セクション開始「依頼者からの回答」 |
| 14 | Description076 | 回答内容 | 説明(拡張・3桁連番) |
| 15 | Attachments072 | 回答ファイル | 添付ファイル(拡張・3桁連番) |
| 16 | Check072 | 回答あり | チェックボックス(拡張・3桁連番) |
| 17 | Num072 | 回答回数 | 数値(拡張・3桁連番) |
| 18 | Date074 | 回答確認日 | 日付(拡張・3桁連番)(営業積算課の確認操作時に設定) |
タブ: _Tab-3(積算発注、Dept25/26/28/66/36またはUserId 913/987/1153限定表示)
| 順 | 項目名(内部) | ラベル名 | 種別/備考 |
|---|---|---|---|
| 1 | _Section-14 | — | セクション開始「営業積算課からの引継ぎ」 |
| 2 | Description111 | 引継ぎ備考 | 説明(拡張・3桁連番) |
| 3 | Attachments111 | NET見積原本 | 添付ファイル(拡張・3桁連番) |
| 4 | Attachments112 | 算出根拠資料 | 添付ファイル(拡張・3桁連番) |
| 5 | Class111 | 採用書類 | 文字列(拡張分類・3桁連番) |
| 6 | Attachments113 | その他 | 添付ファイル(拡張・3桁連番) |
| 7 | _Section-15 | — | セクション開始「積算発注課管理」 |
| 8 | Date111 | データ出力日 | 日付(拡張・3桁連番) |
Sections定義一覧(Id/ラベル/AllowExpand):1(案件情報,true) / 2(物件情報,false) / 3(問い合わせ内容 ※入力不備があった場合は差し戻します※,false) / 17(無題,false) / 18(無題,false) / 6(営業積算 ※※読み取り専用※※,false) / 7(見積回答結果,false) / 11(受付処理,false) / 12(遅延連絡,false) / 13(算出回答結果,false) / 8(質疑回答ログ,true) / 9(営業積算課からの質疑,false) / 10(依頼者からの回答,false) / 14(営業積算課からの引継ぎ,false) / 15(積算発注課管理,false)。SectionLatestId: 18。全セクションExpand:true(初期展開済み)。
4. 項目定義(Columns)全項目
種別はColumnNameプレフィックスからの推定。ChoicesTextが[[Users*]]はユーザー一覧選択、[[数字]]は単純Link参照、JSON配列/オブジェクトはLookup設定(詳細は7章)。
4.1 ①物件基本情報(335410)— 全50項目
| 列名(内部) | 種別 | ラベル | GridLabel | 主要設定 | 備考 |
|---|---|---|---|---|---|
| Comments | コメント欄 | 変更履歴 | 画像貼付不可 | ||
| Status | 状態(Status) | 登録状況 | Hide, CSS:code-readonly2 | 選択肢: 100,新規登録,新規,new-status-white / 200,登録完了,登録,new-status-pink / 900,依頼終了,依頼,new-status-blue / 910,保留,保留,new-status-grey / 920,取消,取消,new-status-grey | |
| ClassA | 文字列(分類・1文字) | 管理番号 | NoWrap, CSS:code-readonly2, 自動採番:[yy][nnnn][ClassM][ClassN] | 補足HTML付き(4.3節参照) | |
| ClassB | 文字列(分類・1文字) | 登録者 | 検索窓あり, CSS:code-readonly2, 初期値:Self | ユーザー一覧選択 | |
| ClassF | 文字列(分類・1文字) | 年 | Hide | ||
| ClassZ | 文字列(分類・1文字) | 契約コード | コピー時デフォルト保持, CSS:code-readonly2 | ClassY選択時にマスターシート(189112)ClassAから自動転記 | |
| ClassS | 文字列(分類・1文字) | 案件状況 | NoWrap, 空欄なし, 初期値:0 | 選択肢: 0,未契約,未契約,new-status-light-white / 1,契約済み,契約済み,new-status-green(ClassY/ClassZの値からスクリプト自動判定、10章参照) | |
| Class001 | 文字列(拡張分類・3桁連番) | 施主1・姓 | 施主名 | NoWrap, GridDesign:[Class001][Class002]\n[Class003][Class004] | |
| Class002 | 文字列(拡張分類・3桁連番) | 施主1・名 | |||
| Class003 | 文字列(拡張分類・3桁連番) | 施主1・姓(カナ) | NoWrap, 必須, 正規表現検証あり(^[ァ-ヶー0-90-9 ]+$) | 入力ガイド:全角カタカナで入力 | |
| Class004 | 文字列(拡張分類・3桁連番) | 施主1・名(カナ) | 必須, 正規表現検証あり(^[ァ-ヶー0-90-9 ]+$) | 入力ガイド:全角カタカナで入力 | |
| Class005 | 文字列(拡張分類・3桁連番) | 施主2・姓 | NoWrap | ||
| Class006 | 文字列(拡張分類・3桁連番) | 施主2・名 | |||
| Class007 | 文字列(拡張分類・3桁連番) | 施主2・姓(カナ) | NoWrap, 正規表現検証あり | ||
| Class008 | 文字列(拡張分類・3桁連番) | 施主2・名(カナ) | 正規表現検証あり | ||
| Class009 | 文字列(拡張分類・3桁連番) | 施主3・姓 | NoWrap | ||
| Class010 | 文字列(拡張分類・3桁連番) | 施主3・名 | |||
| Class011 | 文字列(拡張分類・3桁連番) | 施主3・姓(カナ) | NoWrap, 正規表現検証あり | ||
| Class012 | 文字列(拡張分類・3桁連番) | 施主3・名(カナ) | 正規表現検証あり | ||
| ClassM | 文字列(分類・1文字) | エリア記号 | Hide, NoWrap | Class022(エリア)選択時にServerScript事前取得マスタから逆引き転記 | |
| ClassN | 文字列(分類・1文字) | ブランド記号 | Hide | Class023(契約ブランド)選択時にServerScript事前取得マスタから逆引き転記 | |
| Date001 | 日付(拡張・3桁連番) | 承認図予定日 | |||
| Date002 | 日付(拡張・3桁連番) | 着工予定日 | |||
| Class199 | 文字列(拡張分類・3桁連番) | 重複許容 | 選択肢: 重複1〜重複10(営業積算専用の重複チェック回避弁、CreateColumnAccessControls/ReadColumnAccessControls/UpdateColumnAccessControlsでDept25限定、5章参照) | ||
| Class200 | 文字列(拡張分類・3桁連番) | 重複チェック | Hide, 重複チェック対象(NoDuplication) | 重複時メッセージ:本物件はすでに登録されております。同姓同名の別物件の場合は営業積算窓口へご連絡ください(施主カナ+エリア+重複許容の連結値、10章スクリプト参照) | |
| UpdatedTime | 更新日時(システム項目) | GridFormat:Ymdhm | |||
| Class061 | 文字列(拡張分類・3桁連番) | CC① | 検索窓あり | ユーザー一覧選択 | |
| Class062 | 文字列(拡張分類・3桁連番) | CC② | 検索窓あり | ユーザー一覧選択 | |
| Class063 | 文字列(拡張分類・3桁連番) | CC③ | NoWrap, 検索窓あり | ユーザー一覧選択 | |
| Class064 | 文字列(拡張分類・3桁連番) | CC④ | 検索窓あり | ユーザー一覧選択 | |
| Class031 | 文字列(拡張分類・3桁連番) | 契約営業所 | JSON形式Lookup設定あり(SiteId 96243、7章参照) | ||
| Class032 | 文字列(拡張分類・3桁連番) | 主担当営業 | NoWrap, 検索窓あり | ユーザー一覧選択 | |
| Class033 | 文字列(拡張分類・3桁連番) | 副担当営業 | 検索窓あり | ユーザー一覧選択 | |
| Class034 | 文字列(拡張分類・3桁連番) | 営業設計 | NoWrap, 検索窓あり | ユーザー一覧選択 | |
| Class035 | 文字列(拡張分類・3桁連番) | 管理設計 | 検索窓あり | ユーザー一覧選択 | |
| Class036 | 文字列(拡張分類・3桁連番) | 担当IC | NoWrap, 検索窓あり | ユーザー一覧選択 | |
| Class037 | 文字列(拡張分類・3桁連番) | 担当監督 | 検索窓あり | ユーザー一覧選択 | |
| Class038 | 文字列(拡張分類・3桁連番) | 所属長 | NoWrap, 検索窓あり | ユーザー一覧選択(Class031選択時にServerScript事前取得マスタから逆引き転記) | |
| Class022 | 文字列(拡張分類・3桁連番) | エリア | 必須 | JSON形式Lookup設定あり(SiteId 210699、7章参照) | |
| Class023 | 文字列(拡張分類・3桁連番) | 契約ブランド | NoWrap | JSON形式Lookup設定あり(SiteId 109100、7章参照) | |
| Class024 | 文字列(拡張分類・3桁連番) | 防火指定 | NoWrap | 選択肢: 無,無,無,status-grey / 準防火,準防火,準防,status-yellow / 準耐火,準耐火,準耐,status-red | |
| Class025 | 文字列(拡張分類・3桁連番) | 建物種類 | 選択肢: 平屋 / 2階建て / 3階建て | ||
| Class021 | 文字列(拡張分類・3桁連番) | 建上地 | CSS:field-wide | ||
| Class040 | 文字列(拡張分類・3桁連番) | 物件担当者 | GridDesign専用列(4.2節参照) | ChoicesText/LabelTextなし。一覧表示専用の合成列 | |
| ClassY | 文字列(分類・1文字) | マスターシート | NoWrap, 検索窓あり, コピー時デフォルト保持, CSS:field-wide | JSON形式Lookup設定あり(SiteId 189112、SearchFormat:「[ClassA] - [ClassQ] 様 ([ClassF] [ClassG])」、7章参照)。補足HTML付き(4.3節参照) | |
| Class015 | 文字列(拡張分類・3桁連番) | 施主名マージ | Hide, NoWrap, CSS:field-wide | 施主1姓名+カナのマージ文字列(スクリプト自動生成) | |
| Class151 | 文字列(拡張分類・3桁連番) | 担当者一覧 | Hide, 検索窓あり, 複数選択, CSS:field-wide | ユーザー一覧選択(主担当営業〜所属長の重複除去マージ、スクリプト自動生成) | |
| Class152 | 文字列(拡張分類・3桁連番) | CC一覧 | Hide, 検索窓あり, 複数選択, CSS:field-wide | ユーザー一覧選択(CC①〜④の重複除去マージ、スクリプト自動生成) | |
| Class153 | 文字列(拡張分類・3桁連番) | 担当者・CC一覧 | Hide, 検索窓あり, 複数選択, CSS:field-wide | ユーザー一覧選択(Class151+Class152の重複除去マージ、スクリプト自動生成) | |
| Class154 | 文字列(拡張分類・3桁連番) | 依頼者・担当者・CC一覧 | Hide, 検索窓あり, 複数選択, CSS:field-wide | ユーザー一覧選択(登録者ClassB+Class153の重複除去マージ、スクリプト自動生成) |
4.2 ②問い合わせ内容管理(335411)— 全107項目(2026-08-09時点。Class007・Description051追加、Date101・Date102・Description095削除により108→107)
| 列名(内部) | 種別 | ラベル | GridLabel | 主要設定 | 備考 |
|---|---|---|---|---|---|
| ClassA | 文字列(分類・1文字) | 親番 | NoWrap, コピー時デフォルト保持, 空欄なし, CSS:code-readonly2 | ||
| Owner | ユーザー選択(Owner) | NoWrap | 標準Owner項目、ChoicesText空・DefaultInput空 | ||
| ClassD | 文字列(分類・1文字) | 主担当営業 | 検索窓あり, CSS:code-readonly2 | ユーザー一覧選択(①ClassZ経由の自動転記先) | |
| ClassE | 文字列(分類・1文字) | アップ希望時間 | アップ希望日時 | NoWrap, 空欄なし, 初期値:指定なし, GridDesign:[DateA]\n[ClassE] | 選択肢: 指定なし / 9:00〜18:00(30分刻み) |
| ClassF | 文字列(分類・1文字) | 事前相談者 | 選択肢: 営業積算相談窓口 / 小竹 忍 / 菅原 千夏 / 大内 詩織。説明・入力ガイド:事前相談した担当を選択してください | ||
| ClassG | 文字列(分類・1文字) | 現在の進捗 | NoWrap, 表示形式:Radio | 選択肢: 0,商談~承認図 / 1,承認図後・着工中(監督確認済/お客様請求可)(値によりClassH/ClassI/DescriptionAの表示切替、10章参照) | |
| ClassT | 文字列(分類・1文字) | 現在の進捗(控え) | Hide | ClassGの常時ミラー(ClassG読取専用化後も表示判定に使うための控え) | |
| ClassH | 文字列(分類・1文字) | 現場の進捗 | NoWrap, 表示形式:Radio | 選択肢: 0,承認図後~着工前 / 1,着工中 / 2,引渡し前 | |
| ClassI | 文字列(分類・1文字) | 必要な見積金額 | NoWrap, 表示形式:Radio | 選択肢: 0,承認図時まで / 1,承認図以降 | |
| DateA | 日付(1文字) | アップ希望日 | 入力ガイド:土日祝日を除く5営業日以降(DateJ選択可最短日との比較チェックあり) | ||
| CheckA | チェックボックス(1文字) | 事前相談済み | NoWrap | ||
| DescriptionA | 説明(複数行文字列・1文字) | 問い合わせ経緯 | 画像貼付不可 | 入力ガイド:(入力例)承認図時に決定していた商品が廃盤となり、商品の再選定が必要になったため。補足HTML付き(4.3節参照) | |
| DateB | 日付(1文字) | 承認図予定日 | CSS:code-readonly2 | ①Date001からの自動転記(readonly) | |
| DateC | 日付(1文字) | 着工予定日 | CSS:code-readonly2 | ①Date002からの自動転記(readonly) | |
| Class001 | 文字列(拡張分類・3桁連番) | 施主名 | CSS:code-readonly2, CSS:field-wide | ①Class015(施主名マージ)からの自動転記(readonly) | |
| Class002 | 文字列(拡張分類・3桁連番) | 建上地 | CSS:code-readonly2, CSS:field-wide | ①Class021からの自動転記(readonly) | |
| Class003 | 文字列(拡張分類・3桁連番) | エリア | CSS:code-readonly2, Linkフラグ(参照専用表示) | SiteId 210699 参照(単純Link、①Class022からの自動転記readonly) | |
| Class004 | 文字列(拡張分類・3桁連番) | ブランド | CSS:code-readonly2, Linkフラグ(参照専用表示) | SiteId 109100 参照(単純Link、①Class023からの自動転記readonly) | |
| Class005 | 文字列(拡張分類・3桁連番) | 建物種類 | NoWrap, CSS:code-readonly2 | 選択肢: 平屋 / 2階建て / 3階建て(①Class025からの自動転記readonly) | |
| Class006 | 文字列(拡張分類・3桁連番) | 防火指定 | CSS:code-readonly2 | 選択肢: 無 / 準防火 / 準耐火(①Class024からの自動転記readonly) | |
| Status | 状態(Status) | NoWrap | 選択肢14種(8章参照)。Class062と相互同期(10章スクリプト) | ||
| Class031 | 文字列(拡張分類・3桁連番) | メーカー | CSS:field-wide | 入力ガイド:正式名称で入力してください(例)DAIKEN | |
| Class038 | 文字列(拡張分類・3桁連番) | 積算Ver. | NoWrap | 入力ガイド:(入力例)260211-AC-全-002。補足HTML付き(4.3節参照) | |
| Class040 | 文字列(拡張分類・3桁連番) | nexthouseシリーズ | NoWrap, 空欄なし, 表示形式:Radio | 選択肢: LAVIE / FAMILIE・PRIME / CERAGRAN。必須は問合せ種別(Class007)=マトリックスかつブランド(Class004)=109101(nexthouse)/268122(nexthouse VALUE)のときのみ(10章スクリプト)。補足HTML付き2件:赤字説明+選択解除ボタン(4.3節参照、2026-08-09追加) | |
| Class007 | 文字列(拡張分類・3桁連番) | 問合せ種別 | 空欄なし, 表示形式:Radio, 選択肢2つ | 選択肢: マトリックス以外 / マトリックス。初期未選択。2026-08-09追加。選択に応じてマトリックス以外群(Class031〜034,Description031,Class045,Class050)/マトリックス群(Class038〜040)の表示・必須・DescriptionCマージ対象を切替、保存時に非選択側はクリア(10章スクリプト)。補足HTML付き(4.3節参照) | |
| Class045 | 文字列(拡張分類・3桁連番) | 標準仕様 | CSS:field-wide | 説明:標準仕様からの変更差額を希望の場合は、該当の仕様をこちらへ入力してください。補足HTML付き(4.3節参照) | |
| Class050 | 文字列(拡張分類・3桁連番) | 前回の管理番号 | CSS:field-wide | 説明:既出問い合わせからの変更の場合は、該当の番号を選択してください | |
| Class052 | 文字列(拡張分類・3桁連番) | 回答希望送付先 | 検索窓あり, 複数選択, CSS:field-wide | JSON形式Lookup設定あり(Usersテーブル、Class153絞込。7章参照)。補足HTML付き(4.3節参照) | |
| Class061 | 文字列(拡張分類・3桁連番) | 受付者 | CSS:code-readonly2 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更)。Tab2 Class081から自動転記readonly(転記時UserId→氏名解決、10章スクリプト) | |
| Date061 | 日付(拡張・3桁連番) | 受付日 | CSS:code-readonly2 | Tab2 Date081から自動転記readonly | |
| Date062 | 日付(拡張・3桁連番) | 見積依頼日 | NoWrap, CSS:code-readonly2 | Tab2 Date082から自動転記readonly | |
| Class062 | 文字列(拡張分類・3桁連番) | 状況 | NoWrap, CSS:code-readonly2 | 選択肢14種(Statusと同一・相互同期) | |
| Class063 | 文字列(拡張分類・3桁連番) | 見積依頼先 | CSS:code-readonly2, CSS:field-wide | Tab2 Class083から自動転記readonly | |
| Date063 | 日付(拡張・3桁連番) | 見積受信日 | CSS:code-readonly2 | Tab2 Date090から自動転記readonly | |
| Class064 | 文字列(拡張分類・3桁連番) | 採用状況 | Hide, NoWrap, 表示形式:Radio | 選択肢: 採用 / 不採用 / 打合せ中(未使用の可能性、12章参照) | |
| Class065 | 文字列(拡張分類・3桁連番) | 見積種別 | NoWrap, 表示形式:Radio, CSS:code-readonly2 | 選択肢: 概算 / 正式(Tab2 Class091から自動転記readonly) | |
| Description061 | 説明(拡張・3桁連番) | 見積結果 | 画像貼付不可, CSS:code-readonly2 | Description091+092のマージ結果を転記(readonly、10章スクリプト) | |
| Description062 | 説明(拡張・3桁連番) | 見積回答ファイル | 画像貼付不可, CSS:code-readonly2 | 見積回答プロセス実行時にAttachments101から一覧DLリンク生成(10章スクリプト) | |
| Date071 | 日付(拡張・3桁連番) | 質疑回答送信日時 | Hide | 現状未使用(12章参照) | |
| Date073 | 日付(拡張・3桁連番) | 質疑回答期日 | CSS:code-readonly2 | 質疑送信時に土日祝除く5営業日後を自動計算 | |
| Date074 | 日付(拡張・3桁連番) | 回答確認日 | NoWrap, CSS:code-readonly2 | 営業積算確認操作時に本日日付を自動設定 | |
| Description071 | 説明(拡張・3桁連番) | 質疑回答データ保存用 | Hide, 画像貼付不可 | 質疑応答メッセージ履歴JSON配列の実体 | |
| Description075 | 説明(拡張・3桁連番) | 質疑内容 | 画像貼付不可 | 送信すると自動クリアされる入力欄 | |
| Description076 | 説明(拡張・3桁連番) | 回答内容 | 画像貼付不可 | 送信すると自動クリアされる入力欄 | |
| Attachments071 | 添付ファイル(拡張・3桁連番) | 質疑ファイル | |||
| Attachments072 | 添付ファイル(拡張・3桁連番) | 回答ファイル | |||
| Class071 | 文字列(拡張分類・3桁連番) | 質疑回答ステータス | Hide, 初期値:0 | 選択肢: 0,質疑なし,質疑なし,new-status-white / 1,質疑あり,質疑あり,new-status-red / 2,回答あり,回答あり,new-status-yellow / 3,回答確認済み,回答確認済み,new-status-green | |
| Class075 | 文字列(拡張分類・3桁連番) | 質疑回答送信者 | Hide | 質疑・回答送信のたびに送信者氏名で上書き | |
| Date081 | 日付(拡張・3桁連番) | 受付日 | Tab2入力欄(→Date061へ転記) | ||
| Date082 | 日付(拡張・3桁連番) | 見積依頼日 | NoWrap | Tab2入力欄(→Date062へ転記) | |
| Date085 | 日付(拡張・3桁連番) | 返信予定日 | NoWrap | 変更時にDescription081内の%返信予定日%プレースホルダー/旧値を置換(10章スクリプト) | |
| Date090 | 日付(拡張・3桁連番) | 見積受信日 | Tab2入力欄(→Date063へ転記) | ||
| Date103 | 日付(拡張・3桁連番) | 返信日 | NoWrap | プロセス「一部回答」「見積回答」実行時、$p.setで本日日付を設定(旧「データの変更」から移設、2026-08-09) | |
| Class081 | 文字列(拡張分類・3桁連番) | 受付者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更)。プロセス「受付」実行時にログインユーザーを設定 | ||
| Class082 | 文字列(拡張分類・3桁連番) | 見積依頼者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更)。プロセス「外部見積待ちへ」実行時にログインユーザーを設定 | ||
| Class083 | 文字列(拡張分類・3桁連番) | 見積依頼先 | CSS:field-wide | ||
| Class084 | 文字列(拡張分類・3桁連番) | 見積備考 | CSS:field-wide | ||
| Description051 | 説明(拡張・3桁連番) | 受付処理備考 | 2026-08-09追加。受付処理セクション(Tab-2)末尾、Class084の直後・Status手前 | ||
| Class085 | 文字列(拡張分類・3桁連番) | 遅延送付先 | 検索窓あり, 複数選択, CSS:field-wide | JSON形式Lookup設定あり(Usersテーブル、Class153絞込。7章参照) | |
| Class086 | 文字列(拡張分類・3桁連番) | 遅延理由 | AutoPostBack | JSON形式Lookup設定あり(SiteId 335450、Description081へ自動返送。7章参照) | |
| Class087 | 文字列(拡張分類・3桁連番) | 遅延連絡者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更)。プロセス「遅延連絡」実行時にログインユーザーを設定 | ||
| Description081 | 説明(拡張・3桁連番) | 遅延連絡内容 | 画像貼付不可 | 遅延理由マスタからの雛形文+%返信予定日%プレースホルダー置換 | |
| Description083 | 説明(拡張・3桁連番) | 遅延連絡ログ | 遅延連絡送信のたびに先頭追記(10章スクリプト) | ||
| Description091 | 説明(拡張・3桁連番) | 見積回答 | 画像貼付不可 | ||
| Description092 | 説明(拡張・3桁連番) | 別途費用・伝達事項 | 画像貼付不可 | ||
| Class101 | 文字列(拡張分類・3桁連番) | 算出者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更) | ||
| Class102 | 文字列(拡張分類・3桁連番) | チェック者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更) | ||
| Class103 | 文字列(拡張分類・3桁連番) | 返信者 | Usersテーブル検索型(DeptId=25限定・UserIdソート、2026-08-09変更)。プロセス「一部回答」「見積回答」実行時にログインユーザーを設定。返信者欄の右横に一部回答(上段)・見積回答(下段)ボタンを縦並び配置 | ||
| Description101 | 説明(拡張・3桁連番) | 見積回答ログ | 画像貼付不可 | 算出対応ログの累積保存先(10章スクリプト) | |
| Attachments101 | 添付ファイル(拡張・3桁連番) | 見積回答ファイル | 見積回答プロセス実行時にDescription062へ一覧転記 | ||
| Attachments111 | 添付ファイル(拡張・3桁連番) | NET見積原本 | Tab3、CreateColumnAccessControls/UpdateColumnAccessControlsでDept25/26限定(5章) | ||
| Attachments112 | 添付ファイル(拡張・3桁連番) | 算出根拠資料 | Tab3、Dept25/26限定 | ||
| Class111 | 文字列(拡張分類・3桁連番) | 採用書類 | CSS:field-wide | Tab3、Dept25/26限定 | |
| Attachments113 | 添付ファイル(拡張・3桁連番) | その他 | Tab3、Dept25/26限定 | ||
| Description111 | 説明(拡張・3桁連番) | 引継ぎ備考 | 画像貼付不可 | Tab3、Dept25/26限定 | |
| Num071 | 数値(拡張・3桁連番) | 質疑回数 | |||
| Num072 | 数値(拡張・3桁連番) | 回答回数 | |||
| Num101 | 数値(拡張・3桁連番) | 算出回答回数 | NoWrap | ||
| Date064 | 日付(拡張・3桁連番) | 使用期限 | Hide | 補足HTML付き(4.3節参照、現状未使用の可能性12章参照) | |
| Attachments031 | 添付ファイル(拡張・3桁連番) | 依頼ファイル | |||
| Date111 | 日付(拡張・3桁連番) | データ出力日 | Tab3、Dept25/26限定 | ||
| ClassC | 文字列(分類・1文字) | 依頼者 | 担当者 | NoWrap, CSS:code-readonly2, 初期値:Self, GridDesign:依頼者:[ClassC]\n営業担当:[ClassD] | ユーザー一覧選択。多くのProcess View条件(ClassC:["Own"])で本人判定に使用 |
| Class091 | 文字列(拡張分類・3桁連番) | 見積種別 | NoWrap, 表示形式:Radio | 選択肢: 概算 / 正式(Tab2入力欄→Class065へ転記) | |
| Date091 | 日付(拡張・3桁連番) | 使用期限 | 補足HTML付き(4.3節参照、EditorColumnHash非掲載=現状未表示の可能性12章参照) | ||
| Class095 | 文字列(拡張分類・3桁連番) | 回答送付先 | 複数選択, CSS:field-wide | JSON形式Lookup設定あり(Usersテーブル、Class153絞込)。「一部回答」「見積回答」通知メールのCC先[Class095]として使用中と確認し存置(2026-08-09、Description095は使用箇所なしのため削除) |
|
| Class077 | 文字列(拡張分類・3桁連番) | 質疑送付先 | 検索窓あり, 複数選択, CSS:field-wide | JSON形式Lookup設定あり(Usersテーブル、Class153絞込) | |
| UpdatedTime | 更新日時(システム項目) | GridDesign:[UpdatedTime]\n[Updator] | |||
| ClassO | 文字列(分類・1文字) | 施主情報 | GridDesign専用列(4.2節参照) | GridDesign:[Class001]\n[Class002]\n[Class004]。ChoicesText/LabelTextなし | |
| DescriptionC | 説明(複数行文字列・1文字) | 依頼内容 | Hide | 説明:依頼内容マージ。Class031〜034,Description031,Class038〜040の自動集約先(10章スクリプト) | |
| DescriptionF | 説明(複数行文字列・1文字) | 見積回答 | GridDesign専用列(4.2節参照) | GridDesign:[Description091]\n[Description092]。ChoicesText/LabelTextなし | |
| Class032 | 文字列(拡張分類・3桁連番) | 商品名 | CSS:field-wide | 入力ガイド:正しい商品名を入力してください(例)イエリア 格子間仕切 | |
| Class033 | 文字列(拡張分類・3桁連番) | 品番 | CSS:field-wide | 入力ガイド:正しい品番を入力してください(例)QW404-■■ | |
| Description031 | 説明(拡張・3桁連番) | 依頼内容補足 | 画像貼付不可 | 説明:メーカー・商品名・品番以外に補足がある場合は、こちらに詳細を入力してください。 | |
| Class080 | 文字列(拡張分類・3桁連番) | 問い合わせジャンル | NoWrap, 空欄なし | 選択肢: A.基本〜I.造作家具・建具・洗面 他(9択+案内文) | |
| Title | タイトル | レコード名 | |||
| Class039 | 文字列(拡張分類・3桁連番) | マトリックスサイズ | NoWrap, 表示形式:Radio | 入力ガイド:(入力例)7×13 | |
| ClassZ | 文字列(分類・1文字) | テスト・リンク用 | Hide, AutoPostBack, CSS:field-wide | JSON形式Lookup設定あり(SiteId 335410、15項目転記。7章参照)。実質「①物件基本情報からのリンク元ID」として機能(ラベル名と実際の用途が乖離、12章参照)。ChoicesTextにNotReturnParentRecord:true追加(2026-08-09。Pleasanter標準機能、①からの新規依頼作成後に遷移元へ戻らず②自身のレコードに留まる) |
|
| Class034 | 文字列(拡張分類・3桁連番) | 施工範囲・箇所 | CSS:field-wide | 説明:施工場所を入力してください ※複数パターンある場合は『A:●●、B:■■』のようにわかるように記載してください | |
| Date084 | 日付(拡張・3桁連番) | 遅延連絡日 | NoWrap | ||
| DateJ | 日付(1文字) | 選択可最短日 | CSS:code-readonly2 | サーバースクリプト取得の祝日データからクライアント側で自動算出(readonly) | |
| ClassB | 文字列(分類・1文字) | 子番 | CSS:code-readonly2 | ClassXから算出、クライアント・サーバー両方に同一ロジックで自動保存(10章) | |
| ClassX | 文字列(分類・1文字) | 連番処理 | Hide, NoWrap, 自動採番:[ClassA]-[nnn] | ClassBの算出元 | |
| Description082 | 説明(拡張・3桁連番) | 遅延連絡内容(控え) | Hide | Date085置換処理のキー保持用(前回置換に使った返信予定日を記憶) | |
| Check071 | チェックボックス(拡張・3桁連番) | 質疑あり | NoWrap, CSS:code-readonly2 | ||
| Check072 | チェックボックス(拡張・3桁連番) | 回答あり | NoWrap, CSS:code-readonly2 | ||
| Class153 | 文字列(拡張分類・3桁連番) | 担当者・CC一覧 | Hide, 検索窓あり, 複数選択, CSS:field-wide | JSON形式Lookup設定あり(①ClassZ経由でClass153同士連携。Class052/077/085/095の絞込元) | |
| ClassY | 文字列(分類・1文字) | 依頼者・担当者・CC一覧 | Hide, 複数選択, CSS:field-wide | ユーザー一覧選択(ClassC+Class153の重複除去マージ、10章スクリプト) |
4.3 補足HTML(ExtendedHtmlAfterControl)一覧
335410
| 列名 | ラベル | HTML内容 |
|---|---|---|
| ClassA | 管理番号 | <div class="commentAdd small blue">※自動採番</div> |
| ClassY | マスターシート | <div class="commentAdd red nowrap">※マスターシート登録済み物件は、上記より選択し作成ボタンを押すと登録情報が自動反映されます</div> |
335411
| 列名 | ラベル | HTML内容 |
|---|---|---|
| Class007 | 問合せ種別 | <div class="commentAdd red nowrap">※最初に問い合わせの種類を選択してください※</div>(2026-08-09追加) |
| DescriptionA | 問い合わせ経緯 | <div class="commentAdd red nowrap">※承認図以降の問い合わせの場合は、「承認図時の内容」であっても必ず経緯を入力してください</div> |
| Class038 | 積算Ver. | <div class="commentAdd red nowrap">※入力漏れ多いので注意!</div> |
| Class040 | nexthouseシリーズ | <div class="commentAdd red nowrap">※nexthouseの場合のみ選択してください※</div> + <button type="button" onclick="$p.ex.clearClass040();">選択解除</button>(2026-08-09追加。ラジオボタンの選択解除用ボタン) |
| Class045 | 標準仕様 | <div class="commentAdd red nowrap">※標準仕様からの変更差額出しを希望する場合は、こちらに標準仕様を記載してください</div> |
| Class052 | 回答希望送付先 | <div class="commentAdd blue nowrap">※見積回答を送付して欲しい方を選択してください(選択ない場合は、依頼者と担当営業に回答送信します)</div> |
| Date064 | 使用期限 | <div class="commentAdd red nowrap">※他物件使用ルールに基づいて使用してください</div> |
| Date091 | 使用期限 | <div class="commentAdd red nowrap">※他物件使用ルールに基づいて使用してください</div> |
5. フィールド単位の権限・ロック制御
5.1 PermissionForCreating / PermissionForUpdating
| Site | 対象列 | 値 |
|---|---|---|
| 335410 | Class154(依頼者・担当者・CC一覧) | 4 |
| 335411 | ClassY(依頼者・担当者・CC一覧) | 4 |
いずれも「担当者・CC一覧をマージした複数選択の隠し集約列」に対する作成・更新権限設定。値4のビット意味は環境のロール定義に依存するため断定不可(12章参照)。
5.2 Create/Update/ReadColumnAccessControls(部署単位のフィールドアクセス制御)
335410:
| 制御種別 | 対象列 | 対象Dept |
|---|---|---|
| CreateColumnAccessControls | Class199(重複許容) | 25(営業積算課) |
| ReadColumnAccessControls | Class199(重複許容) | 25(営業積算課) |
| UpdateColumnAccessControls | Class199(重複許容) | 25(営業積算課) |
→ Class199(重複チェック回避弁)は営業積算課(Dept25)以外には作成・閲覧・更新いずれも不可。
335411:
CreateColumnAccessControls / UpdateColumnAccessControls(いずれも同一対象列、Dept25・26限定):
| 対象列 | ラベル |
|---|---|
| Description111 | 引継ぎ備考 |
| Attachments111 | NET見積原本 |
| Attachments112 | 算出根拠資料 |
| Class111 | 採用書類 |
| Attachments113 | その他 |
| Date111 | データ出力日 |
→ Tab-3「積算発注」タブの全項目が対象。ReadColumnAccessControlsの設定はなし(閲覧自体はタブ表示制御=CSSクラスによるTab-3非表示で担保、5.3節・11章参照。読み取り自体をAPIレベルで禁止してはいない)。
5.3 StatusControls(Statusに応じた項目ReadOnly化)
335410「登録完了後ロック」:
- 対象Status:
["200","900","910","920"](登録完了・依頼終了・保留・取消のいずれか) - ReadOnly対象列: Class003, Class004(重複チェックキーの姓名カナ)、Class022(エリア、こちらも重複チェックキー)
335411「依頼ロック」:
- 対象Status:
["200","400","450","480","500","510","520","800","810","900","901","910","920"](Status=100「新規」以外のほぼ全状態) - ReadOnly対象列: ClassE, ClassF, ClassG, ClassH, ClassI, Class007, Class031〜034, Class038〜040, Class045, Class050, Class052, DateA, DescriptionA, Description031, CheckA, Attachments031(=新規依頼の入力項目一式。Class007は2026-08-09追加)。ClassFは既にColumns/EditorColumnHashから削除済みの列だが、ColumnHash上には残存(12章参照)
→ 新規依頼発行後(Status=100を離れた後)は依頼内容が改変不可になる設計。「依頼修正」プロセス(13章参照)でStatusを100へ戻すことで再びロック解除される。
6. 集計設定(Aggregations)
両サイトとも設定なし(Aggregations: [])。
7. 他サイト連携(Links/ルックアップ/暗黙参照)
7.1 SiteSettings.Links(トップレベル)
335410:設定なし(Links: [])。
335411:
| 起点列 | 参照先SiteId | 内容 |
|---|---|---|
| Class003(エリア) | 210699 | エリアマスタ参照 |
| Class004(ブランド) | 109100 | 契約ブランドマスタ参照 |
| Class086(遅延理由) | 335450 | 遅延理由マスタ。ColumnSorterHash: NumA ascでソート。Lookup: マスタDescriptionA→自サイトDescription081。AutoPostBack: true, ColumnsReturnedWhenAutomaticPostback: Description081(遅延理由選択と同時にDescription081へ雛形文を自動転記) |
| ClassZ(テスト・リンク用) | 335410 | ①物件基本情報。15項目のLookup(詳細下表) |
| Class052(回答希望送付先) | 0(Usersテーブル直接参照) | TableName: Users, ColumnFilterExpressions: {UserId: "=[@Class153]"}(Class153担当者・CC一覧に含まれるユーザーのみに絞込選択) |
| Class077(質疑送付先) | 0(Usersテーブル直接参照) | 同上、Class153絞込 |
| Class085(遅延送付先) | 0(Usersテーブル直接参照) | 同上、Class153絞込 |
| Class095(回答送付先) | 0(Usersテーブル直接参照) | 同上、Class153絞込。加えてColumnFilterHash: {Disabled: "false"}(退職者等を除外) |
ClassZ→335410 のLookup全15項目:
| ①335410側(From) | ②335411側(To) | 内容 |
|---|---|---|
| ClassA | ClassA | 管理番号→親番 |
| Class032 | ClassD | 主担当営業 |
| Date001 | DateB | 承認図予定日 |
| Date002 | DateC | 着工予定日 |
| Class015 | Class001 | 施主名マージ→施主名 |
| Class021 | Class002 | 建上地 |
| Class022 | Class003 | エリア |
| Class023 | Class004 | 契約ブランド→ブランド |
| Class025 | Class005 | 建物種類 |
| Class024 | Class006 | 防火指定 |
| Class153 | Class153 | 担当者・CC一覧(そのまま) |
| Class153 | Class052 | 担当者・CC一覧→回答希望送付先の選択母集団 |
| Class153 | Class077 | 担当者・CC一覧→質疑送付先の選択母集団 |
| Class153 | Class085 | 担当者・CC一覧→遅延送付先の選択母集団 |
| Class153 | Class095 | 担当者・CC一覧→回答送付先の選択母集団 |
→ ②サイトが新規作成される際(①の「新規見積依頼」ボタン経由)、この15項目が①から②へ一括コピーされる。ClassZという列名は「テスト・リンク用」というラベルのままだが、実際には①→②の参照キー兼自動転記トリガーとして本番運用されている(ラベル名と用途の乖離、12章参照)。
7.2 項目のChoicesTextに埋め込まれたLookup(335410)
Links配列トップレベルには現れないが、各列のChoicesText内にJSON形式で個別Lookup設定を持つ。
| 起点列(335410) | 参照先SiteId | 内容 |
|---|---|---|
| ClassY(マスターシート) | 189112(★マスターシート) | SearchFormat: "[ClassA] - [ClassQ] 様 ([ClassF] [ClassG])"。ColumnSorterHash: ResultId desc。27項目のLookup(下表) |
| Class022(エリア) | 210699 | Lookup: マスタClassA→自サイトClassM(エリア記号)、OverwriteForm: true |
| Class023(契約ブランド) | 109100 | Lookup: マスタClassA→自サイトClassN(ブランド記号)、OverwriteForm: true |
| Class031(契約営業所) | 96243 | Lookup: マスタClassK→自サイトClass038(所属長)、OverwriteForm: true |
ClassY→189112(★マスターシート) のLookup全27項目:
| マスターシート(From) | 335410側(To) |
|---|---|
| ClassA | ClassZ(契約コード) |
| Class001〜004 | Class001〜004(施主1情報) |
| Class009,010,011,012 | Class005,006,007,008(施主2情報) |
| Class017,018,019,020 | Class009,010,011,012(施主3情報) |
| Class029 | Class021(建上地) |
| ClassW | Class022(エリア) |
| Class033 | Class023(契約ブランド) |
| Class040 | Class024(防火指定) |
| Class043 | Class025(建物種類) |
| ClassF,G,H,I,L,K,J | Class031〜037(契約営業所〜担当監督) |
| Date063 | Date001(承認図予定日) |
| DateH | Date002(着工予定日) |
いずれもOverwrite: false(転記先に既存値があれば上書きしない)。実際の転記処理はサーバー側AutoPostBackではなく、10章記載のクライアントスクリプト($p.apiGetによるその場取得)で行われる。
7.3 ServerScriptがSiteIdを直接ハードコード参照している暗黙的テーブル参照
正式なLinks設定には現れない、items.Get(SiteId)による直接参照。
| サイト | ServerScriptファイル | 参照先SiteId | 目的 |
|---|---|---|---|
| 335410 | serverscripts/1_マスターデータ取得.js | 210699 | エリアマスタ全件取得→hidden格納(クライアント側Class022変更時の逆引き転記用) |
| 335410 | serverscripts/1_マスターデータ取得.js | 109100 | 契約ブランドマスタ全件取得→hidden格納(Class023変更時の逆引き転記用) |
| 335410 | serverscripts/1_マスターデータ取得.js | 96243 | 契約営業所マスタ全件取得→hidden格納(Class031変更時の逆引き転記用) |
| 335411 | serverscripts/1_祝日リスト取得スクリプト.js | 192267(祝日テーブル) | 本日−30日〜+60日の祝日一覧取得→hidden格納(DateA/DateJ/Date073等の営業日計算用) |
7.4 サイトツリー上の位置関係
74663(親サイト、詳細不明・本仕様書の対象外)
├─ 335410 ①物件基本情報 Ver0.3
└─ 335411 ②問い合わせ内容管理 Ver0.3
335410↔335411は上記7.1のClassZ Linksで対等連携。加えて両サイトとも下記の外部マスタサイトを参照:
189112 ★マスターシート ← 335410(ClassY)
210699 エリアマスタ ← 335410(Class022) / 335411(Class003, Links)
109100 契約ブランドマスタ ← 335410(Class023) / 335411(Class004, Links)
96243 契約営業所マスタ ← 335410(Class031)
192267 祝日テーブル ← 335411(ServerScript直接参照)
335450 遅延理由マスタ ← 335411(Class086, Links)
8. プロセス設定(Processes)・ワークフロー・通知設定
定義ファイル:335410/processes.json / 335411/processes.json
Status選択肢(ChoicesText由来、コード,正式名称,一覧表示名,色クラスの順):
335410 Status: 100,新規登録,新規,new-status-white / 200,登録完了,登録,new-status-pink / 900,依頼終了,依頼,new-status-blue / 910,保留,保留,new-status-grey / 920,取消,取消,new-status-grey
335411 Status(Class062と共通): 100,新規,新規,white / 200,新規依頼,新規依頼,light-orange / 400,受付済,受付済,orange / 450,依頼者質疑中,質疑中,yellow / 480,依頼者回答有,回答有,white / 500,積算対応中,積算対応中,green / 510,外部見積待ち,外部見積待ち,green / 520,社内確認中,社内確認中,green / 800,一部回答,一部回答,pink / 810,回答完了,回答完了,light-blue / 900,完了,完了,blue / 901,問合せ不要,問合せ不要,blue / 910,保留,保留,grey / 920,取消,取消,grey
8.1 335410 プロセス一覧(全5件)
| Id | Name | CurrentStatus→ChangedStatus | 実行者条件 | 入力チェック | 確認ダイアログ | 通知 |
|---|---|---|---|---|---|---|
| 1 | 登録完了 | 100→200 | ClassB(登録者)="Own"(本人限定) | Class001〜004,021〜023,031,032必須 | あり | あり(1件) |
| 2 | 依頼受付終了 | 200→900 | Dept25 | なし | あり | あり(1件) |
| 3 | 物件保留 | -1(任意)→910 | Dept25 | なし | あり | あり(1件) |
| 4 | 物件取消 | -1(任意)→920 | Dept25 | なし | あり | あり(1件) |
| 5 | 登録完了へ戻す | -1(Status=910/920から, ColumnFilterHash指定)→200 | Dept25 | なし | あり | あり(1件) |
通知詳細(335410):
- 登録完了:宛先
e-sekisan@next-hd.co.jp固定。件名【営業積算】[ClassA] [Class001]様 : 物件登録完了。本文「物件情報の登録が完了しました。内容をご確認ください。{Url} ---------- 管理番号:[ClassA] 施主名:[Class001] [Class002]様 ----------」 - 依頼受付終了:宛先
[ClassB](登録者本人)、CCe-sekisan@next-hd.co.jp。件名…依頼受付終了。本文「営業積算より本件について依頼受付を終了しましたのでご連絡します。」+送信専用文言+URL+管理番号/施主名 - 物件保留:宛先
[ClassB]、CCe-sekisan@next-hd.co.jp。件名…物件保留。本文「本件を保留といたしました」 - 物件取消:宛先
[ClassB]、CCe-sekisan@next-hd.co.jp。件名…物件取消。本文「本件を取消といたしました」 - 登録完了へ戻す:宛先
[ClassB]、CCe-sekisan@next-hd.co.jp。件名…登録完了へ再設定。本文「保留・取消を解除し、登録完了へ戻しました」
全通知共通:Type:1(メール)、MediaType: application/json、BeforeCondition/AfterCondition/Expression: 0。
335410 ワークフロー図(Mermaid):
flowchart LR
100((新規登録<br/>100))
200((登録完了<br/>200))
900((依頼終了<br/>900))
910((保留<br/>910))
920((取消<br/>920))
any((任意))
100 -->|"📧登録完了"| 200
200 -->|"📧依頼受付終了 (Dept25)"| 900
any -->|"📧物件保留 (Dept25)"| 910
any -->|"📧物件取消 (Dept25)"| 920
910 -->|"📧登録完了へ戻す (Dept25)"| 200
920 -->|"📧登録完了へ戻す (Dept25)"| 200
8.2 335411 プロセス一覧(全19件)
| Id | Name | Current→Changed | 実行者条件 | 入力チェック | 通知 |
|---|---|---|---|---|---|
| 1 | 積算へ依頼(2026-08-09改称、旧名称「新規依頼」) | 100→200 | ClassC="Own" | なし(旧ValidateInputs 12項目は2026-08-09全削除。問合せ種別(Class007)に応じた必須チェックはスクリプト側checkInquiryTypeRequiredFields()へ移行、10章参照) |
あり |
| 2 | 受付 | 200→400 | Dept25 | なし | なし(受付日Date081・受付者Class081は2026-08-09よりプロセス側「データの変更」ではなくクリック時にスクリプト側setTodayAndUser()で設定、10章参照) |
| 3 | 質疑送信 | -1(Status:100,400,450,480,500,510,520,800)→450 | Dept25 | なし(スクリプト側でDescription075必須チェック) | あり |
| 4 | 回答送信 | 450→480 | ClassY="Own"(依頼者・担当者・CC一覧に本人含む) | なし(スクリプト側でDescription076必須チェック、かつuserId=ClassC二重判定) | あり |
| 5 | 質疑回答削除 | 450→450 | Dept25 | なし(スクリプト側でチェック選択必須) | あり |
| 6 | 積算対応中へ | -1(Status:400,450,480,500,510,520)→500 | Dept25 | なし | なし |
| 7 | 外部見積待ちへ | -1(同上範囲)→510 | Dept25 | なし | なし(見積依頼日Date082・見積依頼者Class082は2026-08-09よりスクリプト側setTodayAndUser()で設定) |
| 8 | 社内確認中へ | -1(同上範囲)→520 | Dept25 | なし | なし |
| 9 | 一部回答 | -1(Status:400,450,480,500,510,520,800)→800 | Dept25 | なし | あり(返信日Date103・返信者Class103は2026-08-09よりスクリプト側setTodayAndUser()で設定) |
| 10 | 見積回答 | -1(Status:400,450,480,500,510,520,800,810)→810 | Dept25 | なし | あり(返信日Date103・返信者Class103は2026-08-09よりスクリプト側setTodayAndUser()で設定。以前あったクライアント側activate/disable制御(estimateReplyEnabledStatuses)は2026-08-09撤去、実行可否は本Process View条件のみに一本化) |
| 11 | 完了 | -1(Status:800,810)→900 | Dept25 | なし | なし |
| 12 | 問い合わせ不要 | -1(Status:200,400,450,480,500,510,520)→901 | Dept25 | なし | あり |
| 13 | 依頼修正 | 200→100 | ClassC="Own" | なし | なし |
| 14 | 差戻し | -1(Status:400,450,480,500,510,520)→100 | Dept25 | なし | あり |
| 15 | 依頼取消 | -1(Status:400,450,480,500,510,520,910)→920 | Dept25 | なし | あり |
| 16 | 保留 | -1(Status:400,450,480,500,510,520)→910 | Dept25 | なし | なし |
| 17 | 保留解除 | 910→500 | Dept25 | なし | なし |
| 18 | 遅延連絡 | -1(Status:400,450,480,500,510,520,800)→-1(状態変化なし) | Dept25 | なし(スクリプト側でDescription081必須チェック) | あり(遅延連絡日Date084・遅延連絡者Class087は2026-08-09よりスクリプト側setTodayAndUser()で設定) |
| 19 | ①物件基本情報へ戻る | -1→-1(状態変化なし、ActionType:90=画面遷移のみ) | 制限なし | なし | なし |
通知詳細(335411、あり=Type:1メール、MediaType application/json共通):
- 積算へ依頼(旧名称:新規依頼):宛先
e-sekisan@next-hd.co.jp、CC[ClassC](依頼者)。件名【営業積算】[ClassA]-[ClassB] [Class001]様 : 新規依頼(通知の件名・本文はDisplayName改称の影響を受けず現状維持)。本文「依頼者より新規依頼が届きました。内容を確認し、受付処理をお願いします。」+URL+親番/子番/施主名 - 質疑送信:宛先
[ClassC]、CC[Class077](質疑送付先)。件名…質疑送信。本文「営業積算より依頼内容についての質疑が届きました。」+質疑回答期日[Date073]+質疑内容[Description075] - 回答送信:宛先
e-sekisan@next-hd.co.jp、CC[ClassC], [Class077]。件名…回答送信。本文「依頼者より質疑回答が届きました。」+回答内容[Description076] - 質疑回答削除:宛先
[ClassC]、CC[Class077]。件名…質疑回答削除。本文「質疑回答の内容が削除されました。再度ご回答をお願いする場合がございます。」 - 一部回答:宛先
[ClassC]、CC[Class095](回答送付先)。件名…一部回答。本文「一部見積回答が届きました。([Num101]回目の回答)」+見積回答[Description091]+別途費用[Description092] - 見積回答:宛先
[ClassC]、CC[Class095]。件名…見積回答。本文構成は一部回答と同一(「見積回答が届きました」) - 問い合わせ不要:宛先
[ClassC]、CC[Class052](回答希望送付先)。件名…問い合わせ不要。本文「本件について問い合わせ不要と判断いたしました」 - 差戻し:宛先
[ClassC]のみ(CCなし)。件名…差戻し。本文「依頼内容の見直しをお願いしたく、差し戻しいたします。」 - 依頼取消:宛先
[ClassC]、CC[Class052]。件名…依頼取消。本文「依頼者からの申し出により依頼取消となりました」 - 遅延連絡:宛先
[ClassC]、CC[Class085](遅延送付先)。件名…遅延連絡。本文「見積回答遅延のご連絡です。」+遅延理由[Class086]+返信予定日[Date085]+遅延連絡内容[Description081]
いずれも「※このメールは送信専用です。本メールに返信いただいても、内容は確認できませんのでご了承ください。お問い合わせは、営業積算窓口までご連絡ください。」の定型文を含む(新規依頼・質疑回答削除・問い合わせ不要・依頼取消は定型文なし版)。
335411 ワークフロー図(Mermaid):
flowchart LR
100((新規<br/>100))
200((新規依頼<br/>200))
400((受付済<br/>400))
450((依頼者質疑中<br/>450))
480((依頼者回答有<br/>480))
500((積算対応中<br/>500))
510((外部見積待ち<br/>510))
520((社内確認中<br/>520))
800((一部回答<br/>800))
810((回答完了<br/>810))
900((完了<br/>900))
901((問合せ不要<br/>901))
920((取消<br/>920))
910((保留<br/>910))
any((任意))
100 -->|"📧積算へ依頼"| 200
200 -->|"受付 (Dept25)"| 400
100 -->|"📧質疑送信 (Dept25)"| 450
400 -->|"📧質疑送信 (Dept25)"| 450
450 -->|"📧質疑送信 (Dept25)"| 450
480 -->|"📧質疑送信 (Dept25)"| 450
500 -->|"📧質疑送信 (Dept25)"| 450
510 -->|"📧質疑送信 (Dept25)"| 450
520 -->|"📧質疑送信 (Dept25)"| 450
800 -->|"📧質疑送信 (Dept25)"| 450
450 -->|"📧回答送信"| 480
450 -->|"📧質疑回答削除 (Dept25)"| 450
400 -->|"積算対応中へ (Dept25)"| 500
450 -->|"積算対応中へ (Dept25)"| 500
480 -->|"積算対応中へ (Dept25)"| 500
500 -->|"積算対応中へ (Dept25)"| 500
510 -->|"積算対応中へ (Dept25)"| 500
520 -->|"積算対応中へ (Dept25)"| 500
400 -->|"外部見積待ちへ (Dept25)"| 510
450 -->|"外部見積待ちへ (Dept25)"| 510
480 -->|"外部見積待ちへ (Dept25)"| 510
500 -->|"外部見積待ちへ (Dept25)"| 510
510 -->|"外部見積待ちへ (Dept25)"| 510
520 -->|"外部見積待ちへ (Dept25)"| 510
400 -->|"社内確認中へ (Dept25)"| 520
450 -->|"社内確認中へ (Dept25)"| 520
480 -->|"社内確認中へ (Dept25)"| 520
500 -->|"社内確認中へ (Dept25)"| 520
510 -->|"社内確認中へ (Dept25)"| 520
520 -->|"社内確認中へ (Dept25)"| 520
400 -->|"📧一部回答 (Dept25)"| 800
450 -->|"📧一部回答 (Dept25)"| 800
480 -->|"📧一部回答 (Dept25)"| 800
500 -->|"📧一部回答 (Dept25)"| 800
510 -->|"📧一部回答 (Dept25)"| 800
520 -->|"📧一部回答 (Dept25)"| 800
800 -->|"📧一部回答 (Dept25)"| 800
400 -->|"📧見積回答 (Dept25)"| 810
450 -->|"📧見積回答 (Dept25)"| 810
480 -->|"📧見積回答 (Dept25)"| 810
500 -->|"📧見積回答 (Dept25)"| 810
510 -->|"📧見積回答 (Dept25)"| 810
520 -->|"📧見積回答 (Dept25)"| 810
800 -->|"📧見積回答 (Dept25)"| 810
810 -->|"📧見積回答 (Dept25)"| 810
800 -->|"完了 (Dept25)"| 900
810 -->|"完了 (Dept25)"| 900
200 -->|"📧問い合わせ不要 (Dept25)"| 901
400 -->|"📧問い合わせ不要 (Dept25)"| 901
450 -->|"📧問い合わせ不要 (Dept25)"| 901
480 -->|"📧問い合わせ不要 (Dept25)"| 901
500 -->|"📧問い合わせ不要 (Dept25)"| 901
510 -->|"📧問い合わせ不要 (Dept25)"| 901
520 -->|"📧問い合わせ不要 (Dept25)"| 901
200 -->|"依頼修正"| 100
400 -->|"📧差戻し (Dept25)"| 100
450 -->|"📧差戻し (Dept25)"| 100
480 -->|"📧差戻し (Dept25)"| 100
500 -->|"📧差戻し (Dept25)"| 100
510 -->|"📧差戻し (Dept25)"| 100
520 -->|"📧差戻し (Dept25)"| 100
400 -->|"📧依頼取消 (Dept25)"| 920
450 -->|"📧依頼取消 (Dept25)"| 920
480 -->|"📧依頼取消 (Dept25)"| 920
500 -->|"📧依頼取消 (Dept25)"| 920
510 -->|"📧依頼取消 (Dept25)"| 920
520 -->|"📧依頼取消 (Dept25)"| 920
910 -->|"📧依頼取消 (Dept25)"| 920
400 -->|"保留 (Dept25)"| 910
450 -->|"保留 (Dept25)"| 910
480 -->|"保留 (Dept25)"| 910
500 -->|"保留 (Dept25)"| 910
510 -->|"保留 (Dept25)"| 910
520 -->|"保留 (Dept25)"| 910
910 -->|"保留解除 (Dept25)"| 500
400 -->|"📧遅延連絡 (Dept25)"| 400
450 -->|"📧遅延連絡 (Dept25)"| 450
480 -->|"📧遅延連絡 (Dept25)"| 480
500 -->|"📧遅延連絡 (Dept25)"| 500
510 -->|"📧遅延連絡 (Dept25)"| 510
520 -->|"📧遅延連絡 (Dept25)"| 520
800 -->|"📧遅延連絡 (Dept25)"| 800
any -->|"①物件基本情報へ戻る"| any
8.3 プロセスボタンのUI上の扱い(335411固有)
335411は19プロセスのうち12件(受付/Id2、質疑送信/Id3、回答送信/Id4、質疑回答削除/Id5、積算対応中へ/Id6、外部見積待ちへ/Id7、社内確認中へ/Id8、一部回答/Id9、見積回答/Id10、問い合わせ不要/Id12、差戻し/Id14、遅延連絡/Id18)について、ServerScript1_祝日リスト取得スクリプト.jsがelements.DisplayType('Process_N', 3)でPleasanter標準のプロセスボタン自体を非表示化(2026-08-09、従来6件から12件に拡大)し、代わりに3_質疑応答スクリプト.jsが対応する独自ボタンを動的生成・各項目欄の右横(または直下)へ配置している。独自ボタンのクリックは$p.ex.clickProcessByName(name)でラベル名一致の隠れた標準ボタンを探して.click()する方式(Processes側のId構成変更に影響されない設計)。
独自ボタンの生成条件(2026-08-09変更):以前は一部のボタン(営業積算確認・遅延連絡・見積回答・質疑回答削除)が$p.deptId()=='25'のハードコード判定(eSekisanFlag)で表示可否を決めていたが、対応する標準プロセスボタンがelements.DisplayType(id,3)=「invisible」(DOM上には残るが見た目のみ非表示)であることを踏まえ、DOM実在判定$p.ex.processButtonExists('プロセス名')(Depts・View条件にそのまま追従、部署IDのハードコードなし)に全面統一した。これにより、Process設定側のDepts変更が独自ボタンの表示可否へ自動反映される。
独自ボタンの配置一覧(2026-08-09時点):
| ボタン | ラベル | 配置先 | 生成関数 |
|---|---|---|---|
| 質疑送信 | 質疑送信 | 質疑ファイル(Attachments071)欄の下 | messageSaveButton() |
| 回答送信 | 回答送信 | 回答ファイル(Attachments072)欄の下 | messageSaveButton() |
| 積算対応中へ | 営業積算確認(ボタン表示名のみ独自) | 回答ファイル(Attachments072)欄の下 | messageSaveButton() |
| 質疑回答削除 | 質疑回答削除 | 質疑内容(Description075)欄の上 | messageHistory()内 |
| 受付 | 受付 | 受付者(Class081)欄の右横 | receiptButton()(2026-08-09新設) |
| 外部見積待ちへ | 外部見積待ちへ | 見積依頼者(Class082)欄の右横 | externalEstimateWaitButton()(2026-08-09新設) |
| 社内確認中へ・問い合わせ不要・差戻し | 社内確認中へ/問い合わせ不要/差戻し | 状況(Status)欄の右横に一列 | statusRowButtons()(2026-08-09新設) |
| 一部回答 | 一部回答 | 返信者(Class103)欄の右横(上段) | partialAnswerButton()(2026-08-09新設) |
| 見積回答 | 見積回答 | 返信者(Class103)欄の右横(一部回答の直下) | estimateReplyButton()(2026-08-09、配置先をDescription092欄の下から移動) |
| 遅延連絡 | 遅延連絡 | 遅延連絡内容(Description081)欄の下 | delayNoticeButton()(配置先は変更なし) |
一部回答・見積回答はいずれも実行可否をProcess側View条件(Status)のみに委ね、クライアント側でのactivate/disable制御は行わない(2026-08-09、見積回答のestimateReplyEnabledStatusesによる独自disable判定を撤去)。
9. スタイル(Styles)全文
9.1 335410(2ファイル)
| Id | Title | 適用範囲 | ファイル |
|---|---|---|---|
| 1 | 01_読み取り専用スタイル | New/Edit | styles/1_01.読み取り専用.css |
| 2 | 02_共通スタイル | New/Edit | styles/2_02.共通.css |
1_01.読み取り専用.css
/*
@config : next999.json
@filename : 01_読み取り専用スタイル.css
@title : 01_読み取り専用スタイル
@name :
@siteIds : 335101
@siteTitles : ①物件基本情報
@disabled: false
*/
label.error {
pointer-events: none;
z-index: 100;
border-top: none !important;
}
.code-readonly {
pointer-events: none;
background-color: lightgrey;
font-weight: bold;
}
.code-readonly2,
.code-readonly2+button,
.code-readonly2+div.ui-icon,
.code-readonly3,
.code-readonly3+button,
.code-readonly3+div.ui-icon {
font-weight: bold;
&:active {
pointer-events: none;
}
a:not(.ui-spinner-button) {
pointer-events: auto;
}
}
.code-readonly2 select,
.code-readonly2 input,
.code-readonly2 button.ui-multiselect,
.code-readonly2 .control-markup,
.code-readonly2 .ui-spinner,
span[data-readonly="1"] {
pointer-events: none;
border: dashed 1px black;
background-color: lightgray !important;
color: black !important;
.ui-spinner-input {
border: none;
}
}
.code-readonly3 select,
.code-readonly3 input,
.code-readonly3 button.ui-multiselect,
.code-readonly3 .control-markup,
.code-readonly3 .ui-spinner {
pointer-events: none;
border: dashed 1px black;
.ui-spinner-input {
border: none;
}
}
.code-readonly2,
.code-readonly3,
.field-normal:has(span[data-readonly="1"]),
.field-wide:has(span[data-readonly="1"]) {
position: relative;
&:hover::after {
content: "読取り専用です";
position: absolute;
left: 120px;
top: calc(100% - 9px);
margin-top: 4px;
background: #00000080;
color: #fff;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: normal !important;
pointer-events: none;
z-index: 9999;
white-space: nowrap;
}
}
.code-required,
select[data-validate-required="1"],
input[data-validate-required="1"] {
border: double 3px red !important;
&+button.ui-multiselect {
border: double 3px red !important;
}
}
.field-normal:has(.code-required),
.field-wide:has(.code-required),
.field-normal:has(select[data-validate-required="1"]),
.field-wide:has(select[data-validate-required="1"]),
.field-normal:has(input[data-validate-required="1"]),
.field-wide:has(input[data-validate-required="1"]) {
position: relative;
&:hover::after {
content: "入力必須です";
position: absolute;
left: 120px;
top: calc(100% - 9px);
margin-top: 4px;
background: #ff000080;
color: #fff;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: normal !important;
pointer-events: none;
z-index: 9999;
white-space: nowrap;
}
}
.error {
border: double 3px red !important;
}
2_02.共通.css
/*
@config : next999.json
@filename : 02_共通スタイル.css
@title : 02_共通スタイル
@name :
@siteIds : 335101
@siteTitles : ①物件基本情報
@disabled: false
*/
/*見出しサイズ*/
.field-section {
font-size: 1.5em !important;
font-weight: bold !important;
height: auto;
}
/*項目の後ろの行間を少し狭める*/
.field-normal {
height: 35px;
}
.field-wide {
min-height: 35px;
padding: 0 10px 5px 0;
}
/*コメントのある項目の高さだけ少し広げる*/
div:has(.commentAdd).field-normal {
height: 50px;
}
9.2 335411(3ファイル)
| Id | Title | 適用範囲 | ファイル |
|---|---|---|---|
| 1 | 00_読み取り専用スタイル | New/Edit | styles/1_00_読み取り専用スタイル.css.css |
| 2 | 01_共通スタイル | New/Edit | styles/2_01_共通スタイル.css.css |
| 3 | 04_質疑回答テーブル | New/Edit | styles/3_3_04_質疑回答テーブル.css.css |
1_00_読み取り専用スタイル.css.css
335410の1_01.読み取り専用.cssと内容は完全に同一(コメントヘッダのfilename記述のみ異なる:00_読み取り専用スタイル.css)。重複掲載は省略、9.1節参照。
2_01_共通スタイル.css.css
/*
@config :
@filename : ${psFileBasename}
@title : 01_共通スタイル
@name
@siteIds :
@siteTitles : 物件基本情報
@disabled: false
*/
/*見出しサイズ*/
.field-section {
font-size: 1.5em !important;
font-weight: bold !important;
height: auto;
}
/*項目の後ろの行間を少し狭める*/
.field-normal {
height: 35px;
}
.field-wide {
min-height: 35px;
padding: 0 10px 5px 0;
}
/*コメントのある項目の高さだけ少し広げる*/
div:has(.commentAdd).field-normal {
height: 50px;
}
.dateA_alert {
background-color: red !important;
color: white !important;
}
/*「営業積算」タブ(_Tab-2/FieldSetTab2):既定非表示。営業積算課(Dept25)のみ01_基本スクリプト.jsが
bodyへshow-sekisan-tabクラスを付与し表示化する(01_基本スクリプト.js側の判定と対)*/
#EditorTabs li[aria-controls="FieldSetTab2"] {
display: none !important;
}
#FieldSetTab2 {
display: none !important;
}
body.show-sekisan-tab #EditorTabs li[aria-controls="FieldSetTab2"] {
display: list-item !important;
}
body.show-sekisan-tab #FieldSetTab2 {
display: block !important;
}
/*「積算発注」タブ(_Tab-3/FieldSetTab3):既定非表示。dept25/26/28/66/36所属者またはuserId 913/987/1153のみ
bodyへshow-sekisan-hachu-tabクラスを付与し表示化する(01_基本スクリプト.js側の判定と対)*/
#EditorTabs li[aria-controls="FieldSetTab3"] {
display: none !important;
}
#FieldSetTab3 {
display: none !important;
}
body.show-sekisan-hachu-tab #EditorTabs li[aria-controls="FieldSetTab3"] {
display: list-item !important;
}
body.show-sekisan-hachu-tab #FieldSetTab3 {
display: block !important;
}
3_3_04_質疑回答テーブル.css.css
/*
@config :
@filename : 04_質疑回答テーブル.css
@title : 04_質疑回答テーブル
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
#messageTable {
width: 98%;
margin: 8px;
border-collapse: separate !important;
}
.radius-table {
border-radius: 10px !important;
border-spacing: 0;
border: none;
border-left: 1px solid #999;
border-top: 1px solid #999;
}
.radius-table tr>* {
padding: 5px 10px;
border: none;
border-right: 1px solid #999;
border-bottom: 1px solid #999;
}
.radius-table tr:first-child>*:first-child {
border-radius: 10px 0 0 0;
}
.radius-table tr:first-child>*:last-child {
border-radius: 0 10px 0 0;
}
.radius-table tr:last-child>*:first-child {
border-radius: 0 0 0 10px;
}
.radius-table tr:last-child>*:last-child {
border-radius: 0 0 10px 0;
}
.Question>td {
background-color: coral;
vertical-align: middle;
}
.Question\.Disabled>td,
.Answer\.Disabled>td {
background-color: grey !important;
}
.Answer>td {
background-color: yellow;
vertical-align: middle;
}
.shitsugi_type {
font-weight: bold;
font-size: 1.5em;
}
.shitsugi_userName {
text-align: center;
width: 150px;
font-size: 1.2em;
}
.shitsugi_messageTime {
text-align: right;
font-size: 1.2em;
}
.shitsugi_messageBody {
background-color: white !important;
}
.shitsugi_deleteCheck {
background-color: white !important;
text-align: center;
width: 80px;
}
.shitsugi_preWrap {
white-space: pre-wrap;
text-overflow: ellipsis;
break-inside: avoid;
}
10. スクリプト(Scripts)/サーバースクリプト(ServerScripts)全文
10.1 335410 Scripts(2ファイル)
| Id | Title | 適用範囲 | 概要 |
|---|---|---|---|
| 1 | 01_基本スクリプト | New/Edit | 施主名・重複チェックキー管理、マスターシート/エリア/契約ブランド/契約営業所の自動転記、案件状況(ClassS)自動判定、担当者一覧マージ、readonly項目のTab移動除外、ボタン名称変更・表示制御 |
| 2 | 02_プロセス処理スクリプト | New/Edit | プロセスボタン共通ディスパッチャ(processScript)、確認ダイアログ制御、必須エラー項目の集約表示 |
scripts/1_01_基本スクリプト.js.js
/*
@config :
@filename : ${psFileBasename}
@title : 01_基本スクリプト
@name :
@siteIds :
@siteTitles : 物件基本情報
@disabled: false
*/
let pageLoading = 0;
//----------------------------------------------------------------------
//初期処理
window.addEventListener('load', function () {
if (pageLoading == 0) {
console.log('Window load Event');
$p.ex.onLoad();
}
});
$p.events.on_editor_load = function () {
if (pageLoading == 0) {
console.log('on Editor Load');
$p.ex.onLoad();
}
}
//----------------------------------------------------------------------
$p.ex.onLoad = function () {
//ロード開始
pageLoading = 1;
//初期処理
$p.ex.init();
}
//初期処理
$p.ex.init = function () {
console.log('initialize...');
//ロード時に施主名を予め控えておく
$p.loadCustomerName();
//依頼発行ボタン名称変更
$p.changeButtonName();
//登録状況(Status)に応じて依頼発行ボタンの表示・非表示を切り替え
$p.toggleNewRequestButton();
//ServerScriptが事前取得したエリア・契約ブランド・契約営業所マスタをhiddenから読み込む
$p.loadStaticMasters();
//マスターシート・エリア・契約ブランド・契約営業所の現在値を「変更前の値」として記憶
//(未選択→選択のときは既存値を保持、選択済み→別選択のときは上書き、を判定するため)
$p.rememberLookupBaseValues();
//登録完了(Status=200)以降、マスターシートが選択済みの場合のみ選択変更を不可にする
//(未選択のままなら登録完了後も選択可能にする)
$p.toggleClassYLock();
//code-readonly2(読取り専用)が設定されている全フィールドをTab移動対象から除外する
//(ClassYの動的ロックも含め、上記の処理が確定した後に実行する)
$p.applyReadonlyTabindex();
}
//入力チェック後処理
$p.events.after_validate = function (args) {
//担当者一覧のマージ
$p.mergeUserList();
}
//画面更新後
$p.events.after_set_Update = function (args) {
//初期処理
$p.ex.init();
}
//----------------------------------------------------------------------
//施主名を控える
let customerName = {
'firstName': '',
'lastName': '',
'firstNameKana': '',
'lastNameKana': ''
}
//ロード時に施主名を予め控えておく
$p.loadCustomerName = function () {
customerName.firstName = $('#Results_Class002').val(); //施主1・名
customerName.lastName = $('#Results_Class001').val(); //施主1・姓
customerName.firstNameKana = $('#Results_Class004').val(); //施主1・名(カナ)
customerName.lastNameKana = $('#Results_Class003').val(); //施主1・姓(カナ)
}
//----------------------------------------------------------------------
//入力チェック前処理
//更新ボタンを押したあと、入力必須項目などのチェックの前に処理される関数
$p.events.before_validate = function (args) {
//新規作成時のみ、重複チェックキー(施主氏名カナ・エリア)の最終確認を促す
//(登録完了処理後、この項目は編集不可になるため、新規作成の保存前に一度だけ確認する)
if ($p.action() === 'new') {
if (!confirm($p.buildNewRecordKeyConfirmMessage())) {
return false; //保存中断
}
}
//施主名がどれか1つでも変更されていたら処理する
if (customerName.firstName != $('#Results_Class002').val() || customerName.lastName != $('#Results_Class001').val() || customerName.firstNameKana != $('#Results_Class004').val() || customerName.lastNameKana != $('#Results_Class003').val()) {
$p.mergeCustomerName();
}
//担当者一覧のマージ
$p.mergeUserList();
//重複チェック用の連結値をClass200へ保存(Class200はNoDuplication設定済みのため、
//保存時にPleasanter標準機能が重複判定・アラート表示・保存ブロックを行う)
$p.mergeClass200();
}
//----------------------------------------------------------------------
//重複チェック用に施主1・姓(カナ)+施主1・名(カナ)+エリア+重複許容をClass200へ連結する
//主担当営業・契約営業所は人事異動等で変わりうるためキーに含めない(同一物件でも担当者交代で
//別レコード扱いになってしまうのを防ぐ)。エリアは物件の位置そのもので不変のためキーに採用。
//エリアが異なれば同姓同名でも別物件として許容する。
//Class199(重複許容、営業積算専用・通常空欄)は、同一エリアに同姓同名の別物件が実在する
//稀なケースの回避弁。営業積算が「重複1」~「重複10」のいずれかを設定すると、その値も
//キーに含まれるため重複チェックをすり抜けられる(同一エリア同姓同名は最大10件まで想定)。
$p.mergeClass200 = function () {
let str1 = $p.normalizeForDuplicationKey($('#Results_Class003').val()); //施主1・姓(カナ)
let str2 = $p.normalizeForDuplicationKey($('#Results_Class004').val()); //施主1・名(カナ)
let str3 = $p.normalizeForDuplicationKey($('#Results_Class022').val()); //エリア
let str4 = $('#Results_Class199').val() || ''; //重複許容(営業積算専用)
$p.set($('#Results_Class200'), str1 + str2 + str3 + str4); //重複チェック
}
//----------------------------------------------------------------------
//重複チェックキー用の文字列正規化
//カナ項目はClientRegexValidation(^[ァ-ヶー0-90-9 ]+$)で全角カタカナ・長音・数字・スペース以外
//入力できない設計だが、半角カナはこの正規表現をすり抜けるため変換する。ひらがな・全角英数字の変換は
//保険(通常入力では発生しないがAPI経由投入等に備える)。
$p.normalizeForDuplicationKey = function (str) {
if (!str) return '';
let s = String(str);
//半角カナ→全角カナ変換(濁点・半濁点合成)
s = $p.halfKanaToFullKana(s);
//ひらがな→全角カタカナ変換
s = s.replace(/[ぁ-ゖ]/g, function (match) {
return String.fromCharCode(match.charCodeAt(0) + 0x60);
});
//全角英数字→半角変換
s = s.replace(/[A-Za-z0-9]/g, function (match) {
return String.fromCharCode(match.charCodeAt(0) - 0xFEE0);
});
//半角スペース→全角スペースに統一
s = s.replace(/ /g, ' ');
//記号除去(氏名区切りとして使われる中黒「・」と長音「ー」は残す)
s = s.replace(/[!-\/:-@\[-`{-~!-/:-@[-`{-~]/g, function (match) {
return (match === '・') ? match : '';
});
return s;
}
//半角カナ文字列を全角カナへ変換する(濁点・半濁点はゼロ幅文字゙゚として後続するため合成する)
$p.halfKanaToFullKana = function (str) {
let kanaMap = {
'ア': 'ア', 'イ': 'イ', 'ウ': 'ウ', 'エ': 'エ', 'オ': 'オ',
'カ': 'カ', 'キ': 'キ', 'ク': 'ク', 'ケ': 'ケ', 'コ': 'コ',
'サ': 'サ', 'シ': 'シ', 'ス': 'ス', 'セ': 'セ', 'ソ': 'ソ',
'タ': 'タ', 'チ': 'チ', 'ツ': 'ツ', 'テ': 'テ', 'ト': 'ト',
'ナ': 'ナ', 'ニ': 'ニ', 'ヌ': 'ヌ', 'ネ': 'ネ', 'ノ': 'ノ',
'ハ': 'ハ', 'ヒ': 'ヒ', 'フ': 'フ', 'ヘ': 'ヘ', 'ホ': 'ホ',
'マ': 'マ', 'ミ': 'ミ', 'ム': 'ム', 'メ': 'メ', 'モ': 'モ',
'ヤ': 'ヤ', 'ユ': 'ユ', 'ヨ': 'ヨ',
'ラ': 'ラ', 'リ': 'リ', 'ル': 'ル', 'レ': 'レ', 'ロ': 'ロ',
'ワ': 'ワ', 'ヲ': 'ヲ', 'ン': 'ン',
'ァ': 'ァ', 'ィ': 'ィ', 'ゥ': 'ゥ', 'ェ': 'ェ', 'ォ': 'ォ',
'ッ': 'ッ', 'ャ': 'ャ', 'ュ': 'ュ', 'ョ': 'ョ', 'ー': 'ー'
};
let dakutenMap = { 'カ': 'ガ', 'キ': 'ギ', 'ク': 'グ', 'ケ': 'ゲ', 'コ': 'ゴ', 'サ': 'ザ', 'シ': 'ジ', 'ス': 'ズ', 'セ': 'ゼ', 'ソ': 'ゾ', 'タ': 'ダ', 'チ': 'ヂ', 'ツ': 'ヅ', 'テ': 'デ', 'ト': 'ド', 'ハ': 'バ', 'ヒ': 'ビ', 'フ': 'ブ', 'ヘ': 'ベ', 'ホ': 'ボ' };
let handakutenMap = { 'ハ': 'パ', 'ヒ': 'ピ', 'フ': 'プ', 'ヘ': 'ペ', 'ホ': 'ポ' };
let result = '';
for (let i = 0; i < str.length; i++) {
let ch = str[i];
let next = str[i + 1];
let full = kanaMap[ch];
if (full === undefined) {
result += ch;
continue;
}
if (next === '゙' && dakutenMap[full]) {
result += dakutenMap[full];
i++;
} else if (next === '゚' && handakutenMap[full]) {
result += handakutenMap[full];
i++;
} else {
result += full;
}
}
return result;
}
//----------------------------------------------------------------------
//新規作成時の最終確認メッセージ(重複チェックキーは登録完了後に編集不可になるための警告)
$p.buildNewRecordKeyConfirmMessage = function () {
let seiKana = $('#Results_Class003').val() || '(未入力)';
let meiKana = $('#Results_Class004').val() || '(未入力)';
let areaText = $('#Results_Class022 option:selected').text().trim() || '(未選択)';
return '新規作成します。\n\n以下の内容は、登録完了処理後は変更できません。内容に間違いがないか最終確認をお願いします。\n\n施主氏名(カナ):' + seiKana + ' ' + meiKana + '\nエリア:' + areaText + '\n\nよろしいですか?';
}
//----------------------------------------------------------------------
//施主名をマージする
$p.mergeCustomerName = function () {
//4つの項目を1つにマージする
let str1 = $('#Results_Class001').val(); //施主1・姓
let str2 = $('#Results_Class002').val(); //施主1・名
let str3 = $('#Results_Class003').val(); //施主1・姓(カナ)
let str4 = $('#Results_Class004').val(); //施主1・名(カナ)
//スペースで結合して、施主名(カナ)を作成し、保管
let mergeName = str1 + ' ' + str2 + '(' + str3 + ' ' + str4 + ')';
$p.set($('#Results_Class015'), mergeName); //施主名マージ
}
//----------------------------------------------------------------------
//ボタン名称変更
$p.changeButtonName = function () {
//ボタンの特定(②問い合わせ内容管理への新規作成ボタン)
let siteId = $p.siteId();
let button = $('button[data-from-site-id="' + siteId + '"][data-to-site-id="335411"].confirm-unload');
button.each(function () {
//+アイコンのspanはそのまま残し、末尾のテキストノードだけ書き換える
let textNode = $(this).contents().filter(function () {
return this.nodeType === 3; //テキストノードのみ対象
}).last();
if (textNode.length) {
textNode[0].nodeValue = '新規見積依頼';
}
});
}
//----------------------------------------------------------------------
//登録完了(Status=200)以降、マスターシート(ClassY)が選択済みの場合のみ選択変更を不可にする
//未選択のままなら登録完了後も選択可能(Pleasanter標準のStatusControls ColumnHashは値の有無で
//条件分岐できないため、こちらでクライアント側から制御する)。
//フィールドコンテナ(Results_ClassYField)へ既存の読取り専用CSSクラス(code-readonly2)を
//付け外しする方式(335410の01.読み取り専用スタイルで定義済み、pointer-events:none等を適用)。
//tabindexの制御は$p.applyReadonlyTabindexに一本化(クラス付与後に呼ばれる想定)。
$p.toggleClassYLock = function () {
let status = $('#Results_Status').val();
let classYVal = $('#Results_ClassY').val();
let statusLocked = ['200', '900', '910', '920'].includes(status);
let $classYField = $('#Results_ClassYField');
if (statusLocked && classYVal) {
$classYField.addClass('code-readonly2');
} else {
$classYField.removeClass('code-readonly2');
}
}
//----------------------------------------------------------------------
//code-readonly2(読取り専用)が設定されている全フィールドをTab移動対象から除外する。
//pointer-events:noneはマウス操作のみ無効化し、Tabキーでのフォーカス移動・キーボード操作での
//選択変更は防げないため、tabindex="-1"を併用する。静的にExtendedFieldCss: "code-readonly2"が
//設定されている項目(Status/ClassA/ClassB/ClassZ等)と、ClassYのように動的にクラスが
//付け外しされる項目の両方を、都度まとめて処理する(対象外に戻った要素はtabindexも解除)。
$p.applyReadonlyTabindex = function () {
$('select, input, textarea').each(function () {
let $el = $(this);
if ($el.closest('.code-readonly2').length) {
$el.attr('tabindex', '-1');
} else if ($el.attr('tabindex') === '-1') {
$el.removeAttr('tabindex');
}
});
}
//----------------------------------------------------------------------
//登録状況(Status)が「登録完了(200)」のときだけ依頼発行ボタンを表示、それ以外は非表示にする
$p.toggleNewRequestButton = function () {
let siteId = $p.siteId();
let button = $('button[data-from-site-id="' + siteId + '"][data-to-site-id="335411"].confirm-unload');
let status = $('#Results_Status').val();
if (status == '200') {
button.show();
} else {
button.hide();
}
}
$p.mergeUserList = function () {
//担当者一覧のマージ対象項目(重複除去)
let users1 = [];
[
$('#Results_Class032').val(), //主担当営業
$('#Results_Class033').val(), //副担当営業
$('#Results_Class034').val(), //営業設計
$('#Results_Class035').val(), //管理設計
$('#Results_Class036').val(), //担当IC
$('#Results_Class037').val(), //担当監督
$('#Results_Class038').val(), //所属長
].forEach(function (v) {
if (v && !users1.includes(v)) users1.push(v);
});
let mergeValue = JSON.stringify(users1); //担当者一覧のマージ対象項目をJSON文字列化
let class151 = JSON.stringify($('#Results_Class151').val()); //担当者一覧
//前と内容が同じ場合は上書きしない
if (mergeValue !== class151) {
$p.set($('#Results_Class151'), mergeValue); //担当者一覧
}
//CC一覧のマージ対象項目(重複除去)
let usersCC = [];
[
$('#Results_Class061').val(), //CC1
$('#Results_Class062').val(), //CC2
$('#Results_Class063').val(), //CC3
$('#Results_Class064').val(), //CC4
].forEach(function (v) {
if (v && !usersCC.includes(v)) usersCC.push(v);
});
let mergeValueCC = JSON.stringify(usersCC); //CC一覧のマージ対象項目をJSON文字列化
let class152 = JSON.stringify($('#Results_Class152').val()); //CC一覧
//前と内容が同じ場合は上書きしない
if (mergeValueCC !== class152) {
$p.set($('#Results_Class152'), mergeValueCC); //CC一覧
}
//担当者一覧・CC一覧のマージ(重複除去)
let usersAll = [];
users1.concat(usersCC).forEach(function (v) {
if (v && !usersAll.includes(v)) usersAll.push(v);
});
let mergeValueAll = JSON.stringify(usersAll); //担当者一覧・CC一覧のマージ対象項目をJSON文字列化
let class153 = JSON.stringify($('#Results_Class153').val()); //担当者・CC一覧
//前と内容が同じ場合は上書きしない
if (mergeValueAll !== class153) {
$p.set($('#Results_Class153'), mergeValueAll); //担当者・CC一覧
}
//登録者(依頼者)+担当者・CC一覧をマージしてClass154(依頼者・担当者・CC一覧)へ保存
$p.mergeClass154(usersAll);
}
//----------------------------------------------------------------------
//重複を除いてClass154(依頼者・担当者・CC一覧、複数選択・非表示)へ保存する
$p.mergeClass154 = function (usersAll) {
let classBVal = $('#Results_ClassB').val(); //登録者(依頼者)
let merged = [];
if (classBVal && !merged.includes(classBVal)) merged.push(classBVal);
usersAll.forEach(function (v) {
if (v && !merged.includes(v)) merged.push(v);
});
let mergeValue = JSON.stringify(merged);
let class154 = JSON.stringify($('#Results_Class154').val()); //依頼者・担当者・CC一覧
//前と内容が同じ場合は上書きしない
if (mergeValue !== class154) {
$p.set($('#Results_Class154'), mergeValue); //依頼者・担当者・CC一覧
}
}
//----------------------------------------------------------------------
//マスターシート・エリア・契約ブランド・契約営業所連携
//
//AutoPostBack(Links設定によるサーバー側自動転記)は処理負荷を避けるため全廃し、
//クライアントスクリプトで転記を行う。
// ・ClassY(マスターシート): 選択のたびに$p.apiGetでその場でレコード取得(対象レコードが多いため)
// ・Class022/023/031(エリア・契約ブランド・契約営業所): BeforeOpeningPageのServerScriptが
// 事前に全件取得しhiddenへ格納済みのデータをクライアント側で逆引きするだけ(apiGetは呼ばない)
//
//上書きルール(共通、重複チェックキーも含め全転記先に適用):
// ・未選択 → 選択:転記先に値があっても上書きしない(先に手入力していた値を尊重)
// ・選択済み → 別選択:転記先に値があっても上書きする(マスタを変えたら再転記)
//
//ClassY(マスターシート)自体は、登録完了(Status=200)以降かつ既に選択済みの場合のみ
//選択変更不可にする(未選択のままなら登録完了後も選択可能にする、$p.toggleClassYLock参照)
//ServerScriptが埋め込んだhiddenのマスタデータ(エリア/契約ブランド/契約営業所)
let areaMasterData = {};
let brandMasterData = {};
let officeMasterData = {};
$p.loadStaticMasters = function () {
areaMasterData = $('#areaMaster').val() ? JSON.parse($('#areaMaster').val()) : {};
brandMasterData = $('#brandMaster').val() ? JSON.parse($('#brandMaster').val()) : {};
officeMasterData = $('#officeMaster').val() ? JSON.parse($('#officeMaster').val()) : {};
}
//変更前値の記憶(未選択→選択/選択済み→別選択の判定に使う)
let previousLookupValues = {
ClassY: '',
Class022: '',
Class023: '',
Class031: ''
};
$p.rememberLookupBaseValues = function () {
previousLookupValues.ClassY = $('#Results_ClassY').val();
previousLookupValues.Class022 = $('#Results_Class022').val();
previousLookupValues.Class023 = $('#Results_Class023').val();
previousLookupValues.Class031 = $('#Results_Class031').val();
}
//APIレスポンス($p.apiGet結果)から、列名に応じたHash経由で値を取り出す
$p.getApiRecordValue = function (record, fieldName) {
if (fieldName === 'ResultId' || fieldName === 'Title' || fieldName === 'ItemTitle') {
return record[fieldName];
}
let hashKey =
fieldName.indexOf('Date') === 0 ? 'DateHash' :
fieldName.indexOf('Num') === 0 ? 'NumHash' :
fieldName.indexOf('Description') === 0 ? 'DescriptionHash' :
fieldName.indexOf('Check') === 0 ? 'CheckHash' :
fieldName.indexOf('Attachments') === 0 ? 'AttachmentsHash' :
'ClassHash';
return record[hashKey] ? record[hashKey][fieldName] : undefined;
}
//転記先1項目へ、上書きルールに従って値をセットする
//wasEmpty=true(未選択→選択): 転記先が空の場合のみセット
//wasEmpty=false(選択済み→別選択): 常にセット
//戻り値: 実際にセットした場合はtrue(呼び出し元で連鎖転記の要否判定に使う)
$p.applyLookupValue = function (toColumn, value, wasEmpty) {
if (value === undefined || value === null || value === '') return false;
let $target = $('#Results_' + toColumn);
if (!$target.length) return false;
if (wasEmpty && $target.val()) return false; //既存の手入力値を保持
$p.set($target, value);
return true;
}
//----------------------------------------------------------------------
//ClassY(マスターシート、SiteId:189112)選択時の転記
//重複チェックキー(Class003姓カナ/Class004名カナ/Class022エリア)も転記対象に含める。
//上書き可否はキー項目も含め全項目共通のwasEmptyルールに従う(applyLookupValue参照)。
$p.classYFieldMap = [
{ from: 'ClassA', to: 'ClassZ' },
{ from: 'Class001', to: 'Class001' },
{ from: 'Class002', to: 'Class002' },
{ from: 'Class003', to: 'Class003' },
{ from: 'Class004', to: 'Class004' },
{ from: 'Class009', to: 'Class005' },
{ from: 'Class010', to: 'Class006' },
{ from: 'Class011', to: 'Class007' },
{ from: 'Class012', to: 'Class008' },
{ from: 'Class017', to: 'Class009' },
{ from: 'Class018', to: 'Class010' },
{ from: 'Class019', to: 'Class011' },
{ from: 'Class020', to: 'Class012' },
{ from: 'Class029', to: 'Class021' },
{ from: 'ClassW', to: 'Class022' },
{ from: 'Class033', to: 'Class023' },
{ from: 'Class040', to: 'Class024' },
{ from: 'Class043', to: 'Class025' },
{ from: 'ClassF', to: 'Class031' },
{ from: 'ClassG', to: 'Class032' },
{ from: 'ClassH', to: 'Class033' },
{ from: 'ClassI', to: 'Class034' },
{ from: 'ClassL', to: 'Class035' },
{ from: 'ClassK', to: 'Class036' },
{ from: 'ClassJ', to: 'Class037' },
{ from: 'Date063', to: 'Date001' },
{ from: 'DateH', to: 'Date002' }
];
$p.on('change', 'ClassY', function () {
let newVal = $('#Results_ClassY').val();
let wasEmpty = !previousLookupValues.ClassY;
if (newVal) {
$p.apiGet({
'id': newVal,
'async': false,
'done': function (data) {
if (!data.Response || !data.Response.Data || !data.Response.Data[0]) {
console.log('マスターシートのレコード取得に失敗しました');
$p.ex.setMyMessage('new-status-red', 'データの取得に失敗しました。営業積算へお問い合わせください');
return;
}
//Class022/023/031(エリア・契約ブランド・契約営業所)がClassY経由で更新された場合、
//$p.setのtrigger('change')により各項目のchangeハンドラが連鎖発火し、
//ClassM/ClassN/Class038への転記も自動的に行われる想定
let record = data.Response.Data[0];
$p.classYFieldMap.forEach(function (m) {
let value = $p.getApiRecordValue(record, m.from);
$p.applyLookupValue(m.to, value, wasEmpty);
});
//案件状況(ClassS)を契約コード(ClassZ)の転記結果に応じて自動判定
$p.judgeClassS();
},
'fail': function (data) {
console.log('マスターシートのレコード取得に失敗しました', data);
$p.ex.setMyMessage('new-status-red', 'データの取得に失敗しました。営業積算へお問い合わせください');
}
});
} else {
//マスターシート選択解除 → 未契約
$p.judgeClassS();
}
previousLookupValues.ClassY = newVal;
});
//----------------------------------------------------------------------
//案件状況(ClassS)の自動判定
//マスターシート(ClassY)未選択の場合は未契約。選択済みの場合は、転記された契約コード(ClassZ)が
//"9999AAABB"(未契約扱いのダミーコード)なら未契約、それ以外なら契約済みと判定する。
//自動判定後もClassS自体は通常項目のまま(読取り専用化しない)なので、手入力での変更を妨げない。
$p.judgeClassS = function () {
let classYVal = $('#Results_ClassY').val();
let classZVal = $('#Results_ClassZ').val();
let judged = (!classYVal || classZVal === '9999AAABB') ? '0' : '1';
$p.set($('#Results_ClassS'), judged);
}
//----------------------------------------------------------------------
//Class022(エリア、SiteId:210699)→ ClassM(エリア記号)転記
//Class022自身は重複チェックキーのため、この項目自体はロック対象・必須のまま
$p.applyAreaMasterLookup = function (wasEmpty) {
let newVal = $('#Results_Class022').val();
if (newVal && areaMasterData[newVal] !== undefined) {
$p.applyLookupValue('ClassM', areaMasterData[newVal], wasEmpty);
}
}
$p.on('change', 'Class022', function () {
let wasEmpty = !previousLookupValues.Class022;
$p.applyAreaMasterLookup(wasEmpty);
previousLookupValues.Class022 = $('#Results_Class022').val();
});
//----------------------------------------------------------------------
//Class023(契約ブランド、SiteId:109100)選択時の転記 → ClassN(ブランド記号)
$p.on('change', 'Class023', function () {
let newVal = $('#Results_Class023').val();
let wasEmpty = !previousLookupValues.Class023;
if (newVal && brandMasterData[newVal] !== undefined) {
$p.applyLookupValue('ClassN', brandMasterData[newVal], wasEmpty);
}
previousLookupValues.Class023 = newVal;
});
//----------------------------------------------------------------------
//Class031(契約営業所、SiteId:96243)選択時の転記 → Class038(所属長)
$p.on('change', 'Class031', function () {
let newVal = $('#Results_Class031').val();
let wasEmpty = !previousLookupValues.Class031;
if (newVal && officeMasterData[newVal] !== undefined) {
$p.applyLookupValue('Class038', officeMasterData[newVal], wasEmpty);
}
previousLookupValues.Class031 = newVal;
});
(本ファイルClassYのchangeハンドラ内で「案件状況(ClassS)を契約コード(ClassZ)の転記結果に応じて自動判定」する$p.judgeClassS()呼び出しは、2026-08-03の改修(modify/2026-08-03T1520_案件状況自動判定/)で追加されたもの。判定はClassY変更時のみ発火するため、判定後の手動変更は次回ClassY操作まで保持される。)
scripts/2_02_プロセス処理スクリプト.js.js
/*
@config :
@filename : 02_プロセス処理スクリプト.js
@title : 02_プロセス処理スクリプト.js
@name :
@siteIds :
@siteTitles : プロセス実行前の処理
@disabled: false
*/
//プロセスボタンから呼び出す共通ディスパッチャ(雛形)
//Process定義側のOnClickは $p.ex.processScript($(this)); の形(ボタン要素そのものを渡す形)で呼び出す想定。
//procNum(数値文字列)ではなく、ボタンの表示ラベル文字列(procName)で処理を振り分ける方針(②サイトに倣う)。
//プロセス再設計時、switch内にプロセス名ごとのcaseを追加していく。
$p.ex.processScript = async function (target) {
let $btn = $(target);
if (!$btn.length) {
console.log('processScript: 対象ボタンが取得できませんでした');
return false;
}
let procName = $btn.text().replace(/^\s+/, '').trim();
console.log('processScript ' + procName);
//確認ダイアログを出さないプロセス名。ここにprocNameを追加すると、そのプロセスはconfirmをスキップして即実行する
let noConfirmProcesses = [];
switch (procName) {
case '登録完了':
//実行者:登録者(本人限定、Process側View.ColumnFilterHash: ClassB=Ownで担保)
//要入力チェックあり(Process側ValidateInputsで担保)
//確認プロンプトあり
let popMessage1 = '"' + procName + '" 処理を実行します。よろしいですか?';
if (!confirm(popMessage1)) return;
break;
default:
let popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (!noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
}
//プロセス実行
$p.execProcess($btn);
//プロセス側で設定した入力検証(ValidateInputs)が未入力エラーになった場合、
//Pleasanterが対象項目へ<label id="Results_XXX-error" class="error" for="Results_XXX">入力必須項目です。</label>を
//動的追加する。これを拾い、for属性から通常のフィールドラベル(class="error"でない方)を逆引きして
//まとめて下部にアラート表示する(必須項目名はスクリプト内にハードコードしない、②サイトと同じ方式)
setTimeout(function () {
let $errorLabels = $('label.error[id$="-error"]');
if ($errorLabels.length) {
let labelTexts = [];
$errorLabels.each(function () {
let forAttr = $(this).attr('for');
let $fieldLabel = $('label[for="' + forAttr + '"]').not('.error').first();
labelTexts.push($fieldLabel.length ? $fieldLabel.text().trim() : forAttr);
});
$p.ex.setMyMessage('new-status-red', labelTexts.join('、') + ' は必須項目です。入力してください。');
}
}, 300);
}
10.2 335410 ServerScripts(1ファイル)
| Id | Title | 実行タイミング | 概要 |
|---|---|---|---|
| 1 | マスターデータ取得 | (BeforeOpeningPage想定) | エリア(210699)・契約ブランド(109100)・契約営業所(96243)マスタを全件取得しHiddenへJSON格納。クライアント側の逆引き転記(10.1節)で使用 |
serverscripts/1_マスターデータ取得.js
try {
context.Log('マスターデータ取得(エリア・契約ブランド・契約営業所)開始');
getAreaMaster();
getBrandMaster();
getOfficeMaster();
} catch (error) {
context.Log(error);
}
//エリアマスタ取得(SiteId:210699)
//Class022(エリア)選択時、クライアント側でこのデータを逆引きしてClassM(エリア記号)へ転記する
function getAreaMaster() {
let masterTableId = '210699';
let results = items.Get(masterTableId);
if (results.Length === 0) {
context.Log('エリアマスタが見つかりませんでした');
return;
}
let areaMaster = {};
for (let item of results) {
areaMaster[item.ResultId] = item.ClassA;
}
hidden.Add('areaMaster', JSON.stringify(areaMaster));
context.Log('エリアマスタを取得しました');
}
//契約ブランドマスタ取得(SiteId:109100)
//Class023(契約ブランド)選択時、クライアント側でこのデータを逆引きしてClassN(ブランド記号)へ転記する
function getBrandMaster() {
let masterTableId = '109100';
let results = items.Get(masterTableId);
if (results.Length === 0) {
context.Log('契約ブランドマスタが見つかりませんでした');
return;
}
let brandMaster = {};
for (let item of results) {
brandMaster[item.ResultId] = item.ClassA;
}
hidden.Add('brandMaster', JSON.stringify(brandMaster));
context.Log('契約ブランドマスタを取得しました');
}
//契約営業所マスタ取得(SiteId:96243)
//Class031(契約営業所)選択時、クライアント側でこのデータを逆引きしてClass038(所属長)へ転記する
function getOfficeMaster() {
let masterTableId = '96243';
let results = items.Get(masterTableId);
if (results.Length === 0) {
context.Log('契約営業所マスタが見つかりませんでした');
return;
}
let officeMaster = {};
for (let item of results) {
officeMaster[item.ResultId] = item.ClassK;
}
hidden.Add('officeMaster', JSON.stringify(officeMaster));
context.Log('契約営業所マスタを取得しました');
}
10.3 335411 Scripts(4ファイル)
| Id | Title | 適用範囲 | 概要 |
|---|---|---|---|
| 1 | 01_基本スクリプト | New/Edit | 営業積算課/積算発注課判定によるタブ表示制御、①からのリンク経由チェック、祝日計算初期化、各種ボタン生成呼び出し、進捗表示切替、Status⇔Class062同期、依頼内容・ログ類の自動マージ処理、問合せ種別(Class007)による表示・必須・DescriptionCマージ切替、UserId→氏名解決(resolveUserName、2026-08-09追加。ServerScript「Dept25所属者取得スクリプト」がHidden格納したDept25Membersから動的読込、2026-08-09変更)、部署/ユーザー/ブランドのハードコードコードは全てファイル先頭のグローバル変数へ集約(2026-08-09) |
| 2 | 02_添付ファイルDL | New/Edit | 添付ファイル項目へのチェックボックス・一括/選択ダウンロードボタン追加、JSZip+axiosによるZIP一括DL |
| 3 | 03_質疑応答スクリプト | New/Edit | 質疑・回答メッセージの送受信・履歴表示・削除、各種操作ボタン(質疑送信/回答送信/営業積算確認/受付/外部見積待ちへ/社内確認中へ/一部回答/見積回答/問い合わせ不要/差戻し/遅延連絡)の動的生成、営業日計算。見積回答ファイル一括DLボタンはAttachments101が1件も無い場合は非表示(2026-08-09) |
| 4 | 04_プロセス処理スクリプト | New/Edit | 全19プロセスの共通ディスパッチャ(processScript)、プロセスごとの確認ダイアログ・入力チェック・付随処理の振り分け、問合せ種別必須チェック、日付・ユーザー設定(setTodayAndUser、2026-08-09追加) |
scripts/1_01_基本スクリプト.js.js
/*
@config :
@filename : ${psFileBasename}
@title : 01_基本スクリプト
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
let pageLoading = 0;
//組織によって質疑応答の操作可否を分岐するフラグ(DeptId 25 = 営業積算課)
let eSekisanFlag = false;
let eSekisanDeptId = '25';
//dept25(営業積算課)所属者のUserId→氏名解決マップ(ログ文字列生成・タブ間ミラー表示用のみ)。
//ServerScript「Dept25所属者取得スクリプト」が画面表示前に取得済みのDept25Members(Hidden項目)から読み込む。
//選択肢自体はChoicesText側でUsersマスタからDeptId=25指定で動的取得している
let dept25UserNames = $('#Dept25Members').length > 0 ? JSON.parse($('#Dept25Members').val()) : {};
//UserIdを氏名に解決する。マップに無い値(旧レコードの氏名文字列そのまま等)はそのまま返す
function resolveUserName(val) {
if (!val) return val;
return dept25UserNames[val] || val;
}
//積算発注タブ(_Tab-3/FieldSetTab3)表示対象の組織コード:25(営業積算課),26(積算発注管理課),28(東京工務課),66(君津住宅工務課),36(愛知工務課)
let sekisanHachuViewDeptIds = ['25', '26', '28', '66', '36'];
//積算発注タブ(_Tab-3/FieldSetTab3)表示対象の個別ユーザーコード(上記部署に属さなくても表示許可する例外ユーザー):
//913(青地渉),987(中川真悟),1153(長谷川等志)
let sekisanHachuViewUserIds = ['913', '987', '1153'];
//ブランドコード(Results_Class004の値):109101=nexthouse, 268122=nexthouse VALUE
//マトリックスの場合のnexthouseシリーズ(Class040)必須判定でのみ使用
let nexthouseBrandCodes = ['109101', '268122'];
//----------------------------------------------------------------------
//初期処理
window.addEventListener('load', function () {
if (pageLoading == 0) {
console.log('Window load Event');
$p.ex.onLoad();
}
});
$p.events.on_editor_load = function () {
if (pageLoading == 0) {
console.log('on Editor Load');
$p.ex.onLoad();
}
}
//----------------------------------------------------------------------
$p.ex.onLoad = function () {
//ロード開始
pageLoading = 1;
//初期処理
$p.ex.init();
}
//初期処理
$p.ex.init = function () {
console.log('initialize...');
//営業積算課(DeptId 25)かどうかを判定
eSekisanFlag = ($p.deptId() == eSekisanDeptId);
//「営業積算」タブ(_Tab-2/FieldSetTab2)は営業積算課(Dept25)のみ閲覧・編集可。
//既定は01_共通スタイル.cssで非表示、Dept25のみここでbodyへクラス付与し表示化する
if (eSekisanFlag) {
$('body').addClass('show-sekisan-tab');
}
//積算発注タブ(_Tab-3/FieldSetTab3)表示対象:dept25(営業積算),26(積算発注),28,66,36(各エリア工務)またはuserId 913,987,1153
//(sekisanHachuViewDeptIds/sekisanHachuViewUserIdsはファイル先頭のグローバル変数宣言を参照)
let deptIdStr = $p.deptId().toString();
let userIdStr = $p.userId().toString();
if (sekisanHachuViewDeptIds.includes(deptIdStr) || sekisanHachuViewUserIds.includes(userIdStr)) {
$('body').addClass('show-sekisan-hachu-tab');
}
//新規作成時のみ処理
if ($p.action() == 'new') {
//①物件基本情報からのリンク経由でない場合(ClassZ空欄)は警告表示(保存自体はbefore_validateでブロック)
if (!$('#Results_ClassZ').val()) {
$p.ex.setMyMessage('new-status-red', '①物件基本情報から新規依頼を作成してください');
}
//祝日計算
$p.getHolidayTable();
} else {
//スタートアップメッセージ表示
//$p.ex.startupMessage();
$p.setSubNumber();
//質疑応答送信ボタン生成
$p.ex.messageSaveButton();
//受付ボタン生成
$p.ex.receiptButton();
//外部見積待ちへボタン生成
$p.ex.externalEstimateWaitButton();
//社内確認中へ・問い合わせ不要・差戻しボタン生成
$p.ex.statusRowButtons();
//遅延連絡ボタン生成
$p.ex.delayNoticeButton();
//一部回答ボタン生成(見積回答ボタンより先に生成し、見積回答をその直下に配置する)
$p.ex.partialAnswerButton();
//見積回答ボタン生成
$p.ex.estimateReplyButton();
//見積回答ファイル(Attachments101)一括DLボタン生成(Description062の下に配置)
$p.ex.estimateAttachmentsDLButton();
//「①物件基本情報へ戻る」ボタンをコマンド欄の一番左に移動
$p.ex.moveBackButtonToFirst();
//質疑応答メッセージ履歴表示
$p.ex.messageHistory();
}
//添付ファイル項目欄調整
let attachColumns = $('.control-attachments');
$.each(attachColumns, function (index, value) {
let attach = value.dataset.name;
attachmentComments(attach);
attachFileCheckbox(attach);
})
//----------------------------------------------------------------------
//Results_ClassG(現在の進捗)変更時に項目の表示/非表示を切替
//ClassGは「新規依頼」プロセス実行後に読取専用化されるため、控え項目ClassT(現在の進捗(控え))へ常に同期しておき、
//表示切替の判定はClassTを参照する(読取専用化後は$('#Results_ClassG').val()が空になるため)
$('input[name="Results_ClassG"]').on('change', function () {
$p.toggleClassGFields($(this).val());
$p.set($('#Results_ClassT'), $(this).val());
});
$p.toggleClassGFields($('#Results_ClassT').val());
//----------------------------------------------------------------------
//Results_Class007(問合せ種別)変更時にマトリックス以外/マトリックスの入力欄表示を切替
$('input[name="Results_Class007"]').on('change', function () {
$p.ex.toggleInquiryTypeFields($(this).val());
});
$p.ex.toggleInquiryTypeFields($('#Results_Class007').val());
//----------------------------------------------------------------------
//Results_Class055(不備・追加)欄は項目削除に伴い不要化(2026-08-01コメントアウト)
/*
$('input[name="Results_Class055"]').on('change', function () {
$p.toggleClass055Fields($(this).val());
});
$p.toggleClass055Fields($('#Results_Class055').val());
*/
//アップ希望日チェックイベント登録
$p.on('change', 'DateA', function () {
$p.clearMessage();
$p.upDateCheck();
});
//----------------------------------------------------------------------
//Status(状況)とClass062(状況)の値を相互に同期
$('#Results_Status').on('change', function () {
$p.ex.syncStatusAndClass062('Status');
});
$('#Results_Class062').on('change', function () {
$p.ex.syncStatusAndClass062('Class062');
});
//code-readonly2(読取り専用)が設定されている全フィールドをTab移動対象から除外する
$p.applyReadonlyTabindex();
}
//----------------------------------------------------------------------
//code-readonly2(読取り専用)が設定されている全フィールドをTab移動対象から除外する。
//pointer-events:noneはマウス操作のみ無効化し、Tabキーでのフォーカス移動・キーボード操作での
//選択変更は防げないため、tabindex="-1"を併用する(①物件基本情報サイトと同じ方式)。
//StatusControls「依頼ロック」によるReadOnly化(ExtendedFieldCssとは別のPleasanter標準機構)は
//DOM上の表現が未確認のため対象外。ExtendedFieldCss: "code-readonly2"の静的設定項目のみ対応。
$p.applyReadonlyTabindex = function () {
$('select, input, textarea').each(function () {
let $el = $(this);
if ($el.closest('.code-readonly2').length) {
$el.attr('tabindex', '-1');
} else if ($el.attr('tabindex') === '-1') {
$el.removeAttr('tabindex');
}
});
}
//Status変更時はClass062へ、Class062変更時はStatusへ値をコピーする(無限ループ防止のため値が異なる場合のみ更新)
$p.ex.syncStatusAndClass062 = function (changedField) {
let statusVal = $('#Results_Status').val();
let class062Val = $('#Results_Class062').val();
if (changedField === 'Status') {
if (class062Val !== statusVal) {
$p.set($('#Results_Class062'), statusVal);
}
} else {
if (statusVal !== class062Val) {
$p.set($('#Results_Status'), class062Val);
}
}
}
$p.setSubNumber = function () {
// サブ番号の設定ロジック
let classB = $('#Results_ClassB').val();
let classX = $('#Results_ClassX').val();
let subNumber = classX.split('-')[1]; // 管理番号を「-」で分割して、子番を取得
if (subNumber && subNumber !== classB) {
// サブ番号を設定
$p.set($('#Results_ClassB'), subNumber);
}
};
//Results_ClassG(現在の進捗)変更時に項目の表示/非表示を切替
$p.toggleClassGFields = function (value) {
if (value == '1') {
//表示
$('#Results_ClassHField').show();
$('#Results_ClassIField').show();
$('#Results_DescriptionAField').show();
} else {
//非表示
$('#Results_ClassHField').hide();
$('#Results_ClassIField').hide();
$('#Results_DescriptionAField').hide();
}
}
//----------------------------------------------------------------------
//Results_Class007(問合せ種別)の値に応じて、マトリックス以外/マトリックスそれぞれの入力欄を表示/非表示
//「マトリックス以外」→Class031-034,Description031,Class045,Class050を表示
//「マトリックス」→Class038,Class039,Class040を表示
//未選択の場合は両方非表示
$p.ex.otherTypeFieldNames = ['Class031', 'Class032', 'Class033', 'Class034', 'Description031', 'Class045', 'Class050'];
$p.ex.matrixTypeFieldNames = ['Class038', 'Class039', 'Class040'];
$p.ex.toggleInquiryTypeFields = function (value) {
if (value == 'マトリックス以外') {
$p.ex.otherTypeFieldNames.forEach(function (c) { $('#Results_' + c + 'Field').show(); });
$p.ex.matrixTypeFieldNames.forEach(function (c) { $('#Results_' + c + 'Field').hide(); });
} else if (value == 'マトリックス') {
$p.ex.otherTypeFieldNames.forEach(function (c) { $('#Results_' + c + 'Field').hide(); });
$p.ex.matrixTypeFieldNames.forEach(function (c) { $('#Results_' + c + 'Field').show(); });
} else {
$p.ex.otherTypeFieldNames.concat($p.ex.matrixTypeFieldNames).forEach(function (c) { $('#Results_' + c + 'Field').hide(); });
}
}
//保存時、問合せ種別で選択されなかった側の項目に入力が残っていた場合はクリアする
$p.ex.clearInquiryTypeFields = function () {
let type = $('#Results_Class007').val();
let clearTargets;
if (type == 'マトリックス以外') {
clearTargets = $p.ex.matrixTypeFieldNames;
} else if (type == 'マトリックス') {
clearTargets = $p.ex.otherTypeFieldNames;
} else {
clearTargets = $p.ex.otherTypeFieldNames.concat($p.ex.matrixTypeFieldNames);
}
clearTargets.forEach(function (c) {
let $el = $('#Results_' + c);
if ($el.val()) $p.set($el, '');
});
}
//問合せ種別に応じた必須項目チェック(プロセス「積算へ依頼」実行前にのみ呼ばれる)
//マトリックスの場合のnexthouseシリーズ(Class040)は、ブランド(Class004)が
//109101(nexthouse)または268122(nexthouse VALUE)のときのみ必須とする
$p.ex.checkInquiryTypeRequiredFields = function () {
let type = $('#Results_Class007').val();
let emptyLabels = [];
if (!type) {
emptyLabels.push('問合せ種別');
} else if (type == 'マトリックス以外') {
if (!$('#Results_Class031').val()) emptyLabels.push('メーカー');
if (!$('#Results_Class032').val()) emptyLabels.push('商品名');
if (!$('#Results_Class033').val()) emptyLabels.push('品番');
if (!$('#Results_Class034').val()) emptyLabels.push('施工範囲・箇所');
} else if (type == 'マトリックス') {
if (!$('#Results_Class038').val()) emptyLabels.push('積算Ver.');
if (!$('#Results_Class039').val()) emptyLabels.push('マトリックスサイズ');
let class004 = $('#Results_Class004').val();
if (nexthouseBrandCodes.includes(String(class004)) && !$('#Results_Class040').val()) {
emptyLabels.push('nexthouseシリーズ');
}
}
if (emptyLabels.length > 0) {
window.alert(emptyLabels.join('、') + ' は必須項目です。入力してください。');
return false;
}
return true;
}
//Class040(nexthouseシリーズ)のラジオボタン選択を解除する
$p.ex.clearClass040 = function () {
$('input[name="Results_Class040"]').prop('checked', false);
$p.set($('#Results_Class040'), '');
}
//Results_Class055(不備・追加)欄は項目削除に伴い不要化(2026-08-01コメントアウト)
/*
$p.toggleClass055Fields = function (value) {
if (value == '1') {
//表示
$('#Results_Class056Field').show();
$('#Results_Attachments055Field').show();
$('#Results_Description055Field').show();
$('#Attachments055_dlButton1').show(); //添付ファイルのダウンロードボタンを表示
$('#Attachments055_dlButton2').show(); //添付ファイルのダウンロードボタンを表示
} else {
//非表示
$('#Results_Class056Field').hide();
$('#Results_Attachments055Field').hide();
$('#Results_Description055Field').hide();
$('#Attachments055_dlButton1').hide(); //添付ファイルのダウンロードボタンを非表示
$('#Attachments055_dlButton2').hide(); //添付ファイルのダウンロードボタンを非表示
}
}
*/
//入力チェック前処理
$p.events.before_validate = function (args) {
//ClassZ(①物件基本情報からのリンク元ID)が空欄の場合、新規作成をブロックする
if (!$p.ex.checkClassZRequired()) {
return false;
}
//問合せ種別で選択されなかった側の項目をクリア
$p.ex.clearInquiryTypeFields();
//依頼内容マージ
$p.mergeMainMessage();
//算出対応ログの保存
$p.saveCalculateLog();
//遅延連絡ログ
$p.saveReasonLog();
//見積回答・別途費用の統合と転記
$p.mergeEstimateResult();
//営業積算タブ→問い合わせ情報タブへの転記(同一レコード内)
$p.syncSekisanTabToGeneralTab();
//依頼者(ClassC)+担当者・CC一覧(Class153)をマージしてClassY(依頼者・担当者・CC一覧)へ保存
$p.mergeClassY();
//ClassG(現在の進捗)の必須チェックは「新規依頼」プロセス実行前処理(04_プロセス処理スクリプト.js)へ移設。
//新規依頼プロセス実行後はClassG/ClassH/ClassIが読取専用化され$('#Results_ClassH').val()等が取得できなくなるため、
//通常保存時(本関数)では行わない
//DescriptionC(依頼内容)はHide化しmergeMainMessageによる自動集約専用項目になったため、
//ユーザー入力前提の必須チェック(checkDescriptionCRequired)は廃止
}
//入力チェック後処理
$p.events.after_validate = function (args) {
}
//画面更新後
$p.events.after_set_Update = function (args) {
//初期処理
$p.ex.init();
}
//①物件基本情報から新規依頼作成後、遷移元(①)へ戻らず作成した②自身のレコードに留まる設定は
//ClassZ(①物件基本情報からのリンク元ID)のChoicesText側でNotReturnParentRecordオプションとして
//宣言的に設定する(Pleasanter標準機能。スクリプト側での対応は不要)
//参照: https://pleasanter.org/ja/manual/table-management-choices-text-link
//アップ希望日時の確認と警告
$p.upDateCheck = function () {
if ($('#Results_DateA').val() != '') {
let dateA = new Date($('#Results_DateA').val());
let dateJ = new Date($('#Results_DateJ').val());
//もしdateJが空の場合 $p.getHolidayTable() を再度実行して、dateJを取得する
if ($('#Results_DateJ').val() == '') {
$p.getHolidayTable();
dateJ = new Date($('#Results_DateJ').val());
}
//アップ希望日が期限日より手前の場合
if (dateA.getTime() < dateJ.getTime()) {
$('#Results_DateA').addClass('dateA_alert');
$p.ex.setMyMessage('new-status-red', '期日は選択可最短日以降を選択してください。');
return false;
} else {
$('#Results_DateA').removeClass('dateA_alert');
return true;
}
}
}
//祝日判定テーブルの取得
//ServerScript「祝日リスト取得スクリプト」が画面表示前に取得済みのHolidaysList(Hidden項目)から読み込む
$p.getHolidayTable = function () {
let holidays = $('#HolidaysList').length > 0 ? JSON.parse($('#HolidaysList').val()) : [];
let limitDate = new Date();
limitDate.setHours(0);
limitDate.setMinutes(0);
limitDate.setSeconds(0);
//土日祝日を除く5日後をカウント
let count = 0;
while (count < 5) {
limitDate.setDate(limitDate.getDate() + 1);
let limitDateStr = limitDate.getFullYear() + '/' + (limitDate.getMonth() + 1).toString().padStart(2, '0') + '/' + limitDate.getDate().toString().padStart(2, '0');
if (!(limitDate.getDay() == 0) && !(limitDate.getDay() == 6) && !(holidays.includes(limitDateStr))) {
count++;
}
}
let limitDateStr = limitDate.getFullYear() + '/' + (limitDate.getMonth() + 1).toString().padStart(2, '0') + '/' + limitDate.getDate().toString().padStart(2, '0');
$p.set($('#Results_DateJ'), limitDateStr);
}
//----------------------------------------------------------------------
//依頼内容マージ:OK6/21
//問合せ種別(Class007)により、DescriptionCへマージする項目セットを切り替える
//0:マトリックス以外→Class031(メーカー)、Class032(商品名)、Class033(品番)、Class034(施工範囲・箇所)、
// Description031(依頼内容補足)、Class045(標準仕様)、Class050(前回の管理番号)
//1:マトリックス→Class038(積算Ver.)、Class039(マトリックスサイズ)、Class040(nexthouseシリーズ)
$p.mergeMainMessage = function (args) {
//対象項目(種別自体の変更含む)の変更があった場合のみ処理する
let includeList = ['Results_Class007', 'Results_Class031', 'Results_Class032', 'Results_Class033', 'Results_Class034', 'Results_Description031', 'Results_Class045', 'Results_Class050', 'Results_Class038', 'Results_Class039', 'Results_Class040'];
let checkFlug = false;
$.each($p.data.MainForm, function (index, val) {
if (index.startsWith('Results')) {
if (includeList.includes(index)) {
// 処理内容
checkFlug = true;
}
}
});
if (checkFlug) {
let type = $('#Results_Class007').val();
let mergeStr = []; //マージする文字列を格納する配列
if (type == 'マトリックス') {
mergeStr.push($('#Results_Class038').val()); //Class038(積算Ver.)の値を配列に格納
mergeStr.push($('#Results_Class039').val()); //Class039(マトリックスサイズ)の値を配列に格納
mergeStr.push($('#Results_Class040').val()); //Class040(nexthouseシリーズ)の値を配列に格納
} else {
mergeStr.push($('#Results_Class031').val()); //Class031(メーカー)の値を配列に格納
mergeStr.push($('#Results_Class032').val()); //Class032(商品名)の値を配列に格納
mergeStr.push($('#Results_Class033').val()); //Class033(品番)の値を配列に格納
mergeStr.push($('#Results_Class034').val()); //Class034(施工範囲・箇所)の値を配列に格納
mergeStr.push($('#Results_Description031').val()); //Description031(依頼内容補足)の値を配列に格納
mergeStr.push($('#Results_Class045').val()); //Class045(標準仕様)の値を配列に格納
mergeStr.push($('#Results_Class050').val()); //Class050(前回の管理番号)の値を配列に格納
}
//空欄(未入力)の項目を除外する
mergeStr = mergeStr.filter(function (val) {
return val && val.trim() !== '';
});
//改行で結合して、DescriptionCに格納
$p.set($('#Results_DescriptionC'), mergeStr.join('\n'));
}
}
//----------------------------------------------------------------------
//遅延連絡ログ
//Class086遅延理由、Class087遅延連絡者
//Date084遅延連絡日、Date085返信予定日
//Description081遅延連絡内容
$p.saveReasonLog = function () {
//対象項目の変更があった場合のみログを保存する
let includeList = ['Results_Class086', 'Results_Class087', 'Results_Date084', 'Results_Date085', 'Results_Description081'];
let checkFlug = false;
$.each($p.data.MainForm, function (index, val) {
if (index.startsWith('Results')) {
if (includeList.includes(index)) {
// 処理内容
checkFlug = true;
}
}
});
if (checkFlug) {
let logStr = [];
let reasonText = $('#Results_Class086 option:selected').text(); //遅延理由の表示テキストを取得
logStr.push('遅延理由:' + reasonText); //Class086の表示文字列を配列に格納
logStr.push('遅延連絡者:' + resolveUserName($('#Results_Class087').val())); //Class087の値を氏名に解決して配列に格納
logStr.push('遅延連絡日:' + $('#Results_Date084').val()); //Date084の値を配列に格納
logStr.push('返信予定日:' + $('#Results_Date085').val()); //Date085の値を配列に格納
logStr.push('遅延連絡内容:' + '\n' + $('#Results_Description081').val()); //Description081の値を配列に格納
let orgLog = $('#Results_Description083').val(); //Description083の既存の値を取得
//新しいログを先頭(上)にして、既存のログがある場合はその下に結合。ない場合は新しいログのみ
let saveLog = orgLog ? logStr.join('\n') + '\n----------------------\n' + orgLog : logStr.join('\n');
$p.set($('#Results_Description083'), saveLog); //Description083に保存
}
}
//算出対応ログ
$p.saveCalculateLog = function () {
//対象項目の変更があった場合のみログを保存する
let includeList = ['Results_Class101', 'Results_Class102', 'Results_Date103', 'Results_Class103', 'Results_Description091', 'Results_Description092'];
let checkFlug = false;
$.each($p.data.MainForm, function (index, val) {
if (index.startsWith('Results')) {
if (includeList.includes(index)) {
// 処理内容
checkFlug = true;
}
}
});
if (checkFlug) {
//Class101算出者、Class102チェック者、Date103返信日、Class103返信者
//Description091見積回答、Description092別途費用・伝達事項"
let logStr = [];
logStr.push('算出者:' + resolveUserName($('#Results_Class101').val())); //Class101の値を氏名に解決して配列に格納
logStr.push('チェック者:' + resolveUserName($('#Results_Class102').val())); //Class102の値を氏名に解決して配列に格納
logStr.push('返信日:' + $('#Results_Date103').val()); //Date103の値を配列に格納
logStr.push('返信者:' + resolveUserName($('#Results_Class103').val())); //Class103の値を氏名に解決して配列に格納
//ラベルと内容の間に改行を入れることで、ラベルが独立した行になり、内容は次の行から表示される
logStr.push('見積回答:' + '\n' + $('#Results_Description091').val()); //Description091の値を配列に格納
logStr.push('別途費用・伝達事項:' + '\n' + $('#Results_Description092').val()); //Description092の値を配列に格納
let orgLog = $('#Results_Description101').val(); //Description101の既存の値を取得
//既存のログがある場合はそれを先頭にして新しいログを結合、ない場合は新しいログのみを結合
let saveLog = orgLog ? orgLog + '\n----------------------\n' + logStr.join('\n') : logStr.join('\n');
$p.set($('#Results_Description101'), saveLog); //Description101に保存
}
}
//見積回答(Description091)・別途費用・伝達事項(Description092)をDescription093へ統合し、
//Description093の内容をDescription061(見積結果)へ転記する
$p.mergeEstimateResult = function () {
//対象項目の変更があった場合のみ処理する
let includeList = ['Results_Description091', 'Results_Description092'];
let checkFlug = false;
$.each($p.data.MainForm, function (index, val) {
if (index.startsWith('Results')) {
if (includeList.includes(index)) {
checkFlug = true;
}
}
});
if (checkFlug) {
//091・092を改行で結合してDescription093へ格納(内容に変更が無ければ上書きしない)
let mergeStr = $('#Results_Description091').val() + '\n' + $('#Results_Description092').val();
if ($('#Results_Description093').val() !== mergeStr) {
$p.set($('#Results_Description093'), mergeStr);
}
//Description093をDescription061へ転記(内容に変更が無ければ上書きしない)
if ($('#Results_Description061').val() !== mergeStr) {
$p.set($('#Results_Description061'), mergeStr);
}
}
}
//----------------------------------------------------------------------
//ClassZ(①物件基本情報からのリンク元ID)が新規作成時に空欄の場合、新規作成をブロックする
//(①物件基本情報のリンクを経由せず直接このサイトで新規作成しようとした異常系を検知)
$p.ex.checkClassZRequired = function () {
if ($p.action() == 'new' && !$('#Results_ClassZ').val()) {
$p.ex.setMyMessage('new-status-red', '①物件基本情報から新規依頼を作成してください');
return false;
}
return true;
}
//----------------------------------------------------------------------
//営業積算タブ(_Tab-2)→問い合わせ情報タブ(General)への転記(同一レコード内)
//受付日Date081→Date061、受付者Class081→Class061
//見積依頼日Date082→Date062、見積依頼先Class083→Class063
//見積受信日Date090→Date063、見積種別Class091→Class065
//転記先の値に差異がある場合のみ転記し、差異が無い場合はスキップする
$p.syncSekisanTabToGeneralTab = function () {
let mapping = [
{ from: 'Class081', to: 'Class061', resolveUser: true }, //受付者(Usersマスタ参照のため氏名に解決して転記)
{ from: 'Class083', to: 'Class063' }, //見積依頼先
{ from: 'Date081', to: 'Date061' }, //受付日
{ from: 'Date082', to: 'Date062' }, //見積依頼日
{ from: 'Date090', to: 'Date063' }, //見積受信日
{ from: 'Class091', to: 'Class065' }, //見積種別
];
mapping.forEach(function (item) {
let fromVal = $('#Results_' + item.from).val();
if (item.resolveUser) fromVal = resolveUserName(fromVal);
let toVal = $('#Results_' + item.to).val();
//値に差異がある場合のみ転記する
if (fromVal !== toVal) {
$p.set($('#Results_' + item.to), fromVal);
}
});
}
//----------------------------------------------------------------------
//依頼者(ClassC:単一選択)と担当者・CC一覧(Class153:複数選択)をマージし、
//重複を除いてClassY(依頼者・担当者・CC一覧、複数選択・非表示)へ保存する
$p.mergeClassY = function () {
let classCVal = $('#Results_ClassC').val();
let class153Vals = $('#Results_Class153').val() || [];
let merged = [];
if (classCVal) merged.push(classCVal);
class153Vals.forEach(function (v) {
if (v && !merged.includes(v)) merged.push(v);
});
$p.set($('#Results_ClassY'), JSON.stringify(merged));
}
//----------------------------------------------------------------------
//返信予定日 Date085 変更イベントを検知して実行
$p.on('change', 'Date085', function () {
if ($('#Results_Date085').val() != '') {
console.log('返信予定日が変更されました。');
$p.replaceDate085();
}
});
//Description082には「直前に本文へ置換した返信予定日の値」をキーとして保持する
//(レコード保存・再読込後も同じ値が残るため、次回変更時に古い値だけを新しい値へ置換できる)
$p.replaceDate085 = function () {
if ($('#Results_Date085').val() != '') {
console.log('返信予定日が変更されました。');
//返信予定日(今回の新しい値)を取得
let date085 = $('#Results_Date085').val();
//遅延理由メッセージを取得
let content081 = $('#Results_Description081').val();
//前回置換に使った返信予定日(キー)をDescription082から取得
let content082 = $('#Results_Description082').val();
let newContent;
if (content082 == '') {
//初回:プレースホルダー(%返信予定日%)を返信予定日に置換
newContent = content081.replace(/%返信予定日%/g, date085);
} else {
//2回目以降:前回の返信予定日(キー)を今回の返信予定日に置換
newContent = content081.split(content082).join(date085);
}
//遅延理由を再設定する
$p.set($('#Results_Description081'), newContent);
//今回の返信予定日を次回のキーとしてDescription082に保存
$p.set($('#Results_Description082'), date085);
}
}
//----------------------------------------------------------------------
//日付文字列を「年/月/日(曜日)」形式に整形する(例:"2026/06/25" → "2026/6/25(木)")
function formatDateWithWeekday(dateStr) {
if (!dateStr) return '';
let datePart = dateStr.split(' ')[0]; //時刻が付いている場合は日付部分のみ取り出す
let parts = datePart.split('/');
if (parts.length < 3) return datePart; //想定外の形式の場合はそのまま返す
let year = parseInt(parts[0], 10);
let month = parseInt(parts[1], 10);
let day = parseInt(parts[2], 10);
let dateObj = new Date(year, month - 1, day);
let weekdays = ['日', '月', '火', '水', '木', '金', '土'];
let weekday = weekdays[dateObj.getDay()];
return year + '/' + month + '/' + day + '(' + weekday + ')'; //例:2026/6/25(木)
}
scripts/2_02_添付ファイルDL.js.js
/*
@config :
@filename : 02_添付ファイルDL.js
@title : 02_添付ファイルDL
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
/*
init内部
let attachColumns = $('.control-attachments');
$.each(attachColumns, function (index, value) {
let attach = value.dataset.name;
attachmentComments(attach);
attachFileCheckbox(attach);
})
*/
//----------------------------------------------------------------------
//添付ファイル項目にボタンの追加
function attachFileCheckbox(attach) {
//管理番号 Results_ClassX
let title = $('#Results_ClassX').val();
let colName = $('#Results_' + attach + 'Field label')[0].innerHTML;
let attachData = JSON.parse($p.getControl(attach).val());
let zipFilename = title + '■' + colName + '■';
//console.log(attachData.length);
if (attachData.length >= 1) {
$.each(attachData, function (index, value) {
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.style.margin = '2px';
checkbox.style.float = 'left';
checkbox.name = attach + '_checkbox';
checkbox.id = value.Guid + '_checkbox';
checkbox.value = value.Guid;
$('#' + value.Guid).prepend(checkbox);
});
//ダウンロードボタン
let div = document.createElement('div');
let dlButton1 = document.createElement('button');
dlButton1.type = 'button';
dlButton1.id = attach + '_dlButton1';
dlButton1.style.marginRight = '5px';
dlButton1.innerHTML = '全ファイルDL';
dlButton1.setAttribute('attach', attach);
dlButton1.setAttribute('onclick', 'dlAllFiles("' + attach + '","' + zipFilename + '全ファイルDL' + '")');
let dlButton2 = document.createElement('button');
dlButton2.type = 'button';
dlButton2.id = attach + '_dlButton2';
dlButton2.style.marginRight = '5px';
dlButton2.innerHTML = '選択DL';
dlButton2.setAttribute('attach', attach);
dlButton2.setAttribute('onclick', 'dlSelectedFiles("' + attach + '","' + zipFilename + '選択DL' + '")');
div.style.margin = '5px';
div.style.marginTop = '0px';
div.style.marginLeft = '120px';
div.style.marginRight = '5px';
div.style.marginBottom = '10px';
div.style.clear = 'both';
div.append(dlButton1);
div.append(dlButton2);
$('#Results_' + attach + 'Field').after(div);
$('#' + attach + '_dlButton1').button({
icon: 'ui-icon-document'
});
$('#' + attach + '_dlButton2').button({
icon: 'ui-icon-document'
});
}
}
function dlSelectedFiles(attach, zipFilename) {
//console.log(zipFilename);
//チェックがついているもののみ
const fileList = $('input[name="' + attach + '_checkbox"]:checked');
let guidList = new Array();
for (item of fileList) {
//console.log(item);
guidList.push(item.value);
}
if (guidList.length > 0) {
dlZipAttachements(guidList, zipFilename);
}
}
function dlAllFiles(attach, zipFilename) {
let guidList = new Array();
//全部
const fileList = JSON.parse($p.getControl(attach).val());
for (item of fileList) {
guidList.push(item.Guid);
}
if (guidList.length > 0) {
dlZipAttachements(guidList, zipFilename);
}
}
/**
* 複数ファイルを1ファイルにzip圧縮してDLする
* @param guidList DLする添付ファイルのGUIDのリスト
* @param zipFilename zipファイル名
* @return なし
*/
async function dlZipAttachements(guidList, zipFilename) {
var binary;
var filename;
const zip = new JSZip();
const jsonReq = {
ApiVersion: 1.1
};
for (guid of guidList) {
await axios.post("/pleasanter/api/binaries/" + guid + "/get", jsonReq)
.then(function (response) {
binary = decodeBase64(response.data.Response.Base64);
filename = response.data.Response.FileName;
zip.folder(zipFilename).file(filename, binary, {
binary: true
});
})
.catch(function (error) {
console.log(error);
})
.finally(function () { });
}
saveAs(await zip.generateAsync({
type: "blob"
}), zipFilename + ".zip");
}
/**
* base64デコード処理
* @param base64 base64文字列
* @return デコードしたバイナリデータ
*/
function decodeBase64(base64) {
const bin = atob(base64.replace(/^.*,/, ''));
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
return buffer.buffer;
}
//説明欄にコメントがある場合、追加
function attachmentComments(attach) {
let comments = '';
if ($('#Results_' + attach + 'Field>div.field-control').attr('title')) {
comments = $('#Results_' + attach + 'Field>div.field-control').attr('title');
}
if (comments) {
let div1 = document.createElement('div');
div1.innerHTML = comments;
div1.style.marginLeft = '121px';
div1.style.fontWeight = 'bold';
div1.style.color = '#00F';
//位置調整とコメント追加
$('#Results_' + attach + 'Field').append(div1);
$('#Results_' + attach + 'Field').css('margin-bottom', '10px');
}
}
scripts/3_03_質疑応答スクリプト.js.js
/*
@config :
@filename : 03_質疑応答スクリプト.js
@title : 03_質疑応答スクリプト
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
//----------------------------------------------------------------------
//質疑応答機能:configs\参考ソース\■3_310405_Re③依頼項目\sites\scripts\03_質疑応答スクリプト.js を
//本サイト(335411)の既存予約項目(Description075/076/071、Num071/072、Class071/075/077、Date073/074、
//Attachments071/072)に合わせて移植・拡張したもの。
//
//項目対応(参照元 → 本サイト)
// Description131(質疑入力欄) → Description075「質疑内容」
// Description132(回答入力欄) → Description076「回答内容」
// Description135(ログ保存用) → Description071「質疑回答データ保存用」
// Num131/132(質疑回数/回答回数) → Num071/Num072
// Class131(質疑回答ステータス0-3) → Class071(選択肢の値・色分けとも参照元と一致)
// Attachments131/132 → Attachments071/072
//
//本サイトにのみ存在し参照元スクリプトにはなかった項目は、下記の想定で組み込んでいます(要確認)。
// Class075「質疑回答送信者」 :質疑または回答を送信した本人の氏名を、送信のたびに上書き記録
// Class077「質疑送付先」 :質疑送信時に選択した宛先ユーザーを、ログの表示にのみ利用(通知メール送信は未実装)
// Date073「質疑回答期日」 :質疑送信時に、$p.getHolidayTable()と同じ祝日データ(HolidaysList)を使って
// 土日祝を除く5営業日後を自動計算して設定
// Date074「営業積算確認日」 :営業積算課が回答内容を確認した際(営業積算確認プロセス実行時)に本日日付を設定
//
//回答送信ボタンの表示自体はProcess「回答送信」側のView条件(ClassC:["Own"])でサーバー側に強制されている
//(ClassC=依頼者本人のみ実行可)。processScript側にも念のため同じ判定を残している
//(processScript本体は04_プロセス処理スクリプト.jsへ統合済み。プロセスボタン共通ディスパッチャは
// 1ファイルに1つしか定義できないため、19プロセス全件を扱う04側へ集約し、本ファイルには
// saveQuestionMessage等の実処理関数のみを残す構成にしている)。
//----------------------------------------------------------------------
//営業積算課からの質疑を保存
$p.ex.saveQuestionMessage = function () {
let message = $('#Results_Description075').val();
let userName = $p.userName();
let now = new Date();
let nowStr = now.getFullYear() + '/' + (now.getMonth() + 1).toString().padStart(2, '0') + '/' + now.getDate().toString().padStart(2, '0') + ' ' +
now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
let messageID = 'a' + now.getFullYear() + '' + (now.getMonth() + 1).toString().padStart(2, '0') + '' + now.getDate().toString().padStart(2, '0') + '' +
now.getHours().toString().padStart(2, '0') + '' + now.getMinutes().toString().padStart(2, '0') + '' + now.getSeconds().toString().padStart(2, '0') + '-' + $p.userId();
let num = parseFloat($('#Results_Num071').val() || 0) + 1;
//質疑送付先(Class077:複数ユーザー選択、要確認:表示形式によってはセレクタの調整が必要)
let sendToText = $('#Results_Class077').val() || '';
let data = {
'messageID': messageID,
'type': 'Question',
'num': toCircledNumber(num),
'typeStr': '営業積算課からの質疑',
'userName': userName,
'sendTo': sendToText,
'messageTime': nowStr,
'messageBody': message,
'enabled': true,
};
//質疑回答 保管用項目に保存する
let saveData = $('#Results_Description071').val();
let shitsugiData;
if (saveData == '') {
shitsugiData = [];
shitsugiData.push(data);
} else {
shitsugiData = JSON.parse(saveData);
shitsugiData.push(data);
}
$p.set($('#Results_Description071'), JSON.stringify(shitsugiData));
//送信者を記録
$p.set($('#Results_Class075'), userName);
//質疑回数をカウントアップ
$p.set($('#Results_Num071'), num);
//質疑回答ステータスを「質疑あり」に更新
$p.set($('#Results_Class071'), '1');
//質疑回答期日(土日祝を除く5営業日後)を設定
$p.set($('#Results_Date073'), $p.ex.calcBusinessDeadline(5));
//質疑ありチェックをオンにする
$p.set($('#Results_Check071'), 'checked');
//送信したら入力欄は削除する
$p.set($('#Results_Description075'), '');
}
//依頼者からの回答を保存
$p.ex.saveAnswerMessage = function () {
let message = $('#Results_Description076').val();
let userName = $p.userName();
let now = new Date();
let nowStr = now.getFullYear() + '/' + (now.getMonth() + 1).toString().padStart(2, '0') + '/' + now.getDate().toString().padStart(2, '0') + ' ' +
now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
let messageID = 'b' + now.getFullYear() + '' + (now.getMonth() + 1).toString().padStart(2, '0') + '' + now.getDate().toString().padStart(2, '0') + '' +
now.getHours().toString().padStart(2, '0') + '' + now.getMinutes().toString().padStart(2, '0') + '' + now.getSeconds().toString().padStart(2, '0') + '-' + $p.userId();
let num = parseFloat($('#Results_Num072').val() || 0) + 1;
let data = {
'messageID': messageID,
'type': 'Answer',
'num': toCircledNumber(num),
'typeStr': '依頼者からの回答',
'userName': userName,
'messageTime': nowStr,
'messageBody': message,
'enabled': true,
};
//質疑回答 保管用項目に保存する
let saveData = $('#Results_Description071').val();
let shitsugiData;
if (saveData == '') {
shitsugiData = [];
shitsugiData.push(data);
} else {
shitsugiData = JSON.parse(saveData);
shitsugiData.push(data);
}
$p.set($('#Results_Description071'), JSON.stringify(shitsugiData));
//送信者を記録
$p.set($('#Results_Class075'), userName);
//回答回数をカウントアップ
$p.set($('#Results_Num072'), num);
//質疑回答ステータスを「回答あり」に更新
$p.set($('#Results_Class071'), '2');
//回答ありチェックをオンにする
$p.set($('#Results_Check072'), 'checked');
//送信したら入力欄は削除する
$p.set($('#Results_Description076'), '');
}
//営業積算課が回答内容を確認
$p.ex.confirmAnswerMessage = function () {
let now = new Date();
let nowStr = now.getFullYear() + '/' + (now.getMonth() + 1).toString().padStart(2, '0') + '/' + now.getDate().toString().padStart(2, '0');
//営業積算確認日を設定
$p.set($('#Results_Date074'), nowStr);
//質疑回答ステータスを「営業積算確認済み」に更新
$p.set($('#Results_Class071'), '3');
}
//質疑回答メッセージ履歴の表示
$p.ex.messageHistory = function () {
//質疑回数と回答回数の参照
let num071 = parseFloat($('#Results_Num071').val());
let num072 = parseFloat($('#Results_Num072').val());
if (num071 > 0 || num072 > 0) {
//タブの色の変更(要確認:本サイトでは質疑応答セクションは_Tab-1=FieldSetTab1に配置)
let shitsugi_Status = $('#Results_Class071').val();
switch (shitsugi_Status) {
case '': //質疑なし
case '0': //質疑なし
$('a[href="#FieldSetTab1"] label').text('質疑なし');
$('a[href="#FieldSetTab1"]').css('background-color', 'blue');
$('a[href="#FieldSetTab1"]').css('color', 'white');
break;
case '1': //質疑あり
$('a[href="#FieldSetTab1"] label').text('質疑あり');
$('a[href="#FieldSetTab1"]').css('background-color', 'red');
$('a[href="#FieldSetTab1"]').css('color', 'white');
break;
case '2': //回答あり
$('a[href="#FieldSetTab1"] label').text('回答あり');
$('a[href="#FieldSetTab1"]').css('background-color', 'yellow');
$('a[href="#FieldSetTab1"]').css('color', 'black');
break;
case '3': //営業積算確認済み
$('a[href="#FieldSetTab1"] label').text('営業積算確認済み');
$('a[href="#FieldSetTab1"]').css('background-color', 'green');
$('a[href="#FieldSetTab1"]').css('color', 'white');
break;
}
//メッセージテーブルがすでに存在したら削除
if ($('#messageTable').length) {
$('#messageTable').remove();
}
//削除ボタン削除
if ($('#messageDelete').length) {
$('#messageDelete').remove();
}
let saveData = $('#Results_Description071').val();
if (saveData) {
//メッセージデータの取得
let messageData = JSON.parse(saveData);
let templateHtml =
`<table id="messageTable" class="radius-table">
<tbody>
{{#messageData}}
<tr class="{{type}}">
<td class="shitsugi_type">{{typeStr}} {{num}}</td>
<td class="shitsugi_userName">{{userName}}</td>
<td class="shitsugi_messageTime">{{messageTime}}</td>
</tr>
<tr class="{{type}}">
<td colspan="2" class="shitsugi_messageBody">
<pre class="shitsugi_preWrap">{{{messageBody}}}</pre>
</td>
<td class="shitsugi_deleteCheck"><input type="checkbox" name="messageDeleteCheck" value="{{messageID}}"></input></td>
</tr>
{{/messageData}}
</tbody>
</table>`;
let template = Hogan.compile(templateHtml);
let outputHtml = template.render({
"messageData": messageData
});
//質疑内容入力欄の直上にメッセージ履歴テーブルを表示する
//(本サイトにはSectionFields番号を直接指定できる根拠がないため、既知の項目=Description075Fieldを起点に挿入)
setTimeout(function () {
$('#SectionFields8').append(outputHtml);
}, 300);
}
//対応するプロセスボタン「質疑回答削除」が実在する場合のみ削除ボタンを表示(Depts・View条件にそのまま追従)
if ($p.ex.processButtonExists('質疑回答削除')) {
let button3 = document.createElement('button');
button3.type = 'button';
button3.id = 'messageDelete';
button3.innerHTML = '質疑回答削除';
button3.setAttribute('onclick', "$p.ex.clickProcessByName('質疑回答削除')");
button3.style.margin = '5px';
button3.style.float = 'right';
button3.style.fontSize = '1.3em';
setTimeout(function () {
$('#Results_Description075Field').before(button3);
$('#messageDelete').button({
icon: 'ui-icon-document'
});
}, 300);
}
}
}
//質疑・回答送信ボタン作成
$p.ex.messageSaveButton = function () {
//対応するプロセスボタン「質疑送信」が実在する場合のみ表示(Depts・View条件による表示制御にそのまま追従)
if ($p.ex.processButtonExists('質疑送信') && !$('#saveQuestion').length) {
let div1 = document.createElement('div');
div1.style.textAlign = 'right';
let button1 = document.createElement('button');
button1.type = 'button';
button1.id = 'saveQuestion';
button1.innerHTML = '質疑送信';
button1.setAttribute('onclick', "$p.ex.clickProcessByName('質疑送信')");
button1.style.margin = '5px';
button1.style.fontSize = '1.3em';
div1.append(button1);
$('#Results_Attachments071Field').after(div1);
$('#saveQuestion').button({
icon: 'ui-icon-document'
});
} else {
//営業積算課以外は質疑入力欄・添付欄を非表示
$('#Results_Description075Field').hide();
$('#Results_Attachments071Field').hide();
}
//対応するプロセスボタン「積算対応中へ」が実在する場合のみ表示(Depts・View条件による表示制御にそのまま追従)
//(ボタンのラベル「営業積算確認」は維持。実行先プロセスは新プロセス仕様で「積算対応中へ」(Id:6)に統合されたため、
// clickProcessByNameの対象名のみ変更。confirmAnswerMessage()は04_プロセス処理スクリプト.jsのcase'積算対応中へ'内で呼ぶ)
if ($p.ex.processButtonExists('積算対応中へ') && !$('#confirmAnswer').length) {
let div4 = document.createElement('div');
div4.style.textAlign = 'right';
let button4 = document.createElement('button');
button4.type = 'button';
button4.id = 'confirmAnswer';
button4.innerHTML = '営業積算確認';
button4.setAttribute('onclick', "$p.ex.clickProcessByName('積算対応中へ')");
button4.style.margin = '5px';
button4.style.fontSize = '1.3em';
div4.append(button4);
$('#Results_Attachments072Field').after(div4);
$('#confirmAnswer').button({
icon: 'ui-icon-document'
});
}
//対応するプロセスボタン「回答送信」が実在する場合のみ表示
//(Process「回答送信」側のView条件(ClassC:["Own"])による表示制御にそのまま追従。userId直接比較は行わない)
if ($p.ex.processButtonExists('回答送信') && !$('#saveAnswer').length) {
let div2 = document.createElement('div');
div2.style.textAlign = 'right';
let button2 = document.createElement('button');
button2.type = 'button';
button2.id = 'saveAnswer';
button2.innerHTML = '回答送信';
button2.setAttribute('onclick', "$p.ex.clickProcessByName('回答送信')");
button2.style.margin = '5px';
button2.style.fontSize = '1.3em';
div2.append(button2);
$('#Results_Attachments072Field').after(div2);
$('#saveAnswer').button({
icon: 'ui-icon-document'
});
} else {
//依頼者本人以外は回答入力欄・添付欄を非表示
$('#Results_Description076Field').hide();
$('#Results_Attachments072Field').hide();
}
}
//遅延連絡ボタン作成(対応するプロセスボタン「遅延連絡」が実在する場合のみ表示。Depts・View条件にそのまま追従)
$p.ex.delayNoticeButton = function () {
if ($p.ex.processButtonExists('遅延連絡') && !$('#sendDelayNotice').length) {
let div5 = document.createElement('div');
div5.style.textAlign = 'right';
div5.style.float = 'right';
let button5 = document.createElement('button');
button5.type = 'button';
button5.id = 'sendDelayNotice';
button5.innerHTML = '遅延連絡';
button5.setAttribute('onclick', "$p.ex.clickProcessByName('遅延連絡')");
button5.style.margin = '0px 10px 5px 5px';
button5.style.fontSize = '1.3em';
div5.append(button5);
$('#Results_Description081Field').after(div5);
$('#sendDelayNotice').button({
icon: 'ui-icon-mail-closed'
});
}
}
//受付ボタン作成(受付者(Class081)欄の右横に配置。対応するプロセスボタン「受付」が実在する場合のみ表示)
$p.ex.receiptButton = function () {
if ($p.ex.processButtonExists('受付') && !$('#sendReceipt').length) {
let div = document.createElement('div');
div.style.textAlign = 'right';
div.style.float = 'right';
let button = document.createElement('button');
button.type = 'button';
button.id = 'sendReceipt';
button.innerHTML = '受付';
button.setAttribute('onclick', "$p.ex.clickProcessByName('受付')");
button.style.margin = '0px 10px 5px 5px';
button.style.fontSize = '1.3em';
div.append(button);
$('#Results_Class081Field').after(div);
$('#sendReceipt').button({
icon: 'ui-icon-mail-closed'
});
}
}
//外部見積待ちへボタン作成(見積依頼者(Class082)欄の右横に配置)
$p.ex.externalEstimateWaitButton = function () {
if ($p.ex.processButtonExists('外部見積待ちへ') && !$('#sendExternalEstimateWait').length) {
let div = document.createElement('div');
div.style.textAlign = 'right';
div.style.float = 'right';
let button = document.createElement('button');
button.type = 'button';
button.id = 'sendExternalEstimateWait';
button.innerHTML = '外部見積待ちへ';
button.setAttribute('onclick', "$p.ex.clickProcessByName('外部見積待ちへ')");
button.style.margin = '0px 10px 5px 5px';
button.style.fontSize = '1.3em';
div.append(button);
$('#Results_Class082Field').after(div);
$('#sendExternalEstimateWait').button({
icon: 'ui-icon-mail-closed'
});
}
}
//社内確認中へ・問い合わせ不要・差戻しボタン作成(状況(Status)欄の右横に一列で配置)
$p.ex.statusRowButtons = function () {
if ($('#sendInternalConfirmRow').length) {
return; //二重生成防止
}
let defs = [
{ id: 'sendInternalConfirm', label: '社内確認中へ', process: '社内確認中へ' },
{ id: 'sendInquiryUnnecessary', label: '問い合わせ不要', process: '問い合わせ不要' },
{ id: 'sendReturnRequest', label: '差戻し', process: '差戻し' },
];
let visibleDefs = defs.filter(function (d) { return $p.ex.processButtonExists(d.process); });
if (visibleDefs.length === 0) {
return;
}
let div = document.createElement('div');
div.id = 'sendInternalConfirmRow';
div.style.textAlign = 'right';
div.style.float = 'right';
visibleDefs.forEach(function (d) {
let button = document.createElement('button');
button.type = 'button';
button.id = d.id;
button.innerHTML = d.label;
button.setAttribute('onclick', "$p.ex.clickProcessByName('" + d.process + "')");
button.style.margin = '0px 10px 5px 5px';
button.style.fontSize = '1.3em';
div.append(button);
});
$('#Results_StatusField').after(div);
visibleDefs.forEach(function (d) {
$('#' + d.id).button({
icon: 'ui-icon-mail-closed'
});
});
}
//一部回答・見積回答ボタン作成(返信者(Class103)欄の右横に、一部回答を上段・見積回答を下段で縦に配置)
//いずれも対応するプロセスボタンが実在する場合のみ表示。Depts・View条件にそのまま追従。
//活性/非活性のクライアント側制御は行わず、実行可否もProcess側のView条件(Status)にのみ委ねる
$p.ex.partialAnswerButton = function () {
if ($p.ex.processButtonExists('一部回答') && !$('#sendPartialAnswer').length) {
let div = document.createElement('div');
div.style.textAlign = 'right';
div.style.float = 'right';
let button = document.createElement('button');
button.type = 'button';
button.id = 'sendPartialAnswer';
button.innerHTML = '一部回答';
button.setAttribute('onclick', "$p.ex.clickProcessByName('一部回答')");
button.style.margin = '0px 10px 5px 5px';
button.style.fontSize = '1.3em';
div.append(button);
$('#Results_Class103Field').after(div);
$('#sendPartialAnswer').button({
icon: 'ui-icon-mail-closed'
});
}
}
$p.ex.estimateReplyButton = function () {
if ($p.ex.processButtonExists('見積回答') && !$('#sendEstimateReply').length) {
let div6 = document.createElement('div');
div6.style.textAlign = 'right';
div6.style.float = 'right';
let button6 = document.createElement('button');
button6.type = 'button';
button6.id = 'sendEstimateReply';
button6.innerHTML = '見積回答';
button6.setAttribute('onclick', "$p.ex.clickProcessByName('見積回答')");
button6.style.margin = '0px 10px 5px 5px';
button6.style.fontSize = '1.3em';
div6.append(button6);
//一部回答ボタン(#sendPartialAnswer)が生成済みならその直後、無ければClass103Fieldの直後に配置し、
//一部回答の真下(画面上は右寄せの縦並びで一部回答が上段)になるようにする
let $anchor = $('#sendPartialAnswer').closest('div');
if ($anchor.length) {
$anchor.after(div6);
} else {
$('#Results_Class103Field').after(div6);
}
$('#sendEstimateReply').button({
icon: 'ui-icon-mail-closed'
});
}
}
//見積回答ボタン押下時、営業積算タブ添付ファイル(Attachments101)を問い合わせタブ(Description062)へ転記する
//単一項目からの転記のため「■■■項目名■■■」の見出しは付けない
$p.ex.transcribeEstimateAttachments = function () {
let attachData = [];
try {
attachData = JSON.parse($p.getControl('Attachments101').val());
} catch (e) {
attachData = [];
}
let lines = attachData.map(function (file) {
return `▷ [${file.Name}](/pleasanter/binaries/${file.Guid}/show) [▶DL](/pleasanter/binaries/${file.Guid}/download)`;
});
$p.set($('#Results_Description062'), lines.join(' \n'));
}
//Description062の下に、見積回答ファイル(Attachments101)一括DLボタンを配置する
//DLファイル名は「親番子番_見積回答ファイル」。Attachments101が1件も無い場合は生成しない
$p.ex.estimateAttachmentsDLButton = function () {
if ($('#estimateAttachmentsDL').length) {
return; //二重生成防止
}
let attachData = [];
try {
attachData = JSON.parse($p.getControl('Attachments101').val());
} catch (e) {
attachData = [];
}
if (!attachData.length) {
return; //添付ファイルが1件も無い場合は表示しない
}
let classA = $('#Results_ClassA').val(); //親番
let classB = $('#Results_ClassB').val(); //子番
let zipFilename = classA + classB + '_見積回答ファイル';
let div = document.createElement('div');
div.style.margin = '5px';
div.style.float = 'right';
let button = document.createElement('button');
button.type = 'button';
button.id = 'estimateAttachmentsDL';
button.innerHTML = '一括DL';
button.setAttribute('onclick', 'dlAllFiles("Attachments101","' + zipFilename + '")');
div.append(button);
$('#Results_Description062Field').after(div);
$('#estimateAttachmentsDL').button({
icon: 'ui-icon-disk'
});
}
//「①物件基本情報へ戻る」ボタンをコマンド欄(#MainCommands)の一番左に移動する
//(Processes側のId構成に依存しないよう、ボタンラベル文字列で対象を特定する)
$p.ex.moveBackButtonToFirst = function () {
let $backButton = $('button[id^="Process_"]').filter(function () {
return $(this).text().replace(/^\s+/, '').trim() === '①物件基本情報へ戻る';
});
if ($backButton.length) {
$('#MainCommands').prepend($backButton);
}
}
//質疑回答メッセージの削除(チェックの入った履歴を無効化)
$p.ex.messageDelete = function () {
let messageData = JSON.parse($('#Results_Description071').val());
$('input[name="messageDeleteCheck"]:checked').each(function (index) {
let messageID = $(this).val();
messageData = messageData.map(item => {
if (item.messageID === messageID) {
return {
...item,
enabled: false,
type: item.type + '.Disabled'
};
}
return item;
});
});
$p.set($('#Results_Description071'), JSON.stringify(messageData));
$p.ex.setMyMessage('new-status-green', '選択した質疑回答メッセージを削除しました');
}
//ボタンラベル文字列に一致する、現在表示されているプロセスボタンをクリックする
//(Processes側のId構成・件数に依存しないようにするための共通ヘルパー)
$p.ex.clickProcessByName = function (name) {
let target = $('button[id^="Process_"]').filter(function () {
return $(this).text().replace(/^\s+/, '').trim() === name;
}).first();
if (target.length) {
target.click();
} else {
$p.ex.setMyMessage('new-status-red', '現在の状態では「' + name + '」を実行できません');
}
}
//ボタンラベル文字列に一致する、現在表示されているプロセスボタンが実在するかどうかを判定する
//(Depts・View条件によるプロセスボタンの表示制御にそのまま追従させるため。deptコード等の直接判定はしない)
$p.ex.processButtonExists = function (name) {
return $('button[id^="Process_"]').filter(function () {
return $(this).text().replace(/^\s+/, '').trim() === name;
}).length > 0;
}
//processScript(プロセスボタン共通ディスパッチャ)は04_プロセス処理スクリプト.jsへ統合済み。
//本ファイルには実処理関数(saveQuestionMessage等)のみ残す。
//質疑回答期日を計算(本日からholidays(#HolidaysList)を除いたN営業日後)
$p.ex.calcBusinessDeadline = function (businessDays) {
let holidays = $('#HolidaysList').length > 0 ? JSON.parse($('#HolidaysList').val()) : [];
let limitDate = new Date();
limitDate.setHours(0);
limitDate.setMinutes(0);
limitDate.setSeconds(0);
let count = 0;
while (count < businessDays) {
limitDate.setDate(limitDate.getDate() + 1);
let limitDateStr = limitDate.getFullYear() + '/' + (limitDate.getMonth() + 1).toString().padStart(2, '0') + '/' + limitDate.getDate().toString().padStart(2, '0');
if (!(limitDate.getDay() == 0) && !(limitDate.getDay() == 6) && !(holidays.includes(limitDateStr))) {
count++;
}
}
return limitDate.getFullYear() + '/' + (limitDate.getMonth() + 1).toString().padStart(2, '0') + '/' + limitDate.getDate().toString().padStart(2, '0');
}
// numを丸数字に変換して表示
function toCircledNumber(n) {
n = Math.floor(Number(n));
if (isNaN(n) || n < 0 || n > 100) return n;
if (n === 0) return '⓪';
if (n <= 20) return String.fromCharCode(9311 + n); // 9312 = ①
if (n <= 35) return String.fromCharCode(12881 + n - 21); // 12881 = ㉑
if (n <= 50) return String.fromCharCode(12977 + n - 36); // 12977 = ㊱
if (n <= 100) return n; // fallback for 51-100
}
scripts/4_04_プロセス処理スクリプト.js.js
/*
@config :
@filename : 04_プロセス処理スクリプト.js
@title : 04_プロセス処理スクリプト.js
@name :
@siteIds :
@siteTitles : プロセス実行前の処理
@disabled: false
*/
//プロセスボタンから呼び出す共通ディスパッチャ
//Process定義側のOnClickは $p.ex.processScript($(this)); の形(ボタン要素そのものを渡す形)。
//procNum(数値文字列)ではなく、ボタンの表示ラベル文字列(procName)で処理を振り分ける
//(Processes側のId構成や件数が今後変わっても影響を受けないため。
// 3_03_質疑応答スクリプト.jsに元々あったprocessScript本体をこちらへ統合済み)
$p.ex.processScript = async function (target) {
let $btn = $(target);
if (!$btn.length) {
console.log('processScript: 対象ボタンが取得できませんでした');
return false;
}
let procName = $btn.text().replace(/^\s+/, '').trim();
console.log('processScript ' + procName);
let popMessage = '';
//確認ダイアログを出さないプロセス名。ここにprocNameを追加すると、そのプロセスはconfirmをスキップして即実行する
let noConfirmProcesses = [];
// プロセス名に応じた処理(popMessageが未定義(空文字)、またはnoConfirmProcessesに含まれる場合は確認メッセージを出さず実行)
switch (procName) {
case '積算へ依頼':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//ClassG(現在の進捗)が「1:承認図後・着工中」の場合の必須項目チェック(プロセス実行後はClassG/H/Iが読取専用化されるため、実行前のここでのみチェック可能)
if (!$p.ex.checkClassGRequiredFields()) return;
//問合せ種別(マトリックス以外/マトリックス)に応じた必須項目チェック
if (!$p.ex.checkInquiryTypeRequiredFields()) return;
break;
case '受付':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//受付日・受付者をここで設定(Process側の「データの変更」だと反映がexecProcess後(サーバー側)になり、
//先に動く01_基本スクリプト.jsのbefore_validate/ログ転記処理が値取得できず欠落するため、ここで直接設定する)
$p.ex.setTodayAndUser('Date081', 'Class081');
break;
case '質疑送信':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
if ($('#Results_Description075').val()) {
$p.ex.saveQuestionMessage();
} else {
$p.ex.setMyMessage('new-status-red', '質疑内容を入力してください');
return false;
}
break;
case '回答送信': //念のためのボタン非表示以外のサーバー側判定はProcess側View条件(ClassC:["Own"])が担う
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
if ($p.userId().toString() != $('#Results_ClassC').val()) {
$p.ex.setMyMessage('new-status-red', '回答送信は依頼者本人のみ行えます');
return false;
}
if ($('#Results_Description076').val()) {
$p.ex.saveAnswerMessage();
} else {
$p.ex.setMyMessage('new-status-red', '回答内容を入力してください');
return false;
}
break;
case '質疑回答削除':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
if ($('input[name="messageDeleteCheck"]:checked').length) {
$p.ex.messageDelete();
} else {
$p.ex.setMyMessage('new-status-red', '削除したい質疑回答メッセージを選択してください');
return false;
}
break;
case '積算対応中へ':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//旧「営業積算確認」プロセスを吸収。営業積算確認日(Date074)・質疑回答ステータス(Class071='3')を更新
$p.ex.confirmAnswerMessage();
break;
case '外部見積待ちへ':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//見積依頼日・見積依頼者をここで設定(理由は「受付」と同じ)
$p.ex.setTodayAndUser('Date082', 'Class082');
break;
case '社内確認中へ':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '一部回答':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//返信日・返信者をここで設定(理由は「受付」と同じ)
$p.ex.setTodayAndUser('Date103', 'Class103');
break;
case '見積回答':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
//実行可否はProcess側View条件(Status)のみで担保しているため、追加の入力チェックは行わない
//返信日・返信者をここで設定(理由は「受付」と同じ)
$p.ex.setTodayAndUser('Date103', 'Class103');
//営業積算タブ添付ファイル(Attachments101)を問い合わせタブ(Description062)へ転記
$p.ex.transcribeEstimateAttachments();
break;
case '完了':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '問い合わせ不要':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '依頼修正':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '差戻し':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '依頼取消':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '保留':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '保留解除':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
break;
case '遅延連絡':
popMessage = '"' + procName + '" 処理を実行します。よろしいですか?';
if (popMessage && !noConfirmProcesses.includes(procName) && !confirm(popMessage)) return;
if (!$('#Results_Description081').val()) {
$p.ex.setMyMessage('new-status-red', '遅延連絡内容を入力してください');
return false;
}
//遅延連絡日・遅延連絡者をここで設定(理由は「受付」と同じ。saveReasonLogが直後に読み取るため必須)
$p.ex.setTodayAndUser('Date084', 'Class087');
break;
case '①物件基本情報へ戻る':
//Status変更を伴わないため execProcess を経由せず直接画面遷移する
let parentId = $('#Results_ClassZ').val();
//自分のURL+parentIdへ遷移 自分のURLの例:https://neo999.next-hd.net/pleasanter/items/335482
let currentUrl = window.location.href;
let baseUrl = currentUrl.split('/items/')[0];
let targetUrl = baseUrl + '/items/' + parentId;
window.location.href = targetUrl;
break;
default:
console.log('processScript: 未対応のプロセス名です: ' + procName);
return false;
}
// プロセス実行
$p.execProcess($btn);
//プロセス側で設定した入力検証(ValidateInputs)が未入力エラーになった場合、
//Pleasanterが対象項目へ<label id="Results_XXX-error" class="error" for="Results_XXX">入力必須項目です。</label>を
//動的追加する。これを拾い、for属性から通常のフィールドラベル(class="error"でない方)を逆引きして
//まとめて下部にアラート表示する(必須項目名はスクリプト内にハードコードしない)
setTimeout(function () {
let $errorLabels = $('label.error[id$="-error"]');
if ($errorLabels.length) {
let labelTexts = [];
$errorLabels.each(function () {
let forAttr = $(this).attr('for');
let $fieldLabel = $('label[for="' + forAttr + '"]').not('.error').first();
labelTexts.push($fieldLabel.length ? $fieldLabel.text().trim() : forAttr);
});
$p.ex.setMyMessage('new-status-red', labelTexts.join('、') + ' は必須項目です。入力してください。');
}
}, 300);
}
//----------------------------------------------------------------------
//日付項目(dateColumn)に本日日付、ユーザー項目(userColumn)にログインユーザーを設定する。
//以前はProcess側の「データの変更」機能で設定していたが、その反映(サーバー側)はexecProcess後になり、
//先に動くbefore_validate側のログ転記処理(saveReasonLog/saveCalculateLog等)が値を取得できず欠落する
//不具合があったため、execProcess呼び出し前にクライアント側で直接設定するここへ移設した
$p.ex.setTodayAndUser = function (dateColumn, userColumn) {
let now = new Date();
let dateStr = now.getFullYear() + '/' + (now.getMonth() + 1).toString().padStart(2, '0') + '/' + now.getDate().toString().padStart(2, '0');
$p.set($('#Results_' + dateColumn), dateStr);
$p.set($('#Results_' + userColumn), $p.userId());
}
//----------------------------------------------------------------------
//ClassG(現在の進捗)が「1:承認図後・着工中」の場合、ClassH(現場の進捗)・ClassI(必要な見積金額)・
//DescriptionA(問い合わせ経緯)が未入力ならアラート表示(プロセス実行自体をブロックする)
//「新規依頼」プロセス実行前のみ呼ばれる(新規依頼以降はStatusControlsによりClassG/H/Iが読取専用化されるため、01_基本スクリプト.jsのbefore_validateからここへ移設)
$p.ex.checkClassGRequiredFields = function () {
if ($('#Results_ClassG').val() == '1') {
let emptyLabels = [];
if (!$('#Results_ClassH').val()) emptyLabels.push('現場の進捗');
if (!$('#Results_ClassI').val()) emptyLabels.push('必要な見積金額');
if (!$('#Results_DescriptionA').val()) emptyLabels.push('問い合わせ経緯');
if (emptyLabels.length > 0) {
window.alert(emptyLabels.join('、') + ' は必須項目です。入力してください。');
return false;
}
}
return true;
}
10.4 335411 ServerScripts(3ファイル)
| Id | Title | 実行タイミング | 概要 |
|---|---|---|---|
| 0 | 子番自動保存スクリプト | 新規作成直後(サーバー側) | ClassX(自動採番)確定後に子番ClassBを算出しDB保存。クライアント側$p.setSubNumberと同一ロジックをサーバー側にも実装(編集画面を開かないとClassBが保存されない不具合対策) |
| 1 | 祝日リスト取得スクリプト | (BeforeOpeningPage想定) | 祝日テーブル(SiteId 192267)から本日-30日〜+60日分を取得しHidden格納。あわせて独自ボタンで代替済みの標準プロセスボタン6件を非表示化 |
| 2 | Dept25所属者取得スクリプト | BeforeOpeningPage | depts.Get('25').GetMembers()でDept25(営業積算課)所属者のUserId→氏名マップを取得しHidden項目Dept25Membersへ格納。クライアント側resolveUserNameが読み込み参照(従来の固定ハードコードマップを置換、2026-08-09新規追加) |
serverscripts/0_子番自動保存スクリプト.js.js
/*
@config :
@filename : 子番自動保存スクリプト.js
@title : 子番自動保存スクリプト
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
try {
setSubNumber();
} catch (error) {
context.Log(error);
}
//新規作成直後(自動採番=ClassX確定後)にClassXから子番(ClassB)を算出しDBへ保存する。
//クライアント側 $p.setSubNumber(scripts/1_01_基本スクリプト.js.js)と同じロジック・同じガードをサーバー側にも実装し、
//「編集画面を開かない限りClassBがDBへ保存されない」不具合に対応する。
function setSubNumber() {
let classX = model.ClassX;
if (!classX) {
context.Log('[子番自動保存] ClassXが空のためスキップしました。ResultId=' + model.ResultId);
return;
}
let subNumber = classX.split('-')[1]; //管理番号を「-」で分割して子番を取得
if (subNumber && subNumber !== model.ClassB) {
model.ClassB = subNumber;
model.UpdateOnExit = true; //スクリプト終了時に現在のレコード情報を自動保存
context.Log('[子番自動保存] ClassBを保存しました。ResultId=' + model.ResultId + ' ClassB=' + subNumber);
}
}
serverscripts/1_祝日リスト取得スクリプト.js.js
/*
@config :
@filename : 祝日リスト取得スクリプト.js
@title : 祝日リスト取得スクリプト
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
try {
getHolidayList();
//サイト内に個別設置した独自ボタン(質疑送信・回答送信・質疑回答削除・営業積算確認・受付・外部見積待ちへ・
//社内確認中へ・一部回答・見積回答・問い合わせ不要・差戻し・遅延連絡、いずれも3_03_質疑応答スクリプト.jsの
//clickProcessByNameで対応プロセスを呼び出す)で代替しているプロセスボタンは非表示にする。
//それ以外のプロセスボタンはPleasanter標準の表示制御(Depts・View条件)に委ね、ここでは操作しない
elements.DisplayType('Process_2', 3); //受付
elements.DisplayType('Process_3', 3); //質疑送信
elements.DisplayType('Process_4', 3); //回答送信
elements.DisplayType('Process_5', 3); //質疑回答削除
elements.DisplayType('Process_6', 3); //積算対応中へ(営業積算確認ボタンで代替)
elements.DisplayType('Process_7', 3); //外部見積待ちへ
elements.DisplayType('Process_8', 3); //社内確認中へ
elements.DisplayType('Process_9', 3); //一部回答
elements.DisplayType('Process_10', 3); //見積回答
elements.DisplayType('Process_12', 3); //問い合わせ不要
elements.DisplayType('Process_14', 3); //差戻し
elements.DisplayType('Process_18', 3); //遅延連絡
} catch (error) {
context.Log(error);
}
//祝日判定テーブルの取得(本日時点から手前30日~先60日分をHiddenに格納)
function getHolidayList() {
let today = new Date();
let startDate = new Date();
let endDate = new Date();
//30日前の日付を計算
startDate.setDate(today.getDate() - 30);
//60日後の日付を計算
endDate.setDate(today.getDate() + 60);
let startDateStr = formatDate(startDate) + ' 00:00:00';
let endDateStr = formatDate(endDate) + ' 23:59:59';
let filterDate = '["' + startDateStr + ',' + endDateStr + '"]';
let data = {
'View': {
'ColumnFilterHash': {
'DateA': filterDate,
},
'ColumnSorterHash': {
'DateA': 'asc',
}
}
};
//祝日テーブル(SiteId: 192267)からデータを取得
let results = items.Get(192267, JSON.stringify(data));
context.Log(`取得した祝日の件数: ${results.Length}`);
let holidays = [];
for (let item of results) {
holidays.push(formatDate(new Date(item.DateA)));
}
//取得した祝日日付リストをHiddenに格納
hidden.Add('HolidaysList', JSON.stringify(holidays));
}
//日付をYYYY/MM/DD形式に整形
function formatDate(date) {
return date.getFullYear() + '/' +
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
date.getDate().toString().padStart(2, '0');
}
serverscripts/2_Dept25所属者取得スクリプト.js.js
/*
@config :
@filename : Dept25所属者取得スクリプト.js
@title : Dept25所属者取得スクリプト
@name :
@siteIds :
@siteTitles : 問合せ内容管理
@disabled: false
*/
try {
getDept25Members();
} catch (error) {
context.Log(error);
}
//Dept25(営業積算課)所属者のUserId→氏名マップを取得しHiddenへ格納する。
//クライアント側resolveUserName()がここから読み込み、表示専用の氏名解決に使う
function getDept25Members() {
let myDept = depts.Get('25');
let map = {};
if (myDept) {
let members = myDept.GetMembers();
for (let m of members) {
map[m.UserId] = m.Name;
}
}
hidden.Add('Dept25Members', JSON.stringify(map));
}
dept.GetMembers()はPleasanter公式マニュアル(サーバースクリプトでの部署メンバー取得)に基づく実装。実機テストで動作確認済み(2026-08-09、Dept25所属3名=647/737/912のUserId・Nameを正しく取得。members.Lengthは大文字プロパティが存在せずundefinedだが、for...ofでの反復自体は正常動作)。
11. その他設定
| 設定項目 | 335410 | 335411 |
|---|---|---|
| AutoVerUpType | 3 | 2 |
| AllowCopy | false(コピー機能無効) | 未設定(既定=有効) |
| HideLink | true | true |
| NoDisplayIfReadOnly | false | false |
| NotInheritPermissionsWhenCreatingSite | false | false |
| TabLatestId | 1(追加タブなし) | 4 |
| SectionLatestId | 9 | 18 |
| Comments(コメント機能) | ラベル「変更履歴」として使用 | ラベル未設定(EditorColumnHashにも非掲載=実質未使用) |
| Htmls(外部ライブラリ読込) | 設定なし | あり(下記) |
335411 Htmls(外部ライブラリ、PositionType:9000, New/Edit両方に適用):
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.18.1/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
→ 10.3節 scripts/2_02_添付ファイルDL.js.jsのZIP一括ダウンロード機能(JSZip/axios/saveAs)が依存する外部CDNライブラリ。335410にはこの読込設定がなく、335410側スクリプトに同種のZIP DL機能はない。
12. 気になる点(レビュー観点)
- 335411 Description093 が Columns 未定義:
10.3節のスクリプト(mergeEstimateResult)は$('#Results_Description093')へ値をセットしているが、SiteSettings.ColumnsにもEditorColumnHashにもDescription093は存在しない。編集画面上に該当DOM要素が存在しないため、このセット処理は実質的に空振り(no-op)になっている可能性が高い。既存のDescription061(見積結果)への転記は別行で行われているため実害はなさそうだが、デッドコード/設計意図と実装の乖離として要確認。 - 335411 Class064(採用状況)が編集画面のどのタブにも配置されていない:Columns定義(Hide:true、選択肢:採用/不採用/打合せ中)はあるがEditorColumnHash全タブを走査しても出現しない。スクリプトからの参照も無し。未使用列の疑い。
- 335411 Date071(質疑回答送信日時)が事実上未使用:
_Tab-1にHide項目として配置されてはいるが、スクリプト内で値をセットしている箇所が見当たらない(Date073質疑回答期日、Date074回答確認日は使われているが、Date071自体への代入処理なし)。 - 335411 Date091(使用期限)がEditorColumnHashに存在しない:ExtendedHtmlAfterControl付きのColumns定義はあるが、どのタブにも配置されておらず画面上には出現しない。類似ラベル・同一文言のExtendedHtmlAfterControlを持つ
Date064(Hide:true)との重複・リネーム痕跡の可能性がある(どちらか一方が旧項目の残骸と推測)。 - 335411 ClassZ のラベルが実態と乖離:ラベルは「テスト・リンク用」だが、実際には①335410との連携キー(15項目Lookup、
AutoPostBack)として本番稼働中の中核列。命名がテスト目的のまま放置されている。 - 335411 Tab-3「積算発注」はReadColumnAccessControls未設定:Create/UpdateColumnAccessControlsはDept25/26限定だが、Read(閲覧)側の部署制限はAPIレベルでは設定されていない。UI上はCSS(
show-sekisan-hachu-tabクラス)でタブごと非表示にしているのみのため、API直接呼び出し等でCSSによる制御を経由しない経路からは対象部署外でも閲覧できる可能性がある(335410のClass199同様、Read側も明示的に絞る設計との一貫性という観点で要検討)。 - PermissionType値の意味は環境依存:5.1節のPermissionForCreating/Updating(値4)、2章の各Permissions(511/7/3等)は、いずれもビットフラグの加算方式のため本仕様書単体では正確な権限範囲を断定できない。環境のロール定義と突き合わせが必要。
- 335411 Class050「前回の管理番号」の入力方式:説明文は「該当の番号を選択してください」と選択式を示唆する文言だが、ChoicesTextが設定されておらず実際は自由入力のテキスト項目。ガイド文と実装のUI種別が一致していない。
- 335411 Description061に
ExtendedFieldCssとExtendedControlCssが両方code-readonly2設定:他のreadonly項目はExtendedFieldCssのみが一般的で、本列のみ二重指定になっている(実害は無いと見られるが実装の非対称箇所として記録)。 - 335410↔335411間で同名だが用途の異なる列名が複数存在:例えば
ClassYは335410で「マスターシート」(Lookup選択列)、335411では「依頼者・担当者・CC一覧」(Hide集約列)と全く異なる用途。Class153は両サイトとも「担当者・CC一覧」の同一用途で連携されているが、ClassZ(335410=契約コード/335411=①へのリンクキー)、ClassS(335410のみ存在)等、列名の対応関係は個別に本仕様書(4章・7章)で都度確認する必要がある。横断的な命名規則は存在しない。 - 335411 StatusControls「依頼ロック」ColumnHashにClassF(削除済み列)が残存:2026-08-09時点でClassF(事前相談者)はColumns/EditorColumnHashから既に削除されているが、StatusControls「依頼ロック」(Id:1)のColumnHashには
"ClassF":"ReadOnly"が残ったまま(今回の改修で新たに追加したClass007と合わせて、削除漏れとして残置を確認)。存在しない列を指定しているだけで実害はないと見られるが、整理対象として記録。 - 335411 ClassZのNotReturnParentRecordはJSON形式Lookupへの類推適用:公式マニュアル(table-management-choices-text-link)はシンプル記法
[[100,NotReturnParentRecord]]のみ明記しており、ClassZのようなLookups付きJSON形式ChoicesTextへ"NotReturnParentRecord": trueを追加する形式(Priority設定と同じ配置パターンから類推)が同じ効果を持つかは未確認。実機で①からの新規依頼作成→②の新規レコードに留まるかの確認が必要(2026-08-09追加)。