ken_nogi/Pleasanter/.claude/js/site-scripts/営業積算システム/restructure-site-335411.js
Kenichiro NOGI ce58cb4be4 初回コミット: dev配下(NodeSrv/Pleasanter等)をGitea管理下に統合
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>
2026-09-04 15:37:06 +09:00

221 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* restructure-site-335411.js
* ------------------------------------------------------------
* site-335411②問い合わせ内容管理へ以下をまとめて反映するTask2、フル更新
* Scripts/Styles/ServerScripts/Processesは一切変更しない現状のまま送信
*
* A. StatusControls Id:1「依頼ロック」ColumnHashへ DescriptionC:"ReadOnly" 追加
* (要望①: 積算へ依頼後、依頼内容編集不可化)
* B. Sections LabelText変更 Id:9/14営業積算課→営業積算、要望④の画面表示分
* C. 新規Columns4点追加要望②a/b/c, ⑦)
* - Class088 担当監督SiteId 210712参照、単純選択
* - Check073 稟議ではないことを確認済み
* - Check074 承認図後追加変更契約 定義書①~④以外である
* - Class089 不要理由SiteId 335603参照、Lookups: DescriptionA→Description091、AutoPostBack
* EditorColumnHash.General: DescriptionA(問い合わせ経緯)の直後にClass088/Check073/Check074を挿入
* EditorColumnHash._Tab-2: Description091(見積回答)の直前にClass089を挿入
* D. Links配列へ Class088→210712, Class089→335603 を追加
* E. 既存Columns8項目Date081/Class081, Date082/Class082, Date103/Class103, Date084/Class087。
* プロセス自動設定項目)へ ExtendedFieldCss:"code-readonly2" 追加(要望⑥)
*
* 使い方:
* node restructure-site-335411.js … 差分表示のみ(送信なし)
* node restructure-site-335411.js --execute … 実際に送信する
* ------------------------------------------------------------
*/
const fs = require("fs");
const path = require("path");
const { findSiteDir, newModifyDir } = require("../../site-paths");
const { REPO_ROOT, loadServerConfig } = require("../../resolve-project");
const PROJECT_NAME = "営業積算システム";
const ENV = "development"; // このサイトはテストサーバー(neo999.next-hd.net)専用
const baseDir = path.join(REPO_ROOT, PROJECT_NAME);
const execute = process.argv.includes("--execute");
const SITE_ID = 335411;
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, "-");
}
function insertAfter(arr, afterName, items) {
const idx = arr.indexOf(afterName);
if (idx === -1) throw new Error(`挿入基準列が見つかりません: ${afterName}`);
arr.splice(idx + 1, 0, ...items);
}
function insertBefore(arr, beforeName, items) {
const idx = arr.indexOf(beforeName);
if (idx === -1) throw new Error(`挿入基準列が見つかりません: ${beforeName}`);
arr.splice(idx, 0, ...items);
}
const config = loadServerConfig(ENV);
const siteDir = findSiteDir(path.join(baseDir, "configs", ENV), SITE_ID);
(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 ss = JSON.parse(JSON.stringify(data.SiteSettings));
// --- A. 依頼ロックへDescriptionC追加 ---
const lockControl = (ss.StatusControls || []).find((s) => s.Id === 1);
if (!lockControl) throw new Error("StatusControls Id:1依頼ロックが見つかりません");
if (lockControl.ColumnHash.DescriptionC) {
console.log("[SKIP] DescriptionCは既に依頼ロック対象です");
} else {
lockControl.ColumnHash.DescriptionC = "ReadOnly";
console.log("[A] StatusControls Id:1 ColumnHashへ DescriptionC:ReadOnly を追加");
}
// --- B. Sections LabelText変更 ---
const sec9 = (ss.Sections || []).find((s) => s.Id === 9);
const sec14 = (ss.Sections || []).find((s) => s.Id === 14);
if (!sec9 || !sec14) throw new Error("Sections Id:9またはId:14が見つかりません");
console.log(`[B] Sections Id:9 "${sec9.LabelText}" -> "営業積算からの質疑"`);
console.log(`[B] Sections Id:14 "${sec14.LabelText}" -> "営業積算からの引継ぎ"`);
sec9.LabelText = "営業積算からの質疑";
sec14.LabelText = "営業積算からの引継ぎ";
// --- C. 新規Columns4点 ---
const newColumns = [
{
ColumnName: "Class088",
LabelText: "担当監督",
ChoicesText: '[\n {\n "SiteId": 210712\n }\n]',
UseSearch: true,
SearchType: "PartialMatch",
},
{
ColumnName: "Check073",
LabelText: "稟議ではないことを確認済み",
NoWrap: true,
},
{
ColumnName: "Check074",
LabelText: "承認図後追加変更契約 定義書①~④以外である",
NoWrap: true,
},
{
ColumnName: "Class089",
LabelText: "不要理由",
ChoicesText:
'[\n {\n "SiteId": 335603,\n "View": {\n "ColumnSorterHash": {\n "NumA": "asc"\n }\n },\n "Lookups": [\n {\n "From": "DescriptionA",\n "To": "Description091"\n }\n ]\n }\n]',
AutoPostBack: true,
ColumnsReturnedWhenAutomaticPostback: "Description091",
SearchType: "PartialMatch",
},
];
const existingNames = new Set((ss.Columns || []).map((c) => c.ColumnName));
newColumns.forEach((c) => {
if (existingNames.has(c.ColumnName)) throw new Error(`ColumnName重複: ${c.ColumnName}`);
});
ss.Columns.push(...newColumns);
console.log(`[C] Columns新規追加: ${newColumns.map((c) => c.ColumnName + "(" + c.LabelText + ")").join(", ")}`);
// --- EditorColumnHash挿入 ---
insertAfter(ss.EditorColumnHash.General, "DescriptionA", ["Class088", "Check073", "Check074"]);
console.log('[C] EditorColumnHash.General: DescriptionAの直後にClass088/Check073/Check074を挿入');
insertBefore(ss.EditorColumnHash["_Tab-2"], "Description091", ["Class089"]);
console.log('[C] EditorColumnHash._Tab-2: Description091の直前にClass089を挿入');
// --- D. Links追加 ---
ss.Links = ss.Links || [];
ss.Links.push({ ColumnName: "Class088", SiteId: 210712 });
ss.Links.push({
ColumnName: "Class089",
SiteId: 335603,
View: {
Id: 0,
ColumnSorterHash: { NumA: "asc" },
ApiColumnKeyDisplayType: 0,
ApiColumnValueDisplayType: 0,
CalendarSiteId: 0,
ApiDataType: 0,
},
Lookups: [{ From: "DescriptionA", To: "Description091" }],
JsonFormat: true,
});
console.log(`[D] Links追加後の件数: ${ss.Links.length}Class088→210712, Class089→335603を追加`);
// --- E. 読み取り専用化プロセス自動設定項目8列 ---
const readonlyTargets = ["Date081", "Class081", "Date082", "Class082", "Date103", "Class103", "Date084", "Class087"];
readonlyTargets.forEach((name) => {
const col = (ss.Columns || []).find((c) => c.ColumnName === name);
if (!col) throw new Error(`読取専用化対象列が見つかりません: ${name}`);
if (col.ExtendedFieldCss === "code-readonly2") {
console.log(`[SKIP][E] ${name}${col.LabelText}は既にcode-readonly2設定済み`);
return;
}
col.ExtendedFieldCss = "code-readonly2";
console.log(`[E] ${name}${col.LabelText})に ExtendedFieldCss:"code-readonly2" を追加`);
});
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: ss,
};
const modifyDir = newModifyDir(siteDir, `2026-08-11T${new Date().toTimeString().slice(0, 5).replace(":", "")}_要望9件対応フル更新`);
const ts = timestamp();
const maskedBody = { ...body, ApiKey: maskApiKey(body.ApiKey) };
const previewPath = path.join(modifyDir, `site-${SITE_ID}_full_update_preview_${ts}.json`);
fs.writeFileSync(previewPath, JSON.stringify(maskedBody, null, 2), "utf-8");
console.log(`\n[OK] 送信予定内容を保存しました: ${previewPath}`);
const beforePath = path.join(modifyDir, `before_site-${SITE_ID}_latest.json`);
fs.writeFileSync(beforePath, JSON.stringify(getJson, null, 2), "utf-8");
console.log(`[OK] 変更前スナップショットを保存しました: ${beforePath}`);
if (!execute) {
console.log("\n[注意] --execute が指定されていないため、送信は行っていません。");
console.log("内容を確認の上、問題なければ次を実行してください: node restructure-site-335411.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}_full_update_result_${ts}.json`);
fs.writeFileSync(resultOutPath, text, "utf-8");
console.log(`HTTP ${res.status} ${res.statusText}`);
console.log(`[OK] 応答を保存しました: ${resultOutPath}`);
console.log(text);
})();