# Калькулятор суммы заказа (пример реализации)

### ⚠ Важно

Этот пример показывает **общую логику работы Python-скрипта в Nextbot**.

{% hint style="info" %}

#### Это не строгий шаблон. Вы можете:

* менять структуру таблицы
* добавлять свои поля
* менять названия колонок
* дорабатывать логику расчёта
  {% endhint %}

Используйте пример как основу, а не как обязательную инструкцию "сделать один в один".

## Задача

Предположим, у вас есть таблица в **Google Sheets** с прайс-листом.

Пример колонок:

* `Материал`
* `Цена за м2`
* `Мин. заказ м2`

<div align="left"><figure><img src="/files/qpm0DmT8PDO54XSHEoME" alt="" width="375"><figcaption></figcaption></figure></div>

Вы хотите, чтобы ИИ-агент:

1. Запрашивал у клиента:
   * название материала
   * ширину
   * длину
2. Рассчитывал итоговую стоимость заказа
3. Возвращал результат клиенту

## Пошаговая инструкция

У вас есть ИИ-агент:\
\&#xNAN;**"Продавец стройматериалов"**

## Шаг 1. Создание функции с действием "Python Script" для подсчета суммы заказа

Создайте функцию, например, с названием:

```
calculate_summ
```

(Название может быть любым, главное, чтобы оно описывало действие функции.)

### Параметры функции

Добавьте параметры:

| Имя параметра | Тип параметра | Рекомендуемая инструкция для параметра | Обязательный |
| ------------- | ------------- | -------------------------------------- | ------------ |
| material      | Текстовый     | Материал                               | Да           |
| length        | Текстовый     | Длина                                  | Да           |
| width         | Текстовый     | Ширина                                 | Да           |

{% hint style="info" icon="question" %}
**Почему текстовый тип?**

Потому что пользователь может написать:\
`2 м`, `160 см`, `0.5`, и мы нормализуем это в коде.
{% endhint %}

Добавьте действие **"Python Script"** и замените шаблон в редакторе кода на необходимый.

<details>

<summary><strong>Пример PYTHON скрипта для подсчета суммы заказа</strong></summary>

Ниже приведён пример кода.\
Вы можете адаптировать его под свою таблицу.

{% code overflow="wrap" %}

