~~Opening a draft PR to discuss a couple of implementation details that we should align on~~
**What**
Add workflow and API endpoint for creating payment sessions for a payment collection. Endpoint is currently `POST /store/payment-collection/:id/payment-sessions`. I suggested an alternative in a comment below.
Please note, we intentionally do not want to support creating payment sessions in bulk, as this would become a mess when having to manage multiple calls to third-party providers.
**What**
Adds endpoints to manage tax rules on a tax rate:
- Create a tax rule: POST /admin/tax-rates/:id/rules
- Delete a tax rule: DELETE /admin/tax-rates/:id/rules/:rule_id
- Replace tax rules: POST /admin/tax-rates/:id -- with { rules: [...] } in body.
### Noteworthy things I bumped into
**Updating nested relationships**
A TaxRate can have multiple TaxRules and in this PR we enable users to replace all TaxRules associated with a TaxRate in one operation. If working with the module directly this can be done with:
```javascript
taxModuleService.update(rateId, { rules: [{ ... }] })
```
Internally in the `update` function the TaxModule first soft deletes any TaxRules that exist on the TaxRate and then creates new TaxRules for the passed rules ([see test](https://github.com/medusajs/medusa/pull/6557/files#diff-cdcbab80ac7928b80648088ec57a3ab09dddd4409d6afce034f2caff08ee022bR78)).
A challenge arises when doing this in a compensatable way in a workflow. To see this imagine the following:
1. `updateTaxRatesWorkflow` gets the current data for the tax rates to update. This includes the tax rates' rules.
2. `updateTaxRatesWorkflow` calls `taxModuleService.update` with new rules.
3. Internally, the tax module deletes the rules in 1. and creates new rules.
4. Imagine an error happens in a following step and the workflow has to compensate.
5. The workflow uses the data from 1. and calls upsert. The tax module may correctly update the previous tax rules so they are no longer soft deleted. However, upsert (at least not by default) doesn't delete the new rules that were created in 2.
As illustrated by 5. compensating the update is not pretty. To get around this I instead opted to let the workflow handle setting the rules for a rate that makes the compensation more straightforward to handle. [See workflow here](https://github.com/medusajs/medusa/pull/6557/files#diff-ff19e1f2fa32289aefff90d33c05c154f9605a3c5da6a62683071a1fcaedfd7bR89).
**Using nested workflows**
Initially, I wanted to use the `setTaxRateRulesWorkflow` within the `updateTaxRatesWorkflow`. And this worked great for the invoke phase. However, when I needed to compensate the update workflow (and hence also had to compensate the set rules workflow), I found that the workflow engine no longer had the set rules transaction in memory and therefore could not roll it back. ([This is where I try to rollback](https://github.com/medusajs/medusa/pull/6557/files#diff-ff19e1f2fa32289aefff90d33c05c154f9605a3c5da6a62683071a1fcaedfd7bR62), but the transaction id can't be found).
I therefore opted to copy the steps from the set tax rate rules workflow into the update tax rates workflow; however, once we figure out a good way to ensure we can compensate nested workflows we should move to the nested workflow instead.
This also made me realize that the current implementation of workflows that use `refreshCartPromotions` may create inconsistencies in case of failures (cc: @riqwan).
I ultimately went with having a flat list of settings for the store. It wouldn't be very difficult to change it if we wish to do so, but for now this keeps the codebase simpler
**What**
- Implements new Region domain design
- Adds new SplitView component for managing adding nested relations in FocusModals, eg. adding countries to a region.
- Adds new Combobox component for multi select fields in forms
**medusajs/ui**
- Fix styling of RadioGroup.Choicebox component
CLOSES CORE-1650, CORE-1671
what:
- promotion modules (application method and campaign budget) uses big number fields instead of numeric fields
- refreshes the migrations to include new fields for big numbers
- adds a promotion_ prefix to promotion models
- uses `take: null` within the module to prevent the default pagination on performing actions within the module
what:
- add util to transform get response to an update request
RESOLVES CORE-1687
Given an update data object, we can infer the `fields` and `selects` of a data object using a util we already have - `getSelectsAndRelationsFromObjectArray`. Using the `fields` and `selects`, we can call either the module `retrieve` or `list` method to get a snapshot of the data down to its exact attributes. We can pass this data into the revert step.
In the revert step, we just need to convert the snapshot received from `retrieve` or `list` to a shape that the `update` methods will accept. The util that is introduced in this PR aims to do exactly that, so that the revert step looks as simple as:
```
const { snapshotData, selects, relations } = revertInput
await promotionModule.updateCampaigns(
convertItemResponseToUpdateRequest(snapshotData, selects, relations)
)
```
entity before update:
```
Campaign: {
id: "campaign-test-1",
name: "test campaign",
budget: {
total: 2000
},
promotions: [{ id: "promotion-1" }],
rules: [
{ id: "rule-1", operator: "gt", value: "10" }
]
}
```
This is how the util will transform the data for different types of attributes in the object:
simple attributes:
```
invoke:
{
id: "campaign-test-1",
name: "change name",
}
compensate:
{
id: "test-1",
name: "test campaign"
}
```
one to one relationship:
```
invoke:
{
id: "campaign-test-1",
budget: { total: 4000 }
}
compensate:
{
id: "campaign-test-1",
budget: { total: 2000 }
}
```
one to many / many to many relationship:
```
invoke:
{
id: "campaign-test-1",
promotions: [{ id: "promotion-2" }]
rules: [
{ id: "rule-1", operator: "gt", value: "20" },
{ operator: "gt", value: "20" }
]
}
compensate:
{
id: "campaign-test-1",
promotions: [{ id: "promotion-1" }]
rules: [{ id: "rule-1", operator: "gt", value: "20" }]
}
```
all together:
```
invoke:
{
id: "campaign-test-1",
name: "change name",
promotions: [{ id: "promotion-2" }],
budget: { total: 4000 },
rules: [
{ id: "rule-1", operator: "gt", value: "20" },
{ operator: "gt", value: "20" }
]
}
compensate:
{
id: "test-1",
name: "test campaign",
promotions: [{ id: "promotion-1" }],
budget: { total: 2000 },
rules: [{ id: "rule-1", operator: "gt", value: "20" }],
}
```
**What**
- Re add the ability to use the original column name as part of a select in both select in and joined strategy
- The undefined error message will now display the original column name instead of the underscored one
**What**
- Ensures that country and province codes are always stored as lower case.
- Ensures that calculation context normalizes input before getting region rates.
**What**
Should be able to list the shipping options with or without a context, when a context is provided all the rules of the shipping options must be valid for the shipping options to be returned.
FIXES CORE-1765