Автоматизація погодження документів у Google Drive: статуси, версії та журнал рішень
Файли final, finalnew і finalv7_approved не показують погоджений документ. Автоматизація погодження документів керує станами, версіями й рішеннями.
Для workflow достатньо Sheets, Drive й Apps Script. Не ECM-система: практичний процес.

Чому файли final_v7 не працюють
Назва файлу — коментар автора, а не стан процесу. Хтось може завантажити final_v8, не знаючи, що попередню версію погодили, або змінити файл після approval.
Типові симптоми:
- версію шукають у чатах;
- «погоджено» не пов'язане з файлом;
- невідомі approver і дедлайн;
- документ перезаписують без історії;
- усім видали editor-доступ.
Як і в автоматизації замовлень, дія залежить від статусу, а не повідомлення в чаті.
Реєстр документів у Google Sheets

Один рядок реєстру представляє документ як бізнес-об'єкт:
| Поле | Призначення |
|---|---|
document_id | стабільний ID, незалежний від номера рядка |
title | зрозуміла назва документа |
file_id | ID актуального файла в Google Drive |
version | номер версії |
owner | хто готує та оновлює документ |
approver | хто приймає рішення |
status | поточний стан workflow |
submitted_at | коли відправлено на погодження |
due_at | строк рішення |
decision_at | час останнього рішення |
decision_comment | причина approval, rejection або змін |
documentid не будуйте з номера рядка: таблицю сортують. Підійде DOC-2026-0042. fileid вказує на робочу версію, snapshot-копії зберігаються окремо.
Статуси погодження
Мінімальна статусна модель:
| Статус | Дозволена дія |
|---|---|
draft | owner редагує документ |
submitted | approver перевіряє зафіксовану версію |
changes_requested | owner створює наступну версію |
approved | версію дозволено використовувати |
rejected | документ відхилено з коментарем |
archived | процес закрито, файл лише для історії |
Не дозволяйте approved напряму з draft: потрібні submitted, snapshot і запис у журналі.
function submitDocument(documentId) {
const doc = getDocumentById(documentId);
if (!doc || !["draft", "changes_requested"].includes(doc.status)) {
throw new Error("Document is not ready for submission");
}
const snapshot = createVersionSnapshot(doc);
const now = new Date();
updateDocument(documentId, {
status: "submitted",
submitted_at: now.toISOString(),
due_at: addBusinessDays(now, 2).toISOString(),
snapshot_file_id: snapshot.getId()
});
writeApprovalLog(documentId, "submitted", {
actor: Session.getActiveUser().getEmail(),
version: doc.version,
snapshot_file_id: snapshot.getId()
});
}Як зберігати версії й коментарі

Перед submit створіть snapshot в окремій папці, інакше після правок не видно, що погоджував approver. Це підтримує Drive service.
function createVersionSnapshot(doc) {
const source = DriveApp.getFileById(doc.file_id);
const folder = DriveApp.getFolderById(getSetting("APPROVAL_ARCHIVE_FOLDER"));
const version = String(doc.version).padStart(2, "0");
const name = `${doc.document_id}-v${version}-${doc.title}`;
const copy = source.makeCopy(name, folder);
copy.setDescription(
`Snapshot for approval; document=${doc.document_id}; version=${doc.version}`
);
return copy;
}Після changes_requested owner збільшує version і подає документ повторно. Старий snapshot не змінюється. Рішення записується структуровано:
function recordDecision(documentId, decision, comment) {
const allowed = ["approved", "rejected", "changes_requested"];
const doc = getDocumentById(documentId);
const actor = Session.getActiveUser().getEmail();
if (!doc || doc.status !== "submitted") {
throw new Error("Only submitted documents can receive a decision");
}
if (actor !== doc.approver || !allowed.includes(decision)) {
throw new Error("Decision is not allowed");
}
if (decision !== "approved" && !String(comment || "").trim()) {
throw new Error("Comment is required");
}
updateDocument(documentId, {
status: decision,
decision_at: new Date().toISOString(),
decision_comment: String(comment || "").trim()
});
writeApprovalLog(documentId, decision, {
actor,
version: doc.version,
comment
});
}Права owner, approver і viewer

