@xbstracts/medusa-coupons 0.0.1
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/.medusa/server/src/admin/index.js +3207 -0
- package/.medusa/server/src/admin/index.mjs +3253 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/entitlements/route.js +9 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/mirror-promotion/route.js +132 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/preview/route.js +28 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/route.js +18 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/variants/[variant_config_id]/route.js +14 -0
- package/.medusa/server/src/api/admin/coupon-products/[product_id]/variants/route.js +14 -0
- package/.medusa/server/src/api/admin/coupon-products/configs/[id]/route.js +53 -0
- package/.medusa/server/src/api/admin/coupon-products/configs/[id]/variants/[variant_config_id]/route.js +26 -0
- package/.medusa/server/src/api/admin/coupon-products/configs/[id]/variants/route.js +27 -0
- package/.medusa/server/src/api/admin/coupon-products/configs/route.js +36 -0
- package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/cancel/route.js +15 -0
- package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/redeem/route.js +15 -0
- package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/route.js +16 -0
- package/.medusa/server/src/api/admin/coupon-products/entitlements/route.js +22 -0
- package/.medusa/server/src/api/admin/coupon-products/options/route.js +9 -0
- package/.medusa/server/src/api/admin/coupon-products/products/route.js +16 -0
- package/.medusa/server/src/api/admin/coupon-products/route.js +13 -0
- package/.medusa/server/src/api/admin/coupon-products/uploads/route.js +7 -0
- package/.medusa/server/src/api/coupon-product-routes.js +546 -0
- package/.medusa/server/src/api/entitlement-utils.js +90 -0
- package/.medusa/server/src/api/media-upload.js +57 -0
- package/.medusa/server/src/api/middlewares.js +92 -0
- package/.medusa/server/src/api/product-utils.js +304 -0
- package/.medusa/server/src/api/promotion-code-resolver.js +225 -0
- package/.medusa/server/src/api/seller-utils.js +101 -0
- package/.medusa/server/src/api/utils.js +25 -0
- package/.medusa/server/src/api/validators.js +199 -0
- package/.medusa/server/src/api/vendor/coupon-products/[product_id]/entitlements/route.js +9 -0
- package/.medusa/server/src/api/vendor/coupon-products/[product_id]/route.js +18 -0
- package/.medusa/server/src/api/vendor/coupon-products/[product_id]/variants/[variant_config_id]/route.js +14 -0
- package/.medusa/server/src/api/vendor/coupon-products/[product_id]/variants/route.js +14 -0
- package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/route.js +60 -0
- package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/variants/[variant_config_id]/route.js +41 -0
- package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/variants/route.js +45 -0
- package/.medusa/server/src/api/vendor/coupon-products/configs/route.js +51 -0
- package/.medusa/server/src/api/vendor/coupon-products/entitlements/[id]/route.js +25 -0
- package/.medusa/server/src/api/vendor/coupon-products/entitlements/route.js +45 -0
- package/.medusa/server/src/api/vendor/coupon-products/options/route.js +9 -0
- package/.medusa/server/src/api/vendor/coupon-products/products/route.js +19 -0
- package/.medusa/server/src/api/vendor/coupon-products/route.js +13 -0
- package/.medusa/server/src/api/vendor/coupon-products/uploads/route.js +7 -0
- package/.medusa/server/src/jobs/retry-coupon-media-deletes.js +14 -0
- package/.medusa/server/src/links/coupon-issuance-order-line-item.js +17 -0
- package/.medusa/server/src/links/coupon-issuance-order.js +15 -0
- package/.medusa/server/src/links/coupon-product-config-product.js +17 -0
- package/.medusa/server/src/links/coupon-product-variant-config-variant.js +17 -0
- package/.medusa/server/src/links/coupon-promotion-mirror-promotion.js +17 -0
- package/.medusa/server/src/links/customer-coupon-entitlement-customer.js +15 -0
- package/.medusa/server/src/modules/coupon/constants.js +46 -0
- package/.medusa/server/src/modules/coupon/index.js +16 -0
- package/.medusa/server/src/modules/coupon/loaders/reconcile-coupon-products.js +65 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260709000000.js +40 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260710000000.js +22 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260711000000.js +25 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260712000000.js +78 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260719000000.js +19 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260722000000.js +17 -0
- package/.medusa/server/src/modules/coupon/migrations/Migration20260723000000.js +18 -0
- package/.medusa/server/src/modules/coupon/models/coupon-entitlement-promotion.js +31 -0
- package/.medusa/server/src/modules/coupon/models/coupon-entitlement-seller.js +28 -0
- package/.medusa/server/src/modules/coupon/models/coupon-issuance.js +35 -0
- package/.medusa/server/src/modules/coupon/models/coupon-media-asset.js +41 -0
- package/.medusa/server/src/modules/coupon/models/coupon-product-config.js +45 -0
- package/.medusa/server/src/modules/coupon/models/coupon-product-variant-config.js +36 -0
- package/.medusa/server/src/modules/coupon/models/coupon-promotion-mirror.js +27 -0
- package/.medusa/server/src/modules/coupon/models/customer-coupon-entitlement.js +56 -0
- package/.medusa/server/src/modules/coupon/models/index.js +23 -0
- package/.medusa/server/src/modules/coupon/payment-order-utils.js +76 -0
- package/.medusa/server/src/modules/coupon/product-metadata.js +24 -0
- package/.medusa/server/src/modules/coupon/purchase-conditions.js +138 -0
- package/.medusa/server/src/modules/coupon/service.js +628 -0
- package/.medusa/server/src/modules/coupon/types.js +3 -0
- package/.medusa/server/src/modules/coupon/utils.js +302 -0
- package/.medusa/server/src/subscribers/issue-coupons-from-order.js +88 -0
- package/.medusa/server/src/vendor/index.mjs +3237 -0
- package/.medusa/server/src/workflows/hooks/validate-coupon-add-to-cart.js +13 -0
- package/.medusa/server/src/workflows/hooks/validate-coupon-complete-cart.js +8 -0
- package/.medusa/server/src/workflows/index.js +21 -0
- package/.medusa/server/src/workflows/steps.js +27 -0
- package/.medusa/server/src/workflows/validate-coupon-purchase.js +85 -0
- package/README.md +84 -0
- package/package.json +102 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
4
|
+
const validate_coupon_purchase_1 = require("../validate-coupon-purchase");
|
|
5
|
+
core_flows_1.addToCartWorkflow.hooks.validate(async ({ input, cart }, { container }) => {
|
|
6
|
+
await (0, validate_coupon_purchase_1.validateCouponPurchaseForCart)({
|
|
7
|
+
cart,
|
|
8
|
+
container,
|
|
9
|
+
deferMissingCountry: true,
|
|
10
|
+
inputItems: input.items || [],
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGUtY291cG9uLWFkZC10by1jYXJ0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9ob29rcy92YWxpZGF0ZS1jb3Vwb24tYWRkLXRvLWNhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw0REFBK0Q7QUFDL0QsMEVBQTJFO0FBRTNFLDhCQUFpQixDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUN4RSxNQUFNLElBQUEsd0RBQTZCLEVBQUM7UUFDbEMsSUFBSTtRQUNKLFNBQVM7UUFDVCxtQkFBbUIsRUFBRSxJQUFJO1FBQ3pCLFVBQVUsRUFBRyxLQUFhLENBQUMsS0FBSyxJQUFJLEVBQUU7S0FDdkMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQUEifQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
4
|
+
const validate_coupon_purchase_1 = require("../validate-coupon-purchase");
|
|
5
|
+
core_flows_1.completeCartWorkflow.hooks.validate(async ({ cart }, { container }) => {
|
|
6
|
+
await (0, validate_coupon_purchase_1.validateCouponPurchaseForCart)({ cart, container });
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGUtY291cG9uLWNvbXBsZXRlLWNhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL2hvb2tzL3ZhbGlkYXRlLWNvdXBvbi1jb21wbGV0ZS1jYXJ0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsNERBQWtFO0FBQ2xFLDBFQUEyRTtBQUUzRSxpQ0FBb0IsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUNwRSxNQUFNLElBQUEsd0RBQTZCLEVBQUMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQTtBQUMxRCxDQUFDLENBQUMsQ0FBQSJ9
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateCouponProductConfigStep = exports.issueCustomerCouponsFromOrderStep = exports.createCouponProductConfigStep = exports.issueCustomerCouponsFromOrderWorkflow = exports.updateCouponProductConfigWorkflow = exports.createCouponProductConfigWorkflow = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const steps_1 = require("./steps");
|
|
6
|
+
Object.defineProperty(exports, "createCouponProductConfigStep", { enumerable: true, get: function () { return steps_1.createCouponProductConfigStep; } });
|
|
7
|
+
Object.defineProperty(exports, "issueCustomerCouponsFromOrderStep", { enumerable: true, get: function () { return steps_1.issueCustomerCouponsFromOrderStep; } });
|
|
8
|
+
Object.defineProperty(exports, "updateCouponProductConfigStep", { enumerable: true, get: function () { return steps_1.updateCouponProductConfigStep; } });
|
|
9
|
+
exports.createCouponProductConfigWorkflow = (0, workflows_sdk_1.createWorkflow)("create-coupon-product-config-workflow", (input) => {
|
|
10
|
+
const config = (0, steps_1.createCouponProductConfigStep)(input);
|
|
11
|
+
return new workflows_sdk_1.WorkflowResponse(config);
|
|
12
|
+
});
|
|
13
|
+
exports.updateCouponProductConfigWorkflow = (0, workflows_sdk_1.createWorkflow)("update-coupon-product-config-workflow", (input) => {
|
|
14
|
+
const config = (0, steps_1.updateCouponProductConfigStep)(input);
|
|
15
|
+
return new workflows_sdk_1.WorkflowResponse(config);
|
|
16
|
+
});
|
|
17
|
+
exports.issueCustomerCouponsFromOrderWorkflow = (0, workflows_sdk_1.createWorkflow)("issue-customer-coupons-from-order-workflow", (input) => {
|
|
18
|
+
const issuances = (0, steps_1.issueCustomerCouponsFromOrderStep)(input);
|
|
19
|
+
return new workflows_sdk_1.WorkflowResponse(issuances);
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUkwQztBQUUxQyxtQ0FJZ0I7QUEyQmQsOEdBOUJBLHFDQUE2QixPQThCQTtBQUM3QixrSEE5QkEseUNBQWlDLE9BOEJBO0FBQ2pDLDhHQTlCQSxxQ0FBNkIsT0E4QkE7QUEzQmxCLFFBQUEsaUNBQWlDLEdBQUcsSUFBQSw4QkFBYyxFQUM3RCx1Q0FBdUMsRUFDdkMsQ0FBQyxLQUFnRSxFQUFFLEVBQUU7SUFDbkUsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQ0FBNkIsRUFBQyxLQUFLLENBQUMsQ0FBQTtJQUNuRCxPQUFPLElBQUksZ0NBQWdCLENBQUMsTUFBTSxDQUFDLENBQUE7QUFDckMsQ0FBQyxDQUNGLENBQUE7QUFFWSxRQUFBLGlDQUFpQyxHQUFHLElBQUEsOEJBQWMsRUFDN0QsdUNBQXVDLEVBQ3ZDLENBQUMsS0FBd0QsRUFBRSxFQUFFO0lBQzNELE1BQU0sTUFBTSxHQUFHLElBQUEscUNBQTZCLEVBQUMsS0FBSyxDQUFDLENBQUE7SUFDbkQsT0FBTyxJQUFJLGdDQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFBO0FBQ3JDLENBQUMsQ0FDRixDQUFBO0FBRVksUUFBQSxxQ0FBcUMsR0FBRyxJQUFBLDhCQUFjLEVBQ2pFLDRDQUE0QyxFQUM1QyxDQUFDLEtBQWdDLEVBQUUsRUFBRTtJQUNuQyxNQUFNLFNBQVMsR0FBRyxJQUFBLHlDQUFpQyxFQUFDLEtBQUssQ0FBQyxDQUFBO0lBQzFELE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUN4QyxDQUFDLENBQ0YsQ0FBQSJ9
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.issueCustomerCouponsFromOrderStep = exports.updateCouponProductConfigStep = exports.createCouponProductConfigStep = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const coupon_1 = require("../modules/coupon");
|
|
6
|
+
exports.createCouponProductConfigStep = (0, workflows_sdk_1.createStep)("create-coupon-product-config", async (input, { container }) => {
|
|
7
|
+
const service = container.resolve(coupon_1.COUPON_MODULE);
|
|
8
|
+
const config = await service.createProductConfig(input);
|
|
9
|
+
return new workflows_sdk_1.StepResponse(config, config.id);
|
|
10
|
+
}, async (id, { container }) => {
|
|
11
|
+
if (!id)
|
|
12
|
+
return;
|
|
13
|
+
const service = container.resolve(coupon_1.COUPON_MODULE);
|
|
14
|
+
await service.deleteCouponProductConfigs(id);
|
|
15
|
+
});
|
|
16
|
+
exports.updateCouponProductConfigStep = (0, workflows_sdk_1.createStep)("update-coupon-product-config", async (input, { container }) => {
|
|
17
|
+
const service = container.resolve(coupon_1.COUPON_MODULE);
|
|
18
|
+
const { id, ...data } = input;
|
|
19
|
+
const config = await service.updateProductConfig(id, data);
|
|
20
|
+
return new workflows_sdk_1.StepResponse(config);
|
|
21
|
+
});
|
|
22
|
+
exports.issueCustomerCouponsFromOrderStep = (0, workflows_sdk_1.createStep)("issue-customer-coupons-from-order", async (input, { container }) => {
|
|
23
|
+
const service = container.resolve(coupon_1.COUPON_MODULE);
|
|
24
|
+
const issuances = await service.issueFromOrder(input);
|
|
25
|
+
return new workflows_sdk_1.StepResponse(issuances);
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL3N0ZXBzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUE0RTtBQUM1RSw4Q0FBaUQ7QUFJcEMsUUFBQSw2QkFBNkIsR0FBRyxJQUFBLDBCQUFVLEVBQ3JELDhCQUE4QixFQUM5QixLQUFLLEVBQ0gsS0FBa0QsRUFDbEQsRUFBRSxTQUFTLEVBQUUsRUFDYixFQUFFO0lBQ0YsTUFBTSxPQUFPLEdBQXdCLFNBQVMsQ0FBQyxPQUFPLENBQUMsc0JBQWEsQ0FBQyxDQUFBO0lBQ3JFLE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ3ZELE9BQU8sSUFBSSw0QkFBWSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUE7QUFDNUMsQ0FBQyxFQUNELEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQzFCLElBQUksQ0FBQyxFQUFFO1FBQUUsT0FBTTtJQUNmLE1BQU0sT0FBTyxHQUF3QixTQUFTLENBQUMsT0FBTyxDQUFDLHNCQUFhLENBQUMsQ0FBQTtJQUNyRSxNQUFNLE9BQU8sQ0FBQywwQkFBMEIsQ0FBQyxFQUFFLENBQUMsQ0FBQTtBQUM5QyxDQUFDLENBQ0YsQ0FBQTtBQUVZLFFBQUEsNkJBQTZCLEdBQUcsSUFBQSwwQkFBVSxFQUNyRCw4QkFBOEIsRUFDOUIsS0FBSyxFQUFFLEtBQTBDLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQ2xFLE1BQU0sT0FBTyxHQUF3QixTQUFTLENBQUMsT0FBTyxDQUFDLHNCQUFhLENBQUMsQ0FBQTtJQUNyRSxNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxFQUFFLEdBQUcsS0FBSyxDQUFBO0lBQzdCLE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLG1CQUFtQixDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQTtJQUMxRCxPQUFPLElBQUksNEJBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQTtBQUNqQyxDQUFDLENBQ0YsQ0FBQTtBQUVZLFFBQUEsaUNBQWlDLEdBQUcsSUFBQSwwQkFBVSxFQUN6RCxtQ0FBbUMsRUFDbkMsS0FBSyxFQUFFLEtBQWtCLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQzFDLE1BQU0sT0FBTyxHQUF3QixTQUFTLENBQUMsT0FBTyxDQUFDLHNCQUFhLENBQUMsQ0FBQTtJQUNyRSxNQUFNLFNBQVMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDckQsT0FBTyxJQUFJLDRCQUFZLENBQUMsU0FBUyxDQUFDLENBQUE7QUFDcEMsQ0FBQyxDQUNGLENBQUEifQ==
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateCouponPurchaseForCart = validateCouponPurchaseForCart;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const coupon_1 = require("../modules/coupon");
|
|
6
|
+
const purchase_conditions_1 = require("../modules/coupon/purchase-conditions");
|
|
7
|
+
const ATTRIBUTE_LABELS = {
|
|
8
|
+
"customer.groups.id": "Customer Group",
|
|
9
|
+
"region.id": "Region",
|
|
10
|
+
"shipping_address.country_code": "Country",
|
|
11
|
+
sales_channel_id: "Sales Channel",
|
|
12
|
+
currency_code: "Currency Code",
|
|
13
|
+
};
|
|
14
|
+
async function retrieveCouponPurchaseCart(container, cartId) {
|
|
15
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
16
|
+
const { data } = await query.graph({
|
|
17
|
+
entity: "cart",
|
|
18
|
+
fields: [
|
|
19
|
+
"id",
|
|
20
|
+
"currency_code",
|
|
21
|
+
"region_id",
|
|
22
|
+
"sales_channel_id",
|
|
23
|
+
"region.id",
|
|
24
|
+
"sales_channel.id",
|
|
25
|
+
"shipping_address.country_code",
|
|
26
|
+
"customer.id",
|
|
27
|
+
"customer.groups.id",
|
|
28
|
+
"items.id",
|
|
29
|
+
"items.product_id",
|
|
30
|
+
"items.variant_id",
|
|
31
|
+
"items.product.id",
|
|
32
|
+
"items.variant.id",
|
|
33
|
+
],
|
|
34
|
+
filters: { id: cartId },
|
|
35
|
+
});
|
|
36
|
+
return data?.[0] || null;
|
|
37
|
+
}
|
|
38
|
+
async function resolveInputLines(container, items) {
|
|
39
|
+
const variantIds = Array.from(new Set(items.map((item) => item.variant_id).filter(Boolean)));
|
|
40
|
+
if (!variantIds.length)
|
|
41
|
+
return [];
|
|
42
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
43
|
+
const { data } = await query.graph({
|
|
44
|
+
entity: "variant",
|
|
45
|
+
fields: ["id", "product_id", "product.id"],
|
|
46
|
+
filters: { id: variantIds },
|
|
47
|
+
});
|
|
48
|
+
const byId = Object.fromEntries((data || []).map((variant) => [variant.id, variant]));
|
|
49
|
+
return items.flatMap((item) => {
|
|
50
|
+
const variant = byId[item.variant_id];
|
|
51
|
+
return variant
|
|
52
|
+
? [
|
|
53
|
+
{
|
|
54
|
+
product_id: variant.product_id || variant.product?.id,
|
|
55
|
+
variant_id: variant.id,
|
|
56
|
+
product: variant.product,
|
|
57
|
+
variant,
|
|
58
|
+
},
|
|
59
|
+
]
|
|
60
|
+
: [];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async function validateCouponPurchaseForCart({ cart, container, deferMissingCountry = false, inputItems, }) {
|
|
64
|
+
const fullCart = await retrieveCouponPurchaseCart(container, cart.id);
|
|
65
|
+
if (!fullCart)
|
|
66
|
+
return;
|
|
67
|
+
const lines = inputItems?.length
|
|
68
|
+
? await resolveInputLines(container, inputItems)
|
|
69
|
+
: (fullCart.items || []);
|
|
70
|
+
const service = container.resolve(coupon_1.COUPON_MODULE);
|
|
71
|
+
const context = (0, purchase_conditions_1.buildCouponPurchaseContext)(fullCart);
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const config = await service.resolveConfigForLineItem(line);
|
|
74
|
+
if (!config?.purchase_conditions.length)
|
|
75
|
+
continue;
|
|
76
|
+
const evaluation = (0, purchase_conditions_1.evaluateCouponPurchaseConditions)(config.purchase_conditions, context, { defer_missing_country: deferMissingCountry });
|
|
77
|
+
if (!evaluation.eligible) {
|
|
78
|
+
const failed = evaluation.failed_attributes
|
|
79
|
+
.map((attribute) => ATTRIBUTE_LABELS[attribute] || attribute)
|
|
80
|
+
.join(", ");
|
|
81
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `This coupon product cannot be purchased with the current cart conditions: ${failed}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGUtY291cG9uLXB1cmNoYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy92YWxpZGF0ZS1jb3Vwb24tcHVyY2hhc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUEwRUEsc0VBdUNDO0FBakhELHFEQUFrRjtBQUNsRiw4Q0FBaUQ7QUFHakQsK0VBRzhDO0FBRTlDLE1BQU0sZ0JBQWdCLEdBQTJCO0lBQy9DLG9CQUFvQixFQUFFLGdCQUFnQjtJQUN0QyxXQUFXLEVBQUUsUUFBUTtJQUNyQiwrQkFBK0IsRUFBRSxTQUFTO0lBQzFDLGdCQUFnQixFQUFFLGVBQWU7SUFDakMsYUFBYSxFQUFFLGVBQWU7Q0FDL0IsQ0FBQTtBQUVELEtBQUssVUFBVSwwQkFBMEIsQ0FBQyxTQUFjLEVBQUUsTUFBYztJQUN0RSxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ2hFLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUM7UUFDakMsTUFBTSxFQUFFLE1BQU07UUFDZCxNQUFNLEVBQUU7WUFDTixJQUFJO1lBQ0osZUFBZTtZQUNmLFdBQVc7WUFDWCxrQkFBa0I7WUFDbEIsV0FBVztZQUNYLGtCQUFrQjtZQUNsQiwrQkFBK0I7WUFDL0IsYUFBYTtZQUNiLG9CQUFvQjtZQUNwQixVQUFVO1lBQ1Ysa0JBQWtCO1lBQ2xCLGtCQUFrQjtZQUNsQixrQkFBa0I7WUFDbEIsa0JBQWtCO1NBQ25CO1FBQ0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRTtLQUN4QixDQUFDLENBQUE7SUFFRixPQUFPLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQTtBQUMxQixDQUFDO0FBRUQsS0FBSyxVQUFVLGlCQUFpQixDQUFDLFNBQWMsRUFBRSxLQUFZO0lBQzNELE1BQU0s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
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# medusa-coupons
|
|
2
|
+
|
|
3
|
+
Reusable Medusa v2 plugin for coupon products.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
`medusa-coupons` treats a coupon product as a normal Medusa product or variant with coupon issuance configuration. When a configured product is purchased, the plugin creates generic Medusa-owned customer coupon entitlements and emits coupon events that a project integration can consume.
|
|
8
|
+
|
|
9
|
+
This plugin intentionally does not implement gift cards, store credit, POS sync, digital downloads, license keys, Strapi sync, or His/Her-specific behavior. Medusa's loyalty plugin remains responsible for gift cards and store credit.
|
|
10
|
+
|
|
11
|
+
## Main Surfaces
|
|
12
|
+
|
|
13
|
+
- Module: `coupon`
|
|
14
|
+
- Admin routes: `/admin/coupon-products/*`
|
|
15
|
+
- Dashboard pages: `/dashboard/coupons` and `/dashboard/coupons/products`
|
|
16
|
+
- Subscriber: `payment.captured` coupon issuance and redemption
|
|
17
|
+
- Events:
|
|
18
|
+
- `coupon_product_config.created`
|
|
19
|
+
- `coupon_product_config.updated`
|
|
20
|
+
- `coupon_product_config.deleted`
|
|
21
|
+
- `coupon_product.purchased`
|
|
22
|
+
- `coupon_issuance.created`
|
|
23
|
+
- `customer_coupon_entitlement.issued`
|
|
24
|
+
- `customer_coupon_entitlement.redeemed`
|
|
25
|
+
- `customer_coupon_entitlement.canceled`
|
|
26
|
+
- `coupon_promotion_mirror.created`
|
|
27
|
+
- `coupon_promotion_mirror.failed`
|
|
28
|
+
|
|
29
|
+
## Data Model
|
|
30
|
+
|
|
31
|
+
- `CouponProductConfig`: product-level coupon defaults.
|
|
32
|
+
- `CouponProductVariantConfig`: optional variant-level overrides.
|
|
33
|
+
- `CouponIssuance`: idempotent order line issuance record.
|
|
34
|
+
- `CustomerCouponEntitlement`: generic Medusa-owned customer entitlement.
|
|
35
|
+
- `CouponPromotionMirror`: optional Medusa Promotion mirror state.
|
|
36
|
+
|
|
37
|
+
Product config is authoritative. Product metadata only receives a small `metadata.medusa_coupons` marker for discovery.
|
|
38
|
+
|
|
39
|
+
Fixed-amount coupons use `amounts: [{ currency_code, amount }]`. Issued
|
|
40
|
+
entitlements keep an immutable copy of every configured currency amount. The
|
|
41
|
+
plugin translates entitlement codes applied to Medusa carts and draft orders
|
|
42
|
+
into one-use internal promotions for the exact order currency; it never
|
|
43
|
+
converts currencies or falls back to another configured amount.
|
|
44
|
+
|
|
45
|
+
Percentage coupons can target products or the whole order. Product-scoped
|
|
46
|
+
coupons preserve `each` or `once` allocation. Order-scoped coupons persist
|
|
47
|
+
`promotion_template.application_method.target_type = "order"` and always use
|
|
48
|
+
`allocation = "across"`. The target and allocation are copied into immutable
|
|
49
|
+
issuance and entitlement snapshots.
|
|
50
|
+
|
|
51
|
+
## Purchase Conditions
|
|
52
|
+
|
|
53
|
+
Coupon products can restrict purchase by customer group, region, shipping
|
|
54
|
+
country, sales channel, and currency. Conditions use `eq`, `in`, or `ne`, each
|
|
55
|
+
attribute can appear once, and all configured conditions must match. A product
|
|
56
|
+
without conditions is available to every cart context.
|
|
57
|
+
|
|
58
|
+
Fixed-amount coupons require a Currency Code condition. Their coupon values are
|
|
59
|
+
stored once per effective currency; product selling prices remain independent.
|
|
60
|
+
A `ne` currency condition derives its effective values from the store's current
|
|
61
|
+
supported currencies, so the coupon must be reviewed when a new store currency
|
|
62
|
+
is introduced.
|
|
63
|
+
|
|
64
|
+
The Sales Channel condition also synchronizes the product's Medusa sales-channel
|
|
65
|
+
links. Country validation is deferred while adding a line when the cart has no
|
|
66
|
+
shipping address, then enforced strictly by `completeCartWorkflow`. Every other
|
|
67
|
+
condition is enforced during both add-to-cart and checkout. Issuance snapshots
|
|
68
|
+
retain the configured conditions and the cart context that matched them.
|
|
69
|
+
|
|
70
|
+
## Promotion Mirroring
|
|
71
|
+
|
|
72
|
+
Promotion mirroring is opt-in. Shared-code configs can create reusable Medusa
|
|
73
|
+
promotions. Unique and shared entitlement codes are also resolved at checkout
|
|
74
|
+
to customer-bound, one-use internal promotions. Percentage-off-order coupons
|
|
75
|
+
map to a Medusa `PERCENTAGE + ORDER + ACROSS` application method; percentage
|
|
76
|
+
redemption is recorded separately from fixed-amount redemption.
|
|
77
|
+
|
|
78
|
+
## Commands
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install
|
|
82
|
+
npm test
|
|
83
|
+
npm run build
|
|
84
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xbstracts/medusa-coupons",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Reusable Medusa coupon product plugin.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/xbstracts/medusa-coupons#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/xbstracts/medusa-coupons.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/xbstracts/medusa-coupons/issues"
|
|
13
|
+
},
|
|
14
|
+
"author": "Patiparnne <patiparnne@gmail.com>",
|
|
15
|
+
"buy_me_a_coffee": "https://buymeacoffee.com/patiparnne",
|
|
16
|
+
"patreon": "https://www.patreon.com/patiparnne",
|
|
17
|
+
"files": [
|
|
18
|
+
".medusa/server"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
"./package.json": "./package.json",
|
|
22
|
+
"./workflows": "./.medusa/server/src/workflows/index.js",
|
|
23
|
+
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
24
|
+
"./modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
25
|
+
"./providers/*": "./.medusa/server/src/providers/*/index.js",
|
|
26
|
+
"./*": "./.medusa/server/src/*.js",
|
|
27
|
+
"./admin": {
|
|
28
|
+
"import": "./.medusa/server/src/admin/index.mjs",
|
|
29
|
+
"require": "./.medusa/server/src/admin/index.js",
|
|
30
|
+
"default": "./.medusa/server/src/admin/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./vendor": {
|
|
33
|
+
"import": "./.medusa/server/src/vendor/index.mjs",
|
|
34
|
+
"default": "./.medusa/server/src/vendor/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"medusa",
|
|
39
|
+
"coupon-products",
|
|
40
|
+
"coupons",
|
|
41
|
+
"plugin",
|
|
42
|
+
"medusa-plugin",
|
|
43
|
+
"medusa-v2"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "medusa plugin:build && node scripts/write-dashboard-extension-mjs.mjs && node scripts/assert-dashboard-extensions.mjs",
|
|
47
|
+
"dev": "medusa plugin:develop",
|
|
48
|
+
"lint": "medusa lint",
|
|
49
|
+
"test": "jest --runInBand",
|
|
50
|
+
"prepublishOnly": "npm run build"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@medusajs/admin-sdk": "2.13.4",
|
|
54
|
+
"@medusajs/framework": "2.13.4",
|
|
55
|
+
"@medusajs/icons": "2.13.4",
|
|
56
|
+
"@medusajs/medusa": "2.13.4",
|
|
57
|
+
"@medusajs/ui": "4.1.1",
|
|
58
|
+
"@mercurjs/dashboard-sdk": "2.1.1",
|
|
59
|
+
"@tanstack/react-query": "5.64.2",
|
|
60
|
+
"multer": "^2.0.2",
|
|
61
|
+
"react": "^18.3.1",
|
|
62
|
+
"react-dom": "^18.3.1",
|
|
63
|
+
"react-i18next": "13.5.0",
|
|
64
|
+
"react-router-dom": "6.30.3"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@hookform/resolvers": "3.4.2",
|
|
68
|
+
"@medusajs/cli": "2.13.4",
|
|
69
|
+
"@medusajs/eslint-plugin": "2.17.2",
|
|
70
|
+
"@medusajs/test-utils": "2.13.4",
|
|
71
|
+
"@swc/core": "^1.7.28",
|
|
72
|
+
"@swc/jest": "^0.2.36",
|
|
73
|
+
"@types/jest": "^29.5.13",
|
|
74
|
+
"@types/multer": "^2.0.0",
|
|
75
|
+
"@types/node": "^20.0.0",
|
|
76
|
+
"@types/react": "^18.3.2",
|
|
77
|
+
"@types/react-dom": "^18.2.25",
|
|
78
|
+
"esbuild": "0.21.5",
|
|
79
|
+
"eslint": "^9.0.0",
|
|
80
|
+
"jest": "^29.7.0",
|
|
81
|
+
"jiti": "^2.0.0",
|
|
82
|
+
"prop-types": "^15.8.1",
|
|
83
|
+
"react-hook-form": "7.49.1",
|
|
84
|
+
"ts-node": "^10.9.2",
|
|
85
|
+
"typescript": "5.9.3",
|
|
86
|
+
"vite": "^5.2.11",
|
|
87
|
+
"yalc": "^1.0.0-pre.53"
|
|
88
|
+
},
|
|
89
|
+
"peerDependencies": {
|
|
90
|
+
"@medusajs/admin-sdk": "2.13.4",
|
|
91
|
+
"@medusajs/cli": "2.13.4",
|
|
92
|
+
"@medusajs/framework": "2.13.4",
|
|
93
|
+
"@medusajs/icons": "2.13.4",
|
|
94
|
+
"@medusajs/medusa": "2.13.4",
|
|
95
|
+
"@medusajs/test-utils": "2.13.4",
|
|
96
|
+
"@medusajs/ui": "4.1.1"
|
|
97
|
+
},
|
|
98
|
+
"engines": {
|
|
99
|
+
"node": ">=20"
|
|
100
|
+
},
|
|
101
|
+
"packageManager": "npm@11.4.2"
|
|
102
|
+
}
|