Files
portal/app/playwright/supplier-balance.js
T
Дмитрий 22ad20337a feat(балансы): баланс поставщика = остаток номеров × 20 ₽
У кабинета crm.bp-gr нет денежного баланса — есть «Баланс ГЦК» (остаток номеров)
в выпадашке шапки (table.balancetbl). supplier-balance.js логинится, раскрывает
выпадашку, читает «Баланс ГЦК» -> {numbers}. Провайдер: деньги = numbers ×
number_price_rub (20 ₽/шт, подтверждено владельцем). Live: 3096 -> 61 920 ₽.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 07:54:10 +03:00

104 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Headless Playwright чтение остатка НОМЕРОВ в кабинете поставщика crm.bp-gr.ru.
*
* У кабинета нет денежного баланса: в шапке выпадашка «Баланс» (js-dropdown,
* data-toggle="dropdown") с таблицей table.balancetbl. Нужная строка —
* «Баланс ГЦК» = количество доступных номеров (подтверждено владельцем 28.06).
* Деньги считаются на стороне PHP: номера × number_price_rub (20 ₽/шт).
*
* Логин-флоу — копия refresh-session.js (Yii2 LoginForm).
*
* Input (JSON через stdin): {login, password, url}
* Output (JSON через stdout): {numbers: <int>}
*
* Exit codes:
* 0 — success (число номеров найдено)
* 1 — auth failed (логин/пароль отклонены)
* 2 — строка «Баланс ГЦК» не найдена (разметка кабинета изменилась)
* 3 — timeout (60s)
* 4 — invalid input или другая ошибка
*/
const { chromium } = require('playwright');
const TIMEOUT_MS = 60_000;
async function readNumbers(args) {
let browser = null;
try {
browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(args.url, { waitUntil: 'load', timeout: TIMEOUT_MS });
const loginSelector = '#loginform-username';
await page.fill(loginSelector, args.login);
await page.fill('#loginform-password', args.password);
await page.click('button[type=submit]');
await page
.waitForFunction((sel) => !document.querySelector(sel), loginSelector, { timeout: TIMEOUT_MS })
.catch(() => {});
await page.waitForLoadState('networkidle', { timeout: TIMEOUT_MS }).catch(() => {});
if ((await page.locator(loginSelector).count()) > 0) {
process.stderr.write(JSON.stringify({ error: 'login rejected: still on login page after submit' }));
process.exit(1);
}
// Раскрыть выпадашку «Баланс» (js-dropdown в шапке), затем прочитать table.balancetbl.
await page.getByText('Баланс', { exact: true }).first().click().catch(() => {});
await page.waitForTimeout(1500);
// Найти в таблице баланса строку «Баланс ГЦК» и извлечь число.
const numbers = await page.evaluate(() => {
const rows = Array.from(document.querySelectorAll('table.balancetbl tr'));
for (const tr of rows) {
const cells = tr.querySelectorAll('td');
if (cells.length >= 2 && /Баланс\s*ГЦК/i.test(cells[0].textContent || '')) {
const raw = (cells[1].textContent || '').replace(/[^\d-]/g, '');
const n = parseInt(raw, 10);
return Number.isFinite(n) ? n : null;
}
}
return null;
});
if (numbers === null) {
process.stderr.write(JSON.stringify({ error: 'строка «Баланс ГЦК» не найдена (разметка кабинета изменилась)' }));
process.exit(2);
}
process.stdout.write(JSON.stringify({ numbers }));
process.exit(0);
} catch (err) {
process.stderr.write(JSON.stringify({ error: err.message }));
process.exit(err.message && err.message.includes('Timeout') ? 3 : 4);
} finally {
if (browser) {
await browser.close();
}
}
}
let input = '';
process.stdin.on('data', (chunk) => { input += chunk; });
process.stdin.on('end', () => {
let args;
try {
args = JSON.parse(input);
} catch (e) {
process.stderr.write(JSON.stringify({ error: 'invalid JSON on stdin' }));
process.exit(4);
}
if (!args.login || !args.password || !args.url) {
process.stderr.write(JSON.stringify({ error: 'missing required keys: login, password, url' }));
process.exit(4);
}
readNumbers(args).catch((err) => {
const message = err && err.message ? err.message : String(err);
process.stderr.write(JSON.stringify({ error: message }));
process.exit(4);
});
});