```
scope = [
  'https://www.googleapis.com/auth/spreadsheets',
  'https://www.googleapis.com/auth/drive'
]
debug("Область доступа определена")

# Данные сервисного аккаунта в формате JSON
json_data =  {
        "type": "service_account",
        "project_id": "your_project_id",
        "private_key_id": "your_private_key_id",
        "private_key": "-----BEGIN PRIVATE KEY-----\nyour_private_key\n-----END PRIVATE KEY-----\n",
        "client_email": "your_service_account_email",
        "client_id": "your_client_id",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "your_client_cert_url"
}

debug("JSON данные сервисного аккаунта подготовлены")

def normalize_to_meters(value):
    if isinstance(value, (int, float)):
        return float(value)

    value = str(value).lower().replace(",", ".").strip()

    number_match = re.search(r"[\d.]+", value)
    if not number_match:
        raise ValueError("Некорректный формат размера")

    number = float(number_match.group())

    if "мм" in value:
        return number / 1000
    elif "см" in value:
        return number / 100
    elif "м" in value:
        return number
    else:
        return number

# ================================
# ФУНКЦИЯ РАСЧЁТА СТОИМОСТИ
# ================================
# material_name — выбранный материал (например: МДФ)
# width — ширина изделия в метрах
# length — длина изделия в метрах
# Функция берёт цену материала из Google Таблицы
# и рассчитывает итоговую стоимость

def calculate_price(material_name, width, length):
    # Подключаемся к Google Sheets через сервисный аккаунт
    debug("Получаем ServiceAccountCredentials")
    ServiceAccountCredentials = oauth2client['service_account']['ServiceAccountCredentials']
    # Создаём учетные данные для авторизации
    debug("Создаем учетные данные")
    creds = ServiceAccountCredentials.from_json_keyfile_dict(json_data, scope)
    # Авторизуемся в Google Sheets
    debug("Авторизация в Google Sheets")
    client = gspread.authorize(creds)
    # Открываем таблицу с ценами
    debug(f"Открываем таблицу {table_name}")
    sheet = client.open(table_name).worksheet("Цены")
    # Получаем все строки таблицы
    debug("Получаем данные из таблицы")
    rows = sheet.get_all_records()

    debug("Ищем выбранный материал в таблице")
    material_row = None
    for row in rows:
        if row["Материал"].lower() == material_name.lower():
            material_row = row
            break

    # Если материал не найден — возвращаем ошибку
    if not material_row:
        return {"error": "Материал не найден. Проверьте название."}
    
    # Берём цену за м² и минимальный заказ
    price_per_m2 = float(material_row["Цена за м2"])
    min_order = float(material_row["Мин. заказ м2"])

    debug("Рассчитываем площадь")
    area = float(width) * float(length)

    # Если площадь меньше минимального заказа —
    # считаем по минимальному объёму
    if area < min_order:
        area = min_order

    debug("Рассчитываем итоговую стоимость")
    total_price = area * price_per_m2

    # Возвращаем результат расчёта
    return {
        "Материал": material_name,
        "Площадь м2": round(area, 2),
        "Итоговая стоимость": round(total_price, 2)
    }

# ================================
# ПАРАМЕТРЫ ОТ ПОЛЬЗОВАТЕЛЯ
# ================================
# material — название материала
# width — ширина в метрах
# length — длина в метрах

table_name = 'Стройматериалы'  # Название Google Таблицы

material_name = args.get("material", "")
width = normalize_to_meters(args.get("width", ''))
length = normalize_to_meters(args.get("length", ''))

# Выполняем расчёт
result_data = calculate_price(material_name, width, length)

debug("Возвращаем ответ")
result = {
  "status": "success",
  "data": result_data
}

```

{% endcode %}

{% hint style="warning" %}
Обратите внимание что в переменной json\_data надо вставить данные из вашего JSON ключа, а в переменной table\_name надо прописать имя вашей таблицы.
{% endhint %}

</details>

{% hint style="warning" %}

#### Обязательно проверьте

* вы вставили реальный JSON сервисного аккаунта
* email сервисного аккаунта имеет доступ к таблице
* названия колонок в коде совпадают с таблицей
  {% endhint %}

### Тестирование

1. Нажмите **"Тестировать код"**.
2. Введите тестовые значения в поля "material", "length" и "width", например:

* material: `Фанера`
* width: `2 м`
* length: `160 см`

3. Нажмите **"Запустить тест"**.

<figure><img src="/files/qT2cfU4EpAYC7bKSjiL6" alt=""><figcaption></figcaption></figure>

4. Проверьте результат:

<details>

<summary>Пример результата</summary>

{% code overflow="wrap" %}

```
{
  "status": "success",
  "data": {
    "Материал": "Фанера",
    "Площадь м2": 2,
    "Итоговая стоимость": 6400
  }
}
```

{% endcode %}

<figure><img src="/files/r765VnwS621cG36T3a9I" alt=""><figcaption></figcaption></figure>

</details>

## Шаг 2. Создание функции с действием "Python Script" для получения доступных материалов (опционально).

Чтобы ИИ-агент знал о тех материалах, которые доступны в данный момент, вы можете дополнительно:

* добавить колонку `Наличие м2`
* добавить функцию получения доступных материалов

<div align="left"><figure><img src="/files/6bWYB4PiwMUV49Tuy1AI" alt=""><figcaption></figcaption></figure></div>

Создайте функцию, например: **`get_available_materials`** **(без параметров**).

Добавьте действие **"Python Script"** и замените шаблон в редакторе кода на необходимый.

<details>

<summary><strong>Пример PYTHON скрипта для получения доступных материалов</strong></summary>

Ниже приведён пример кода.\
Вы можете адаптировать его под свою таблицу.

{% code overflow="wrap" %}

