/** * useLocatorCatalog — one client-side entry point for "locators per organization": * * const { fetchLocators, resolveLocator, ensureLocator, isVirtualLocator } = useLocatorCatalog() * list = await fetchLocators({ organizationId, warehouseId }) // org locators + master preview (isVirtual) * real = await resolveLocator(picked, { organizationId, warehouseId }) // creates a virtual one on first use * * Virtual entries come from the master org's full locator set (id `new:`); they * are created in the org's own warehouse the moment they are selected or scanned * (`/api/mobile/locator-ensure`). Scans already go through `/api/mobile/locator-lookup` * / `locator-by-xyz`, which create on the server the same way. resolveLocator() patches * the picked object in place (real id, isVirtual=false) so list state stays consistent, * and shows a short toast when a locator was created. */ import { toast } from 'bulma-toast' export interface CatalogLocatorEntry { id: number | string code: string isDefault?: boolean isVirtual?: boolean warehouseId?: number | null organizationId?: number | null orgId?: number | null qtyOnHand?: number created?: boolean [key: string]: any } export const isVirtualLocatorId = (id: any): boolean => typeof id === 'string' && id.startsWith('new:') /** Toast for lookup responses (`/api/mobile/locator-lookup`, `locator-by-xyz`) that created the bin server-side. */ export const notifyLocatorCreated = (res: any) => { if (!res?.created) return try { toast({ message: `Lagerplatz ${res.code || res.Value || ''} angelegt`, type: 'is-success', duration: 2500, position: 'top-center' }) } catch (e) { /* ignore */ } } export const useLocatorCatalog = () => { const headers = () => useRequestHeaders(['cookie']) const isVirtualLocator = (l: any): boolean => !!(l && (l.isVirtual || isVirtualLocatorId(l.id))) /** Org locators + master-org preview entries, sorted by code. */ const fetchLocators = async (ctx: { organizationId?: number | string | null; warehouseId?: number | string | null }): Promise => { const organizationId = Number(ctx?.organizationId) || null const warehouseId = Number(ctx?.warehouseId) || null if (!organizationId && !warehouseId) return [] const res: any = await $fetch('/api/mobile/locators', { method: 'POST', headers: headers(), body: { organizationId, warehouseId } }) return (res?.locators || []).map((l: any) => ({ ...l, orgId: l.organizationId })) } /** Find-or-create by code / coordinates / role for an org (server creates from the master template). */ const ensureLocator = async (req: { organizationId: number | string | null | undefined; warehouseId?: number | string | null; code?: string; x?: string; y?: string; z?: string; role?: 'default' | 'return' }): Promise => { const organizationId = Number(req?.organizationId) || 0 if (!organizationId) throw new Error('organizationId is required') return await $fetch('/api/mobile/locator-ensure', { method: 'POST', headers: headers(), body: { organizationId, warehouseId: Number(req.warehouseId) || null, code: req.code, x: req.x, y: req.y, z: req.z, role: req.role } }) } /** * Real locator for a picked list entry. Existing ones are returned untouched; a virtual * one is created for the org and the entry is patched in place (id, isVirtual, created). * Throws when the creation fails (callers show their usual "not found" feedback). */ const resolveLocator = async (locator: CatalogLocatorEntry | null | undefined, ctx: { organizationId?: number | string | null; warehouseId?: number | string | null } = {}): Promise => { if (!locator) return null if (!isVirtualLocator(locator)) return locator const res = await ensureLocator({ organizationId: ctx.organizationId || locator.organizationId || locator.orgId, warehouseId: ctx.warehouseId || locator.warehouseId, code: locator.code }) if (!res?.id) throw new Error('Locator could not be created') Object.assign(locator, { id: res.id, code: res.code || locator.code, isVirtual: false, created: !!res.created, warehouseId: res.warehouseId ?? locator.warehouseId ?? null, organizationId: res.organizationId ?? locator.organizationId ?? null, orgId: res.organizationId ?? locator.orgId ?? null }) notifyLocatorCreated(res) return locator } return { fetchLocators, ensureLocator, resolveLocator, isVirtualLocator, isVirtualLocatorId } }