運営者向けの運用ガイドです。初期設定から日次運用まで、必要な手順を順番にまとめています。
Code.gs に下記コードを貼り付けGAS URL に貼り付け、GAS URLを保存このHTMLに固定 を押す接続テスト 実行後、右上 ↻ で同期確認const DEALS_SHEET_NAME = 'Deals';
const CONFIG_SHEET_NAME = 'Config';
const DEAL_HEADERS = ['id', 'year', 'month', 'cat', 'st', 'tanto', 'name', 'uri', 'gen', 'note'];
function initSheets() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
if (!ss.getSheetByName(DEALS_SHEET_NAME)) {
const s = ss.insertSheet(DEALS_SHEET_NAME);
s.appendRow(DEAL_HEADERS);
}
if (!ss.getSheetByName(CONFIG_SHEET_NAME)) {
const s = ss.insertSheet(CONFIG_SHEET_NAME);
s.appendRow(['key', 'value']);
}
}
function doGet(e) {
initSheets();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const dealSheet = ss.getSheetByName(DEALS_SHEET_NAME);
const dealsData = dealSheet.getDataRange().getValues();
const headers = dealsData[0];
const deals = [];
for (let i = 1; i < dealsData.length; i++) {
let obj = {};
for (let j = 0; j < headers.length; j++) obj[headers[j]] = dealsData[i][j];
deals.push(obj);
}
const configSheet = ss.getSheetByName(CONFIG_SHEET_NAME);
const configData = configSheet.getDataRange().getValues();
let config = {};
for (let i = 1; i < configData.length; i++) {
try { config[configData[i][0]] = JSON.parse(configData[i][1]); } catch(err) {}
}
return ContentService.createTextOutput(JSON.stringify({ ok: true, deals: deals, config: config }))
.setMimeType(ContentService.MimeType.JSON);
}
function doPost(e) {
initSheets();
const ss = SpreadsheetApp.getActiveSpreadsheet();
let data;
try {
data = JSON.parse(e.postData.contents);
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({ok: false, error: 'Invalid JSON'}))
.setMimeType(ContentService.MimeType.JSON);
}
if (data.action === 'update_config') {
const sheet = ss.getSheetByName(CONFIG_SHEET_NAME);
const rows = sheet.getDataRange().getValues();
let found = false;
for (let i = 1; i < rows.length; i++) {
if (rows[i][0] === data.key) {
sheet.getRange(i + 1, 2).setValue(JSON.stringify(data.value));
found = true;
break;
}
}
if (!found) sheet.appendRow([data.key, JSON.stringify(data.value)]);
return ContentService.createTextOutput(JSON.stringify({ok: true}))
.setMimeType(ContentService.MimeType.JSON);
}
const dealSheet = ss.getSheetByName(DEALS_SHEET_NAME);
if (data.action === 'create') {
let d = data.deal;
dealSheet.appendRow([d.id, d.year, d.month, d.cat, d.st, d.tanto, d.name, d.uri, d.gen, d.note]);
} else if (data.action === 'update') {
let d = data.deal;
let rows = dealSheet.getDataRange().getValues();
for(let i = 1; i < rows.length; i++) {
if(rows[i][0] == d.id) {
dealSheet.getRange(i + 1, 1, 1, DEAL_HEADERS.length)
.setValues([[d.id, d.year, d.month, d.cat, d.st, d.tanto, d.name, d.uri, d.gen, d.note]]);
break;
}
}
} else if (data.action === 'delete') {
let id = data.id;
let rows = dealSheet.getDataRange().getValues();
for(let i = 1; i < rows.length; i++) {
if(rows[i][0] == id) {
dealSheet.deleteRow(i + 1);
break;
}
}
}
return ContentService.createTextOutput(JSON.stringify({ok: true}))
.setMimeType(ContentService.MimeType.JSON);
}
+ 案件追加 をクリック🔗 でリンク付与| 症状 | 原因 | 対応 |
|---|---|---|
| ローカルモードのまま | GAS URL未設定 / 権限不足 | URLとデプロイ権限を確認 |
| 同期エラー | GAS停止 / CORS / タイムアウト | GAS再デプロイ・URL再確認 |
| Access denied | 許可条件(メール/APIキー)不一致 | Code.gsとHTML設定を合わせる |
| 数字不一致 | 計上月/ステータス混在 | フィルタ条件を確認 |