@aglyn/plugins-commerce 1.0.0-beta.287 → 1.0.0-beta.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/lib/components/console/order-webhooks-card.component.js +1 -1
- package/src/lib/components/console/order-webhooks-card.component.js.map +1 -1
- package/src/lib/declarations.js +5 -1
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/model/plugin-permissions.d.ts +7 -0
- package/src/lib/model/plugin-permissions.js +1 -1
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/model/workspace-site-permissions.d.ts +26 -0
- package/src/lib/model/workspace-site-permissions.js +81 -0
- package/src/lib/model/workspace-site-permissions.js.map +1 -0
- package/src/lib/server/commerce-site-keys.d.ts +19 -0
- package/src/lib/server/commerce-site-keys.js +23 -2
- package/src/lib/server/commerce-site-keys.js.map +1 -1
- package/src/lib/server/order-webhooks.js +8 -8
- package/src/lib/server/order-webhooks.js.map +1 -1
- package/src/lib/server/pos-ops-gate.d.ts +13 -5
- package/src/lib/server/pos-ops-gate.js +12 -14
- package/src/lib/server/pos-ops-gate.js.map +1 -1
- package/src/lib/server/pos-refund-authority.d.ts +3 -1
- package/src/lib/server/pos-refund-authority.js +3 -2
- package/src/lib/server/pos-refund-authority.js.map +1 -1
- package/src/lib/server/pos-return.js +3 -6
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/pos-staff-pin.js +7 -7
- package/src/lib/server/pos-staff-pin.js.map +1 -1
- package/src/lib/server/refund.js +26 -36
- package/src/lib/server/refund.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-refund-authority.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { posMoney } from '../model/commerce-pos-ops'\nimport { posOpsSettings } from '../pos-ops-config'\nimport {\n defaultPosOpsDeps,\n posOpsCleanId,\n resolvePosStaffAssertion,\n type PosOpsDeps,\n} from './pos-ops-gate'\n\n/*==========================================\n * HOW MUCH THE REGISTER MAY REFUND (AGL-3609).\n *\n *
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-refund-authority.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { posMoney } from '../model/commerce-pos-ops'\nimport { commerceWorkspacePermissionRefusal } from '../model/workspace-site-permissions'\nimport { posOpsSettings } from '../pos-ops-config'\nimport {\n defaultPosOpsDeps,\n posOpsCleanId,\n resolvePosStaffAssertion,\n type PosOpsDeps,\n} from './pos-ops-gate'\n\n/*==========================================\n * HOW MUCH THE REGISTER MAY REFUND (AGL-3609).\n *\n * \"Refund orders\" (`commerce.refunds`, AGL-4047) refunds anything, as a\n * workspace admin always could. A member who works the register refunds a\n * POS sale up to the site's cashier refund limit (`posRefundLimit`, $0 by\n * default, so out of the box every register refund needs a manager), and\n * above it only with \"Approve refunds over the register's limit\"\n * (`commerce.posApprovals`) or a MANAGER ASSERTION: the PIN of someone who\n * holds it, entered at that register for that refund, minted for two\n * minutes and re-checked against their permissions when it is used. Both\n * keys' preset is the gate they replaced: an admin of the whole workspace\n * who is also the site's admin.\n *\n * Only a POS sale. An online order's refund stays \"Refund orders\", so the\n * register's limit is not a way to refund the storefront.\n *=========================================*/\n\nexport type PosRefundAuthority =\n | { ok: true; approvedBy?: string }\n | { ok: false; body: { error: string; needsManager?: boolean; limitCents?: number } }\n\n/** The limit question, with the money already known. */\nexport async function decidePosRefundAuthority(\n deps: PosOpsDeps,\n input: {\n hostId: string\n hostRef: FirebaseFirestore.DocumentReference\n registerId: string\n refundCents: number\n limitCents: number\n /** The member asking holds \"Approve refunds over the register's limit\" (AGL-4047). */\n isManager: boolean\n managerAssertion: unknown\n },\n): Promise<PosRefundAuthority> {\n if (input.isManager || input.refundCents <= input.limitCents) return { ok: true }\n const registerId = posOpsCleanId(input.registerId)\n const approvedBy =\n registerId && input.managerAssertion\n ? await resolvePosStaffAssertion(deps, input.hostRef, input.managerAssertion, {\n hostId: input.hostId,\n registerId,\n purpose: 'manager',\n })\n : null\n if (approvedBy) return { ok: true, approvedBy }\n return {\n ok: false,\n body: {\n error:\n input.limitCents > 0\n ? `Refunds over ${posMoney(input.limitCents)} need a manager's PIN.`\n : \"Register refunds need a manager's PIN.\",\n needsManager: true,\n limitCents: input.limitCents,\n },\n }\n}\n\n/**\n * For `refund.ts`: a register member refunding from the order dialog. The\n * amount is the one the route is about to reserve — the amount asked, else\n * the named lines, else everything left.\n */\nexport async function posRegisterRefundAuthority(\n input: {\n hostId: string\n hostRef: FirebaseFirestore.DocumentReference\n order: {\n channel?: string\n totals?: { totalCents?: number } | null\n amountCents?: number\n refundedCents?: number\n }\n amountCents: number | null\n lineCents: number | null\n registerId: string\n managerAssertion: unknown\n /** The member asking holds \"Approve refunds over the register's limit\" themselves (AGL-4047). */\n isManager?: boolean\n },\n deps: PosOpsDeps = defaultPosOpsDeps(),\n): Promise<PosRefundAuthority> {\n if (input.order.channel !== 'pos') {\n return {\n ok: false,\n body: { error: commerceWorkspacePermissionRefusal('commerce.refunds') },\n }\n }\n const remaining = Math.max(\n 0,\n Math.round(Number(input.order.totals?.totalCents ?? input.order.amountCents ?? 0)) -\n Math.round(Number(input.order.refundedCents ?? 0)),\n )\n const asked =\n input.amountCents != null && Number.isFinite(input.amountCents)\n ? Math.round(input.amountCents)\n : input.lineCents != null\n ? input.lineCents\n : remaining\n const owner = await deps.orgForHost(input.hostId)\n const settings = posOpsSettings(\n owner?.orgId ? await deps.pluginConfig(owner.orgId, input.hostId).catch(() => ({})) : {},\n )\n return decidePosRefundAuthority(deps, {\n hostId: input.hostId,\n hostRef: input.hostRef,\n registerId: input.registerId,\n refundCents: Math.min(asked, remaining),\n limitCents: settings.refundLimitCents,\n isManager: input.isManager === true,\n managerAssertion: input.managerAssertion,\n })\n}\n"],"names":["posMoney","commerceWorkspacePermissionRefusal","posOpsSettings","defaultPosOpsDeps","posOpsCleanId","resolvePosStaffAssertion","decidePosRefundAuthority","deps","input","isManager","refundCents","limitCents","ok","registerId","approvedBy","managerAssertion","hostRef","hostId","purpose","body","error","needsManager","posRegisterRefundAuthority","order","channel","remaining","Math","max","round","Number","totals","totalCents","amountCents","refundedCents","asked","isFinite","lineCents","owner","orgForHost","settings","orgId","pluginConfig","catch","min","refundLimitCents"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,QAAQ,QAAQ,+BAA2B;AACpD,SAASC,kCAAkC,QAAQ,yCAAqC;AACxF,SAASC,cAAc,QAAQ,uBAAmB;AAClD,SACEC,iBAAiB,EACjBC,aAAa,EACbC,wBAAwB,QAEnB,oBAAgB;AAwBvB,sDAAsD,GACtD,OAAO,eAAeC,yBACpBC,IAAgB,EAChBC,KASC;IAED,IAAIA,MAAMC,SAAS,IAAID,MAAME,WAAW,IAAIF,MAAMG,UAAU,EAAE,OAAO;QAAEC,IAAI;IAAK;IAChF,MAAMC,aAAaT,cAAcI,MAAMK,UAAU;IACjD,MAAMC,aACJD,cAAcL,MAAMO,gBAAgB,GAChC,MAAMV,yBAAyBE,MAAMC,MAAMQ,OAAO,EAAER,MAAMO,gBAAgB,EAAE;QAC1EE,QAAQT,MAAMS,MAAM;QACpBJ;QACAK,SAAS;IACX,KACA;IACN,IAAIJ,YAAY,OAAO;QAAEF,IAAI;QAAME;IAAW;IAC9C,OAAO;QACLF,IAAI;QACJO,MAAM;YACJC,OACEZ,MAAMG,UAAU,GAAG,IACf,CAAC,aAAa,EAAEX,SAASQ,MAAMG,UAAU,EAAE,sBAAsB,CAAC,GAClE;YACNU,cAAc;YACdV,YAAYH,MAAMG,UAAU;QAC9B;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeW,2BACpBd,KAeC,EACDD,OAAmBJ,mBAAmB;QAUlBK,aACEA;QADFA;IARpB,IAAIA,MAAMe,KAAK,CAACC,OAAO,KAAK,OAAO;QACjC,OAAO;YACLZ,IAAI;YACJO,MAAM;gBAAEC,OAAOnB,mCAAmC;YAAoB;QACxE;IACF;IACA,MAAMwB,YAAYC,KAAKC,GAAG,CACxB,GACAD,KAAKE,KAAK,CAACC,QAAOrB,iBAAAA,sBAAAA,MAAMe,KAAK,CAACO,MAAM,qBAAlBtB,oBAAoBuB,UAAU,oBAAIvB,MAAMe,KAAK,CAACS,WAAW,YAAzDxB,OAA6D,MAC7EkB,KAAKE,KAAK,CAACC,QAAOrB,6BAAAA,MAAMe,KAAK,CAACU,aAAa,YAAzBzB,6BAA6B;IAEnD,MAAM0B,QACJ1B,MAAMwB,WAAW,IAAI,QAAQH,OAAOM,QAAQ,CAAC3B,MAAMwB,WAAW,IAC1DN,KAAKE,KAAK,CAACpB,MAAMwB,WAAW,IAC5BxB,MAAM4B,SAAS,IAAI,OACjB5B,MAAM4B,SAAS,GACfX;IACR,MAAMY,QAAQ,MAAM9B,KAAK+B,UAAU,CAAC9B,MAAMS,MAAM;IAChD,MAAMsB,WAAWrC,eACfmC,CAAAA,yBAAAA,MAAOG,KAAK,IAAG,MAAMjC,KAAKkC,YAAY,CAACJ,MAAMG,KAAK,EAAEhC,MAAMS,MAAM,EAAEyB,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA,KAAM,CAAC;IAEzF,OAAOpC,yBAAyBC,MAAM;QACpCU,QAAQT,MAAMS,MAAM;QACpBD,SAASR,MAAMQ,OAAO;QACtBH,YAAYL,MAAMK,UAAU;QAC5BH,aAAagB,KAAKiB,GAAG,CAACT,OAAOT;QAC7Bd,YAAY4B,SAASK,gBAAgB;QACrCnC,WAAWD,MAAMC,SAAS,KAAK;QAC/BM,kBAAkBP,MAAMO,gBAAgB;IAC1C;AACF"}
|
|
@@ -340,7 +340,7 @@ export async function handlePosReturn(deps, req) {
|
|
|
340
340
|
};
|
|
341
341
|
const cashier = await resolvePosCashier(deps, staff, registerId, body['cashierAssertion']);
|
|
342
342
|
// The limit is the person at the till's: a PIN-switched cashier is held
|
|
343
|
-
// to it even on a device
|
|
343
|
+
// to it even on a device someone who holds the approval signed in on.
|
|
344
344
|
const cashierIsManager = cashier.cashierId === staff.uid ? staff.isManager : await memberIsPosManager(deps, staff.hostRef, staff.hostId, cashier.cashierId);
|
|
345
345
|
const authority = await decidePosRefundAuthority(deps, {
|
|
346
346
|
hostId: staff.hostId,
|
|
@@ -782,11 +782,8 @@ export async function handlePosReturn(deps, req) {
|
|
|
782
782
|
}
|
|
783
783
|
return held;
|
|
784
784
|
}
|
|
785
|
-
/** Whether a member
|
|
786
|
-
|
|
787
|
-
const host = await hostRef.get();
|
|
788
|
-
const role = ((_host_get = host.get('memberRoles')) != null ? _host_get : {})[uid];
|
|
789
|
-
return isPosManager(role, await deps.membership(uid, hostId));
|
|
785
|
+
/** Whether a member holds "Approve refunds over the register's limit" here, asked now (AGL-4047). */ async function memberIsPosManager(deps, hostRef, hostId, uid) {
|
|
786
|
+
return isPosManager(await hostRef.get(), uid, await deps.membership(uid, hostId));
|
|
790
787
|
}
|
|
791
788
|
let ReturnRefusal = class ReturnRefusal extends Error {
|
|
792
789
|
constructor(status, message){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-return.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { claimAttempt, type PluginApiHandler, type PluginApiRequest } from '@aglyn/aglyn/server'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { nameSearchKey } from '@aglyn/aglyn/app-utils/name-search'\nimport { recordPluginPersonRefund } from '@aglyn/aglyn/plugin-manager/plugin-person-records'\nimport { reverseOrderConversion } from '@aglyn/aglyn/plugin-manager/plugin-conversion-credit'\nimport { checkoutCreditProvider } from '@aglyn/aglyn/plugin-manager/plugin-checkout-credits'\nimport * as CommerceModel from '../model'\nimport {\n planPosReturn,\n posMoney,\n posRefundableByTender,\n posReturnedQuantities,\n posReturnLineValues,\n posTenderLabel,\n POS_STRIPE_TENDERS,\n splitPosRefund,\n type PosCashEvent,\n type PosRefundAllocation,\n type PosRegisterReturn,\n type PosReturnPick,\n type PosReturnSource,\n} from '../model/commerce-pos-ops'\nimport { ORDER_REFUNDED_EVENT, RETURN_REFUNDED_EVENT } from '../model/order-events'\nimport { raiseOrderEvent } from './order-events'\nimport { applyGiftCardRiskToOrder } from './gift-card-risk'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { decidePosRefundAuthority } from './pos-refund-authority'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n posOpsBody,\n posOpsCleanId,\n posOpsIdempotencyKey,\n readPosRegister,\n isPosManager,\n resolvePosCashier,\n type PosOpsDeps,\n} from './pos-ops-gate'\nimport { createStripeRefund } from './stripe-refund'\nimport { parsePosHardwareInPersonConfig } from '../model/pos-hardware'\n\n/*==========================================\n * RETURNS AT THE REGISTER (AGL-3609): `POST /api/commerce/pos-return`.\n *\n * find an order by its number (typed or scanned off the receipt's\n * barcode), its id, or the customer's email\n * refund picked lines and quantities back, restocked to the register's\n * location, the money sent back to the sale's OWN payments\n *\n * Each payment gets its money back the way it came: a card through Stripe\n * (the same refund the order dialog sends), cash out of the drawer — a cash\n * event on the open shift, so the count still balances — a gift card\n * re-credited, and a room charge reversed on the stay's folio.\n *\n * THE ORDER OF WORK is what keeps a failure honest:\n * 1. every refusal that needs no money to move (role, limit, quantities,\n * the split, the shift rule) — before anything is written;\n * 2. the attempt claim, so a retried tap replays instead of refunding twice;\n * 3. the RESERVATION on the order, in a transaction that re-reads it: the\n * cents, the units and each payment's share are taken before any money\n * moves, so two registers returning the same line cannot both succeed;\n * 4. the Stripe refunds first, because they are the ones that can say no —\n * if the first one does, nothing has moved and the reservation is\n * handed back whole;\n * 5. then the tenders that cannot fail for a reason outside this database.\n * A card that fails after another payment already went back leaves a\n * PARTIAL return: what moved is recorded, what did not is handed back on\n * the order, and the cashier is told the amount still owed.\n *=========================================*/\n\nexport interface PosReturnSideEffects {\n hostId: string\n orderId: string\n /** The return's id, the same under the register and in the store's returns. */\n returnId: string\n /** The return as the store's returns list holds it, for the return event. */\n storeReturn: CommerceModel.HostReturn\n /** The Stripe refunds the return made, if any, for the refund event. */\n refundIds: string[]\n email: string | null | undefined\n refundCents: number\n closedTheOrder: boolean\n returnedProductIds: string[]\n fullyRefunded: boolean\n lineIndexes: number[]\n /** The lines this return finished taking back in full. */\n fullLineIndexes: number[]\n /**\n * Each line taken back, with its units and its share of the refund, for\n * whatever follows a line in particular — a card reader sold in person,\n * whose hardware order is told (AGL-4038).\n */\n lines?: Array<{ index: number; quantity: number; refundCents: number }>\n}\n\nexport interface PosReturnDeps extends PosOpsDeps {\n /** What every refund tells the rest of the workspace, after the money moved. Never throws. */\n afterRefund(input: PosReturnSideEffects): Promise<void>\n}\n\nexport function defaultPosReturnDeps(): PosReturnDeps {\n const base = defaultPosOpsDeps()\n return {\n ...base,\n afterRefund: async (input) => {\n const settle = async (label: string, work: () => Promise<unknown>) => {\n try {\n await work()\n } catch (error) {\n console.error(`[pos-return] ${label} failed`, input.orderId, error)\n }\n }\n const hostRef = base.firestore().collection('hosts').doc(input.hostId)\n if (input.returnedProductIds.length) {\n await settle('gift card void', () =>\n applyGiftCardRiskToOrder({\n firestore: base.firestore(),\n hostRef,\n orderId: input.orderId,\n action: {\n kind: 'void',\n reason: 'refund',\n ...(input.fullyRefunded ? {} : { productIds: input.returnedProductIds }),\n },\n }),\n )\n }\n await settle('person refund', () =>\n recordPluginPersonRefund({\n hostId: input.hostId,\n refId: input.orderId,\n email: input.email,\n amountCents: input.refundCents,\n closedTheSale: input.closedTheOrder,\n }),\n )\n await settle('conversion reversal', () =>\n reverseOrderConversion({\n hostId: input.hostId,\n orderId: input.orderId,\n amountCents: input.refundCents,\n closedTheOrder: input.closedTheOrder,\n }),\n )\n // The same events a refund from the order dialog and a refunded online\n // return raise, so accounting and the merchant's webhooks hear a\n // register return too (AGL-3611's events).\n await settle('refund event', () =>\n raiseOrderEvent(ORDER_REFUNDED_EVENT, {\n hostId: input.hostId,\n orderId: input.orderId,\n key: `pos-return:${input.returnId}`,\n extra: {\n refund: {\n id: input.refundIds[0] ?? null,\n amountCents: input.refundCents,\n lineItemIds: input.fullLineIndexes,\n full: input.fullyRefunded,\n },\n },\n }),\n )\n await settle('return event', () =>\n raiseOrderEvent(RETURN_REFUNDED_EVENT, {\n hostId: input.hostId,\n orderId: input.orderId,\n key: `return-refunded:${input.returnId}`,\n extra: {\n return: {\n id: input.returnId,\n status: input.storeReturn.status,\n lines: input.storeReturn.lines.map((line) => ({ ...line })),\n refundCents: input.storeReturn.refundCents ?? null,\n },\n },\n }),\n )\n // A card reader the platform's own store sold in person (AGL-4038):\n // its hardware order and retained sale record take the return too.\n // Answered from the environment for every other store.\n if (input.lines?.length && parsePosHardwareInPersonConfig(process.env)?.sellerHostId === input.hostId) {\n await settle('hardware return', async () =>\n (await import('./pos-hardware-returns')).recordRegisterHardwareReturn({\n config: parsePosHardwareInPersonConfig(process.env),\n hostId: input.hostId,\n registerOrderId: input.orderId,\n returnId: input.returnId,\n refundCents: input.refundCents,\n lines: input.lines ?? [],\n }),\n )\n }\n await settle('buyer notice', () =>\n notifyOrderBuyer({ hostId: input.hostId, orderId: input.orderId }, 'refunded', {\n refundId: '',\n refundCents: input.refundCents,\n refundLineIndexes: input.lineIndexes,\n fullyRefunded: input.fullyRefunded,\n }),\n )\n },\n }\n}\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\n/** The statuses a return may be taken against. */\nconst RETURNABLE: ReadonlySet<string> = new Set([\n 'paid',\n 'partially_fulfilled',\n 'fulfilled',\n 'delivered',\n])\n\n/** One order as the return dialog lists it. */\nfunction returnSummary(id: string, raw: Record<string, any>, held: readonly number[] = []) {\n const order = CommerceModel.liftLegacyOrder(raw as any) as unknown as PosReturnSource &\n Record<string, any>\n const values = posReturnLineValues(order)\n const returned = posReturnedQuantities(order)\n return {\n id,\n number: order['number'] ?? null,\n label: CommerceModel.formatOrderNumber(order as any, id),\n status: order['status'],\n channel: order['channel'] ?? 'online',\n createdAtMs: Number(order['createdAtMs'] ?? 0),\n customerEmail: order['customerEmail'] ?? null,\n customerName: order['customerName'] ?? null,\n totalCents: Number(order.totals?.totalCents ?? order.amountCents ?? 0),\n refundedCents: Number(order.refundedCents ?? 0),\n returnable: RETURNABLE.has(String(order['status'])),\n lines: (order.lineItems ?? []).map((line: any, index: number) => ({\n index,\n name: String(line?.name ?? ''),\n variantLabel: line?.variantLabel ?? null,\n quantity: Math.max(1, Math.round(Number(line?.quantity ?? 1))),\n returned: returned[index] ?? 0,\n // Units an online return holds or already took (AGL-3611).\n held: held[index] ?? 0,\n valueCents: values[index] ?? 0,\n })),\n tenders: posRefundableByTender(order).map((tender) => ({\n id: tender.id,\n method: tender.method,\n label: posTenderLabel(tender),\n amountCents: tender.amountCents,\n refundableCents: tender.refundableCents,\n })),\n }\n}\n\nasync function findOrders(\n hostRef: FirebaseFirestore.DocumentReference,\n rawText: unknown,\n): Promise<Array<{ id: string; data: Record<string, any> }>> {\n const text = String(rawText ?? '').trim().slice(0, 200)\n if (!text) return []\n const orders = hostRef.collection('orders')\n const digits = text.replace(/^#/, '')\n if (/^\\d{1,12}$/.test(digits)) {\n const snapshot = await orders.where('number', '==', Number(digits)).limit(5).get()\n return snapshot.docs.map((doc) => ({ id: doc.id, data: (doc.data() ?? {}) as Record<string, any> }))\n }\n if (text.includes('@')) {\n const snapshot = await orders\n .where('customerEmailLower', '==', nameSearchKey(text))\n .orderBy('createdAtMs', 'desc')\n .limit(10)\n .get()\n return snapshot.docs.map((doc) => ({ id: doc.id, data: (doc.data() ?? {}) as Record<string, any> }))\n }\n const id = posOpsCleanId(text)\n if (!id) return []\n const snapshot = await orders.doc(id).get()\n return snapshot.exists ? [{ id, data: (snapshot.data() ?? {}) as Record<string, any> }] : []\n}\n\nfunction parsePicks(raw: unknown): PosReturnPick[] {\n if (!Array.isArray(raw)) return []\n return raw.slice(0, 200).map((entry: any) => ({\n index: Number(entry?.index),\n quantity: Number(entry?.quantity),\n }))\n}\n\nfunction parseSplit(raw: unknown): Array<{ paymentId: string; amountCents: number }> | null {\n if (!Array.isArray(raw) || !raw.length) return null\n return raw.slice(0, 20).map((entry: any) => ({\n paymentId: String(entry?.paymentId ?? ''),\n amountCents: Number(entry?.amountCents),\n }))\n}\n\n/** What a refunded tender moved, and how to hand back a reservation it did not. */\ninterface TenderResult {\n allocation: PosRefundAllocation\n status: 'refunded' | 'failed'\n refundId?: string\n error?: string\n}\n\nexport async function handlePosReturn(deps: PosReturnDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = posOpsBody(req)\n // Finding a sale to take back and refunding it are both the refund key's\n // (AGL-4047).\n const gate = await authorizePosOps(deps, req, body['hostId'], 'posRefund')\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n const action = String(body['action'] ?? '')\n\n if (action === 'find') {\n const found = await findOrders(staff.hostRef, body['text'])\n const held = await Promise.all(\n found.map(async (entry) =>\n unitsHeldByOnlineReturns(\n (await staff.hostRef.collection('returns').where('orderId', '==', entry.id).get()).docs,\n ),\n ),\n )\n return {\n status: 200,\n body: { orders: found.map((entry, at) => returnSummary(entry.id, entry.data, held[at])) },\n }\n }\n if (action !== 'refund') return { status: 400, body: { error: 'Unknown action' } }\n\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const registerId = register.ref.id\n const orderId = posOpsCleanId(body['orderId'])\n if (!orderId) return { status: 400, body: { error: 'Missing orderId' } }\n const idempotencyKey = posOpsIdempotencyKey(req)\n if (!idempotencyKey) {\n return { status: 400, body: { error: 'A refund needs an Idempotency-Key header.' } }\n }\n const picks = parsePicks(body['lines'])\n const requestedSplit = parseSplit(body['tenders'])\n const restock = body['restock'] !== false\n const reason = String(body['reason'] ?? '').trim().slice(0, 200)\n const reasonCode: CommerceModel.ReturnReason = CommerceModel.RETURN_REASONS.includes(\n body['reasonCode'] as CommerceModel.ReturnReason,\n )\n ? (body['reasonCode'] as CommerceModel.ReturnReason)\n : 'other'\n const firestore = deps.firestore()\n const orderRef = staff.hostRef.collection('orders').doc(orderId)\n const storeReturns = staff.hostRef.collection('returns').where('orderId', '==', orderId)\n\n // 1. Every refusal that needs no money to move.\n const snapshot = await orderRef.get()\n if (!snapshot.exists) return { status: 404, body: { error: 'Unknown order' } }\n const order = CommerceModel.liftLegacyOrder(snapshot.data() as any) as unknown as PosReturnSource &\n Record<string, any>\n if (!RETURNABLE.has(String(order['status']))) {\n return { status: 409, body: { error: `Orders in \"${order['status']}\" cannot be returned.` } }\n }\n if (CommerceModel.orderDisputeBlocksRefund(order as any)) {\n return {\n status: 409,\n body: {\n error:\n 'A chargeback is open on this order, so it was not refunded. Respond to the ' +\n 'dispute or accept it in the Stripe dashboard first.',\n },\n }\n }\n const plan = planPosReturn(order, picks, unitsHeldByOnlineReturns((await storeReturns.get()).docs))\n if ('error' in plan) return { status: 400, body: { error: plan.error } }\n const split = splitPosRefund(order, plan.refundCents, requestedSplit)\n if ('error' in split) return { status: 400, body: { error: split.error } }\n\n const cashier = await resolvePosCashier(deps, staff, registerId, body['cashierAssertion'])\n // The limit is the person at the till's: a PIN-switched cashier is held\n // to it even on a device a workspace admin signed in on.\n const cashierIsManager =\n cashier.cashierId === staff.uid\n ? staff.isManager\n : await memberIsPosManager(deps, staff.hostRef, staff.hostId, cashier.cashierId)\n const authority = await decidePosRefundAuthority(deps, {\n hostId: staff.hostId,\n hostRef: staff.hostRef,\n registerId,\n refundCents: plan.refundCents,\n limitCents: staff.settings.refundLimitCents,\n isManager: cashierIsManager,\n managerAssertion: body['managerAssertion'],\n })\n if ('body' in authority) return { status: 403, body: authority.body }\n\n const needsCash = split.allocations.some((slice) => slice.method === 'cash')\n const needsStripe = split.allocations.some((slice) => POS_STRIPE_TENDERS.has(slice.method))\n const openShiftId = String(register.data['openShiftId'] ?? '') || null\n if (needsCash && !openShiftId && staff.settings.requireOpenShift) {\n return { status: 409, body: { error: 'Open a shift before paying cash out of the drawer.' } }\n }\n if (needsStripe && !process.env.STRIPE_SECRET_KEY) {\n return { status: 501, body: { error: 'Payments are not configured.' } }\n }\n\n // 2. The attempt claim.\n const claimed = await claimAttempt(firestore as any, {\n kind: 'pos-return',\n scopeId: staff.hostId,\n orgId: staff.orgId,\n key: `${orderId}:${idempotencyKey}`,\n busyMessage: 'This return is already being refunded',\n })\n if ('replay' in claimed) return { status: claimed.replay.status, body: claimed.replay.body as any }\n const claim = claimed.claim\n\n // 3. The reservation, re-read under the transaction.\n type Reserved = {\n refundCents: number\n lineCents: number[]\n completedLines: number[]\n allocations: PosRefundAllocation[]\n prior: { refundedCents: number; returnedQuantities: Record<string, number>; posPaymentRefunds: Record<string, number> }\n }\n let reserved: Reserved\n try {\n reserved = await firestore.runTransaction(async (transaction) => {\n const fresh = CommerceModel.liftLegacyOrder(\n ((await transaction.get(orderRef)).data() ?? {}) as any,\n ) as unknown as PosReturnSource & Record<string, any>\n if (!RETURNABLE.has(String(fresh['status']))) {\n throw new ReturnRefusal(409, `Orders in \"${fresh['status']}\" cannot be returned.`)\n }\n const freshPlan = planPosReturn(\n fresh,\n picks,\n unitsHeldByOnlineReturns((await transaction.get(storeReturns)).docs),\n )\n if ('error' in freshPlan) throw new ReturnRefusal(409, freshPlan.error)\n const freshSplit = splitPosRefund(fresh, freshPlan.refundCents, requestedSplit)\n if ('error' in freshSplit) throw new ReturnRefusal(409, freshSplit.error)\n const prior = {\n refundedCents: Number(fresh.refundedCents ?? 0),\n returnedQuantities: { ...(fresh.returnedQuantities ?? {}) },\n posPaymentRefunds: { ...(fresh.posPaymentRefunds ?? {}) },\n }\n const posPaymentRefunds = { ...prior.posPaymentRefunds }\n for (const slice of freshSplit.allocations) {\n posPaymentRefunds[slice.paymentId] = (posPaymentRefunds[slice.paymentId] ?? 0) + slice.amountCents\n }\n transaction.update(orderRef, {\n refundedCents: prior.refundedCents + freshPlan.refundCents,\n returnedQuantities: freshPlan.returnedQuantities,\n posPaymentRefunds,\n })\n return {\n refundCents: freshPlan.refundCents,\n lineCents: freshPlan.lineCents,\n completedLines: freshPlan.completedLines,\n allocations: freshSplit.allocations,\n prior,\n }\n })\n } catch (error) {\n await claim.release()\n if (error instanceof ReturnRefusal) return { status: error.status, body: { error: error.message } }\n throw error\n }\n\n // 4–5. The money, Stripe first.\n const tenders = posRefundableByTender(order)\n const tenderOf = (paymentId: string) => tenders.find((tender) => tender.id === paymentId)\n const ordered = [...reserved.allocations].sort(\n (a, b) => Number(POS_STRIPE_TENDERS.has(b.method)) - Number(POS_STRIPE_TENDERS.has(a.method)),\n )\n const results: TenderResult[] = []\n for (const allocation of ordered) {\n const tender = tenderOf(allocation.paymentId)\n const anyMoved = results.some((result) => result.status === 'refunded')\n try {\n if (POS_STRIPE_TENDERS.has(allocation.method)) {\n const paymentIntentId = tender?.paymentIntentId ?? String(order['paymentIntentId'] ?? '')\n if (!paymentIntentId) throw new ReturnRefusal(409, 'That card payment has nothing Stripe can refund.')\n const refund = await createStripeRefund({\n paymentIntentId,\n amountCents: allocation.amountCents,\n idempotencyKey: claim.stripeKey ? `${claim.stripeKey}:${allocation.paymentId}` : null,\n metadata: { hostId: staff.hostId, orderId, registerId, kind: 'pos-return' },\n fetch: deps.fetch,\n })\n if ('error' in refund) throw new ReturnRefusal(refund.status, refund.error)\n results.push({ allocation, status: 'refunded', refundId: refund.refundId })\n } else if (allocation.method === 'gift_card') {\n await creditGiftCard(firestore, staff.hostRef, tender?.giftCardId ?? '', allocation.amountCents, orderId, deps.now())\n results.push({ allocation, status: 'refunded' })\n } else if (allocation.method === 'folio') {\n await reverseFolio(staff.hostRef, tender?.reservationId ?? String(order['reservationId'] ?? ''), allocation.amountCents, orderId, deps.now())\n results.push({ allocation, status: 'refunded' })\n } else if (allocation.method === 'credit') {\n await restorePluginCredit(\n staff.hostRef.id,\n tender,\n allocation.amountCents,\n orderId,\n `pos-return:${orderId}:${allocation.paymentId}:${reserved.prior.posPaymentRefunds[allocation.paymentId] ?? 0}`,\n )\n results.push({ allocation, status: 'refunded' })\n } else {\n // Cash: recorded on the drawer with the return itself, below.\n results.push({ allocation, status: 'refunded' })\n }\n } catch (error) {\n const message = error instanceof ReturnRefusal ? error.message : 'Refund failed'\n if (!(error instanceof ReturnRefusal)) console.error('[pos-return] tender failed', allocation, error)\n if (!anyMoved) {\n // Nothing has moved: hand the whole reservation back and let the\n // same attempt be tried again.\n await firestore\n .runTransaction(async (transaction) => {\n await transaction.get(orderRef)\n transaction.update(orderRef, reserved.prior)\n })\n .catch((rollback) => console.error('[pos-return] rollback failed', orderId, rollback))\n await claim.release()\n return {\n status: error instanceof ReturnRefusal ? error.status : 502,\n body: { error: message },\n }\n }\n results.push({ allocation, status: 'failed', error: message })\n }\n }\n\n const failed = results.filter((result) => result.status === 'failed')\n const refundedCents = results\n .filter((result) => result.status === 'refunded')\n .reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const cashCents = results\n .filter((result) => result.status === 'refunded' && result.allocation.method === 'cash')\n .reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const now = deps.now()\n // One id names the return under the register (its tenders and shift) and\n // in the store's returns list beside the online ones (AGL-3611).\n const returnId = createResourceUid()\n const returnRef = register.ref.collection('returns').doc(returnId)\n const storeReturnRef = staff.hostRef.collection('returns').doc(returnId)\n const orderLabel = CommerceModel.formatOrderNumber(order as any, orderId)\n const lines = picks.map((pick, at) => ({\n index: pick.index,\n quantity: pick.quantity,\n name: String(order.lineItems?.[pick.index]?.name ?? ''),\n refundCents: reserved.lineCents[at] ?? 0,\n }))\n\n // The record, the drawer, the order: one transaction.\n let closedTheOrder = false\n let fullyRefunded = false\n let shiftIdForReturn: string | null = null\n let storeReturn: CommerceModel.HostReturn | null = null\n await firestore.runTransaction(async (transaction) => {\n const freshRegister = await transaction.get(register.ref)\n const shiftId = String(freshRegister.get('openShiftId') ?? '') || null\n const shiftRef = shiftId ? register.ref.collection('shifts').doc(shiftId) : null\n const shiftSnapshot = shiftRef ? await transaction.get(shiftRef) : null\n const fresh = CommerceModel.liftLegacyOrder(\n ((await transaction.get(orderRef)).data() ?? {}) as any,\n ) as unknown as PosReturnSource & Record<string, any>\n const shiftOpen = Boolean(shiftSnapshot?.exists && shiftSnapshot.get('status') === 'open')\n shiftIdForReturn = shiftOpen ? shiftId : null\n\n // Hand back what a failed card did not refund.\n const failedCents = failed.reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const posPaymentRefunds = { ...(fresh.posPaymentRefunds ?? {}) }\n for (const result of failed) {\n posPaymentRefunds[result.allocation.paymentId] = Math.max(\n 0,\n (posPaymentRefunds[result.allocation.paymentId] ?? 0) - result.allocation.amountCents,\n )\n }\n const orderRefunded = Math.max(0, Number(fresh.refundedCents ?? 0) - failedCents)\n const totalCents = Number(fresh.totals?.totalCents ?? fresh.amountCents ?? 0)\n fullyRefunded = orderRefunded >= totalCents && totalCents > 0\n closedTheOrder =\n fullyRefunded &&\n fresh['status'] !== 'refunded' &&\n CommerceModel.canTransitionOrder(fresh['status'] as CommerceModel.OrderStatus, 'refunded')\n const completedLines = Object.entries(fresh.returnedQuantities ?? {})\n .filter(([index, count]) => count >= Math.max(1, Number(fresh.lineItems?.[Number(index)]?.quantity ?? 1)))\n .map(([index]) => Number(index))\n const tenderWords = results\n .map(\n (result) =>\n `${posMoney(result.allocation.amountCents)} to ${posTenderLabel(\n tenderOf(result.allocation.paymentId) ?? { method: result.allocation.method },\n )}${result.status === 'failed' ? ' (FAILED)' : ''}`,\n )\n .join(', ')\n const units = picks.reduce((sum, pick) => sum + pick.quantity, 0)\n transaction.update(orderRef, {\n refundedCents: orderRefunded,\n posPaymentRefunds,\n ...(closedTheOrder ? { status: 'refunded' } : {}),\n ...(completedLines.length\n ? {\n refundedLineItemIds: [\n ...new Set([...(fresh.refundedLineItemIds ?? []), ...completedLines]),\n ].sort((a, b) => a - b),\n }\n : {}),\n timeline: CommerceModel.appendOrderEvent(\n fresh as any,\n 'refund',\n `Returned at the register: ${units} ${units === 1 ? 'item' : 'items'}, ${tenderWords}` +\n (restock ? '; restocked' : '') +\n (authority.approvedBy ? '; approved by a manager' : ''),\n now,\n ),\n })\n const record: PosRegisterReturn = {\n hostId: staff.hostId,\n registerId,\n orderId,\n ...(typeof order['number'] === 'number' ? { orderNumber: order['number'] } : {}),\n ...(shiftOpen && shiftId ? { shiftId } : {}),\n lines,\n refundedCents,\n tenders: results.map((result) => ({\n ...result.allocation,\n status: result.status,\n ...(result.refundId ? { refundId: result.refundId } : {}),\n ...(result.error ? { error: result.error } : {}),\n })),\n restocked: restock,\n ...(register.data['locationId'] ? { locationId: String(register.data['locationId']) } : {}),\n cashierId: cashier.cashierId,\n ...(authority.approvedBy ? { approvedBy: authority.approvedBy } : {}),\n ...(reason ? { reason } : {}),\n status: failed.length ? 'partial' : 'refunded',\n atMs: now,\n }\n transaction.create(returnRef, record)\n const restockedLines = picks.map((pick) => ({ lineItemId: pick.index, quantity: pick.quantity }))\n const registerLocationId = register.data['locationId'] ? String(register.data['locationId']) : undefined\n storeReturn = {\n orderId,\n orderNumber: orderLabel,\n customerEmail: (order['customerEmail'] as string | undefined) ?? null,\n customerName: (order['customerName'] as string | undefined) ?? null,\n lines: picks.map((pick) => ({ lineItemId: pick.index, quantity: pick.quantity, reason: reasonCode })),\n status: 'refunded',\n requestedBy: 'merchant',\n source: 'register',\n registerId,\n ...(reason ? { merchantNote: reason } : {}),\n ...(restock\n ? {\n restock: {\n lines: restockedLines,\n ...(registerLocationId ? { locationId: registerLocationId } : {}),\n atMs: now,\n },\n }\n : {}),\n refundedAtMs: now,\n refundCents: refundedCents,\n timeline: [\n {\n atMs: now,\n event: 'refunded',\n detail: `At the register${failed.length ? `; ${posMoney(failed.reduce((sum, result) => sum + result.allocation.amountCents, 0))} still owed` : ''}`,\n },\n ],\n createdAtMs: now,\n updatedAtMs: now,\n }\n transaction.create(storeReturnRef, storeReturn)\n if (shiftOpen && shiftRef && cashCents > 0) {\n const event: PosCashEvent = {\n id: returnRef.id,\n type: 'refund',\n amountCents: cashCents,\n reason: `Return ${orderLabel}`,\n by: cashier.cashierId,\n atMs: now,\n orderId,\n }\n const cashEvents = Array.isArray(shiftSnapshot?.get('cashEvents')) ? shiftSnapshot!.get('cashEvents') : []\n transaction.update(shiftRef, { cashEvents: [...cashEvents, event] })\n }\n })\n\n // Restock what came back to the register's location.\n let restockedUnits = 0\n if (restock) {\n restockedUnits = await restockReturn(\n firestore,\n staff.hostRef,\n order,\n picks,\n String(register.data['locationId'] ?? order['locationId'] ?? '') || undefined,\n orderId,\n now,\n ).catch((error) => {\n console.error('[pos-return] restock failed', orderId, error)\n return 0\n })\n }\n\n // The drawer opens for the cash being handed back.\n if (cashCents > 0) {\n await deps.printer.kickDrawer({\n hostId: staff.hostId,\n registerId,\n reason: 'cash_refund',\n causeId: returnRef.id,\n createdBy: cashier.cashierId,\n })\n }\n\n const payload = {\n returnId: returnRef.id,\n refundedCents,\n outstandingCents: failed.reduce((sum, result) => sum + result.allocation.amountCents, 0),\n partial: failed.length > 0,\n tenders: results.map((result) => ({\n method: result.allocation.method,\n label: posTenderLabel(tenderOf(result.allocation.paymentId) ?? { method: result.allocation.method }),\n amountCents: result.allocation.amountCents,\n status: result.status,\n ...(result.error ? { error: result.error } : {}),\n })),\n cashOutCents: cashCents,\n restockedUnits,\n shiftId: shiftIdForReturn,\n fullyRefunded,\n }\n await claim.record(200, payload)\n if (refundedCents > 0) {\n await deps.afterRefund({\n hostId: staff.hostId,\n orderId,\n email: order['customerEmail'] ?? null,\n refundCents: refundedCents,\n closedTheOrder,\n fullyRefunded,\n returnedProductIds: [\n ...new Set(\n picks\n .map((pick) => String((order.lineItems?.[pick.index] as { productId?: string } | undefined)?.productId ?? ''))\n .filter(Boolean),\n ),\n ],\n lineIndexes: picks.map((pick) => pick.index),\n fullLineIndexes: reserved.completedLines,\n lines: lines.map((line) => ({ index: line.index, quantity: line.quantity, refundCents: line.refundCents })),\n returnId,\n storeReturn: storeReturn as unknown as CommerceModel.HostReturn,\n refundIds: results.flatMap((result) => (result.refundId ? [result.refundId] : [])),\n })\n }\n return { status: 200, body: payload }\n}\n\n/**\n * Units per order line that the store's OTHER returns — the online returns\n * flow (AGL-3611) — hold or already took back. The register's own returns are\n * left out: `returnedQuantities` on the order already counts them.\n */\nexport function unitsHeldByOnlineReturns(\n docs: ReadonlyArray<{ data(): FirebaseFirestore.DocumentData | undefined }>,\n): number[] {\n const held: number[] = []\n for (const doc of docs) {\n const entry = (doc.data() ?? {}) as CommerceModel.HostReturn\n if (entry.source === 'register' || !CommerceModel.returnHoldsUnits(entry)) continue\n for (const line of entry.lines ?? []) {\n const index = Number(line?.lineItemId)\n if (!Number.isInteger(index) || index < 0) continue\n held[index] = (held[index] ?? 0) + Math.max(0, Math.floor(Number(line?.quantity) || 0))\n }\n }\n return held\n}\n\n/** Whether a member is a workspace admin and this site's admin, asked now. */\nasync function memberIsPosManager(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n hostId: string,\n uid: string,\n): Promise<boolean> {\n const host = await hostRef.get()\n const role = (host.get('memberRoles') ?? {})[uid]\n return isPosManager(role, await deps.membership(uid, hostId))\n}\n\nclass ReturnRefusal extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message)\n }\n}\n\n/**\n * Store credit back on the card it came off. A voided or frozen card takes\n * nothing: a void is a fraud or refund decision, and re-crediting it would\n * undo that decision by the back door.\n */\nasync function creditGiftCard(\n firestore: FirebaseFirestore.Firestore,\n hostRef: FirebaseFirestore.DocumentReference,\n code: string,\n amountCents: number,\n orderId: string,\n now: number,\n): Promise<void> {\n const id = String(code ?? '').trim()\n if (!id || id.includes('/') || id.length > 64) throw new ReturnRefusal(409, 'That gift card payment does not name its card.')\n const ref = hostRef.collection('giftCards').doc(id)\n await firestore.runTransaction(async (transaction) => {\n const card = await transaction.get(ref)\n if (!card.exists) throw new ReturnRefusal(409, 'That gift card no longer exists.')\n if (card.get('voidedAtMs') != null || card.get('frozenAtMs') != null) {\n throw new ReturnRefusal(409, 'That gift card is voided or frozen, so it cannot take a refund.')\n }\n transaction.update(ref, {\n balanceCents: Math.max(0, Math.round(Number(card.get('balanceCents') ?? 0))) + amountCents,\n lastCreditAtMs: now,\n lastCreditOrderId: orderId,\n })\n })\n}\n\n/**\n * Store credit back to the plugin that keeps it (AGL-3640): a rewards\n * balance gets back what this payment took. Idempotent per `key`, so a\n * retried return gives back once. A friend's referral credit was a discount,\n * not a balance, and its provider gives nothing back — correctly: nobody is\n * owed money for it.\n */\nasync function restorePluginCredit(\n hostId: string,\n tender: { creditProviderId?: string; creditReference?: string } | undefined,\n amountCents: number,\n orderId: string,\n key: string,\n): Promise<void> {\n const entry = tender?.creditProviderId ? checkoutCreditProvider(tender.creditProviderId) : null\n if (!entry || !tender?.creditReference) {\n throw new ReturnRefusal(409, 'The store credit on this sale cannot take a refund right now.')\n }\n await entry.provider.restore({ hostId, reference: tender.creditReference, orderId, cents: amountCents, key })\n}\n\n/** A room charge taken back: a negative line on the same stay's folio. */\nasync function reverseFolio(\n hostRef: FirebaseFirestore.DocumentReference,\n reservationId: string,\n amountCents: number,\n orderId: string,\n now: number,\n): Promise<void> {\n const id = posOpsCleanId(reservationId)\n if (!id) throw new ReturnRefusal(409, 'That room charge does not name its stay.')\n const ref = hostRef.collection('reservations').doc(id)\n const firestore = hostRef.firestore\n await firestore.runTransaction(async (transaction) => {\n const stay = await transaction.get(ref)\n if (!stay.exists) throw new ReturnRefusal(409, 'That stay no longer exists.')\n const folio = Array.isArray(stay.get('folio')) ? stay.get('folio') : []\n transaction.update(ref, {\n folio: [...folio, { orderId, amountCents: -amountCents, note: 'Return at the register', atMs: now }],\n })\n })\n}\n\n/**\n * The returned units back on the shelf, at the register's location, with an\n * adjustment row each so the stock history says why the count went up.\n * Only variants that track stock move.\n */\nasync function restockReturn(\n firestore: FirebaseFirestore.Firestore,\n hostRef: FirebaseFirestore.DocumentReference,\n order: PosReturnSource & Record<string, any>,\n picks: readonly PosReturnPick[],\n locationId: string | undefined,\n orderId: string,\n now: number,\n): Promise<number> {\n const byProduct = new Map<string, Array<{ variantId?: string; quantity: number }>>()\n for (const pick of picks) {\n const line = (order.lineItems ?? [])[pick.index] as Record<string, any> | undefined\n const productId = String(line?.['productId'] ?? '')\n if (!productId) continue\n byProduct.set(productId, [\n ...(byProduct.get(productId) ?? []),\n { ...(line?.['variantId'] ? { variantId: String(line['variantId']) } : {}), quantity: pick.quantity },\n ])\n }\n let units = 0\n for (const [productId, lines] of byProduct) {\n const ref = hostRef.collection('products').doc(productId)\n units += await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return 0\n const product = CommerceModel.liftLegacyProduct(snapshot.data() as any)\n let variants = product.variants\n const rows: Array<{ variantId: string; quantity: number }> = []\n for (const line of lines) {\n const variantId = line.variantId ?? product.variants[0]?.id\n const variant = variants.find((candidate) => candidate.id === variantId)\n if (!variantId || !variant || variant.inventory == null) continue\n variants = CommerceModel.adjustVariantInventory({ variants }, variantId, line.quantity, locationId)\n rows.push({ variantId, quantity: line.quantity })\n }\n if (!rows.length) return 0\n transaction.update(ref, {\n variants,\n ...CommerceModel.productStockFields({ ...product, variants }),\n updatedAtMs: now,\n })\n for (const row of rows) {\n transaction.create(hostRef.collection('inventoryAdjustments').doc(createResourceUid()), {\n productId,\n variantId: row.variantId,\n delta: row.quantity,\n reason: 'refund',\n orderId,\n ...(locationId ? { locationId } : {}),\n atMs: now,\n } satisfies CommerceModel.InventoryAdjustment)\n }\n return rows.reduce((sum, row) => sum + row.quantity, 0)\n })\n }\n return units\n}\n\nexport function createPosReturnHandler(\n deps: () => PosReturnDeps = defaultPosReturnDeps,\n): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosReturn(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-return] failed', error)\n return res.status(500).json({ error: 'The return could not be refunded' })\n }\n }\n}\n\nexport const posReturnHandler = createPosReturnHandler()\n"],"names":["claimAttempt","createResourceUid","nameSearchKey","recordPluginPersonRefund","reverseOrderConversion","checkoutCreditProvider","CommerceModel","planPosReturn","posMoney","posRefundableByTender","posReturnedQuantities","posReturnLineValues","posTenderLabel","POS_STRIPE_TENDERS","splitPosRefund","ORDER_REFUNDED_EVENT","RETURN_REFUNDED_EVENT","raiseOrderEvent","applyGiftCardRiskToOrder","notifyOrderBuyer","decidePosRefundAuthority","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","posOpsIdempotencyKey","readPosRegister","isPosManager","resolvePosCashier","createStripeRefund","parsePosHardwareInPersonConfig","defaultPosReturnDeps","base","afterRefund","input","settle","label","work","error","console","orderId","hostRef","firestore","collection","doc","hostId","returnedProductIds","length","action","kind","reason","fullyRefunded","productIds","refId","email","amountCents","refundCents","closedTheSale","closedTheOrder","key","returnId","extra","refund","id","refundIds","lineItemIds","fullLineIndexes","full","return","status","storeReturn","lines","map","line","process","env","sellerHostId","recordRegisterHardwareReturn","config","registerOrderId","refundId","refundLineIndexes","lineIndexes","RETURNABLE","Set","returnSummary","raw","held","order","liftLegacyOrder","values","returned","number","formatOrderNumber","channel","createdAtMs","Number","customerEmail","customerName","totalCents","totals","refundedCents","returnable","has","String","lineItems","index","name","variantLabel","quantity","Math","max","round","valueCents","tenders","tender","method","refundableCents","findOrders","rawText","snapshot","text","trim","slice","orders","digits","replace","test","where","limit","get","docs","data","includes","orderBy","exists","parsePicks","Array","isArray","entry","parseSplit","paymentId","handlePosReturn","deps","req","body","register","gate","staff","found","Promise","all","unitsHeldByOnlineReturns","at","registerId","ref","idempotencyKey","picks","requestedSplit","restock","reasonCode","RETURN_REASONS","orderRef","storeReturns","orderDisputeBlocksRefund","plan","split","cashier","cashierIsManager","cashierId","uid","isManager","memberIsPosManager","authority","limitCents","settings","refundLimitCents","managerAssertion","needsCash","allocations","some","needsStripe","openShiftId","requireOpenShift","STRIPE_SECRET_KEY","claimed","scopeId","orgId","busyMessage","replay","claim","reserved","runTransaction","transaction","fresh","ReturnRefusal","freshPlan","freshSplit","prior","returnedQuantities","posPaymentRefunds","update","lineCents","completedLines","release","message","tenderOf","find","ordered","sort","a","b","results","allocation","anyMoved","result","paymentIntentId","stripeKey","metadata","fetch","push","creditGiftCard","giftCardId","now","reverseFolio","reservationId","restorePluginCredit","catch","rollback","failed","filter","reduce","sum","cashCents","returnRef","storeReturnRef","orderLabel","pick","shiftIdForReturn","freshRegister","shiftId","shiftRef","shiftSnapshot","shiftOpen","Boolean","failedCents","orderRefunded","canTransitionOrder","Object","entries","count","tenderWords","join","units","refundedLineItemIds","timeline","appendOrderEvent","approvedBy","record","orderNumber","restocked","locationId","atMs","create","restockedLines","lineItemId","registerLocationId","undefined","requestedBy","source","merchantNote","refundedAtMs","event","detail","updatedAtMs","type","by","cashEvents","restockedUnits","restockReturn","printer","kickDrawer","causeId","createdBy","payload","outstandingCents","partial","cashOutCents","productId","flatMap","returnHoldsUnits","isInteger","floor","host","role","membership","Error","code","card","balanceCents","lastCreditAtMs","lastCreditOrderId","creditProviderId","creditReference","provider","restore","reference","cents","stay","folio","note","byProduct","Map","set","variantId","product","liftLegacyProduct","variants","rows","variant","candidate","inventory","adjustVariantInventory","productStockFields","row","delta","createPosReturnHandler","res","outcome","json","posReturnHandler"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-return.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { claimAttempt, type PluginApiHandler, type PluginApiRequest } from '@aglyn/aglyn/server'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { nameSearchKey } from '@aglyn/aglyn/app-utils/name-search'\nimport { recordPluginPersonRefund } from '@aglyn/aglyn/plugin-manager/plugin-person-records'\nimport { reverseOrderConversion } from '@aglyn/aglyn/plugin-manager/plugin-conversion-credit'\nimport { checkoutCreditProvider } from '@aglyn/aglyn/plugin-manager/plugin-checkout-credits'\nimport * as CommerceModel from '../model'\nimport {\n planPosReturn,\n posMoney,\n posRefundableByTender,\n posReturnedQuantities,\n posReturnLineValues,\n posTenderLabel,\n POS_STRIPE_TENDERS,\n splitPosRefund,\n type PosCashEvent,\n type PosRefundAllocation,\n type PosRegisterReturn,\n type PosReturnPick,\n type PosReturnSource,\n} from '../model/commerce-pos-ops'\nimport { ORDER_REFUNDED_EVENT, RETURN_REFUNDED_EVENT } from '../model/order-events'\nimport { raiseOrderEvent } from './order-events'\nimport { applyGiftCardRiskToOrder } from './gift-card-risk'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { decidePosRefundAuthority } from './pos-refund-authority'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n posOpsBody,\n posOpsCleanId,\n posOpsIdempotencyKey,\n readPosRegister,\n isPosManager,\n resolvePosCashier,\n type PosOpsDeps,\n} from './pos-ops-gate'\nimport { createStripeRefund } from './stripe-refund'\nimport { parsePosHardwareInPersonConfig } from '../model/pos-hardware'\n\n/*==========================================\n * RETURNS AT THE REGISTER (AGL-3609): `POST /api/commerce/pos-return`.\n *\n * find an order by its number (typed or scanned off the receipt's\n * barcode), its id, or the customer's email\n * refund picked lines and quantities back, restocked to the register's\n * location, the money sent back to the sale's OWN payments\n *\n * Each payment gets its money back the way it came: a card through Stripe\n * (the same refund the order dialog sends), cash out of the drawer — a cash\n * event on the open shift, so the count still balances — a gift card\n * re-credited, and a room charge reversed on the stay's folio.\n *\n * THE ORDER OF WORK is what keeps a failure honest:\n * 1. every refusal that needs no money to move (role, limit, quantities,\n * the split, the shift rule) — before anything is written;\n * 2. the attempt claim, so a retried tap replays instead of refunding twice;\n * 3. the RESERVATION on the order, in a transaction that re-reads it: the\n * cents, the units and each payment's share are taken before any money\n * moves, so two registers returning the same line cannot both succeed;\n * 4. the Stripe refunds first, because they are the ones that can say no —\n * if the first one does, nothing has moved and the reservation is\n * handed back whole;\n * 5. then the tenders that cannot fail for a reason outside this database.\n * A card that fails after another payment already went back leaves a\n * PARTIAL return: what moved is recorded, what did not is handed back on\n * the order, and the cashier is told the amount still owed.\n *=========================================*/\n\nexport interface PosReturnSideEffects {\n hostId: string\n orderId: string\n /** The return's id, the same under the register and in the store's returns. */\n returnId: string\n /** The return as the store's returns list holds it, for the return event. */\n storeReturn: CommerceModel.HostReturn\n /** The Stripe refunds the return made, if any, for the refund event. */\n refundIds: string[]\n email: string | null | undefined\n refundCents: number\n closedTheOrder: boolean\n returnedProductIds: string[]\n fullyRefunded: boolean\n lineIndexes: number[]\n /** The lines this return finished taking back in full. */\n fullLineIndexes: number[]\n /**\n * Each line taken back, with its units and its share of the refund, for\n * whatever follows a line in particular — a card reader sold in person,\n * whose hardware order is told (AGL-4038).\n */\n lines?: Array<{ index: number; quantity: number; refundCents: number }>\n}\n\nexport interface PosReturnDeps extends PosOpsDeps {\n /** What every refund tells the rest of the workspace, after the money moved. Never throws. */\n afterRefund(input: PosReturnSideEffects): Promise<void>\n}\n\nexport function defaultPosReturnDeps(): PosReturnDeps {\n const base = defaultPosOpsDeps()\n return {\n ...base,\n afterRefund: async (input) => {\n const settle = async (label: string, work: () => Promise<unknown>) => {\n try {\n await work()\n } catch (error) {\n console.error(`[pos-return] ${label} failed`, input.orderId, error)\n }\n }\n const hostRef = base.firestore().collection('hosts').doc(input.hostId)\n if (input.returnedProductIds.length) {\n await settle('gift card void', () =>\n applyGiftCardRiskToOrder({\n firestore: base.firestore(),\n hostRef,\n orderId: input.orderId,\n action: {\n kind: 'void',\n reason: 'refund',\n ...(input.fullyRefunded ? {} : { productIds: input.returnedProductIds }),\n },\n }),\n )\n }\n await settle('person refund', () =>\n recordPluginPersonRefund({\n hostId: input.hostId,\n refId: input.orderId,\n email: input.email,\n amountCents: input.refundCents,\n closedTheSale: input.closedTheOrder,\n }),\n )\n await settle('conversion reversal', () =>\n reverseOrderConversion({\n hostId: input.hostId,\n orderId: input.orderId,\n amountCents: input.refundCents,\n closedTheOrder: input.closedTheOrder,\n }),\n )\n // The same events a refund from the order dialog and a refunded online\n // return raise, so accounting and the merchant's webhooks hear a\n // register return too (AGL-3611's events).\n await settle('refund event', () =>\n raiseOrderEvent(ORDER_REFUNDED_EVENT, {\n hostId: input.hostId,\n orderId: input.orderId,\n key: `pos-return:${input.returnId}`,\n extra: {\n refund: {\n id: input.refundIds[0] ?? null,\n amountCents: input.refundCents,\n lineItemIds: input.fullLineIndexes,\n full: input.fullyRefunded,\n },\n },\n }),\n )\n await settle('return event', () =>\n raiseOrderEvent(RETURN_REFUNDED_EVENT, {\n hostId: input.hostId,\n orderId: input.orderId,\n key: `return-refunded:${input.returnId}`,\n extra: {\n return: {\n id: input.returnId,\n status: input.storeReturn.status,\n lines: input.storeReturn.lines.map((line) => ({ ...line })),\n refundCents: input.storeReturn.refundCents ?? null,\n },\n },\n }),\n )\n // A card reader the platform's own store sold in person (AGL-4038):\n // its hardware order and retained sale record take the return too.\n // Answered from the environment for every other store.\n if (input.lines?.length && parsePosHardwareInPersonConfig(process.env)?.sellerHostId === input.hostId) {\n await settle('hardware return', async () =>\n (await import('./pos-hardware-returns')).recordRegisterHardwareReturn({\n config: parsePosHardwareInPersonConfig(process.env),\n hostId: input.hostId,\n registerOrderId: input.orderId,\n returnId: input.returnId,\n refundCents: input.refundCents,\n lines: input.lines ?? [],\n }),\n )\n }\n await settle('buyer notice', () =>\n notifyOrderBuyer({ hostId: input.hostId, orderId: input.orderId }, 'refunded', {\n refundId: '',\n refundCents: input.refundCents,\n refundLineIndexes: input.lineIndexes,\n fullyRefunded: input.fullyRefunded,\n }),\n )\n },\n }\n}\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\n/** The statuses a return may be taken against. */\nconst RETURNABLE: ReadonlySet<string> = new Set([\n 'paid',\n 'partially_fulfilled',\n 'fulfilled',\n 'delivered',\n])\n\n/** One order as the return dialog lists it. */\nfunction returnSummary(id: string, raw: Record<string, any>, held: readonly number[] = []) {\n const order = CommerceModel.liftLegacyOrder(raw as any) as unknown as PosReturnSource &\n Record<string, any>\n const values = posReturnLineValues(order)\n const returned = posReturnedQuantities(order)\n return {\n id,\n number: order['number'] ?? null,\n label: CommerceModel.formatOrderNumber(order as any, id),\n status: order['status'],\n channel: order['channel'] ?? 'online',\n createdAtMs: Number(order['createdAtMs'] ?? 0),\n customerEmail: order['customerEmail'] ?? null,\n customerName: order['customerName'] ?? null,\n totalCents: Number(order.totals?.totalCents ?? order.amountCents ?? 0),\n refundedCents: Number(order.refundedCents ?? 0),\n returnable: RETURNABLE.has(String(order['status'])),\n lines: (order.lineItems ?? []).map((line: any, index: number) => ({\n index,\n name: String(line?.name ?? ''),\n variantLabel: line?.variantLabel ?? null,\n quantity: Math.max(1, Math.round(Number(line?.quantity ?? 1))),\n returned: returned[index] ?? 0,\n // Units an online return holds or already took (AGL-3611).\n held: held[index] ?? 0,\n valueCents: values[index] ?? 0,\n })),\n tenders: posRefundableByTender(order).map((tender) => ({\n id: tender.id,\n method: tender.method,\n label: posTenderLabel(tender),\n amountCents: tender.amountCents,\n refundableCents: tender.refundableCents,\n })),\n }\n}\n\nasync function findOrders(\n hostRef: FirebaseFirestore.DocumentReference,\n rawText: unknown,\n): Promise<Array<{ id: string; data: Record<string, any> }>> {\n const text = String(rawText ?? '').trim().slice(0, 200)\n if (!text) return []\n const orders = hostRef.collection('orders')\n const digits = text.replace(/^#/, '')\n if (/^\\d{1,12}$/.test(digits)) {\n const snapshot = await orders.where('number', '==', Number(digits)).limit(5).get()\n return snapshot.docs.map((doc) => ({ id: doc.id, data: (doc.data() ?? {}) as Record<string, any> }))\n }\n if (text.includes('@')) {\n const snapshot = await orders\n .where('customerEmailLower', '==', nameSearchKey(text))\n .orderBy('createdAtMs', 'desc')\n .limit(10)\n .get()\n return snapshot.docs.map((doc) => ({ id: doc.id, data: (doc.data() ?? {}) as Record<string, any> }))\n }\n const id = posOpsCleanId(text)\n if (!id) return []\n const snapshot = await orders.doc(id).get()\n return snapshot.exists ? [{ id, data: (snapshot.data() ?? {}) as Record<string, any> }] : []\n}\n\nfunction parsePicks(raw: unknown): PosReturnPick[] {\n if (!Array.isArray(raw)) return []\n return raw.slice(0, 200).map((entry: any) => ({\n index: Number(entry?.index),\n quantity: Number(entry?.quantity),\n }))\n}\n\nfunction parseSplit(raw: unknown): Array<{ paymentId: string; amountCents: number }> | null {\n if (!Array.isArray(raw) || !raw.length) return null\n return raw.slice(0, 20).map((entry: any) => ({\n paymentId: String(entry?.paymentId ?? ''),\n amountCents: Number(entry?.amountCents),\n }))\n}\n\n/** What a refunded tender moved, and how to hand back a reservation it did not. */\ninterface TenderResult {\n allocation: PosRefundAllocation\n status: 'refunded' | 'failed'\n refundId?: string\n error?: string\n}\n\nexport async function handlePosReturn(deps: PosReturnDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = posOpsBody(req)\n // Finding a sale to take back and refunding it are both the refund key's\n // (AGL-4047).\n const gate = await authorizePosOps(deps, req, body['hostId'], 'posRefund')\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n const action = String(body['action'] ?? '')\n\n if (action === 'find') {\n const found = await findOrders(staff.hostRef, body['text'])\n const held = await Promise.all(\n found.map(async (entry) =>\n unitsHeldByOnlineReturns(\n (await staff.hostRef.collection('returns').where('orderId', '==', entry.id).get()).docs,\n ),\n ),\n )\n return {\n status: 200,\n body: { orders: found.map((entry, at) => returnSummary(entry.id, entry.data, held[at])) },\n }\n }\n if (action !== 'refund') return { status: 400, body: { error: 'Unknown action' } }\n\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const registerId = register.ref.id\n const orderId = posOpsCleanId(body['orderId'])\n if (!orderId) return { status: 400, body: { error: 'Missing orderId' } }\n const idempotencyKey = posOpsIdempotencyKey(req)\n if (!idempotencyKey) {\n return { status: 400, body: { error: 'A refund needs an Idempotency-Key header.' } }\n }\n const picks = parsePicks(body['lines'])\n const requestedSplit = parseSplit(body['tenders'])\n const restock = body['restock'] !== false\n const reason = String(body['reason'] ?? '').trim().slice(0, 200)\n const reasonCode: CommerceModel.ReturnReason = CommerceModel.RETURN_REASONS.includes(\n body['reasonCode'] as CommerceModel.ReturnReason,\n )\n ? (body['reasonCode'] as CommerceModel.ReturnReason)\n : 'other'\n const firestore = deps.firestore()\n const orderRef = staff.hostRef.collection('orders').doc(orderId)\n const storeReturns = staff.hostRef.collection('returns').where('orderId', '==', orderId)\n\n // 1. Every refusal that needs no money to move.\n const snapshot = await orderRef.get()\n if (!snapshot.exists) return { status: 404, body: { error: 'Unknown order' } }\n const order = CommerceModel.liftLegacyOrder(snapshot.data() as any) as unknown as PosReturnSource &\n Record<string, any>\n if (!RETURNABLE.has(String(order['status']))) {\n return { status: 409, body: { error: `Orders in \"${order['status']}\" cannot be returned.` } }\n }\n if (CommerceModel.orderDisputeBlocksRefund(order as any)) {\n return {\n status: 409,\n body: {\n error:\n 'A chargeback is open on this order, so it was not refunded. Respond to the ' +\n 'dispute or accept it in the Stripe dashboard first.',\n },\n }\n }\n const plan = planPosReturn(order, picks, unitsHeldByOnlineReturns((await storeReturns.get()).docs))\n if ('error' in plan) return { status: 400, body: { error: plan.error } }\n const split = splitPosRefund(order, plan.refundCents, requestedSplit)\n if ('error' in split) return { status: 400, body: { error: split.error } }\n\n const cashier = await resolvePosCashier(deps, staff, registerId, body['cashierAssertion'])\n // The limit is the person at the till's: a PIN-switched cashier is held\n // to it even on a device someone who holds the approval signed in on.\n const cashierIsManager =\n cashier.cashierId === staff.uid\n ? staff.isManager\n : await memberIsPosManager(deps, staff.hostRef, staff.hostId, cashier.cashierId)\n const authority = await decidePosRefundAuthority(deps, {\n hostId: staff.hostId,\n hostRef: staff.hostRef,\n registerId,\n refundCents: plan.refundCents,\n limitCents: staff.settings.refundLimitCents,\n isManager: cashierIsManager,\n managerAssertion: body['managerAssertion'],\n })\n if ('body' in authority) return { status: 403, body: authority.body }\n\n const needsCash = split.allocations.some((slice) => slice.method === 'cash')\n const needsStripe = split.allocations.some((slice) => POS_STRIPE_TENDERS.has(slice.method))\n const openShiftId = String(register.data['openShiftId'] ?? '') || null\n if (needsCash && !openShiftId && staff.settings.requireOpenShift) {\n return { status: 409, body: { error: 'Open a shift before paying cash out of the drawer.' } }\n }\n if (needsStripe && !process.env.STRIPE_SECRET_KEY) {\n return { status: 501, body: { error: 'Payments are not configured.' } }\n }\n\n // 2. The attempt claim.\n const claimed = await claimAttempt(firestore as any, {\n kind: 'pos-return',\n scopeId: staff.hostId,\n orgId: staff.orgId,\n key: `${orderId}:${idempotencyKey}`,\n busyMessage: 'This return is already being refunded',\n })\n if ('replay' in claimed) return { status: claimed.replay.status, body: claimed.replay.body as any }\n const claim = claimed.claim\n\n // 3. The reservation, re-read under the transaction.\n type Reserved = {\n refundCents: number\n lineCents: number[]\n completedLines: number[]\n allocations: PosRefundAllocation[]\n prior: { refundedCents: number; returnedQuantities: Record<string, number>; posPaymentRefunds: Record<string, number> }\n }\n let reserved: Reserved\n try {\n reserved = await firestore.runTransaction(async (transaction) => {\n const fresh = CommerceModel.liftLegacyOrder(\n ((await transaction.get(orderRef)).data() ?? {}) as any,\n ) as unknown as PosReturnSource & Record<string, any>\n if (!RETURNABLE.has(String(fresh['status']))) {\n throw new ReturnRefusal(409, `Orders in \"${fresh['status']}\" cannot be returned.`)\n }\n const freshPlan = planPosReturn(\n fresh,\n picks,\n unitsHeldByOnlineReturns((await transaction.get(storeReturns)).docs),\n )\n if ('error' in freshPlan) throw new ReturnRefusal(409, freshPlan.error)\n const freshSplit = splitPosRefund(fresh, freshPlan.refundCents, requestedSplit)\n if ('error' in freshSplit) throw new ReturnRefusal(409, freshSplit.error)\n const prior = {\n refundedCents: Number(fresh.refundedCents ?? 0),\n returnedQuantities: { ...(fresh.returnedQuantities ?? {}) },\n posPaymentRefunds: { ...(fresh.posPaymentRefunds ?? {}) },\n }\n const posPaymentRefunds = { ...prior.posPaymentRefunds }\n for (const slice of freshSplit.allocations) {\n posPaymentRefunds[slice.paymentId] = (posPaymentRefunds[slice.paymentId] ?? 0) + slice.amountCents\n }\n transaction.update(orderRef, {\n refundedCents: prior.refundedCents + freshPlan.refundCents,\n returnedQuantities: freshPlan.returnedQuantities,\n posPaymentRefunds,\n })\n return {\n refundCents: freshPlan.refundCents,\n lineCents: freshPlan.lineCents,\n completedLines: freshPlan.completedLines,\n allocations: freshSplit.allocations,\n prior,\n }\n })\n } catch (error) {\n await claim.release()\n if (error instanceof ReturnRefusal) return { status: error.status, body: { error: error.message } }\n throw error\n }\n\n // 4–5. The money, Stripe first.\n const tenders = posRefundableByTender(order)\n const tenderOf = (paymentId: string) => tenders.find((tender) => tender.id === paymentId)\n const ordered = [...reserved.allocations].sort(\n (a, b) => Number(POS_STRIPE_TENDERS.has(b.method)) - Number(POS_STRIPE_TENDERS.has(a.method)),\n )\n const results: TenderResult[] = []\n for (const allocation of ordered) {\n const tender = tenderOf(allocation.paymentId)\n const anyMoved = results.some((result) => result.status === 'refunded')\n try {\n if (POS_STRIPE_TENDERS.has(allocation.method)) {\n const paymentIntentId = tender?.paymentIntentId ?? String(order['paymentIntentId'] ?? '')\n if (!paymentIntentId) throw new ReturnRefusal(409, 'That card payment has nothing Stripe can refund.')\n const refund = await createStripeRefund({\n paymentIntentId,\n amountCents: allocation.amountCents,\n idempotencyKey: claim.stripeKey ? `${claim.stripeKey}:${allocation.paymentId}` : null,\n metadata: { hostId: staff.hostId, orderId, registerId, kind: 'pos-return' },\n fetch: deps.fetch,\n })\n if ('error' in refund) throw new ReturnRefusal(refund.status, refund.error)\n results.push({ allocation, status: 'refunded', refundId: refund.refundId })\n } else if (allocation.method === 'gift_card') {\n await creditGiftCard(firestore, staff.hostRef, tender?.giftCardId ?? '', allocation.amountCents, orderId, deps.now())\n results.push({ allocation, status: 'refunded' })\n } else if (allocation.method === 'folio') {\n await reverseFolio(staff.hostRef, tender?.reservationId ?? String(order['reservationId'] ?? ''), allocation.amountCents, orderId, deps.now())\n results.push({ allocation, status: 'refunded' })\n } else if (allocation.method === 'credit') {\n await restorePluginCredit(\n staff.hostRef.id,\n tender,\n allocation.amountCents,\n orderId,\n `pos-return:${orderId}:${allocation.paymentId}:${reserved.prior.posPaymentRefunds[allocation.paymentId] ?? 0}`,\n )\n results.push({ allocation, status: 'refunded' })\n } else {\n // Cash: recorded on the drawer with the return itself, below.\n results.push({ allocation, status: 'refunded' })\n }\n } catch (error) {\n const message = error instanceof ReturnRefusal ? error.message : 'Refund failed'\n if (!(error instanceof ReturnRefusal)) console.error('[pos-return] tender failed', allocation, error)\n if (!anyMoved) {\n // Nothing has moved: hand the whole reservation back and let the\n // same attempt be tried again.\n await firestore\n .runTransaction(async (transaction) => {\n await transaction.get(orderRef)\n transaction.update(orderRef, reserved.prior)\n })\n .catch((rollback) => console.error('[pos-return] rollback failed', orderId, rollback))\n await claim.release()\n return {\n status: error instanceof ReturnRefusal ? error.status : 502,\n body: { error: message },\n }\n }\n results.push({ allocation, status: 'failed', error: message })\n }\n }\n\n const failed = results.filter((result) => result.status === 'failed')\n const refundedCents = results\n .filter((result) => result.status === 'refunded')\n .reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const cashCents = results\n .filter((result) => result.status === 'refunded' && result.allocation.method === 'cash')\n .reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const now = deps.now()\n // One id names the return under the register (its tenders and shift) and\n // in the store's returns list beside the online ones (AGL-3611).\n const returnId = createResourceUid()\n const returnRef = register.ref.collection('returns').doc(returnId)\n const storeReturnRef = staff.hostRef.collection('returns').doc(returnId)\n const orderLabel = CommerceModel.formatOrderNumber(order as any, orderId)\n const lines = picks.map((pick, at) => ({\n index: pick.index,\n quantity: pick.quantity,\n name: String(order.lineItems?.[pick.index]?.name ?? ''),\n refundCents: reserved.lineCents[at] ?? 0,\n }))\n\n // The record, the drawer, the order: one transaction.\n let closedTheOrder = false\n let fullyRefunded = false\n let shiftIdForReturn: string | null = null\n let storeReturn: CommerceModel.HostReturn | null = null\n await firestore.runTransaction(async (transaction) => {\n const freshRegister = await transaction.get(register.ref)\n const shiftId = String(freshRegister.get('openShiftId') ?? '') || null\n const shiftRef = shiftId ? register.ref.collection('shifts').doc(shiftId) : null\n const shiftSnapshot = shiftRef ? await transaction.get(shiftRef) : null\n const fresh = CommerceModel.liftLegacyOrder(\n ((await transaction.get(orderRef)).data() ?? {}) as any,\n ) as unknown as PosReturnSource & Record<string, any>\n const shiftOpen = Boolean(shiftSnapshot?.exists && shiftSnapshot.get('status') === 'open')\n shiftIdForReturn = shiftOpen ? shiftId : null\n\n // Hand back what a failed card did not refund.\n const failedCents = failed.reduce((sum, result) => sum + result.allocation.amountCents, 0)\n const posPaymentRefunds = { ...(fresh.posPaymentRefunds ?? {}) }\n for (const result of failed) {\n posPaymentRefunds[result.allocation.paymentId] = Math.max(\n 0,\n (posPaymentRefunds[result.allocation.paymentId] ?? 0) - result.allocation.amountCents,\n )\n }\n const orderRefunded = Math.max(0, Number(fresh.refundedCents ?? 0) - failedCents)\n const totalCents = Number(fresh.totals?.totalCents ?? fresh.amountCents ?? 0)\n fullyRefunded = orderRefunded >= totalCents && totalCents > 0\n closedTheOrder =\n fullyRefunded &&\n fresh['status'] !== 'refunded' &&\n CommerceModel.canTransitionOrder(fresh['status'] as CommerceModel.OrderStatus, 'refunded')\n const completedLines = Object.entries(fresh.returnedQuantities ?? {})\n .filter(([index, count]) => count >= Math.max(1, Number(fresh.lineItems?.[Number(index)]?.quantity ?? 1)))\n .map(([index]) => Number(index))\n const tenderWords = results\n .map(\n (result) =>\n `${posMoney(result.allocation.amountCents)} to ${posTenderLabel(\n tenderOf(result.allocation.paymentId) ?? { method: result.allocation.method },\n )}${result.status === 'failed' ? ' (FAILED)' : ''}`,\n )\n .join(', ')\n const units = picks.reduce((sum, pick) => sum + pick.quantity, 0)\n transaction.update(orderRef, {\n refundedCents: orderRefunded,\n posPaymentRefunds,\n ...(closedTheOrder ? { status: 'refunded' } : {}),\n ...(completedLines.length\n ? {\n refundedLineItemIds: [\n ...new Set([...(fresh.refundedLineItemIds ?? []), ...completedLines]),\n ].sort((a, b) => a - b),\n }\n : {}),\n timeline: CommerceModel.appendOrderEvent(\n fresh as any,\n 'refund',\n `Returned at the register: ${units} ${units === 1 ? 'item' : 'items'}, ${tenderWords}` +\n (restock ? '; restocked' : '') +\n (authority.approvedBy ? '; approved by a manager' : ''),\n now,\n ),\n })\n const record: PosRegisterReturn = {\n hostId: staff.hostId,\n registerId,\n orderId,\n ...(typeof order['number'] === 'number' ? { orderNumber: order['number'] } : {}),\n ...(shiftOpen && shiftId ? { shiftId } : {}),\n lines,\n refundedCents,\n tenders: results.map((result) => ({\n ...result.allocation,\n status: result.status,\n ...(result.refundId ? { refundId: result.refundId } : {}),\n ...(result.error ? { error: result.error } : {}),\n })),\n restocked: restock,\n ...(register.data['locationId'] ? { locationId: String(register.data['locationId']) } : {}),\n cashierId: cashier.cashierId,\n ...(authority.approvedBy ? { approvedBy: authority.approvedBy } : {}),\n ...(reason ? { reason } : {}),\n status: failed.length ? 'partial' : 'refunded',\n atMs: now,\n }\n transaction.create(returnRef, record)\n const restockedLines = picks.map((pick) => ({ lineItemId: pick.index, quantity: pick.quantity }))\n const registerLocationId = register.data['locationId'] ? String(register.data['locationId']) : undefined\n storeReturn = {\n orderId,\n orderNumber: orderLabel,\n customerEmail: (order['customerEmail'] as string | undefined) ?? null,\n customerName: (order['customerName'] as string | undefined) ?? null,\n lines: picks.map((pick) => ({ lineItemId: pick.index, quantity: pick.quantity, reason: reasonCode })),\n status: 'refunded',\n requestedBy: 'merchant',\n source: 'register',\n registerId,\n ...(reason ? { merchantNote: reason } : {}),\n ...(restock\n ? {\n restock: {\n lines: restockedLines,\n ...(registerLocationId ? { locationId: registerLocationId } : {}),\n atMs: now,\n },\n }\n : {}),\n refundedAtMs: now,\n refundCents: refundedCents,\n timeline: [\n {\n atMs: now,\n event: 'refunded',\n detail: `At the register${failed.length ? `; ${posMoney(failed.reduce((sum, result) => sum + result.allocation.amountCents, 0))} still owed` : ''}`,\n },\n ],\n createdAtMs: now,\n updatedAtMs: now,\n }\n transaction.create(storeReturnRef, storeReturn)\n if (shiftOpen && shiftRef && cashCents > 0) {\n const event: PosCashEvent = {\n id: returnRef.id,\n type: 'refund',\n amountCents: cashCents,\n reason: `Return ${orderLabel}`,\n by: cashier.cashierId,\n atMs: now,\n orderId,\n }\n const cashEvents = Array.isArray(shiftSnapshot?.get('cashEvents')) ? shiftSnapshot!.get('cashEvents') : []\n transaction.update(shiftRef, { cashEvents: [...cashEvents, event] })\n }\n })\n\n // Restock what came back to the register's location.\n let restockedUnits = 0\n if (restock) {\n restockedUnits = await restockReturn(\n firestore,\n staff.hostRef,\n order,\n picks,\n String(register.data['locationId'] ?? order['locationId'] ?? '') || undefined,\n orderId,\n now,\n ).catch((error) => {\n console.error('[pos-return] restock failed', orderId, error)\n return 0\n })\n }\n\n // The drawer opens for the cash being handed back.\n if (cashCents > 0) {\n await deps.printer.kickDrawer({\n hostId: staff.hostId,\n registerId,\n reason: 'cash_refund',\n causeId: returnRef.id,\n createdBy: cashier.cashierId,\n })\n }\n\n const payload = {\n returnId: returnRef.id,\n refundedCents,\n outstandingCents: failed.reduce((sum, result) => sum + result.allocation.amountCents, 0),\n partial: failed.length > 0,\n tenders: results.map((result) => ({\n method: result.allocation.method,\n label: posTenderLabel(tenderOf(result.allocation.paymentId) ?? { method: result.allocation.method }),\n amountCents: result.allocation.amountCents,\n status: result.status,\n ...(result.error ? { error: result.error } : {}),\n })),\n cashOutCents: cashCents,\n restockedUnits,\n shiftId: shiftIdForReturn,\n fullyRefunded,\n }\n await claim.record(200, payload)\n if (refundedCents > 0) {\n await deps.afterRefund({\n hostId: staff.hostId,\n orderId,\n email: order['customerEmail'] ?? null,\n refundCents: refundedCents,\n closedTheOrder,\n fullyRefunded,\n returnedProductIds: [\n ...new Set(\n picks\n .map((pick) => String((order.lineItems?.[pick.index] as { productId?: string } | undefined)?.productId ?? ''))\n .filter(Boolean),\n ),\n ],\n lineIndexes: picks.map((pick) => pick.index),\n fullLineIndexes: reserved.completedLines,\n lines: lines.map((line) => ({ index: line.index, quantity: line.quantity, refundCents: line.refundCents })),\n returnId,\n storeReturn: storeReturn as unknown as CommerceModel.HostReturn,\n refundIds: results.flatMap((result) => (result.refundId ? [result.refundId] : [])),\n })\n }\n return { status: 200, body: payload }\n}\n\n/**\n * Units per order line that the store's OTHER returns — the online returns\n * flow (AGL-3611) — hold or already took back. The register's own returns are\n * left out: `returnedQuantities` on the order already counts them.\n */\nexport function unitsHeldByOnlineReturns(\n docs: ReadonlyArray<{ data(): FirebaseFirestore.DocumentData | undefined }>,\n): number[] {\n const held: number[] = []\n for (const doc of docs) {\n const entry = (doc.data() ?? {}) as CommerceModel.HostReturn\n if (entry.source === 'register' || !CommerceModel.returnHoldsUnits(entry)) continue\n for (const line of entry.lines ?? []) {\n const index = Number(line?.lineItemId)\n if (!Number.isInteger(index) || index < 0) continue\n held[index] = (held[index] ?? 0) + Math.max(0, Math.floor(Number(line?.quantity) || 0))\n }\n }\n return held\n}\n\n/** Whether a member holds \"Approve refunds over the register's limit\" here, asked now (AGL-4047). */\nasync function memberIsPosManager(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n hostId: string,\n uid: string,\n): Promise<boolean> {\n return isPosManager(await hostRef.get(), uid, await deps.membership(uid, hostId))\n}\n\nclass ReturnRefusal extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message)\n }\n}\n\n/**\n * Store credit back on the card it came off. A voided or frozen card takes\n * nothing: a void is a fraud or refund decision, and re-crediting it would\n * undo that decision by the back door.\n */\nasync function creditGiftCard(\n firestore: FirebaseFirestore.Firestore,\n hostRef: FirebaseFirestore.DocumentReference,\n code: string,\n amountCents: number,\n orderId: string,\n now: number,\n): Promise<void> {\n const id = String(code ?? '').trim()\n if (!id || id.includes('/') || id.length > 64) throw new ReturnRefusal(409, 'That gift card payment does not name its card.')\n const ref = hostRef.collection('giftCards').doc(id)\n await firestore.runTransaction(async (transaction) => {\n const card = await transaction.get(ref)\n if (!card.exists) throw new ReturnRefusal(409, 'That gift card no longer exists.')\n if (card.get('voidedAtMs') != null || card.get('frozenAtMs') != null) {\n throw new ReturnRefusal(409, 'That gift card is voided or frozen, so it cannot take a refund.')\n }\n transaction.update(ref, {\n balanceCents: Math.max(0, Math.round(Number(card.get('balanceCents') ?? 0))) + amountCents,\n lastCreditAtMs: now,\n lastCreditOrderId: orderId,\n })\n })\n}\n\n/**\n * Store credit back to the plugin that keeps it (AGL-3640): a rewards\n * balance gets back what this payment took. Idempotent per `key`, so a\n * retried return gives back once. A friend's referral credit was a discount,\n * not a balance, and its provider gives nothing back — correctly: nobody is\n * owed money for it.\n */\nasync function restorePluginCredit(\n hostId: string,\n tender: { creditProviderId?: string; creditReference?: string } | undefined,\n amountCents: number,\n orderId: string,\n key: string,\n): Promise<void> {\n const entry = tender?.creditProviderId ? checkoutCreditProvider(tender.creditProviderId) : null\n if (!entry || !tender?.creditReference) {\n throw new ReturnRefusal(409, 'The store credit on this sale cannot take a refund right now.')\n }\n await entry.provider.restore({ hostId, reference: tender.creditReference, orderId, cents: amountCents, key })\n}\n\n/** A room charge taken back: a negative line on the same stay's folio. */\nasync function reverseFolio(\n hostRef: FirebaseFirestore.DocumentReference,\n reservationId: string,\n amountCents: number,\n orderId: string,\n now: number,\n): Promise<void> {\n const id = posOpsCleanId(reservationId)\n if (!id) throw new ReturnRefusal(409, 'That room charge does not name its stay.')\n const ref = hostRef.collection('reservations').doc(id)\n const firestore = hostRef.firestore\n await firestore.runTransaction(async (transaction) => {\n const stay = await transaction.get(ref)\n if (!stay.exists) throw new ReturnRefusal(409, 'That stay no longer exists.')\n const folio = Array.isArray(stay.get('folio')) ? stay.get('folio') : []\n transaction.update(ref, {\n folio: [...folio, { orderId, amountCents: -amountCents, note: 'Return at the register', atMs: now }],\n })\n })\n}\n\n/**\n * The returned units back on the shelf, at the register's location, with an\n * adjustment row each so the stock history says why the count went up.\n * Only variants that track stock move.\n */\nasync function restockReturn(\n firestore: FirebaseFirestore.Firestore,\n hostRef: FirebaseFirestore.DocumentReference,\n order: PosReturnSource & Record<string, any>,\n picks: readonly PosReturnPick[],\n locationId: string | undefined,\n orderId: string,\n now: number,\n): Promise<number> {\n const byProduct = new Map<string, Array<{ variantId?: string; quantity: number }>>()\n for (const pick of picks) {\n const line = (order.lineItems ?? [])[pick.index] as Record<string, any> | undefined\n const productId = String(line?.['productId'] ?? '')\n if (!productId) continue\n byProduct.set(productId, [\n ...(byProduct.get(productId) ?? []),\n { ...(line?.['variantId'] ? { variantId: String(line['variantId']) } : {}), quantity: pick.quantity },\n ])\n }\n let units = 0\n for (const [productId, lines] of byProduct) {\n const ref = hostRef.collection('products').doc(productId)\n units += await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return 0\n const product = CommerceModel.liftLegacyProduct(snapshot.data() as any)\n let variants = product.variants\n const rows: Array<{ variantId: string; quantity: number }> = []\n for (const line of lines) {\n const variantId = line.variantId ?? product.variants[0]?.id\n const variant = variants.find((candidate) => candidate.id === variantId)\n if (!variantId || !variant || variant.inventory == null) continue\n variants = CommerceModel.adjustVariantInventory({ variants }, variantId, line.quantity, locationId)\n rows.push({ variantId, quantity: line.quantity })\n }\n if (!rows.length) return 0\n transaction.update(ref, {\n variants,\n ...CommerceModel.productStockFields({ ...product, variants }),\n updatedAtMs: now,\n })\n for (const row of rows) {\n transaction.create(hostRef.collection('inventoryAdjustments').doc(createResourceUid()), {\n productId,\n variantId: row.variantId,\n delta: row.quantity,\n reason: 'refund',\n orderId,\n ...(locationId ? { locationId } : {}),\n atMs: now,\n } satisfies CommerceModel.InventoryAdjustment)\n }\n return rows.reduce((sum, row) => sum + row.quantity, 0)\n })\n }\n return units\n}\n\nexport function createPosReturnHandler(\n deps: () => PosReturnDeps = defaultPosReturnDeps,\n): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosReturn(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-return] failed', error)\n return res.status(500).json({ error: 'The return could not be refunded' })\n }\n }\n}\n\nexport const posReturnHandler = createPosReturnHandler()\n"],"names":["claimAttempt","createResourceUid","nameSearchKey","recordPluginPersonRefund","reverseOrderConversion","checkoutCreditProvider","CommerceModel","planPosReturn","posMoney","posRefundableByTender","posReturnedQuantities","posReturnLineValues","posTenderLabel","POS_STRIPE_TENDERS","splitPosRefund","ORDER_REFUNDED_EVENT","RETURN_REFUNDED_EVENT","raiseOrderEvent","applyGiftCardRiskToOrder","notifyOrderBuyer","decidePosRefundAuthority","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","posOpsIdempotencyKey","readPosRegister","isPosManager","resolvePosCashier","createStripeRefund","parsePosHardwareInPersonConfig","defaultPosReturnDeps","base","afterRefund","input","settle","label","work","error","console","orderId","hostRef","firestore","collection","doc","hostId","returnedProductIds","length","action","kind","reason","fullyRefunded","productIds","refId","email","amountCents","refundCents","closedTheSale","closedTheOrder","key","returnId","extra","refund","id","refundIds","lineItemIds","fullLineIndexes","full","return","status","storeReturn","lines","map","line","process","env","sellerHostId","recordRegisterHardwareReturn","config","registerOrderId","refundId","refundLineIndexes","lineIndexes","RETURNABLE","Set","returnSummary","raw","held","order","liftLegacyOrder","values","returned","number","formatOrderNumber","channel","createdAtMs","Number","customerEmail","customerName","totalCents","totals","refundedCents","returnable","has","String","lineItems","index","name","variantLabel","quantity","Math","max","round","valueCents","tenders","tender","method","refundableCents","findOrders","rawText","snapshot","text","trim","slice","orders","digits","replace","test","where","limit","get","docs","data","includes","orderBy","exists","parsePicks","Array","isArray","entry","parseSplit","paymentId","handlePosReturn","deps","req","body","register","gate","staff","found","Promise","all","unitsHeldByOnlineReturns","at","registerId","ref","idempotencyKey","picks","requestedSplit","restock","reasonCode","RETURN_REASONS","orderRef","storeReturns","orderDisputeBlocksRefund","plan","split","cashier","cashierIsManager","cashierId","uid","isManager","memberIsPosManager","authority","limitCents","settings","refundLimitCents","managerAssertion","needsCash","allocations","some","needsStripe","openShiftId","requireOpenShift","STRIPE_SECRET_KEY","claimed","scopeId","orgId","busyMessage","replay","claim","reserved","runTransaction","transaction","fresh","ReturnRefusal","freshPlan","freshSplit","prior","returnedQuantities","posPaymentRefunds","update","lineCents","completedLines","release","message","tenderOf","find","ordered","sort","a","b","results","allocation","anyMoved","result","paymentIntentId","stripeKey","metadata","fetch","push","creditGiftCard","giftCardId","now","reverseFolio","reservationId","restorePluginCredit","catch","rollback","failed","filter","reduce","sum","cashCents","returnRef","storeReturnRef","orderLabel","pick","shiftIdForReturn","freshRegister","shiftId","shiftRef","shiftSnapshot","shiftOpen","Boolean","failedCents","orderRefunded","canTransitionOrder","Object","entries","count","tenderWords","join","units","refundedLineItemIds","timeline","appendOrderEvent","approvedBy","record","orderNumber","restocked","locationId","atMs","create","restockedLines","lineItemId","registerLocationId","undefined","requestedBy","source","merchantNote","refundedAtMs","event","detail","updatedAtMs","type","by","cashEvents","restockedUnits","restockReturn","printer","kickDrawer","causeId","createdBy","payload","outstandingCents","partial","cashOutCents","productId","flatMap","returnHoldsUnits","isInteger","floor","membership","Error","code","card","balanceCents","lastCreditAtMs","lastCreditOrderId","creditProviderId","creditReference","provider","restore","reference","cents","stay","folio","note","byProduct","Map","set","variantId","product","liftLegacyProduct","variants","rows","variant","candidate","inventory","adjustVariantInventory","productStockFields","row","delta","createPosReturnHandler","res","outcome","json","posReturnHandler"],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
17
|
import { POS_PIN_LOCKOUT_MS, POS_PIN_MAX_ATTEMPTS, posPinProblem } from "../model/commerce-pos-ops.js";
|
|
18
18
|
import { holdsPosPermission } from "../model/plugin-permissions.js";
|
|
19
|
+
import { commerceWorkspacePermissionRefusal } from "../model/workspace-site-permissions.js";
|
|
19
20
|
import { hashMemberPassword, verifyMemberPassword } from "./membership.js";
|
|
20
21
|
import { authorizePosOps, defaultPosOpsDeps, isPosManager, mayWorkRegister, mintPosAssertion, posOpsBody, posOpsCleanId, readPosAssertion, readPosRegister, resolvePosStaffAssertion } from "./pos-ops-gate.js";
|
|
21
22
|
function pinRef(hostRef, uid) {
|
|
@@ -61,7 +62,7 @@ export async function handlePosStaffPin(deps, req) {
|
|
|
61
62
|
return {
|
|
62
63
|
status: 403,
|
|
63
64
|
body: {
|
|
64
|
-
error:
|
|
65
|
+
error: commerceWorkspacePermissionRefusal('commerce.posApprovals')
|
|
65
66
|
}
|
|
66
67
|
};
|
|
67
68
|
}
|
|
@@ -133,7 +134,7 @@ export async function handlePosStaffPin(deps, req) {
|
|
|
133
134
|
}
|
|
134
135
|
case 'verify':
|
|
135
136
|
{
|
|
136
|
-
var _body_pin1
|
|
137
|
+
var _body_pin1;
|
|
137
138
|
const register = await readPosRegister(staff, body['registerId']);
|
|
138
139
|
if ('error' in register) return {
|
|
139
140
|
status: register.status,
|
|
@@ -200,7 +201,7 @@ export async function handlePosStaffPin(deps, req) {
|
|
|
200
201
|
return {
|
|
201
202
|
status: 423,
|
|
202
203
|
body: {
|
|
203
|
-
error: 'Too many wrong PINs. Try again later, or ask
|
|
204
|
+
error: 'Too many wrong PINs. Try again later, or ask someone who can approve refunds over the register’s limit to reset it.',
|
|
204
205
|
lockedUntilMs: claim.lockedUntilMs
|
|
205
206
|
}
|
|
206
207
|
};
|
|
@@ -215,7 +216,7 @@ export async function handlePosStaffPin(deps, req) {
|
|
|
215
216
|
return {
|
|
216
217
|
status: 423,
|
|
217
218
|
body: {
|
|
218
|
-
error: 'Too many wrong PINs. Try again later, or ask
|
|
219
|
+
error: 'Too many wrong PINs. Try again later, or ask someone who can approve refunds over the register’s limit to reset it.',
|
|
219
220
|
lockedUntilMs: now + POS_PIN_LOCKOUT_MS
|
|
220
221
|
}
|
|
221
222
|
};
|
|
@@ -236,16 +237,15 @@ export async function handlePosStaffPin(deps, req) {
|
|
|
236
237
|
// The PIN is right; whether it still buys anything is today's
|
|
237
238
|
// permissions, and for a manager's approval today's role.
|
|
238
239
|
const host = await staff.hostRef.get();
|
|
239
|
-
const role = ((_host_get = host.get('memberRoles')) != null ? _host_get : {})[memberUid];
|
|
240
240
|
const membership = await deps.membership(memberUid, staff.hostId);
|
|
241
241
|
// A cashier switched in by PIN rings sales, so they need the register's
|
|
242
242
|
// sell key too (AGL-4047), not only the register.
|
|
243
|
-
const allowed = purpose === 'manager' ? isPosManager(
|
|
243
|
+
const allowed = purpose === 'manager' ? isPosManager(host, memberUid, membership) : mayWorkRegister(membership) && holdsPosPermission(membership.permissions, 'posSell');
|
|
244
244
|
if (!allowed) {
|
|
245
245
|
return {
|
|
246
246
|
status: 403,
|
|
247
247
|
body: {
|
|
248
|
-
error: purpose === 'manager' ? '
|
|
248
|
+
error: purpose === 'manager' ? commerceWorkspacePermissionRefusal('commerce.posApprovals') : 'That member cannot use the register.'
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
251
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-staff-pin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport {\n POS_PIN_LOCKOUT_MS,\n POS_PIN_MAX_ATTEMPTS,\n posPinProblem,\n} from '../model/commerce-pos-ops'\nimport { holdsPosPermission } from '../model/plugin-permissions'\nimport { hashMemberPassword, verifyMemberPassword } from './membership'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n isPosManager,\n mayWorkRegister,\n mintPosAssertion,\n posOpsBody,\n posOpsCleanId,\n readPosAssertion,\n readPosRegister,\n resolvePosStaffAssertion,\n type PosAssertionPurpose,\n type PosOpsDeps,\n} from './pos-ops-gate'\n\n/*==========================================\n * STAFF PINS (AGL-3609): `POST /api/commerce/pos-staff-pin`.\n *\n * status whether the signed-in member has a PIN on this site\n * set a member sets their own PIN; a workspace admin may set or\n * reset anyone's, which also lifts a lockout\n * clear the same, removing it\n * roster who can switch in at this register: members with a PIN\n * verify a member's PIN at a register → a signed, short-lived\n * assertion that they are at it (`cashier`), or that a manager\n * approved one action (`manager`)\n * refresh a still-valid cashier assertion → a fresh one, re-checked\n *\n * The hash is scrypt with a per-PIN salt — the membership password helper —\n * at `hosts/{hostId}/posStaffPins/{uid}`, a collection no client may read or\n * write (the rules deny it outright, staff included).\n *\n * LOCKOUT is counted BEFORE the PIN is checked: each attempt is claimed in a\n * transaction that refuses once {@link POS_PIN_MAX_ATTEMPTS} have failed, so\n * a burst of parallel guesses gets five tries in the lockout window, not five\n * each. A right PIN clears the count.\n *=========================================*/\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\nfunction pinRef(hostRef: FirebaseFirestore.DocumentReference, uid: string) {\n return hostRef.collection('posStaffPins').doc(uid)\n}\n\nexport async function handlePosStaffPin(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = posOpsBody(req)\n const gate = await authorizePosOps(deps, req, body['hostId'])\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n const action = String(body['action'] ?? '')\n const firestore = deps.firestore()\n\n switch (action) {\n case 'status': {\n const snapshot = await pinRef(staff.hostRef, staff.uid).get()\n return {\n status: 200,\n body: {\n hasPin: snapshot.exists && Boolean(snapshot.get('pinScrypt')),\n isManager: staff.isManager,\n },\n }\n }\n\n case 'set':\n case 'clear': {\n const target = posOpsCleanId(body['memberUid']) || staff.uid\n if (target !== staff.uid && !staff.isManager) {\n return {\n status: 403,\n body: { error: \"Only a workspace admin can set another member's PIN.\" },\n }\n }\n // Whoever works this site's register, by permission, not by site role\n // (AGL-4047): the gate every PIN use re-asks.\n if (target !== staff.uid && !mayWorkRegister(await deps.membership(target, staff.hostId))) {\n return { status: 404, body: { error: 'That member does not work on this site.' } }\n }\n if (action === 'clear') {\n await pinRef(staff.hostRef, target).delete()\n return { status: 200, body: { ok: true, hasPin: false } }\n }\n const pin = String(body['pin'] ?? '')\n const problem = posPinProblem(pin)\n if (problem) return { status: 400, body: { error: problem } }\n await pinRef(staff.hostRef, target).set({\n pinScrypt: hashMemberPassword(pin),\n setAtMs: deps.now(),\n setBy: staff.uid,\n failedAttempts: 0,\n lockedUntilMs: null,\n })\n return { status: 200, body: { ok: true, hasPin: true } }\n }\n\n case 'roster': {\n const pins = await staff.hostRef.collection('posStaffPins').limit(200).get()\n // Those who may still ring a sale here, by permission (AGL-4047): the\n // switch-in below refuses anybody else.\n const holders = pins.docs.map((doc) => String(doc.id))\n const sells = await Promise.all(\n holders.map(async (uid) => {\n const membership = await deps.membership(uid, staff.hostId)\n return mayWorkRegister(membership) && holdsPosPermission(membership.permissions, 'posSell')\n }),\n )\n const staffWithPins = holders.filter((_, index) => sells[index])\n const members = await Promise.all(\n staffWithPins.map(async (uid) => ({ uid, name: await deps.memberName(uid) })),\n )\n members.sort((a, b) => a.name.localeCompare(b.name))\n return { status: 200, body: { members } }\n }\n\n case 'verify': {\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const purpose: PosAssertionPurpose = body['purpose'] === 'manager' ? 'manager' : 'cashier'\n const memberUid = posOpsCleanId(body['memberUid'])\n const pin = String(body['pin'] ?? '')\n if (!memberUid || !/^\\d{4,6}$/.test(pin)) {\n return { status: 400, body: { error: 'Choose your name and enter your PIN.' } }\n }\n const ref = pinRef(staff.hostRef, memberUid)\n const now = deps.now()\n // Claim the attempt first; the PIN is checked only inside the budget.\n const claim = await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists || !snapshot.get('pinScrypt')) return { kind: 'none' as const }\n const lockedUntilMs = Number(snapshot.get('lockedUntilMs') ?? 0)\n if (lockedUntilMs > now) return { kind: 'locked' as const, lockedUntilMs }\n const failed = lockedUntilMs ? 0 : Number(snapshot.get('failedAttempts') ?? 0)\n if (failed >= POS_PIN_MAX_ATTEMPTS) {\n const until = now + POS_PIN_LOCKOUT_MS\n transaction.update(ref, { lockedUntilMs: until, failedAttempts: 0 })\n return { kind: 'locked' as const, lockedUntilMs: until }\n }\n transaction.update(ref, { failedAttempts: failed + 1, lockedUntilMs: null })\n return { kind: 'claimed' as const, hash: String(snapshot.get('pinScrypt')), attempt: failed + 1 }\n })\n if (claim.kind === 'none') {\n return { status: 404, body: { error: 'That member has not set a PIN on this site.' } }\n }\n if (claim.kind === 'locked') {\n return {\n status: 423,\n body: {\n error: 'Too many wrong PINs. Try again later, or ask a workspace admin to reset it.',\n lockedUntilMs: claim.lockedUntilMs,\n },\n }\n }\n if (!verifyMemberPassword(pin, claim.hash)) {\n const left = POS_PIN_MAX_ATTEMPTS - claim.attempt\n if (left <= 0) {\n await ref.update({ lockedUntilMs: now + POS_PIN_LOCKOUT_MS, failedAttempts: 0 })\n return {\n status: 423,\n body: {\n error: 'Too many wrong PINs. Try again later, or ask a workspace admin to reset it.',\n lockedUntilMs: now + POS_PIN_LOCKOUT_MS,\n },\n }\n }\n return {\n status: 401,\n body: { error: `That PIN is not right. ${left} ${left === 1 ? 'try' : 'tries'} left.`, attemptsLeft: left },\n }\n }\n await ref.update({ failedAttempts: 0, lockedUntilMs: null, lastUsedAtMs: now })\n // The PIN is right; whether it still buys anything is today's\n // permissions, and for a manager's approval today's role.\n const host = await staff.hostRef.get()\n const role = (host.get('memberRoles') ?? {})[memberUid]\n const membership = await deps.membership(memberUid, staff.hostId)\n // A cashier switched in by PIN rings sales, so they need the register's\n // sell key too (AGL-4047), not only the register.\n const allowed =\n purpose === 'manager'\n ? isPosManager(role, membership)\n : mayWorkRegister(membership) && holdsPosPermission(membership.permissions, 'posSell')\n if (!allowed) {\n return {\n status: 403,\n body: {\n error:\n purpose === 'manager'\n ? 'Only a workspace admin can approve this.'\n : 'That member cannot use the register.',\n },\n }\n }\n let minted: { token: string; expiresAtMs: number }\n try {\n minted = mintPosAssertion(deps, {\n hostId: staff.hostId,\n registerId: register.ref.id,\n memberUid,\n purpose,\n })\n } catch {\n return { status: 501, body: { error: 'Staff PINs are not configured on this server.' } }\n }\n return {\n status: 200,\n body: {\n assertion: minted.token,\n expiresAtMs: minted.expiresAtMs,\n memberUid,\n name: await deps.memberName(memberUid),\n purpose,\n },\n }\n }\n\n case 'refresh': {\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const expected = { hostId: staff.hostId, registerId: register.ref.id, purpose: 'cashier' as const }\n if (!readPosAssertion(deps, body['assertion'], expected)) {\n return { status: 401, body: { error: 'Enter your PIN again.' } }\n }\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, body['assertion'], expected)\n if (!memberUid) return { status: 403, body: { error: 'That member cannot use the register.' } }\n const minted = mintPosAssertion(deps, { ...expected, memberUid })\n return { status: 200, body: { assertion: minted.token, expiresAtMs: minted.expiresAtMs, memberUid } }\n }\n\n default:\n return { status: 400, body: { error: 'Unknown action' } }\n }\n}\n\nexport function createPosStaffPinHandler(deps: () => PosOpsDeps = defaultPosOpsDeps): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosStaffPin(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-staff-pin] failed', error)\n return res.status(500).json({ error: 'The PIN could not be checked' })\n }\n }\n}\n\nexport const posStaffPinHandler = createPosStaffPinHandler()\n"],"names":["POS_PIN_LOCKOUT_MS","POS_PIN_MAX_ATTEMPTS","posPinProblem","holdsPosPermission","hashMemberPassword","verifyMemberPassword","authorizePosOps","defaultPosOpsDeps","isPosManager","mayWorkRegister","mintPosAssertion","posOpsBody","posOpsCleanId","readPosAssertion","readPosRegister","resolvePosStaffAssertion","pinRef","hostRef","uid","collection","doc","handlePosStaffPin","deps","req","body","method","status","error","gate","staff","action","String","firestore","snapshot","get","hasPin","exists","Boolean","isManager","target","membership","hostId","delete","ok","pin","problem","set","pinScrypt","setAtMs","now","setBy","failedAttempts","lockedUntilMs","pins","limit","holders","docs","map","id","sells","Promise","all","permissions","staffWithPins","filter","_","index","members","name","memberName","sort","a","b","localeCompare","host","register","purpose","memberUid","test","ref","claim","runTransaction","transaction","kind","Number","failed","until","update","hash","attempt","left","attemptsLeft","lastUsedAtMs","role","allowed","minted","registerId","assertion","token","expiresAtMs","expected","createPosStaffPinHandler","res","outcome","json","console","posStaffPinHandler"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-staff-pin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport {\n POS_PIN_LOCKOUT_MS,\n POS_PIN_MAX_ATTEMPTS,\n posPinProblem,\n} from '../model/commerce-pos-ops'\nimport { holdsPosPermission } from '../model/plugin-permissions'\nimport { commerceWorkspacePermissionRefusal } from '../model/workspace-site-permissions'\nimport { hashMemberPassword, verifyMemberPassword } from './membership'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n isPosManager,\n mayWorkRegister,\n mintPosAssertion,\n posOpsBody,\n posOpsCleanId,\n readPosAssertion,\n readPosRegister,\n resolvePosStaffAssertion,\n type PosAssertionPurpose,\n type PosOpsDeps,\n} from './pos-ops-gate'\n\n/*==========================================\n * STAFF PINS (AGL-3609): `POST /api/commerce/pos-staff-pin`.\n *\n * status whether the signed-in member has a PIN on this site\n * set a member sets their own PIN; whoever holds \"Approve refunds over\n * the register's limit\" (AGL-4047) may set or\n * reset anyone's, which also lifts a lockout\n * clear the same, removing it\n * roster who can switch in at this register: members with a PIN\n * verify a member's PIN at a register → a signed, short-lived\n * assertion that they are at it (`cashier`), or that a manager\n * approved one action (`manager`)\n * refresh a still-valid cashier assertion → a fresh one, re-checked\n *\n * The hash is scrypt with a per-PIN salt — the membership password helper —\n * at `hosts/{hostId}/posStaffPins/{uid}`, a collection no client may read or\n * write (the rules deny it outright, staff included).\n *\n * LOCKOUT is counted BEFORE the PIN is checked: each attempt is claimed in a\n * transaction that refuses once {@link POS_PIN_MAX_ATTEMPTS} have failed, so\n * a burst of parallel guesses gets five tries in the lockout window, not five\n * each. A right PIN clears the count.\n *=========================================*/\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\nfunction pinRef(hostRef: FirebaseFirestore.DocumentReference, uid: string) {\n return hostRef.collection('posStaffPins').doc(uid)\n}\n\nexport async function handlePosStaffPin(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = posOpsBody(req)\n const gate = await authorizePosOps(deps, req, body['hostId'])\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n const action = String(body['action'] ?? '')\n const firestore = deps.firestore()\n\n switch (action) {\n case 'status': {\n const snapshot = await pinRef(staff.hostRef, staff.uid).get()\n return {\n status: 200,\n body: {\n hasPin: snapshot.exists && Boolean(snapshot.get('pinScrypt')),\n isManager: staff.isManager,\n },\n }\n }\n\n case 'set':\n case 'clear': {\n const target = posOpsCleanId(body['memberUid']) || staff.uid\n if (target !== staff.uid && !staff.isManager) {\n return {\n status: 403,\n body: { error: commerceWorkspacePermissionRefusal('commerce.posApprovals') },\n }\n }\n // Whoever works this site's register, by permission, not by site role\n // (AGL-4047): the gate every PIN use re-asks.\n if (target !== staff.uid && !mayWorkRegister(await deps.membership(target, staff.hostId))) {\n return { status: 404, body: { error: 'That member does not work on this site.' } }\n }\n if (action === 'clear') {\n await pinRef(staff.hostRef, target).delete()\n return { status: 200, body: { ok: true, hasPin: false } }\n }\n const pin = String(body['pin'] ?? '')\n const problem = posPinProblem(pin)\n if (problem) return { status: 400, body: { error: problem } }\n await pinRef(staff.hostRef, target).set({\n pinScrypt: hashMemberPassword(pin),\n setAtMs: deps.now(),\n setBy: staff.uid,\n failedAttempts: 0,\n lockedUntilMs: null,\n })\n return { status: 200, body: { ok: true, hasPin: true } }\n }\n\n case 'roster': {\n const pins = await staff.hostRef.collection('posStaffPins').limit(200).get()\n // Those who may still ring a sale here, by permission (AGL-4047): the\n // switch-in below refuses anybody else.\n const holders = pins.docs.map((doc) => String(doc.id))\n const sells = await Promise.all(\n holders.map(async (uid) => {\n const membership = await deps.membership(uid, staff.hostId)\n return mayWorkRegister(membership) && holdsPosPermission(membership.permissions, 'posSell')\n }),\n )\n const staffWithPins = holders.filter((_, index) => sells[index])\n const members = await Promise.all(\n staffWithPins.map(async (uid) => ({ uid, name: await deps.memberName(uid) })),\n )\n members.sort((a, b) => a.name.localeCompare(b.name))\n return { status: 200, body: { members } }\n }\n\n case 'verify': {\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const purpose: PosAssertionPurpose = body['purpose'] === 'manager' ? 'manager' : 'cashier'\n const memberUid = posOpsCleanId(body['memberUid'])\n const pin = String(body['pin'] ?? '')\n if (!memberUid || !/^\\d{4,6}$/.test(pin)) {\n return { status: 400, body: { error: 'Choose your name and enter your PIN.' } }\n }\n const ref = pinRef(staff.hostRef, memberUid)\n const now = deps.now()\n // Claim the attempt first; the PIN is checked only inside the budget.\n const claim = await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists || !snapshot.get('pinScrypt')) return { kind: 'none' as const }\n const lockedUntilMs = Number(snapshot.get('lockedUntilMs') ?? 0)\n if (lockedUntilMs > now) return { kind: 'locked' as const, lockedUntilMs }\n const failed = lockedUntilMs ? 0 : Number(snapshot.get('failedAttempts') ?? 0)\n if (failed >= POS_PIN_MAX_ATTEMPTS) {\n const until = now + POS_PIN_LOCKOUT_MS\n transaction.update(ref, { lockedUntilMs: until, failedAttempts: 0 })\n return { kind: 'locked' as const, lockedUntilMs: until }\n }\n transaction.update(ref, { failedAttempts: failed + 1, lockedUntilMs: null })\n return { kind: 'claimed' as const, hash: String(snapshot.get('pinScrypt')), attempt: failed + 1 }\n })\n if (claim.kind === 'none') {\n return { status: 404, body: { error: 'That member has not set a PIN on this site.' } }\n }\n if (claim.kind === 'locked') {\n return {\n status: 423,\n body: {\n error: 'Too many wrong PINs. Try again later, or ask someone who can approve refunds over the register’s limit to reset it.',\n lockedUntilMs: claim.lockedUntilMs,\n },\n }\n }\n if (!verifyMemberPassword(pin, claim.hash)) {\n const left = POS_PIN_MAX_ATTEMPTS - claim.attempt\n if (left <= 0) {\n await ref.update({ lockedUntilMs: now + POS_PIN_LOCKOUT_MS, failedAttempts: 0 })\n return {\n status: 423,\n body: {\n error: 'Too many wrong PINs. Try again later, or ask someone who can approve refunds over the register’s limit to reset it.',\n lockedUntilMs: now + POS_PIN_LOCKOUT_MS,\n },\n }\n }\n return {\n status: 401,\n body: { error: `That PIN is not right. ${left} ${left === 1 ? 'try' : 'tries'} left.`, attemptsLeft: left },\n }\n }\n await ref.update({ failedAttempts: 0, lockedUntilMs: null, lastUsedAtMs: now })\n // The PIN is right; whether it still buys anything is today's\n // permissions, and for a manager's approval today's role.\n const host = await staff.hostRef.get()\n const membership = await deps.membership(memberUid, staff.hostId)\n // A cashier switched in by PIN rings sales, so they need the register's\n // sell key too (AGL-4047), not only the register.\n const allowed =\n purpose === 'manager'\n ? isPosManager(host, memberUid, membership)\n : mayWorkRegister(membership) && holdsPosPermission(membership.permissions, 'posSell')\n if (!allowed) {\n return {\n status: 403,\n body: {\n error:\n purpose === 'manager'\n ? commerceWorkspacePermissionRefusal('commerce.posApprovals')\n : 'That member cannot use the register.',\n },\n }\n }\n let minted: { token: string; expiresAtMs: number }\n try {\n minted = mintPosAssertion(deps, {\n hostId: staff.hostId,\n registerId: register.ref.id,\n memberUid,\n purpose,\n })\n } catch {\n return { status: 501, body: { error: 'Staff PINs are not configured on this server.' } }\n }\n return {\n status: 200,\n body: {\n assertion: minted.token,\n expiresAtMs: minted.expiresAtMs,\n memberUid,\n name: await deps.memberName(memberUid),\n purpose,\n },\n }\n }\n\n case 'refresh': {\n const register = await readPosRegister(staff, body['registerId'])\n if ('error' in register) return { status: register.status, body: { error: register.error } }\n const expected = { hostId: staff.hostId, registerId: register.ref.id, purpose: 'cashier' as const }\n if (!readPosAssertion(deps, body['assertion'], expected)) {\n return { status: 401, body: { error: 'Enter your PIN again.' } }\n }\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, body['assertion'], expected)\n if (!memberUid) return { status: 403, body: { error: 'That member cannot use the register.' } }\n const minted = mintPosAssertion(deps, { ...expected, memberUid })\n return { status: 200, body: { assertion: minted.token, expiresAtMs: minted.expiresAtMs, memberUid } }\n }\n\n default:\n return { status: 400, body: { error: 'Unknown action' } }\n }\n}\n\nexport function createPosStaffPinHandler(deps: () => PosOpsDeps = defaultPosOpsDeps): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosStaffPin(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-staff-pin] failed', error)\n return res.status(500).json({ error: 'The PIN could not be checked' })\n }\n }\n}\n\nexport const posStaffPinHandler = createPosStaffPinHandler()\n"],"names":["POS_PIN_LOCKOUT_MS","POS_PIN_MAX_ATTEMPTS","posPinProblem","holdsPosPermission","commerceWorkspacePermissionRefusal","hashMemberPassword","verifyMemberPassword","authorizePosOps","defaultPosOpsDeps","isPosManager","mayWorkRegister","mintPosAssertion","posOpsBody","posOpsCleanId","readPosAssertion","readPosRegister","resolvePosStaffAssertion","pinRef","hostRef","uid","collection","doc","handlePosStaffPin","deps","req","body","method","status","error","gate","staff","action","String","firestore","snapshot","get","hasPin","exists","Boolean","isManager","target","membership","hostId","delete","ok","pin","problem","set","pinScrypt","setAtMs","now","setBy","failedAttempts","lockedUntilMs","pins","limit","holders","docs","map","id","sells","Promise","all","permissions","staffWithPins","filter","_","index","members","name","memberName","sort","a","b","localeCompare","register","purpose","memberUid","test","ref","claim","runTransaction","transaction","kind","Number","failed","until","update","hash","attempt","left","attemptsLeft","lastUsedAtMs","host","allowed","minted","registerId","assertion","token","expiresAtMs","expected","createPosStaffPinHandler","res","outcome","json","console","posStaffPinHandler"],"mappings":"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"}
|