Files
hotelsync/src/lib/agent.ts
HotelSync fbb06265f9 feat: касса — полная переработка PosPage + интеграция с агентом
- PosPage.tsx: полный редизайн вокруг реальной кассовой работы
  • Автодетект агента (localhost:9000), статус смены каждые 15с
  • Экраны: агент не подключён / смена закрыта / смена истекла / касса
  • Шапка смены: №, длительность, остаток наличных, статус ОФД
  • Кнопки: X-отчёт, Внести/Изъять наличные, Закрыть смену
  • Левая панель: гости с балансом + быстрые услуги
  • Правая панель: чек с количеством, итого, выбор наличные/карта
  • Экран успеха после пробития чека с номером ФД
- agent.ts: клиент localhost:9000 — identity + все kkt операции
- index.css: btn-danger класс

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:55:12 +03:00

130 lines
3.8 KiB
TypeScript
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.
/**
* Клиент для общения с HotelSync Agent на localhost:9000
* Все кассовые операции идут напрямую через агент, минуя сервер
*/
const AGENT_URL = 'http://localhost:9000'
const TIMEOUT = 30000 // 30 секунд для кассовых операций
async function agentReq<T>(
method: 'GET' | 'POST',
path: string,
body?: object,
timeout = TIMEOUT,
): Promise<T> {
const res = await fetch(`${AGENT_URL}${path}`, {
method,
headers: body ? { 'Content-Type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(timeout),
})
return res.json() as Promise<T>
}
// ── Типы ──────────────────────────────────────────────────────────────────────
export interface AgentIdentity {
agent_id: string
workstation_id: string
workstation_name: string
hostname: string
error?: string
}
export interface ShiftStatus {
state: 'closed' | 'open' | 'expired'
shift_number: number
opened_at?: string
duration_min?: number
cash_sum?: number
receipt_count?: number
cashier_name?: string
}
export interface KktInfo {
serial_number: string
model: string
fn_number: string
fn_expires_at?: string
ofd_status: 'ok' | 'error' | 'no_connection'
version: string
}
export interface KktStatus {
shift?: ShiftStatus
info?: KktInfo
dto_connected: boolean
dto_version?: string
}
export interface CashierInfo {
name: string
inn?: string
}
export interface ReceiptItem {
name: string
quantity: number
price: number
vat?: 'none' | 'vat0' | 'vat10' | 'vat20'
payment_object?: 'commodity' | 'service'
}
export interface ReceiptData {
type: 'sell' | 'sell_return'
items: ReceiptItem[]
total: number
payment_type: 'cash' | 'card' | 'prepaid'
cashier?: CashierInfo
customer_email?: string
customer_phone?: string
tax_system?: number
}
export interface AgentResult {
ok: boolean
error?: string
receipt_number?: string
fiscal_sign?: string
shift_number?: number
}
// ── API ───────────────────────────────────────────────────────────────────────
/** Проверяет что агент запущен и возвращает рабочее место. Timeout 500ms. */
export async function getIdentity(): Promise<AgentIdentity | null> {
try {
const data = await agentReq<AgentIdentity>('GET', '/identity', undefined, 500)
if (data.error) return null
return data
} catch {
return null
}
}
export const kkt = {
status: () =>
agentReq<KktStatus>('GET', '/kkt/status', undefined, 5000),
openShift: (cashier: CashierInfo, tax_system = 1) =>
agentReq<AgentResult>('POST', '/kkt/shift/open', { cashier, tax_system }),
closeShift: (cashier: CashierInfo) =>
agentReq<AgentResult>('POST', '/kkt/shift/close', { cashier }),
xReport: () =>
agentReq<AgentResult>('POST', '/kkt/report/x', {}),
cashIn: (amount: number, cashier: CashierInfo) =>
agentReq<AgentResult>('POST', '/kkt/cash-in', { amount, cashier }),
cashOut: (amount: number, cashier: CashierInfo) =>
agentReq<AgentResult>('POST', '/kkt/cash-out', { amount, cashier }),
printReceipt: (data: ReceiptData) =>
agentReq<AgentResult>('POST', '/kkt/receipt', data),
printReturn: (data: ReceiptData) =>
agentReq<AgentResult>('POST', '/kkt/return', data),
}