order.e2e-spec.ts 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. /* tslint:disable:no-non-null-assertion */
  2. import { pick } from '@vendure/common/lib/pick';
  3. import { createTestEnvironment, SimpleGraphQLClient } from '@vendure/testing';
  4. import gql from 'graphql-tag';
  5. import path from 'path';
  6. import { initialData } from '../../../e2e-common/e2e-initial-data';
  7. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  8. import {
  9. failsToSettlePaymentMethod,
  10. singleStageRefundablePaymentMethod,
  11. twoStagePaymentMethod,
  12. } from './fixtures/test-payment-methods';
  13. import { ORDER_FRAGMENT } from './graphql/fragments';
  14. import {
  15. AddNoteToOrder,
  16. CancelOrder,
  17. CreateFulfillment,
  18. DeleteOrderNote,
  19. GetCustomerList,
  20. GetOrder,
  21. GetOrderFulfillmentItems,
  22. GetOrderFulfillments,
  23. GetOrderHistory,
  24. GetOrderList,
  25. GetOrderListFulfillments,
  26. GetProductWithVariants,
  27. GetStockMovement,
  28. HistoryEntryType,
  29. OrderItemFragment,
  30. RefundOrder,
  31. SettlePayment,
  32. SettleRefund,
  33. StockMovementType,
  34. UpdateOrderNote,
  35. UpdateProductVariants,
  36. } from './graphql/generated-e2e-admin-types';
  37. import { AddItemToOrder, DeletionResult, GetActiveOrder } from './graphql/generated-e2e-shop-types';
  38. import {
  39. GET_CUSTOMER_LIST,
  40. GET_ORDER,
  41. GET_PRODUCT_WITH_VARIANTS,
  42. GET_STOCK_MOVEMENT,
  43. UPDATE_PRODUCT_VARIANTS,
  44. } from './graphql/shared-definitions';
  45. import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER } from './graphql/shop-definitions';
  46. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  47. import { addPaymentToOrder, proceedToArrangingPayment } from './utils/test-order-utils';
  48. describe('Orders resolver', () => {
  49. const { server, adminClient, shopClient } = createTestEnvironment({
  50. ...testConfig,
  51. paymentOptions: {
  52. paymentMethodHandlers: [
  53. twoStagePaymentMethod,
  54. failsToSettlePaymentMethod,
  55. singleStageRefundablePaymentMethod,
  56. ],
  57. },
  58. });
  59. let customers: GetCustomerList.Items[];
  60. const password = 'test';
  61. beforeAll(async () => {
  62. await server.init({
  63. initialData,
  64. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  65. customerCount: 3,
  66. });
  67. await adminClient.asSuperAdmin();
  68. // Create a couple of orders to be queried
  69. const result = await adminClient.query<GetCustomerList.Query, GetCustomerList.Variables>(
  70. GET_CUSTOMER_LIST,
  71. {
  72. options: {
  73. take: 3,
  74. },
  75. },
  76. );
  77. customers = result.customers.items;
  78. await shopClient.asUserWithCredentials(customers[0].emailAddress, password);
  79. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  80. productVariantId: 'T_1',
  81. quantity: 1,
  82. });
  83. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  84. productVariantId: 'T_2',
  85. quantity: 1,
  86. });
  87. await shopClient.asUserWithCredentials(customers[1].emailAddress, password);
  88. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  89. productVariantId: 'T_2',
  90. quantity: 1,
  91. });
  92. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  93. productVariantId: 'T_3',
  94. quantity: 3,
  95. });
  96. }, TEST_SETUP_TIMEOUT_MS);
  97. afterAll(async () => {
  98. await server.destroy();
  99. });
  100. it('orders', async () => {
  101. const result = await adminClient.query<GetOrderList.Query>(GET_ORDERS_LIST);
  102. expect(result.orders.items.map((o) => o.id)).toEqual(['T_1', 'T_2']);
  103. });
  104. it('order', async () => {
  105. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, { id: 'T_2' });
  106. expect(result.order!.id).toBe('T_2');
  107. });
  108. it('order history initially empty', async () => {
  109. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  110. GET_ORDER_HISTORY,
  111. { id: 'T_1' },
  112. );
  113. expect(order!.history.totalItems).toBe(0);
  114. expect(order!.history.items).toEqual([]);
  115. });
  116. describe('payments', () => {
  117. it('settlePayment fails', async () => {
  118. await shopClient.asUserWithCredentials(customers[0].emailAddress, password);
  119. await proceedToArrangingPayment(shopClient);
  120. const order = await addPaymentToOrder(shopClient, failsToSettlePaymentMethod);
  121. expect(order.state).toBe('PaymentAuthorized');
  122. const payment = order.payments![0];
  123. const { settlePayment } = await adminClient.query<
  124. SettlePayment.Mutation,
  125. SettlePayment.Variables
  126. >(SETTLE_PAYMENT, {
  127. id: payment.id,
  128. });
  129. expect(settlePayment!.id).toBe(payment.id);
  130. expect(settlePayment!.state).toBe('Authorized');
  131. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  132. id: order.id,
  133. });
  134. expect(result.order!.state).toBe('PaymentAuthorized');
  135. });
  136. it('settlePayment succeeds', async () => {
  137. await shopClient.asUserWithCredentials(customers[1].emailAddress, password);
  138. await proceedToArrangingPayment(shopClient);
  139. const order = await addPaymentToOrder(shopClient, twoStagePaymentMethod);
  140. expect(order.state).toBe('PaymentAuthorized');
  141. const payment = order.payments![0];
  142. const { settlePayment } = await adminClient.query<
  143. SettlePayment.Mutation,
  144. SettlePayment.Variables
  145. >(SETTLE_PAYMENT, {
  146. id: payment.id,
  147. });
  148. expect(settlePayment!.id).toBe(payment.id);
  149. expect(settlePayment!.state).toBe('Settled');
  150. // further metadata is combined into existing object
  151. expect(settlePayment!.metadata).toEqual({
  152. baz: 'quux',
  153. moreData: 42,
  154. });
  155. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  156. id: order.id,
  157. });
  158. expect(result.order!.state).toBe('PaymentSettled');
  159. expect(result.order!.payments![0].state).toBe('Settled');
  160. });
  161. it('order history contains expected entries', async () => {
  162. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  163. GET_ORDER_HISTORY,
  164. { id: 'T_2' },
  165. );
  166. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  167. {
  168. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  169. data: {
  170. from: 'AddingItems',
  171. to: 'ArrangingPayment',
  172. },
  173. },
  174. {
  175. type: HistoryEntryType.ORDER_PAYMENT_TRANSITION,
  176. data: {
  177. paymentId: 'T_2',
  178. from: 'Created',
  179. to: 'Authorized',
  180. },
  181. },
  182. {
  183. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  184. data: {
  185. from: 'ArrangingPayment',
  186. to: 'PaymentAuthorized',
  187. },
  188. },
  189. {
  190. type: HistoryEntryType.ORDER_PAYMENT_TRANSITION,
  191. data: {
  192. paymentId: 'T_2',
  193. from: 'Authorized',
  194. to: 'Settled',
  195. },
  196. },
  197. {
  198. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  199. data: {
  200. from: 'PaymentAuthorized',
  201. to: 'PaymentSettled',
  202. },
  203. },
  204. ]);
  205. });
  206. });
  207. describe('fulfillment', () => {
  208. it(
  209. 'throws if Order is not in "PaymentSettled" state',
  210. assertThrowsWithMessage(async () => {
  211. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  212. id: 'T_1',
  213. });
  214. expect(order!.state).toBe('PaymentAuthorized');
  215. await adminClient.query<CreateFulfillment.Mutation, CreateFulfillment.Variables>(
  216. CREATE_FULFILLMENT,
  217. {
  218. input: {
  219. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: l.quantity })),
  220. method: 'Test',
  221. },
  222. },
  223. );
  224. }, 'One or more OrderItems belong to an Order which is in an invalid state'),
  225. );
  226. it(
  227. 'throws if lines is empty',
  228. assertThrowsWithMessage(async () => {
  229. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  230. id: 'T_2',
  231. });
  232. expect(order!.state).toBe('PaymentSettled');
  233. await adminClient.query<CreateFulfillment.Mutation, CreateFulfillment.Variables>(
  234. CREATE_FULFILLMENT,
  235. {
  236. input: {
  237. lines: [],
  238. method: 'Test',
  239. },
  240. },
  241. );
  242. }, 'Nothing to fulfill'),
  243. );
  244. it(
  245. 'throws if all quantities are zero',
  246. assertThrowsWithMessage(async () => {
  247. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  248. id: 'T_2',
  249. });
  250. expect(order!.state).toBe('PaymentSettled');
  251. await adminClient.query<CreateFulfillment.Mutation, CreateFulfillment.Variables>(
  252. CREATE_FULFILLMENT,
  253. {
  254. input: {
  255. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 0 })),
  256. method: 'Test',
  257. },
  258. },
  259. );
  260. }, 'Nothing to fulfill'),
  261. );
  262. it('creates a partial fulfillment', async () => {
  263. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  264. id: 'T_2',
  265. });
  266. expect(order!.state).toBe('PaymentSettled');
  267. const lines = order!.lines;
  268. const { fulfillOrder } = await adminClient.query<
  269. CreateFulfillment.Mutation,
  270. CreateFulfillment.Variables
  271. >(CREATE_FULFILLMENT, {
  272. input: {
  273. lines: lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  274. method: 'Test1',
  275. trackingCode: '111',
  276. },
  277. });
  278. expect(fulfillOrder!.method).toBe('Test1');
  279. expect(fulfillOrder!.trackingCode).toBe('111');
  280. expect(fulfillOrder!.orderItems).toEqual([
  281. { id: lines[0].items[0].id },
  282. { id: lines[1].items[0].id },
  283. ]);
  284. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  285. id: 'T_2',
  286. });
  287. expect(result.order!.state).toBe('PartiallyFulfilled');
  288. expect(result.order!.lines[0].items[0].fulfillment!.id).toBe(fulfillOrder!.id);
  289. expect(
  290. result.order!.lines[1].items.filter(
  291. (i) => i.fulfillment && i.fulfillment.id === fulfillOrder.id,
  292. ).length,
  293. ).toBe(1);
  294. expect(result.order!.lines[1].items.filter((i) => i.fulfillment == null).length).toBe(2);
  295. });
  296. it('creates a second partial fulfillment', async () => {
  297. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  298. id: 'T_2',
  299. });
  300. expect(order!.state).toBe('PartiallyFulfilled');
  301. const lines = order!.lines;
  302. const { fulfillOrder } = await adminClient.query<
  303. CreateFulfillment.Mutation,
  304. CreateFulfillment.Variables
  305. >(CREATE_FULFILLMENT, {
  306. input: {
  307. lines: [{ orderLineId: lines[1].id, quantity: 1 }],
  308. method: 'Test2',
  309. trackingCode: '222',
  310. },
  311. });
  312. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  313. id: 'T_2',
  314. });
  315. expect(result.order!.state).toBe('PartiallyFulfilled');
  316. expect(result.order!.lines[1].items.filter((i) => i.fulfillment != null).length).toBe(2);
  317. expect(result.order!.lines[1].items.filter((i) => i.fulfillment == null).length).toBe(1);
  318. });
  319. it(
  320. 'throws if an OrderItem already part of a Fulfillment',
  321. assertThrowsWithMessage(async () => {
  322. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  323. id: 'T_2',
  324. });
  325. expect(order!.state).toBe('PartiallyFulfilled');
  326. await adminClient.query<CreateFulfillment.Mutation, CreateFulfillment.Variables>(
  327. CREATE_FULFILLMENT,
  328. {
  329. input: {
  330. method: 'Test',
  331. lines: [
  332. {
  333. orderLineId: order!.lines[0].id,
  334. quantity: 1,
  335. },
  336. ],
  337. },
  338. },
  339. );
  340. }, 'One or more OrderItems have already been fulfilled'),
  341. );
  342. it('completes fulfillment', async () => {
  343. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  344. id: 'T_2',
  345. });
  346. expect(order!.state).toBe('PartiallyFulfilled');
  347. const orderItems = order!.lines.reduce(
  348. (items, line) => [...items, ...line.items],
  349. [] as OrderItemFragment[],
  350. );
  351. const unfulfilledItem = order!.lines[1].items.find((i) => i.fulfillment == null)!;
  352. const { fulfillOrder } = await adminClient.query<
  353. CreateFulfillment.Mutation,
  354. CreateFulfillment.Variables
  355. >(CREATE_FULFILLMENT, {
  356. input: {
  357. lines: [
  358. {
  359. orderLineId: order!.lines[1].id,
  360. quantity: 1,
  361. },
  362. ],
  363. method: 'Test3',
  364. trackingCode: '333',
  365. },
  366. });
  367. expect(fulfillOrder!.method).toBe('Test3');
  368. expect(fulfillOrder!.trackingCode).toBe('333');
  369. expect(fulfillOrder!.orderItems).toEqual([{ id: unfulfilledItem.id }]);
  370. const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  371. id: 'T_2',
  372. });
  373. expect(result.order!.state).toBe('Fulfilled');
  374. });
  375. it('order history contains expected entries', async () => {
  376. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  377. GET_ORDER_HISTORY,
  378. {
  379. id: 'T_2',
  380. options: {
  381. skip: 5,
  382. },
  383. },
  384. );
  385. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  386. {
  387. type: HistoryEntryType.ORDER_FULLFILLMENT,
  388. data: {
  389. fulfillmentId: 'T_1',
  390. },
  391. },
  392. {
  393. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  394. data: {
  395. from: 'PaymentSettled',
  396. to: 'PartiallyFulfilled',
  397. },
  398. },
  399. {
  400. type: HistoryEntryType.ORDER_FULLFILLMENT,
  401. data: {
  402. fulfillmentId: 'T_2',
  403. },
  404. },
  405. {
  406. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  407. data: {
  408. from: 'PartiallyFulfilled',
  409. to: 'PartiallyFulfilled',
  410. },
  411. },
  412. {
  413. type: HistoryEntryType.ORDER_FULLFILLMENT,
  414. data: {
  415. fulfillmentId: 'T_3',
  416. },
  417. },
  418. {
  419. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  420. data: {
  421. from: 'PartiallyFulfilled',
  422. to: 'Fulfilled',
  423. },
  424. },
  425. ]);
  426. });
  427. it('order.fullfillments resolver for single order', async () => {
  428. const { order } = await adminClient.query<
  429. GetOrderFulfillments.Query,
  430. GetOrderFulfillments.Variables
  431. >(GET_ORDER_FULFILLMENTS, {
  432. id: 'T_2',
  433. });
  434. expect(order!.fulfillments).toEqual([
  435. { id: 'T_1', method: 'Test1' },
  436. { id: 'T_2', method: 'Test2' },
  437. { id: 'T_3', method: 'Test3' },
  438. ]);
  439. });
  440. it('order.fullfillments resolver for order list', async () => {
  441. const { orders } = await adminClient.query<GetOrderListFulfillments.Query>(
  442. GET_ORDER_LIST_FULFILLMENTS,
  443. );
  444. expect(orders.items[0].fulfillments).toEqual([]);
  445. expect(orders.items[1].fulfillments).toEqual([
  446. { id: 'T_1', method: 'Test1' },
  447. { id: 'T_2', method: 'Test2' },
  448. { id: 'T_3', method: 'Test3' },
  449. ]);
  450. });
  451. it('order.fullfillments.orderItems resolver', async () => {
  452. const { order } = await adminClient.query<
  453. GetOrderFulfillmentItems.Query,
  454. GetOrderFulfillmentItems.Variables
  455. >(GET_ORDER_FULFILLMENT_ITEMS, {
  456. id: 'T_2',
  457. });
  458. expect(order!.fulfillments![0].orderItems).toEqual([{ id: 'T_3' }, { id: 'T_4' }]);
  459. expect(order!.fulfillments![1].orderItems).toEqual([{ id: 'T_5' }]);
  460. });
  461. });
  462. describe('cancellation by orderId', () => {
  463. it('cancel from AddingItems state', async () => {
  464. const testOrder = await createTestOrder(
  465. adminClient,
  466. shopClient,
  467. customers[0].emailAddress,
  468. password,
  469. );
  470. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  471. id: testOrder.orderId,
  472. });
  473. expect(order!.state).toBe('AddingItems');
  474. const { cancelOrder } = await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(
  475. CANCEL_ORDER,
  476. {
  477. input: {
  478. orderId: testOrder.orderId,
  479. },
  480. },
  481. );
  482. const { order: order2 } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  483. id: testOrder.orderId,
  484. });
  485. expect(order2!.state).toBe('Cancelled');
  486. expect(order2!.active).toBe(false);
  487. await assertNoStockMovementsCreated(testOrder.product.id);
  488. });
  489. it('cancel from ArrangingPayment state', async () => {
  490. const testOrder = await createTestOrder(
  491. adminClient,
  492. shopClient,
  493. customers[0].emailAddress,
  494. password,
  495. );
  496. await proceedToArrangingPayment(shopClient);
  497. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  498. id: testOrder.orderId,
  499. });
  500. expect(order!.state).toBe('ArrangingPayment');
  501. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  502. input: {
  503. orderId: testOrder.orderId,
  504. },
  505. });
  506. const { order: order2 } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  507. id: testOrder.orderId,
  508. });
  509. expect(order2!.state).toBe('Cancelled');
  510. expect(order2!.active).toBe(false);
  511. await assertNoStockMovementsCreated(testOrder.product.id);
  512. });
  513. it('cancel from PaymentAuthorized state', async () => {
  514. const testOrder = await createTestOrder(
  515. adminClient,
  516. shopClient,
  517. customers[0].emailAddress,
  518. password,
  519. );
  520. await proceedToArrangingPayment(shopClient);
  521. const order = await addPaymentToOrder(shopClient, failsToSettlePaymentMethod);
  522. expect(order.state).toBe('PaymentAuthorized');
  523. const result1 = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  524. GET_STOCK_MOVEMENT,
  525. {
  526. id: 'T_3',
  527. },
  528. );
  529. let variant1 = result1.product!.variants[0];
  530. expect(variant1.stockOnHand).toBe(98);
  531. expect(variant1.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  532. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  533. { type: StockMovementType.SALE, quantity: -2 },
  534. ]);
  535. const { cancelOrder } = await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(
  536. CANCEL_ORDER,
  537. {
  538. input: {
  539. orderId: testOrder.orderId,
  540. },
  541. },
  542. );
  543. expect(cancelOrder.lines.map((l) => l.items.map(pick(['id', 'cancelled'])))).toEqual([
  544. [
  545. { id: 'T_11', cancelled: true },
  546. { id: 'T_12', cancelled: true },
  547. ],
  548. ]);
  549. const { order: order2 } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  550. id: testOrder.orderId,
  551. });
  552. expect(order2!.active).toBe(false);
  553. expect(order2!.state).toBe('Cancelled');
  554. const result2 = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  555. GET_STOCK_MOVEMENT,
  556. {
  557. id: 'T_3',
  558. },
  559. );
  560. variant1 = result2.product!.variants[0];
  561. expect(variant1.stockOnHand).toBe(100);
  562. expect(variant1.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  563. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  564. { type: StockMovementType.SALE, quantity: -2 },
  565. { type: StockMovementType.CANCELLATION, quantity: 1 },
  566. { type: StockMovementType.CANCELLATION, quantity: 1 },
  567. ]);
  568. });
  569. async function assertNoStockMovementsCreated(productId: string) {
  570. const result = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  571. GET_STOCK_MOVEMENT,
  572. {
  573. id: productId,
  574. },
  575. );
  576. const variant2 = result.product!.variants[0];
  577. expect(variant2.stockOnHand).toBe(100);
  578. expect(variant2.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  579. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  580. ]);
  581. }
  582. });
  583. describe('cancellation by OrderLine', () => {
  584. let orderId: string;
  585. let product: GetProductWithVariants.Product;
  586. let productVariantId: string;
  587. beforeAll(async () => {
  588. const result = await createTestOrder(
  589. adminClient,
  590. shopClient,
  591. customers[0].emailAddress,
  592. password,
  593. );
  594. orderId = result.orderId;
  595. product = result.product;
  596. productVariantId = result.productVariantId;
  597. });
  598. it(
  599. 'cannot cancel from AddingItems state',
  600. assertThrowsWithMessage(async () => {
  601. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  602. id: orderId,
  603. });
  604. expect(order!.state).toBe('AddingItems');
  605. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  606. input: {
  607. orderId,
  608. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  609. },
  610. });
  611. }, 'Cannot cancel OrderLines from an Order in the "AddingItems" state'),
  612. );
  613. it(
  614. 'cannot cancel from ArrangingPayment state',
  615. assertThrowsWithMessage(async () => {
  616. await proceedToArrangingPayment(shopClient);
  617. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  618. id: orderId,
  619. });
  620. expect(order!.state).toBe('ArrangingPayment');
  621. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  622. input: {
  623. orderId,
  624. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  625. },
  626. });
  627. }, 'Cannot cancel OrderLines from an Order in the "ArrangingPayment" state'),
  628. );
  629. it(
  630. 'throws if lines are empty',
  631. assertThrowsWithMessage(async () => {
  632. const order = await addPaymentToOrder(shopClient, twoStagePaymentMethod);
  633. expect(order.state).toBe('PaymentAuthorized');
  634. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  635. input: {
  636. orderId,
  637. lines: [],
  638. },
  639. });
  640. }, 'Nothing to cancel'),
  641. );
  642. it(
  643. 'throws if all quantities zero',
  644. assertThrowsWithMessage(async () => {
  645. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  646. id: orderId,
  647. });
  648. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  649. input: {
  650. orderId,
  651. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 0 })),
  652. },
  653. });
  654. }, 'Nothing to cancel'),
  655. );
  656. it('partial cancellation', async () => {
  657. const result1 = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  658. GET_STOCK_MOVEMENT,
  659. {
  660. id: product.id,
  661. },
  662. );
  663. const variant1 = result1.product!.variants[0];
  664. expect(variant1.stockOnHand).toBe(98);
  665. expect(variant1.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  666. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  667. { type: StockMovementType.SALE, quantity: -2 },
  668. { type: StockMovementType.CANCELLATION, quantity: 1 },
  669. { type: StockMovementType.CANCELLATION, quantity: 1 },
  670. { type: StockMovementType.SALE, quantity: -2 },
  671. ]);
  672. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  673. id: orderId,
  674. });
  675. const { cancelOrder } = await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(
  676. CANCEL_ORDER,
  677. {
  678. input: {
  679. orderId,
  680. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  681. reason: 'cancel reason 1',
  682. },
  683. },
  684. );
  685. expect(cancelOrder.lines[0].quantity).toBe(1);
  686. expect(cancelOrder.lines[0].items.sort((a, b) => (a.id < b.id ? -1 : 1))).toEqual([
  687. { id: 'T_13', cancelled: true },
  688. { id: 'T_14', cancelled: false },
  689. ]);
  690. const { order: order2 } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  691. id: orderId,
  692. });
  693. expect(order2!.state).toBe('PaymentAuthorized');
  694. expect(order2!.lines[0].quantity).toBe(1);
  695. const result2 = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  696. GET_STOCK_MOVEMENT,
  697. {
  698. id: product.id,
  699. },
  700. );
  701. const variant2 = result2.product!.variants[0];
  702. expect(variant2.stockOnHand).toBe(99);
  703. expect(variant2.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  704. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  705. { type: StockMovementType.SALE, quantity: -2 },
  706. { type: StockMovementType.CANCELLATION, quantity: 1 },
  707. { type: StockMovementType.CANCELLATION, quantity: 1 },
  708. { type: StockMovementType.SALE, quantity: -2 },
  709. { type: StockMovementType.CANCELLATION, quantity: 1 },
  710. ]);
  711. });
  712. it('complete cancellation', async () => {
  713. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  714. id: orderId,
  715. });
  716. await adminClient.query<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
  717. input: {
  718. orderId,
  719. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  720. reason: 'cancel reason 2',
  721. },
  722. });
  723. const { order: order2 } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  724. id: orderId,
  725. });
  726. expect(order2!.state).toBe('Cancelled');
  727. const result = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
  728. GET_STOCK_MOVEMENT,
  729. {
  730. id: product.id,
  731. },
  732. );
  733. const variant2 = result.product!.variants[0];
  734. expect(variant2.stockOnHand).toBe(100);
  735. expect(variant2.stockMovements.items.map(pick(['type', 'quantity']))).toEqual([
  736. { type: StockMovementType.ADJUSTMENT, quantity: 100 },
  737. { type: StockMovementType.SALE, quantity: -2 },
  738. { type: StockMovementType.CANCELLATION, quantity: 1 },
  739. { type: StockMovementType.CANCELLATION, quantity: 1 },
  740. { type: StockMovementType.SALE, quantity: -2 },
  741. { type: StockMovementType.CANCELLATION, quantity: 1 },
  742. { type: StockMovementType.CANCELLATION, quantity: 1 },
  743. ]);
  744. });
  745. it('order history contains expected entries', async () => {
  746. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  747. GET_ORDER_HISTORY,
  748. {
  749. id: orderId,
  750. options: {
  751. skip: 0,
  752. },
  753. },
  754. );
  755. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  756. {
  757. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  758. data: {
  759. from: 'AddingItems',
  760. to: 'ArrangingPayment',
  761. },
  762. },
  763. {
  764. type: HistoryEntryType.ORDER_PAYMENT_TRANSITION,
  765. data: {
  766. paymentId: 'T_4',
  767. from: 'Created',
  768. to: 'Authorized',
  769. },
  770. },
  771. {
  772. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  773. data: {
  774. from: 'ArrangingPayment',
  775. to: 'PaymentAuthorized',
  776. },
  777. },
  778. {
  779. type: HistoryEntryType.ORDER_CANCELLATION,
  780. data: {
  781. orderItemIds: ['T_13'],
  782. reason: 'cancel reason 1',
  783. },
  784. },
  785. {
  786. type: HistoryEntryType.ORDER_CANCELLATION,
  787. data: {
  788. orderItemIds: ['T_14'],
  789. reason: 'cancel reason 2',
  790. },
  791. },
  792. {
  793. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  794. data: {
  795. from: 'PaymentAuthorized',
  796. to: 'Cancelled',
  797. },
  798. },
  799. ]);
  800. });
  801. });
  802. describe('refunds', () => {
  803. let orderId: string;
  804. let product: GetProductWithVariants.Product;
  805. let productVariantId: string;
  806. let paymentId: string;
  807. let refundId: string;
  808. beforeAll(async () => {
  809. const result = await createTestOrder(
  810. adminClient,
  811. shopClient,
  812. customers[0].emailAddress,
  813. password,
  814. );
  815. orderId = result.orderId;
  816. product = result.product;
  817. productVariantId = result.productVariantId;
  818. });
  819. it(
  820. 'cannot refund from PaymentAuthorized state',
  821. assertThrowsWithMessage(async () => {
  822. await proceedToArrangingPayment(shopClient);
  823. const order = await addPaymentToOrder(shopClient, twoStagePaymentMethod);
  824. expect(order.state).toBe('PaymentAuthorized');
  825. paymentId = order.payments![0].id;
  826. await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(REFUND_ORDER, {
  827. input: {
  828. lines: order.lines.map((l) => ({ orderLineId: l.id, quantity: 1 })),
  829. shipping: 0,
  830. adjustment: 0,
  831. paymentId,
  832. },
  833. });
  834. }, 'Cannot refund an Order in the "PaymentAuthorized" state'),
  835. );
  836. it(
  837. 'throws if no lines and no shipping',
  838. assertThrowsWithMessage(async () => {
  839. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  840. id: orderId,
  841. });
  842. const { settlePayment } = await adminClient.query<
  843. SettlePayment.Mutation,
  844. SettlePayment.Variables
  845. >(SETTLE_PAYMENT, {
  846. id: order!.payments![0].id,
  847. });
  848. expect(settlePayment!.state).toBe('Settled');
  849. await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(REFUND_ORDER, {
  850. input: {
  851. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: 0 })),
  852. shipping: 0,
  853. adjustment: 0,
  854. paymentId,
  855. },
  856. });
  857. }, 'Nothing to refund'),
  858. );
  859. it(
  860. 'throws if paymentId not valid',
  861. assertThrowsWithMessage(async () => {
  862. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  863. id: orderId,
  864. });
  865. const { refundOrder } = await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(
  866. REFUND_ORDER,
  867. {
  868. input: {
  869. lines: [],
  870. shipping: 100,
  871. adjustment: 0,
  872. paymentId: 'T_999',
  873. },
  874. },
  875. );
  876. }, "No Payment with the id '999' could be found"),
  877. );
  878. it(
  879. 'throws if payment and order lines do not belong to the same Order',
  880. assertThrowsWithMessage(async () => {
  881. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  882. id: orderId,
  883. });
  884. const { refundOrder } = await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(
  885. REFUND_ORDER,
  886. {
  887. input: {
  888. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: l.quantity })),
  889. shipping: 100,
  890. adjustment: 0,
  891. paymentId: 'T_1',
  892. },
  893. },
  894. );
  895. }, 'The Payment and OrderLines do not belong to the same Order'),
  896. );
  897. it('creates a Refund to be manually settled', async () => {
  898. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  899. id: orderId,
  900. });
  901. const { refundOrder } = await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(
  902. REFUND_ORDER,
  903. {
  904. input: {
  905. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: l.quantity })),
  906. shipping: order!.shipping,
  907. adjustment: 0,
  908. reason: 'foo',
  909. paymentId,
  910. },
  911. },
  912. );
  913. expect(refundOrder.shipping).toBe(order!.shipping);
  914. expect(refundOrder.items).toBe(order!.subTotal);
  915. expect(refundOrder.total).toBe(order!.total);
  916. expect(refundOrder.transactionId).toBe(null);
  917. expect(refundOrder.state).toBe('Pending');
  918. refundId = refundOrder.id;
  919. });
  920. it(
  921. 'throws if attempting to refund the same item more than once',
  922. assertThrowsWithMessage(async () => {
  923. const { order } = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
  924. id: orderId,
  925. });
  926. const { refundOrder } = await adminClient.query<RefundOrder.Mutation, RefundOrder.Variables>(
  927. REFUND_ORDER,
  928. {
  929. input: {
  930. lines: order!.lines.map((l) => ({ orderLineId: l.id, quantity: l.quantity })),
  931. shipping: order!.shipping,
  932. adjustment: 0,
  933. paymentId,
  934. },
  935. },
  936. );
  937. }, 'Cannot refund an OrderItem which has already been refunded'),
  938. );
  939. it('manually settle a Refund', async () => {
  940. const { settleRefund } = await adminClient.query<SettleRefund.Mutation, SettleRefund.Variables>(
  941. SETTLE_REFUND,
  942. {
  943. input: {
  944. id: refundId,
  945. transactionId: 'aaabbb',
  946. },
  947. },
  948. );
  949. expect(settleRefund.state).toBe('Settled');
  950. expect(settleRefund.transactionId).toBe('aaabbb');
  951. });
  952. it('order history contains expected entries', async () => {
  953. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  954. GET_ORDER_HISTORY,
  955. {
  956. id: orderId,
  957. options: {
  958. skip: 0,
  959. },
  960. },
  961. );
  962. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  963. {
  964. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  965. data: {
  966. from: 'AddingItems',
  967. to: 'ArrangingPayment',
  968. },
  969. },
  970. {
  971. type: HistoryEntryType.ORDER_PAYMENT_TRANSITION,
  972. data: {
  973. paymentId: 'T_5',
  974. from: 'Created',
  975. to: 'Authorized',
  976. },
  977. },
  978. {
  979. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  980. data: {
  981. from: 'ArrangingPayment',
  982. to: 'PaymentAuthorized',
  983. },
  984. },
  985. {
  986. type: HistoryEntryType.ORDER_PAYMENT_TRANSITION,
  987. data: {
  988. paymentId: 'T_5',
  989. from: 'Authorized',
  990. to: 'Settled',
  991. },
  992. },
  993. {
  994. type: HistoryEntryType.ORDER_STATE_TRANSITION,
  995. data: {
  996. from: 'PaymentAuthorized',
  997. to: 'PaymentSettled',
  998. },
  999. },
  1000. {
  1001. type: HistoryEntryType.ORDER_REFUND_TRANSITION,
  1002. data: {
  1003. refundId: 'T_1',
  1004. reason: 'foo',
  1005. from: 'Pending',
  1006. to: 'Settled',
  1007. },
  1008. },
  1009. ]);
  1010. });
  1011. });
  1012. describe('order notes', () => {
  1013. let orderId: string;
  1014. let firstNoteId: string;
  1015. beforeAll(async () => {
  1016. const result = await createTestOrder(
  1017. adminClient,
  1018. shopClient,
  1019. customers[2].emailAddress,
  1020. password,
  1021. );
  1022. orderId = result.orderId;
  1023. });
  1024. it('private note', async () => {
  1025. const { addNoteToOrder } = await adminClient.query<
  1026. AddNoteToOrder.Mutation,
  1027. AddNoteToOrder.Variables
  1028. >(ADD_NOTE_TO_ORDER, {
  1029. input: {
  1030. id: orderId,
  1031. note: 'A private note',
  1032. isPublic: false,
  1033. },
  1034. });
  1035. expect(addNoteToOrder.id).toBe(orderId);
  1036. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  1037. GET_ORDER_HISTORY,
  1038. {
  1039. id: orderId,
  1040. options: {
  1041. skip: 0,
  1042. },
  1043. },
  1044. );
  1045. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  1046. {
  1047. type: HistoryEntryType.ORDER_NOTE,
  1048. data: {
  1049. note: 'A private note',
  1050. },
  1051. },
  1052. ]);
  1053. firstNoteId = order!.history.items[0].id;
  1054. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1055. expect(activeOrder!.history.items.map(pick(['type', 'data']))).toEqual([]);
  1056. });
  1057. it('public note', async () => {
  1058. const { addNoteToOrder } = await adminClient.query<
  1059. AddNoteToOrder.Mutation,
  1060. AddNoteToOrder.Variables
  1061. >(ADD_NOTE_TO_ORDER, {
  1062. input: {
  1063. id: orderId,
  1064. note: 'A public note',
  1065. isPublic: true,
  1066. },
  1067. });
  1068. expect(addNoteToOrder.id).toBe(orderId);
  1069. const { order } = await adminClient.query<GetOrderHistory.Query, GetOrderHistory.Variables>(
  1070. GET_ORDER_HISTORY,
  1071. {
  1072. id: orderId,
  1073. options: {
  1074. skip: 1,
  1075. },
  1076. },
  1077. );
  1078. expect(order!.history.items.map(pick(['type', 'data']))).toEqual([
  1079. {
  1080. type: HistoryEntryType.ORDER_NOTE,
  1081. data: {
  1082. note: 'A public note',
  1083. },
  1084. },
  1085. ]);
  1086. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1087. expect(activeOrder!.history.items.map(pick(['type', 'data']))).toEqual([
  1088. {
  1089. type: HistoryEntryType.ORDER_NOTE,
  1090. data: {
  1091. note: 'A public note',
  1092. },
  1093. },
  1094. ]);
  1095. });
  1096. it('update note', async () => {
  1097. const { updateOrderNote } = await adminClient.query<
  1098. UpdateOrderNote.Mutation,
  1099. UpdateOrderNote.Variables
  1100. >(UPDATE_ORDER_NOTE, {
  1101. input: {
  1102. noteId: firstNoteId,
  1103. note: 'An updated note',
  1104. },
  1105. });
  1106. expect(updateOrderNote.data).toEqual({
  1107. note: 'An updated note',
  1108. });
  1109. });
  1110. it('delete note', async () => {
  1111. const { order: before } = await adminClient.query<
  1112. GetOrderHistory.Query,
  1113. GetOrderHistory.Variables
  1114. >(GET_ORDER_HISTORY, { id: orderId });
  1115. expect(before?.history.totalItems).toBe(2);
  1116. const { deleteOrderNote } = await adminClient.query<
  1117. DeleteOrderNote.Mutation,
  1118. DeleteOrderNote.Variables
  1119. >(DELETE_ORDER_NOTE, {
  1120. id: firstNoteId,
  1121. });
  1122. expect(deleteOrderNote.result).toBe(DeletionResult.DELETED);
  1123. const { order: after } = await adminClient.query<
  1124. GetOrderHistory.Query,
  1125. GetOrderHistory.Variables
  1126. >(GET_ORDER_HISTORY, { id: orderId });
  1127. expect(after?.history.totalItems).toBe(1);
  1128. });
  1129. });
  1130. });
  1131. async function createTestOrder(
  1132. adminClient: SimpleGraphQLClient,
  1133. shopClient: SimpleGraphQLClient,
  1134. emailAddress: string,
  1135. password: string,
  1136. ): Promise<{
  1137. orderId: string;
  1138. product: GetProductWithVariants.Product;
  1139. productVariantId: string;
  1140. }> {
  1141. const result = await adminClient.query<GetProductWithVariants.Query, GetProductWithVariants.Variables>(
  1142. GET_PRODUCT_WITH_VARIANTS,
  1143. {
  1144. id: 'T_3',
  1145. },
  1146. );
  1147. const product = result.product!;
  1148. const productVariantId = product.variants[0].id;
  1149. // Set the ProductVariant to trackInventory
  1150. const { updateProductVariants } = await adminClient.query<
  1151. UpdateProductVariants.Mutation,
  1152. UpdateProductVariants.Variables
  1153. >(UPDATE_PRODUCT_VARIANTS, {
  1154. input: [
  1155. {
  1156. id: productVariantId,
  1157. trackInventory: true,
  1158. },
  1159. ],
  1160. });
  1161. // Add the ProductVariant to the Order
  1162. await shopClient.asUserWithCredentials(emailAddress, password);
  1163. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(
  1164. ADD_ITEM_TO_ORDER,
  1165. {
  1166. productVariantId,
  1167. quantity: 2,
  1168. },
  1169. );
  1170. const orderId = addItemToOrder!.id;
  1171. return { product, productVariantId, orderId };
  1172. }
  1173. export const GET_ORDERS_LIST = gql`
  1174. query GetOrderList($options: OrderListOptions) {
  1175. orders(options: $options) {
  1176. items {
  1177. ...Order
  1178. }
  1179. totalItems
  1180. }
  1181. }
  1182. ${ORDER_FRAGMENT}
  1183. `;
  1184. export const SETTLE_PAYMENT = gql`
  1185. mutation SettlePayment($id: ID!) {
  1186. settlePayment(id: $id) {
  1187. id
  1188. state
  1189. metadata
  1190. }
  1191. }
  1192. `;
  1193. export const CREATE_FULFILLMENT = gql`
  1194. mutation CreateFulfillment($input: FulfillOrderInput!) {
  1195. fulfillOrder(input: $input) {
  1196. id
  1197. method
  1198. trackingCode
  1199. orderItems {
  1200. id
  1201. }
  1202. }
  1203. }
  1204. `;
  1205. export const GET_ORDER_FULFILLMENTS = gql`
  1206. query GetOrderFulfillments($id: ID!) {
  1207. order(id: $id) {
  1208. id
  1209. fulfillments {
  1210. id
  1211. method
  1212. }
  1213. }
  1214. }
  1215. `;
  1216. export const GET_ORDER_LIST_FULFILLMENTS = gql`
  1217. query GetOrderListFulfillments {
  1218. orders {
  1219. items {
  1220. id
  1221. fulfillments {
  1222. id
  1223. method
  1224. }
  1225. }
  1226. }
  1227. }
  1228. `;
  1229. export const GET_ORDER_FULFILLMENT_ITEMS = gql`
  1230. query GetOrderFulfillmentItems($id: ID!) {
  1231. order(id: $id) {
  1232. id
  1233. fulfillments {
  1234. id
  1235. orderItems {
  1236. id
  1237. }
  1238. }
  1239. }
  1240. }
  1241. `;
  1242. export const CANCEL_ORDER = gql`
  1243. mutation CancelOrder($input: CancelOrderInput!) {
  1244. cancelOrder(input: $input) {
  1245. id
  1246. lines {
  1247. quantity
  1248. items {
  1249. id
  1250. cancelled
  1251. }
  1252. }
  1253. }
  1254. }
  1255. `;
  1256. export const REFUND_ORDER = gql`
  1257. mutation RefundOrder($input: RefundOrderInput!) {
  1258. refundOrder(input: $input) {
  1259. id
  1260. state
  1261. items
  1262. transactionId
  1263. shipping
  1264. total
  1265. metadata
  1266. }
  1267. }
  1268. `;
  1269. export const SETTLE_REFUND = gql`
  1270. mutation SettleRefund($input: SettleRefundInput!) {
  1271. settleRefund(input: $input) {
  1272. id
  1273. state
  1274. items
  1275. transactionId
  1276. shipping
  1277. total
  1278. metadata
  1279. }
  1280. }
  1281. `;
  1282. export const GET_ORDER_HISTORY = gql`
  1283. query GetOrderHistory($id: ID!, $options: HistoryEntryListOptions) {
  1284. order(id: $id) {
  1285. id
  1286. history(options: $options) {
  1287. totalItems
  1288. items {
  1289. id
  1290. type
  1291. administrator {
  1292. id
  1293. }
  1294. data
  1295. }
  1296. }
  1297. }
  1298. }
  1299. `;
  1300. export const ADD_NOTE_TO_ORDER = gql`
  1301. mutation AddNoteToOrder($input: AddNoteToOrderInput!) {
  1302. addNoteToOrder(input: $input) {
  1303. id
  1304. }
  1305. }
  1306. `;
  1307. export const UPDATE_ORDER_NOTE = gql`
  1308. mutation UpdateOrderNote($input: UpdateOrderNoteInput!) {
  1309. updateOrderNote(input: $input) {
  1310. id
  1311. data
  1312. isPublic
  1313. }
  1314. }
  1315. `;
  1316. export const DELETE_ORDER_NOTE = gql`
  1317. mutation DeleteOrderNote($id: ID!) {
  1318. deleteOrderNote(id: $id) {
  1319. result
  1320. message
  1321. }
  1322. }
  1323. `;