feat: minibar — category dropdown, учёт остатков (приходы/списания/инвентаризация/отчёт)

- MinibarSettingsPage: поле категории → выпадающий список с существующими + «Новая...»
- Кнопка «Учёт и остатки» ведёт на /settings/minibar/stock
- MinibarStockPage: 5 вкладок — Остатки, Приходы, Списания, Инвентаризация, Отчёт
- api.ts: добавлены методы и типы для stock/receipts/writeoffs/inventory/report

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 15:36:40 +03:00
parent 5b46175c91
commit 1f4def0a34
7 changed files with 1340 additions and 26 deletions

View File

@@ -740,6 +740,39 @@ export const api = {
getBookingMinibar: (slug: string, bookingId: string) =>
req<MinibarBookingCharge[]>('GET', `/api/hotels/${slug}/bookings/${bookingId}/minibar`),
getStock: (slug: string) =>
req<MinibarStockItem[]>('GET', `/api/hotels/${slug}/minibar-stock`),
getReceipts: (slug: string) =>
req<MinibarReceipt[]>('GET', `/api/hotels/${slug}/minibar-receipts`),
createReceipt: (slug: string, data: { supplier?: string; notes?: string; items: Array<{ itemId: string; quantity: number; costPrice?: number }> }) =>
req<MinibarReceipt>('POST', `/api/hotels/${slug}/minibar-receipts`, data),
getWriteoffs: (slug: string) =>
req<MinibarWriteoff[]>('GET', `/api/hotels/${slug}/minibar-writeoffs`),
createWriteoff: (slug: string, data: { reason: string; notes?: string; items: Array<{ itemId: string; quantity: number }> }) =>
req<MinibarWriteoff>('POST', `/api/hotels/${slug}/minibar-writeoffs`, data),
getInventories: (slug: string) =>
req<MinibarInventoryCheck[]>('GET', `/api/hotels/${slug}/minibar-inventory`),
createInventory: (slug: string, data: { checkedAt?: string; notes?: string }) =>
req<MinibarInventoryCheck>('POST', `/api/hotels/${slug}/minibar-inventory`, data),
getInventory: (slug: string, checkId: string) =>
req<MinibarInventoryCheck>('GET', `/api/hotels/${slug}/minibar-inventory/${checkId}`),
updateInventoryItem: (slug: string, checkId: string, itemId: string, actualQty: number) =>
req<{ ok: boolean }>('PATCH', `/api/hotels/${slug}/minibar-inventory/${checkId}/items/${itemId}`, { actual_qty: actualQty }),
completeInventory: (slug: string, checkId: string) =>
req<{ ok: boolean }>('POST', `/api/hotels/${slug}/minibar-inventory/${checkId}/complete`, {}),
getReport: (slug: string, start?: string, end?: string) =>
req<MinibarReportRow[]>('GET', `/api/hotels/${slug}/minibar-report${start ? `?start=${start}&end=${end ?? ''}` : ''}`),
},
// ── Deposits ──────────────────────────────────────────────────────────────
@@ -1380,6 +1413,66 @@ export interface MinibarBookingCharge {
recordedByName: string | null
}
export interface MinibarStockItem extends MinibarItem {
stockQty: number
}
export interface MinibarReceiptItem {
id: string
itemId: string
itemName: string
quantity: number
costPrice: number | null
}
export interface MinibarReceipt {
id: string
supplier: string | null
notes: string | null
createdAt: string
items: MinibarReceiptItem[]
}
export interface MinibarWriteoffItem {
id: string
itemId: string
itemName: string
quantity: number
}
export interface MinibarWriteoff {
id: string
reason: string
notes: string | null
createdAt: string
items: MinibarWriteoffItem[]
}
export interface MinibarInventoryItem {
id: string
itemId: string
itemName: string
category: string | null
expectedQty: number
actualQty: number
}
export interface MinibarInventoryCheck {
id: string
checkedAt: string
notes: string | null
isComplete: boolean
createdAt: string
items?: MinibarInventoryItem[]
}
export interface MinibarReportRow {
name: string
category: string | null
totalQty: number
totalRevenue: number
}
// ── Deposit types ─────────────────────────────────────────────────────────────
export interface DepositPreset {