order.e2e-spec.ts 51 KB

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