# Order Replacement and Weight Change Actions

This document describes all order-level actions related to **product replacement**, **weight changes**, **capture**, and **additional payment handling**.

Each action includes endpoint definitions, request/response structures, validation rules, business constraints, audit behavior, and configuration dependencies.

## <mark style="color:red;">1. Capture Order</mark>

This action is executed **only once per order**.

It captures the order’s previously authorized transaction. This operation converts the prior authorization obtained from the customer into an actual payment.

#### Endpoint

```
POST /api/v1/orders/{order_id}/capture_order/
```

#### Request Example

```http
POST /api/v1/orders/12345/capture_order/
Authorization: Token your_token_here
Content-Type: application/json

{
  "force_refund": false
}
```

#### Request Parameters

* `force_refund` (boolean, optional, default: `false`)
  * If the order already has a **purchase transaction**, setting this value to `true` triggers a refund for the excess amount and applies a capture-like behavior.

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

(Empty response body)
```

#### Business Rules

1. This action **must be executed only once** per order.
2. If the order has an **authorized transaction**, the order status **cannot** be `waiting_for_substitute`.
   * Otherwise, `FinalizeCaptureWaitingPaymentException` is raised.
3. Concurrent capture attempts are blocked.
   * A cache lock prevents multiple captures on the same order for **60 seconds**.
4. The order total amount is validated via:

   ```
   order.validate_total_amount()
   ```
5. The order must have either:
   * an **authorize transaction**, or
   * a **purchase transaction**
   * Otherwise, `OrderCaptureException` is raised.
6. If `force_refund = true` and the order already has a purchase transaction:
   * The excess amount of the purchase transaction is refunded.
7. If the order status is `confirmation_waiting`, it is updated to `approved`.
8. An audit log entry is created with action:

   ```
   order_capture
   ```

#### ENV & Dynamic Settings

* **ENV**: None
* **Dynamic Settings**: None

***

## <mark style="color:red;">2. Bulk Replace Products</mark>

It bulk replaces the products of multiple order items in an order.&#x20;

The existing order items are canceled, and new order items are created with the new products.

#### Endpoint

```
POST /api/v1/orders/{order_id}/bulk_replace_products/
```

#### Request Example

```http
POST /api/v1/orders/12345/bulk_replace_products/
Authorization: Token your_token_here
Content-Type: application/json

[
  {
    "order_item": 100,
    "new_product_sku": "NEW_PRODUCT_SKU_123"
  },
  {
    "order_item": 101,
    "new_product_sku": "NEW_PRODUCT_SKU_456"
  }
]
```

#### Request Parameters

* `order_item` (integer, optional): ID of the order item to be replaced.
* `new_product_sku` (string, optional): SKU of the replacement product.

**Validation Note**

* At least one of `order_item` or `new_product_sku` **must** be provided.
* If both are missing, serializer validation fails.

#### Response Example

```json
{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "approved",
  "amount": "1500.00",
  "currency": "TRY",
  "orderitem_set": [
    {
      "pk": 200,
      "price": "1500.00",
      "status": "approved"
    }
  ]
}
```

#### Business Rules

1. Each request item must include at least one of the `order_item` or `new_product_sku` parameters. If neither is provided, a serializer validation error is raised.
2. The order status must be one of the following:
   * `confirmation_waiting`
   * `approved`
   * `preparing`\
     Otherwise, `OrderItemChangeOrderNotAllowedException` is raised.
3. The order’s channel type must be `web`. Otherwise, `OrderItemChangeOrderNotAllowedException` is raised.
4. The order transaction is validated (`validate_transaction()`).
5. The order must not have been sent to the ERP system (`is_send=False`). Otherwise, `OrderItemReplacementOrderIsSendException` is raised.
6. For each order item:
   * The order item must not have an active cancellation plan.
   * The order item status must be one of: `waiting`, `payment_waiting`, `confirmation_waiting`, `approved`, `preparing`.
   * The new product must belong to the same catalog.
   * The new product must be in the same stock list.
   * The stock quantity of the new product must not be less than the order item’s quantity.
   * The stock unit type must be `quantity`.
   * The price of the new product must be greater than the order item’s total benefit share amount.
7. If the new order amount is greater than the previous amount:
   * The `ORDER_ITEM_UPPER_PRICE_ENABLE` dynamic setting must be enabled. Otherwise, `OrderItemPriceExceedsCurrentPriceException` is raised.
   * The order status is updated to `waiting_for_substitute`.
   * A `create_replacement_order` event is sent to Commerce.
8. The existing order items are canceled (`status=cancelled`, `price=0`).
9. New order items are created.
10. The order amount is recalculated.
11. An audit log entry is created with the action:

    ```
    order_change_product
    ```

#### ENV & Dynamic Settings

* **ENV**: None
* **Dynamic Settings**
  * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` (boolean, default: `false`): Determines whether order item product updates are enabled.
  * `ORDER_ITEM_UPPER_PRICE_ENABLE` (boolean, default: `false`): Allows the new product price to be higher than the existing price.

