## Summary
**What** — What changes are introduced in this PR?
Add a method in the Tax Module's servie to retrieve a provider by its ID.
**Why** — Why are these changes relevant or necessary?
The Tax Module Provider could be used for use cases other than calculating tax lines. For example, Avalara supports importing products to manage product-specific taxes. However, it's not possible right now to listen to the `product.created` event and create the product in Avalara with its provider. Instead, you'll have to create a separate module that also connects to Avalara and resolve it in the subsriber.
This also matches the pattern in the Analytics Module, which allows retrieving the underlying provider.
**How** — How have these changes been implemented?
Add a `getProvider` method to the Tax Module's service and its interface.
**Testing** — How have these changes been tested, or how can the reviewer test the feature?
Added integration test for the method.
---
## Examples
Provide examples or code snippets that demonstrate how this feature works, or how it can be used in practice.
This helps with documentation and ensures maintainers can quickly understand and verify the change.
```ts
const avalaraProvider = taxModuleService.getProvider("tp_avalara_avalara")
```
---
## Checklist
Please ensure the following before requesting a review:
- [x] I have added a **changeset** for this PR
- Every non-breaking change should be marked as a **patch**
- To add a changeset, run `yarn changeset` and follow the prompts
- [x] The changes are covered by relevant **tests**
- [x] I have verified the code works as intended locally
- [ ] I have linked the related issue(s) if applicable
---
## Additional Context
Add any additional context, related issues, or references that might help the reviewer understand this PR.
### What
Add a new `once` allocation strategy to promotions that limits application to a maximum number of items across the entire cart, rather than per line item.
### Why
Merchants want to create promotions that apply to a limited number of items across the entire cart. For example:
- "Get $10 off, applied to one item only"
- "20% off up to 2 items in your cart"
Current allocation strategies:
- `each`: Applies to each line item independently (respects `max_quantity` per item)
- `across`: Distributes proportionally across all items
Neither supports limiting total applications across the entire cart.
### How
Add `once` to the `ApplicationMethodAllocation` enum.
Behavior:
- Applies promotion to maximum `max_quantity` items across entire cart
- Always prioritizes lowest-priced eligible items first
- Distributes sequentially across items until quota exhausted
- Requires `max_quantity` field to be set
### Example Usage
**Scenario 1: Fixed discount**
```javascript
{
type: "fixed",
allocation: "once",
value: 10, // $10 off
max_quantity: 2 // Apply to 2 items max across cart
}
Cart:
- Item A: 3 units @ $100/unit
- Item B: 5 units @ $50/unit (lowest price)
Result: $20 discount on Item B (2 units × $10)
```
**Scenario 2: Distribution across items**
```javascript
{
type: "fixed",
allocation: "once",
value: 5,
max_quantity: 4
}
Cart:
- Item A: 2 units @ $50/unit
- Item B: 3 units @ $60/unit
Result:
- Item A: $10 discount (2 units × $5)
- Item B: $10 discount (2 units × $5, remaining quota)
```
**Scenario 3: Percentage discount - single item**
```javascript
{
type: "percentage",
allocation: "once",
value: 20, // 20% off
max_quantity: 3 // Apply to 3 items max
}
Cart:
- Item A: 5 units @ $100/unit
- Item B: 4 units @ $50/unit (lowest price)
Result: $30 discount on Item B (3 units × $50 × 20% = $30)
```
**Scenario 4: Percentage discount - distributed across items**
```javascript
{
type: "percentage",
allocation: "once",
value: 15, // 15% off
max_quantity: 5
}
Cart:
- Item A: 2 units @ $40/unit (lowest price)
- Item B: 4 units @ $80/unit
Result:
- Item A: $12 discount (2 units × $40 × 15% = $12)
- Item B: $36 discount (3 units × $80 × 15% = $36, remaining quota)
Total: $48 discount
```
**Scenario 5: Percentage with max_quantity = 1**
```javascript
{
type: "percentage",
allocation: "once",
value: 25, // 25% off
max_quantity: 1 // Only one item
}
Cart:
- Item A: 3 units @ $60/unit
- Item B: 2 units @ $30/unit (lowest price)
Result: $7.50 discount on Item B (1 unit × $30 × 25%)
```
* wip
* chore: prepare for PR
* move to end
* Change order of operations in refundPaymentWorkflow
Updated the order of operations in the refundPaymentWorkflow.
* chore: Add validation
When starting the Medusa application i see the following in the console:
```
update-order-tax-lines: "when" name should be defined. A random one will be assigned to it, which is not recommended for production.
({ input }) => {
return input.item_ids?.length > 0;
}
update-order-tax-lines: "when" name should be defined. A random one will be assigned to it, which is not recommended for production.
({ input }) => {
return input.shipping_method_ids?.length > 0;
}
```
This PR fixes the issue by passing a step name as a first parameter to the `when` usages in `updateOrderTaxLinesWorkflow`
**What**
- implement promotion usage limits per customer/email
- fix registering spend usage over the limit
- fix type errors in promotion module tests
**How**
- introduce a new type of campaign budget that can be defined by an attribute such as customer id or email
- add `CampaignBudgetUsage` entity to keep track of the number of uses per attribute value
- update `registerUsage` and `computeActions` in the promotion module to work with the new type
- update `core-flows` to pass context needed for usage calculation to the promotion module
**Breaking**
- registering promotion usage now throws (and cart complete fails) if the budget limit is exceeded or if the cart completion would result in a breached limit
---
CLOSES CORE-1172
CLOSES CORE-1173
CLOSES CORE-1174
CLOSES CORE-1175
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
I added a lock in all workflows of the draft order. I don't think there are drawbacks and it will make sure we don't run into concurrency issues in with draft orders. I don't see why we would not add this in the order workflows, let me know and I can add it too.
I also didn't see any workflow that is long enough to justify adding a timeout of more than 2 seconds, but let me know if you think otherwise, we can discuss adjustments :)
CLOSES-1228
**What**
After lot of investigation, we finally found one of our performance regerssion point (see [here](https://github.com/mikro-orm/mikro-orm/issues/6905)), this pr downgrade mikro orm and move the strategy back to select in where needed
**What**
It seems that for some reason the weak map fail in some scenario, but after investigation, the usage of map would not have a bad impact as it will be released after the Distributed transaction if finished. Therefore, falling back to Map instead
FIXES https://github.com/medusajs/medusa/issues/13654
NOTE: Waiting for the user feedback as he is also using node 18. We also use the exact same pattern in all our core flows without issues 🤔
Closes#13163
I have a few questions about expected behaviour, since this currently breaks some tests:
- Many tests use the productModule to create products, with default status == "draft", and use the addToCart workflow which now throws. Should I change all breaking tests to specify status == "published" whne creating the product? The alternative would be to check the status in the store API route before the workflow but 1. it would be an extra query and 2. the addToCart workflow is only used in the store currently, and even if it was to be used admin-side, it still doesn't make sense to add a draft product to cart
- After this PR an unpublished product would give the same error as a variant that doesn't exist. While imho this is correct, the thrown error (for both) is "Items do not have a price" which doesn't make much sense(i believe the workflows goes through with an empty variants list and then errors at the price check point). Should I throw a different error when a variant doesn't exists/isn't published?
---
> [!NOTE]
> Enforces that only variants from published products can be added to carts, adds status fetching, refines errors, and updates tests to use ProductStatus.PUBLISHED.
>
> - **Core Flows**:
> - addToCart: Validate variants exist and belong to `product.status = PUBLISHED`; throw clear `INVALID_DATA` when not found/unpublished.
> - Data fetching: Include `product.status` in `cart` and `order` variant field selections.
> - **Tests/Fixtures**:
> - Update integration tests to set `status: ProductStatus.PUBLISHED` when creating products and import `ProductStatus` where needed.
> - Add cases for unpublished products and non-existent variants producing the new error message.
>
> <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ca72532e957964d2d8e6bcecbb0905054c677ded. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup>
RESOLVES CORE-1153
**What**
- This pr mainly lay the foundation the caching layer. It comes with a modules (built in memory cache) and a redis provider.
- Apply caching to few touch point to test
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
**What**
The context reference is being mutated by the repository leading to an empty context. Also, the filter is built from the pricing context instead of pricing context -> context leading to always fetch all preferences all the time
PARTIALLY RESOLVES CORE-1156
**What**
Improve upsertWithReplace to batch as much as possible what can be batched. Performance of this method will be much greater specially for cases with maybe entities and batch (e.g we seen many cases where they bulk product with hundreds variants and options etc)
for example let take the following object:
- entity 1
- entity 2 []
- entity 3 []
- entity 2 []
- entity 3 []
here all entity 3 will be batched and all entity 2 will be batched
I ve also added a pretty detail test that check all the stage and what is batched or not with many comments so that it is less harder to consume and remember in the future
Also includes:
- mikro orm upgade (issues found and fixes)
- order module hooks fixes
**NOTE**
It was easier for now to do this instead of rewriting the different areas where it is being used, also, maybe it means that we will have closer performance to what we would expect to have natively
**NOTE 2**
Also fix the fact that integration tests of the core packages never ran 😂
RESOLVES CORE-1204
**What**
- Fix wrong price tier when multiple items are targetting the same variant
- fix type import from the wrong package
**Notes**
If you are struggling navigating the changes, you can focus on the following files:
```
integration-tests/http/__tests__/cart/store/cart.spec.ts
integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts
packages/core/core-flows/src/cart/steps/get-promotion-codes-to-apply.ts
packages/core/core-flows/src/cart/steps/get-variant-price-sets.ts
packages/core/core-flows/src/cart/workflows/add-to-cart.ts
packages/core/core-flows/src/cart/workflows/create-carts.ts
packages/core/core-flows/src/cart/workflows/get-variants-and-items-with-prices.ts
packages/core/core-flows/src/cart/workflows/refresh-cart-items.ts
packages/core/core-flows/src/order/workflows/add-line-items.ts
packages/core/core-flows/src/order/workflows/create-order.ts
```
**What**
- add InfiniteList on location selection for inventory level -> fixes issue with location pagination
- fix removal of location level for an inventory item
- refresh the levels table when locations are updated
- add search input for filtering locations
---
CLOSES CORE-1208
CLOSES CORE-1209
This PR just adds the stuff necessary to support refund reasons in the dashboard. It adds the option in the settings tab and allows viewing, creating, editing and deleting refund reasons. I hate to open such a big PR but most of it is copy pasted from the return reasons. Major difference is only the fact that refund reasons don't have a `value` field
cc @willbouch since you asked to be tagged if I opened this PR.
The /store/shipping-options is one of only 2 store endpoints to not allow custom `fields` to be passed(other one is /store/returns if you guys want to add it to the backlog). This PR fixes that so that custom linked models can also be retrieved.
Note: This fix reveals a bug in the next.js starter
eac359cc8d/src/lib/data/fulfillment.ts (L23-L24)
`fields` was previously ignored, now it errors since it tries to parse the misspelled "fulfllment"(the i is missing). It worked before since both fields are already defined by default inside the workflow. So the `fields` line is totally redundant and should be removed(ideally before merging this).
Maybe in the next release notes it should also warn users to remove it for those that already have a modified copy of the starter.
* chore(): remove ssl_mode from url and also use sslmode
* improve regexp
* chore(): remove ssl_mode from url and also use sslmode
* chore(): remove ssl_mode from url and also use sslmode
* Update SSL mode configuration in changeset
Removed 'ssl_mode' from URL and replaced it with 'sslmode'.