# Knife Sushi — документація бекенду

## Що це і для кого

PHP-бекенд для ресторану суші **Knife Sushi** (Познань, Польща). Бекенд живе на shared-хостингу (cPanel) і виконує три задачі:

1. **Приймає замовлення** з сайту та надсилає їх у Telegram-чат персоналу.
2. **Дозволяє персоналу** через Telegram вибрати час доставки — клієнт одразу отримує SMS-підтвердження.
3. **Надсилає SMS з проханням залишити відгук** після виконання замовлення (запускається за розкладом через cron).

---

## Схема роботи — крок за кроком

### Нове замовлення

```
Клієнт оформлює замовлення на сайті
         ↓
sushiknifeprod.php отримує POST-запит
         ↓
Зберігає дані замовлення в orders/{orderId}.json
         ↓
Надсилає повідомлення в Telegram з кнопками вибору часу
         ↓
Telegram показує персоналу: "+30 хв", "+45 хв", "+60 хв",
                             "Вибрати власний час", "Вибрати дату і час"
```

### Персонал вибирає час доставки

```
Персонал натискає кнопку в Telegram
         ↓
Telegram надсилає callback на telegram_callback.php
         ↓
Файл визначає дію (set_time / choose_time / select_hour / тощо)
         ↓
Якщо час > 90 хв → SMS-вибачення клієнту
Якщо час ≤ 90 хв → SMS-підтвердження з часом доставки
         ↓
Telegram показує підтвердження персоналу з кнопкою "Вибрано неправильний час"
         ↓
orders/{orderId}.json оновлюється: timeSelected = true
```

### Відгук після замовлення

```
scheduled_sender.php запускається кроном кожні N хвилин
         ↓
Читає scheduled_messages.json
         ↓
Для кожного повідомлення, у якого настав час sendAt:
  → надсилає SMS з проханням залишити відгук в Instagram
  → видаляє повідомлення зі списку
```

> **Примітка:** Логіка планування SMS-відгуку (запис у scheduled_messages.json) в коді наразі не реалізована — scheduled_sender.php лише читає і відправляє вже заплановані.

### Заявка з лендінгу (форма "передзвоніть мені")

```
Клієнт заповнює форму на окремому лендінгу
         ↓
submit.php отримує POST-запит (ім'я + телефон)
         ↓
Надсилає повідомлення персоналу в Telegram (HTML-формат)
```

---

## Опис файлів

### Кореневі файли

#### `sushiknifeprod.php`
**Що робить:** Приймає нове замовлення з сайту.

