# Edara API v3: Sales Stores

Read the [guide](../guide.md) first: authentication, paging, errors, rate limits and the shared `PagedResult`, `BatchResult` and `*Reference` shapes.

- `GET /v3/sales-stores`: Lists sales stores.
- `POST /v3/sales-stores`: Creates a sales store.

## Endpoints

### `GET /v3/sales-stores`: Lists sales stores.

Operation `GetSalesStores` · permission `read:sales-store`

**Query string**: `GetSalesStoresQuery`

| field | type | default | validation | description |
|---|---|---|---|---|
| `offset` | `int?` | 0 | >= 0 | Number of items to skip before returning results. Defaults to 0. |
| `limit` | `int?` | 100 | 1..1000 (out of range → 400) | Maximum number of items to return. Defaults to 100, maximum 1000. |
| `updateDate` | `DateTime?` |  |  | Returns records created or updated after the specified date. |

**Responses**: OK `PagedResult<SalesStoreResponse>`: Paged list of sales stores.

**Notes**

- The only filter is `updateDate`. It returns stores created or updated strictly after that time, and without it all stores are returned.
- Inactive stores are included.
- Results are ordered by id and paged with `offset` and `limit`.
- No match returns `200` with an empty `items`, not `404`.
- An `offset` past the last store returns `totalCount` 0, not the real total.

---

### `POST /v3/sales-stores`: Creates a sales store.

Operation `CreateSalesStore` · permission `create:sales-store`

**Body**: `SalesStoreUpsertRequest`

| field | type | default | validation | description |
|---|---|---|---|---|
| `code` | `string` |  | NotEmpty() .MaximumLength(50) | Sales-store code. |
| `description` | `string` |  | NotEmpty() .NotNull() .MaximumLength(50) | Sales-store description. |
| `defaultCustomerId` | `int?` |  | GreaterThan(0) .When(x => x.DefaultCustomerId.HasValue) | Optional default customer id. |
| `defaultCostCenterId` | `int?` |  | GreaterThan(0) .When(x => x.DefaultCostCenterId.HasValue) | Optional default cost-center id. |
| `tags` | `string[]` |  | each: NotEmpty() .MaximumLength(200) .When(x => x.Tags != null) | Optional tags. |

**Responses**: Created `SalesStoreResponse`: Sales store created.

**Business errors** (HTTP 409, match on `errorCode`):

- `DupplicatedCode`: Another sales store already uses the same code.
- `DupplicatedName`: Another sales store already uses the same description.

**Notes**

- Values in `tags` are trimmed.
- Commas are removed from `description` when it is saved. The `201` response still shows the text you sent.
- A new store is always active. The user that creates it is automatically given data permission for it.
- A duplicate `code` returns `409` with `errorCode` `DupplicatedCode`. A duplicate `description` returns `409` with `errorCode` `DupplicatedName`.
- A `defaultCustomerId` or `defaultCostCenterId` that does not exist is not reported as not found. It returns `400` with a generic message.

## Types

### `CostCenterReference`

Lightweight cost-center reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The cost-center id. |
| `code` | `string` | The cost-center code. |
| `name` | `string` | The cost-center name. |

### `CustomerReference`

Lightweight customer reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The customer id. |
| `code` | `string` | The customer code. |
| `name` | `string` | The customer name. |

### `SalesStoreResponse`

Response describing a sales store.

| field | type | description |
|---|---|---|
| `id` | `int` | Sales-store identifier. |
| `code` | `string` | Sales-store code. |
| `description` | `string` | Sales-store description. |
| `defaultCustomer` | `CustomerReference` | Optional default customer reference. |
| `defaultCostCenter` | `CostCenterReference` | Optional default cost-center reference. |
| `isActive` | `bool` | Whether the sales store is active. |
| `tags` | `string[]` | Optional tags. |