***

## <mark style="color:red;">3. Bulk Reduce Weights</mark>

It bulk reduces the weights of multiple order items in an order. This operation is used for products sold by kilogram.

This action **does not allow weight increase**.

#### Endpoint

```
POST /api/v1/orders/{order_id}/bulk_reduce_weights/
```

#### Request Example

```http
POST /api/v1/orders/12345/bulk_reduce_weights/
Authorization: Token your_token_here
Content-Type: application/json

[
  {
    "order_item": 100,
    "new_weight": 2.5
  },
  {
    "order_item": 101,
    "new_weight": 1.8
  }
]
```

#### Request Parameters

* `order_item` (integer, required): The ID of the order item whose weight will be changed.
* `new_weight` (decimal, required): The new weight value (in kilograms).

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "approved",
  "amount": "1200.00",
  "currency": "TRY",
  "orderitem_set": [
    {
      "pk": 100,
      "price": "1200.00",
      "attributes": {
        "unit_weight": "2.5",
        "old_unit_weight": "3.0"
      },
      ...
    }
  ],
  ...
}
```

#### Business Rules

1. The `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` dynamic setting must be enabled **OR** the `ORDER_ITEM_WEIGHT_KEY` setting must be defined.\
   Otherwise, `OrderItemReplacementNotEnabledException` is raised.
2. The order transaction is validated by calling `validate_transaction()`.
3. For each order item:
   * The order item must not have an active cancellation plan.
   * The order item status must be one of the following:\
     `waiting`, `payment_waiting`, `confirmation_waiting`, `approved`, `preparing`.
   * The stock unit type must be `kilogram`.
   * The `ORDER_ITEM_WEIGHT_KEY` key must exist in the order item’s `attributes` field.
   * The new weight must be different from the previous weight.
4. The price is recalculated based on the weight change ratio:
   * `new_price = old_price * (new_weight / old_weight)`
   * If `new_weight = 0`, the order item price is set to `0`, and all benefit applicants are cancelled.
5. The order item’s `attributes` field is updated as follows:
   * `old_{ORDER_ITEM_WEIGHT_KEY}`: stores the previous weight
   * `{ORDER_ITEM_WEIGHT_KEY}`: stores the new weight
6. If the new order total amount is greater than the previous total:
   * The `ORDER_ITEM_UPPER_PRICE_ENABLE` dynamic setting must be enabled.\
     Otherwise, `OrderItemPriceExceedsCurrentPriceException` is raised.
   * The order status is updated to `waiting_for_substitute`.
   * A `create_replacement_order` event is sent to Commerce.
7. The order total amount is recalculated.
8. An audit log entry is created with the action:

   ```
   bulk_order_item_change_weight
   ```

#### ENV & Dynamic Settings

* **ENV**
  * `ORDER_ITEM_WEIGHT_KEY` (string, default: `None`): The key name used to store weight information in the order item’s `attributes` field (e.g. `"unit_weight"`).
* **Dynamic Settings**
  * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` (boolean, default: `False`): Determines whether order item updates are enabled.
  * `ORDER_ITEM_UPPER_PRICE_ENABLE` (boolean, default: `False`): Allows the price calculated from the new weight to be higher than the previous price.

