@unchainedshop/core-products 5.0.0-alpha.4 → 5.0.0-alpha.7
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/README.md +11 -106
- package/lib/module/configureProductsModule.d.ts +15 -0
- package/lib/module/configureProductsModule.js +37 -12
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -13,121 +13,26 @@ npm install @unchainedshop/core-products
|
|
|
13
13
|
|
|
14
14
|
## Usage
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
import { configureProductsModule, ProductType } from '@unchainedshop/core-products';
|
|
16
|
+
The platform initializes this module as `platform.unchainedAPI.modules.products`.
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
```typescript
|
|
19
|
+
import { ProductType } from '@unchainedshop/core-products';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
type: ProductType.
|
|
24
|
-
|
|
21
|
+
const { products } = platform.unchainedAPI.modules;
|
|
22
|
+
const product = await products.create({
|
|
23
|
+
type: ProductType.SIMPLE_PRODUCT,
|
|
24
|
+
tags: ['featured'],
|
|
25
25
|
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const products = await productsModule.findProducts({
|
|
26
|
+
await products.publish(product);
|
|
27
|
+
const publishedProducts = await products.findProducts({
|
|
29
28
|
tags: ['featured'],
|
|
30
29
|
includeDrafts: false,
|
|
31
30
|
});
|
|
32
|
-
|
|
33
|
-
// Publish a product
|
|
34
|
-
await productsModule.publish(productId);
|
|
35
31
|
```
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
### Module Configuration
|
|
40
|
-
|
|
41
|
-
| Export | Description |
|
|
42
|
-
|--------|-------------|
|
|
43
|
-
| `configureProductsModule` | Configure and return the products module |
|
|
44
|
-
|
|
45
|
-
### Queries
|
|
46
|
-
|
|
47
|
-
| Method | Description |
|
|
48
|
-
|--------|-------------|
|
|
49
|
-
| `findProduct` | Find product by ID or slug |
|
|
50
|
-
| `findProducts` | Find products with filtering and pagination |
|
|
51
|
-
| `count` | Count products matching query |
|
|
52
|
-
| `productExists` | Check if product exists |
|
|
53
|
-
|
|
54
|
-
### Mutations
|
|
55
|
-
|
|
56
|
-
| Method | Description |
|
|
57
|
-
|--------|-------------|
|
|
58
|
-
| `create` | Create a new product |
|
|
59
|
-
| `update` | Update product data |
|
|
60
|
-
| `delete` | Soft delete a product |
|
|
61
|
-
| `publish` | Publish a draft product |
|
|
62
|
-
| `unpublish` | Unpublish a product |
|
|
63
|
-
| `addAssignment` | Add product proxy assignment |
|
|
64
|
-
| `removeAssignment` | Remove product assignment |
|
|
65
|
-
|
|
66
|
-
### Submodules
|
|
67
|
-
|
|
68
|
-
#### Media (`products.media`)
|
|
69
|
-
| Method | Description |
|
|
70
|
-
|--------|-------------|
|
|
71
|
-
| `findProductMedia` | Find media for a product |
|
|
72
|
-
| `createMedia` | Add media to product |
|
|
73
|
-
| `updateMedia` | Update media metadata |
|
|
74
|
-
| `deleteMedia` | Remove media from product |
|
|
75
|
-
| `reorderMedia` | Reorder product media |
|
|
76
|
-
|
|
77
|
-
#### Prices (`products.prices`)
|
|
78
|
-
| Method | Description |
|
|
79
|
-
|--------|-------------|
|
|
80
|
-
| `findProductPrices` | Get product prices |
|
|
81
|
-
| `createPrice` | Add a price |
|
|
82
|
-
| `updatePrice` | Update a price |
|
|
83
|
-
| `deletePrice` | Remove a price |
|
|
84
|
-
|
|
85
|
-
#### Reviews (`products.reviews`)
|
|
86
|
-
| Method | Description |
|
|
87
|
-
|--------|-------------|
|
|
88
|
-
| `findProductReviews` | Find reviews for product |
|
|
89
|
-
| `createReview` | Add a review |
|
|
90
|
-
| `updateReview` | Update a review |
|
|
91
|
-
| `deleteReview` | Remove a review |
|
|
92
|
-
|
|
93
|
-
#### Texts (`products.texts`)
|
|
94
|
-
| Method | Description |
|
|
95
|
-
|--------|-------------|
|
|
96
|
-
| `findProductTexts` | Find localized texts |
|
|
97
|
-
| `updateTexts` | Update product texts |
|
|
98
|
-
|
|
99
|
-
#### Variations (`products.variations`)
|
|
100
|
-
| Method | Description |
|
|
101
|
-
|--------|-------------|
|
|
102
|
-
| `findProductVariations` | Find product variations |
|
|
103
|
-
| `createVariation` | Add a variation |
|
|
104
|
-
| `updateVariation` | Update a variation |
|
|
105
|
-
| `deleteVariation` | Remove a variation |
|
|
106
|
-
|
|
107
|
-
### Constants
|
|
108
|
-
|
|
109
|
-
| Export | Description |
|
|
110
|
-
|--------|-------------|
|
|
111
|
-
| `ProductType` | Product types (SimpleProduct, ConfigurableProduct, BundleProduct, PlanProduct, TokenizedProduct) |
|
|
112
|
-
| `ProductStatus` | Product status values (ACTIVE, DRAFT) |
|
|
113
|
-
|
|
114
|
-
### Types
|
|
115
|
-
|
|
116
|
-
| Export | Description |
|
|
117
|
-
|--------|-------------|
|
|
118
|
-
| `Product` | Product document type |
|
|
119
|
-
| `ProductQuery` | Query parameters type |
|
|
120
|
-
| `ProductsModule` | Module interface type |
|
|
121
|
-
|
|
122
|
-
## Events
|
|
33
|
+
`create` returns the product document, and `publish` accepts that document. The module exposes `media`, `prices`, `reviews`, `texts`, `variations`, `assignments`, and `bundleItems` submodules. Catalog prices are written as `commerce.pricing` entries with `minQuantity` tiers; computed pricing is coordinated by the core pricing services and plugins.
|
|
123
34
|
|
|
124
|
-
|
|
125
|
-
|-------|-------------|
|
|
126
|
-
| `PRODUCT_CREATE` | Product created |
|
|
127
|
-
| `PRODUCT_UPDATE` | Product updated |
|
|
128
|
-
| `PRODUCT_REMOVE` | Product deleted |
|
|
129
|
-
| `PRODUCT_PUBLISH` | Product published |
|
|
130
|
-
| `PRODUCT_UNPUBLISH` | Product unpublished |
|
|
35
|
+
See the [module guide](https://docs.unchained.shop/platform-configuration/modules/products), [pricing guide](https://docs.unchained.shop/concepts/pricing-system), [public exports](src/products-index.ts), and [module implementation](src/module/configureProductsModule.ts).
|
|
131
36
|
|
|
132
37
|
## License
|
|
133
38
|
|
|
@@ -119,6 +119,21 @@ export declare const configureProductsModule: (moduleInput: ModuleInput<Products
|
|
|
119
119
|
}) => Promise<number>;
|
|
120
120
|
publish: (product: Product) => Promise<boolean>;
|
|
121
121
|
unpublish: (product: Product) => Promise<boolean>;
|
|
122
|
+
bulkPublish: (productIds: string[]) => Promise<{
|
|
123
|
+
successIds: string[];
|
|
124
|
+
failedIds: string[];
|
|
125
|
+
}>;
|
|
126
|
+
bulkUnpublish: (productIds: string[]) => Promise<{
|
|
127
|
+
successIds: string[];
|
|
128
|
+
failedIds: string[];
|
|
129
|
+
}>;
|
|
130
|
+
bulkUpdateTags: (productIds: string[], { add, remove }: {
|
|
131
|
+
add?: string[];
|
|
132
|
+
remove?: string[];
|
|
133
|
+
}) => Promise<{
|
|
134
|
+
successIds: string[];
|
|
135
|
+
failedIds: string[];
|
|
136
|
+
}>;
|
|
122
137
|
assignments: {
|
|
123
138
|
addProxyAssignment: ({ productId, proxyId, vectors, }: {
|
|
124
139
|
productId: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { emit, registerEvents } from '@unchainedshop/events';
|
|
2
2
|
import { findPreservingIds, generateDbFilterById, buildSortOptions, mongodb, generateDbObjectId, } from '@unchainedshop/mongodb';
|
|
3
|
-
import { SortDirection } from '@unchainedshop/utils';
|
|
3
|
+
import { executeBulkOperation, SortDirection } from '@unchainedshop/utils';
|
|
4
4
|
import { ProductsCollection, ProductStatus, ProductType, } from "../db/ProductsCollection.js";
|
|
5
5
|
import { configureProductMediaModule } from "./configureProductMediaModule.js";
|
|
6
6
|
import { configureProductPricesModule } from "./configureProductPrices.js";
|
|
@@ -126,6 +126,16 @@ export const configureProductsModule = async (moduleInput) => {
|
|
|
126
126
|
}
|
|
127
127
|
return false;
|
|
128
128
|
};
|
|
129
|
+
const updateProduct = async (productId, doc) => {
|
|
130
|
+
const product = await Products.findOneAndUpdate(generateDbFilterById(productId), {
|
|
131
|
+
$set: {
|
|
132
|
+
updated: new Date(),
|
|
133
|
+
...doc,
|
|
134
|
+
},
|
|
135
|
+
}, { returnDocument: 'after' });
|
|
136
|
+
await emit('PRODUCT_UPDATE', { productId, ...doc, product });
|
|
137
|
+
return productId;
|
|
138
|
+
};
|
|
129
139
|
const proxyProducts = async (product, vectors = [], { includeInactive = false } = {}) => {
|
|
130
140
|
const { proxy } = product;
|
|
131
141
|
let filtered = [...(proxy?.assignments || [])];
|
|
@@ -260,17 +270,7 @@ export const configureProductsModule = async (moduleInput) => {
|
|
|
260
270
|
await emit('PRODUCT_CREATE', { product });
|
|
261
271
|
return product;
|
|
262
272
|
},
|
|
263
|
-
update:
|
|
264
|
-
const updateDoc = doc;
|
|
265
|
-
const product = await Products.findOneAndUpdate(generateDbFilterById(productId), {
|
|
266
|
-
$set: {
|
|
267
|
-
updated: new Date(),
|
|
268
|
-
...updateDoc,
|
|
269
|
-
},
|
|
270
|
-
}, { returnDocument: 'after' });
|
|
271
|
-
await emit('PRODUCT_UPDATE', { productId, ...updateDoc, product });
|
|
272
|
-
return productId;
|
|
273
|
-
},
|
|
273
|
+
update: updateProduct,
|
|
274
274
|
firstActiveProductProxy: async (productId) => {
|
|
275
275
|
return Products.findOne({ 'proxy.assignments.productId': productId });
|
|
276
276
|
},
|
|
@@ -292,6 +292,31 @@ export const configureProductsModule = async (moduleInput) => {
|
|
|
292
292
|
deleteProductPermanently,
|
|
293
293
|
publish: publishProduct,
|
|
294
294
|
unpublish: unpublishProduct,
|
|
295
|
+
bulkPublish: async (productIds) => executeBulkOperation(productIds, async (productId) => {
|
|
296
|
+
const product = await Products.findOne(generateDbFilterById(productId), {});
|
|
297
|
+
if (!product || !(await publishProduct(product)))
|
|
298
|
+
throw new Error('publish-failed');
|
|
299
|
+
}),
|
|
300
|
+
bulkUnpublish: async (productIds) => executeBulkOperation(productIds, async (productId) => {
|
|
301
|
+
const product = await Products.findOne(generateDbFilterById(productId), {});
|
|
302
|
+
if (!product || !(await unpublishProduct(product)))
|
|
303
|
+
throw new Error('unpublish-failed');
|
|
304
|
+
}),
|
|
305
|
+
bulkUpdateTags: async (productIds, { add = [], remove = [] }) => {
|
|
306
|
+
const tagsToRemove = new Set(remove);
|
|
307
|
+
return executeBulkOperation(productIds, async (productId) => {
|
|
308
|
+
const product = await Products.findOne(generateDbFilterById(productId), {});
|
|
309
|
+
if (!product || (product.status !== ProductStatus.ACTIVE && !isDraftStatus(product.status))) {
|
|
310
|
+
throw new Error('not-found');
|
|
311
|
+
}
|
|
312
|
+
const tags = (product.tags || []).filter((tag) => !tagsToRemove.has(tag));
|
|
313
|
+
for (const tag of add) {
|
|
314
|
+
if (!tags.includes(tag))
|
|
315
|
+
tags.push(tag);
|
|
316
|
+
}
|
|
317
|
+
await updateProduct(productId, { tags });
|
|
318
|
+
});
|
|
319
|
+
},
|
|
295
320
|
assignments: {
|
|
296
321
|
addProxyAssignment: async ({ productId, proxyId, vectors, }) => {
|
|
297
322
|
const assignment = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/core-products",
|
|
3
3
|
"description": "Product catalog management module for the Unchained Engine",
|
|
4
|
-
"version": "5.0.0-alpha.
|
|
4
|
+
"version": "5.0.0-alpha.7",
|
|
5
5
|
"main": "lib/products-index.js",
|
|
6
6
|
"types": "lib/products-index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@unchainedshop/events": "^5.0.0-alpha.
|
|
39
|
-
"@unchainedshop/logger": "^5.0.0-alpha.
|
|
40
|
-
"@unchainedshop/mongodb": "^5.0.0-alpha.
|
|
41
|
-
"@unchainedshop/utils": "^5.0.0-alpha.
|
|
38
|
+
"@unchainedshop/events": "^5.0.0-alpha.7",
|
|
39
|
+
"@unchainedshop/logger": "^5.0.0-alpha.7",
|
|
40
|
+
"@unchainedshop/mongodb": "^5.0.0-alpha.7",
|
|
41
|
+
"@unchainedshop/utils": "^5.0.0-alpha.7"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^26.2.0",
|