```
scope = [
    'https://www.googleapis.com/auth/spreadsheets',
    'https://www.googleapis.com/auth/drive'
]
debug("Область доступа определена")

# Данные сервисного аккаунта в формате JSON
json_data =  {
        "type": "service_account",
        "project_id": "your_project_id",
        "private_key_id": "your_private_key_id",
        "private_key": "-----BEGIN PRIVATE KEY-----\nyour_private_key\n-----END PRIVATE KEY-----\n",
        "client_email": "your_service_account_email",
        "client_id": "your_client_id",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "your_client_cert_url"
}

debug("JSON данные сервисного аккаунта подготовлены")

def get_available_materials():
    """
    Возвращает список материалов,
    у которых значение в колонке 'Наличие, м2' больше 1.5
    """
    # Подключаемся к Google Sheets через сервисный аккаунт
    debug("Получаем ServiceAccountCredentials")
    ServiceAccountCredentials = oauth2client['service_account']['ServiceAccountCredentials']
  
    # Создаём учетные данные для авторизации
    debug("Создаем учетные данные")
    creds = ServiceAccountCredentials.from_json_keyfile_dict(json_data, scope)
  
    # Авторизуемся в Google Sheets
    debug("Авторизация в Google Sheets")
    client = gspread.authorize(creds)
  
    # Открываем таблицу с ценами
    debug(f"Открываем таблицу {table_name}")
    sheet = client.open(table_name).worksheet(worksheet_name)
  
    # Получаем все строки таблицы
    debug("Получаем данные из таблицы")
    rows = sheet.get_all_records()

    materials = []

    for row in rows:
        material_name = str(row.get("Материал", "")).strip()
        availability_raw = str(row.get("Наличие м2", "")).strip()

        if not material_name or not availability_raw:
            continue

        # нормализация числа (поддержка 1,7)
        availability_raw = availability_raw.replace(",", ".")

        try:
            availability = float(availability_raw)
        except ValueError:
            continue

        if availability > 1.5:
            materials.append({
                "Материал": material_name,
                "Наличие, м2": availability
            })

    return materials

table_name = "Стройматериалы"
worksheet_name = "Цены"

available_materials = get_available_materials()

result = {
    "status": "success",
    "available_materials": available_materials
}
```

{% endcode %}

{% hint style="warning" %}
Обратите внимание что в переменной json\_data надо вставить данные из вашего JSON ключа, в переменной table\_name надо прописать имя вашей таблицы, а в worksheet\_name - название вкладки в таблице.
{% endhint %}

</details>

{% hint style="warning" %}

#### Обязательно проверьте

* вы вставили реальный JSON сервисного аккаунта
* email сервисного аккаунта имеет доступ к таблице
* названия колонок в коде совпадают с таблицей
  {% endhint %}

## Шаг 3. Добавить в [системный промпт](/functional/setting-up-agent/system-prompt.md) инструкцию по использованию функций.

<details>

<summary><strong>Пример системного промпта</strong></summary>

{% code overflow="wrap" %}

````
Ты Василий, консультант в онлайн-магазине стройматериалов. Твоя задача — помочь клиенту подобрать нужный строительный материал (из предоставленных на текущий момент в наличии), уточнить его размеры, передать данные для расчёта стоимости и сообщить результат. Всегда следуй процессу в вежливой и профессиональной форме, избегая ненужных отвлечений.

# Steps

1. **Проверка наличия материалов:**
   - Первым делом вызови функцию `get_available_materials` и получи список материалов, которые сейчас есть в наличии.
   - Сообщи клиенту о поддерживаемых материалах, например: "На данный момент доступны следующие материалы: фанера, кирпич, цемент. Какой из них вам нужен?"
   - Если клиент запрашивает материал, которого нет в наличии, извинись и предложи ему выбрать из доступных.

2. **Сбор информации:**
   - Узнай у клиента, какой длины и ширины ему нужен выбранный материал.
   - Если клиент не уточняет единицы измерения, уточни, например: "Пожалуйста, уточни, это в миллиметрах (мм), сантиметрах (см) или метрах (м)?"