***

## <mark style="color:red;">4. Bulk Replace Product and Reduce Weight</mark>

Replaces the products of multiple order items in an order and/or reduces their weights.\
This action allows **both product replacement and weight reduction** to be performed within the same request.

#### Endpoint

```
POST /api/v1/orders/{order_id}/bulk_replace_product_and_reduce_weight/
```

#### Request Example

```http
POST /api/v1/orders/12345/bulk_replace_product_and_reduce_weight/
Authorization: Token your_token_here
Content-Type: application/json

[
  {
    "order_item": 100,
    "new_product_sku": "NEW_PRODUCT_SKU_123"
  },
  {
    "order_item": 101,
    "new_weight": 2.5
  }
]
```

#### Request Parameters

* `order_item` (integer, optional): The ID of the order item to be updated.
* `new_product_sku` (string, optional): The SKU of the new product (for product replacement).
* `new_weight` (decimal, optional): The new weight value (for weight reduction).

**Notes**:

* `new_product_sku` and `new_weight` cannot be provided at the same time.\
  If both are provided, a validation error is raised.
* At least one of `new_product_sku`, `new_weight`, or `order_item` must be provided.\
  If none are provided, a validation error is raised.

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "approved",
  "amount": "1500.00",
  "currency": "TRY",
  "orderitem_set": [...],
  ...
}
```

#### Business Rules

1. Each request item must include at least one of the following parameters:\
   `new_product_sku`, `new_weight`, or `order_item`.\
   If none are provided, a serializer validation error is raised.
2. `new_product_sku` and `new_weight` cannot be provided together in the same request item.\
   If both are provided, a serializer validation error is raised.
3. The `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` dynamic setting must be enabled **OR** the `ORDER_ITEM_WEIGHT_KEY` setting must be defined.\
   Otherwise, `OrderItemReplacementNotEnabledException` is raised.
4. The order transaction is validated by calling `validate_transaction()`.
5. For each order item:
   * If `new_product_sku` is provided, **all business rules of the `bulk_replace_products` action apply**.
   * If `new_weight` is provided, **all business rules of the `bulk_reduce_weights` action apply**.
6. Product replacement and weight reduction can both be applied to the **same order item**, executed sequentially.
7. If the new order total amount is greater than the previous total:
   * The `ORDER_ITEM_UPPER_PRICE_ENABLE` dynamic setting must be enabled.\
     Otherwise, `OrderItemPriceExceedsCurrentPriceException` is raised.
   * The order status is updated to `waiting_for_substitute`.
   * A `create_replacement_order` event is sent to Commerce.
8. The order total amount is recalculated.
9. An audit log entry is created with the action:

   ```
   order_bulk_replace_product_and_change_weight
   ```

#### ENV and Dynamic Settings

* **ENV**
  * `ORDER_ITEM_WEIGHT_KEY` (string, default: `None`): The key name used to store weight information in the order item’s `attributes` field.
* **Dynamic Settings**
  * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` (boolean, default: `False`): Determines whether order item updates are enabled.
  * `ORDER_ITEM_UPPER_PRICE_ENABLE` (boolean, default: `False`): Allows the new price to be higher than the previous price.

***

## <mark style="color:red;">5. Bulk Change Weight</mark>

Updates the weights of multiple order items within an order in bulk (the weight can be **increased or decreased**).

The difference from the `bulk_reduce_weights` action is that this action **also allows weight increases**.

#### Endpoint

```
POST /api/v1/orders/{order_id}/bulk_change_weight/
```

