@haus-tech/campaign-price-plugin 1.0.2
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/CHANGELOG.md +17 -0
- package/README.md +129 -0
- package/package.json +17 -0
- package/src/api/api-extensions.d.ts +1 -0
- package/src/api/api-extensions.js +21 -0
- package/src/api/api-extensions.js.map +1 -0
- package/src/api/campaign-price-product.resolver.d.ts +8 -0
- package/src/api/campaign-price-product.resolver.js +72 -0
- package/src/api/campaign-price-product.resolver.js.map +1 -0
- package/src/api/campaign-price-search.resolver.d.ts +20 -0
- package/src/api/campaign-price-search.resolver.js +52 -0
- package/src/api/campaign-price-search.resolver.js.map +1 -0
- package/src/api/campaign-price-variant.resolver.d.ts +6 -0
- package/src/api/campaign-price-variant.resolver.js +49 -0
- package/src/api/campaign-price-variant.resolver.js.map +1 -0
- package/src/campaign-price.plugin.d.ts +2 -0
- package/src/campaign-price.plugin.js +47 -0
- package/src/campaign-price.plugin.js.map +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/strategy/campaign-price.strategy.d.ts +11 -0
- package/src/strategy/campaign-price.strategy.js +37 -0
- package/src/strategy/campaign-price.strategy.js.map +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## 1.0.1
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- optimize price resolution in campaign price plugin ([706bc2e](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/706bc2e))
|
|
6
|
+
- add index file for campaign price plugin ([d0123d7](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/d0123d7))
|
|
7
|
+
- add campaign price plugin for Vendure ([e6579e9](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/e6579e9))
|
|
8
|
+
|
|
9
|
+
### 🩹 Fixes
|
|
10
|
+
|
|
11
|
+
- add TODO comment for tax support in campaign price label ([c6a7063](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/c6a7063))
|
|
12
|
+
|
|
13
|
+
# 1.0.0
|
|
14
|
+
|
|
15
|
+
### 🚀 Features
|
|
16
|
+
|
|
17
|
+
- add campaign price plugin for Vendure ([e6579e9](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/e6579e9))
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: campaign-price-plugin
|
|
3
|
+
title: Campaign Price Plugin
|
|
4
|
+
description: Vendure plugin that adds campaign pricing with an ordinaryPrice field for "was / now" display.
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
tags: [vendure, plugin, campaign, price, sale]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Campaign Price Plugin
|
|
10
|
+
|
|
11
|
+
The **Campaign Price Plugin** is a Vendure plugin that lets you run campaigns and sales by setting a campaign price on product variant prices. When a campaign price is set, it is used as the selling price instead of the ordinary price. The plugin also exposes an `ordinaryPrice` field in the Shop API so storefronts can show “was X, now Y” pricing.
|
|
12
|
+
|
|
13
|
+
## Functionality
|
|
14
|
+
|
|
15
|
+
- **Campaign price custom field** — Add a `campaignPrice` (integer) to `ProductVariantPrice`, editable per channel/currency in the Admin UI.
|
|
16
|
+
- **Price calculation strategy** — If a variant has a campaign price set and it is greater than zero, that price is used; otherwise the existing price calculation strategy is used.
|
|
17
|
+
- **Ordinary price in Shop API** — `ProductVariant`, `Product`, and `SearchResult` get an `ordinaryPrice` field so the storefront can display the original price alongside the current (possibly campaign) price.
|
|
18
|
+
|
|
19
|
+
## Use Cases
|
|
20
|
+
|
|
21
|
+
- Time-limited sales or campaigns with a different price per channel/currency.
|
|
22
|
+
- Showing strikethrough “was X” and “now Y” prices on product and search pages.
|
|
23
|
+
- Running campaigns without changing the base price data; turn the campaign off by clearing the campaign price.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
To install the `Campaign Price Plugin`, follow these steps:
|
|
28
|
+
|
|
29
|
+
1. Install the plugin package:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add @haus-tech/campaign-price-plugin
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or, if using npm:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @haus-tech/campaign-price-plugin
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. Add the plugin to your Vendure configuration in `vendure-config.ts`:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { CampaignPricePlugin } from '@haus-tech/campaign-price-plugin'
|
|
45
|
+
|
|
46
|
+
export const config = {
|
|
47
|
+
plugins: [CampaignPricePlugin],
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. Restart your Vendure server.
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
### Admin UI
|
|
56
|
+
|
|
57
|
+
In the Admin UI, each product variant price has a **Campaign Price** field (per channel/currency). Set it to run a campaign; leave it empty or zero to use the ordinary price. The field uses the currency form input component.
|
|
58
|
+
|
|
59
|
+
### Shop API
|
|
60
|
+
|
|
61
|
+
The plugin extends the Shop API so you can read both the current price (which may be the campaign price) and the ordinary price.
|
|
62
|
+
|
|
63
|
+
- **`ProductVariant.ordinaryPrice`** — `Int!` — The ordinary (pre-campaign) price for the variant.
|
|
64
|
+
- **`Product.ordinaryPrice`** — `SearchResultPrice!` — Min/max ordinary price across the product’s variants (single value or range).
|
|
65
|
+
- **`SearchResult.ordinaryPrice`** — `SearchResultPrice!` — Ordinary price for search results. Requires the default search plugin to index `product-ordinaryPriceMin` and `product-ordinaryPriceMax` if you use search; add these to your search index configuration if needed.
|
|
66
|
+
|
|
67
|
+
Example: show “was / now” on a variant:
|
|
68
|
+
|
|
69
|
+
```graphql
|
|
70
|
+
query VariantPricing($id: ID!) {
|
|
71
|
+
productVariant(id: $id) {
|
|
72
|
+
id
|
|
73
|
+
price
|
|
74
|
+
priceWithTax
|
|
75
|
+
ordinaryPrice
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Example: product with variant price range:
|
|
81
|
+
|
|
82
|
+
```graphql
|
|
83
|
+
query ProductPricing($id: ID!) {
|
|
84
|
+
product(id: $id) {
|
|
85
|
+
id
|
|
86
|
+
priceRange {
|
|
87
|
+
min
|
|
88
|
+
max
|
|
89
|
+
}
|
|
90
|
+
ordinaryPrice {
|
|
91
|
+
... on SinglePrice {
|
|
92
|
+
value
|
|
93
|
+
}
|
|
94
|
+
... on PriceRange {
|
|
95
|
+
min
|
|
96
|
+
max
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `price` / `priceRange` for the current selling price (campaign or ordinary) and `ordinaryPrice` for the original price in your “was X, now Y” UI.
|
|
104
|
+
|
|
105
|
+
## Testing
|
|
106
|
+
|
|
107
|
+
1. Run `yarn test` to execute the tests.
|
|
108
|
+
2. Implement additional tests to cover your specific use cases.
|
|
109
|
+
|
|
110
|
+
## Publish to NPM
|
|
111
|
+
|
|
112
|
+
1. Make sure you are [logged in to NPM](https://docs.npmjs.com/cli/v9/commands/npm-login).
|
|
113
|
+
2. Build the plugin:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
yarn build
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. Publish the plugin:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
yarn publish
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Resources
|
|
126
|
+
|
|
127
|
+
- [Vendure Plugin Documentation](https://www.vendure.io/docs/plugins/)
|
|
128
|
+
- [GraphQL Code Generator](https://the-guild.dev/graphql/codegen) for generating TypeScript types for custom GraphQL types.
|
|
129
|
+
- [Vendure Price Calculation](https://www.vendure.io/docs/products-stock/pricing/) for custom strategies
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@haus-tech/campaign-price-plugin",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Campaign price plugin",
|
|
5
|
+
"author": "Haus Tech",
|
|
6
|
+
"repository": "https://github.com/WeAreHausTech/haus-tech-vendure-plugins",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.0.0"
|
|
10
|
+
},
|
|
11
|
+
"main": "src/index.js",
|
|
12
|
+
"types": "src/index.d.ts",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "yarn ts-node test/dev-server.ts"
|
|
15
|
+
},
|
|
16
|
+
"type": "commonjs"
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const apiExtensions: import("graphql").DocumentNode;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.apiExtensions = void 0;
|
|
7
|
+
const graphql_tag_1 = __importDefault(require("graphql-tag"));
|
|
8
|
+
exports.apiExtensions = (0, graphql_tag_1.default) `
|
|
9
|
+
extend type ProductVariant {
|
|
10
|
+
ordinaryPrice: Int!
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
extend type Product {
|
|
14
|
+
ordinaryPrice: SearchResultPrice!
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
extend type SearchResult {
|
|
18
|
+
ordinaryPrice: SearchResultPrice!
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
//# sourceMappingURL=api-extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-extensions.js","sourceRoot":"","sources":["../../../../../packages/campaign-price-plugin/src/api/api-extensions.ts"],"names":[],"mappings":";;;;;;AAAA,8DAA6B;AAEhB,QAAA,aAAa,GAAG,IAAA,qBAAG,EAAA;;;;;;;;;;;;CAY/B,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RequestContext, Product, ProductVariantService } from '@vendure/core';
|
|
2
|
+
import { SearchResultPrice } from '@vendure/common/lib/generated-shop-types';
|
|
3
|
+
export declare class CampaignPriceProductResolver {
|
|
4
|
+
private productVariantService;
|
|
5
|
+
constructor(productVariantService: ProductVariantService);
|
|
6
|
+
private resolvePrice;
|
|
7
|
+
ordinaryPrice(ctx: RequestContext, product: Product): Promise<SearchResultPrice>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CampaignPriceProductResolver = void 0;
|
|
16
|
+
const graphql_1 = require("@nestjs/graphql");
|
|
17
|
+
const core_1 = require("@vendure/core");
|
|
18
|
+
let CampaignPriceProductResolver = class CampaignPriceProductResolver {
|
|
19
|
+
productVariantService;
|
|
20
|
+
constructor(productVariantService) {
|
|
21
|
+
this.productVariantService = productVariantService;
|
|
22
|
+
}
|
|
23
|
+
resolvePrice(min, max) {
|
|
24
|
+
if (min === undefined || max === undefined) {
|
|
25
|
+
return {
|
|
26
|
+
__typename: 'SinglePrice',
|
|
27
|
+
value: 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (min === max) {
|
|
31
|
+
return {
|
|
32
|
+
__typename: 'SinglePrice',
|
|
33
|
+
value: min,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
__typename: 'PriceRange',
|
|
38
|
+
min,
|
|
39
|
+
max,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async ordinaryPrice(ctx, product) {
|
|
43
|
+
if (!product || !product.variants || !product.variants.length) {
|
|
44
|
+
return this.resolvePrice(0, 0);
|
|
45
|
+
}
|
|
46
|
+
// TODO: Cache the prices
|
|
47
|
+
const prices = await Promise.all(product.variants.map(async (variant) => {
|
|
48
|
+
if (!variant)
|
|
49
|
+
return 0;
|
|
50
|
+
const variantPrices = await this.productVariantService.getProductVariantPrices(ctx, variant.id);
|
|
51
|
+
return variantPrices?.[0]?.price || 0;
|
|
52
|
+
}));
|
|
53
|
+
const min = Math.min(...prices);
|
|
54
|
+
const max = Math.max(...prices);
|
|
55
|
+
return this.resolvePrice(min, max);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.CampaignPriceProductResolver = CampaignPriceProductResolver;
|
|
59
|
+
__decorate([
|
|
60
|
+
(0, graphql_1.ResolveField)('ordinaryPrice'),
|
|
61
|
+
__param(0, (0, core_1.Ctx)()),
|
|
62
|
+
__param(1, (0, graphql_1.Parent)()),
|
|
63
|
+
__metadata("design:type", Function),
|
|
64
|
+
__metadata("design:paramtypes", [core_1.RequestContext,
|
|
65
|
+
core_1.Product]),
|
|
66
|
+
__metadata("design:returntype", Promise)
|
|
67
|
+
], CampaignPriceProductResolver.prototype, "ordinaryPrice", null);
|
|
68
|
+
exports.CampaignPriceProductResolver = CampaignPriceProductResolver = __decorate([
|
|
69
|
+
(0, graphql_1.Resolver)('Product'),
|
|
70
|
+
__metadata("design:paramtypes", [core_1.ProductVariantService])
|
|
71
|
+
], CampaignPriceProductResolver);
|
|
72
|
+
//# sourceMappingURL=campaign-price-product.resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-price-product.resolver.js","sourceRoot":"","sources":["../../../../../packages/campaign-price-plugin/src/api/campaign-price-product.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAAgE;AAChE,wCAAmF;AAI5E,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IACnB;IAApB,YAAoB,qBAA4C;QAA5C,0BAAqB,GAArB,qBAAqB,CAAuB;IAAG,CAAC;IAE5D,YAAY,CAAC,GAAY,EAAE,GAAY;QAC7C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3C,OAAO;gBACL,UAAU,EAAE,aAAa;gBACzB,KAAK,EAAE,CAAC;aACT,CAAA;QACH,CAAC;QAED,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YAChB,OAAO;gBACL,UAAU,EAAE,aAAa;gBACzB,KAAK,EAAE,GAAG;aACX,CAAA;QACH,CAAC;QAED,OAAO;YACL,UAAU,EAAE,YAAY;YACxB,GAAG;YACH,GAAG;SACJ,CAAA;IACH,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACV,GAAmB,EAChB,OAAgB;QAE1B,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC9D,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAChC,CAAC;QAED,yBAAyB;QACzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACrC,IAAI,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAA;YACtB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,uBAAuB,CAC5E,GAAG,EACH,OAAO,CAAC,EAAE,CACX,CAAA;YACD,OAAO,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QACvC,CAAC,CAAC,CACH,CAAA;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAA;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAA;QAE/B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACpC,CAAC;CACF,CAAA;AAnDY,oEAA4B;AA0BjC;IADL,IAAA,sBAAY,EAAC,eAAe,CAAC;IAE3B,WAAA,IAAA,UAAG,GAAE,CAAA;IACL,WAAA,IAAA,gBAAM,GAAE,CAAA;;qCADG,qBAAc;QACP,cAAO;;iEAsB3B;uCAlDU,4BAA4B;IADxC,IAAA,kBAAQ,EAAC,SAAS,CAAC;qCAEyB,4BAAqB;GADrD,4BAA4B,CAmDxC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SearchResult } from '@vendure/common/lib/generated-shop-types';
|
|
2
|
+
interface ExtendedSearchResult extends SearchResult {
|
|
3
|
+
'product-ordinaryPriceMin'?: number;
|
|
4
|
+
'product-ordinaryPriceMax'?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class CampaignPriceSearchResolver {
|
|
7
|
+
private resolvePrice;
|
|
8
|
+
ordinaryPrice(searchResult: ExtendedSearchResult): {
|
|
9
|
+
__typename: string;
|
|
10
|
+
value: number;
|
|
11
|
+
min?: undefined;
|
|
12
|
+
max?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
__typename: string;
|
|
15
|
+
min: number;
|
|
16
|
+
max: number;
|
|
17
|
+
value?: undefined;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CampaignPriceSearchResolver = void 0;
|
|
16
|
+
const graphql_1 = require("@nestjs/graphql");
|
|
17
|
+
let CampaignPriceSearchResolver = class CampaignPriceSearchResolver {
|
|
18
|
+
resolvePrice(min, max) {
|
|
19
|
+
if (min === undefined || max === undefined) {
|
|
20
|
+
return {
|
|
21
|
+
__typename: 'SinglePrice',
|
|
22
|
+
value: 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (min === max) {
|
|
26
|
+
return {
|
|
27
|
+
__typename: 'SinglePrice',
|
|
28
|
+
value: min,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
__typename: 'PriceRange',
|
|
33
|
+
min,
|
|
34
|
+
max,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
ordinaryPrice(searchResult) {
|
|
38
|
+
return this.resolvePrice(searchResult['product-ordinaryPriceMin'], searchResult['product-ordinaryPriceMax']);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.CampaignPriceSearchResolver = CampaignPriceSearchResolver;
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, graphql_1.ResolveField)('ordinaryPrice'),
|
|
44
|
+
__param(0, (0, graphql_1.Parent)()),
|
|
45
|
+
__metadata("design:type", Function),
|
|
46
|
+
__metadata("design:paramtypes", [Object]),
|
|
47
|
+
__metadata("design:returntype", void 0)
|
|
48
|
+
], CampaignPriceSearchResolver.prototype, "ordinaryPrice", null);
|
|
49
|
+
exports.CampaignPriceSearchResolver = CampaignPriceSearchResolver = __decorate([
|
|
50
|
+
(0, graphql_1.Resolver)('SearchResult')
|
|
51
|
+
], CampaignPriceSearchResolver);
|
|
52
|
+
//# sourceMappingURL=campaign-price-search.resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-price-search.resolver.js","sourceRoot":"","sources":["../../../../../packages/campaign-price-plugin/src/api/campaign-price-search.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAAgE;AASzD,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IAC9B,YAAY,CAAC,GAAY,EAAE,GAAY;QAC7C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3C,OAAO;gBACL,UAAU,EAAE,aAAa;gBACzB,KAAK,EAAE,CAAC;aACT,CAAA;QACH,CAAC;QAED,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YAChB,OAAO;gBACL,UAAU,EAAE,aAAa;gBACzB,KAAK,EAAE,GAAG;aACX,CAAA;QACH,CAAC;QAED,OAAO;YACL,UAAU,EAAE,YAAY;YACxB,GAAG;YACH,GAAG;SACJ,CAAA;IACH,CAAC;IAGD,aAAa,CAAW,YAAkC;QACxD,OAAO,IAAI,CAAC,YAAY,CACtB,YAAY,CAAC,0BAA0B,CAAC,EACxC,YAAY,CAAC,0BAA0B,CAAC,CACzC,CAAA;IACH,CAAC;CACF,CAAA;AA9BY,kEAA2B;AAwBtC;IADC,IAAA,sBAAY,EAAC,eAAe,CAAC;IACf,WAAA,IAAA,gBAAM,GAAE,CAAA;;;;gEAKtB;sCA7BU,2BAA2B;IADvC,IAAA,kBAAQ,EAAC,cAAc,CAAC;GACZ,2BAA2B,CA8BvC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RequestContext, ProductVariant, ProductVariantService } from '@vendure/core';
|
|
2
|
+
export declare class CampaignPriceVariantResolver {
|
|
3
|
+
private productVariantService;
|
|
4
|
+
constructor(productVariantService: ProductVariantService);
|
|
5
|
+
ordinaryPrice(ctx: RequestContext, productVariant: ProductVariant): Promise<number>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CampaignPriceVariantResolver = void 0;
|
|
16
|
+
const graphql_1 = require("@nestjs/graphql");
|
|
17
|
+
const core_1 = require("@vendure/core");
|
|
18
|
+
let CampaignPriceVariantResolver = class CampaignPriceVariantResolver {
|
|
19
|
+
productVariantService;
|
|
20
|
+
constructor(productVariantService) {
|
|
21
|
+
this.productVariantService = productVariantService;
|
|
22
|
+
}
|
|
23
|
+
async ordinaryPrice(ctx, productVariant) {
|
|
24
|
+
// Prefer using the already-loaded price on the ProductVariant, if available,
|
|
25
|
+
// to avoid triggering an extra service call per variant (N+1 pattern).
|
|
26
|
+
const directPrice = productVariant.price;
|
|
27
|
+
if (typeof directPrice === 'number') {
|
|
28
|
+
return directPrice;
|
|
29
|
+
}
|
|
30
|
+
// TODO: Cache the prices
|
|
31
|
+
const variantPrices = await this.productVariantService.getProductVariantPrices(ctx, productVariant.id);
|
|
32
|
+
return variantPrices[0]?.price || 0;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.CampaignPriceVariantResolver = CampaignPriceVariantResolver;
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, graphql_1.ResolveField)(),
|
|
38
|
+
__param(0, (0, core_1.Ctx)()),
|
|
39
|
+
__param(1, (0, graphql_1.Parent)()),
|
|
40
|
+
__metadata("design:type", Function),
|
|
41
|
+
__metadata("design:paramtypes", [core_1.RequestContext,
|
|
42
|
+
core_1.ProductVariant]),
|
|
43
|
+
__metadata("design:returntype", Promise)
|
|
44
|
+
], CampaignPriceVariantResolver.prototype, "ordinaryPrice", null);
|
|
45
|
+
exports.CampaignPriceVariantResolver = CampaignPriceVariantResolver = __decorate([
|
|
46
|
+
(0, graphql_1.Resolver)('ProductVariant'),
|
|
47
|
+
__metadata("design:paramtypes", [core_1.ProductVariantService])
|
|
48
|
+
], CampaignPriceVariantResolver);
|
|
49
|
+
//# sourceMappingURL=campaign-price-variant.resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-price-variant.resolver.js","sourceRoot":"","sources":["../../../../../packages/campaign-price-plugin/src/api/campaign-price-variant.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6CAAgE;AAChE,wCAA0F;AAGnF,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IACnB;IAApB,YAAoB,qBAA4C;QAA5C,0BAAqB,GAArB,qBAAqB,CAAuB;IAAG,CAAC;IAG9D,AAAN,KAAK,CAAC,aAAa,CACV,GAAmB,EAChB,cAA8B;QAExC,6EAA6E;QAC7E,uEAAuE;QACvE,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAA;QACxC,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,WAAW,CAAA;QACpB,CAAC;QAED,yBAAyB;QACzB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,uBAAuB,CAC5E,GAAG,EACH,cAAc,CAAC,EAAE,CAClB,CAAA;QAED,OAAO,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;IACrC,CAAC;CACF,CAAA;AAvBY,oEAA4B;AAIjC;IADL,IAAA,sBAAY,GAAE;IAEZ,WAAA,IAAA,UAAG,GAAE,CAAA;IACL,WAAA,IAAA,gBAAM,GAAE,CAAA;;qCADG,qBAAc;QACA,qBAAc;;iEAgBzC;uCAtBU,4BAA4B;IADxC,IAAA,kBAAQ,EAAC,gBAAgB,CAAC;qCAEkB,4BAAqB;GADrD,4BAA4B,CAuBxC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CampaignPricePlugin = void 0;
|
|
10
|
+
const core_1 = require("@vendure/core");
|
|
11
|
+
const campaign_price_strategy_1 = require("./strategy/campaign-price.strategy");
|
|
12
|
+
const api_extensions_1 = require("./api/api-extensions");
|
|
13
|
+
const campaign_price_variant_resolver_1 = require("./api/campaign-price-variant.resolver");
|
|
14
|
+
const campaign_price_product_resolver_1 = require("./api/campaign-price-product.resolver");
|
|
15
|
+
const campaign_price_search_resolver_1 = require("./api/campaign-price-search.resolver");
|
|
16
|
+
let CampaignPricePlugin = class CampaignPricePlugin {
|
|
17
|
+
};
|
|
18
|
+
exports.CampaignPricePlugin = CampaignPricePlugin;
|
|
19
|
+
exports.CampaignPricePlugin = CampaignPricePlugin = __decorate([
|
|
20
|
+
(0, core_1.VendurePlugin)({
|
|
21
|
+
imports: [core_1.PluginCommonModule],
|
|
22
|
+
shopApiExtensions: {
|
|
23
|
+
schema: api_extensions_1.apiExtensions,
|
|
24
|
+
resolvers: [
|
|
25
|
+
campaign_price_variant_resolver_1.CampaignPriceVariantResolver,
|
|
26
|
+
campaign_price_product_resolver_1.CampaignPriceProductResolver,
|
|
27
|
+
campaign_price_search_resolver_1.CampaignPriceSearchResolver,
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
configuration: (config) => {
|
|
31
|
+
config.customFields.ProductVariantPrice.push({
|
|
32
|
+
name: 'campaignPrice',
|
|
33
|
+
type: 'int',
|
|
34
|
+
nullable: true,
|
|
35
|
+
public: true,
|
|
36
|
+
ui: { component: 'currency-form-input' },
|
|
37
|
+
label: [
|
|
38
|
+
{ languageCode: core_1.LanguageCode.en, value: 'Campaign Price' },
|
|
39
|
+
{ languageCode: core_1.LanguageCode.sv, value: 'Kampanjpris (inkl. moms)' }, // TODO: We need to support prices without tax?
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
config.catalogOptions.productVariantPriceCalculationStrategy = new campaign_price_strategy_1.CampaignPriceStrategy(config.catalogOptions.productVariantPriceCalculationStrategy);
|
|
43
|
+
return config;
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
], CampaignPricePlugin);
|
|
47
|
+
//# sourceMappingURL=campaign-price.plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-price.plugin.js","sourceRoot":"","sources":["../../../../packages/campaign-price-plugin/src/campaign-price.plugin.ts"],"names":[],"mappings":";;;;;;;;;AAAA,wCAA+E;AAC/E,gFAA0E;AAC1E,yDAAoD;AACpD,2FAAoF;AACpF,2FAAoF;AACpF,yFAAkF;AAgC3E,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAAG,CAAA;AAAtB,kDAAmB;8BAAnB,mBAAmB;IA9B/B,IAAA,oBAAa,EAAC;QACb,OAAO,EAAE,CAAC,yBAAkB,CAAC;QAC7B,iBAAiB,EAAE;YACjB,MAAM,EAAE,8BAAa;YACrB,SAAS,EAAE;gBACT,8DAA4B;gBAC5B,8DAA4B;gBAC5B,4DAA2B;aAC5B;SACF;QACD,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE;YACxB,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC;gBAC3C,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;gBACZ,EAAE,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE;gBACxC,KAAK,EAAE;oBACL,EAAE,YAAY,EAAE,mBAAY,CAAC,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;oBAC1D,EAAE,YAAY,EAAE,mBAAY,CAAC,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,+CAA+C;iBACtH;aACF,CAAC,CAAA;YAEF,MAAM,CAAC,cAAc,CAAC,sCAAsC,GAAG,IAAI,+CAAqB,CACtF,MAAM,CAAC,cAAc,CAAC,sCAAsC,CAC7D,CAAA;YAED,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAC;GACW,mBAAmB,CAAG"}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './campaign-price.plugin';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./campaign-price.plugin"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/campaign-price-plugin/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAuC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ProductVariantPriceCalculationStrategy, PriceCalculationResult, ProductVariantPriceCalculationArgs } from '@vendure/core';
|
|
2
|
+
declare module '@vendure/core/dist/entity/custom-entity-fields' {
|
|
3
|
+
interface CustomProductVariantPriceFields {
|
|
4
|
+
campaignPrice: number;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export declare class CampaignPriceStrategy implements ProductVariantPriceCalculationStrategy {
|
|
8
|
+
private existingStrategy;
|
|
9
|
+
constructor(existingStrategy: ProductVariantPriceCalculationStrategy);
|
|
10
|
+
calculate(args: ProductVariantPriceCalculationArgs): Promise<PriceCalculationResult>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CampaignPriceStrategy = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let CampaignPriceStrategy = class CampaignPriceStrategy {
|
|
15
|
+
existingStrategy;
|
|
16
|
+
constructor(existingStrategy) {
|
|
17
|
+
this.existingStrategy = existingStrategy;
|
|
18
|
+
}
|
|
19
|
+
async calculate(args) {
|
|
20
|
+
const { productVariant } = args;
|
|
21
|
+
const variantPrices = productVariant.productVariantPrices;
|
|
22
|
+
const campaignPrice = variantPrices[0]?.customFields?.campaignPrice;
|
|
23
|
+
if (campaignPrice != null && campaignPrice > 0) {
|
|
24
|
+
return {
|
|
25
|
+
price: campaignPrice,
|
|
26
|
+
priceIncludesTax: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return this.existingStrategy.calculate(args);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.CampaignPriceStrategy = CampaignPriceStrategy;
|
|
33
|
+
exports.CampaignPriceStrategy = CampaignPriceStrategy = __decorate([
|
|
34
|
+
(0, common_1.Injectable)(),
|
|
35
|
+
__metadata("design:paramtypes", [Object])
|
|
36
|
+
], CampaignPriceStrategy);
|
|
37
|
+
//# sourceMappingURL=campaign-price.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-price.strategy.js","sourceRoot":"","sources":["../../../../../packages/campaign-price-plugin/src/strategy/campaign-price.strategy.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA2C;AAcpC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACZ;IAApB,YAAoB,gBAAwD;QAAxD,qBAAgB,GAAhB,gBAAgB,CAAwC;IAAG,CAAC;IAEhF,KAAK,CAAC,SAAS,CAAC,IAAwC;QACtD,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAA;QAE/B,MAAM,aAAa,GAAG,cAAc,CAAC,oBAAoB,CAAA;QACzD,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,aAAa,CAAA;QAEnE,IAAI,aAAa,IAAI,IAAI,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YAC/C,OAAO;gBACL,KAAK,EAAE,aAAa;gBACpB,gBAAgB,EAAE,IAAI;aACvB,CAAA;QACH,CAAC;QAED,OAAO,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC;CACF,CAAA;AAlBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;;GACA,qBAAqB,CAkBjC"}
|