- Отримує дані форми (ім'я, телефон, адреса, склад замовлення тощо).
- Очищає orderId від небезпечних символів (`sanitizeOrderId`).
- Зберігає мінімальні дані замовлення в `orders/{orderId}.json` — вони потрібні пізніше для надсилання SMS.
- Формує красиве HTML-повідомлення і надсилає в Telegram з кнопками вибору часу доставки.

#### `telegram_callback.php`
**Що робить:** Обробляє всі натискання кнопок у Telegram.

Коли персонал натискає кнопку в Telegram, Telegram надсилає `callback_query` на цей файл. Файл визначає, яку кнопку натиснули (`action`), і викликає відповідний обробник:

| Дія | Що відбувається |
|---|---|
| `set_time` | Обраний швидкий час (+30/+45/+60 хв) |
| `choose_time` | Показує вибір годин |
| `select_hour` | Показує вибір хвилин для обраної години |
| `select_minute` | Фінальний вибір часу (сьогодні) |
| `choose_date_time` | Показує вибір дати |
| `select_date` | Показує години для обраної дати |
| `select_date_hour` | Показує хвилини для обраної дати і години |
| `select_date_minute` | Фінальний вибір часу (конкретна дата) |
| `back_to_hours` | Повернення назад до вибору годин |
| `incorrect_time` | Персонал вибрав неправильний час — скидає вибір і просить обрати знову |

#### `submit.php`
**Що робить:** Приймає заявки з форми "передзвоніть мені" на лендінгу. Надсилає ім'я і телефон клієнта в Telegram.

#### `scheduled_sender.php`
**Що робить:** Запускається cron-ом. Читає файл `scheduled_messages.json`, надсилає SMS-відгуки клієнтам, у яких настав час відправки.

Щоб cron правильно знаходив файли незалежно від поточної директорії, всі шляхи починаються з `__DIR__`.

#### `status_callback.php`
**Що робить:** Приймає звіти про доставку SMS від Twilio (delivered / failed тощо). Наразі лише логує статус — додаткова логіка не реалізована.

#### `config.php`
**Що робить:** Зберігає секретні налаштування — токени, ключі API. **Ніколи не комітити в git.**

Вміст:
```php
return [
    'telegram_bot_token' => '...',
    'telegram_chat_id'   => '...',
    'twilio_sid'         => '...',
    'twilio_token'       => '...',
];
```

---

### Модулі в `src/`

#### `src/constants.php`
**Що робить:** Єдине місце для всіх бізнес-налаштувань ресторану.

Тут змінюються: робочі години, тексти SMS, адреса самовивозу, телефон, порогове значення для SMS-вибачення. **Більше нічого не треба чіпати** — всі інші файли автоматично використовують ці значення.

#### `src/functions.php`
**Що робить:** Утилітарні функції, що використовуються в кількох файлах.

- `sanitizeOrderId()` — очищає ID замовлення від небезпечних символів.
- `escapeMarkdownV2()` — екранує спецсимволи для Telegram MarkdownV2.
- `getWorkingHours()` — повертає час відкриття і закриття на конкретний день (враховує пятницю/суботу).
- `generateSetTimeButtons()` — формує кнопки швидкого вибору часу (+30/+45/+60 хв).
- `generateHourButtons()` — формує кнопки вибору годин.
- `generateMinuteButtons()` — формує кнопки вибору хвилин для конкретної години.
- `generateDateButtons()` — формує кнопки вибору дати (наступні 7 днів).
- `generateDateHourButtons()` — формує кнопки вибору годин для конкретної дати.
- `generateDateMinuteButtons()` — формує кнопки вибору хвилин для конкретної дати і години.

#### `src/telegram.php`
**Що робить:** Всі функції для роботи з Telegram Bot API.

- `sendMessageToTelegram()` — надсилає нове повідомлення з HTML-розміткою (для замовлень).
- `sendNewMessage()` — надсилає нове повідомлення з MarkdownV2-розміткою (для callback-відповідей).
- `updateMessage()` — редагує існуюче повідомлення (коли персонал переходить між кроками вибору часу).
- `sendRequest()` — внутрішня функція HTTP-запиту (без повернення результату).
- `sendRequestWithResult()` — те саме, але повертає відповідь API (використовується в `sendMessageToTelegram`).

> **Важливо про екранування:** `sendNewMessage()` і `updateMessage()` **автоматично** екранують текст через `escapeMarkdownV2()`. Не потрібно екранувати текст перед передачею в ці функції — інакше символи будуть подвійно екрановані.

#### `src/sms.php`
**Що робить:** Надсилає SMS через Twilio.

- `sendSMS($phoneNumber, $message)` — приймає номер у форматі E.164 (наприклад, `+48123456789`) і текст. Використовує відправника `KnifeSushi` (альфанумерний sender ID).

#### `src/orders.php`
**Що робить:** Читає і зберігає дані замовлень у JSON-файлах.

- `getOrderData($orderId)` — читає `orders/{orderId}.json`, повертає масив або `null`.
- `saveOrderData($orderId, $orderData)` — записує дані в `orders/{orderId}.json` з ексклюзивним блокуванням файлу (захист від паралельних запитів).

---

## Як змінити робочі години

Відкрий [src/constants.php](src/constants.php) і знайди блок:

```php
define('WH_OPEN',           12); // час відкриття — всі дні
define('WH_CLOSE_DEFAULT',  22); // час закриття — нд–чт
define('WH_CLOSE_EXTENDED', 23); // час закриття — пт–сб
```

Просто змінюй числа. Наприклад, якщо ресторан відкривається о 11:00:
```php
define('WH_OPEN', 11);
```

Якщо п'ятниця і субота закриваються о 23:30 — наразі підтримуються лише цілі години. Якщо потрібні хвилини, звернись до розробника.

---

## Як змінити тексти SMS

Всі SMS-шаблони знаходяться в [src/constants.php](src/constants.php) у блоці `SMS templates`.

Плейсхолдери:
- `%1$s` — номер замовлення (orderId)
- `%2$s` — час доставки (наприклад, `2025-05-08 18:30`)

Приклад — SMS при доставці кур'єром (швидкий час):
```php
define('SMS_DELIVERY_QUICK',
    "Dzień dobry!\nTwoje zamówienie %1\$s zostało przyjęte i będzie dostarczone maksymalnie do %2\$s.\nDziękujemy!"
);
```

`\n` — перенос рядка в SMS. `\$s` — це просто `$s` з екранованим `$` (вимога PHP в подвійних лапках).

Список всіх шаблонів:

| Константа | Коли надсилається |
|---|---|
| `SMS_DELIVERY_QUICK` | Доставка кур'єром, швидкий час (+30/+45/+60 хв) |
| `SMS_PICKUP_QUICK` | Самовивіз, швидкий час |
| `SMS_DELIVERY_CUSTOM` | Доставка кур'єром, вибраний конкретний час |
| `SMS_PICKUP_CUSTOM` | Самовивіз, вибраний конкретний час |
| `SMS_APOLOGY` | Час очікування перевищує 90 хвилин |
| `SMS_INCORRECT_TIME` | Персонал скинув час через помилку |
| `SMS_FEEDBACK` | Відгук після замовлення (cron) |

---

## Як додати нову кнопку в Telegram

Розберемо на прикладі додавання кнопки "Замовлення готове" після вибору часу.

### Крок 1 — Додай кнопку в `finalizeTimeSelection()`

У [telegram_callback.php](telegram_callback.php) знайди функцію `finalizeTimeSelection()` (рядок ~128). Там формується масив `$buttons`:

```php
$buttons = [[['text' => 'Wybrano błędny czas', 'callback_data' => 'incorrect_time|' . $orderId]]];
```

Додай нову кнопку:
```php
$buttons = [
    [['text' => 'Wybrano błędny czas',  'callback_data' => 'incorrect_time|'  . $orderId]],
    [['text' => 'Zamówienie gotowe',    'callback_data' => 'order_ready|'     . $orderId]],
];
```

### Крок 2 — Навчи парсер розуміти нову дію

У функції `parseCallbackData()` (рядок ~60) додай новий `case`:

```php
case 'order_ready':
    return ['action' => $action, 'orderId' => $parts[1] ?? null];
```

### Крок 3 — Додай обробник

Внизу файлу, поряд з іншими `handle*` функціями, додай:

```php
function handleOrderReady(int $chatId, array $data): void
{
    $orderId = sanitizeOrderId($data['orderId']);
    // тут твоя логіка, наприклад:
    sendNewMessage($chatId, "Замовлення #{$orderId} помічено як готове.", []);
}
```

### Крок 4 — Підключи обробник у `switch`

У блоці `switch ($data['action'])` (рядок ~39) додай:

```php
case 'order_ready': handleOrderReady($chatId, $data); break;
```

---

## Структура директорії

```
/
├── config.php              ← секрети (не в git)
├── sushiknifeprod.php      ← прийом замовлень
├── telegram_callback.php   ← обробка кнопок Telegram
├── submit.php              ← форма лендінгу
├── scheduled_sender.php    ← SMS-відгуки (cron)
├── status_callback.php     ← статуси SMS від Twilio
├── scheduled_messages.json ← черга SMS-відгуків
├── src/
│   ├── constants.php       ← всі налаштування ресторану
│   ├── functions.php       ← утиліти (кнопки, години)
│   ├── telegram.php        ← Telegram API
│   ├── sms.php             ← Twilio SMS
│   └── orders.php          ← читання/запис замовлень
├── orders/                 ← JSON-файли замовлень (генеруються автоматично)
└── vendor/                 ← Composer залежності (Twilio SDK)
```

---

## Як деплоїти зміни на сервер

### Варіант A — через FTP / File Manager cPanel

1. Зміни потрібний файл локально.
2. Підключись до хостингу через FileZilla (або cPanel → File Manager).
3. Завантаж файл на сервер у ту саму директорію, перезаписавши старий.
4. Перевір у браузері або через Telegram-тест.

> Ніколи не завантажуй `config.php` — він уже на сервері з реальними ключами. Локально тримай окремий тестовий `config.php`.

### Варіант B — через SSH (якщо підключений git)

```bash
git add src/constants.php
git commit -m "Оновлено час закриття у пятницю"
git push
```

На сервері:
```bash
cd /public_html
git pull
```

### Що перевірити після деплою

- Відкрий сайт і зроби тестове замовлення.
- В Telegram має з'явитися повідомлення з кнопками.
- Натисни "+30 хв" — клієнт (тестовий номер) має отримати SMS.
- Перевір `error_log` на сервері (cPanel → Logs) якщо щось пішло не так.

### Тестовий режим

У [src/constants.php](src/constants.php) є константа:

```php
define('TEST_MODE', false);
```

Зміни на `true` — і `getWorkingHours()` завжди повертатиме 00:00–23:59, тобто всі кнопки часу будуть доступні незалежно від реального часу. Не забудь повернути `false` перед деплоєм на продакшн.
