/** * restore-checkh-189112.js * ------------------------------------------------------------ * 189112「★マスターシート」のstaging環境で、CheckH(「建築資材価格変動等に関する合意書」の * 読み合わせを行い、請負契約時にサインいただくことに了承いただいた。)のColumns定義と、 * Processes(Id=11「[設契]事前申請」/Id=12「[設契]WF再申請」)のValidateInputs(必須項目 * チェック)を復元する。 * * 経緯: staging/productionのSiteSettings比較検証(2026-08-31)で、production側にのみ * CheckHのColumns定義とProcesses必須項目チェックが存在し、staging側には最古の記録時点 * (このプロジェクトの作業開始前)から既に欠落していたことが判明した。ユーザー確認により * 「CheckHはproduction側にあとから追加した仕様変更であり、staging側にも同じ設定が必要。 * このまま反映するとCheckHの設定が消えてしまう」との指示を受け、production側の内容を * そのままstaging側へ復元する。 * * 使い方: * node restore-checkh-189112.js --project="★マスターシート" --env=staging * … 差分表示のみ(送信なし) * node restore-checkh-189112.js --project="★マスターシート" --env=staging --execute * … 上記に加えて実際に送信する * ------------------------------------------------------------ */ const path = require("path"); const fs = require("fs"); const { resolveProjectRoot, loadServerConfig } = require("../../resolve-project"); const { findSiteDir, newModifyDir } = require("../../site-paths"); const { baseDir, env } = resolveProjectRoot(); const execute = process.argv.includes("--execute"); const SITE_ID = 189112; const CHECKH_COLUMN = { ColumnName: "CheckH", LabelText: "「建築資材価格変動等に関する合意書」の読み合わせを行い、請負契約時にサインいただくことに了承いただいた。", FieldCss: "field-wide", }; const CHECKH_VALIDATE_INPUT = { Id: 39, ColumnName: "CheckH", Required: true }; const TARGET_PROCESS_IDS = [11, 12]; function maskApiKey(key) { if (!key) return key; return key.length > 8 ? key.slice(0, 4) + "****" + key.slice(-4) : "****"; } (async () => { const config = loadServerConfig(env); console.log("========================================"); console.log(` 189112 CheckH復元(${env})${execute ? "(実行)" : "(プレビューのみ)"}`); console.log("========================================"); const getUrl = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${SITE_ID}/getsite`; const getRes = await fetch(getUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ApiVersion: config.ApiVersion || "1.1", ApiKey: config.ApiKey }), }); const getJson = await getRes.json(); if (getJson.StatusCode !== 200) { console.error("[エラー] getsiteに失敗しました:", JSON.stringify(getJson).slice(0, 500)); process.exit(1); } const data = getJson.Response.Data; const siteSettings = data.SiteSettings; console.log(`[OK] 取得しました(現在のVer: ${data.Ver}, UpdatedTime: ${data.UpdatedTime})`); let changed = false; console.log("\n--- Columns ---"); const hasCheckHColumn = siteSettings.Columns.some((c) => c.ColumnName === "CheckH"); if (hasCheckHColumn) { console.log(" CheckH: 既にColumns定義あり(変更なし)"); } else { siteSettings.Columns.push(CHECKH_COLUMN); console.log(" CheckH: Columns末尾に追加 ->", JSON.stringify(CHECKH_COLUMN)); changed = true; } console.log("\n--- EditorColumnHash._Tab-11(画面表示配置) ---"); const tab11 = siteSettings.EditorColumnHash["_Tab-11"]; if (!tab11) { console.error("[エラー] EditorColumnHash._Tab-11 が見つかりません"); process.exit(1); } if (tab11.includes("CheckH")) { console.log(" CheckH: 既に配置あり(変更なし)"); } else { const insertAt = tab11.indexOf("Attachments035"); if (insertAt === -1) { console.error("[エラー] 挿入位置の基準(Attachments035)が見つかりません"); process.exit(1); } tab11.splice(insertAt, 0, "CheckH"); console.log(" CheckH: Attachments033直後/Attachments035直前に挿入 ->", JSON.stringify(tab11)); changed = true; } console.log("\n--- Processes ValidateInputs ---"); TARGET_PROCESS_IDS.forEach((processId) => { const proc = siteSettings.Processes.find((p) => p.Id === processId); if (!proc) { console.error(`[エラー] Process Id=${processId} が見つかりません`); process.exit(1); } const has = proc.ValidateInputs.some((v) => v.ColumnName === "CheckH"); if (has) { console.log(` Process Id=${processId} (${proc.DisplayName}): 既にCheckH検証あり(変更なし)`); } else { proc.ValidateInputs.push(CHECKH_VALIDATE_INPUT); console.log(` Process Id=${processId} (${proc.DisplayName}): ValidateInputs末尾に追加 ->`, JSON.stringify(CHECKH_VALIDATE_INPUT)); changed = true; } }); if (!changed) { console.log("\n変更なし。既にCheckHは復元済みです。"); process.exit(0); } console.log("\n他のSiteSettings(他Columns/Processes/GridColumns等)・Permissions・Title等は一切変更しません。"); const body = { ApiVersion: config.ApiVersion || "1.1", ApiKey: config.ApiKey, SiteId: SITE_ID, Title: data.Title, ReferenceType: data.ReferenceType, ParentId: data.ParentId, InheritPermission: data.InheritPermission, Permissions: data.Permissions, SiteSettings: siteSettings, }; const siteDir = findSiteDir(path.join(baseDir, "configs", env), SITE_ID); const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) }; const ts = new Date().toISOString().replace(/[:.]/g, "-"); if (siteDir) { const modifyDir = newModifyDir(siteDir, "restore-checkh"); fs.writeFileSync( path.join(modifyDir, `site-${SITE_ID}_preview_${ts}.json`), JSON.stringify(maskedBody, null, 2), "utf-8" ); console.log(`\n[OK] 送信予定内容を保存しました: ${path.join(modifyDir, `site-${SITE_ID}_preview_${ts}.json`)}`); } if (!execute) { console.log("\n[注意] --execute が指定されていないため、送信は行っていません。"); process.exit(0); } console.log("\n実際に送信します(updatesite)..."); const url = `${config.BaseUrl.replace(/\/+$/, "")}/api/items/${SITE_ID}/updatesite`; const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const text = await res.text(); console.log(`HTTP ${res.status} ${res.statusText}`); console.log(text); })();