# logo nullInvoice **nullInvoice** - это микросервис Spring Boot для **автоматической генерации и управления счетами** с **полностью настраиваемыми HTML-шаблонами**, предназначенный для интеграции с веб-магазинами и SaaS-платформами. ## Обзор Компании используют nullInvoice для генерации счетов после завершения продаж. Поставщики настраиваются один раз через веб-интерфейс, затем ваше приложение вызывает REST API для автоматической генерации корректных счетов по запросу. **Как это работает:** 1. **Настройка**: Настройте поставщиков в UI с данными компании, локалью, валютой, ставками налогов, пользовательским брендингом и шаблонами счетов 2. **Аутентификация**: Сгенерируйте API-ключи из Панели администратора для безопасного доступа к REST API 3. **Интеграция**: Ваш веб-магазин/SaaS выполняет аутентифицированные API-вызовы к `/api/v1/invoices/generate` с использованием ID поставщика 4. **Генерация**: Счета создаются из полностью настраиваемых HTML-шаблонов и возвращаются как JSON или PDF с метаданными в заголовках 5. **Доставка**: Ваше приложение получает счет и может отправить его клиентам или сохранить для учета ### Типичный поток интеграции ``` ┌─────────────────┐ │ Клиент │ │ завершает │ │ покупку │ └────────┬────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Ваша платформа Webstore/SaaS │ │ ───────────────────────────────────── │ │ 1. Обработка платежа │ │ 2. Выдача цифрового чека │ │ (обязательно) │ │ 3. Клиент запрашивает счет? ────────┐ │ └──────────────────────────────────────┼──┘ │ │ API-вызов ▼ ┌──────────────────────────────┐ │ Сервис nullInvoice │ │ ────────────────────────── │ │ POST /api/v1/invoices/ │ │ generate │ │ │ │ - Валидирует ID поставщика │ │ - Применяет шаблон │ │ - Сохраняет HTML-снимок │ │ - Генерирует PDF │ │ - Возвращает счет │ └──────────────┬───────────────┘ │ │ Ответ (JSON или PDF) ▼ ┌──────────────────────────────────────────┐ │ Ваша платформа Webstore/SaaS │ │ ────────────────────────────────────── │ │ - Получает JSON-метаданные ИЛИ PDF-файл │ │ - Сохраняет номер счета для учета │ │ - Отправляет PDF клиенту по email │ └──────────────────────────────────────────┘ ``` ### Основные возможности **Полностью настраиваемые шаблоны** - Счета основаны на HTML-шаблонах, определяемых пользователем, с inline CSS - Шаблоны поддерживают более 30 плейсхолдеров для данных поставщика, клиента и финансовых данных - Шаблоны по умолчанию для поставщиков позволяют различный брендинг для разных бизнес-единиц **Неизменяемость документов** - Каждый счет хранит **снимок разобранного HTML-шаблона** в момент генерации - Изменения шаблона не влияют на уже созданные счета - Счета можно последовательно регенерировать из сохраненного снимка - Права в базе данных предотвращают удаление записей счетов (финансовое соответствие) **Готовность к мульти-арендности** - Настройте нескольких поставщиков с независимыми настройками (локаль, валюта, нумерация, брендинг) - Вызывающие API указывают ID поставщика для генерации счетов для разных юридических лиц **Гибкая доставка** - Возврат метаданных счета как JSON для учета (`response_type: number`) - Возврат PDF напрямую с метаданными в заголовках для немедленной доставки (`response_type: pdf`) - Получение PDF позже через `/api/v1/invoices/{invoiceNumber}/pdf` **Асинхронная очередь генерации** - Альтернативный путь для интеграций, которые не хотят синхронно ждать завершения генерации - Запрос сохраняется в очередь, фоновый обработчик создаёт счёт, клиенты опрашивают статус - Те же гарантии нумерации счетов, что и у синхронного пути (общая блокировка строки поставщика) - Обработчик можно отключить через `QUEUE_ENABLED=false` (по умолчанию `true`) **Документация OpenAPI** - Интерактивная документация API доступна на `/swagger` - Спецификация OpenAPI JSON на `/openapi` - Полные примеры запросов/ответов и функция try-it-out ## Важные уведомления **⚠️ Уведомление о безопасности** nullInvoice включает **встроенную аутентификацию** (вход в UI по сессии + аутентификация по API-ключу для REST-эндпоинтов). Хотя это обеспечивает безопасность по умолчанию, приложение предназначено для развертывания во внутренней/частной сети. **Рекомендуемое развертывание:** - За firewall или VPN - В приватной сети, доступной только доверенным приложениям - С включенным HTTPS/TLS для всех соединений - За reverse proxy с настроенным rate limiting **Для продакшн-развертываний необходимы дополнительные меры безопасности:** - Включить HTTPS/TLS - Настроить rate limiting на уровне reverse proxy - Использовать надежные пароли администратора - Регулярно ротировать API-ключи - Хранить API-ключи в безопасном хранилище секретов (HashiCorp Vault, AWS Secrets Manager и др.) См. раздел [Безопасность и лучшие практики](#безопасность-и-лучшие-практики) для полного чек-листа продакшн. **⚠️ Это приложение НЕ является бухгалтерским инструментом.** nullInvoice - это **конвейер генерации счетов**, предназначенный для создания, хранения и доставки документов счетов. Оно не: - Отслеживает платежи или статус платежа за пределами базовых флагов "unpaid/issued" - Управляет дебиторской или кредиторской задолженностью - Формирует финансовые отчеты или балансы - Интегрируется с бухгалтерскими системами (главные книги, журналы и т. п.) - Выполняет бухгалтерию, сверку или налоговую отчетность Для комплексного финансового управления интегрируйте nullInvoice с отдельной бухгалтерской системой. Используйте этот сервис для генерации счетов, затем импортируйте их в вашу бухгалтерскую систему для учета и соответствия. ## Стек - Java 21, Spring Boot 3.5.3 - MariaDB + JPA - Thymeleaf (UI) - OpenHTMLToPDF (PDFBox) - OpenAPI на `/openapi`, Swagger UI на `/swagger` ## Предварительные требования **Для развертывания Docker (рекомендуется):** - Docker - Docker Compose **Для локальной разработки:** - Java 21 (JDK - Eclipse Temurin или OpenJDK) - Maven 3.9+ - MariaDB 10.5+ (или MySQL 8.0+) - Standalone-бинарник Tailwind CSS (для сборки CSS - из [GitHub releases](https://github.com/tailwindlabs/tailwindcss/releases)) ## Настройка базы данных **Создайте базу данных:** ```sql CREATE DATABASE nullinvoice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` **Создайте выделенного пользователя приложения с ограниченными правами:** ```sql CREATE USER 'nullinvoice'@'localhost' IDENTIFIED BY 'your_secure_password'; -- Права для обычных операций и миграций схемы GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'localhost'; GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'localhost'; FLUSH PRIVILEGES; ``` **Для удаленного доступа настройте хост:** ```sql CREATE USER 'nullinvoice'@'%' IDENTIFIED BY 'your_secure_password'; GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'%'; GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'%'; FLUSH PRIVILEGES; ``` **Важно: почему такие ограниченные права?** Счета - **неизменяемые финансовые документы**, которые никогда не должны удаляться после создания. Пользователь приложения намеренно ограничен и не может: - `DELETE` - не может удалять записи - `DROP` - не может удалять таблицы или базу данных - `TRUNCATE` - не может очищать таблицы - `GRANT` - не может выдавать права другим Это обеспечивает целостность данных и соответствие требованиям хранения финансовых записей. Право `UPDATE` предоставляется для изменения статуса (например, пометить счет как оплаченный) и soft delete для записей сторон. ### Управление схемой - Схема базы данных управляется миграциями **Flyway** - Начальная схема: `nullInvoice/src/main/resources/db/migration/V1__initial_schema.sql` - Схема автоматически создается/обновляется при запуске приложения - Режим Hibernate DDL установлен в `none` (Flyway управляет всеми изменениями схемы) ## Конфигурация Пример конфигурационного файла находится в `.env.example`. Скопируйте его в `.env` и настройте значения под вашу среду. Переменные окружения (значения по умолчанию в скобках): - `TZ` - **ОБЯЗАТЕЛЬНО** системный часовой пояс (Europe/Sofia) - `APP_PORT` (8080) - `DB_HOST` (localhost) - `DB_PORT` (3306) - `DB_USER` (nullinvoice) - `DB_PASSWORD` (пусто) - `DB_NAME` (nullinvoice) - `DB_PARAMS` - **ОБЯЗАТЕЛЬНО** параметры JDBC, добавляемые к URL подключения ### **ВАЖНО: Настройка часового пояса** **НУЖНО установить часовой пояс в ДВУХ местах:** 1. **`TZ`** переменная окружения - задает системный/приложенческий часовой пояс 2. **`serverTimezone`** параметр в `DB_PARAMS` - задает часовой пояс подключения к базе данных **Оба значения ДОЛЖНЫ совпадать с часовым поясом сервера базы данных**, чтобы корректно интерпретировать и сохранять дату/время. Пример конфигурации: ```bash TZ=Europe/Sofia DB_PARAMS=?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Sofia ``` Распространенные часовые пояса: `UTC`, `Europe/London`, `America/New_York`, `Asia/Tokyo`. См. [полный список](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). **Несовпадающие часовые пояса приведут к неверным датам и временным меткам счетов.** ### Первоначальная настройка администратора При первом запуске приложение перенаправит на `/setup` для создания начальной учетной записи администратора. Это необходимо до доступа к остальному функционалу. **Чтобы сбросить и создать новую учетную запись администратора (экстренное восстановление):** 1. Остановите приложение 2. Очистите таблицы `users` и `api_keys` в базе данных 3. Перезапустите приложение 4. Пройдите `/setup` снова **Рекомендации по безопасности:** - Используйте надежный пароль для учетной записи администратора - Установите подсказку пароля (необязательно, но рекомендуется) - Генерируйте отдельные API-ключи для разных окружений (dev, staging, prod) - Отзывайте неиспользуемые API-ключи ## Безопасность и лучшие практики **Архитектура аутентификации:** - **Доступ к UI:** Аутентификация по сессии с form login - **Доступ к API:** Stateless Bearer token аутентификация (API-ключи) - **Пароли:** BCrypt с 10 раундами - **API-ключи:** UUID-формат, хешируются BCrypt, показываются один раз при генерации **Время сессии:** По умолчанию сессии UI истекают после **30 минут неактивности** (по умолчанию для Spring Boot/Tomcat). Пользователи будут автоматически выведены из системы и перенаправлены на страницу входа. Чтобы настроить время сессии, добавьте следующее в `nullInvoice/src/main/resources/application.yml`: ```yaml server: servlet: session: timeout: 60m # Варианты: 15m, 30m, 1h, 2h, и т.д. ``` Распространенные значения timeout: - `15m` - 15 минут (более строгая безопасность) - `30m` - 30 минут (по умолчанию) - `1h` - 1 час (удобство для активных пользователей) - `8h` - 8 часов (расширенное для длительных сессий) **Чек-лист для продакшн:** - Включить HTTPS/TLS для всех соединений - Использовать надежный пароль администратора - Создавать отдельные API-ключи для каждого приложения/окружения - Развертывать за firewall или VPN - Настроить rate limiting на уровне reverse proxy - Настроить корректное логирование и мониторинг - Регулярно проверять использование API-ключей (timestamp последнего использования) - Немедленно отзывать неиспользуемые или скомпрометированные API-ключи - Хранить API-ключи в переменных окружения, никогда в коде - Использовать безопасное хранение секретов (HashiCorp Vault, AWS Secrets Manager и др.) **Защита CSRF:** - Включена для всех UI-форм - Отключена для API-эндпоинтов (stateless Bearer token auth) **Безопасность при первом запуске:** - Приложение недоступно, пока не будет создана учетная запись администратора через `/setup` - Страница настройки доступна только если администратор еще не создан - После настройки требуется вход для всех функций ## Docker Compose **Официальные Docker-образы:** Готовые образы доступны на [Docker Hub](https://hub.docker.com). Найдите официальный образ nullInvoice, чтобы пропустить шаг сборки. Сборка без кеша: ```bash docker compose build --no-cache ``` Запуск стека: ```bash docker compose up -d ``` Остановка стека: ```bash docker compose down ``` ## Локальная разработка (без Docker) ### Настройка 1. Убедитесь, что MariaDB запущена, и создайте базу данных. См. [Настройка базы данных](#настройка-базы-данных) 2. Сборка Tailwind CSS (нужно перед первым запуском): ```bash ./build-tailwind.sh ``` 3. Сборка проекта с Maven: ```bash cd nullInvoice mvn clean package ``` 4. Запуск приложения: ```bash java -jar target/nullinvoice-0.0.1-SNAPSHOT.jar ``` ### Использование NetBeans IDE Проект собран в NetBeans и может быть открыт как Maven-проект с плагином Spring Boot. **Настройка переменных окружения в NetBeans:** Вариант 1 - через IDE: 1. Правой кнопкой по проекту >> Properties 2. Перейдите в Actions >> Run 3. Укажите переменные окружения: `APP_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` Вариант 2 - редактирование напрямую: - Измените `nbactions.xml` в корне проекта ## Начало работы ### Настройка при первом запуске При первом доступе вы будете перенаправлены на `/setup` для создания начальной учетной записи администратора: 1. Перейдите на `http://localhost:8080` (или ваш настроенный URL) 2. Вы будете автоматически перенаправлены на `/setup` 3. Создайте учетную запись администратора с именем пользователя, паролем и необязательной подсказкой 4. После настройки вы будете перенаправлены на `/login` **Вход:** - Откройте страницу входа `/login` - Используйте учетные данные администратора, которые вы создали - Подсказка пароля доступна через иконку info (если настроена) **Панель администратора:** После входа получите доступ к панели администратора из меню пользователя: - Изменить пароль администратора - Сгенерировать API-ключи для доступа к REST API - Отозвать API-ключи - Просмотреть использование API-ключей (timestamp последнего использования) ### Настройка первого поставщика **Предварительные условия:** - Завершена первоначальная настройка (учетная запись администратора создана) - Вход с учетными данными администратора Прежде чем вы сможете генерировать счета через API, необходимо настроить хотя бы одного поставщика через веб-интерфейс. Поставщики определяют данные компании, локаль, валюту, ставки налогов, пользовательский брендинг (через шаблоны) и нумерацию счетов для генерации. 1. Войдите в веб-интерфейс по адресу `http://localhost:8080` (или на вашем порту) 2. Перейдите в Поставщики 3. Создайте нового поставщика с данными компании 4. Настройте локаль, валюту и налоговые параметры 5. Установите параметры нумерации счетов (префикс, padding) 6. Запишите ID поставщика для API-интеграции 7. Сгенерируйте API-ключ в Администратор > API-ключи для доступа к REST API **Для примеров настройки поставщика см. `example-images` - `en-us` для примера США или не ЕС; и `eu-de` для примера ЕС.** После настройки вы можете создать XHTML-шаблон и использовать ID поставщика (показан в меню редактирования поставщика), чтобы выполнять вызовы к `/api/v1/invoices/generate`. ## Возможности - Управление поставщиками и клиентами с soft delete и проверками уникальности. - Создание и управление шаблонами счетов с пользовательским брендингом, с глобальным шаблоном по умолчанию и по поставщику. - Генерация счетов с сохраненными HTML-снимками в записи счета. - Рендеринг счетов в PDF по запросу. - Поиск и сортировка счетов по номеру, дате, клиенту, поставщику и статусу. - Управление статусом счета как `unpaid` или `issued` (оплачен/финальный). ## Жизненный цикл и статус счетов - Значения статуса: `unpaid` и `issued`. `issued` считается оплаченным и финальным. - Создание счета через API всегда приводит к статусу `issued`, и API не принимает переопределения статуса. - Создание счета через UI может пометить счет как `unpaid` только при установленном сроке оплаты. - Неоплаченные счета можно пометить как `issued` на странице деталей счета. - Выставленные счета нельзя вернуть в `unpaid`. ## Поведение UI - `/invoices/new` создает счета с использованием поставщика, выбранного из cookie (если есть). - Переключатель `unpaid` отключен, пока не задан срок оплаты. - `/invoices` поддерживает фильтрацию по поставщику (dropdown) и поиск по номеру, дате или клиенту. Поиск по дате принимает ISO (`YYYY-MM-DD`) или `dd.MM.yyyy`. - Список счетов поддерживает сортировку по статусу; сортировка по статусу переключает порядок `unpaid` и `issued`. - `/invoices/{invoiceNumber}` показывает статус, итоги, сохраненный HTML-предпросмотр и предоставляет одностороннее действие «Отметить как оплаченный» для неоплаченных счетов. ## Рабочий процесс UI 1) Поставщики: сначала настройте данные поставщика. Профиль поставщика определяет локаль, валюту, нумерацию счетов и ставку налога по умолчанию. 2) Шаблоны: создайте шаблон для пользовательского брендинга и установите его по умолчанию. Используйте глобальный шаблон по умолчанию или задайте шаблон по поставщику, чтобы переопределить глобальный выбор. 3) Клиенты (опционально): клиентов можно добавлять вручную, но генерация счетов также создает/обновляет клиентов из введенных данных. 4) Выбор активного поставщика: выберите поставщика по умолчанию в UI, что задает cookie, используемую при создании счетов. 5) ID поставщика для API: откройте поставщика в режиме редактирования и используйте ID, показанный в левом верхнем углу страницы. 6) Счета: список, поиск и фильтрация счетов; откройте счет, чтобы посмотреть детали и отметить неоплаченные счета как выставленные/оплаченные. 7) Генерация счета: введите данные клиента или найдите существующего клиента, добавьте позиции и задайте налог для каждой позиции. Если позиция без налога, применяется ставка поставщика по умолчанию. 8) Скидки и примечания: введите фиксированную скидку или используйте калькулятор % скидки; добавьте примечания и сгенерируйте счет, чтобы увидеть страницу обзора. Генерация счетов использует пессимистичную блокировку записи поставщика, чтобы избежать гонок при вычислении следующего номера счета. Это блокирует параллельные запросы для одного и того же поставщика, пока номер не назначен. ## Аутентификация **Требуется аутентификация:** Все API-эндпоинты требуют: - Bearer token в заголовке `Authorization` (рекомендуется для интеграций) - Активную сессию (если вы вошли через веб-интерфейс) ### Аутентификация по API-ключу (рекомендуется для внешних интеграций) Сгенерируйте API-ключ в панели администратора и укажите его в заголовке `Authorization`: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices ``` ### Сессионная аутентификация (для запросов, инициированных UI) Если вы вошли через веб-интерфейс, ваша сессия автоматически используется для API-запросов. **Генерация API-ключа:** 1. Войдите в веб-интерфейс 2. Перейдите в Администратор (меню пользователя) 3. Прокрутите до раздела "API-ключи" 4. Введите необязательное описание и нажмите "Сгенерировать ключ" 5. **Скопируйте ключ сразу** - он не будет показан повторно 6. Ключ отображается в формате `Authorization: Bearer {key}` **Примечания по безопасности:** - API-ключи хранятся в базе данных в виде хеша (BCrypt) - Ключи можно отозвать в любое время из панели администратора - Для каждого ключа отслеживается timestamp последнего использования - Генерируйте отдельные ключи для разных приложений/окружений ## REST API (База: `/api/v1`) ### Генерация счетов `POST /api/v1/invoices/generate` **Требуется аутентификация** - укажите Bearer token в заголовке `Authorization`. - Требуются `supplier_id` и `client`. - `response_type` поддерживает `number` (по умолчанию) или `pdf`. - `number`: возвращает JSON только с метаданными счета - `pdf`: возвращает PDF напрямую с метаданными в заголовках ответа - Статус всегда `issued` для счетов, созданных через API. Пример запроса (существующий клиент по id): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "number", "supplier_id": 1, "client": { "id": 42 }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ], "issue_date": "2026-01-16", "due_date": "2026-01-30", "currency_code": "EUR", "notes": "Thank you" }' ``` Пример запроса (новые данные клиента): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "number", "supplier_id": 1, "client": { "name": "Client Co", "addressLine1": "2 Side St", "city": "Burgas", "country": "BG", "taxId": "123", "vatId": "BG123" }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' ``` Пример ответа (response_type: number): ```json { "status": "issued", "message": "invoice generated", "invoiceNumber": "INV-000001", "issueDate": "2026-01-16" } ``` Пример запроса (response_type: pdf для прямой загрузки PDF): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "pdf", "supplier_id": 1, "client": { "name": "Client Co", "addressLine1": "2 Side St", "city": "Plovdiv", "country": "BG", "taxId": "123", "vatId": "BG123" }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' \ -o invoice.pdf -i ``` Пример ответа (response_type: pdf): ``` HTTP/1.1 200 OK Content-Type: application/pdf Content-Disposition: attachment; filename="INV-000001.pdf" X-Invoice-Number: INV-000001 X-Invoice-Status: issued X-Invoice-Issue-Date: 2026-01-16 [PDF binary data] ``` PDF-ответ включает метаданные счета в пользовательских заголовках ответа (`X-Invoice-Number`, `X-Invoice-Status`, `X-Invoice-Issue-Date`), позволяя вашему приложению сохранять детали счета, одновременно получая PDF напрямую. ### Список и фильтрация счетов `GET /api/v1/invoices` **Требуется аутентификация** - укажите Bearer token в заголовке `Authorization`. - Опциональный фильтр: `status=unpaid` или `status=issued` Пример запроса: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices?status=unpaid ``` Пример ответа (фильтр): ```json [ { "invoiceNumber": "INV-000002", "status": "unpaid" } ] ``` ### Получение счета **Требуется аутентификация** - укажите Bearer token в заголовке `Authorization`. Получение метаданных счета: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001 ``` Скачать PDF счета: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001/pdf \ -o invoice.pdf ``` ### Асинхронная очередь генерации счетов Альтернатива `POST /api/v1/invoices/generate` для вызывающих, которые не хотят блокироваться в ожидании синхронной генерации. Запрос сохраняется в очередь, фоновый обработчик создаёт счёт, а клиенты опрашивают статус. Оба пути проходят через одну и ту же блокировку строки поставщика, поэтому номера счетов остаются последовательными между синхронным и асинхронным путём. #### Отправка запроса на генерацию `POST /api/v1/invoice-requests` **Требуется аутентификация** - укажите Bearer token в заголовке `Authorization`. Тело запроса идентично `POST /api/v1/invoices/generate`. Поле `response_type` игнорируется - асинхронные запросы только сохраняют строку в очереди; после завершения извлеките счёт через стандартные endpoint'ы с использованием `invoiceNumber`, возвращённого в ответе о статусе. ```bash curl -X POST http://localhost:8080/api/v1/invoice-requests \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "supplier_id": 1, "client": { "id": 42 }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' ``` Ответ (201 Created): ```json { "requestId": 42, "status": "PENDING", "message": "invoice generation request queued", "createdAt": "2026-05-17T10:00:00" } ``` #### Проверка статуса запроса `GET /api/v1/invoice-requests/{requestId}` ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoice-requests/42 ``` Ответ при `COMPLETED`: ```json { "requestId": 42, "status": "COMPLETED", "invoiceId": 107, "invoiceNumber": "INV-000042", "attempts": 1, "createdAt": "2026-05-17T10:00:00", "completedAt": "2026-05-17T10:00:02" } ``` #### Жизненный цикл статусов ``` новый запрос -> PENDING обработчик успешно завершил -> COMPLETED обработчик выбросил ошибку -> PENDING (будет повторная попытка) attempts >= max_attempts -> FAILED (терминальное состояние) ``` `PENDING` - единственное состояние, допускающее повторную обработку. Сбои во время обработки откатываются полностью и не расходуют бюджет повторов. После `COMPLETED` извлеките счёт через `GET /api/v1/invoices/{invoiceNumber}` (JSON) или `/api/v1/invoices/{invoiceNumber}/pdf` (PDF). Обработчик можно отключить через переменную окружения `QUEUE_ENABLED=false`. ### Стороны **Требуется аутентификация** - укажите Bearer token в заголовке `Authorization`. - `GET /api/v1/parties/client?taxId=...&vatId=...` (нужно одно из taxId/vatId) - `GET /api/v1/parties/clients/search?q=...` (минимум 2 символа) - `GET /api/v1/parties/suppliers` Пример: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/parties/suppliers ``` ### Health - `GET /api/v1/health` (аутентификация не требуется) ## Шаблоны и PDF - Шаблоны находятся в `invoice_templates` и должны включать HTML-контент. - Генерация счетов требует действующего шаблона по умолчанию (по поставщику или глобального). - Поставщики могут переопределять глобальный шаблон по умолчанию на шаблон поставщика. - Сгенерированные счета сохраняют HTML-снимок для последовательного повторного рендеринга. - PDF рендерятся из сохраненного HTML-снимка, когда он доступен. ### Требования к формату шаблона Шаблоны должны быть в формате **XHTML** для корректного PDF-рендеринга: - Включите XML-декларацию: `` - Используйте XHTML namespace: `` - Весь CSS должен быть **inline** внутри тега `