vv-iiko-pp-parser
v1.0.11
Published
Unofficial iiko Partner Portal parser with session handling and Chain support
Maintainers
Readme
vv-iiko-pp-parser
🇺🇿 Unofficial SDK for iiko Partner Portal API — Node.js клиент для получения данных инвойсов из партнерского портала iiko.
⚠️ Важно: Это частный пакет с ограниченным доступом. Для установки требуется авторизация в npm.
📋 Что это?
Этот пакет предоставляет простой API для работы с закрытым веб-интерфейсом iiko Partner Portal, позволяя автоматизировать получение:
- 📄 Инвойсов с детальной информацией
- 🏢 Данных компании и профиля
- 🔍 Фильтрации по различным критериям
🚀 Установка
Авторизация в npm
Перед установкой необходимо авторизоваться в npm:
npm loginУстановка пакета
npm install @mirzaev/vv-iiko-pp-parser⚙️ Конфигурация
Создайте .env файл с вашими учетными данными:
IIKO_PP_LOGIN=your_username
IIKO_PP_PASSWORD=your_password
# Опционально: пользовательский путь для хранения cookies
# По умолчанию используется: {projectRoot}/.vv-cookies
# VV_IIKO_COOKIES_DIR=/custom/path/to/cookies📂 Хранение cookies
Cookies сохраняются автоматически после авторизации для переиспользования между запусками.
Расположение по умолчанию: .vv-cookies/ в корне вашего проекта
Пользовательский путь:
const parser = new IikoParser({
login: process.env.IIKO_PP_LOGIN,
password: process.env.IIKO_PP_PASSWORD,
cookiesPath: '/custom/path/iiko.cookies.json' // Абсолютный путь к файлу
});Или через переменную окружения:
VV_IIKO_COOKIES_DIR=/custom/path/to/cookies🎯 Быстрый старт
import { IikoParser } from '@mirzaev/vv-iiko-pp-parser';
import 'dotenv/config';
const parser = new IikoParser({
baseUrl: 'https://pp.iiko.uz',
language: 'en',
login: process.env.IIKO_PP_LOGIN,
password: process.env.IIKO_PP_PASSWORD
});
// Инициализация сессии
await parser.ensureSession();
// Получение всех инвойсов за январь 2026
const invoices = await parser.getInvoices({
dateFrom: '2026-01-01',
dateTo: '2026-01-31'
});
console.log(`Найдено ${invoices.length} инвойсов`);🔍 Фильтрация данных
По периоду времени
Фильтрация по датам выполняется через параметры dateFrom и dateTo в формате YYYY-MM-DD:
const invoices = await parser.getInvoices({
dateFrom: '2026-01-01', // С какой даты
dateTo: '2026-01-31' // До какой даты (включительно)
});📝 Формат дат:
YYYY-MM-DD(например,2026-01-15)
API iiko автоматически конвертирует в форматdd.mm.yyyy
Загрузка деталей из модального окна
По умолчанию items и subscriptions остаются пустыми массивами. Чтобы загрузить детали из модальных окон, используйте флаг withDetails:
const invoices = await parser.getInvoices({
withDetails: true // 🔥 Загрузит items и subscriptions для каждого инвойса
});
// Теперь у каждого инвойса заполнены:
console.log(invoices[0].items); // [{ productName, price, quantity, discount, amount }]
console.log(invoices[0].subscriptions); // [{ productName, quantity, price, amount }]⚠️ Важно: Загрузка деталей выполняется последовательно для каждого инвойса, это может занять время при большом количестве инвойсов.
По tax_id компании
⚠️ Важно: Используйте формат с пробелами как в системе!
const invoices = await parser.getInvoices({
dateFrom: '2026-01-01',
dateTo: '2026-01-31',
searchBy: 'tax_id',
searchValue: '305 845 090' // ← Формат с пробелами!
});По номеру инвойса
const invoice = await parser.getInvoices({
dateFrom: '2025-01-01',
dateTo: '2026-01-31',
searchBy: 'invoice_number',
searchValue: 'INV3816690'
});По имени клиента
const invoices = await parser.getInvoices({
dateFrom: '2025-01-01',
dateTo: '2026-01-31',
searchBy: 'client',
searchValue: 'KURANT'
});По имени фирмы
const invoices = await parser.getInvoices({
dateFrom: '2025-01-01',
dateTo: '2026-01-31',
searchBy: 'firm',
searchValue: 'VEDA VECTOR'
});� Пагинация
По умолчанию загружаются все страницы инвойсов автоматически.
Вы можете управлять пагинацией:
// Ограничить количество страниц
const invoices = await parser.getInvoices({
pagination: {
maxPages: 2 // Загрузить только первые 2 страницы
}
});// Изменить размер страницы
const invoices = await parser.getInvoices({
pagination: {
pageSize: 100, // 100 инвойсов на страницу (по умолчанию: 50)
maxPages: 5 // Максимум 5 страниц
}
});// Без ограничений (поведение по умолчанию)
const invoices = await parser.getInvoices();
// Загрузит все страницы до пустой⚠️ Примечание: Пагинация останавливается автоматически при достижении пустой страницы, даже если лимит maxPages не достигнут.
�📊 Структура данных
InvoiceData
interface InvoiceData {
invoiceNumber: string; // Номер инвойса (например: "INV3816690")
invoiceDate: string; // Дата инвойса
dueDate: string; // Срок оплаты
clientName: string; // Название клиента
companyName: string; // Название компании
taxId: string; // Tax ID (только цифры)
status: string; // Статус (Issued, Paid, Cancelled)
amount: number; // Общая сумма (число)
detailsUrl: string; // URL для загрузки деталей из модального окна
items: InvoiceItem[]; // Элементы инвойса (заполняются при withDetails: true)
subscriptions: Subscription[]; // Подписки (заполняются при withDetails: true)
}InvoiceItem
interface InvoiceItem {
productName: string; // Название продукта
price: number; // Цена
quantity: number; // Количество
discount: number; // Скидка (%)
amount: number; // Сумма
}Subscription
interface Subscription {
productName: string; // Название продукта
quantity: number; // Количество
price: number; // Цена
amount: number; // Сумма
}CompanyProfile
interface CompanyProfile {
name: string; // Название компании
phone: string; // Телефон
email: string; // Email
address: string; // Адрес
}ClientData
interface ClientData {
uuid: string; // UUID клиента (формат: XXX-XXX-XXX)
name: string; // Название клиента
clientId: string; // ID клиента в системе
iikoVersion?: string; // Версия iiko (например: "9.2.6029")
email?: string; // Email клиента
phone?: string; // Телефон клиента
address?: string; // Адрес клиента
legalEntity?: string; // Юридическое лицо
tin?: string; // ИНН (только цифры)
}ClientsFilter
interface ClientsFilter {
query?: string; // Поиск по названию или UID
perPage?: number; // Количество клиентов на странице (по умолчанию: 20)
maxPages?: number; // Максимальное количество страниц (по умолчанию: все)
}InvoicesFilter
interface InvoicesFilter {
dateFrom?: string; // Дата начала (YYYY-MM-DD)
dateTo?: string; // Дата окончания (YYYY-MM-DD)
searchBy?: 'client' | 'firm' | 'tax_id' | 'invoice_number';
searchValue?: string; // Значение для поиска
}👥 Получение списка клиентов
// Получить всех клиентов с полной информацией
const allClients = await parser.getClients();
console.log(`Найдено ${allClients.length} клиентов`);
// Вывести информацию о первом клиенте
const client = allClients[0];
console.log({
name: client.name,
uuid: client.uuid,
iikoVersion: client.iikoVersion,
email: client.email,
phone: client.phone,
address: client.address,
legalEntity: client.legalEntity,
tin: client.tin
});Фильтрация клиентов
// Поиск по названию
const restaurantClients = await parser.getClients({
query: 'Restaurant'
});
// Поиск по UUID
const specificClient = await parser.getClients({
query: '212-191-451'
});
// Ограничение пагинации
const firstPage = await parser.getClients({
perPage: 20,
maxPages: 1
});
// Загрузить больше клиентов за раз
const manyClients = await parser.getClients({
perPage: 50,
maxPages: 5
});Обработка данных клиентов
const clients = await parser.getClients();
// Фильтрация клиентов с email
const clientsWithEmail = clients.filter(c => c.email);
// Группировка по версии iiko
const byVersion = clients.reduce((acc, client) => {
const version = client.iikoVersion || 'Unknown';
if (!acc[version]) acc[version] = [];
acc[version].push(client);
return acc;
}, {});
// Клиенты без ИНН
const withoutTin = clients.filter(c => !c.tin);
// Сохранение в файл
import fs from 'fs';
fs.writeFileSync('clients.json', JSON.stringify(clients, null, 2));🏢 Получение профиля компании
const profile = await parser.getCompanyProfile();
console.log(profile);
// {
// name: "VEDA VECTOR",
// phone: "+998901234567",
// email: "[email protected]",
// address: ""
// }🔧 Продвинутые примеры
Обработка ошибок
try {
await parser.ensureSession();
const invoices = await parser.getInvoices({
dateFrom: '2026-01-01',
dateTo: '2026-01-31'
});
console.log(`✅ Получено ${invoices.length} инвойсов`);
} catch (error) {
console.error('❌ Ошибка получения данных:', error.message);
}🔌 Интеграция с фреймворками
NestJS
import { Injectable } from '@nestjs/common';
import { IikoParser } from 'vv-iiko-pp-parser';
@Injectable()
export class IikoService {
private parser: IikoParser;
constructor() {
this.parser = new IikoParser({
baseUrl: 'https://pp.iiko.uz',
language: 'en',
login: process.env.IIKO_PP_LOGIN!,
password: process.env.IIKO_PP_PASSWORD!
});
}
async getRecentInvoices() {
await this.parser.ensureSession();
return this.parser.getInvoices({
dateFrom: '2026-01-01',
dateTo: '2026-01-31'
});
}
}🚨 Важные замечания
Форматы tax_id
Используйте точно такой же формат, как отображается в системе:
// ✅ Правильно
searchValue: '305 845 090' // С пробелами
searchValue: '302 987 179' // С пробелами
// ❌ Неправильно
searchValue: '305845090' // Без пробелов
searchValue: '302987179' // Без пробеловУправление сессиями
Пакет автоматически:
- 🍪 Сохраняет cookies в файл
- 🔄 Восстанавливает сессию при перезапуске
- ✅ Проверяет валидность сессии
- 🔐 Переавторизуется при необходимости
📚 Примеры кода
Полные рабочие примеры доступны в папке examples/:
basic.js— Базовое использованиеfiltering.js— Примеры фильтрацииnestjs-service.ts— Интеграция с NestJS
🛡️ TypeScript
Пакет полностью типизирован и экспортирует все необходимые типы:
import {
IikoParser,
IikoParserOptions,
InvoicesFilter,
InvoiceData,
CompanyProfile
} from 'vv-iiko-pp-parser';🐛 Устранение неполадок
Ошибки авторизации
❌ Ошибка: Invalid credentialsРешение: Проверьте правильность логина и пароля в .env файле.
Пустые результаты поиска
✅ Найдено 0 инвойсовВозможные причины:
- Неправильный формат
tax_id(должен быть с пробелами) - Неправильный диапазон дат
- Инвойсы отсутствуют за указанный период
Проблемы с сессиями
❌ Ошибка: Session expiredРешение: Пакет автоматически переавторизуется. Если проблема повторяется, очистите файл cookies:
rm -f .cookies.json� Progress Tracking
Вы можете подписаться на события прогресса выполнения для отслеживания состояния парсера:
const parser = new IikoParser({
baseUrl: 'https://pp.iiko.uz',
login: process.env.IIKO_PP_LOGIN,
password: process.env.IIKO_PP_PASSWORD,
onProgress: (event) => {
console.log(`[${event.stage}] ${event.message}`);
if (event.current && event.total) {
const progress = Math.round((event.current / event.total) * 100);
console.log(`Progress: ${progress}% (${event.current}/${event.total})`);
}
if (event.invoiceNumber) {
console.log(`Processing invoice: ${event.invoiceNumber}`);
}
}
});События прогресса
| Stage | Описание | Поля |
|-------|----------|------|
| session | Инициализация сессии | message |
| auth | Процесс авторизации | message |
| fetch_invoices | Загрузка страниц инвойсов | message, current, total |
| parse_invoices | Парсинг HTML страниц | message |
| fetch_invoice_details | Загрузка деталей инвойсов | message, current, total, invoiceNumber |
| fetch_clients | Загрузка страниц со списком клиентов | message, current, total |
| parse_clients | Парсинг списка клиентов | message, current, total |
| fetch_client_details | Загрузка профилей клиентов | message, current, total |
| done | Завершение работы | message |
Интеграция с WebSocket/SSE
// В NestJS с WebSocket
@WebSocketGateway()
export class ProgressGateway {
constructor(private parser: IikoParser) {
this.parser = new IikoParser({
// ... config
onProgress: (event) => {
this.server.emit('parser-progress', event);
}
});
}
}
// В Express с SSE
app.get('/parse-progress', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
});
const parser = new IikoParser({
// ... config
onProgress: (event) => {
res.write(`data: ${JSON.stringify(event)}\n\n`);
}
});
// Start parsing...
});📝 Лицензия
UNLICENSED
⚠️ Disclaimer
Это неофициальный пакет. Используйте в соответствии с условиями использования iiko Partner Portal.
