/** * apply-9item-phase1-496626.js * ------------------------------------------------------------ * site-496626「新・着工要因(東京)v2」9項目改修のうち、item1,2,3,4,5,6,7,9の * SiteSettings構造変更(Sections/Columns/EditorColumnHash/GridColumns/ * FilterColumns/Aggregations/Links/Summaries/Notifications)をまとめて反映する。 * item8(引越し削除+20工程番号リネーム)は影響範囲が大きく別スクリプトで扱う。 * * 現在のサイト全体(getsite)を取得し、in-memoryで変換した上でupdatesite(全体更新) * で送信する。Scripts/Styles/ServerScripts等、この改修で対象外のフィールドは * 取得した現在値をそのまま使うため変更されない。 * * 使い方: * node apply-9item-phase1-496626.js … 差分表示のみ(送信なし) * node apply-9item-phase1-496626.js --execute … 上記に加えて実際に送信する * ------------------------------------------------------------ */ const fs = require("fs"); const path = require("path"); const { findSiteDir, newModifyDir, latestModifyDir } = require("./site-paths"); const SITE_ID = 496626; const baseDir = path.join(__dirname, "..", ".."); const configsDir = path.join(baseDir, "configs"); const execute = process.argv.includes("--execute"); function loadJson(p) { return JSON.parse(fs.readFileSync(p, "utf-8")); } function maskApiKey(key) { if (!key) return key; return key.length > 8 ? key.slice(0, 4) + "****" + key.slice(-4) : "****"; } function timestamp() { return new Date().toISOString().replace(/[:.]/g, "-"); } const config = loadJson(path.join(baseDir, "config.json")); const siteDir = findSiteDir(configsDir, SITE_ID); function transform(ss) { const log = []; // ---- item1: ゼロエミ承認 → ゼロエミ交付申請 ---- { const sec = ss.Sections.find((s) => s.Id === 20); sec.LabelText = "ゼロエミ交付申請"; const col = ss.Columns.find((c) => c.ColumnName === "Date020"); col.GridLabelText = "ゼロエミ交付申請"; log.push("item1: ゼロエミ承認 -> ゼロエミ交付申請"); } // ---- item2: 取出有無業者連絡 → 取出業者手配 ---- { const sec = ss.Sections.find((s) => s.Id === 15); sec.LabelText = "取出業者手配"; const col = ss.Columns.find((c) => c.ColumnName === "Date015"); col.GridLabelText = "取出業者手配"; log.push("item2: 取出有無業者連絡 -> 取出業者手配"); } // ---- item5: 物件詳細2 → 補助金項目 ---- { const col = ss.Columns.find((c) => c.ColumnName === "DescriptionB"); col.LabelText = "補助金項目"; log.push("item5: 物件詳細2 -> 補助金項目"); } // ---- item6: 一覧からDateD列を非表示 ---- { const removeDateD = (arr) => { const idx = arr.indexOf("DateD"); if (idx >= 0) arr.splice(idx, 1); }; removeDateD(ss.GridColumns); ss.Views.forEach((v) => { if (v.GridColumns) removeDateD(v.GridColumns); }); const colDateD = ss.Columns.find((c) => c.ColumnName === "DateD"); colDateD.GridDesign = "[DateD]\n~着工可能:[NumE]\n~完成予定:[NumG]"; log.push("item6: DateD列をGridColumns(4箇所)から削除。GridDesignをNumG参照に更新"); } // ---- item9: 変更・注意点等コメント(DescriptionC)を一覧列から削除 ---- { const removeDescC = (arr) => { const idx = arr.indexOf("DescriptionC"); if (idx >= 0) arr.splice(idx, 1); }; removeDescC(ss.GridColumns); ss.Views.forEach((v) => { if (v.GridColumns) removeDescC(v.GridColumns); }); log.push("item9: DescriptionC列をGridColumns(4箇所)から削除"); } // ---- item7: NumE/NumF/NumG 再定義 ---- { const numFIdx = ss.Columns.findIndex((c) => c.ColumnName === "NumF"); ss.Columns[numFIdx].LabelText = "着工可能から完成予定日数"; const numG = { ColumnName: "NumG", LabelText: "契約から完成予定", DefaultInput: "0", Unit: "日" }; ss.Columns.splice(numFIdx + 1, 0, numG); const genIdx = ss.EditorColumnHash.General.indexOf("NumF"); ss.EditorColumnHash.General.splice(genIdx + 1, 0, "NumG"); log.push("item7: NumF LabelText変更、NumG新規追加(Columns+EditorColumnHash)"); } // ---- item3: 着工可能日セクション(Id=6)廃止、DateAへ一本化 ---- { // Columns: DateY, DateZ, ClassY, ClassZ を削除 ss.Columns = ss.Columns.filter( (c) => !["DateY", "DateZ", "ClassY", "ClassZ"].includes(c.ColumnName) ); // Sections: Id=6 を削除 ss.Sections = ss.Sections.filter((s) => s.Id !== 6); // EditorColumnHash.General: _Section-6 と DateY/DateZ/ClassZ/ClassY を削除 // (Comments/Title/Class200/Description200 はitem4の処理で一括してSection5末尾へ移設する) ss.EditorColumnHash.General = ss.EditorColumnHash.General.filter( (t) => !["_Section-6", "DateY", "DateZ", "ClassZ", "ClassY"].includes(t) ); // DateA を編集可能化(読取専用CSSを除去) const colDateA = ss.Columns.find((c) => c.ColumnName === "DateA"); delete colDateA.ExtendedFieldCss; // FilterColumns / Aggregations / Links / Summaries から ClassZ 関連を削除 ss.FilterColumns = ss.FilterColumns.filter((c) => c !== "ClassZ"); ss.Aggregations = ss.Aggregations.filter((a) => a.GroupBy !== "ClassZ"); ss.Links = ss.Links.filter((l) => l.ColumnName !== "ClassZ"); ss.Summaries = ss.Summaries.filter((s) => s.LinkColumn !== "ClassZ"); // Notifications Id:1(着工日更新通知)の監視列を DateZ -> DateA に付け替え // (DateZ削除により監視対象が消滅し、通知が永久に発火しなくなるための対応) const notif1 = ss.Notifications.find((n) => n.Id === 1); if (notif1) { notif1.MonitorChangesColumns = notif1.MonitorChangesColumns.map((c) => c === "DateZ" ? "DateA" : c ); } log.push( "item3: DateY/DateZ/ClassY/ClassZ削除、Section6削除、DateA編集可能化、FilterColumns/Aggregations/Links/Summaries整理、Notification1監視列をDateA化" ); } // ---- item4: リードタイム関連情報セクション(Id=7)廃止、Class190-192は存続 ---- { // Columns: Class113〜Class119 のみ削除(Class190〜192は残す) const readtimeCols = ["Class113", "Class114", "Class115", "Class116", "Class117", "Class118", "Class119"]; ss.Columns = ss.Columns.filter((c) => !readtimeCols.includes(c.ColumnName)); // Sections: Id=7 を削除 ss.Sections = ss.Sections.filter((s) => s.Id !== 7); // EditorColumnHash.General: _Section-7 と Class113〜119 を削除 ss.EditorColumnHash.General = ss.EditorColumnHash.General.filter( (t) => !["_Section-7", ...readtimeCols].includes(t) ); // Class190/191/192 を現在の位置から削除し、Section5末尾(DescriptionDの直後)へ移設 ss.EditorColumnHash.General = ss.EditorColumnHash.General.filter( (t) => !["Class190", "Class191", "Class192"].includes(t) ); const descDIdx = ss.EditorColumnHash.General.indexOf("DescriptionD"); ss.EditorColumnHash.General.splice(descDIdx + 1, 0, "Class190", "Class191", "Class192"); log.push( "item4: Class113-119削除、Section7削除、Class190-192をSection5末尾へ移設(Notifications3件は宛先存続のため変更なし)" ); } return log; } (async () => { console.log("[INFO] 現在のサイト情報を取得しています..."); 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; console.log(`[OK] 取得しました(現在のVer: ${data.Ver}, UpdatedTime: ${data.UpdatedTime})`); const siteSettings = JSON.parse(JSON.stringify(data.SiteSettings)); console.log("========================================"); console.log(` 9項目改修 Phase1(item1,2,3,4,5,6,7,9)SiteSettings構造変更(updatesite全体更新)${execute ? "(実行)" : "(プレビューのみ)"}`); console.log("========================================"); const log = transform(siteSettings); log.forEach((l) => console.log(" " + l)); 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 modifyDir = latestModifyDir(siteDir) || newModifyDir(siteDir, "9item-revision"); const ts = timestamp(); const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) }; const previewPath = path.join(modifyDir, `site-${SITE_ID}_phase1_update_preview_${ts}.json`); fs.writeFileSync(previewPath, JSON.stringify(maskedBody, null, 2), "utf-8"); console.log(`\n[OK] 送信予定内容を保存しました: ${previewPath}`); if (!execute) { console.log("\n[注意] --execute が指定されていないため、送信は行っていません。"); console.log("内容を確認の上、問題なければ次を実行してください: node apply-9item-phase1-496626.js --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(); const resultOutPath = path.join(modifyDir, `site-${SITE_ID}_phase1_update_result_${ts}.json`); fs.writeFileSync(resultOutPath, text, "utf-8"); console.log(`HTTP ${res.status} ${res.statusText}`); console.log(`[OK] 応答を保存しました: ${resultOutPath}`); console.log(text); })();