@stamhoofd/backend 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template.json +63 -0
- package/.eslintrc.js +61 -0
- package/README.md +40 -0
- package/index.ts +172 -0
- package/jest.config.js +11 -0
- package/migrations.ts +33 -0
- package/package.json +48 -0
- package/src/crons.ts +845 -0
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +42 -0
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +320 -0
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +171 -0
- package/src/endpoints/auth/CreateAdminEndpoint.ts +137 -0
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +68 -0
- package/src/endpoints/auth/CreateTokenEndpoint.ts +200 -0
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +31 -0
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +70 -0
- package/src/endpoints/auth/GetUserEndpoint.test.ts +64 -0
- package/src/endpoints/auth/GetUserEndpoint.ts +57 -0
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +90 -0
- package/src/endpoints/auth/PatchUserEndpoint.ts +122 -0
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +37 -0
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +41 -0
- package/src/endpoints/auth/SignupEndpoint.ts +107 -0
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +89 -0
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +95 -0
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +31 -0
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +101 -0
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +53 -0
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +57 -0
- package/src/endpoints/global/files/UploadFile.ts +147 -0
- package/src/endpoints/global/files/UploadImage.ts +119 -0
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +76 -0
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +43 -0
- package/src/endpoints/global/members/GetMembersEndpoint.ts +429 -0
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +734 -0
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +45 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +105 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +146 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +52 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +80 -0
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +49 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +58 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +62 -0
- package/src/endpoints/global/payments/ExchangeSTPaymentEndpoint.ts +153 -0
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +134 -0
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +44 -0
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +39 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +63 -0
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +68 -0
- package/src/endpoints/global/registration/GetUserBalanceEndpoint.ts +39 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +80 -0
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +41 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +134 -0
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +521 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +37 -0
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +115 -0
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +187 -0
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +424 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +67 -0
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +39 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +57 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +129 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +114 -0
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +234 -0
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +85 -0
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +54 -0
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +49 -0
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +63 -0
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +61 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.test.ts +64 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.ts +84 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +42 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetRegisterCodeEndpoint.ts +65 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +281 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +338 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +196 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +48 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +207 -0
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +202 -0
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +233 -0
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +210 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +93 -0
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +59 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +52 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +73 -0
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +60 -0
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +41 -0
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +217 -0
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +51 -0
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +83 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +125 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +204 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +278 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +60 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +379 -0
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +54 -0
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +45 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +78 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +34 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +44 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +82 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +59 -0
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +51 -0
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +40 -0
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +124 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +130 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +50 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +450 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +335 -0
- package/src/helpers/AddressValidator.test.ts +40 -0
- package/src/helpers/AddressValidator.ts +256 -0
- package/src/helpers/AdminPermissionChecker.ts +1031 -0
- package/src/helpers/AuthenticatedStructures.ts +158 -0
- package/src/helpers/BuckarooHelper.ts +279 -0
- package/src/helpers/CheckSettlements.ts +215 -0
- package/src/helpers/Context.ts +202 -0
- package/src/helpers/CookieHelper.ts +45 -0
- package/src/helpers/ForwardHandler.test.ts +216 -0
- package/src/helpers/ForwardHandler.ts +140 -0
- package/src/helpers/OpenIDConnectHelper.ts +284 -0
- package/src/helpers/StripeHelper.ts +293 -0
- package/src/helpers/StripePayoutChecker.ts +188 -0
- package/src/middleware/ContextMiddleware.ts +16 -0
- package/src/migrations/1646578856-validate-addresses.ts +60 -0
- package/src/seeds/0000000000-example.ts +13 -0
- package/src/seeds/1715028563-user-permissions.ts +52 -0
- package/tests/e2e/stock.test.ts +2120 -0
- package/tests/e2e/tickets.test.ts +926 -0
- package/tests/helpers/StripeMocker.ts +362 -0
- package/tests/helpers/TestServer.ts +21 -0
- package/tests/jest.global.setup.ts +29 -0
- package/tests/jest.setup.ts +59 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,2120 @@
|
|
|
1
|
+
/* eslint-disable jest/expect-expect */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
3
|
+
/* eslint-disable jest/no-standalone-expect */
|
|
4
|
+
import { PatchableArray, PatchableArrayAutoEncoder } from "@simonbackx/simple-encoding";
|
|
5
|
+
import { Request } from "@simonbackx/simple-endpoints";
|
|
6
|
+
import { Order, Organization, OrganizationFactory, StripeAccount, Token, UserFactory, Webshop, WebshopFactory } from "@stamhoofd/models";
|
|
7
|
+
import { Address, Cart, CartItem, CartItemOption, CartReservedSeat, Country, Customer, Option, OptionMenu, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, Product, ProductPrice, ProductType, ReservedSeat, SeatingPlan, SeatingPlanRow, SeatingPlanSeat, SeatingPlanSection, TransferSettings, ValidatedAddress, WebshopDeliveryMethod, WebshopMetaData, WebshopOnSiteMethod, WebshopPrivateMetaData, WebshopTakeoutMethod, WebshopTimeSlot } from "@stamhoofd/structures";
|
|
8
|
+
import { v4 as uuidv4 } from "uuid";
|
|
9
|
+
|
|
10
|
+
import { PatchWebshopOrdersEndpoint } from "../../src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint";
|
|
11
|
+
import { PlaceOrderEndpoint } from '../../src/endpoints/organization/webshops/PlaceOrderEndpoint';
|
|
12
|
+
import { StripeMocker } from "../helpers/StripeMocker";
|
|
13
|
+
import { testServer } from "../helpers/TestServer";
|
|
14
|
+
|
|
15
|
+
const address = Address.create({
|
|
16
|
+
street: 'Demostraat',
|
|
17
|
+
number: '15',
|
|
18
|
+
postalCode: '9000',
|
|
19
|
+
city: 'Gent',
|
|
20
|
+
country: Country.Belgium
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const customer = Customer.create({
|
|
24
|
+
firstName: 'John',
|
|
25
|
+
lastName: 'Doe',
|
|
26
|
+
email: 'john@example.com',
|
|
27
|
+
phone: '+32412345678'
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("E2E.Stock", () => {
|
|
31
|
+
// Test endpoint
|
|
32
|
+
const endpoint = new PlaceOrderEndpoint();
|
|
33
|
+
const patchWebshopOrdersEndpoint = new PatchWebshopOrdersEndpoint();
|
|
34
|
+
|
|
35
|
+
let organization: Organization;
|
|
36
|
+
let webshop: Webshop;
|
|
37
|
+
let product: Product;
|
|
38
|
+
let seatProduct: Product;
|
|
39
|
+
let personProduct: Product;
|
|
40
|
+
let takeoutMethod: WebshopTakeoutMethod;
|
|
41
|
+
let deliveryMethod: WebshopDeliveryMethod;
|
|
42
|
+
let onSiteMethod: WebshopOnSiteMethod;
|
|
43
|
+
let slot1: WebshopTimeSlot;
|
|
44
|
+
let slot2: WebshopTimeSlot;
|
|
45
|
+
let slot3: WebshopTimeSlot;
|
|
46
|
+
let slot4: WebshopTimeSlot;
|
|
47
|
+
|
|
48
|
+
let productPrice1: ProductPrice;
|
|
49
|
+
let productPrice2: ProductPrice;
|
|
50
|
+
let freeProductPrice: ProductPrice;
|
|
51
|
+
let personProductPrice: ProductPrice;
|
|
52
|
+
let seatProductPrice: ProductPrice;
|
|
53
|
+
let seatingPlan: SeatingPlan;
|
|
54
|
+
|
|
55
|
+
let multipleChoiceOptionMenu: OptionMenu;
|
|
56
|
+
let chooseOneOptionMenu: OptionMenu;
|
|
57
|
+
let checkboxOption1: Option;
|
|
58
|
+
let checkboxOption2: Option;
|
|
59
|
+
let radioOption1: Option;
|
|
60
|
+
let radioOption2: Option;
|
|
61
|
+
let stripeMocker: StripeMocker
|
|
62
|
+
let stripeAccount: StripeAccount
|
|
63
|
+
let token: Token;
|
|
64
|
+
|
|
65
|
+
async function refreshAll() {
|
|
66
|
+
webshop = (await Webshop.getByID(webshop.id))!;
|
|
67
|
+
product = webshop.products.find(p => p.id == product.id)!;
|
|
68
|
+
seatProduct = webshop.products.find(p => p.id == seatProduct.id)!;
|
|
69
|
+
personProduct = webshop.products.find(p => p.id == personProduct.id)!;
|
|
70
|
+
takeoutMethod = webshop.meta.checkoutMethods.find(m => m.id == takeoutMethod.id)! as WebshopTakeoutMethod;
|
|
71
|
+
deliveryMethod = webshop.meta.checkoutMethods.find(m => m.id == deliveryMethod.id)! as WebshopDeliveryMethod;
|
|
72
|
+
onSiteMethod = webshop.meta.checkoutMethods.find(m => m.id == onSiteMethod.id)! as WebshopOnSiteMethod;
|
|
73
|
+
slot1 = takeoutMethod.timeSlots.timeSlots.find(s => s.id == slot1.id)!;
|
|
74
|
+
slot2 = takeoutMethod.timeSlots.timeSlots.find(s => s.id == slot2.id)!;
|
|
75
|
+
slot3 = deliveryMethod.timeSlots.timeSlots.find(s => s.id == slot3.id)!;
|
|
76
|
+
slot4 = onSiteMethod.timeSlots.timeSlots.find(s => s.id == slot4.id)!;
|
|
77
|
+
productPrice1 = product.prices.find(p => p.id == productPrice1.id)!;
|
|
78
|
+
productPrice2 = product.prices.find(p => p.id == productPrice2.id)!;
|
|
79
|
+
freeProductPrice = product.prices.find(p => p.id == freeProductPrice.id)!;
|
|
80
|
+
multipleChoiceOptionMenu = product.optionMenus.find(m => m.id == multipleChoiceOptionMenu.id)!;
|
|
81
|
+
chooseOneOptionMenu = product.optionMenus.find(m => m.id == chooseOneOptionMenu.id)!;
|
|
82
|
+
checkboxOption1 = multipleChoiceOptionMenu.options.find(o => o.id == checkboxOption1.id)!;
|
|
83
|
+
checkboxOption2 = multipleChoiceOptionMenu.options.find(o => o.id == checkboxOption2.id)!;
|
|
84
|
+
radioOption1 = chooseOneOptionMenu.options.find(o => o.id == radioOption1.id)!;
|
|
85
|
+
radioOption2 = chooseOneOptionMenu.options.find(o => o.id == radioOption2.id)!;
|
|
86
|
+
personProductPrice = personProduct.prices.find(p => p.id == personProductPrice.id)!;
|
|
87
|
+
seatProductPrice = seatProduct.prices.find(p => p.id == seatProductPrice.id)!;
|
|
88
|
+
seatingPlan = webshop.meta.seatingPlans.find(s => s.id === seatingPlan.id)!;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function refreshCartItems(orderId: string, cartItems: CartItem[]): Promise<Order> {
|
|
92
|
+
const order = (await Order.getByID(orderId))!;
|
|
93
|
+
|
|
94
|
+
for (const item of cartItems) {
|
|
95
|
+
const i = order.data.cart.items.find(i => i.id == item.id)!;
|
|
96
|
+
if (i) {
|
|
97
|
+
item.set(i);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return order;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function checkStocks(orderIds: string[], cartItems: CartItem[], excludedCartItems: CartItem[] = []) {
|
|
104
|
+
await refreshAll();
|
|
105
|
+
const orders: Order[] = [];
|
|
106
|
+
for (const orderId of orderIds) {
|
|
107
|
+
orders.push(await refreshCartItems(orderId, [...cartItems, ...excludedCartItems]));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const products = [product, seatProduct, personProduct];
|
|
111
|
+
for (const product of products) {
|
|
112
|
+
let used = 0;
|
|
113
|
+
const seats: ReservedSeat[] = []
|
|
114
|
+
|
|
115
|
+
for (const item of cartItems) {
|
|
116
|
+
if (item.product.id == product.id) {
|
|
117
|
+
used += item.amount;
|
|
118
|
+
// All seats should be reserved
|
|
119
|
+
expect(item.reservedSeats).toIncludeSameMembers(item.seats);
|
|
120
|
+
seats.push(...item.seats.map(s => ReservedSeat.create(s)));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
expect(product.usedStock).toBe(used);
|
|
124
|
+
expect(product.reservedSeats.length).toBe(seats.length);
|
|
125
|
+
expect(product.reservedSeats).toIncludeSameMembers(seats);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const productPrices = [productPrice1, productPrice2, freeProductPrice, personProductPrice];
|
|
129
|
+
for (const price of productPrices) {
|
|
130
|
+
let used = 0;
|
|
131
|
+
for (const item of cartItems) {
|
|
132
|
+
if (item.productPrice.id == price.id) {
|
|
133
|
+
used += item.amount;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
expect(price.usedStock).toBe(used);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const options = [checkboxOption1, checkboxOption2, radioOption1, radioOption2];
|
|
140
|
+
for (const option of options) {
|
|
141
|
+
let used = 0;
|
|
142
|
+
for (const item of cartItems) {
|
|
143
|
+
for (const o of item.options) {
|
|
144
|
+
if (o.option.id == option.id) {
|
|
145
|
+
used += item.amount;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
expect(option.usedStock).toBe(used);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Now check reserved for each item
|
|
153
|
+
for (const item of cartItems) {
|
|
154
|
+
expect(item.reservedAmount).toBe(item.amount);
|
|
155
|
+
|
|
156
|
+
for (const price of productPrices) {
|
|
157
|
+
if (item.productPrice.id == price.id) {
|
|
158
|
+
expect(item.reservedPrices.get(price.id)).toBe(item.amount);
|
|
159
|
+
} else {
|
|
160
|
+
expect(item.reservedPrices.get(price.id) ?? 0).toBe(0);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const option of options) {
|
|
165
|
+
let reserved = 0;
|
|
166
|
+
for (const o of item.options) {
|
|
167
|
+
if (o.option.id == option.id) {
|
|
168
|
+
reserved += item.amount;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
expect(item.reservedOptions.get(option.id) ?? 0).toBe(reserved);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const item of excludedCartItems) {
|
|
176
|
+
expect(item.reservedAmount).toBe(0);
|
|
177
|
+
expect(item.reservedSeats.length).toBe(0);
|
|
178
|
+
|
|
179
|
+
for (const price of productPrices) {
|
|
180
|
+
expect(item.reservedPrices.get(price.id) ?? 0).toBe(0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const option of options) {
|
|
184
|
+
expect(item.reservedOptions.get(option.id) ?? 0).toBe(0);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check order stock
|
|
189
|
+
for (const order of orders) {
|
|
190
|
+
const filteredItems = cartItems.filter(i => !!order.data.cart.items.find(c => c.id === i.id))
|
|
191
|
+
let persons = 0;
|
|
192
|
+
for (const item of filteredItems) {
|
|
193
|
+
if (item.product.type === ProductType.Person) {
|
|
194
|
+
persons += item.amount;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
expect(order.data.reservedPersons).toBe(persons);
|
|
198
|
+
expect(order.data.reservedOrder).toBe(filteredItems.length > 0);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const timeslots = [slot1, slot2, slot3, slot4];
|
|
202
|
+
for (const slot of timeslots) {
|
|
203
|
+
let ordersCount = 0;
|
|
204
|
+
let personsCount = 0;
|
|
205
|
+
|
|
206
|
+
for (const order of orders) {
|
|
207
|
+
if (order.data.timeSlot?.id === slot.id) {
|
|
208
|
+
let persons = 0;
|
|
209
|
+
const filteredItems = cartItems.filter(i => !!order.data.cart.items.find(c => c.id === i.id))
|
|
210
|
+
|
|
211
|
+
for (const item of filteredItems) {
|
|
212
|
+
if (item.product.type === ProductType.Person) {
|
|
213
|
+
persons += item.amount;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ordersCount += filteredItems.length > 0 ? 1 : 0
|
|
218
|
+
personsCount += persons;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
expect(slot.usedOrders).toBe(ordersCount);
|
|
223
|
+
expect(slot.usedPersons).toBe(personsCount);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return orders;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function checkStock(orderId: string, cartItems: CartItem[], excludedCartItems: CartItem[] = []) {
|
|
230
|
+
const otherOrders = (await Order.where({webshopId: webshop.id})).filter(o => o.id !== orderId)
|
|
231
|
+
return (await checkStocks([orderId, ...otherOrders.map(o => o.id)], [...cartItems, ...otherOrders.flatMap(o => o.data.cart.items)], excludedCartItems))[0]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Allows to change the stock */
|
|
235
|
+
async function saveChanges() {
|
|
236
|
+
// Set products
|
|
237
|
+
webshop = (await Webshop.getByID(webshop.id))!;
|
|
238
|
+
webshop.products = [product, seatProduct, personProduct]
|
|
239
|
+
await webshop.save();
|
|
240
|
+
await refreshAll();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
beforeAll(async () => {
|
|
244
|
+
stripeMocker = new StripeMocker();
|
|
245
|
+
stripeMocker.start();
|
|
246
|
+
organization = await new OrganizationFactory({}).create()
|
|
247
|
+
stripeAccount = await stripeMocker.createStripeAccount(organization.id);
|
|
248
|
+
|
|
249
|
+
const user = await new UserFactory({
|
|
250
|
+
organization,
|
|
251
|
+
permissions: Permissions.create({
|
|
252
|
+
level: PermissionLevel.Full
|
|
253
|
+
})
|
|
254
|
+
}).create()
|
|
255
|
+
token = await Token.createToken(user)
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
afterAll(() => {
|
|
259
|
+
stripeMocker.stop();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
beforeEach(async () => {
|
|
263
|
+
stripeMocker.reset();
|
|
264
|
+
let meta = WebshopMetaData.patch({});
|
|
265
|
+
|
|
266
|
+
productPrice1 = ProductPrice.create({
|
|
267
|
+
name: 'productPrice1',
|
|
268
|
+
price: 100,
|
|
269
|
+
stock: 100
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
productPrice2 = ProductPrice.create({
|
|
273
|
+
name: 'productPrice2',
|
|
274
|
+
price: 150,
|
|
275
|
+
stock: 100
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
freeProductPrice = ProductPrice.create({
|
|
279
|
+
name: 'freeProductPrice',
|
|
280
|
+
price: 0,
|
|
281
|
+
stock: 100
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
checkboxOption1 = Option.create({
|
|
285
|
+
name: 'checkboxOption1',
|
|
286
|
+
price: 10,
|
|
287
|
+
stock: 100
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
checkboxOption2 = Option.create({
|
|
291
|
+
name: 'checkboxOption2',
|
|
292
|
+
price: 0,
|
|
293
|
+
stock: 100
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
radioOption1 = Option.create({
|
|
297
|
+
name: 'radioOption1',
|
|
298
|
+
price: 10,
|
|
299
|
+
stock: 100
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
radioOption2 = Option.create({
|
|
303
|
+
name: 'radioOption2',
|
|
304
|
+
price: 0,
|
|
305
|
+
stock: 100
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
multipleChoiceOptionMenu = OptionMenu.create({
|
|
309
|
+
name: 'multipleChoiceOptionMenu',
|
|
310
|
+
multipleChoice: true,
|
|
311
|
+
options: [checkboxOption1, checkboxOption2]
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
chooseOneOptionMenu = OptionMenu.create({
|
|
315
|
+
name: 'chooseOneOptionMenu',
|
|
316
|
+
multipleChoice: false,
|
|
317
|
+
options: [radioOption1, radioOption2]
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
product = Product.create({
|
|
321
|
+
name: 'product',
|
|
322
|
+
stock: 100,
|
|
323
|
+
prices: [productPrice1, productPrice2, freeProductPrice],
|
|
324
|
+
optionMenus: [multipleChoiceOptionMenu, chooseOneOptionMenu]
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
personProduct = Product.create({
|
|
328
|
+
name: 'personProduct',
|
|
329
|
+
type: ProductType.Person,
|
|
330
|
+
stock: 100
|
|
331
|
+
})
|
|
332
|
+
personProductPrice = personProduct.prices[0]
|
|
333
|
+
|
|
334
|
+
seatingPlan = SeatingPlan.create({
|
|
335
|
+
name: 'Testzaal',
|
|
336
|
+
sections: [
|
|
337
|
+
SeatingPlanSection.create({
|
|
338
|
+
rows: [
|
|
339
|
+
SeatingPlanRow.create({
|
|
340
|
+
label: 'A',
|
|
341
|
+
seats: [
|
|
342
|
+
SeatingPlanSeat.create({
|
|
343
|
+
label: '1'
|
|
344
|
+
}),
|
|
345
|
+
SeatingPlanSeat.create({
|
|
346
|
+
label: '2'
|
|
347
|
+
}),
|
|
348
|
+
SeatingPlanSeat.create({
|
|
349
|
+
label: '3'
|
|
350
|
+
}),
|
|
351
|
+
SeatingPlanSeat.create({
|
|
352
|
+
label: '4'
|
|
353
|
+
})
|
|
354
|
+
]
|
|
355
|
+
}),
|
|
356
|
+
SeatingPlanRow.create({
|
|
357
|
+
label: 'B',
|
|
358
|
+
seats: [
|
|
359
|
+
SeatingPlanSeat.create({
|
|
360
|
+
label: '1'
|
|
361
|
+
}),
|
|
362
|
+
SeatingPlanSeat.create({
|
|
363
|
+
label: '2'
|
|
364
|
+
}),
|
|
365
|
+
SeatingPlanSeat.create({
|
|
366
|
+
label: '3'
|
|
367
|
+
}),
|
|
368
|
+
SeatingPlanSeat.create({
|
|
369
|
+
label: '4'
|
|
370
|
+
})
|
|
371
|
+
]
|
|
372
|
+
})
|
|
373
|
+
]
|
|
374
|
+
})
|
|
375
|
+
]
|
|
376
|
+
})
|
|
377
|
+
meta.seatingPlans.addPut(seatingPlan)
|
|
378
|
+
|
|
379
|
+
seatProduct = Product.create({
|
|
380
|
+
name: 'seatProduct',
|
|
381
|
+
type: ProductType.Ticket,
|
|
382
|
+
seatingPlanId: seatingPlan.id
|
|
383
|
+
})
|
|
384
|
+
seatProductPrice = seatProduct.prices[0]
|
|
385
|
+
|
|
386
|
+
// Takeout
|
|
387
|
+
takeoutMethod = WebshopTakeoutMethod.create({
|
|
388
|
+
name: 'Bakkerij Test',
|
|
389
|
+
address
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
slot1 = WebshopTimeSlot.create({
|
|
393
|
+
date: new Date(),
|
|
394
|
+
maxPersons: 100,
|
|
395
|
+
maxOrders: 100
|
|
396
|
+
});
|
|
397
|
+
takeoutMethod.timeSlots.timeSlots.push(slot1)
|
|
398
|
+
|
|
399
|
+
slot2 = WebshopTimeSlot.create({
|
|
400
|
+
date: new Date(),
|
|
401
|
+
maxPersons: 100,
|
|
402
|
+
maxOrders: 100,
|
|
403
|
+
startTime: 14*60,
|
|
404
|
+
endTime: 15*60
|
|
405
|
+
})
|
|
406
|
+
takeoutMethod.timeSlots.timeSlots.push(slot2)
|
|
407
|
+
meta.checkoutMethods.addPut(takeoutMethod)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
// Delivery
|
|
411
|
+
deliveryMethod = WebshopDeliveryMethod.create({
|
|
412
|
+
name: 'Delivery',
|
|
413
|
+
countries: [Country.Belgium]
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
slot3 = WebshopTimeSlot.create({
|
|
417
|
+
date: new Date(),
|
|
418
|
+
maxPersons: 100,
|
|
419
|
+
maxOrders: 100
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
deliveryMethod.timeSlots.timeSlots.push(slot3)
|
|
423
|
+
meta.checkoutMethods.addPut(deliveryMethod)
|
|
424
|
+
|
|
425
|
+
// OnSite
|
|
426
|
+
onSiteMethod = WebshopOnSiteMethod.create({
|
|
427
|
+
name: 'Onsite',
|
|
428
|
+
address
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
slot4 = WebshopTimeSlot.create({
|
|
432
|
+
date: new Date(),
|
|
433
|
+
maxPersons: 100,
|
|
434
|
+
maxOrders: 100
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
onSiteMethod.timeSlots.timeSlots.push(slot4)
|
|
438
|
+
meta.checkoutMethods.addPut(onSiteMethod)
|
|
439
|
+
|
|
440
|
+
const paymentConfigurationPatch = PaymentConfiguration.patch({
|
|
441
|
+
transferSettings: TransferSettings.create({
|
|
442
|
+
iban: 'BE56587127952688' // = random IBAN
|
|
443
|
+
}),
|
|
444
|
+
})
|
|
445
|
+
paymentConfigurationPatch.paymentMethods.addPut(PaymentMethod.PointOfSale)
|
|
446
|
+
paymentConfigurationPatch.paymentMethods.addPut(PaymentMethod.Transfer)
|
|
447
|
+
paymentConfigurationPatch.paymentMethods.addPut(PaymentMethod.Bancontact)
|
|
448
|
+
|
|
449
|
+
const privatePaymentConfiguration = PrivatePaymentConfiguration.patch({
|
|
450
|
+
stripeAccountId: stripeAccount.id
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
meta = meta.patch({
|
|
454
|
+
paymentConfiguration: paymentConfigurationPatch
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
const privateMeta = WebshopPrivateMetaData.patch({
|
|
458
|
+
paymentConfiguration: privatePaymentConfiguration
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
webshop = await new WebshopFactory({
|
|
462
|
+
organizationId: organization.id,
|
|
463
|
+
name: 'Test webshop',
|
|
464
|
+
meta,
|
|
465
|
+
privateMeta,
|
|
466
|
+
products: [product, seatProduct, personProduct]
|
|
467
|
+
}).create()
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe('Reserving stock', () => {
|
|
471
|
+
test("Online payments reserve the stock and remain if they succeed", async () => {
|
|
472
|
+
const orderData = OrderData.create({
|
|
473
|
+
paymentMethod: PaymentMethod.Bancontact,
|
|
474
|
+
checkoutMethod: onSiteMethod,
|
|
475
|
+
timeSlot: slot4,
|
|
476
|
+
cart: Cart.create({
|
|
477
|
+
items: [
|
|
478
|
+
CartItem.create({
|
|
479
|
+
product,
|
|
480
|
+
productPrice: productPrice2,
|
|
481
|
+
amount: 5,
|
|
482
|
+
options: [
|
|
483
|
+
CartItemOption.create({
|
|
484
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
485
|
+
option: checkboxOption1
|
|
486
|
+
}),
|
|
487
|
+
CartItemOption.create({
|
|
488
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
489
|
+
option: checkboxOption2
|
|
490
|
+
}),
|
|
491
|
+
CartItemOption.create({
|
|
492
|
+
optionMenu: chooseOneOptionMenu,
|
|
493
|
+
option: radioOption2
|
|
494
|
+
})
|
|
495
|
+
]
|
|
496
|
+
})
|
|
497
|
+
]
|
|
498
|
+
}),
|
|
499
|
+
customer
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
503
|
+
|
|
504
|
+
const response = await testServer.test(endpoint, r);
|
|
505
|
+
expect(response.body).toBeDefined();
|
|
506
|
+
const order = response.body.order;
|
|
507
|
+
|
|
508
|
+
await checkStock(order.id, order.data.cart.items);
|
|
509
|
+
|
|
510
|
+
// Cancel the payment
|
|
511
|
+
await stripeMocker.succeedPayment(stripeMocker.getLastIntent())
|
|
512
|
+
|
|
513
|
+
const updatedOrder = await checkStock(order.id, order.data.cart.items);
|
|
514
|
+
expect(updatedOrder.status).toBe(OrderStatus.Created);
|
|
515
|
+
expect(updatedOrder.number).toBeDefined();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("Online payments do not reserve the stock if creation fails", async () => {
|
|
519
|
+
stripeMocker.forceFailure();
|
|
520
|
+
const orderData = OrderData.create({
|
|
521
|
+
paymentMethod: PaymentMethod.Bancontact,
|
|
522
|
+
checkoutMethod: onSiteMethod,
|
|
523
|
+
timeSlot: slot4,
|
|
524
|
+
cart: Cart.create({
|
|
525
|
+
items: [
|
|
526
|
+
CartItem.create({
|
|
527
|
+
product,
|
|
528
|
+
productPrice: productPrice2,
|
|
529
|
+
amount: 5,
|
|
530
|
+
options: [
|
|
531
|
+
CartItemOption.create({
|
|
532
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
533
|
+
option: checkboxOption1
|
|
534
|
+
}),
|
|
535
|
+
CartItemOption.create({
|
|
536
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
537
|
+
option: checkboxOption2
|
|
538
|
+
}),
|
|
539
|
+
CartItemOption.create({
|
|
540
|
+
optionMenu: chooseOneOptionMenu,
|
|
541
|
+
option: radioOption2
|
|
542
|
+
})
|
|
543
|
+
]
|
|
544
|
+
})
|
|
545
|
+
]
|
|
546
|
+
}),
|
|
547
|
+
customer
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
551
|
+
|
|
552
|
+
await expect(testServer.test(endpoint, r)).toReject();
|
|
553
|
+
await checkStocks([], []);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test("Free payments reserve the stock", async () => {
|
|
557
|
+
const orderData = OrderData.create({
|
|
558
|
+
paymentMethod: PaymentMethod.Unknown,
|
|
559
|
+
checkoutMethod: onSiteMethod,
|
|
560
|
+
timeSlot: slot4,
|
|
561
|
+
cart: Cart.create({
|
|
562
|
+
items: [
|
|
563
|
+
CartItem.create({
|
|
564
|
+
product,
|
|
565
|
+
productPrice: freeProductPrice,
|
|
566
|
+
amount: 5,
|
|
567
|
+
options: [
|
|
568
|
+
CartItemOption.create({
|
|
569
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
570
|
+
option: checkboxOption2
|
|
571
|
+
}),
|
|
572
|
+
CartItemOption.create({
|
|
573
|
+
optionMenu: chooseOneOptionMenu,
|
|
574
|
+
option: radioOption2
|
|
575
|
+
})
|
|
576
|
+
]
|
|
577
|
+
})
|
|
578
|
+
]
|
|
579
|
+
}),
|
|
580
|
+
customer
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
584
|
+
|
|
585
|
+
const response = await testServer.test(endpoint, r);
|
|
586
|
+
expect(response.body).toBeDefined();
|
|
587
|
+
const order = response.body.order;
|
|
588
|
+
|
|
589
|
+
await checkStock(order.id, order.data.cart.items);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
test("Transfer payments reserve the stock", async () => {
|
|
593
|
+
const orderData = OrderData.create({
|
|
594
|
+
paymentMethod: PaymentMethod.Transfer,
|
|
595
|
+
checkoutMethod: onSiteMethod,
|
|
596
|
+
timeSlot: slot4,
|
|
597
|
+
cart: Cart.create({
|
|
598
|
+
items: [
|
|
599
|
+
CartItem.create({
|
|
600
|
+
product,
|
|
601
|
+
productPrice: productPrice2,
|
|
602
|
+
amount: 5,
|
|
603
|
+
options: [
|
|
604
|
+
CartItemOption.create({
|
|
605
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
606
|
+
option: checkboxOption1
|
|
607
|
+
}),
|
|
608
|
+
CartItemOption.create({
|
|
609
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
610
|
+
option: checkboxOption2
|
|
611
|
+
}),
|
|
612
|
+
CartItemOption.create({
|
|
613
|
+
optionMenu: chooseOneOptionMenu,
|
|
614
|
+
option: radioOption2
|
|
615
|
+
})
|
|
616
|
+
]
|
|
617
|
+
})
|
|
618
|
+
]
|
|
619
|
+
}),
|
|
620
|
+
customer
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
624
|
+
|
|
625
|
+
const response = await testServer.test(endpoint, r);
|
|
626
|
+
expect(response.body).toBeDefined();
|
|
627
|
+
const order = response.body.order;
|
|
628
|
+
|
|
629
|
+
await checkStock(order.id, order.data.cart.items);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test("Transfer payments do not reserve the stock if iban is missing and throws on validating", async () => {
|
|
633
|
+
webshop.meta.paymentConfiguration.transferSettings.iban = null;
|
|
634
|
+
await webshop.save();
|
|
635
|
+
|
|
636
|
+
const orderData = OrderData.create({
|
|
637
|
+
paymentMethod: PaymentMethod.Transfer,
|
|
638
|
+
checkoutMethod: onSiteMethod,
|
|
639
|
+
timeSlot: slot4,
|
|
640
|
+
cart: Cart.create({
|
|
641
|
+
items: [
|
|
642
|
+
CartItem.create({
|
|
643
|
+
product,
|
|
644
|
+
productPrice: productPrice2,
|
|
645
|
+
amount: 5,
|
|
646
|
+
options: [
|
|
647
|
+
CartItemOption.create({
|
|
648
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
649
|
+
option: checkboxOption1
|
|
650
|
+
}),
|
|
651
|
+
CartItemOption.create({
|
|
652
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
653
|
+
option: checkboxOption2
|
|
654
|
+
}),
|
|
655
|
+
CartItemOption.create({
|
|
656
|
+
optionMenu: chooseOneOptionMenu,
|
|
657
|
+
option: radioOption2
|
|
658
|
+
})
|
|
659
|
+
]
|
|
660
|
+
})
|
|
661
|
+
]
|
|
662
|
+
}),
|
|
663
|
+
customer
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
667
|
+
|
|
668
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow('Missing IBAN');
|
|
669
|
+
await checkStocks([], []);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
test("POS payments reserve the stock", async () => {
|
|
673
|
+
const orderData = OrderData.create({
|
|
674
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
675
|
+
checkoutMethod: takeoutMethod,
|
|
676
|
+
timeSlot: slot1,
|
|
677
|
+
cart: Cart.create({
|
|
678
|
+
items: [
|
|
679
|
+
CartItem.create({
|
|
680
|
+
product,
|
|
681
|
+
productPrice: productPrice1,
|
|
682
|
+
amount: 5,
|
|
683
|
+
options: [
|
|
684
|
+
CartItemOption.create({
|
|
685
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
686
|
+
option: checkboxOption2
|
|
687
|
+
}),
|
|
688
|
+
CartItemOption.create({
|
|
689
|
+
optionMenu: chooseOneOptionMenu,
|
|
690
|
+
option: radioOption1
|
|
691
|
+
})
|
|
692
|
+
]
|
|
693
|
+
})
|
|
694
|
+
]
|
|
695
|
+
}),
|
|
696
|
+
customer
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
700
|
+
|
|
701
|
+
const response = await testServer.test(endpoint, r);
|
|
702
|
+
expect(response.body).toBeDefined();
|
|
703
|
+
const order = response.body.order;
|
|
704
|
+
|
|
705
|
+
await checkStock(order.id, order.data.cart.items);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test("Orders placed by an admin reserve the stock", async () => {
|
|
709
|
+
const orderData = OrderData.create({
|
|
710
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
711
|
+
checkoutMethod: takeoutMethod,
|
|
712
|
+
timeSlot: slot1,
|
|
713
|
+
cart: Cart.create({
|
|
714
|
+
items: [
|
|
715
|
+
CartItem.create({
|
|
716
|
+
product,
|
|
717
|
+
productPrice: productPrice1,
|
|
718
|
+
amount: 5,
|
|
719
|
+
options: [
|
|
720
|
+
CartItemOption.create({
|
|
721
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
722
|
+
option: checkboxOption2
|
|
723
|
+
}),
|
|
724
|
+
CartItemOption.create({
|
|
725
|
+
optionMenu: chooseOneOptionMenu,
|
|
726
|
+
option: radioOption1
|
|
727
|
+
})
|
|
728
|
+
]
|
|
729
|
+
})
|
|
730
|
+
]
|
|
731
|
+
}),
|
|
732
|
+
customer
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
736
|
+
|
|
737
|
+
const orderPatch = PrivateOrder.create({
|
|
738
|
+
id: uuidv4(),
|
|
739
|
+
data: orderData,
|
|
740
|
+
webshopId: webshop.id
|
|
741
|
+
});
|
|
742
|
+
patchArray.addPut(orderPatch);
|
|
743
|
+
|
|
744
|
+
// Send a patch
|
|
745
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
746
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
747
|
+
|
|
748
|
+
const response = await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
749
|
+
expect(response.body).toBeDefined();
|
|
750
|
+
const order = response.body[0];
|
|
751
|
+
await checkStock(order.id, order.data.cart.items);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
test("Orders placed by an admin do not reserve the stock if IBAN is missing", async () => {
|
|
755
|
+
|
|
756
|
+
webshop.meta.paymentConfiguration.transferSettings.iban = null;
|
|
757
|
+
await webshop.save();
|
|
758
|
+
|
|
759
|
+
const orderData = OrderData.create({
|
|
760
|
+
paymentMethod: PaymentMethod.Transfer,
|
|
761
|
+
checkoutMethod: takeoutMethod,
|
|
762
|
+
timeSlot: slot1,
|
|
763
|
+
cart: Cart.create({
|
|
764
|
+
items: [
|
|
765
|
+
CartItem.create({
|
|
766
|
+
product,
|
|
767
|
+
productPrice: productPrice1,
|
|
768
|
+
amount: 5,
|
|
769
|
+
options: [
|
|
770
|
+
CartItemOption.create({
|
|
771
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
772
|
+
option: checkboxOption2
|
|
773
|
+
}),
|
|
774
|
+
CartItemOption.create({
|
|
775
|
+
optionMenu: chooseOneOptionMenu,
|
|
776
|
+
option: radioOption1
|
|
777
|
+
})
|
|
778
|
+
]
|
|
779
|
+
})
|
|
780
|
+
]
|
|
781
|
+
}),
|
|
782
|
+
customer
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
786
|
+
|
|
787
|
+
const orderPatch = PrivateOrder.create({
|
|
788
|
+
id: uuidv4(),
|
|
789
|
+
data: orderData,
|
|
790
|
+
webshopId: webshop.id
|
|
791
|
+
});
|
|
792
|
+
patchArray.addPut(orderPatch);
|
|
793
|
+
|
|
794
|
+
// Send a patch
|
|
795
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
796
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
797
|
+
|
|
798
|
+
await expect(testServer.test(patchWebshopOrdersEndpoint, r)).rejects.toThrow('Missing IBAN');
|
|
799
|
+
await checkStocks([], []);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
test("Chosen seats reserve the stock for POS order", async () => {
|
|
803
|
+
const orderData = OrderData.create({
|
|
804
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
805
|
+
checkoutMethod: takeoutMethod,
|
|
806
|
+
timeSlot: slot1,
|
|
807
|
+
cart: Cart.create({
|
|
808
|
+
items: [
|
|
809
|
+
CartItem.create({
|
|
810
|
+
product: seatProduct,
|
|
811
|
+
productPrice: seatProductPrice,
|
|
812
|
+
amount: 2,
|
|
813
|
+
seats: [
|
|
814
|
+
CartReservedSeat.create({
|
|
815
|
+
section: seatingPlan.sections[0].id,
|
|
816
|
+
row: 'A',
|
|
817
|
+
seat: '1'
|
|
818
|
+
}),
|
|
819
|
+
CartReservedSeat.create({
|
|
820
|
+
section: seatingPlan.sections[0].id,
|
|
821
|
+
row: 'A',
|
|
822
|
+
seat: '2'
|
|
823
|
+
})
|
|
824
|
+
]
|
|
825
|
+
})
|
|
826
|
+
]
|
|
827
|
+
}),
|
|
828
|
+
customer
|
|
829
|
+
})
|
|
830
|
+
|
|
831
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
832
|
+
|
|
833
|
+
const response = await testServer.test(endpoint, r);
|
|
834
|
+
expect(response.body).toBeDefined();
|
|
835
|
+
const order = response.body.order;
|
|
836
|
+
|
|
837
|
+
await checkStock(order.id, order.data.cart.items);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
test("Chosen seats reserve the stock for an admin order", async () => {
|
|
841
|
+
const orderData = OrderData.create({
|
|
842
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
843
|
+
checkoutMethod: takeoutMethod,
|
|
844
|
+
timeSlot: slot1,
|
|
845
|
+
cart: Cart.create({
|
|
846
|
+
items: [
|
|
847
|
+
CartItem.create({
|
|
848
|
+
product: seatProduct,
|
|
849
|
+
productPrice: seatProductPrice,
|
|
850
|
+
amount: 2,
|
|
851
|
+
seats: [
|
|
852
|
+
CartReservedSeat.create({
|
|
853
|
+
section: seatingPlan.sections[0].id,
|
|
854
|
+
row: 'A',
|
|
855
|
+
seat: '1'
|
|
856
|
+
}),
|
|
857
|
+
CartReservedSeat.create({
|
|
858
|
+
section: seatingPlan.sections[0].id,
|
|
859
|
+
row: 'A',
|
|
860
|
+
seat: '2'
|
|
861
|
+
})
|
|
862
|
+
]
|
|
863
|
+
})
|
|
864
|
+
]
|
|
865
|
+
}),
|
|
866
|
+
customer
|
|
867
|
+
})
|
|
868
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
869
|
+
|
|
870
|
+
const orderPatch = PrivateOrder.create({
|
|
871
|
+
id: uuidv4(),
|
|
872
|
+
data: orderData,
|
|
873
|
+
webshopId: webshop.id
|
|
874
|
+
});
|
|
875
|
+
patchArray.addPut(orderPatch);
|
|
876
|
+
|
|
877
|
+
// Send a patch
|
|
878
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
879
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
880
|
+
|
|
881
|
+
const response = await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
882
|
+
expect(response.body).toBeDefined();
|
|
883
|
+
const order = response.body[0];
|
|
884
|
+
await checkStock(order.id, order.data.cart.items);
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
test("Amount of a cart item should match the amount of chosen seats", async () => {
|
|
888
|
+
const orderData = OrderData.create({
|
|
889
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
890
|
+
checkoutMethod: takeoutMethod,
|
|
891
|
+
timeSlot: slot1,
|
|
892
|
+
cart: Cart.create({
|
|
893
|
+
items: [
|
|
894
|
+
CartItem.create({
|
|
895
|
+
product: seatProduct,
|
|
896
|
+
productPrice: seatProductPrice,
|
|
897
|
+
amount: 2,
|
|
898
|
+
seats: [
|
|
899
|
+
CartReservedSeat.create({
|
|
900
|
+
section: seatingPlan.sections[0].id,
|
|
901
|
+
row: 'A',
|
|
902
|
+
seat: '1'
|
|
903
|
+
})
|
|
904
|
+
]
|
|
905
|
+
})
|
|
906
|
+
]
|
|
907
|
+
}),
|
|
908
|
+
customer
|
|
909
|
+
})
|
|
910
|
+
|
|
911
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
912
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow('Invalid seats');
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
test.todo("Amount of persons and orders for a takeout method is calculated correctly");
|
|
916
|
+
|
|
917
|
+
test.todo("Amount of persons and orders for a delivery method is calculated correctly");
|
|
918
|
+
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
describe('Full stock', () => {
|
|
922
|
+
test("Cannot place an order when product stock is full", async () => {
|
|
923
|
+
// Set stock
|
|
924
|
+
product.stock = 2;
|
|
925
|
+
await saveChanges();
|
|
926
|
+
|
|
927
|
+
const orderData = OrderData.create({
|
|
928
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
929
|
+
checkoutMethod: takeoutMethod,
|
|
930
|
+
timeSlot: slot1,
|
|
931
|
+
cart: Cart.create({
|
|
932
|
+
items: [
|
|
933
|
+
CartItem.create({
|
|
934
|
+
product,
|
|
935
|
+
productPrice: productPrice1,
|
|
936
|
+
amount: 5,
|
|
937
|
+
options: [
|
|
938
|
+
CartItemOption.create({
|
|
939
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
940
|
+
option: checkboxOption2
|
|
941
|
+
}),
|
|
942
|
+
CartItemOption.create({
|
|
943
|
+
optionMenu: chooseOneOptionMenu,
|
|
944
|
+
option: radioOption1
|
|
945
|
+
})
|
|
946
|
+
]
|
|
947
|
+
})
|
|
948
|
+
]
|
|
949
|
+
}),
|
|
950
|
+
customer
|
|
951
|
+
})
|
|
952
|
+
|
|
953
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
954
|
+
|
|
955
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow('Product unavailable');
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
test("Cannot place an order when product price stock is full", async () => {
|
|
959
|
+
// Set stock
|
|
960
|
+
productPrice1.stock = 2;
|
|
961
|
+
await saveChanges();
|
|
962
|
+
|
|
963
|
+
const orderData = OrderData.create({
|
|
964
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
965
|
+
checkoutMethod: takeoutMethod,
|
|
966
|
+
timeSlot: slot1,
|
|
967
|
+
cart: Cart.create({
|
|
968
|
+
items: [
|
|
969
|
+
CartItem.create({
|
|
970
|
+
product,
|
|
971
|
+
productPrice: productPrice1,
|
|
972
|
+
amount: 5,
|
|
973
|
+
options: [
|
|
974
|
+
CartItemOption.create({
|
|
975
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
976
|
+
option: checkboxOption2
|
|
977
|
+
}),
|
|
978
|
+
CartItemOption.create({
|
|
979
|
+
optionMenu: chooseOneOptionMenu,
|
|
980
|
+
option: radioOption1
|
|
981
|
+
})
|
|
982
|
+
]
|
|
983
|
+
})
|
|
984
|
+
]
|
|
985
|
+
}),
|
|
986
|
+
customer
|
|
987
|
+
})
|
|
988
|
+
|
|
989
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
990
|
+
|
|
991
|
+
await expect(testServer.test(endpoint, r)).rejects.toHaveProperty('human','Er zijn nog maar 2 stuks van productPrice1 beschikbaar');
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
test("Cannot place an order when option stock is full", async () => {
|
|
995
|
+
// Set stock
|
|
996
|
+
radioOption1.stock = 2;
|
|
997
|
+
await saveChanges();
|
|
998
|
+
|
|
999
|
+
const orderData = OrderData.create({
|
|
1000
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1001
|
+
checkoutMethod: takeoutMethod,
|
|
1002
|
+
timeSlot: slot1,
|
|
1003
|
+
cart: Cart.create({
|
|
1004
|
+
items: [
|
|
1005
|
+
CartItem.create({
|
|
1006
|
+
product,
|
|
1007
|
+
productPrice: productPrice1,
|
|
1008
|
+
amount: 5,
|
|
1009
|
+
options: [
|
|
1010
|
+
CartItemOption.create({
|
|
1011
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1012
|
+
option: checkboxOption2
|
|
1013
|
+
}),
|
|
1014
|
+
CartItemOption.create({
|
|
1015
|
+
optionMenu: chooseOneOptionMenu,
|
|
1016
|
+
option: radioOption1
|
|
1017
|
+
})
|
|
1018
|
+
]
|
|
1019
|
+
})
|
|
1020
|
+
]
|
|
1021
|
+
}),
|
|
1022
|
+
customer
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1026
|
+
|
|
1027
|
+
await expect(testServer.test(endpoint, r)).rejects.toHaveProperty('human','Er zijn nog maar 2 stuks van radioOption1 beschikbaar');
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
test("Cannot place an order when multiple choice option stock is full", async () => {
|
|
1031
|
+
// Set stock
|
|
1032
|
+
checkboxOption2.stock = 2;
|
|
1033
|
+
await saveChanges();
|
|
1034
|
+
|
|
1035
|
+
const orderData = OrderData.create({
|
|
1036
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1037
|
+
checkoutMethod: takeoutMethod,
|
|
1038
|
+
timeSlot: slot1,
|
|
1039
|
+
cart: Cart.create({
|
|
1040
|
+
items: [
|
|
1041
|
+
CartItem.create({
|
|
1042
|
+
product,
|
|
1043
|
+
productPrice: productPrice1,
|
|
1044
|
+
amount: 5,
|
|
1045
|
+
options: [
|
|
1046
|
+
CartItemOption.create({
|
|
1047
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1048
|
+
option: checkboxOption2
|
|
1049
|
+
}),
|
|
1050
|
+
CartItemOption.create({
|
|
1051
|
+
optionMenu: chooseOneOptionMenu,
|
|
1052
|
+
option: radioOption1
|
|
1053
|
+
})
|
|
1054
|
+
]
|
|
1055
|
+
})
|
|
1056
|
+
]
|
|
1057
|
+
}),
|
|
1058
|
+
customer
|
|
1059
|
+
})
|
|
1060
|
+
|
|
1061
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1062
|
+
|
|
1063
|
+
await expect(testServer.test(endpoint, r)).rejects.toHaveProperty('human','Er zijn nog maar 2 stuks van checkboxOption2 beschikbaar');
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
test.todo("Cannot place an order when takeout persons stock is full");
|
|
1067
|
+
|
|
1068
|
+
test.todo("Cannot place an order when takeout orders stock is full");
|
|
1069
|
+
|
|
1070
|
+
test("Cannot place an order for a reserved seat", async () => {
|
|
1071
|
+
// Set stock
|
|
1072
|
+
seatProduct.reservedSeats = [
|
|
1073
|
+
ReservedSeat.create({
|
|
1074
|
+
section: seatingPlan.sections[0].id,
|
|
1075
|
+
row: 'A',
|
|
1076
|
+
seat: '1'
|
|
1077
|
+
})
|
|
1078
|
+
]
|
|
1079
|
+
await saveChanges();
|
|
1080
|
+
|
|
1081
|
+
const orderData = OrderData.create({
|
|
1082
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1083
|
+
checkoutMethod: takeoutMethod,
|
|
1084
|
+
timeSlot: slot1,
|
|
1085
|
+
cart: Cart.create({
|
|
1086
|
+
items: [
|
|
1087
|
+
CartItem.create({
|
|
1088
|
+
product: seatProduct,
|
|
1089
|
+
productPrice: seatProductPrice,
|
|
1090
|
+
amount: 2,
|
|
1091
|
+
seats: [
|
|
1092
|
+
CartReservedSeat.create({
|
|
1093
|
+
section: seatingPlan.sections[0].id,
|
|
1094
|
+
row: 'A',
|
|
1095
|
+
seat: '1'
|
|
1096
|
+
}),
|
|
1097
|
+
CartReservedSeat.create({
|
|
1098
|
+
section: seatingPlan.sections[0].id,
|
|
1099
|
+
row: 'A',
|
|
1100
|
+
seat: '2'
|
|
1101
|
+
})
|
|
1102
|
+
]
|
|
1103
|
+
})
|
|
1104
|
+
]
|
|
1105
|
+
}),
|
|
1106
|
+
customer
|
|
1107
|
+
})
|
|
1108
|
+
|
|
1109
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1110
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow('Seats unavailable');
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
test("Admin cannot place an order for a reserved seat", async () => {
|
|
1114
|
+
// Set stock
|
|
1115
|
+
seatProduct.reservedSeats = [
|
|
1116
|
+
ReservedSeat.create({
|
|
1117
|
+
section: seatingPlan.sections[0].id,
|
|
1118
|
+
row: 'A',
|
|
1119
|
+
seat: '1'
|
|
1120
|
+
})
|
|
1121
|
+
]
|
|
1122
|
+
await saveChanges();
|
|
1123
|
+
|
|
1124
|
+
const orderData = OrderData.create({
|
|
1125
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1126
|
+
checkoutMethod: takeoutMethod,
|
|
1127
|
+
timeSlot: slot1,
|
|
1128
|
+
cart: Cart.create({
|
|
1129
|
+
items: [
|
|
1130
|
+
CartItem.create({
|
|
1131
|
+
product: seatProduct,
|
|
1132
|
+
productPrice: seatProductPrice,
|
|
1133
|
+
amount: 2,
|
|
1134
|
+
seats: [
|
|
1135
|
+
CartReservedSeat.create({
|
|
1136
|
+
section: seatingPlan.sections[0].id,
|
|
1137
|
+
row: 'A',
|
|
1138
|
+
seat: '1'
|
|
1139
|
+
}),
|
|
1140
|
+
CartReservedSeat.create({
|
|
1141
|
+
section: seatingPlan.sections[0].id,
|
|
1142
|
+
row: 'A',
|
|
1143
|
+
seat: '2'
|
|
1144
|
+
})
|
|
1145
|
+
]
|
|
1146
|
+
})
|
|
1147
|
+
]
|
|
1148
|
+
}),
|
|
1149
|
+
customer
|
|
1150
|
+
})
|
|
1151
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1152
|
+
|
|
1153
|
+
const orderPatch = PrivateOrder.create({
|
|
1154
|
+
id: uuidv4(),
|
|
1155
|
+
data: orderData,
|
|
1156
|
+
webshopId: webshop.id
|
|
1157
|
+
});
|
|
1158
|
+
patchArray.addPut(orderPatch);
|
|
1159
|
+
|
|
1160
|
+
// Send a patch
|
|
1161
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1162
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1163
|
+
await expect(testServer.test(patchWebshopOrdersEndpoint, r)).rejects.toThrow('Seats unavailable');
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
test.todo("Admin cannot edit an an order to a reserved seat");
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
describe('Cleaning up stock', () => {
|
|
1170
|
+
test("Stock is returned when a payment failed", async () => {
|
|
1171
|
+
const orderData = OrderData.create({
|
|
1172
|
+
paymentMethod: PaymentMethod.Bancontact,
|
|
1173
|
+
checkoutMethod: onSiteMethod,
|
|
1174
|
+
timeSlot: slot4,
|
|
1175
|
+
cart: Cart.create({
|
|
1176
|
+
items: [
|
|
1177
|
+
CartItem.create({
|
|
1178
|
+
product,
|
|
1179
|
+
productPrice: productPrice2,
|
|
1180
|
+
amount: 5,
|
|
1181
|
+
options: [
|
|
1182
|
+
CartItemOption.create({
|
|
1183
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1184
|
+
option: checkboxOption1
|
|
1185
|
+
}),
|
|
1186
|
+
CartItemOption.create({
|
|
1187
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1188
|
+
option: checkboxOption2
|
|
1189
|
+
}),
|
|
1190
|
+
CartItemOption.create({
|
|
1191
|
+
optionMenu: chooseOneOptionMenu,
|
|
1192
|
+
option: radioOption2
|
|
1193
|
+
})
|
|
1194
|
+
]
|
|
1195
|
+
})
|
|
1196
|
+
]
|
|
1197
|
+
}),
|
|
1198
|
+
customer
|
|
1199
|
+
})
|
|
1200
|
+
|
|
1201
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1202
|
+
|
|
1203
|
+
const response = await testServer.test(endpoint, r);
|
|
1204
|
+
expect(response.body).toBeDefined();
|
|
1205
|
+
const order = response.body.order;
|
|
1206
|
+
|
|
1207
|
+
await checkStock(order.id, order.data.cart.items);
|
|
1208
|
+
|
|
1209
|
+
// Cancel the payment
|
|
1210
|
+
await stripeMocker.failPayment(stripeMocker.getLastIntent())
|
|
1211
|
+
|
|
1212
|
+
const updatedOrder = await checkStock(order.id, [], order.data.cart.items);
|
|
1213
|
+
expect(updatedOrder.status).toBe(OrderStatus.Deleted);
|
|
1214
|
+
});
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
describe('Modifying orders', () => {
|
|
1218
|
+
let order: Order;
|
|
1219
|
+
let baseOrder: Order;
|
|
1220
|
+
let productCartItem: CartItem|undefined;
|
|
1221
|
+
let personCartItem: CartItem|undefined;
|
|
1222
|
+
|
|
1223
|
+
beforeEach(async () => {
|
|
1224
|
+
productCartItem = CartItem.create({
|
|
1225
|
+
product,
|
|
1226
|
+
productPrice: productPrice1,
|
|
1227
|
+
amount: 5,
|
|
1228
|
+
options: [
|
|
1229
|
+
CartItemOption.create({
|
|
1230
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1231
|
+
option: checkboxOption1
|
|
1232
|
+
}),
|
|
1233
|
+
CartItemOption.create({
|
|
1234
|
+
optionMenu: chooseOneOptionMenu,
|
|
1235
|
+
option: radioOption1
|
|
1236
|
+
})
|
|
1237
|
+
]
|
|
1238
|
+
})
|
|
1239
|
+
|
|
1240
|
+
personCartItem = CartItem.create({
|
|
1241
|
+
product: personProduct,
|
|
1242
|
+
productPrice: personProductPrice,
|
|
1243
|
+
amount: 2
|
|
1244
|
+
})
|
|
1245
|
+
|
|
1246
|
+
{
|
|
1247
|
+
const orderData = OrderData.create({
|
|
1248
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1249
|
+
checkoutMethod: takeoutMethod,
|
|
1250
|
+
timeSlot: slot1,
|
|
1251
|
+
cart: Cart.create({
|
|
1252
|
+
items: [
|
|
1253
|
+
productCartItem,
|
|
1254
|
+
personCartItem
|
|
1255
|
+
]
|
|
1256
|
+
}),
|
|
1257
|
+
customer
|
|
1258
|
+
})
|
|
1259
|
+
|
|
1260
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1261
|
+
|
|
1262
|
+
const response = await testServer.test(endpoint, r);
|
|
1263
|
+
expect(response.body).toBeDefined();
|
|
1264
|
+
const orderStruct = response.body.order;
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
// Now check the stock has changed for the product
|
|
1268
|
+
order = await checkStock(orderStruct.id, [productCartItem, personCartItem]);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Make sure all items in the cart, options etc, have at least a usedStock of 1, to also test they don't decrease when making
|
|
1272
|
+
// changes to roders
|
|
1273
|
+
{
|
|
1274
|
+
const orderData = OrderData.create({
|
|
1275
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1276
|
+
checkoutMethod: takeoutMethod,
|
|
1277
|
+
timeSlot: slot1,
|
|
1278
|
+
cart: Cart.create({
|
|
1279
|
+
items: [
|
|
1280
|
+
CartItem.create({
|
|
1281
|
+
product,
|
|
1282
|
+
productPrice: productPrice1,
|
|
1283
|
+
amount: 1,
|
|
1284
|
+
options: [
|
|
1285
|
+
CartItemOption.create({
|
|
1286
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1287
|
+
option: checkboxOption1
|
|
1288
|
+
}),
|
|
1289
|
+
CartItemOption.create({
|
|
1290
|
+
optionMenu: chooseOneOptionMenu,
|
|
1291
|
+
option: radioOption1
|
|
1292
|
+
})
|
|
1293
|
+
]
|
|
1294
|
+
}),
|
|
1295
|
+
CartItem.create({
|
|
1296
|
+
product,
|
|
1297
|
+
productPrice: productPrice2,
|
|
1298
|
+
amount: 1,
|
|
1299
|
+
options: [
|
|
1300
|
+
CartItemOption.create({
|
|
1301
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1302
|
+
option: checkboxOption2
|
|
1303
|
+
}),
|
|
1304
|
+
CartItemOption.create({
|
|
1305
|
+
optionMenu: chooseOneOptionMenu,
|
|
1306
|
+
option: radioOption2
|
|
1307
|
+
})
|
|
1308
|
+
]
|
|
1309
|
+
}),
|
|
1310
|
+
CartItem.create({
|
|
1311
|
+
product: personProduct,
|
|
1312
|
+
productPrice: personProductPrice,
|
|
1313
|
+
amount: 1
|
|
1314
|
+
})
|
|
1315
|
+
]
|
|
1316
|
+
}),
|
|
1317
|
+
customer
|
|
1318
|
+
})
|
|
1319
|
+
|
|
1320
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1321
|
+
|
|
1322
|
+
const response = await testServer.test(endpoint, r);
|
|
1323
|
+
expect(response.body).toBeDefined();
|
|
1324
|
+
const orderStruct = response.body.order;
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
// Now check the stock has changed for the product
|
|
1328
|
+
const orders = await checkStocks([order.id, orderStruct.id], [productCartItem, personCartItem, ...orderStruct.data.cart.items]);
|
|
1329
|
+
baseOrder = orders.find(o => o.id === orderStruct.id)!
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
test("Stock is removed when a product is removed or added in two steps", async () => {
|
|
1334
|
+
{
|
|
1335
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1336
|
+
|
|
1337
|
+
const cartPatch = Cart.patch({})
|
|
1338
|
+
cartPatch.items.addDelete(productCartItem!.id)
|
|
1339
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1340
|
+
patchArray.addPatch(orderPatch);
|
|
1341
|
+
|
|
1342
|
+
// Send a patch
|
|
1343
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1344
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1345
|
+
|
|
1346
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1347
|
+
|
|
1348
|
+
await checkStock(order.id, [personCartItem!]);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
{
|
|
1352
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1353
|
+
|
|
1354
|
+
const cartPatch = Cart.patch({})
|
|
1355
|
+
cartPatch.items.addDelete(personCartItem!.id)
|
|
1356
|
+
const newItem = CartItem.create({
|
|
1357
|
+
product,
|
|
1358
|
+
productPrice: productPrice2,
|
|
1359
|
+
amount: 30,
|
|
1360
|
+
options: [
|
|
1361
|
+
CartItemOption.create({
|
|
1362
|
+
optionMenu: chooseOneOptionMenu,
|
|
1363
|
+
option: radioOption2
|
|
1364
|
+
})
|
|
1365
|
+
]
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
cartPatch.items.addPut(
|
|
1369
|
+
newItem
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1373
|
+
patchArray.addPatch(orderPatch);
|
|
1374
|
+
|
|
1375
|
+
// Send a patch
|
|
1376
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1377
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1378
|
+
|
|
1379
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1380
|
+
|
|
1381
|
+
await checkStock(order.id, [newItem]);
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
test("Stock is removed when a product is removed or added in single step", async () => {
|
|
1386
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1387
|
+
|
|
1388
|
+
const cartPatch = Cart.patch({})
|
|
1389
|
+
cartPatch.items.addDelete(productCartItem!.id)
|
|
1390
|
+
cartPatch.items.addDelete(personCartItem!.id)
|
|
1391
|
+
cartPatch.items.addPut(personCartItem!)
|
|
1392
|
+
|
|
1393
|
+
const newItem = CartItem.create({
|
|
1394
|
+
product,
|
|
1395
|
+
productPrice: productPrice2,
|
|
1396
|
+
amount: 40,
|
|
1397
|
+
options: [
|
|
1398
|
+
CartItemOption.create({
|
|
1399
|
+
optionMenu: chooseOneOptionMenu,
|
|
1400
|
+
option: radioOption2
|
|
1401
|
+
})
|
|
1402
|
+
]
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
cartPatch.items.addPut(
|
|
1406
|
+
newItem
|
|
1407
|
+
)
|
|
1408
|
+
|
|
1409
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1410
|
+
patchArray.addPatch(orderPatch);
|
|
1411
|
+
|
|
1412
|
+
// Send a patch
|
|
1413
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1414
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1415
|
+
|
|
1416
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1417
|
+
|
|
1418
|
+
order = await checkStock(order.id, [personCartItem!, newItem]);
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
test("Stock is adjusted if product amount is changed", async () => {
|
|
1422
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1423
|
+
|
|
1424
|
+
const cartPatch = Cart.patch({})
|
|
1425
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1426
|
+
id: productCartItem!.id,
|
|
1427
|
+
amount: 6
|
|
1428
|
+
}))
|
|
1429
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1430
|
+
id: personCartItem!.id,
|
|
1431
|
+
amount: 13
|
|
1432
|
+
}))
|
|
1433
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1434
|
+
patchArray.addPatch(orderPatch);
|
|
1435
|
+
|
|
1436
|
+
// Send a patch
|
|
1437
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1438
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1439
|
+
|
|
1440
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1441
|
+
|
|
1442
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
test("Stock is changed if timeslot is changed", async () => {
|
|
1446
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1447
|
+
|
|
1448
|
+
const orderPatch = PrivateOrder.patch({
|
|
1449
|
+
id: order.id,
|
|
1450
|
+
data: OrderData.patch({
|
|
1451
|
+
timeSlot: slot2
|
|
1452
|
+
})
|
|
1453
|
+
});
|
|
1454
|
+
patchArray.addPatch(orderPatch);
|
|
1455
|
+
|
|
1456
|
+
// Send a patch
|
|
1457
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1458
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1459
|
+
|
|
1460
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1461
|
+
|
|
1462
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
test('Stock is changed if productPrice is changed', async () => {
|
|
1466
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1467
|
+
|
|
1468
|
+
const cartPatch = Cart.patch({})
|
|
1469
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1470
|
+
id: productCartItem!.id,
|
|
1471
|
+
productPrice: productPrice2
|
|
1472
|
+
}))
|
|
1473
|
+
|
|
1474
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1475
|
+
patchArray.addPatch(orderPatch);
|
|
1476
|
+
|
|
1477
|
+
// Send a patch
|
|
1478
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1479
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1480
|
+
|
|
1481
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1482
|
+
|
|
1483
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
test('Stock is changed when option is removed', async () => {
|
|
1487
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1488
|
+
|
|
1489
|
+
const cartPatch = Cart.patch({})
|
|
1490
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1491
|
+
id: productCartItem!.id,
|
|
1492
|
+
options: [
|
|
1493
|
+
CartItemOption.create({
|
|
1494
|
+
optionMenu: chooseOneOptionMenu,
|
|
1495
|
+
option: radioOption1
|
|
1496
|
+
})
|
|
1497
|
+
]
|
|
1498
|
+
}))
|
|
1499
|
+
|
|
1500
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1501
|
+
patchArray.addPatch(orderPatch);
|
|
1502
|
+
|
|
1503
|
+
// Send a patch
|
|
1504
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1505
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1506
|
+
|
|
1507
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1508
|
+
|
|
1509
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1512
|
+
test('Stock is changed when option is changed', async () => {
|
|
1513
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1514
|
+
|
|
1515
|
+
const cartPatch = Cart.patch({})
|
|
1516
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1517
|
+
id: productCartItem!.id,
|
|
1518
|
+
options: [
|
|
1519
|
+
CartItemOption.create({
|
|
1520
|
+
optionMenu: chooseOneOptionMenu,
|
|
1521
|
+
option: radioOption2
|
|
1522
|
+
}),
|
|
1523
|
+
CartItemOption.create({
|
|
1524
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1525
|
+
option: checkboxOption2
|
|
1526
|
+
})
|
|
1527
|
+
]
|
|
1528
|
+
}))
|
|
1529
|
+
|
|
1530
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1531
|
+
patchArray.addPatch(orderPatch);
|
|
1532
|
+
|
|
1533
|
+
// Send a patch
|
|
1534
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1535
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1536
|
+
|
|
1537
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1538
|
+
|
|
1539
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
test('Stock is changed when option is added', async () => {
|
|
1543
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1544
|
+
|
|
1545
|
+
const cartPatch = Cart.patch({})
|
|
1546
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1547
|
+
id: productCartItem!.id,
|
|
1548
|
+
options: [
|
|
1549
|
+
CartItemOption.create({
|
|
1550
|
+
optionMenu: chooseOneOptionMenu,
|
|
1551
|
+
option: radioOption1
|
|
1552
|
+
}),
|
|
1553
|
+
CartItemOption.create({
|
|
1554
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1555
|
+
option: checkboxOption1
|
|
1556
|
+
}),
|
|
1557
|
+
CartItemOption.create({
|
|
1558
|
+
optionMenu: multipleChoiceOptionMenu,
|
|
1559
|
+
option: checkboxOption2
|
|
1560
|
+
})
|
|
1561
|
+
]
|
|
1562
|
+
}))
|
|
1563
|
+
|
|
1564
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1565
|
+
patchArray.addPatch(orderPatch);
|
|
1566
|
+
|
|
1567
|
+
// Send a patch
|
|
1568
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1569
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1570
|
+
|
|
1571
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1572
|
+
|
|
1573
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
test("Stock is changed if delivery method is changed", async () => {
|
|
1577
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1578
|
+
|
|
1579
|
+
const orderPatch = PrivateOrder.patch({
|
|
1580
|
+
id: order.id,
|
|
1581
|
+
data: OrderData.patch({
|
|
1582
|
+
checkoutMethod: deliveryMethod,
|
|
1583
|
+
timeSlot: slot3,
|
|
1584
|
+
address: ValidatedAddress.create({
|
|
1585
|
+
...address,
|
|
1586
|
+
cityId: '',
|
|
1587
|
+
parentCityId: null,
|
|
1588
|
+
provinceId: ''
|
|
1589
|
+
})
|
|
1590
|
+
})
|
|
1591
|
+
});
|
|
1592
|
+
patchArray.addPatch(orderPatch);
|
|
1593
|
+
|
|
1594
|
+
// Send a patch
|
|
1595
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1596
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1597
|
+
|
|
1598
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1599
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
test("Stock is returned when an order is canceled and added when uncanceled again", async () => {
|
|
1603
|
+
{
|
|
1604
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1605
|
+
|
|
1606
|
+
const orderPatch = PrivateOrder.patch({
|
|
1607
|
+
id: order.id,
|
|
1608
|
+
status: OrderStatus.Canceled
|
|
1609
|
+
});
|
|
1610
|
+
patchArray.addPatch(orderPatch);
|
|
1611
|
+
|
|
1612
|
+
// Send a patch
|
|
1613
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1614
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1615
|
+
|
|
1616
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1617
|
+
|
|
1618
|
+
await checkStock(order.id, [], [personCartItem!, productCartItem!]);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Uncancel
|
|
1622
|
+
{
|
|
1623
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1624
|
+
|
|
1625
|
+
const orderPatch = PrivateOrder.patch({
|
|
1626
|
+
id: order.id,
|
|
1627
|
+
status: OrderStatus.Created
|
|
1628
|
+
});
|
|
1629
|
+
patchArray.addPatch(orderPatch);
|
|
1630
|
+
|
|
1631
|
+
// Send a patch
|
|
1632
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1633
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1634
|
+
|
|
1635
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1636
|
+
|
|
1637
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1638
|
+
}
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
test("Stock is returned when an order is deleted and added when undeleted", async () => {
|
|
1642
|
+
{
|
|
1643
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1644
|
+
|
|
1645
|
+
const orderPatch = PrivateOrder.patch({
|
|
1646
|
+
id: order.id,
|
|
1647
|
+
status: OrderStatus.Deleted
|
|
1648
|
+
});
|
|
1649
|
+
patchArray.addPatch(orderPatch);
|
|
1650
|
+
|
|
1651
|
+
// Send a patch
|
|
1652
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1653
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1654
|
+
|
|
1655
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1656
|
+
|
|
1657
|
+
await checkStock(order.id, [], [personCartItem!, productCartItem!]);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// Undelete
|
|
1661
|
+
{
|
|
1662
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1663
|
+
|
|
1664
|
+
const orderPatch = PrivateOrder.patch({
|
|
1665
|
+
id: order.id,
|
|
1666
|
+
status: OrderStatus.Created
|
|
1667
|
+
});
|
|
1668
|
+
patchArray.addPatch(orderPatch);
|
|
1669
|
+
|
|
1670
|
+
// Send a patch
|
|
1671
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1672
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1673
|
+
|
|
1674
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1675
|
+
|
|
1676
|
+
await checkStock(order.id, [personCartItem!, productCartItem!]);
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
describe('Modifying seat orders', () => {
|
|
1682
|
+
let order: Order;
|
|
1683
|
+
let seatCartItem: CartItem|undefined;
|
|
1684
|
+
|
|
1685
|
+
beforeEach(async () => {
|
|
1686
|
+
seatCartItem = CartItem.create({
|
|
1687
|
+
product: seatProduct,
|
|
1688
|
+
productPrice: seatProductPrice,
|
|
1689
|
+
amount: 2,
|
|
1690
|
+
seats: [
|
|
1691
|
+
CartReservedSeat.create({
|
|
1692
|
+
section: seatingPlan.sections[0].id,
|
|
1693
|
+
row: 'A',
|
|
1694
|
+
seat: '1'
|
|
1695
|
+
}),
|
|
1696
|
+
CartReservedSeat.create({
|
|
1697
|
+
section: seatingPlan.sections[0].id,
|
|
1698
|
+
row: 'A',
|
|
1699
|
+
seat: '2'
|
|
1700
|
+
})
|
|
1701
|
+
]
|
|
1702
|
+
})
|
|
1703
|
+
|
|
1704
|
+
const orderData = OrderData.create({
|
|
1705
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1706
|
+
checkoutMethod: takeoutMethod,
|
|
1707
|
+
timeSlot: slot1,
|
|
1708
|
+
cart: Cart.create({
|
|
1709
|
+
items: [
|
|
1710
|
+
seatCartItem
|
|
1711
|
+
]
|
|
1712
|
+
}),
|
|
1713
|
+
customer
|
|
1714
|
+
})
|
|
1715
|
+
|
|
1716
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1717
|
+
|
|
1718
|
+
const response = await testServer.test(endpoint, r);
|
|
1719
|
+
expect(response.body).toBeDefined();
|
|
1720
|
+
const orderStruct = response.body.order;
|
|
1721
|
+
|
|
1722
|
+
// Now check the stock has changed for the product
|
|
1723
|
+
order = await checkStock(orderStruct.id, [seatCartItem]);
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
test("Stock is removed when a product is removed and another is added", async () => {
|
|
1727
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1728
|
+
|
|
1729
|
+
const cartPatch = Cart.patch({})
|
|
1730
|
+
cartPatch.items.addDelete(seatCartItem!.id)
|
|
1731
|
+
|
|
1732
|
+
const newItem = CartItem.create({
|
|
1733
|
+
product: seatProduct,
|
|
1734
|
+
productPrice: seatProductPrice,
|
|
1735
|
+
amount: 1,
|
|
1736
|
+
seats: [
|
|
1737
|
+
CartReservedSeat.create({
|
|
1738
|
+
section: seatingPlan.sections[0].id,
|
|
1739
|
+
row: 'B',
|
|
1740
|
+
seat: '1'
|
|
1741
|
+
})
|
|
1742
|
+
]
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
cartPatch.items.addPut(
|
|
1746
|
+
newItem
|
|
1747
|
+
)
|
|
1748
|
+
|
|
1749
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1750
|
+
patchArray.addPatch(orderPatch);
|
|
1751
|
+
|
|
1752
|
+
// Send a patch
|
|
1753
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1754
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1755
|
+
|
|
1756
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1757
|
+
|
|
1758
|
+
await checkStock(order.id, [newItem]);
|
|
1759
|
+
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
test("Stock is adjusted if extra seat is selected", async () => {
|
|
1763
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1764
|
+
|
|
1765
|
+
const cartPatch = Cart.patch({})
|
|
1766
|
+
const c = CartItem.patch({
|
|
1767
|
+
id: seatCartItem!.id,
|
|
1768
|
+
amount: 3,
|
|
1769
|
+
seats: [
|
|
1770
|
+
CartReservedSeat.create({
|
|
1771
|
+
section: seatingPlan.sections[0].id,
|
|
1772
|
+
row: 'A',
|
|
1773
|
+
seat: '1'
|
|
1774
|
+
}),
|
|
1775
|
+
CartReservedSeat.create({
|
|
1776
|
+
section: seatingPlan.sections[0].id,
|
|
1777
|
+
row: 'A',
|
|
1778
|
+
seat: '2'
|
|
1779
|
+
}),
|
|
1780
|
+
CartReservedSeat.create({
|
|
1781
|
+
section: seatingPlan.sections[0].id,
|
|
1782
|
+
row: 'A',
|
|
1783
|
+
seat: '3'
|
|
1784
|
+
})
|
|
1785
|
+
]
|
|
1786
|
+
});
|
|
1787
|
+
cartPatch.items.addPatch(c)
|
|
1788
|
+
|
|
1789
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1790
|
+
patchArray.addPatch(orderPatch);
|
|
1791
|
+
|
|
1792
|
+
// Send a patch
|
|
1793
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1794
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1795
|
+
|
|
1796
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1797
|
+
|
|
1798
|
+
await checkStock(order.id, [seatCartItem!]);
|
|
1799
|
+
});
|
|
1800
|
+
|
|
1801
|
+
test("Reserved seats are changed when seats are moved", async () => {
|
|
1802
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1803
|
+
|
|
1804
|
+
const cartPatch = Cart.patch({})
|
|
1805
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
1806
|
+
id: seatCartItem?.id,
|
|
1807
|
+
seats: [
|
|
1808
|
+
CartReservedSeat.create({
|
|
1809
|
+
section: seatingPlan.sections[0].id,
|
|
1810
|
+
row: 'B',
|
|
1811
|
+
seat: '1'
|
|
1812
|
+
}),
|
|
1813
|
+
CartReservedSeat.create({
|
|
1814
|
+
section: seatingPlan.sections[0].id,
|
|
1815
|
+
row: 'B',
|
|
1816
|
+
seat: '2'
|
|
1817
|
+
})
|
|
1818
|
+
]
|
|
1819
|
+
}))
|
|
1820
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
1821
|
+
patchArray.addPatch(orderPatch);
|
|
1822
|
+
|
|
1823
|
+
// Send a patch
|
|
1824
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1825
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1826
|
+
|
|
1827
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1828
|
+
|
|
1829
|
+
await checkStock(order.id, [seatCartItem!]);
|
|
1830
|
+
});
|
|
1831
|
+
|
|
1832
|
+
test("Stock is returned when an order is canceled and added when uncanceled again", async () => {
|
|
1833
|
+
{
|
|
1834
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1835
|
+
|
|
1836
|
+
const orderPatch = PrivateOrder.patch({
|
|
1837
|
+
id: order.id,
|
|
1838
|
+
status: OrderStatus.Canceled
|
|
1839
|
+
});
|
|
1840
|
+
patchArray.addPatch(orderPatch);
|
|
1841
|
+
|
|
1842
|
+
// Send a patch
|
|
1843
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1844
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1845
|
+
|
|
1846
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1847
|
+
|
|
1848
|
+
await checkStock(order.id, [], [seatCartItem!]);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// Uncancel
|
|
1852
|
+
{
|
|
1853
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1854
|
+
|
|
1855
|
+
const orderPatch = PrivateOrder.patch({
|
|
1856
|
+
id: order.id,
|
|
1857
|
+
status: OrderStatus.Created
|
|
1858
|
+
});
|
|
1859
|
+
patchArray.addPatch(orderPatch);
|
|
1860
|
+
|
|
1861
|
+
// Send a patch
|
|
1862
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1863
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1864
|
+
|
|
1865
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1866
|
+
|
|
1867
|
+
await checkStock(order.id, [seatCartItem!]);
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
|
|
1871
|
+
test("Stock is returned when an order is deleted and added when undeleted", async () => {
|
|
1872
|
+
{
|
|
1873
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1874
|
+
|
|
1875
|
+
const orderPatch = PrivateOrder.patch({
|
|
1876
|
+
id: order.id,
|
|
1877
|
+
status: OrderStatus.Deleted
|
|
1878
|
+
});
|
|
1879
|
+
patchArray.addPatch(orderPatch);
|
|
1880
|
+
|
|
1881
|
+
// Send a patch
|
|
1882
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1883
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1884
|
+
|
|
1885
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1886
|
+
|
|
1887
|
+
await checkStock(order.id, [], [seatCartItem!]);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// Undelete
|
|
1891
|
+
{
|
|
1892
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1893
|
+
|
|
1894
|
+
const orderPatch = PrivateOrder.patch({
|
|
1895
|
+
id: order.id,
|
|
1896
|
+
status: OrderStatus.Created
|
|
1897
|
+
});
|
|
1898
|
+
patchArray.addPatch(orderPatch);
|
|
1899
|
+
|
|
1900
|
+
// Send a patch
|
|
1901
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1902
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1903
|
+
|
|
1904
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1905
|
+
|
|
1906
|
+
await checkStock(order.id, [seatCartItem!]);
|
|
1907
|
+
}
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
test("Correctly handles duplicate seat booking recovery", async () => {
|
|
1911
|
+
{
|
|
1912
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1913
|
+
|
|
1914
|
+
const orderPatch = PrivateOrder.patch({
|
|
1915
|
+
id: order.id,
|
|
1916
|
+
status: OrderStatus.Canceled
|
|
1917
|
+
});
|
|
1918
|
+
patchArray.addPatch(orderPatch);
|
|
1919
|
+
|
|
1920
|
+
// Send a patch
|
|
1921
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1922
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1923
|
+
|
|
1924
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1925
|
+
|
|
1926
|
+
await checkStock(order.id, [], [seatCartItem!]);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// Place an order for the same seats
|
|
1930
|
+
const newItem = CartItem.create({
|
|
1931
|
+
product: seatProduct,
|
|
1932
|
+
productPrice: seatProductPrice,
|
|
1933
|
+
amount: 2,
|
|
1934
|
+
seats: [
|
|
1935
|
+
CartReservedSeat.create({
|
|
1936
|
+
section: seatingPlan.sections[0].id,
|
|
1937
|
+
row: 'A',
|
|
1938
|
+
seat: '1'
|
|
1939
|
+
}),
|
|
1940
|
+
CartReservedSeat.create({
|
|
1941
|
+
section: seatingPlan.sections[0].id,
|
|
1942
|
+
row: 'A',
|
|
1943
|
+
seat: '2'
|
|
1944
|
+
})
|
|
1945
|
+
]
|
|
1946
|
+
})
|
|
1947
|
+
|
|
1948
|
+
const orderData = OrderData.create({
|
|
1949
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1950
|
+
checkoutMethod: takeoutMethod,
|
|
1951
|
+
timeSlot: slot1,
|
|
1952
|
+
cart: Cart.create({
|
|
1953
|
+
items: [
|
|
1954
|
+
newItem
|
|
1955
|
+
]
|
|
1956
|
+
}),
|
|
1957
|
+
customer
|
|
1958
|
+
})
|
|
1959
|
+
let orders: Order[];
|
|
1960
|
+
|
|
1961
|
+
{
|
|
1962
|
+
const r = Request.buildJson("POST", `/webshop/${webshop.id}/order`, organization.getApiHost(), orderData);
|
|
1963
|
+
|
|
1964
|
+
const response = await testServer.test(endpoint, r);
|
|
1965
|
+
expect(response.body).toBeDefined();
|
|
1966
|
+
const orderStruct = response.body.order;
|
|
1967
|
+
|
|
1968
|
+
// Now check the stock has changed for the product
|
|
1969
|
+
orders = await checkStocks([order.id, orderStruct.id], [newItem], [seatCartItem!]);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// Uncancel
|
|
1973
|
+
{
|
|
1974
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
1975
|
+
|
|
1976
|
+
const orderPatch = PrivateOrder.patch({
|
|
1977
|
+
id: order.id,
|
|
1978
|
+
status: OrderStatus.Created
|
|
1979
|
+
});
|
|
1980
|
+
patchArray.addPatch(orderPatch);
|
|
1981
|
+
|
|
1982
|
+
// Send a patch
|
|
1983
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
1984
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
1985
|
+
|
|
1986
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
1987
|
+
|
|
1988
|
+
orders = await checkStocks(orders.map(o => o.id), [newItem, seatCartItem!]);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// Now we are in a duplicate seat selected situation.
|
|
1992
|
+
// To recover, move seats of one of the orders
|
|
1993
|
+
// and check in the final result, all seats are still correctly reserved (once)
|
|
1994
|
+
|
|
1995
|
+
// Manual check
|
|
1996
|
+
expect(seatProduct.reservedSeats).toHaveLength(4);
|
|
1997
|
+
expect(seatProduct.reservedSeats).toIncludeSameMembers([
|
|
1998
|
+
ReservedSeat.create({
|
|
1999
|
+
section: seatingPlan.sections[0].id,
|
|
2000
|
+
row: 'A',
|
|
2001
|
+
seat: '1'
|
|
2002
|
+
}),
|
|
2003
|
+
ReservedSeat.create({
|
|
2004
|
+
section: seatingPlan.sections[0].id,
|
|
2005
|
+
row: 'A',
|
|
2006
|
+
seat: '1'
|
|
2007
|
+
}),
|
|
2008
|
+
ReservedSeat.create({
|
|
2009
|
+
section: seatingPlan.sections[0].id,
|
|
2010
|
+
row: 'A',
|
|
2011
|
+
seat: '2'
|
|
2012
|
+
}),
|
|
2013
|
+
ReservedSeat.create({
|
|
2014
|
+
section: seatingPlan.sections[0].id,
|
|
2015
|
+
row: 'A',
|
|
2016
|
+
seat: '2'
|
|
2017
|
+
}),
|
|
2018
|
+
])
|
|
2019
|
+
|
|
2020
|
+
// Move seats of first order
|
|
2021
|
+
{
|
|
2022
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
2023
|
+
|
|
2024
|
+
const cartPatch = Cart.patch({})
|
|
2025
|
+
cartPatch.items.addPatch(CartItem.patch({
|
|
2026
|
+
id: seatCartItem?.id,
|
|
2027
|
+
seats: [
|
|
2028
|
+
CartReservedSeat.create({
|
|
2029
|
+
section: seatingPlan.sections[0].id,
|
|
2030
|
+
row: 'B',
|
|
2031
|
+
seat: '1'
|
|
2032
|
+
}),
|
|
2033
|
+
CartReservedSeat.create({
|
|
2034
|
+
section: seatingPlan.sections[0].id,
|
|
2035
|
+
row: 'B',
|
|
2036
|
+
seat: '2'
|
|
2037
|
+
})
|
|
2038
|
+
]
|
|
2039
|
+
}))
|
|
2040
|
+
const orderPatch = PrivateOrder.patch({id: order.id, data: OrderData.patch({cart: cartPatch})});
|
|
2041
|
+
patchArray.addPatch(orderPatch);
|
|
2042
|
+
|
|
2043
|
+
// Send a patch
|
|
2044
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
2045
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
2046
|
+
|
|
2047
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
2048
|
+
|
|
2049
|
+
await checkStocks(orders.map(o => o.id), [newItem, seatCartItem!]);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
// Manual check
|
|
2053
|
+
expect(seatProduct.reservedSeats).toHaveLength(4);
|
|
2054
|
+
expect(seatProduct.reservedSeats).toIncludeSameMembers([
|
|
2055
|
+
ReservedSeat.create({
|
|
2056
|
+
section: seatingPlan.sections[0].id,
|
|
2057
|
+
row: 'A',
|
|
2058
|
+
seat: '1'
|
|
2059
|
+
}),
|
|
2060
|
+
ReservedSeat.create({
|
|
2061
|
+
section: seatingPlan.sections[0].id,
|
|
2062
|
+
row: 'B',
|
|
2063
|
+
seat: '1'
|
|
2064
|
+
}),
|
|
2065
|
+
ReservedSeat.create({
|
|
2066
|
+
section: seatingPlan.sections[0].id,
|
|
2067
|
+
row: 'A',
|
|
2068
|
+
seat: '2'
|
|
2069
|
+
}),
|
|
2070
|
+
ReservedSeat.create({
|
|
2071
|
+
section: seatingPlan.sections[0].id,
|
|
2072
|
+
row: 'B',
|
|
2073
|
+
seat: '2'
|
|
2074
|
+
}),
|
|
2075
|
+
])
|
|
2076
|
+
});
|
|
2077
|
+
|
|
2078
|
+
test("Patching an order triggers an auto recovery of not reserved seats", async () => {
|
|
2079
|
+
// This is required to recover from bugs in stock changes.
|
|
2080
|
+
// Simply patching all orders will fix the stock.
|
|
2081
|
+
|
|
2082
|
+
// Manually remove all reserved seats = caused by a past bug
|
|
2083
|
+
seatProduct.reservedSeats = []
|
|
2084
|
+
await saveChanges();
|
|
2085
|
+
|
|
2086
|
+
{
|
|
2087
|
+
const patchArray: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
|
|
2088
|
+
|
|
2089
|
+
const orderPatch = PrivateOrder.patch({
|
|
2090
|
+
id: order.id,
|
|
2091
|
+
status: OrderStatus.Completed
|
|
2092
|
+
});
|
|
2093
|
+
patchArray.addPatch(orderPatch);
|
|
2094
|
+
|
|
2095
|
+
// Send a patch
|
|
2096
|
+
const r = Request.buildJson("PATCH", `/webshop/${webshop.id}/orders`, organization.getApiHost(), patchArray);
|
|
2097
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
2098
|
+
|
|
2099
|
+
await testServer.test(patchWebshopOrdersEndpoint, r);
|
|
2100
|
+
|
|
2101
|
+
await checkStock(order.id, [seatCartItem!]);
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// Manual check
|
|
2105
|
+
expect(seatProduct.reservedSeats).toHaveLength(2);
|
|
2106
|
+
expect(seatProduct.reservedSeats).toIncludeSameMembers([
|
|
2107
|
+
ReservedSeat.create({
|
|
2108
|
+
section: seatingPlan.sections[0].id,
|
|
2109
|
+
row: 'A',
|
|
2110
|
+
seat: '1'
|
|
2111
|
+
}),
|
|
2112
|
+
ReservedSeat.create({
|
|
2113
|
+
section: seatingPlan.sections[0].id,
|
|
2114
|
+
row: 'A',
|
|
2115
|
+
seat: '2'
|
|
2116
|
+
}),
|
|
2117
|
+
])
|
|
2118
|
+
});
|
|
2119
|
+
});
|
|
2120
|
+
});
|