/** * restructure-site-488755.js * ------------------------------------------------------------ * 488755「土地のみ/建売」再編。Columns/EditorColumnHash/Sections/Scripts/Processesをまとめて改修し、 * modify/フォルダへ site-488755_updated.json として書き出す(APIへは一切送信しない、ファイル操作のみ)。 * 方針: EditorColumnHash配列・既存Columns定義の削除/並び替えは一切行わない。追加のみ。 * ClassPは一切変更しない(4_3.テーブルリンクと情報取得.jsがResults_ClassPをWFスイッチとして参照するため)。 * * 参照: configs/production/site-488755_【開発中】★建売マスターシート/docs/ * site-488755_implementation-plan.md(Task2〜Task10、本ファイルはこれに厳密に従う) * site-488755_improvement-plan.md(背景・設計根拠) * site-488755_field-mapping.md(Task1、分類判定の唯一の根拠) * * 使い方: * cd ClaudePleasanter * node .claude/js/site-scripts/★マスターシート/restructure-site-488755.js * ------------------------------------------------------------ */ const fs = require("fs"); const path = require("path"); const { findSiteDir, latestJsonPath, newModifyDir } = require("../../site-paths"); const { REPO_ROOT } = require("../../resolve-project"); const PROJECT_NAME = "★マスターシート"; const ENV = "production"; const SITE_ID = 488755; const baseDir = path.join(REPO_ROOT, PROJECT_NAME); const configsDir = path.join(baseDir, "configs", ENV); const siteDir = findSiteDir(configsDir, SITE_ID); if (!siteDir) { console.error(`[エラー] SiteId ${SITE_ID} のフォルダが見つかりません。`); process.exit(1); } const latestPath = latestJsonPath(configsDir, SITE_ID); if (!latestPath || !fs.existsSync(latestPath)) { console.error(`[エラー] latest.jsonが見つかりません: ${latestPath}`); process.exit(1); } function fail(msg) { console.error(`[エラー] ${msg}`); process.exit(1); } const raw = JSON.parse(fs.readFileSync(latestPath, "utf-8")); const siteSettings = raw.Response.Data.SiteSettings; const originalCounts = { columns: siteSettings.Columns.length, sections: siteSettings.Sections.length, }; console.log(`[読込] Columns: ${originalCounts.columns}件 / Sections: ${originalCounts.sections}件`); // ============================================================ // Task 2: 分類フィールド(Class032)・物件名/物件名_カナ(Class044/053) // ============================================================ console.log("[Task2] 分類フィールド・物件名を追加しています..."); siteSettings.Columns.push({ ColumnName: "Class032", LabelText: "物件分類", ChoicesText: "1,土地のみ,土地のみ,status-big-orange\n2,建売,建売,status-green-blue\n", DefaultInput: "2", ChoicesControlType: "Radio", NoWrap: true, ValidateRequired: true, SearchType: "PartialMatch", }); siteSettings.Columns.push( { ColumnName: "Class044", LabelText: "物件名", FieldCss: "field-wide" }, { ColumnName: "Class053", LabelText: "物件名_カナ", FieldCss: "field-wide" } ); { const general = siteSettings.EditorColumnHash.General; const idx = general.indexOf("ClassP"); if (idx === -1) fail("ClassPが見つかりません。latest.jsonの再取得漏れの可能性。"); general.splice(idx + 1, 0, "Class032"); } { const general = siteSettings.EditorColumnHash.General; const idx = general.indexOf("_Section-12"); if (idx === -1) fail("_Section-12が見つかりません。"); general.splice(idx + 1, 0, "Class044", "Class053"); } console.log(" OK(Columns 3件、EditorColumnHash.General 3件追加)"); // ============================================================ // Task 2.5: EditorColumnHash未配置の既存Column再配置 // ============================================================ console.log("[Task2.5] 施主1/2/3・勤務先・施主連絡先・顧客情報・紹介・現場案内図詳細・WF添付資料を再配置しています..."); // Step1: 新規セクションId払出(施主1/2/3・勤務先・施主連絡先・紹介) const task25Sections = (() => { const sections = siteSettings.Sections; const maxSectionId = Math.max(0, ...sections.map((s) => s.Id)); let nextId = maxSectionId + 1; const allocSectionId = () => nextId++; const secSekishu1 = allocSectionId(); // 施主1 const secSekishu2 = allocSectionId(); // 施主2 const secSekishu3 = allocSectionId(); // 施主3(建売限定) const secKinmusaki = allocSectionId(); // 勤務先 const secSekishuRenraku = allocSectionId(); // 施主連絡先 const secShokai = allocSectionId(); // 紹介 sections.push( { Id: secSekishu1, LabelText: "施主1", AllowExpand: true, Expand: true }, { Id: secSekishu2, LabelText: "施主2", AllowExpand: true, Expand: true }, { Id: secSekishu3, LabelText: "施主3", AllowExpand: true, Expand: true }, { Id: secKinmusaki, LabelText: "勤務先", AllowExpand: true, Expand: true }, { Id: secSekishuRenraku, LabelText: "施主連絡先", AllowExpand: true, Expand: true }, { Id: secShokai, LabelText: "紹介", AllowExpand: true, Expand: true } ); console.log(" Task2.5 新規セクションId:", { secSekishu1, secSekishu2, secSekishu3, secKinmusaki, secSekishuRenraku, secShokai }); return { secSekishu1, secSekishu2, secSekishu3, secKinmusaki, secSekishuRenraku, secShokai }; })(); // Step2: _Tab-1へ施主1/2/3・勤務先・施主連絡先を追記(Class028〜031と_Section-38の間、既存3件は変更しない) // 確定(ユーザー指示、2026-08-29): 施主1/2の姓名は既存Column(姓/名/姓カナ/名カナ)の分割のまま追記。 // 施主1の法人名項目(CheckA,Class054〜057)はhoujinCheck()による一体UIのためまとめて追記。 { const { secSekishu1, secSekishu2, secSekishu3, secKinmusaki, secSekishuRenraku } = task25Sections; const tab1 = siteSettings.EditorColumnHash["_Tab-1"]; const idx = tab1.indexOf("_Section-38"); if (idx === -1) fail("_Section-38が見つかりません(_Tab-1)。"); tab1.splice(idx, 0, `_Section-${secSekishu1}`, "CheckA", "Class054", "Class055", "Class056", "Class057", "Class001", "Class002", "Class003", "Class004", "Class005", "Date001", "Num001", "Class058", "Class006", "Class007", "Class008", `_Section-${secSekishu2}`, "Class009", "Class010", "Class011", "Class012", "Class013", "Date002", "Num002", "Class059", "Class014", "Class015", "Class016", `_Section-${secSekishu3}`, "Class017", "Class018", "Class019", "Class020", "Class021", "Date003", "Num003", "Class060", "Class022", "Class023", "Class024", `_Section-${secKinmusaki}`, "Class025", "Num005", `_Section-${secSekishuRenraku}`, "Class026", "Class027" ); } // Step3: _Tab-5へ顧客情報3項目を追記(_Section-25の直後、Class068の前に挿入) { const tab5 = siteSettings.EditorColumnHash["_Tab-5"]; const idx = tab5.indexOf("Class068"); if (idx === -1) fail("Class068が見つかりません(_Tab-5)。"); tab5.splice(idx, 0, "Class065", "Class066", "Class067"); } // Step4: _Tab-8へ紹介ブロックを追記(Description110の直後に挿入) { const { secShokai } = task25Sections; const tab8 = siteSettings.EditorColumnHash["_Tab-8"]; tab8.push(`_Section-${secShokai}`, "Class114", "Class115", "Num114"); } // Step5: _Tab-10へ現場案内図の詳細4項目を追記(_Section-39とAttachmentsDの間に挿入。建売限定) { const tab10 = siteSettings.EditorColumnHash["_Tab-10"]; const idx = tab10.indexOf("AttachmentsD"); if (idx === -1) fail("AttachmentsDが見つかりません(_Tab-10)。"); tab10.splice(idx, 0, "Class050", "Class051", "Class052", "Description050"); } // Step6: WF申請添付資料①②のうち実在確認済みの既存Columnを新規配列として作成 { // WF申請添付資料①(土地のみ: 本人確認書類のみ既存確認。建売: 上記+瑕疵担保・画像掲載・ポイントクラブ) siteSettings.EditorColumnHash["_Tab-11"] = [ "Attachments004", // 重要事項説明書(仕入れ用として流用、6.6節「要確認⑦」参照) "Attachments006", // 本人確認書類(共通) "Attachments005", // 瑕疵担保・契約内容確認シート(建売限定) "Attachments038", // 画像掲載承諾書(建売限定) "Attachments035", // ポイントクラブ契約時初回登録特典(建売限定) ]; // WF申請添付資料②(建売限定: 見積根拠書類1〜5、その他添付書類、給排水現場調査報告書) siteSettings.EditorColumnHash["_Tab-14"] = [ "Attachments021", "Attachments022", "Attachments023", "Attachments024", "Attachments025", "Attachments031", "Attachments032", ]; // 見積根拠書類1〜5のLabelTextを改称(ユーザー確定指示: 「[設]見積根拠書類N」→「分譲見積根拠書類N」。189112側は変更しない) ["Attachments021", "Attachments022", "Attachments023", "Attachments024", "Attachments025"].forEach((name, i) => { const col = siteSettings.Columns.find((c) => c.ColumnName === name); if (!col) fail(`Column not found: ${name}`); col.LabelText = `分譲見積根拠書類${i + 1}`; }); } console.log(` OK(_Tab-1: ${siteSettings.EditorColumnHash["_Tab-1"].length}件 / _Tab-5: ${siteSettings.EditorColumnHash["_Tab-5"].length}件 / _Tab-8: ${siteSettings.EditorColumnHash["_Tab-8"].length}件 / _Tab-10: ${siteSettings.EditorColumnHash["_Tab-10"].length}件 / _Tab-11: ${siteSettings.EditorColumnHash["_Tab-11"].length}件 / _Tab-14: ${siteSettings.EditorColumnHash["_Tab-14"].length}件)`); // ============================================================ // Task 3: 粗利・支払情報ブロック(仕入れ土地情報/振込金額/現金、共通+建売限定) // ============================================================ console.log("[Task3] 粗利・支払情報ブロックを追加しています..."); siteSettings.Columns.push( // 仕入れ土地情報(共通) { ColumnName: "Num004", LabelText: "物件価格", Format: "C", NoWrap: true }, { ColumnName: "Num006", LabelText: "仲介手数料", Format: "C", NoWrap: true }, // 振込金額(共通) { ColumnName: "Num007", LabelText: "振込金額(総額)", Format: "C", NoWrap: true }, // 振込1〜3グリッドのデータ格納用(共通、Task7のスクリプトが読み書きする) { ColumnName: "Description006", LabelText: "振込データ", DefaultInput: JSON.stringify([ { 0: "", 1: "", 2: "", 3: "", 4: "", 5: "", 6: "", 7: "" }, { 0: "", 1: "", 2: "", 3: "", 4: "", 5: "", 6: "", 7: "" }, { 0: "", 1: "", 2: "", 3: "", 4: "", 5: "", 6: "", 7: "" }, ]), Hide: true, }, // 現金(共通) { ColumnName: "Num008", LabelText: "金額", Format: "C", NoWrap: true }, { ColumnName: "Description007", LabelText: "支払先" }, { ColumnName: "Description008", LabelText: "内容" }, { ColumnName: "Description009", LabelText: "備考" }, // 融資先・登記依頼先(建売限定、表示切替はTask6のスクリプトで対応) { ColumnName: "Description010", LabelText: "融資先(販売)" }, { ColumnName: "Description011", LabelText: "登記依頼先(販売)" } ); const task3Sections = (() => { const sections = siteSettings.Sections; const maxSectionId = Math.max(0, ...sections.map((s) => s.Id)); let nextId = maxSectionId + 1; const allocSectionId = () => nextId++; const secShiireTochi = allocSectionId(); // 仕入れ土地情報 const secFurikomiSokei = allocSectionId(); // 振込金額(総額) const secFurikomiGrid = allocSectionId(); // 振込(グリッド、振込1〜3を統合) const secGenkin = allocSectionId(); // 現金 const secYushisaki = allocSectionId(); // 融資先(販売) const secTokiIraisaki = allocSectionId(); // 登記依頼先(販売) sections.push( { Id: secShiireTochi, LabelText: "仕入れ土地情報" }, { Id: secFurikomiSokei, LabelText: "振込金額" }, { Id: secFurikomiGrid, LabelText: "振込" }, { Id: secGenkin, LabelText: "現金" }, { Id: secYushisaki, LabelText: "融資先(販売)" }, { Id: secTokiIraisaki, LabelText: "登記依頼先(販売)" } ); // 既存41項目には一切触れず、末尾に追記するのみ siteSettings.EditorColumnHash["_Tab-3"].push( `_Section-${secShiireTochi}`, "Num004", "Num006", `_Section-${secFurikomiSokei}`, "Num007", `_Section-${secFurikomiGrid}`, "Description006", `_Section-${secGenkin}`, "Num008", "Description007", "Description008", "Description009", `_Section-${secYushisaki}`, "Description010", `_Section-${secTokiIraisaki}`, "Description011" ); console.log(" Task3 新規セクションId:", { secShiireTochi, secFurikomiSokei, secFurikomiGrid, secGenkin, secYushisaki, secTokiIraisaki }); return { secShiireTochi, secFurikomiSokei, secFurikomiGrid, secGenkin, secYushisaki, secTokiIraisaki }; })(); console.log(` OK(Columns 10件追加、_Tab-3: ${siteSettings.EditorColumnHash["_Tab-3"].length}件)`); // ============================================================ // Task 3.5: 追加新規Column作成(土地所有者法人・(実)日付群・不動産からの引継事項・WF添付資料の一部) // ============================================================ console.log("[Task3.5] 土地所有者法人・(実)日付群・不動産からの引継事項・WF添付資料の新規Columnを追加しています..."); siteSettings.Columns.push( // 契約情報(General、共通・建売限定混在) { ColumnName: "Class076", LabelText: "土地所有者法人", FieldCss: "field-wide" }, { ColumnName: "Class080", LabelText: "管轄営業所", ChoicesText: "[[96243]]", // 確定⑨(ユーザー指示、2026-08-29): ClassF(契約営業所)と同一マスタ(SiteId 96243)で確定 FieldCss: "field-wide", Link: true, SearchType: "PartialMatch", }, { ColumnName: "Class085", LabelText: "積算チェック者", ChoicesText: "[[Users*]]", UseSearch: true, NoWrap: true, SearchType: "PartialMatch", }, // 管理情報(General、建売限定) { ColumnName: "Date018", LabelText: "分譲OPEN予定日" }, // (実)日付群(土地のみ限定4件、建売限定2件。9章の事業計画連携トリガー候補、今回はColumn追加のみで連携再設計はスコープ外) { ColumnName: "Date004", LabelText: "(実)土地契約日(仕入れ)" }, { ColumnName: "Date005", LabelText: "(実)土地決済日(仕入れ)" }, { ColumnName: "Date006", LabelText: "(実)土地契約日(販売)" }, { ColumnName: "Date007", LabelText: "(実)土地決済日(販売)" }, { ColumnName: "Date008", LabelText: "(実)分譲契約日" }, { ColumnName: "Date009", LabelText: "(実)分譲引渡日" }, // 引継・紹介(建売限定) { ColumnName: "Description012", LabelText: "不動産からの引継事項" }, // WF申請添付資料①(既存Columnが無いと確認済みの分のみ。共通2件・建売限定5件) { ColumnName: "Attachments009", LabelText: "売買契約書(仕入れ)", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments010", LabelText: "事業計画書", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments016", LabelText: "売買代金清算書", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments017", LabelText: "固定資産税清算書", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments018", LabelText: "売買契約書(販売)", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments019", LabelText: "重要事項説明書(販売)", NotDeleteExistHistory: true, OverwriteSameFileName: true }, { ColumnName: "Attachments020", LabelText: "計画図面", NotDeleteExistHistory: true, OverwriteSameFileName: true } ); // Step3: Generalへ Class076/080/085/Date018/Date004〜009 を追記(既存41項目は変更しない) // 確定⑨(ユーザー指示、2026-08-29): 488755のClassZは既に「不動産担当」使用中のため、 // 積算チェック者(189112のClassZ相当)は新規Class085で対応する(誤ってClassZを上書きしない)。 { const general = siteSettings.EditorColumnHash.General; const idxZ = general.indexOf("ClassZ"); if (idxZ === -1) fail("ClassZが見つかりません(General)。"); general.splice(idxZ + 1, 0, "Class076", "Class080", "Class085"); const idxM = general.indexOf("DateM"); if (idxM === -1) fail("DateMが見つかりません(General)。"); general.splice(idxM + 1, 0, "Date018", "Date004", "Date005", "Date006", "Date007", "Date008", "Date009"); } // Step4: _Tab-8へ不動産からの引継事項を追記(紹介ブロックより前、Description110の直前に挿入。建売限定) { const tab8 = siteSettings.EditorColumnHash["_Tab-8"]; const idx = tab8.indexOf("Description110"); if (idx === -1) fail("Description110が見つかりません(_Tab-8)。"); tab8.splice(idx, 0, "Description012"); } // Step5: _Tab-11(WF申請添付資料①)へ新規Attachments7件を追記 { const tab11 = siteSettings.EditorColumnHash["_Tab-11"]; // Attachments004(重要事項説明書)の直前に売買契約書(仕入れ)・事業計画書・売買代金清算書・固定資産税清算書を挿入 const idx004 = tab11.indexOf("Attachments004"); if (idx004 === -1) fail("Attachments004が見つかりません(_Tab-11、Task2.5 Step6が先に必要)。"); tab11.splice(idx004, 0, "Attachments009", "Attachments010", "Attachments016", "Attachments017"); // 建売限定3件(売買契約書(販売)・重要事項説明書(販売)・計画図面)は末尾に追加 tab11.push("Attachments018", "Attachments019", "Attachments020"); } console.log(` OK(Columns 18件追加、General: ${siteSettings.EditorColumnHash.General.length}件、_Tab-8: ${siteSettings.EditorColumnHash["_Tab-8"].length}件、_Tab-11: ${siteSettings.EditorColumnHash["_Tab-11"].length}件)`); // 保留事項(Task3.5スコープ外): お客様データファイル【9】保管状況の1〜8個別項目は今回は分解しない // (site-488755_improvement-plan.md 6.5節「確定⑧」の通り、Class121/Class119の統合ステータスを継続利用) // 【2026-08-30改訂】買主1/2/連絡先/との約束・物件名(土地のみ用)の新規Column追加(旧Task2.6)は // 破棄した。PleasanterのClass型カラム上限(226個、488755は空き13個のみと実データ検証で判明)に // より新規23項目を確保できなかったため。代わりに施主1/2/連絡先/との約束・物件名(Class044/053、 // 既存共通)をそのまま使い、土地のみ選択時にラベルテキストのみ動的に書き換える方式に変更した // (site-488755_buyer-fields-design.md 1〜4章)。ラベル書き換え・色分け・物件名自動転記の実装は // scripts/16_15.分類別表示切替.js側で行う(Column/EditorColumnHashの変更は不要)。 // ============================================================ // Task 4: 両分類とも不要なColumnへの Hide:true 追加(Pleasanter標準機能のみ、スクリプト不要) // ============================================================ console.log("[Task4] 両分類とも不要なColumnにHide:trueを追加しています..."); { const hideTargets = [ // 旧_Tab-3(field-mapping.md該当行、実装計画Task1判明分) "Num071", "Num072", "Num073", "Num074", "Num075", "Num076", "Num038", "Date038", "Num061", "Num062", "Num063", "Num064", "Num065", "Num066", "DescriptionA", "Num039", "Num040", "Num041", "Num042", "DescriptionM", "Class045", "Class046", "Num051", "DescriptionG", "Class047", "Class048", "Num053", "Description061", "Num080", "Num081", "Class081", // _Tab-12(field-mapping.md該当行: 敷地調査関連書類、両方不要-Hide) "Class121", ]; hideTargets.forEach((name) => { const col = siteSettings.Columns.find((c) => c.ColumnName === name); if (!col) fail(`Column not found: ${name}`); col.Hide = true; }); console.log(` [OK] ${hideTargets.length}件にHide:trueを追加`); } { const col = siteSettings.Columns.find((c) => c.ColumnName === "Class046"); delete col.ValidateRequired; console.log(" [OK] Class046のValidateRequiredを除去"); } { const tab4HideTargets = ["DescriptionB", "Num045", "DescriptionC", "Num046", "DescriptionD", "Num047", "DescriptionE", "DescriptionF", "DescriptionN", "Num050"]; tab4HideTargets.forEach((name) => { const col = siteSettings.Columns.find((c) => c.ColumnName === name); if (!col) fail(`Column not found: ${name}`); col.Hide = true; }); console.log(` [OK] _Tab-4分 ${tab4HideTargets.length}件にHide:trueを追加`); } // _Section-23(差異・その他)のLabelTextを field-mapping.md の「新ラベル」列に従い改称。 // Global Constraintsは「EditorColumnHash配列・既存Columns定義」の削除/並び替え禁止であり、 // Sections配列のLabelText変更は対象外(削除でも並び替えでもない、既存Section定義の属性更新)と判断して実施。 { const sec = siteSettings.Sections.find((s) => s.Id === 23); if (!sec) fail("_Section-23が見つかりません。"); sec.LabelText = "その他"; console.log(" [OK] _Section-23のLabelTextを「差異・その他」→「その他」に変更"); } // 【2026-08-29追加】複製時点(189112→488755作成時、今回の改修より前)から Hide:true のまま // 放置されていた項目のうち、要望文に明記があり今回の改修で表示させる対象にしている9件のHideを解除する。 // site-488755_changes-from-189112.md 5.4節で発覚。field-mapping.mdの判定通りEditorColumnHashへの // 配置・Task6の分類別出し分け対象にはなっているが、Column定義側のHideが残ったままでは // Pleasanter側で描画されない懸念があるため解除する(要望文に言及の無い項目は対象外、現状維持)。 { const unhideTargets = [ "ClassD", // 契約会社(共通) "ClassX", // 振込先(共通) "Class061", // 集客事由(共通) "ClassF", // 契約営業所(建売限定) "DateL", // 引渡予定日(建売限定) "Class050", "Class051", "Class052", "Description050", // 現場案内図詳細4項目(建売限定) ]; unhideTargets.forEach((name) => { const col = siteSettings.Columns.find((c) => c.ColumnName === name); if (!col) fail(`Column not found: ${name}`); delete col.Hide; }); console.log(` [OK] ${unhideTargets.length}件のHide:trueを解除(複製時点からの既存放置分、要望文で必要な項目のみ)`); } // ============================================================ // Task 9: Scripts変更をrestructure-site-488755.jsへ組み込む // ============================================================ console.log("[Task9] manifest.json/scripts/*.js/processes.jsonからScripts/Processesを組み立てています..."); { const manifestPath = path.join(siteDir, "manifest.json"); const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")); const scriptsDir = path.join(siteDir, "scripts"); siteSettings.Scripts = manifest.Scripts.map((entry) => { const body = fs.readFileSync(path.join(scriptsDir, entry.File), "utf-8"); const existing = (siteSettings.Scripts || []).find((s) => s.Id === entry.Id) || {}; return { ...existing, Id: entry.Id, Title: entry.Title, Body: body, New: entry.New, Edit: entry.Edit, Index: entry.Index, }; }); const stylesDir = path.join(siteDir, "styles"); siteSettings.Styles = manifest.Styles.map((entry) => { const body = fs.readFileSync(path.join(stylesDir, entry.File), "utf-8"); const existing = (siteSettings.Styles || []).find((s) => s.Id === entry.Id) || {}; return { ...existing, Id: entry.Id, Title: entry.Title, Body: body, New: entry.New, Edit: entry.Edit, Index: entry.Index, All: entry.All, }; }); const processesPath = path.join(siteDir, "processes.json"); siteSettings.Processes = JSON.parse(fs.readFileSync(processesPath, "utf-8")); console.log(` [OK] Scripts ${siteSettings.Scripts.length}件 / Styles ${siteSettings.Styles.length}件 / Processes ${siteSettings.Processes.length}件`); } // ============================================================ // Task 10: modify/フォルダへの出力・整合性チェック // ============================================================ console.log("[Task10] 整合性チェックを実行しています..."); { const errors = []; if (siteSettings.Columns.filter((c) => c.ColumnName === "Class032").length !== 1) { errors.push("Class032が重複または存在しない"); } if (!siteSettings.EditorColumnHash.General.includes("Class032")) { errors.push("EditorColumnHash.GeneralにClass032が無い"); } if (siteSettings.EditorColumnHash["_Tab-4"].length !== 15) { errors.push("_Tab-4の項目数が15件でない(既存項目を変更してしまっている可能性)"); } if (siteSettings.Scripts.length !== 17) { errors.push(`Scripts件数が17件でない(実際: ${siteSettings.Scripts.length}件)`); } if (!siteSettings.Columns.some((c) => c.ColumnName === "Description006")) { errors.push("Description006(振込グリッドデータ)が存在しない"); } const class046 = siteSettings.Columns.find((c) => c.ColumnName === "Class046"); if (class046 && class046.ValidateRequired) { errors.push("Class046のValidateRequiredが残っている"); } const stillHidden = ["ClassD", "ClassX", "Class061", "ClassF", "DateL", "Class050", "Class051", "Class052", "Description050"] .filter((name) => siteSettings.Columns.find((c) => c.ColumnName === name)?.Hide); if (stillHidden.length > 0) { errors.push(`複製時点からのHide解除対象がまだHideのまま: ${stillHidden.join(", ")}`); } const blockedProcess = siteSettings.Processes.find( (p) => [1, 2, 3, 34].includes(p.Id) && p.ValidateInputs.some((v) => ["Class045", "Class047", "Class048", "Class081"].includes(v.ColumnName)) ); if (blockedProcess) { errors.push(`Process ${blockedProcess.Id}のValidateInputsに旧_Tab-3参照が残っている`); } // 追加チェック: Columns/Sections内の重複、EditorColumnHash各配列内の重複トークン const colNames = siteSettings.Columns.map((c) => c.ColumnName); if (new Set(colNames).size !== colNames.length) errors.push("ColumnNameに重複があります。"); const sectionIds = siteSettings.Sections.map((s) => s.Id); if (new Set(sectionIds).size !== sectionIds.length) errors.push("Section.Idに重複があります。"); for (const key of Object.keys(siteSettings.EditorColumnHash)) { const arr = siteSettings.EditorColumnHash[key]; if (new Set(arr).size !== arr.length) errors.push(`EditorColumnHash.${key} 内にトークンの重複があります。`); } // Task2〜3.5合計: Columns +31件(Task2:3, Task3:10, Task3.5:18。旧Task2.6は破棄済み) if (siteSettings.Columns.length !== originalCounts.columns + 31) { errors.push(`Columns件数が想定と異なります(期待: +31, 実際: +${siteSettings.Columns.length - originalCounts.columns})`); } // Task2.5 + Task3合計: Sections +12件(Task2.5:6, Task3:6。旧Task2.6は破棄済み) if (siteSettings.Sections.length !== originalCounts.sections + 12) { errors.push(`Sections件数が想定と異なります(期待: +12, 実際: +${siteSettings.Sections.length - originalCounts.sections})`); } // 旧Task2.6のColumnが誤って残っていないこと ["Class201", "Class221", "Class222", "Class223"].forEach((name) => { if (siteSettings.Columns.some((c) => c.ColumnName === name)) { errors.push(`旧Task2.6のColumnが残っています(破棄済みのはず): ${name}`); } }); if (siteSettings.Styles.length !== 10) { errors.push(`Styles件数が10件でない(実際: ${siteSettings.Styles.length}件)`); } if (errors.length > 0) { console.error("[整合性チェック失敗]"); errors.forEach((e) => console.error(" - " + e)); process.exit(1); } console.log(" [OK] 整合性チェック通過"); } const modifyDir = newModifyDir(siteDir, "土地のみ建売分類対応"); fs.copyFileSync(latestPath, path.join(modifyDir, "before_site-488755_latest.json")); raw.Response.Data.SiteSettings = siteSettings; fs.writeFileSync( path.join(modifyDir, "site-488755_updated.json"), JSON.stringify(raw, null, 2), "utf-8" ); console.log("\n========================================"); console.log(" 完了"); console.log("========================================"); console.log(`出力先: ${modifyDir}`); console.log(` Columns : ${originalCounts.columns} → ${siteSettings.Columns.length}`); console.log(` Sections : ${originalCounts.sections} → ${siteSettings.Sections.length}`); console.log(` Scripts : ${siteSettings.Scripts.length}件`); console.log(` Processes: ${siteSettings.Processes.length}件`);