// The making charge («اجرت») the server will charge this customer for each // variant — fetched from /customer/me/wage-quote, which runs the same formula // that prices the order (MON-08, owner ruling 2026-10-08). The app shows this // figure, never one it computes itself, so what is shown is what is charged. // // Requests are batched: many cards asking at once become one call. import { defineStore } from 'pinia' import { ref, watch } from 'vue' import { api, type WageQuote } from '../api/client' import { useSessionStore } from './session' export const useWageQuoteStore = defineStore('wageQuotes', () => { const session = useSessionStore() const quotes = ref>({}) const pending = new Set() const inFlight = new Set() // Asked for but not answered: the server only returns variants that exist, // and re-asking from a computed on every re-render looped forever. const unanswered = new Set() // A failed call is retried, but not before this time. let retryAfter = 0 let timer: ReturnType | null = null // A different customer has a different percent: never reuse another's quotes. // That includes «ورود به حساب مشتری», which swaps WHICH customer we are // quoting for without swapping the token — so a sales expert would otherwise // see their own making charge on the customer's products, and the figure on // screen would not be the one the order is placed at. watch( () => [session.token, session.actingAs?.id ?? null], () => { quotes.value = {} pending.clear() inFlight.clear() unanswered.clear() retryAfter = 0 }, ) async function flush() { timer = null const ids = [...pending].slice(0, 200) ids.forEach((id) => { pending.delete(id) inFlight.add(id) }) if (pending.size) timer = setTimeout(flush, 0) if (!ids.length) return try { const rows = await api.getWageQuotes(ids) const answered = new Set(rows.map((r) => r.variantId)) ids.forEach((id) => { if (!answered.has(id)) unanswered.add(id) }) if (rows.length) { const next = { ...quotes.value } for (const r of rows) next[r.variantId] = r quotes.value = next } } catch (e) { retryAfter = Date.now() + 30_000 console.warn('wage quote failed', e) } finally { ids.forEach((id) => inFlight.delete(id)) } } /** Asks for a variant's quote (idempotent; safe to call from a computed). */ function request(variantId: string) { if (!session.token || Date.now() < retryAfter) return if (quotes.value[variantId] || unanswered.has(variantId) || pending.has(variantId) || inFlight.has(variantId)) return pending.add(variantId) if (!timer) timer = setTimeout(flush, 30) } /** The charged percent, or null while it is still loading. */ function finalPercent(variantId: string): number | null { const q = quotes.value[variantId] return q ? parseFloat(q.finalPercent) : null } return { quotes, request, finalPercent } })