**What**
- Add support for creating a cart with items
- Add endpoint `POST /store/carts/:id/line-items`
- Add `CreateCartWorkflow`
- Add `AddToCartWorkflow`
- Add steps for both workflows
**Testing**
- Endpoints
- Workflows
I would still call this a first iteration, as we are missing a few pieces of the full flow, such as payment sessions, discounts, and taxes.
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
what:
- adds API + workflow to add/remove promotions in a cart
- minor fixes in promotions module
- minor type fixes in cart module
- typing fix in workflows-sdk (Thanks @adrien2p)
- fix step result in workflows-sdk (Thanks @adrien2p)
RESOLVES CORE-1768
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
what:
- when setting null for nullable columns, big number utils should not throw error
- remove circular soft delete depenedencies for cart module
- trims zeros on big number values
**What**
- Update shipping options with its rules and type
- create/update rules independently
- context based validation fundation
- 🔴 list shipping options with context rules fitlering will come in a separate pr to keep this one smaller
FIXES CORE-1743
FIXES CORE-1764
**What**
Default ordering. By default, only the top level entity ordering is applied using the primary keys, the relations are default ordered by the foreign keys.
It include tests fixing for deterministic data ordering
**What**
> [!NOTE]
> I can see this pr becoming huge, so I d like to get this partial one merged 👍
- Fixes shared connection usage (mikro orm compare the instance to its own package and therefore was resulting in not trully reusing the provided connection leading to exhausting the connection pool as multiple connections was created and end up not being all destroyed properly under the hood, discovered in my integration tests)
- Create shipping options method implementation
- DTO's definition and service interface update
- integration tests
- Re work of the indexes with new util update
- Test runner utils to remove a big chunk of the boilerplate of the packages integrations
FIXES CORE-1742
**What**
- add accept invite endpoint
**Invite accept flow**
- authenticate using the auth endpoint to create and auth-user
- invoke accept endpoint with token and info to create user
**What**
The internal service abstraction should return idempotently if an empty array is given in order to prevent full soft deletion of the full data
**What**
- always return an index expression, also for unique constraints
**why**
- constraints can't be partial, meaning `UNIQUE ... WHERE` is not possible with constraints
- constraints are indicies under the hood so it doesn't change the behavior of the system when we're not using constraint specific features but just using them for `UNIQUE`
**What**
Fix the test utils database to trully run the migrations, the migration path is deprecated and not used anymore as umzung infer the path under the hood. It also fix the schema so that it is possible to specify different schema if needed.
The index helper can now create named constraints for uniqueness.
extracted [from](https://github.com/medusajs/medusa/pull/6381)
> This is a proposal - not necessarily the end result - to kick off the discussion about the implementation of the new totals utilities
### What
Introduces a BigNumber class implementation, enabling us to work with high-precision numeric values.
**Scope**
- Introduce the BigNumber class
- Remain somewhat backward-compatible (in behavior)
- Establish a foundation for handling high-precision values in more complex scenarios
**Not in scope**
- The implementation will not address complex use cases. However, the concept introduced now should be open for extensibility, so this can be added later without major changes to the calculation logic
### How
There are significant changes to three areas in this PR:
- Schemas
- (De)-Serialization
- Totals calculations
**Schemas**
Domains that need high-precision values will have two DB columns for each value in the database: a standard numeric column and a raw value column.
The standard column is for basic operations like sorting and filtering in the database and is what should be publicly exposed in our API.
The raw value is initially used solely for precise calculations and is stored as a JSONB column. Keeping it as JSONB is flexible and will allow us to extend the concept in future iterations. As of now, the raw value will only require a single property `value`.
**(De)-Serialization**
We cast the raw JSONB value to a `BigNumberRawValue` when reading from the database.
We serialize the standard value to a `BigNumber` when reading from the database.
We use the standard numeric value to construct the raw value upon writing to the database.
For example, the unit price and raw unit price on line items will be inserted as follows:
```ts
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "cali")
const asBigNumber = new BigNumber(this.raw_unit_price ?? this.unit_price)
this.unit_price = asBigNumber.numeric
this.raw_unit_price = asBigNumber.raw
}
```
**Totals calculations**
For totals calculations, we will use the [`bignumber.js`](https://github.com/MikeMcl/bignumber.js/) library. The library ships with a `BigNumber` class with arithmetic methods for precise calculations.
When we need to perform a calculation, we construct the BigNumber class from the library using the raw value from the database.
Let's have a look at an oversimplified example:
```ts
// create cart with line items
const [createdCart] = await service.create([
{
currency_code: "eur",
items: [
// li_1234
{
title: "test",
quantity: 2,
unit_price: 100,
},
// li_4321
{
title: "test",
quantity: 3,
// raw price creation
unit_price: 200,
},
],
},
])
```
```ts
// calculating line item totals
import BN from "bignumber.js"
const lineItem1 = await service.retrieveLineItem("li_1234")
const lineItem2 = await service.retrieveLineItem("li_4321")
const bnUnitPrice1 = new BN(lineItem1.unit_price.raw)
const bnUnitPrice2 = new BN(lineItem2.unit_price.raw)
const line1Total = bnUnitPrice1.multipliedBy(lineItem1.quantity)
const line2Total = bnUnitPrice2.multipliedBy(lineItem2.quantity)
const total = line1Total.plus(line2Total)
```
**A note on backward compatibility**
Our BigNumber implementation is built to support the existing behavior of numeric values in the database. So even though we serialize the value to a BigNumber, you will still be able to treat it as a standard number, as we've always done.
For example, the following works perfectly fine:
```ts
const lineItem = await service.createLineItem({
title: "test",
quantity: 2,
unit_price: 100,
})
console.log(lineItem.unit_price) // will print `100`
```
However, the type of `unit_price` will be `number | BigNumber`.
**What**
Initialize work on the fulfillment module entities.
This pr finally also include the indexes as i was working on some utilities i though it would make sense to test them directly.
Also this pr add a new utility to generate proper index for our entity properties. It also include a new monkey patch for the migration generator to handle most of if exists/not exists cases. The monkey patch is a workaround the fact that the transpilation does work well with the ECMA used by mikro orm and therefore we end up with some constructor issue when mikro orm try to instanciate the custom generator class extension.
**Comment**
- The rule part will likely evolved when we reach the point of rule filtering based data, so no need for details review I believe
FIXES CORE-1714
FIXES CORE-1715
FIXES CORE-1718
FIXES CORE-1722
FIXES CORE-1723
Current schema diagram

**What**
- remove auth provider entity
**Why**
- The auth provider entity was not really used anywhere
**How**
- Keeping loader behavior as is but removing the
Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
What:
- Event Aggregator Util
- Preparation for normalizing event in a new format (backward compatible with the current format)
- GQL Schema to joiner config and some Entities configured
- Link modules emmiting events
**What**
- make token auth the default being returned from authentication endpoints in api-v2
- Add `auth/session` to convert token to session based auth
- add regex-scopes to authenticate middleware
Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
**What**
- Remove services that do not have any custom business and replace them with a simple interfaces
- Abstract module service provide the following base implementation
- retrieve
- list
- listAndCount
- delete
- softDelete
- restore
The above methods are created for the main model and also for each other models for which a config is provided
all method such as list, listAndCount, delete, softDelete and restore are pluralized with the model it refers to
**Migration**
- [x] product
- [x] pricing
- [x] promotion
- [x] cart
- [x] auth
- [x] customer
- [x] payment
- [x] Sales channel
- [x] Workflow-*
**Usage**
**Module**
The module service can now extend the ` ModulesSdkUtils.abstractModuleServiceFactory` which returns a class with the default implementation for each method and each model following the standard naming convention mentioned above.
This factory have 3 template arguments being the container, the main model DTO and an object representing the other model with a config object that contains at list the DTO and optionally a singular and plural property in case it needs to be set manually. It looks like the following:
```ts
export default class PricingModuleService</* ... */>
extends ModulesSdkUtils.abstractModuleServiceFactory<
InjectedDependencies,
PricingTypes.PriceSetDTO,
{
Currency: { dto: PricingTypes.CurrencyDTO }
MoneyAmount: { dto: PricingTypes.MoneyAmountDTO }
PriceSetMoneyAmount: { dto: PricingTypes.PriceSetMoneyAmountDTO }
PriceSetMoneyAmountRules: {
dto: PricingTypes.PriceSetMoneyAmountRulesDTO
}
PriceRule: { dto: PricingTypes.PriceRuleDTO }
RuleType: { dto: PricingTypes.RuleTypeDTO }
PriceList: { dto: PricingTypes.PriceListDTO }
PriceListRule: { dto: PricingTypes.PriceListRuleDTO }
}
>(PriceSet, generateMethodForModels, entityNameToLinkableKeysMap)
implements PricingTypes.IPricingModuleService
{
// ...
}
```
In the above, the singular and plural can be inferred as there is no tricky naming. Also, the default implementation does not remove the fact that you need to provides all the overloads etc in your module service interface. The above will provide a default implementation following the interface `AbstractModuleService` which is also auto generated, hence you will have the following methods available:
**for the main model**
- list
- retrieve
- listAndCount
- delete
- softDelete
- restore
**for the other models**
- list**MyModels**
- retrieve**MyModel**
- listAndCount**MyModels**
- delete**MyModels**
- softDelete**MyModels**
- restore**MyModels**
**Internal module service**
The internal module service can now extend `ModulesSdkUtils.internalModuleServiceFactory` which takes only one template argument which is the container type.
All internal services provides a default implementation for all retrieve, list, listAndCount, create, update, delete, softDelete, restore methods which follow the following interface `ModulesSdkTypes.InternalModuleService`:
```ts
export interface InternalModuleService<
TEntity extends {},
TContainer extends object = object
> {
get __container__(): TContainer
retrieve(
idOrObject: string,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity>
retrieve(
idOrObject: object,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity>
list(
filters?: FilterQuery<any> | BaseFilterable<FilterQuery<any>>,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity[]>
listAndCount(
filters?: FilterQuery<any> | BaseFilterable<FilterQuery<any>>,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<[TEntity[], number]>
create(data: any[], sharedContext?: Context): Promise<TEntity[]>
create(data: any, sharedContext?: Context): Promise<TEntity>
update(data: any[], sharedContext?: Context): Promise<TEntity[]>
update(data: any, sharedContext?: Context): Promise<TEntity>
update(
selectorAndData: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
data: any
},
sharedContext?: Context
): Promise<TEntity[]>
update(
selectorAndData: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
data: any
}[],
sharedContext?: Context
): Promise<TEntity[]>
delete(idOrSelector: string, sharedContext?: Context): Promise<void>
delete(idOrSelector: string[], sharedContext?: Context): Promise<void>
delete(idOrSelector: object, sharedContext?: Context): Promise<void>
delete(idOrSelector: object[], sharedContext?: Context): Promise<void>
delete(
idOrSelector: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
},
sharedContext?: Context
): Promise<void>
softDelete(
idsOrFilter: string[] | InternalFilterQuery,
sharedContext?: Context
): Promise<[TEntity[], Record<string, unknown[]>]>
restore(
idsOrFilter: string[] | InternalFilterQuery,
sharedContext?: Context
): Promise<[TEntity[], Record<string, unknown[]>]>
upsert(data: any[], sharedContext?: Context): Promise<TEntity[]>
upsert(data: any, sharedContext?: Context): Promise<TEntity>
}
```
When a service is auto generated you can use that interface to type your class property representing the expected internal service.
**Repositories**
The repositories can now extend `DALUtils.mikroOrmBaseRepositoryFactory` which takes one template argument being the entity or the template entity and provides all the default implementation. If the repository is auto generated you can type it using the `RepositoryService` interface. Here is the new interface typings.
```ts
export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
find(options?: FindOptions<T>, context?: Context): Promise<T[]>
findAndCount(
options?: FindOptions<T>,
context?: Context
): Promise<[T[], number]>
create(data: any[], context?: Context): Promise<T[]>
// Becareful here, if you have a custom internal service, the update data should never be the entity otherwise
// both entity and update will point to the same ref and create issues with mikro orm
update(data: { entity; update }[], context?: Context): Promise<T[]>
delete(
idsOrPKs: FilterQuery<T> & BaseFilterable<FilterQuery<T>>,
context?: Context
): Promise<void>
/**
* Soft delete entities and cascade to related entities if configured.
*
* @param idsOrFilter
* @param context
*
* @returns [T[], Record<string, string[]>] the second value being the map of the entity names and ids that were soft deleted
*/
softDelete(
idsOrFilter: string[] | InternalFilterQuery,
context?: Context
): Promise<[T[], Record<string, unknown[]>]>
restore(
idsOrFilter: string[] | InternalFilterQuery,
context?: Context
): Promise<[T[], Record<string, unknown[]>]>
upsert(data: any[], context?: Context): Promise<T[]>
}
```
What:
- When calling a module's method inside a Local Workflow the MedusaContext is passed as the last argument to the method if not provided
- Add `requestId` to req
- A couple of fixes on Remote Joiner and the data fetcher for internal services
Why:
- The context used to initialize the workflow has to be shared with all modules. properties like transactionId will be used to emit events and requestId to trace logs for example.
**What**
- Catches unique constraints on customer_id, is_default_billing/is_default_shipping and reformats
- Adds an step to create and update of addresses that unsets the previous default shipping/billing address if necessary.
- This creates a behavior in the API where you can always set an address to be default and it will automatically unset the previous one for you.
* initial implementation
* add test for invalid scope
* get config from scope not db
* assign config from scope
* fix package.json
* optional providers
* make providers options
* update type
what:
- adds create endpoint for promotions including workflows and endpoint (RESOLVES CORE-1678)
- adds update endpoint for promotions including workflows and endpoint (RESOLVES CORE-1679)
- adds create endpoint for campaigns including workflows and endpoint (RESOLVES CORE-1684)
- adds update endpoint for campaigns including workflows and endpoint (RESOLVES CORE-1685)