import { resolveTailorAuth } from "../utils/auth-machine-user"; // Fixture for the outbound-shipment-processing flow. Auth is provided by the // shared `./user` fixture (real OAuth sign-in as a broadly permissioned admin), // so this file holds only the flow's GraphQL data setup: prerequisites built // with the machine-user client from `resolveTailorAuth()` (the same primitive // the backend story tests use): real AVAILABLE stock at the issue-from location // and a CONFIRMED sales order to issue against — neither of which the seed // ships. // // Everything the spec imports lives in this one file so no shared helper is // created that a sibling flow's generator might also try to author. // --------------------------------------------------------------------------- // Stable seed master data (see backend/seed/data). These rows are ACTIVE and // carry the roles createSalesOrder / createStockAdjustment require. // --------------------------------------------------------------------------- /** Harbor Retail Co. — ACTIVE BusinessPartner holding the CUSTOMER role. */ const CUSTOMER_ACCOUNT_ID = "a5000000-0000-0000-0000-000000000001"; /** Classic T-Shirt - Black / S — ACTIVE item on the shared "each" unit. */ const ITEM_ID = "e1000000-0000-0000-0000-000000000001"; const UNIT_ID = "16e42fb3-bd7d-5154-ae34-d0a96d3016a2"; /** LA Warehouse / Main Zone — ACTIVE storage location. */ const STORAGE_LOCATION_ID = "0e000000-0000-0000-0000-000000000001"; /** The storage location's display name, as shown in the Issue From combobox. */ export const STORAGE_LOCATION_NAME = "Main Zone"; // --------------------------------------------------------------------------- // GraphQL prerequisites (machine-user client). // --------------------------------------------------------------------------- interface StockAdjustmentLineInput { itemId: string; storageLocationId: string; quantity: string; adjustmentDirection: string; } const CREATE_STOCK_ADJUSTMENT = /* GraphQL */ ` mutation CreateStockAdjustmentForOutboundE2e( $adjustmentType: String! $reasonCode: String! $lines: [CreateStockAdjustmentInputLines!]! ) { createStockAdjustment(adjustmentType: $adjustmentType, reasonCode: $reasonCode, lines: $lines) { id status } } `; const SUBMIT_STOCK_ADJUSTMENT = /* GraphQL */ ` mutation SubmitStockAdjustmentForOutboundE2e($id: String!) { submitStockAdjustment(id: $id) { id status } } `; const CONFIRM_STOCK_ADJUSTMENT = /* GraphQL */ ` mutation ConfirmStockAdjustmentForOutboundE2e($id: String!) { confirmStockAdjustment(id: $id) { id status } } `; const PUBLISH_STANDARD_COST = /* GraphQL */ ` mutation PublishStandardCostForOutboundE2e($itemId: String!, $unitCost: String!) { publishStandardCost(itemId: $itemId, unitCost: $unitCost) { id } } `; const CREATE_SALES_ORDER = /* GraphQL */ ` mutation CreateSalesOrderForOutboundE2e( $customerAccountId: String! $shippingAddress: String $lines: [CreateSalesOrderInputLines!]! ) { createSalesOrder( customerAccountId: $customerAccountId shippingAddress: $shippingAddress lines: $lines ) { id status } } `; const SUBMIT_SALES_ORDER = /* GraphQL */ ` mutation SubmitSalesOrderForOutboundE2e($id: String!) { submitSalesOrder(id: $id) { id status } } `; const CONFIRM_SALES_ORDER = /* GraphQL */ ` mutation ConfirmSalesOrderForOutboundE2e($id: String!) { confirmSalesOrder(id: $id) { id status } } `; const GET_SALES_ORDER = /* GraphQL */ ` query GetSalesOrderForOutboundE2e($id: ID!) { salesOrder(id: $id) { id docNumber orderStatus } } `; const GET_OUTBOUND_SHIPMENT = /* GraphQL */ ` query GetOutboundShipmentForOutboundE2e($id: ID!) { outboundShipment(id: $id) { id docNumber status } } `; interface MutationHeaderResult { id: string; status: string; } /** The CONFIRMED sales order this flow issues against. */ export interface ConfirmedSalesOrder { id: string; docNumber: string; } /** * Build AVAILABLE stock at the issue-from location via a confirmed * CORRECTION / INCREASE stock adjustment — the only app path that writes a * StockLevel row, so a later settle has real stock to reduce. */ export async function ensureAvailableStock(quantity: number): Promise { const { graphqlClient } = resolveTailorAuth(); // Standard costing values the adjustment (and the later issue); publishing is // append-only, so repeating it for the shared seed item is safe. const published = await graphqlClient .mutation<{ publishStandardCost: { id: string } }>(PUBLISH_STANDARD_COST, { itemId: ITEM_ID, unitCost: "5.00", }) .toPromise(); if (published.error) throw published.error; const created = await graphqlClient .mutation<{ createStockAdjustment: MutationHeaderResult }>(CREATE_STOCK_ADJUSTMENT, { adjustmentType: "CORRECTION", reasonCode: `outbound-e2e-stock-${Date.now()}`, lines: [ { itemId: ITEM_ID, storageLocationId: STORAGE_LOCATION_ID, quantity: String(quantity), adjustmentDirection: "INCREASE", }, ] satisfies StockAdjustmentLineInput[], }) .toPromise(); if (created.error) throw created.error; const adjustmentId = created.data?.createStockAdjustment.id; if (!adjustmentId) throw new Error("stock adjustment was not created"); const submitted = await graphqlClient .mutation<{ submitStockAdjustment: MutationHeaderResult }>(SUBMIT_STOCK_ADJUSTMENT, { id: adjustmentId, }) .toPromise(); if (submitted.error) throw submitted.error; const confirmed = await graphqlClient .mutation<{ confirmStockAdjustment: MutationHeaderResult }>(CONFIRM_STOCK_ADJUSTMENT, { id: adjustmentId, }) .toPromise(); if (confirmed.error) throw confirmed.error; if (confirmed.data?.confirmStockAdjustment.status !== "CONFIRMED") { throw new Error("stock adjustment was not confirmed"); } } /** * Raise a DRAFT sales order for the seed customer + item, then SUBMIT and * CONFIRM it so it is shippable and appears in the outbound create form's * "confirmed sales order" picker. Returns the order id and its docNumber (the * label the picker renders). */ export async function createConfirmedSalesOrder( orderedQuantity: number, ): Promise { const { graphqlClient } = resolveTailorAuth(); const created = await graphqlClient .mutation<{ createSalesOrder: MutationHeaderResult }>(CREATE_SALES_ORDER, { customerAccountId: CUSTOMER_ACCOUNT_ID, shippingAddress: "1 Test Way, Warehouse District", lines: [ { itemId: ITEM_ID, quantity: String(orderedQuantity), unitPrice: "9.5", unitId: UNIT_ID, }, ], }) .toPromise(); if (created.error) throw created.error; const salesOrderId = created.data?.createSalesOrder.id; if (!salesOrderId) throw new Error("sales order was not created"); const submitted = await graphqlClient .mutation<{ submitSalesOrder: MutationHeaderResult }>(SUBMIT_SALES_ORDER, { id: salesOrderId }) .toPromise(); if (submitted.error) throw submitted.error; const confirmed = await graphqlClient .mutation<{ confirmSalesOrder: MutationHeaderResult }>(CONFIRM_SALES_ORDER, { id: salesOrderId, }) .toPromise(); if (confirmed.error) throw confirmed.error; if (confirmed.data?.confirmSalesOrder.status !== "CONFIRMED") { throw new Error("sales order was not confirmed"); } const read = await graphqlClient .query<{ salesOrder: { id: string; docNumber: string | null; orderStatus: string } | null }>( GET_SALES_ORDER, { id: salesOrderId }, ) .toPromise(); if (read.error) throw read.error; const docNumber = read.data?.salesOrder?.docNumber; if (!docNumber) throw new Error("sales order docNumber was not found"); return { id: salesOrderId, docNumber }; } /** * Read an outbound shipment's own document number — the label its list row * links on — given the id captured from the detail URL after creating it. */ export async function getOutboundShipmentDocNumber(id: string): Promise { const { graphqlClient } = resolveTailorAuth(); const read = await graphqlClient .query<{ outboundShipment: { id: string; docNumber: string | null; status: string } | null }>( GET_OUTBOUND_SHIPMENT, { id }, ) .toPromise(); if (read.error) throw read.error; const docNumber = read.data?.outboundShipment?.docNumber; if (!docNumber) throw new Error("outbound shipment docNumber was not found"); return docNumber; } // --------------------------------------------------------------------------- // Auth comes from the shared `./user` fixture: the exported `test` provides a // `page` already signed in as a broadly permissioned admin via real OAuth. This // flow adds no data-scoped Playwright fixtures, so it re-exports the shared // `test` unchanged and layers its setup through the plain helpers above. // --------------------------------------------------------------------------- export { test, expect } from "./user";