#### Request Example

```http
POST /api/v1/orders/12345/bulk_change_weight/
Authorization: Token your_token_here
Content-Type: application/json

[
  {
    "order_item": 100,
    "new_weight": 3.5
  },
  {
    "order_item": 101,
    "new_weight": 2.0
  }
]
```

#### Request Parameters

* `order_item` (integer, required): The ID of the order item whose weight will be changed.
* `new_weight` (decimal, required): The new weight value (in kilograms).

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "waiting_for_substitute",
  "amount": "1800.00",
  "currency": "TRY",
  "orderitem_set": [
    {
      "pk": 100,
      "price": "1800.00",
      "attributes": {
        "unit_weight": "3.5",
        "old_unit_weight": "2.5"
      },
      ...
    }
  ],
  ...
}
```

#### Business Rules

1. The `ORDER_ITEM_UPPER_PRICE_ENABLE` dynamic setting **must be enabled**.\
   Otherwise, `OrderItemPriceExceedsCurrentPriceException` is raised.
2. The `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` dynamic setting must be enabled **OR** the `ORDER_ITEM_WEIGHT_KEY` setting must be defined.\
   Otherwise, `OrderItemReplacementNotEnabledException` is raised.
3. The order transaction is validated by calling `validate_transaction()`.
4. **All business rules of the `bulk_reduce_weights` action apply**.
5. The weight may be increased or decreased, but it **must be different from the previous weight**.
6. If the new order total amount is greater than the previous total:
   * The order status is updated to `waiting_for_substitute`.
   * A `create_replacement_order` event is sent to Commerce.
7. The order total amount is recalculated.
8. An audit log entry is created with the action:

   ```
   bulk_order_item_change_weight
   ```

#### ENV and Dynamic Settings

* **ENV**
  * `ORDER_ITEM_WEIGHT_KEY` (string, default: `None`): The key name used to store weight information in the order item’s `attributes` field.
* **Dynamic Settings**
  * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` (boolean, default: `False`): Determines whether order item updates are enabled.
  * `ORDER_ITEM_UPPER_PRICE_ENABLE` (boolean, default: `False`): **MANDATORY** – must be enabled for this action.

***

## <mark style="color:red;">6. Bulk Replace Product and Change Weight</mark>

Allows replacing products and/or changing the weight of **multiple order items** within an order.\
Unlike the `bulk_replace_product_and_reduce_weight` action, this endpoint **allows both increasing and decreasing the weight**.

#### Endpoint

```http
POST /api/v1/orders/{order_id}/bulk_replace_product_and_change_weight/
```

#### Request Example

```http
POST /api/v1/orders/12345/bulk_replace_product_and_change_weight/
Authorization: Token your_token_here
Content-Type: application/json

[
  {
    "order_item": 100,
    "new_product_sku": "NEW_PRODUCT_SKU_123"
  },
  {
    "order_item": 101,
    "new_weight": 3.5
  }
]
```

#### Request Parameters

* `order_item` (integer, **required**): ID of the order item to be updated
* `new_product_sku` (string, optional): SKU of the new product to replace the existing one
* `new_weight` (decimal, optional): New weight value for the order item

**Validation Rules**

* `order_item` is **required** for each request item.
* At least **one of** `new_product_sku` or `new_weight` must be provided.
* `new_product_sku` and `new_weight` **cannot be provided together** in the same request item.
  * If both are provided, a serializer validation error is raised.
* `new_weight` must be **different from the current weight** (both increase and decrease are allowed).

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "waiting_for_substitute",
  "amount": "2000.00",
  "currency": "TRY",
  "orderitem_set": [...],
  ...
}
```

#### Business Rules

1. The `ORDER_ITEM_UPPER_PRICE_ENABLE` dynamic setting **must be enabled**.
   * Otherwise, `OrderItemPriceExceedsCurrentPriceException` is raised.
2. At least **one** of the following conditions must be met:
   * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` dynamic setting is enabled
   * `ORDER_ITEM_WEIGHT_KEY` ENV setting is defined
   * Otherwise, `OrderItemReplacementNotEnabledException` is raised.
