@labdigital/commercetools-mock 2.46.0 → 2.47.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/dist/index.cjs +568 -241
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +782 -58
- package/dist/index.d.ts +782 -58
- package/dist/index.js +556 -229
- package/dist/index.js.map +1 -1
- package/package.json +41 -48
- package/src/ctMock.ts +11 -13
- package/src/index.test.ts +5 -5
- package/src/lib/predicateParser.test.ts +62 -62
- package/src/lib/predicateParser.ts +32 -42
- package/src/lib/productSearchFilter.test.ts +18 -0
- package/src/lib/productSearchFilter.ts +7 -0
- package/src/lib/projectionSearchFilter.test.ts +17 -17
- package/src/lib/projectionSearchFilter.ts +2 -3
- package/src/oauth/server.test.ts +1 -1
- package/src/oauth/server.ts +11 -11
- package/src/priceSelector.ts +1 -1
- package/src/product-projection-search.ts +18 -19
- package/src/repositories/business-unit.ts +17 -16
- package/src/repositories/cart/actions.ts +32 -32
- package/src/repositories/cart/helpers.ts +1 -1
- package/src/repositories/cart/index.ts +8 -8
- package/src/repositories/cart-discount/actions.ts +1 -4
- package/src/repositories/category/actions.ts +2 -6
- package/src/repositories/custom-object.ts +20 -21
- package/src/repositories/customer/actions.ts +4 -4
- package/src/repositories/errors.ts +1 -1
- package/src/repositories/extension.ts +2 -1
- package/src/repositories/helpers.ts +27 -27
- package/src/repositories/index.ts +17 -17
- package/src/repositories/my-customer.ts +1 -1
- package/src/repositories/my-order.ts +2 -2
- package/src/repositories/order/index.ts +1 -1
- package/src/repositories/product/actions.ts +1 -1
- package/src/repositories/quote/actions.ts +83 -0
- package/src/repositories/quote/index.ts +54 -0
- package/src/repositories/quote-request/actions.ts +84 -0
- package/src/repositories/quote-request/index.test.ts +167 -0
- package/src/repositories/quote-request/index.ts +67 -0
- package/src/repositories/quote-staged/actions.ts +84 -0
- package/src/repositories/quote-staged/index.ts +47 -0
- package/src/repositories/review.ts +4 -4
- package/src/repositories/shipping-method/actions.ts +17 -17
- package/src/repositories/shipping-method/index.ts +6 -6
- package/src/repositories/shopping-list/actions.ts +1 -1
- package/src/repositories/shopping-list/index.ts +9 -1
- package/src/repositories/subscription.ts +2 -4
- package/src/server.ts +3 -2
- package/src/services/abstract.ts +7 -7
- package/src/services/as-associate-order.test.ts +1 -1
- package/src/services/cart-discount.test.ts +1 -1
- package/src/services/cart.test.ts +15 -15
- package/src/services/category.test.ts +1 -1
- package/src/services/customer.test.ts +4 -4
- package/src/services/customer.ts +1 -1
- package/src/services/index.ts +20 -14
- package/src/services/inventory-entry.test.ts +5 -5
- package/src/services/my-cart.test.ts +2 -2
- package/src/services/my-customer.test.ts +2 -2
- package/src/services/order.test.ts +8 -8
- package/src/services/product-projection.test.ts +5 -5
- package/src/services/product-projection.ts +12 -14
- package/src/services/product.test.ts +1 -1
- package/src/services/quote-request.test.ts +59 -0
- package/src/services/quote-request.ts +16 -0
- package/src/services/quote-staged.ts +16 -0
- package/src/services/quote.ts +16 -0
- package/src/services/standalone-price.test.ts +4 -4
- package/src/services/state.test.ts +1 -1
- package/src/services/store.test.ts +2 -2
- package/src/services/tax-category.test.ts +1 -1
- package/src/shipping.ts +3 -3
- package/src/storage/in-memory.ts +55 -63
- package/src/testing/customer.ts +1 -1
- package/src/types.ts +51 -31
- package/src/repositories/quote-request.ts +0 -17
- package/src/repositories/quote.ts +0 -14
- package/src/repositories/staged-quote.ts +0 -17
|
@@ -30,10 +30,10 @@ import { ProductTypeRepository } from "./product-type";
|
|
|
30
30
|
import { ProjectRepository } from "./project";
|
|
31
31
|
import { QuoteRepository } from "./quote";
|
|
32
32
|
import { QuoteRequestRepository } from "./quote-request";
|
|
33
|
+
import { StagedQuoteRepository } from "./quote-staged";
|
|
33
34
|
import { ReviewRepository } from "./review";
|
|
34
35
|
import { ShippingMethodRepository } from "./shipping-method";
|
|
35
36
|
import { ShoppingListRepository } from "./shopping-list";
|
|
36
|
-
import { StagedQuoteRepository } from "./staged-quote";
|
|
37
37
|
import { StandAlonePriceRepository } from "./standalone-price";
|
|
38
38
|
import { StateRepository } from "./state";
|
|
39
39
|
import { StoreRepository } from "./store";
|
|
@@ -52,42 +52,42 @@ export const createRepositories = (config: Config) => ({
|
|
|
52
52
|
"associate-role": new AssociateRoleRepository(config),
|
|
53
53
|
"attribute-group": new AttributeGroupRepository(config),
|
|
54
54
|
"business-unit": new BusinessUnitRepository(config),
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
category: new CategoryRepository(config),
|
|
56
|
+
cart: new CartRepository(config),
|
|
57
57
|
"cart-discount": new CartDiscountRepository(config),
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
customer: new CustomerRepository(config),
|
|
59
|
+
channel: new ChannelRepository(config),
|
|
60
60
|
"customer-group": new CustomerGroupRepository(config),
|
|
61
61
|
"discount-code": new DiscountCodeRepository(config),
|
|
62
|
-
|
|
62
|
+
extension: new ExtensionRepository(config),
|
|
63
63
|
"inventory-entry": new InventoryEntryRepository(config),
|
|
64
64
|
"key-value-document": new CustomObjectRepository(config),
|
|
65
|
-
|
|
65
|
+
order: new OrderRepository(config),
|
|
66
66
|
"order-edit": new OrderEditRepository(config),
|
|
67
|
-
|
|
67
|
+
payment: new PaymentRepository(config),
|
|
68
68
|
"my-cart": new CartRepository(config),
|
|
69
69
|
"my-order": new MyOrderRepository(config),
|
|
70
70
|
"my-customer": new MyCustomerRepository(config),
|
|
71
71
|
"my-payment": new PaymentRepository(config),
|
|
72
72
|
"my-shopping-list": new ShoppingListRepository(config),
|
|
73
|
-
|
|
73
|
+
product: new ProductRepository(config),
|
|
74
74
|
"product-type": new ProductTypeRepository(config),
|
|
75
75
|
"product-discount": new ProductDiscountRepository(config),
|
|
76
76
|
"product-projection": new ProductProjectionRepository(config),
|
|
77
77
|
"product-selection": new ProductSelectionRepository(config),
|
|
78
78
|
"product-tailoring": new ProductTailoringRepository(config),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
project: new ProjectRepository(config),
|
|
80
|
+
review: new ReviewRepository(config),
|
|
81
|
+
quote: new QuoteRepository(config),
|
|
82
82
|
"quote-request": new QuoteRequestRepository(config),
|
|
83
83
|
"shipping-method": new ShippingMethodRepository(config),
|
|
84
84
|
"shopping-list": new ShoppingListRepository(config),
|
|
85
85
|
"staged-quote": new StagedQuoteRepository(config),
|
|
86
86
|
"standalone-price": new StandAlonePriceRepository(config),
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
state: new StateRepository(config),
|
|
88
|
+
store: new StoreRepository(config),
|
|
89
|
+
subscription: new SubscriptionRepository(config),
|
|
90
90
|
"tax-category": new TaxCategoryRepository(config),
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
type: new TypeRepository(config),
|
|
92
|
+
zone: new ZoneRepository(config),
|
|
93
93
|
});
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
import { CommercetoolsError } from "~src/exceptions";
|
|
9
9
|
import { hashPassword, validateEmailVerifyToken } from "../lib/password";
|
|
10
10
|
import type { Writable } from "../types";
|
|
11
|
-
import {
|
|
11
|
+
import type { RepositoryContext } from "./abstract";
|
|
12
12
|
import { CustomerRepository } from "./customer";
|
|
13
13
|
|
|
14
14
|
export class MyCustomerRepository extends CustomerRepository {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
import type {
|
|
2
3
|
CartReference,
|
|
3
4
|
MyOrderFromCartDraft,
|
|
4
5
|
Order,
|
|
5
6
|
} from "@commercetools/platform-sdk";
|
|
6
|
-
import
|
|
7
|
-
import { type RepositoryContext } from "./abstract";
|
|
7
|
+
import type { RepositoryContext } from "./abstract";
|
|
8
8
|
import { OrderRepository } from "./order";
|
|
9
9
|
|
|
10
10
|
export class MyOrderRepository extends OrderRepository {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
import type {
|
|
2
3
|
Cart,
|
|
3
4
|
CartReference,
|
|
@@ -13,7 +14,6 @@ import type {
|
|
|
13
14
|
ProductPagedQueryResponse,
|
|
14
15
|
ProductVariant,
|
|
15
16
|
} from "@commercetools/platform-sdk";
|
|
16
|
-
import assert from "assert";
|
|
17
17
|
import type { Config } from "~src/config";
|
|
18
18
|
import { CommercetoolsError } from "~src/exceptions";
|
|
19
19
|
import { generateRandomString, getBaseResourceProperties } from "~src/helpers";
|
|
@@ -420,7 +420,7 @@ export class ProductUpdateHandler
|
|
|
420
420
|
|
|
421
421
|
if (position >= variantImages.length) {
|
|
422
422
|
throw new Error(
|
|
423
|
-
|
|
423
|
+
"Invalid position given. Position in images where the image should be moved. Must be between 0 and the total number of images minus 1.",
|
|
424
424
|
);
|
|
425
425
|
}
|
|
426
426
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InvalidJsonInputError,
|
|
3
|
+
Quote,
|
|
4
|
+
QuoteSetCustomFieldAction,
|
|
5
|
+
QuoteSetCustomTypeAction,
|
|
6
|
+
QuoteTransitionStateAction,
|
|
7
|
+
QuoteUpdateAction,
|
|
8
|
+
StateReference,
|
|
9
|
+
} from "@commercetools/platform-sdk";
|
|
10
|
+
import { CommercetoolsError } from "~src/exceptions";
|
|
11
|
+
import type { Writable } from "~src/types";
|
|
12
|
+
import type { RepositoryContext, UpdateHandlerInterface } from "../abstract";
|
|
13
|
+
import { AbstractUpdateHandler } from "../abstract";
|
|
14
|
+
import { getReferenceFromResourceIdentifier } from "../helpers";
|
|
15
|
+
|
|
16
|
+
export class QuoteUpdateHandler
|
|
17
|
+
extends AbstractUpdateHandler
|
|
18
|
+
implements Partial<UpdateHandlerInterface<Quote, QuoteUpdateAction>>
|
|
19
|
+
{
|
|
20
|
+
setCustomField(
|
|
21
|
+
context: RepositoryContext,
|
|
22
|
+
resource: Quote,
|
|
23
|
+
{ name, value }: QuoteSetCustomFieldAction,
|
|
24
|
+
) {
|
|
25
|
+
if (!resource.custom) {
|
|
26
|
+
throw new Error("Resource has no custom field");
|
|
27
|
+
}
|
|
28
|
+
resource.custom.fields[name] = value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setCustomType(
|
|
32
|
+
context: RepositoryContext,
|
|
33
|
+
resource: Writable<Quote>,
|
|
34
|
+
{ type, fields }: QuoteSetCustomTypeAction,
|
|
35
|
+
) {
|
|
36
|
+
if (!type) {
|
|
37
|
+
resource.custom = undefined;
|
|
38
|
+
} else {
|
|
39
|
+
const resolvedType = this._storage.getByResourceIdentifier(
|
|
40
|
+
context.projectKey,
|
|
41
|
+
type,
|
|
42
|
+
);
|
|
43
|
+
if (!resolvedType) {
|
|
44
|
+
throw new Error(`Type ${type} not found`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
resource.custom = {
|
|
48
|
+
type: {
|
|
49
|
+
typeId: "type",
|
|
50
|
+
id: resolvedType.id,
|
|
51
|
+
},
|
|
52
|
+
fields: fields || {},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
transitionState(
|
|
58
|
+
context: RepositoryContext,
|
|
59
|
+
resource: Writable<Quote>,
|
|
60
|
+
{ state, force }: QuoteTransitionStateAction,
|
|
61
|
+
) {
|
|
62
|
+
let stateReference: StateReference | undefined = undefined;
|
|
63
|
+
if (state) {
|
|
64
|
+
stateReference = getReferenceFromResourceIdentifier<StateReference>(
|
|
65
|
+
state,
|
|
66
|
+
context.projectKey,
|
|
67
|
+
this._storage,
|
|
68
|
+
);
|
|
69
|
+
resource.state = stateReference;
|
|
70
|
+
} else {
|
|
71
|
+
throw new CommercetoolsError<InvalidJsonInputError>(
|
|
72
|
+
{
|
|
73
|
+
code: "InvalidJsonInput",
|
|
74
|
+
message: "Request body does not contain valid JSON.",
|
|
75
|
+
detailedErrorMessage: "actions -> state: Missing required value",
|
|
76
|
+
},
|
|
77
|
+
400,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return resource;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Quote, QuoteDraft } from "@commercetools/platform-sdk";
|
|
2
|
+
import type { Config } from "~src/config";
|
|
3
|
+
import { getBaseResourceProperties } from "~src/helpers";
|
|
4
|
+
import type { RepositoryContext } from "../abstract";
|
|
5
|
+
import { AbstractResourceRepository } from "../abstract";
|
|
6
|
+
import { QuoteUpdateHandler } from "./actions";
|
|
7
|
+
|
|
8
|
+
export class QuoteRepository extends AbstractResourceRepository<"quote"> {
|
|
9
|
+
constructor(config: Config) {
|
|
10
|
+
super("quote", config);
|
|
11
|
+
this.actions = new QuoteUpdateHandler(config.storage);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
create(context: RepositoryContext, draft: QuoteDraft): Quote {
|
|
15
|
+
const staged = this._storage.getByResourceIdentifier<"staged-quote">(
|
|
16
|
+
context.projectKey,
|
|
17
|
+
draft.stagedQuote,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const cart = this._storage.getByResourceIdentifier<"cart">(
|
|
21
|
+
context.projectKey,
|
|
22
|
+
staged.quotationCart,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (!cart.customerId) {
|
|
26
|
+
throw new Error("Cart does not have a customer");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resource: Quote = {
|
|
30
|
+
...getBaseResourceProperties(),
|
|
31
|
+
quoteState: "Accepted",
|
|
32
|
+
quoteRequest: staged.quoteRequest,
|
|
33
|
+
lineItems: cart.lineItems,
|
|
34
|
+
customLineItems: cart.customLineItems,
|
|
35
|
+
customer: {
|
|
36
|
+
typeId: "customer",
|
|
37
|
+
id: cart.customerId,
|
|
38
|
+
},
|
|
39
|
+
stagedQuote: {
|
|
40
|
+
typeId: "staged-quote",
|
|
41
|
+
id: staged.id,
|
|
42
|
+
},
|
|
43
|
+
totalPrice: cart.totalPrice,
|
|
44
|
+
taxedPrice: cart.taxedPrice,
|
|
45
|
+
taxMode: cart.taxMode,
|
|
46
|
+
taxRoundingMode: cart.taxRoundingMode,
|
|
47
|
+
taxCalculationMode: cart.taxCalculationMode,
|
|
48
|
+
billingAddress: cart.billingAddress,
|
|
49
|
+
shippingAddress: cart.shippingAddress,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return resource;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InvalidJsonInputError,
|
|
3
|
+
QuoteRequest,
|
|
4
|
+
QuoteRequestSetCustomFieldAction,
|
|
5
|
+
QuoteRequestSetCustomTypeAction,
|
|
6
|
+
QuoteRequestTransitionStateAction,
|
|
7
|
+
QuoteRequestUpdateAction,
|
|
8
|
+
StateReference,
|
|
9
|
+
} from "@commercetools/platform-sdk";
|
|
10
|
+
import { CommercetoolsError } from "~src/exceptions";
|
|
11
|
+
import type { Writable } from "~src/types";
|
|
12
|
+
import type { RepositoryContext, UpdateHandlerInterface } from "../abstract";
|
|
13
|
+
import { AbstractUpdateHandler } from "../abstract";
|
|
14
|
+
import { getReferenceFromResourceIdentifier } from "../helpers";
|
|
15
|
+
|
|
16
|
+
export class QuoteRequestUpdateHandler
|
|
17
|
+
extends AbstractUpdateHandler
|
|
18
|
+
implements
|
|
19
|
+
Partial<UpdateHandlerInterface<QuoteRequest, QuoteRequestUpdateAction>>
|
|
20
|
+
{
|
|
21
|
+
setCustomField(
|
|
22
|
+
context: RepositoryContext,
|
|
23
|
+
resource: QuoteRequest,
|
|
24
|
+
{ name, value }: QuoteRequestSetCustomFieldAction,
|
|
25
|
+
) {
|
|
26
|
+
if (!resource.custom) {
|
|
27
|
+
throw new Error("Resource has no custom field");
|
|
28
|
+
}
|
|
29
|
+
resource.custom.fields[name] = value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setCustomType(
|
|
33
|
+
context: RepositoryContext,
|
|
34
|
+
resource: Writable<QuoteRequest>,
|
|
35
|
+
{ type, fields }: QuoteRequestSetCustomTypeAction,
|
|
36
|
+
) {
|
|
37
|
+
if (!type) {
|
|
38
|
+
resource.custom = undefined;
|
|
39
|
+
} else {
|
|
40
|
+
const resolvedType = this._storage.getByResourceIdentifier(
|
|
41
|
+
context.projectKey,
|
|
42
|
+
type,
|
|
43
|
+
);
|
|
44
|
+
if (!resolvedType) {
|
|
45
|
+
throw new Error(`Type ${type} not found`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
resource.custom = {
|
|
49
|
+
type: {
|
|
50
|
+
typeId: "type",
|
|
51
|
+
id: resolvedType.id,
|
|
52
|
+
},
|
|
53
|
+
fields: fields || {},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
transitionState(
|
|
59
|
+
context: RepositoryContext,
|
|
60
|
+
resource: Writable<QuoteRequest>,
|
|
61
|
+
{ state, force }: QuoteRequestTransitionStateAction,
|
|
62
|
+
) {
|
|
63
|
+
let stateReference: StateReference | undefined = undefined;
|
|
64
|
+
if (state) {
|
|
65
|
+
stateReference = getReferenceFromResourceIdentifier<StateReference>(
|
|
66
|
+
state,
|
|
67
|
+
context.projectKey,
|
|
68
|
+
this._storage,
|
|
69
|
+
);
|
|
70
|
+
resource.state = stateReference;
|
|
71
|
+
} else {
|
|
72
|
+
throw new CommercetoolsError<InvalidJsonInputError>(
|
|
73
|
+
{
|
|
74
|
+
code: "InvalidJsonInput",
|
|
75
|
+
message: "Request body does not contain valid JSON.",
|
|
76
|
+
detailedErrorMessage: "actions -> state: Missing required value",
|
|
77
|
+
},
|
|
78
|
+
400,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return resource;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Cart, LineItem } from "@commercetools/platform-sdk";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import type { Config } from "~src/config";
|
|
4
|
+
import { InMemoryStorage } from "~src/storage";
|
|
5
|
+
import { QuoteRequestRepository } from ".";
|
|
6
|
+
|
|
7
|
+
describe("QuoteRequest repository", () => {
|
|
8
|
+
const storage = new InMemoryStorage();
|
|
9
|
+
const config: Config = {
|
|
10
|
+
storage,
|
|
11
|
+
strict: false,
|
|
12
|
+
};
|
|
13
|
+
const repository = new QuoteRequestRepository(config);
|
|
14
|
+
|
|
15
|
+
test("create from cart", async () => {
|
|
16
|
+
const cart: Cart = {
|
|
17
|
+
id: "b3875a58-4ab2-4aaa-b399-184ce7561c27",
|
|
18
|
+
version: 1,
|
|
19
|
+
createdAt: "2021-09-02T12:23:30.036Z",
|
|
20
|
+
lastModifiedAt: "2021-09-02T12:23:30.546Z",
|
|
21
|
+
discountCodes: [],
|
|
22
|
+
directDiscounts: [],
|
|
23
|
+
inventoryMode: "None",
|
|
24
|
+
itemShippingAddresses: [],
|
|
25
|
+
lineItems: [
|
|
26
|
+
{
|
|
27
|
+
id: "15fc56ba-a74e-4cf8-b4b0-bada5c101541",
|
|
28
|
+
productId: "PRODUCTID",
|
|
29
|
+
variantId: 1,
|
|
30
|
+
quantity: 1,
|
|
31
|
+
} as unknown as LineItem,
|
|
32
|
+
],
|
|
33
|
+
customLineItems: [],
|
|
34
|
+
totalPrice: {
|
|
35
|
+
type: "centPrecision",
|
|
36
|
+
currencyCode: "EUR",
|
|
37
|
+
centAmount: 10000,
|
|
38
|
+
fractionDigits: 2,
|
|
39
|
+
},
|
|
40
|
+
cartState: "Active",
|
|
41
|
+
shippingMode: "Single",
|
|
42
|
+
shipping: [],
|
|
43
|
+
taxMode: "Platform",
|
|
44
|
+
taxRoundingMode: "HalfEven",
|
|
45
|
+
taxCalculationMode: "UnitPriceLevel",
|
|
46
|
+
refusedGifts: [],
|
|
47
|
+
origin: "Customer",
|
|
48
|
+
anonymousId: "1234567890",
|
|
49
|
+
billingAddress: {
|
|
50
|
+
id: "1234567890",
|
|
51
|
+
country: "NL",
|
|
52
|
+
firstName: "John",
|
|
53
|
+
lastName: "Doe",
|
|
54
|
+
streetName: "Main Street",
|
|
55
|
+
streetNumber: "123",
|
|
56
|
+
postalCode: "123456",
|
|
57
|
+
},
|
|
58
|
+
customerEmail: "john.doe@example.com",
|
|
59
|
+
customerGroup: {
|
|
60
|
+
id: "1234567890",
|
|
61
|
+
typeId: "customer-group",
|
|
62
|
+
},
|
|
63
|
+
customerId: "1234567890",
|
|
64
|
+
custom: {
|
|
65
|
+
type: {
|
|
66
|
+
typeId: "type",
|
|
67
|
+
id: "1234567890",
|
|
68
|
+
},
|
|
69
|
+
fields: {
|
|
70
|
+
description: "example description",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
shippingAddress: {
|
|
75
|
+
id: "1234567890",
|
|
76
|
+
country: "NL",
|
|
77
|
+
firstName: "John",
|
|
78
|
+
lastName: "Doe",
|
|
79
|
+
streetName: "Main Street",
|
|
80
|
+
streetNumber: "123",
|
|
81
|
+
postalCode: "123456",
|
|
82
|
+
},
|
|
83
|
+
shippingInfo: {
|
|
84
|
+
shippingMethodName: "Standard Shipping",
|
|
85
|
+
price: {
|
|
86
|
+
type: "centPrecision",
|
|
87
|
+
currencyCode: "EUR",
|
|
88
|
+
centAmount: 1000,
|
|
89
|
+
fractionDigits: 2,
|
|
90
|
+
},
|
|
91
|
+
shippingRate: {
|
|
92
|
+
price: {
|
|
93
|
+
type: "centPrecision",
|
|
94
|
+
currencyCode: "EUR",
|
|
95
|
+
centAmount: 1000,
|
|
96
|
+
fractionDigits: 2,
|
|
97
|
+
},
|
|
98
|
+
tiers: [],
|
|
99
|
+
},
|
|
100
|
+
shippingMethodState: "Shipped",
|
|
101
|
+
},
|
|
102
|
+
taxedPrice: {
|
|
103
|
+
totalNet: {
|
|
104
|
+
type: "centPrecision",
|
|
105
|
+
currencyCode: "EUR",
|
|
106
|
+
centAmount: 1000,
|
|
107
|
+
fractionDigits: 2,
|
|
108
|
+
},
|
|
109
|
+
taxPortions: [],
|
|
110
|
+
totalGross: {
|
|
111
|
+
type: "centPrecision",
|
|
112
|
+
currencyCode: "EUR",
|
|
113
|
+
centAmount: 1210,
|
|
114
|
+
fractionDigits: 2,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
taxedShippingPrice: {
|
|
118
|
+
totalNet: {
|
|
119
|
+
type: "centPrecision",
|
|
120
|
+
currencyCode: "EUR",
|
|
121
|
+
centAmount: 100,
|
|
122
|
+
fractionDigits: 2,
|
|
123
|
+
},
|
|
124
|
+
taxPortions: [],
|
|
125
|
+
totalGross: {
|
|
126
|
+
type: "centPrecision",
|
|
127
|
+
currencyCode: "EUR",
|
|
128
|
+
centAmount: 121,
|
|
129
|
+
fractionDigits: 2,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
storage.add("dummy", "cart", cart);
|
|
135
|
+
const ctx = { projectKey: "dummy" };
|
|
136
|
+
|
|
137
|
+
const result = repository.create(ctx, {
|
|
138
|
+
cart: {
|
|
139
|
+
id: cart.id,
|
|
140
|
+
typeId: "cart",
|
|
141
|
+
},
|
|
142
|
+
cartVersion: cart.version,
|
|
143
|
+
});
|
|
144
|
+
expect(result.cart?.id).toBe(cart.id);
|
|
145
|
+
|
|
146
|
+
const items = repository.query(ctx);
|
|
147
|
+
expect(items.count).toBe(1);
|
|
148
|
+
|
|
149
|
+
expect(result.billingAddress).toEqual(cart.billingAddress);
|
|
150
|
+
expect(result.cart?.id).toEqual(cart.id);
|
|
151
|
+
expect(result.country).toEqual(cart.country);
|
|
152
|
+
expect(result.custom).toEqual(cart.custom);
|
|
153
|
+
expect(result.customerGroup).toEqual(cart.customerGroup);
|
|
154
|
+
expect(result.customer.id).toEqual(cart.customerId);
|
|
155
|
+
expect(result.customLineItems).toEqual(cart.customLineItems);
|
|
156
|
+
expect(result.directDiscounts).toEqual(cart.directDiscounts);
|
|
157
|
+
expect(result.lineItems).toEqual(cart.lineItems);
|
|
158
|
+
expect(result.paymentInfo).toEqual(cart.paymentInfo);
|
|
159
|
+
expect(result.shippingAddress).toEqual(cart.shippingAddress);
|
|
160
|
+
expect(result.taxCalculationMode).toEqual(cart.taxCalculationMode);
|
|
161
|
+
expect(result.taxedPrice).toEqual(cart.taxedPrice);
|
|
162
|
+
expect(result.taxMode).toEqual(cart.taxMode);
|
|
163
|
+
expect(result.taxRoundingMode).toEqual(cart.taxRoundingMode);
|
|
164
|
+
expect(result.totalPrice).toEqual(cart.totalPrice);
|
|
165
|
+
expect(result.store).toEqual(cart.store);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import type {
|
|
3
|
+
Cart,
|
|
4
|
+
CartReference,
|
|
5
|
+
QuoteRequest,
|
|
6
|
+
QuoteRequestDraft,
|
|
7
|
+
} from "@commercetools/platform-sdk";
|
|
8
|
+
import type { Config } from "~src/config";
|
|
9
|
+
import { getBaseResourceProperties } from "~src/helpers";
|
|
10
|
+
import type { RepositoryContext } from "../abstract";
|
|
11
|
+
import { AbstractResourceRepository } from "../abstract";
|
|
12
|
+
import { QuoteRequestUpdateHandler } from "./actions";
|
|
13
|
+
|
|
14
|
+
export class QuoteRequestRepository extends AbstractResourceRepository<"quote-request"> {
|
|
15
|
+
constructor(config: Config) {
|
|
16
|
+
super("quote-request", config);
|
|
17
|
+
this.actions = new QuoteRequestUpdateHandler(config.storage);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
create(context: RepositoryContext, draft: QuoteRequestDraft): QuoteRequest {
|
|
21
|
+
assert(draft.cart, "draft.cart is missing");
|
|
22
|
+
return this.createFromCart(context, {
|
|
23
|
+
id: draft.cart.id!,
|
|
24
|
+
typeId: "cart",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
createFromCart(context: RepositoryContext, cartReference: CartReference) {
|
|
29
|
+
const cart = this._storage.getByResourceIdentifier(
|
|
30
|
+
context.projectKey,
|
|
31
|
+
cartReference,
|
|
32
|
+
) as Cart | null;
|
|
33
|
+
if (!cart) {
|
|
34
|
+
throw new Error("Cannot find cart");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!cart.customerId) {
|
|
38
|
+
throw new Error("Cart does not have a customer");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const resource: QuoteRequest = {
|
|
42
|
+
...getBaseResourceProperties(),
|
|
43
|
+
billingAddress: cart.billingAddress,
|
|
44
|
+
cart: cartReference,
|
|
45
|
+
country: cart.country,
|
|
46
|
+
custom: cart.custom,
|
|
47
|
+
customer: {
|
|
48
|
+
typeId: "customer",
|
|
49
|
+
id: cart.customerId,
|
|
50
|
+
},
|
|
51
|
+
customerGroup: cart.customerGroup,
|
|
52
|
+
customLineItems: [],
|
|
53
|
+
directDiscounts: cart.directDiscounts,
|
|
54
|
+
lineItems: cart.lineItems,
|
|
55
|
+
paymentInfo: cart.paymentInfo,
|
|
56
|
+
quoteRequestState: "Submitted",
|
|
57
|
+
shippingAddress: cart.shippingAddress,
|
|
58
|
+
taxCalculationMode: cart.taxCalculationMode,
|
|
59
|
+
taxedPrice: cart.taxedPrice,
|
|
60
|
+
taxMode: cart.taxMode,
|
|
61
|
+
taxRoundingMode: cart.taxRoundingMode,
|
|
62
|
+
totalPrice: cart.totalPrice,
|
|
63
|
+
store: cart.store,
|
|
64
|
+
};
|
|
65
|
+
return this.saveNew(context, resource);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InvalidJsonInputError,
|
|
3
|
+
StagedQuote,
|
|
4
|
+
StagedQuoteSetCustomFieldAction,
|
|
5
|
+
StagedQuoteSetCustomTypeAction,
|
|
6
|
+
StagedQuoteTransitionStateAction,
|
|
7
|
+
StagedQuoteUpdateAction,
|
|
8
|
+
StateReference,
|
|
9
|
+
} from "@commercetools/platform-sdk";
|
|
10
|
+
import { CommercetoolsError } from "~src/exceptions";
|
|
11
|
+
import type { Writable } from "~src/types";
|
|
12
|
+
import type { RepositoryContext, UpdateHandlerInterface } from "../abstract";
|
|
13
|
+
import { AbstractUpdateHandler } from "../abstract";
|
|
14
|
+
import { getReferenceFromResourceIdentifier } from "../helpers";
|
|
15
|
+
|
|
16
|
+
export class StagedQuoteUpdateHandler
|
|
17
|
+
extends AbstractUpdateHandler
|
|
18
|
+
implements
|
|
19
|
+
Partial<UpdateHandlerInterface<StagedQuote, StagedQuoteUpdateAction>>
|
|
20
|
+
{
|
|
21
|
+
setCustomField(
|
|
22
|
+
context: RepositoryContext,
|
|
23
|
+
resource: StagedQuote,
|
|
24
|
+
{ name, value }: StagedQuoteSetCustomFieldAction,
|
|
25
|
+
) {
|
|
26
|
+
if (!resource.custom) {
|
|
27
|
+
throw new Error("Resource has no custom field");
|
|
28
|
+
}
|
|
29
|
+
resource.custom.fields[name] = value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setCustomType(
|
|
33
|
+
context: RepositoryContext,
|
|
34
|
+
resource: Writable<StagedQuote>,
|
|
35
|
+
{ type, fields }: StagedQuoteSetCustomTypeAction,
|
|
36
|
+
) {
|
|
37
|
+
if (!type) {
|
|
38
|
+
resource.custom = undefined;
|
|
39
|
+
} else {
|
|
40
|
+
const resolvedType = this._storage.getByResourceIdentifier(
|
|
41
|
+
context.projectKey,
|
|
42
|
+
type,
|
|
43
|
+
);
|
|
44
|
+
if (!resolvedType) {
|
|
45
|
+
throw new Error(`Type ${type} not found`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
resource.custom = {
|
|
49
|
+
type: {
|
|
50
|
+
typeId: "type",
|
|
51
|
+
id: resolvedType.id,
|
|
52
|
+
},
|
|
53
|
+
fields: fields || {},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
transitionState(
|
|
59
|
+
context: RepositoryContext,
|
|
60
|
+
resource: Writable<StagedQuote>,
|
|
61
|
+
{ state, force }: StagedQuoteTransitionStateAction,
|
|
62
|
+
) {
|
|
63
|
+
let stateReference: StateReference | undefined = undefined;
|
|
64
|
+
if (state) {
|
|
65
|
+
stateReference = getReferenceFromResourceIdentifier<StateReference>(
|
|
66
|
+
state,
|
|
67
|
+
context.projectKey,
|
|
68
|
+
this._storage,
|
|
69
|
+
);
|
|
70
|
+
resource.state = stateReference;
|
|
71
|
+
} else {
|
|
72
|
+
throw new CommercetoolsError<InvalidJsonInputError>(
|
|
73
|
+
{
|
|
74
|
+
code: "InvalidJsonInput",
|
|
75
|
+
message: "Request body does not contain valid JSON.",
|
|
76
|
+
detailedErrorMessage: "actions -> state: Missing required value",
|
|
77
|
+
},
|
|
78
|
+
400,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return resource;
|
|
83
|
+
}
|
|
84
|
+
}
|