3. **Уточнение единиц измерения:**
   - Если клиент не указывает единицы измерения, уточни формат передаваемых данных.
   - Если клиент указывает некорректную или нестандартную единицу, уточни и объясни, например: "Мы поддерживаем только миллиметры (мм), сантиметры (см) и метры (м). Пожалуйста, уточни, в каких единицах нужно рассчитать?"

4. **Передача данных для проверки стоимости:**
   - Передай информацию о материале, длине и ширине в функцию `calculate_summ`, используя сокращённые обозначения единиц измерения: мм, см или м.
     Например:  
     ```calculate_summ(material_name="фанера", length="500 см", width="20 см")```

5. **Расчёт стоимости:**
   - Ожидай, что функция вернёт итоговую стоимость на основе переданных данных, включая единицы измерения.

6. **Ответ клиенту:**
   - Сообщи клиенту общую стоимость требуемого материала профессиональным и вежливым тоном.
   - Избегай отвлечённых тем, сосредоточься только на ответе.

7. **Дополнительно (если необходимо):**
   - Если клиент попросит уточнений или дополнительной информации, отвечай кратко и по делу.

# Output Format

- **Диалоговая форма ответа.**
- Включи:
  - Результаты проверки наличия материалов.
  - Вопросы для уточнения данных (материал, длина, ширина).
  - Уточнение единиц измерения, если они не указаны.
  - Передачу данных в функцию (`calculate_summ`) только с сокращёнными обозначениями единиц измерения (мм, см, м).
  - Вежливый ответ с расчётом стоимости.
  - Не выходи за рамки темы.

# Example

**Клиент:** Здравствуйте, мне нужно купить фанеру.  
**Ты:** Здравствуйте! Спасибо, что обратились к нам. Сейчас я уточню, есть ли фанера в наличии. (*вызов функции get_available_materials*)  
**Ты:** На данный момент в наличии фанера, кирпич и цемент. Вам нужна фанера, верно?  
**Клиент:** Да, фанера.  
**Ты:** Спасибо! Пожалуйста, уточни, какая длина и ширина тебе нужна?  
**Клиент:** Длина 5, ширина 20.  
**Ты:** Спасибо! Это в миллиметрах (мм), сантиметрах (см) или метрах (м)?  
**Клиент:** Длина 5 метров, ширина 20 сантиметров.  
**Ты:** Спасибо за уточнение! Передаю размеры для расчёта.  

(*вызов функции calculate_summ(material_name="фанера", length="5 м", width="20 см")*)  

**Ты:** Стоимость будет составлять 3200 рублей. Чем я могу ещё помочь?  

# Notes

- Всегда начинай с вызова функции `get_available_materials` для проверки доступных материалов.
- При недостатке или неполноте данных уточняй их у клиента.
- Передавай в функцию только корректные сокращённые единицы измерения (мм, см, м).
````

{% endcode %}

</details>

## Шаг 4. Тестирование в диалоге (тестовый чат).

1. Откройте **"**[**Тестовый чат**](/functional/chat.md)**"**.
2. Начните диалог с агентом и сообщите агенту необходимые параметры - материал, размеры (например, "Хочу заказать фанеру 160 сантиметров на 2 метра")

Будет вызвана функция "**calculate\_summ**", которая вернет рассчитанную на основе этих параметров стоимость.

<details>

<summary><strong>Пример выполнения функции</strong></summary>

{% code overflow="wrap" %}

```
Сработала функция: calculate_summ

Аргументы:

width: 2 м
length: 160 см
material: Фанера

Результат выполнения:

{
  "status": "success",
  "data": {
    "Материал": "Фанера",
    "Площадь м2": 3.2,
    "Итоговая стоимость": 10240
  }
}
```

{% endcode %}

</details>

<figure><img src="/files/gSjHNdllJFugSuzC5AzV" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.nextbot.ru/functional/functions/sending-result/python/podklyuchenie-i-rabota-s-google-tablicam/kalkulyator-summy-zakaza-primer-realizacii.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