3. The order transaction is validated (`validate_transaction()`).
4. All business rules defined for the `bulk_replace_product_and_reduce_weight` action **also apply**.
5. The order total amount is recalculated.
6. If the new order amount is **greater than** the previous amount:
   * Order status is updated to `waiting_for_substitute`.
   * A `create_replacement_order` event is sent to Commerce.
7. An audit log is created after the operation
   * Action name: `order_bulk_replace_product_and_change_weight`

#### ENV and Dynamic Settings

* **ENV**
  * `ORDER_ITEM_WEIGHT_KEY` (string, default: `None`): Key name used to store weight information in the order item’s `attributes` field
* **Dynamic Settings**
  * `ORDER_ITEM_PRODUCT_UPDATE_AVAILABLE` (boolean, default: `False`): Determines whether order item updates are enabled
  * `ORDER_ITEM_UPPER_PRICE_ENABLE` (boolean, default: `False`): **MANDATORY** – Must be enabled for this action

***

## <mark style="color:red;">7. Waive Additional Payment</mark>

Cancels a pending **additional payment (pay later)** request for an order and updates the order status to `preparing`.

This action is used when an order is waiting for an additional payment from the customer, but the payment request is waived.

#### Endpoint

```http
POST /api/v1/orders/{order_id}/waive_additional_payment/
```

#### Request Example

```http
POST /api/v1/orders/12345/waive_additional_payment/
Authorization: Token your_token_here
Content-Type: application/json

(Empty request body)
```

#### Request Parameters

This action does **not** require a request body.

#### Response Example

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "pk": 12345,
  "number": "ORD-2024-001",
  "status": "preparing",
  "amount": "1500.00",
  "currency": "TRY",
  "channel": {...},
  "customer": {...},
  "orderitem_set": [...],
  ...
}
```

#### Business Rules

* The order status **must be** `waiting_for_substitute`.
  * Otherwise, `PayLaterInfoInvalidOrderStatusException` is raised.
* The order must have an **active `PayLaterInfo` record** with `status=payment_waiting`.
  * If not found, `PayLaterInfoNotFoundFromOrderException` is raised.
* The order status is updated to `preparing`.
* The related `PayLaterInfo` record is updated to `waived`.
* The operation is executed within an **atomic transaction**.

#### ENV and Dynamic Settings

* **ENV**: None
* **Dynamic Settings**: None

***

## <mark style="color:red;">General Notes</mark>

### <mark style="color:red;">Error Handling</mark>

All actions may raise the following exceptions under relevant conditions:

* `OrderItemHasActiveCancellationPlanException`
  * The order item has an active cancellation plan.
* `OrderItemReplacementNotAllowedException`
  * Order item replacement is not allowed.
* `OrderItemReplacementOrderIsSendException`
  * The order has already been sent.
* `OrderItemPriceExceedsCurrentPriceException`
  * The new price exceeds the current price and `ORDER_ITEM_UPPER_PRICE_ENABLE` is disabled.
* `OrderCaptureException`
  * The order transaction cannot be captured.
* `PayLaterInfoNotFoundFromOrderException`
  * No pay later information exists for the order.

### <mark style="color:red;">Audit Logs</mark>

All actions create an **audit log** entry. These logs are used to track and audit order-related operations.

### <mark style="color:red;">Channel Integration</mark>

Some actions (product replacement, weight change) send events to Commerce:

* `order_item_create` – When a new order item is created
* `order_item_update` – When an order item is updated
* `order_update` – When the order is updated
* `create_replacement_order` – When an additional payment is required

### <mark style="color:red;">Transaction Validation</mark>

All replacement-related actions validate the order transaction:

* The order must **not** be captured
* The transaction must be in **authorize** or **purchase** state
