#
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. **Доставка**: Вашето приложение получава фактурата и може да я препрати към клиента или да я съхрани за архив
### Типичен интеграционен поток
```
┌─────────────────┐
│ Клиент │
│ завършва │
│ покупка │
└────────┬────────┘
│
▼
┌─────────────────────────────────────────┐
│ Вашият уебмагазин/SaaS платформа │
│ ───────────────────────────────────── │
│ 1. Обработва плащане │
│ 2. Издава дигитална бележка (нужно) │
│ 3. Клиент иска фактура? ────────────┐ │
└──────────────────────────────────────┼──┘
│
│ API заявка
▼
┌──────────────────────────────┐
│ Услуга nullInvoice │
│ ────────────────────────── │
│ POST /api/v1/invoices/ │
│ generate │
│ │
│ - Валидира ID на доставчик │
│ - Прилага шаблон │
│ - Записва HTML snapshot │
│ - Генерира PDF │
│ - Връща фактура │
└──────────────┬───────────────┘
│
│ Отговор (JSON или PDF)
▼
┌──────────────────────────────────────────┐
│ Вашият уебмагазин/SaaS платформа │
│ ────────────────────────────────────── │
│ - Получава JSON метаданни ИЛИ PDF файл │
│ - Записва номера на фактурата за архив │
│ - Изпраща PDF до клиента по имейл │
└──────────────────────────────────────────┘
```
### Ключови функции
**Напълно персонализируеми шаблони**
- Фактурите са базирани на HTML шаблони, дефинирани от потребителя, с inline CSS
- Шаблоните поддържат 30+ плейсхолдера за доставчик, клиент и финансови данни
- Шаблоните по подразбиране за всеки доставчик позволяват различно брандиране по бизнес единици
**Непроменяемост на документите**
- Всяка фактура съхранява **snapshot на парснатия HTML шаблон** при генериране
- Промените по шаблона не засягат вече генерираните фактури
- Фактурите могат да се регенерират последователно от запазения snapshot
- Правата в базата данни не позволяват изтриване на записи за фактури (финансово съответствие)
**Готово за мулти-тенант среда**
- Конфигурирайте множество доставчици с независими настройки (локал, валута, номерация, брандинг)
- API потребителите посочват ID на доставчика за генериране на фактури под различни бизнес единици
**Гъвкава доставка**
- Връщане на метаданни за фактурата като JSON за архивиране (`response_type: number`)
- Директно връщане на PDF с метаданни в заглавките за незабавна доставка (`response_type: pdf`)
- Изтегляне на PDF по-късно чрез `/api/v1/invoices/{invoiceNumber}/pdf`
**Асинхронна опашка за генериране**
- Алтернативен път за интеграции, които не искат да изчакват синхронно генериране
- Заявката се записва в опашка, фонов работник я обработва, клиентът проверява статуса чрез poll
- Същите гаранции за номерация на фактурите като синхронния път (общо заключване на доставчика)
- Работникът може да се изключи чрез `QUEUE_ENABLED=false` (по подразбиране `true`)
**OpenAPI документация**
- Интерактивна API документация на `/swagger`
- OpenAPI JSON спецификация на `/openapi`
- Пълни примери за заявки/отговори и интерактивно тестване
## Важни бележки
**⚠️ Бележка за сигурността**
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 е **приложение за генериране на фактури**, предназначенo да създава, съхранява и доставя документи за фактури. То не:
- Следи плащания или статус на плащане извън базови флагове "неплатена/издадена"
- Управлява вземания или задължения
- Генерира финансови отчети или баланси
- Интегрира се със счетоводни системи (главни книги, дневници и др.)
- Обработва счетоводство, реконсилация или данъчни декларации
За пълно финансово управление интегрирайте 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+)
- Tailwind CSS standalone binary (за билд на 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 permissions for normal operations and schema migrations
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` (empty)
- `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 токен автентикация (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 токен автентикация)
**Сигурност при първо стартиране:**
- Приложението е неизползваемо, докато не се създаде администраторски акаунт чрез `/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` в root директорията на проекта
## Първи стъпки
### Първоначална настройка при първо стартиране
При първи достъп ще бъдете пренасочени към `/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. Задайте предпочитания за номерация на фактури (префикс, попълване)
6. Запишете ID на доставчика за API интеграция
7. Генерирайте API ключ от Админ > API ключове за REST API достъп
**За примери по настройка на доставчик вижте `docs/example-images` - `en-us` за US или извън ЕС пример; и `eu-de` за пример от ЕС.**
След като е конфигуриран, можете да настроите XHTML шаблон и да използвате ID на доставчика (показан в Edit Supplier менюто), за да правите API заявки към `/api/v1/invoices/generate`.
## Възможности
- Управление на доставчици и клиенти със soft delete и проверки за уникалност.
- Създаване и управление на шаблони за фактури с персонализирано брандиране, с глобален шаблон по подразбиране и по доставчик.
- Генериране на фактури със запазени HTML snapshot-и в записа на фактурата.
- Рендериране на фактури в PDF при поискване.
- Търсене и сортиране на фактури по номер, дата, клиент, доставчик и статус.
- Проследяване на статус на фактурите като `неплатена` или `издадена` (платена/финална).
## Жизнен цикъл и статус на фактурите
- Статусите са `неплатена` и `издадена`. `издадена` се счита за платена и финална.
- Създаване на фактура през API винаги резултира в `издадена` статус и API не приема override на статус.
- Създаване на фактура през UI може да маркира фактура като `неплатена` само когато е зададена дата на падеж.
- Неплатените фактури могат да бъдат маркирани като `издадена` от страницата с детайли на фактурата.
- Издадени фактури не могат да бъдат върнати към `неплатена`.
## Поведение на UI
- `/invoices/new` създава фактури според избрания доставчик от бисквитка (ако има такава).
- Превключвателят за неплатена е деактивиран, докато не се зададе дата на падеж.
- `/invoices` поддържа филтриране по доставчик (dropdown) и търсене по номер, дата или клиент. Търсенето по дата приема ISO (`YYYY-MM-DD`) или `dd.MM.yyyy`.
- Сортирането на списъка с фактури включва статус; сортирането по статус превключва реда `неплатена` и `издадена`.
- `/invoices/{invoiceNumber}` показва статус, суми, запазен HTML преглед и предлага еднопосочно действие „Маркирай като платена“ за неплатени фактури.
## Работен поток в UI
1) Доставчици: първо настройте данните на доставчика. Профилът на доставчика определя локал, валута, номерация на фактури и стандартна данъчна ставка.
2) Шаблони: създайте шаблон за персонализирано брандиране и задайте по подразбиране. Използвайте глобален шаблон по подразбиране или задайте по доставчик, за да замените глобалния избор.
3) Клиенти (по избор): можете да добавяте клиенти ръчно, но генерирането на фактури също създава/обновява клиенти от въведените данни.
4) Избор на активен доставчик: изберете доставчик по подразбиране в UI, което задава бисквитка, използвана при създаване на фактури.
5) ID на доставчик за API: отворете доставчик в режим за редакция и използвайте ID-то, показано в горния ляв ъгъл на страницата.
6) Фактури: списък, търсене и филтриране на фактури; отворете фактура, за да прегледате детайлите и да маркирате неплатени фактури като издадени/платени.
7) Генериране на фактура: въведете данни на клиент или потърсете съществуващ клиент, добавете редове и задайте данък за всеки ред. Ако в даден ред липсва данък, се прилага стандартната данъчна ставка на доставчика.
8) Отстъпки и бележки: въведете фиксирана отстъпка или използвайте калкулатора за % отстъпка; добавете бележки и генерирайте фактурата, за да видите обзорната страница.
Генерирането на фактури използва песимистично заключване за запис върху записа на доставчика, за да се избегнат състезателни условия при изчисляване на следващия номер на фактура. Това блокира конкурентни заявки за същия доставчик, докато номерът не бъде присвоен.
## Автентикация
**Изисква се автентикация:** всички API крайни точки изискват или:
- Bearer токен в `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. Въведете опционално описание и кликнете "Generate Key"
5. **Копирайте ключа веднага** - няма да бъде показан отново
6. Ключът се показва във формат `Authorization: Bearer {key}`
**Бележки за сигурността:**
- API ключовете са хеширани в базата данни (BCrypt)
- Ключовете могат да бъдат отменени по всяко време от администраторското табло
- Времето на последна употреба се проследява за всеки ключ
- Генерирайте отделни ключове за различни приложения/среди
## REST API (Base: `/api/v1`)
### Генериране на фактури
`POST /api/v1/invoices/generate`
**Изисква се автентикация** - включете Bearer токен в `Authorization` заглавката.
- Изисква `supplier_id` и `client`.
- `response_type` поддържа `number` (по подразбиране) или `pdf`.
- `number`: връща JSON само с метаданни за фактурата
- `pdf`: връща PDF файла директно с метаданни в заглавките на отговора
- Статусът е винаги `издадена` за фактури, генерирани през 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 токен в `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 токен в `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` за клиенти, които не искат да блокират изчакването за приключване на генерирането. Заявката се записва в опашка, фонов работник генерира фактурата, а клиентите проверяват статуса чрез poll.
И двата пътя преминават през едно и също заключване на доставчика, така че номерата на фактурите остават последователни между синхронен и асинхронен път.
#### Подаване на заявка за генериране
`POST /api/v1/invoice-requests`
**Изисква се автентикация** - включете Bearer токен в `Authorization` header.
Тялото на заявката е идентично с `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 токен в `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
```
### Здраве
- `GET /api/v1/health` (без автентикация)
## Шаблони и PDF-и
- Шаблоните са в `invoice_templates` и трябва да съдържат HTML съдържание.
- Генерирането на фактури изисква ефективен шаблон по подразбиране (по доставчик или глобален).
- Доставчиците могат да заменят глобалния шаблон по подразбиране с такъв по доставчик.
- Генерираните фактури съхраняват HTML snapshot за последователно пререндериране.
- PDF-ите се рендерират от запазения HTML snapshot, когато е наличен.
### Изисквания към формата на шаблона
Шаблоните трябва да са във формат **XHTML** за правилно PDF рендериране:
- Включете XML декларация: ``
- Използвайте XHTML namespace: ``
- Целият CSS трябва да е **inline** в `