* chore(): Accept an extra agument 'all-or-nothing' on the migrate command
* Create rich-camels-brush.md
* chore(): Accept an extra agument 'all-or-nothing' on the migrate command
* chore(): Accept an extra agument 'all-or-nothing' on the migrate command
* chore(): Accept an extra agument 'all-or-nothing' on the migrate command
* chore(): fix broken down migrations
* chore(): update changeset
* wip
* add wip
* wip
* reuse action
* finish first draft
* fix tests
* cleanup
* Only compute adjustments when necessary
* Create hot-carrots-look.md
* address comments
* minor tweaks
* fix pay col
* fix test
* wip
* Dwip
* wip
* fix: adjustment typo
* fix: import
* fix: workflow imports
* wip: update test
* feat: upsert versioned adjustments when previewing order
* fix: revert unique codes change
* fix: order spec test with versioning
* wip: save
* feat: make adjustments work for preview and confirm flow, wip base repo filtering of older version adjustments
* fix: missing populate where
* wip: populate where loading versioned adjustments
* fix: filter out older adjustment versions
* temp: comment adjustments in repo
* test: add adjustment if no version
* wip: configure populate where in order base repository
* fix: rm manual filtering
* fix: revert base repo changes
* fix: revert
* fix: use order item version instead of order version
* fix: rm only in test
* fix: update case spec
* fix: remove sceanrio, wip test with draft promotion
* feat: test correct adjustments when disabling promotion
* feat: complex test case
* feat: test consecutive order edits
* feat: 2 promotions test case with a fixed promo
* feat: migrate existing order line item adjustments to order items latest version
* feat: update dep after merge
* wip: load adjustments separatley
* feat: adjustments collections
* fix: spread result, handle related entity case
* fix: update lock
* feat: make sure version is loaded, refactor, handle related entity case
* fix: check fields
* feat: loading adjustments for list and count
* fix: correct items version field
* fix: rm empty array
* fix: wip order modules spec
* fix: order module specs
* feat: preinit items adjustments
* fix: rm only
* fix: rm only
* chore: cleanup
* fix: migration files
* fix: dont change formatting
* fix: core package build
* chore: more cleanup
* fix: item update util
* fix: duplicate import
* fix: refresh adjustments for exchanges (#13992)
* wip: exchange adjustments
* feat: test - receive items
* feat: finish test case
* fix: casing
* fix(draft-orders, core-flows, orders) refresh adjustments for draft orders (#14025)
* wip: draft orders adjustments refresh
* feat: rewrite to use REPLACE action + test
* fix: rm only
* feat: cleanup old REPLACE actions
* feat: cleanup adjustemnts when 0 promotions
* wip: canceling draft order
* fix: make version arg optional
* fix: restore promotion links
* feat: test reverting on cancelation
* fix: address comments in tests
* wip: fix summary on preview
* fix: get pending diff on preview summary from total
* fix: revert pending diff change
---------
Co-authored-by: fPolic <mainacc.polic@gmail.com>
Co-authored-by: Frane Polić <16856471+fPolic@users.noreply.github.com>
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* Create lucky-poets-scream.md
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* chore(): Cleanup and organize deps
* dedupe snapshot this build
* split into 4 shard
* re configure packages integration tests
* re configure packages integration tests
* re configure packages integration tests
* re configure packages integration tests
* update scripts
* update scripts
* update scripts
* update scripts
* update scripts
* update scripts
* update scripts
* update scripts
* reduce shard for packages
### 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%)
```
**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>
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* test(): test dynamic max workers
* Clarify test description and improve CI
**What**
- Remove overserialization withing transaction and rely more on internal service or protected method instead which does not serialize and work with Infered entity type
- Slightly rework loop complexity
Overall, this gives a good spare of resources and time spent for serialization
* chore(): Upgrade mikro orm
* handle 'null' value for big number props
* 6.5.2
* remove only
* fix pricing module rule value
* switch select in strategy for balances
* revert to select in strategy for order module
* fix defining DML ManyToOne
* fix define relationship
* test fix
* more fixes
* change order strategy to balanced
* change order strategy to balanced
* prevent unnecessary manager fork
* revert generated www changes
* remove unnecessary changes
* Create real-cobras-deny.md
* address feedback
---------
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
what:
Introduces 2 new features to promotion module:
1. Introduce max quantity limit to promotion application - This will limit the application of the promotion based on the quantity of the target products in the cart.
2. When applying buy get promotions, we will now apply buyget promotion until eligible items are exhausted or max quantity is reached.
```
- Buy 2 t-shirts, Get 1 sweater
- Max quantity -> 1
This means you can add two t-shirts, and get 1 sweaters for free. However, if you add four t-shirts, you only get one sweater for free.
```
```
- Buy 2 t-shirts, Get 1 sweater
- Max quantity -> 3
This means you can add six t-shirts, and get three sweaters for free. However, if you add eight t-shirts, you only get three sweaters for free
```
```
- Buy 4 t-shirts, Get 2 sweater
- Max quantity -> 1
This should throw on creation, as the max quantity should as a minimum be the same value as the target rule quantity
```
RESOLVES SUP-2357 / https://github.com/medusajs/medusa/issues/13265
* feat(dashboard,core,modules): free shipping promotion in dashboard
* self-review
* adapt for edit to work
* changeset
* integration tests
* across for each
* remove only from tests
* remove console log
* revert to across
* update wording for shipping promotions
* modify changeset
* suggestion frane
* fix i18n schema
* fix(utils): fix promotion case of each allocation not applying its amount
* chore: fixed tests
---------
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
Changed the In operator to actually work like an In instead of being same logic as Equals. This means that in promotions you can add a rule to apply when a product is in a category or multiple different. Before the logic had to match all the products categories to apply, which doesnt really make sense when you have nested category structure on products. The logic also applies to tags where you can make a rule apply based on a tag before it also had to match all tags on a product to apply.
Issue:
https://github.com/medusajs/medusa/issues/12669
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
* Move from total to original_total to resolve edge case in adjustment calculation
* Added changeset
* Added test case for correction
---------
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
* This fixes the discount_ calculation logic
* This fixes the adjustment to be handled as a subtotal value in every calculation and applies the tax inclusive logic on the promotion value itself
* Added some testcases and revoked some changes to improve testing output
* Fixed a test case based on feedback
* Corrected promotion/admin test cases
* Corrected cart/store test case
* Improved cart/store test cases for more robust promotion testing considering tax inclusion flags
* Remove unnessary changes as adjustments now automatically are subtotals and therefore the tax inclusive flag does not need to be applied again
* Remove adjustments->is_tax_inclusive usage everywhere
* Migration script to remove is_tax_inclusive in cart line item adjustment
* Forgot to adjust one more testcase
* Corrections based on fPolic feedback
* Refactored PR to consider feedback from oliver
* Added more testcases for promotion in cart
---------
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>