npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vv-iiko-pp-parser

v1.0.11

Published

Unofficial iiko Partner Portal parser with session handling and Chain support

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/:

🛡️ 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.