ownerготує документ, створює нову версію й відправляє її;approverчитає snapshot і приймає рішення, але не має тихо виправляти подану версію;viewerбачить дозволений документ або звіт без права змінювати процес;- технічний admin підтримує Apps Script, налаштування й доступи.
Принцип least privilege важливіший за «дати editor усім». Ролі описані в гайді Google Drive про доступи. Для інтеграцій використовуйте Apps Script webhook-сервер, а не публічний Sheet.
Нагадування відповідальному

Time-driven trigger раз на день знаходить прострочені submitted. Нагадування лише повідомляє approver і пише подію в журнал.
function sendApprovalReminder() {
const now = new Date();
const documents = getDocuments().filter(doc =>
doc.status === "submitted" &&
doc.due_at &&
new Date(doc.due_at) < now
);
documents.forEach(doc => {
MailApp.sendEmail({
to: doc.approver,
subject: `Потрібне рішення: ${doc.title}`,
htmlBody: `Документ ${doc.document_id}, версія ${doc.version}, очікує рішення.`
});
writeApprovalLog(doc.document_id, "reminder_sent", {
actor: "automation",
approver: doc.approver
});
});
}Mail service підходить для простих повідомлень. Ескалації й SLA винесіть в окремі правила.
Журнал рішень
Аркуш ApprovalLog має бути append-only:
| Поле | Приклад |
|---|---|
event_at | 2026-06-08T14:00:00Z |
document_id | DOC-2026-0042 |
event | changes_requested |
actor | email відповідального |
version | 3 |
details_json | коментар, snapshot ID, approver |
function writeApprovalLog(documentId, event, details = {}) {
const sheet = SpreadsheetApp.getActive()
.getSheetByName("ApprovalLog");
sheet.appendRow([
new Date().toISOString(),
documentId,
event,
details.actor || "",
details.version || "",
JSON.stringify(details)
]);
}У details_json не дублюйте секрети чи PII. Журнал пояснює процес, а не копіює документ.
Як журнал рішень прибрав суперечки про актуальну версію
У робочому сценарії кошториси погоджували в чаті. Після «ок» хтось завантажував новий файл, і вже не було зрозуміло, яку версію підтвердили.
Я додав document_id, версію, owner, approver і статус. Submit створював snapshot, рішення потрапляло в ApprovalLog, а правки запускали нову версію.
Команда перестала шукати approval у чатах: стали видимі snapshot, автор, час і коментар. Sheets не стала ECM-системою, але операційна невизначеність зникла.
Практичний підсумок
Автоматизація має фіксувати файл і процес рішення. Реєстр показує стан, snapshot зберігає версію, а ApprovalLog пояснює історію.
Почніть із шести статусів, трьох ролей і п'яти дій: submit, snapshot, decision, reminder та log. Не перезаписуйте погоджену версію й не використовуйте реакцію в месенджері як єдине підтвердження.
Останні статті

Тестування AI-автоматизацій: як перевіряти prompts і не ламати результат після оновлення
Зміна в prompt може покращити JSON і зламати зміст. Модель повертає category , але губить примітку клієнта. Тому тестування AI автоматизацій має перевіряти формат і бізн…

Автоматизація комерційних пропозицій: як зібрати Google Docs, PDF і Gmail в один процес
У комерційній пропозиції більша частина роботи повторюється: підставити компанію, контакт, послуги, ціну, строк дії та менеджера. Тому автоматизація комерційних пропозиц…

Google Apps Script security: токени, права доступу і безпечні webhook-автоматизації
Google Apps Script security часто згадують тільки після того, як автоматизація вже працює. Таблиця приймає заявки, Web App отримує webhook-и, Telegram-бот відправляє пов…

OpenAI API cost control: як рахувати токени, ставити ліміти і не отримати несподіваний рахунок
OpenAI API cost control - це не страх перед LLM, а нормальна фінансова гігієна автоматизації. Якщо модель допомагає обробляти заявки, писати summary, класифікувати зверн…

Надійність автоматизації бізнес-процесів: черги, повтори і idempotency без великої інфраструктури
Надійність автоматизації бізнес-процесів починається не з Kubernetes, RabbitMQ або складної cloud-архітектури. У малому бізнесі вона часто починається з простішого питан…

RAG для бізнесу: як зробити внутрішній чат-бот по регламентах, FAQ і Google Docs
RAG для бізнесу потрібен не для того, щоб зробити ще одного "розумного чат-бота". Його нормальна задача простіша: дати співробітнику відповідь на основі внутрі…