@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.
Files changed (84) hide show
  1. package/.medusa/server/src/admin/index.js +3207 -0
  2. package/.medusa/server/src/admin/index.mjs +3253 -0
  3. package/.medusa/server/src/api/admin/coupon-products/[product_id]/entitlements/route.js +9 -0
  4. package/.medusa/server/src/api/admin/coupon-products/[product_id]/mirror-promotion/route.js +132 -0
  5. package/.medusa/server/src/api/admin/coupon-products/[product_id]/preview/route.js +28 -0
  6. package/.medusa/server/src/api/admin/coupon-products/[product_id]/route.js +18 -0
  7. package/.medusa/server/src/api/admin/coupon-products/[product_id]/variants/[variant_config_id]/route.js +14 -0
  8. package/.medusa/server/src/api/admin/coupon-products/[product_id]/variants/route.js +14 -0
  9. package/.medusa/server/src/api/admin/coupon-products/configs/[id]/route.js +53 -0
  10. package/.medusa/server/src/api/admin/coupon-products/configs/[id]/variants/[variant_config_id]/route.js +26 -0
  11. package/.medusa/server/src/api/admin/coupon-products/configs/[id]/variants/route.js +27 -0
  12. package/.medusa/server/src/api/admin/coupon-products/configs/route.js +36 -0
  13. package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/cancel/route.js +15 -0
  14. package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/redeem/route.js +15 -0
  15. package/.medusa/server/src/api/admin/coupon-products/entitlements/[id]/route.js +16 -0
  16. package/.medusa/server/src/api/admin/coupon-products/entitlements/route.js +22 -0
  17. package/.medusa/server/src/api/admin/coupon-products/options/route.js +9 -0
  18. package/.medusa/server/src/api/admin/coupon-products/products/route.js +16 -0
  19. package/.medusa/server/src/api/admin/coupon-products/route.js +13 -0
  20. package/.medusa/server/src/api/admin/coupon-products/uploads/route.js +7 -0
  21. package/.medusa/server/src/api/coupon-product-routes.js +546 -0
  22. package/.medusa/server/src/api/entitlement-utils.js +90 -0
  23. package/.medusa/server/src/api/media-upload.js +57 -0
  24. package/.medusa/server/src/api/middlewares.js +92 -0
  25. package/.medusa/server/src/api/product-utils.js +304 -0
  26. package/.medusa/server/src/api/promotion-code-resolver.js +225 -0
  27. package/.medusa/server/src/api/seller-utils.js +101 -0
  28. package/.medusa/server/src/api/utils.js +25 -0
  29. package/.medusa/server/src/api/validators.js +199 -0
  30. package/.medusa/server/src/api/vendor/coupon-products/[product_id]/entitlements/route.js +9 -0
  31. package/.medusa/server/src/api/vendor/coupon-products/[product_id]/route.js +18 -0
  32. package/.medusa/server/src/api/vendor/coupon-products/[product_id]/variants/[variant_config_id]/route.js +14 -0
  33. package/.medusa/server/src/api/vendor/coupon-products/[product_id]/variants/route.js +14 -0
  34. package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/route.js +60 -0
  35. package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/variants/[variant_config_id]/route.js +41 -0
  36. package/.medusa/server/src/api/vendor/coupon-products/configs/[id]/variants/route.js +45 -0
  37. package/.medusa/server/src/api/vendor/coupon-products/configs/route.js +51 -0
  38. package/.medusa/server/src/api/vendor/coupon-products/entitlements/[id]/route.js +25 -0
  39. package/.medusa/server/src/api/vendor/coupon-products/entitlements/route.js +45 -0
  40. package/.medusa/server/src/api/vendor/coupon-products/options/route.js +9 -0
  41. package/.medusa/server/src/api/vendor/coupon-products/products/route.js +19 -0
  42. package/.medusa/server/src/api/vendor/coupon-products/route.js +13 -0
  43. package/.medusa/server/src/api/vendor/coupon-products/uploads/route.js +7 -0
  44. package/.medusa/server/src/jobs/retry-coupon-media-deletes.js +14 -0
  45. package/.medusa/server/src/links/coupon-issuance-order-line-item.js +17 -0
  46. package/.medusa/server/src/links/coupon-issuance-order.js +15 -0
  47. package/.medusa/server/src/links/coupon-product-config-product.js +17 -0
  48. package/.medusa/server/src/links/coupon-product-variant-config-variant.js +17 -0
  49. package/.medusa/server/src/links/coupon-promotion-mirror-promotion.js +17 -0
  50. package/.medusa/server/src/links/customer-coupon-entitlement-customer.js +15 -0
  51. package/.medusa/server/src/modules/coupon/constants.js +46 -0
  52. package/.medusa/server/src/modules/coupon/index.js +16 -0
  53. package/.medusa/server/src/modules/coupon/loaders/reconcile-coupon-products.js +65 -0
  54. package/.medusa/server/src/modules/coupon/migrations/Migration20260709000000.js +40 -0
  55. package/.medusa/server/src/modules/coupon/migrations/Migration20260710000000.js +22 -0
  56. package/.medusa/server/src/modules/coupon/migrations/Migration20260711000000.js +25 -0
  57. package/.medusa/server/src/modules/coupon/migrations/Migration20260712000000.js +78 -0
  58. package/.medusa/server/src/modules/coupon/migrations/Migration20260719000000.js +19 -0
  59. package/.medusa/server/src/modules/coupon/migrations/Migration20260722000000.js +17 -0
  60. package/.medusa/server/src/modules/coupon/migrations/Migration20260723000000.js +18 -0
  61. package/.medusa/server/src/modules/coupon/models/coupon-entitlement-promotion.js +31 -0
  62. package/.medusa/server/src/modules/coupon/models/coupon-entitlement-seller.js +28 -0
  63. package/.medusa/server/src/modules/coupon/models/coupon-issuance.js +35 -0
  64. package/.medusa/server/src/modules/coupon/models/coupon-media-asset.js +41 -0
  65. package/.medusa/server/src/modules/coupon/models/coupon-product-config.js +45 -0
  66. package/.medusa/server/src/modules/coupon/models/coupon-product-variant-config.js +36 -0
  67. package/.medusa/server/src/modules/coupon/models/coupon-promotion-mirror.js +27 -0
  68. package/.medusa/server/src/modules/coupon/models/customer-coupon-entitlement.js +56 -0
  69. package/.medusa/server/src/modules/coupon/models/index.js +23 -0
  70. package/.medusa/server/src/modules/coupon/payment-order-utils.js +76 -0
  71. package/.medusa/server/src/modules/coupon/product-metadata.js +24 -0
  72. package/.medusa/server/src/modules/coupon/purchase-conditions.js +138 -0
  73. package/.medusa/server/src/modules/coupon/service.js +628 -0
  74. package/.medusa/server/src/modules/coupon/types.js +3 -0
  75. package/.medusa/server/src/modules/coupon/utils.js +302 -0
  76. package/.medusa/server/src/subscribers/issue-coupons-from-order.js +88 -0
  77. package/.medusa/server/src/vendor/index.mjs +3237 -0
  78. package/.medusa/server/src/workflows/hooks/validate-coupon-add-to-cart.js +13 -0
  79. package/.medusa/server/src/workflows/hooks/validate-coupon-complete-cart.js +8 -0
  80. package/.medusa/server/src/workflows/index.js +21 -0
  81. package/.medusa/server/src/workflows/steps.js +27 -0
  82. package/.medusa/server/src/workflows/validate-coupon-purchase.js +85 -0
  83. package/README.md +84 -0
  84. 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
+ }