import { resolveTailorAuth } from "../utils/auth-machine-user"; // Data-setup helpers for the inventory-visibility flow live in this one file. // Auth is handled by the shared `./user` fixture, which signs in for real // through the app's OAuth hosted login as a broadly permissioned admin; this // file only seeds the GraphQL prerequisites the read-only UI needs. The flow is // read-only in the UI, so every StockLevel row the spec inspects must be // produced first. StockLevel has no direct create mutation — the only way to // produce stock is the way the app does it: create an Item, raise a Purchase // Order for it at a receiving Site, submit + approve it, then create and settle // an Inbound Shipment. `settleInboundShipment` moves stock into the inventory // ledger, which is what the `listStockRollupsBySite` query behind the inventory // list aggregates into onHand / reserved / blocked / available rows. // Minimal shape of a urql operation result — the machine-user client from // `resolveTailorAuth` is a plain urql client, so `.toPromise()` resolves to // `{ data, error }` rather than throwing. interface GqlResult { data?: T; error?: { message: string } | undefined; } async function runQuery(document: string, variables: Record): Promise { const { graphqlClient } = resolveTailorAuth(); const result = (await graphqlClient.query(document, variables).toPromise()) as GqlResult; if (result.error) { throw new Error(`GraphQL query failed: ${result.error.message}`); } if (!result.data) { throw new Error("GraphQL query returned no data"); } return result.data; } async function runMutation(document: string, variables: Record): Promise { const { graphqlClient } = resolveTailorAuth(); const result = (await graphqlClient.mutation(document, variables).toPromise()) as GqlResult; if (result.error) { throw new Error(`GraphQL mutation failed: ${result.error.message}`); } if (!result.data) { throw new Error("GraphQL mutation returned no data"); } return result.data; } interface Edge { node: T; } interface Connection { edges: Edge[]; } const LIST_ITEM_UNIT = ` query InvVisFirstItemUnit { items(query: { status: { eq: ACTIVE } }, first: 1) { edges { node { id unitId } } } } `; const LIST_COMPANY = ` query InvVisFirstCompany { companies(query: { status: { eq: ACTIVE } }, first: 1) { edges { node { id baseCurrencyId } } } } `; const LIST_SUPPLIER = ` query InvVisFirstSupplier { supplierAccounts(query: { status: { eq: ACTIVE } }, first: 1) { edges { node { id } } } } `; // A Site that owns at least one ACTIVE StorageLocation, so a settled receipt // against it produces StockLevel rows at a resolvable site. const LIST_STORAGE_LOCATION = ` query InvVisFirstStorageLocation { storageLocations(query: { status: { eq: ACTIVE } }, first: 1) { edges { node { id siteId } } } } `; const GET_SITE = ` query InvVisSite($id: ID!) { site(id: $id) { id name } } `; const CREATE_ITEM = ` mutation InvVisCreateItem($sku: String!, $name: String!, $unitId: String!) { createItem(sku: $sku, name: $name, unitId: $unitId) { id } } `; const PUBLISH_STANDARD_COST = ` mutation InvVisPublishStandardCost($itemId: String!, $unitCost: String!) { publishStandardCost(itemId: $itemId, unitCost: $unitCost) { id } } `; const CREATE_PURCHASE_ITEM = ` mutation InvVisCreatePurchaseItem($itemId: String!, $requiresPhysicalReceipt: Boolean!) { createPurchaseItem(itemId: $itemId, requiresPhysicalReceipt: $requiresPhysicalReceipt) { id } } `; const CREATE_PURCHASE_ORDER = ` mutation InvVisCreatePurchaseOrder( $companyId: String! $currencyId: String! $supplierAccountId: String! $orderDate: String! $receivingSiteId: String $lines: [CreatePurchaseOrderInputLines!]! ) { createPurchaseOrder( companyId: $companyId currencyId: $currencyId supplierAccountId: $supplierAccountId orderDate: $orderDate receivingSiteId: $receivingSiteId lines: $lines ) { id } } `; const SUBMIT_PURCHASE_ORDER = ` mutation InvVisSubmitPurchaseOrder($id: String!) { submitPurchaseOrder(id: $id) { id } } `; const APPROVE_PURCHASE_ORDER = ` mutation InvVisApprovePurchaseOrder($id: String!) { approvePurchaseOrder(id: $id) { id } } `; const GET_PURCHASE_ORDER_LINES = ` query InvVisPurchaseOrderLines($id: ID!) { purchaseOrder(id: $id) { id lines { edges { node { id } } } } } `; const CREATE_INBOUND_SHIPMENT = ` mutation InvVisCreateInboundShipment( $companyId: String! $supplierAccountId: String! $lines: [CreateInboundShipmentInputLines!]! ) { createInboundShipment(companyId: $companyId, supplierAccountId: $supplierAccountId, lines: $lines) { id } } `; const SETTLE_INBOUND_SHIPMENT = ` mutation InvVisSettleInboundShipment($id: String!) { settleInboundShipment(id: $id) { id status } } `; /** A freshly seeded, stocked item and the receiving site it was stocked at. */ export interface StockedItem { itemId: string; sku: string; name: string; siteId: string; siteName: string; onHand: number; } /** A freshly seeded item that is never receipted, so it has no StockLevel rows. */ export interface UnstockedItem { itemId: string; sku: string; name: string; } function unique(): string { return `${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`; } interface RefContext { unitId: string; companyId: string; currencyId: string; supplierAccountId: string; siteId: string; siteName: string; } async function loadRefContext(): Promise { const [items, companies, suppliers, locations] = await Promise.all([ runQuery<{ items: Connection<{ id: string; unitId: string }> }>(LIST_ITEM_UNIT, {}), runQuery<{ companies: Connection<{ id: string; baseCurrencyId: string | null }> }>( LIST_COMPANY, {}, ), runQuery<{ supplierAccounts: Connection<{ id: string }> }>(LIST_SUPPLIER, {}), runQuery<{ storageLocations: Connection<{ id: string; siteId: string }> }>( LIST_STORAGE_LOCATION, {}, ), ]); const siteId = locations.storageLocations.edges[0].node.siteId; const site = await runQuery<{ site: { id: string; name: string } }>(GET_SITE, { id: siteId }); const currencyId = companies.companies.edges[0].node.baseCurrencyId; if (!currencyId) { throw new Error("Seed company has no base currency"); } return { unitId: items.items.edges[0].node.unitId, companyId: companies.companies.edges[0].node.id, currencyId, supplierAccountId: suppliers.supplierAccounts.edges[0].node.id, siteId, siteName: site.site.name, }; } /** * Create an item and drive it through receiving so it has a StockLevel row at * a known site with the given on-hand quantity. */ export async function seedStockedItem(onHand: number): Promise { const ctx = await loadRefContext(); const id = unique(); const sku = `INV-VIS-STOCKED-${id}`; const name = `Inventory Visibility Stocked ${id}`; const qty = String(onHand); const created = await runMutation<{ createItem: { id: string } }>(CREATE_ITEM, { sku, name, unitId: ctx.unitId, }); const itemId = created.createItem.id; // Standard costing values the receipt below; publish a standard first. await runMutation(PUBLISH_STANDARD_COST, { itemId, unitCost: "5.00" }); // Ordering the item requires its purchasing record. await runMutation(CREATE_PURCHASE_ITEM, { itemId, requiresPhysicalReceipt: true }); const po = await runMutation<{ createPurchaseOrder: { id: string } }>(CREATE_PURCHASE_ORDER, { companyId: ctx.companyId, currencyId: ctx.currencyId, supplierAccountId: ctx.supplierAccountId, orderDate: new Date().toISOString(), receivingSiteId: ctx.siteId, lines: [{ itemId, quantity: qty, unitPrice: "5.00", unitId: ctx.unitId }], }); const purchaseOrderId = po.createPurchaseOrder.id; await runMutation(SUBMIT_PURCHASE_ORDER, { id: purchaseOrderId }); await runMutation(APPROVE_PURCHASE_ORDER, { id: purchaseOrderId }); const poLines = await runQuery<{ purchaseOrder: { lines: Connection<{ id: string }> }; }>(GET_PURCHASE_ORDER_LINES, { id: purchaseOrderId }); const poLineId = poLines.purchaseOrder.lines.edges[0].node.id; const receipt = await runMutation<{ createInboundShipment: { id: string } }>( CREATE_INBOUND_SHIPMENT, { companyId: ctx.companyId, supplierAccountId: ctx.supplierAccountId, lines: [{ purchaseOrderLineId: poLineId, quantity: qty }], }, ); const settled = await runMutation<{ settleInboundShipment: { id: string; status: string } }>( SETTLE_INBOUND_SHIPMENT, { id: receipt.createInboundShipment.id }, ); if (settled.settleInboundShipment.status !== "POSTED") { throw new Error( `Expected settled inbound shipment to be POSTED, got ${settled.settleInboundShipment.status}`, ); } return { itemId, sku, name, siteId: ctx.siteId, siteName: ctx.siteName, onHand }; } /** Create an item that is never receipted, so it never appears in the list. */ export async function seedUnstockedItem(): Promise { const ctx = await loadRefContext(); const id = unique(); const sku = `INV-VIS-UNSTOCKED-${id}`; const name = `Inventory Visibility Unstocked ${id}`; const created = await runMutation<{ createItem: { id: string } }>(CREATE_ITEM, { sku, name, unitId: ctx.unitId, }); return { itemId: created.createItem.id, sku, name }; } // This flow adds no data test fixtures — its prerequisites are seeded via the // exported helpers above — so it reuses the shared real-login `test` verbatim. export { test, expect } from "./user";