> For the complete documentation index, see [llms.txt](https://kopens.gitbook.io/plantpulse-platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kopens.gitbook.io/plantpulse-platform/developer/api-manual/order-service.md).

# 12. Order 서비스

`client.order()` 으로 접근합니다. 작업지시(Work Order)의 **생성 → 시작 → 중단/재개 → 종료**까지 전 생명주기를 ISA-88 기반 상태 머신으로 관리합니다.

## 11.1 작업지시 상태 (ISA-88 기반)

```
CREATE → WAIT ──start()──► START ──pause()──► PAUSE
                            │                  │
                            │                  resume()
                            │                  │
                            ▼                  ▼
                           END ◄──end()──── START
                            │
                            └─ (또는) ──abort()──► ABORTED
```

| 상태        | 의미               | 전이 메서드                |
| --------- | ---------------- | --------------------- |
| `WAIT`    | 대기 (생성 직후 초기 상태) | `create()` 결과         |
| `START`   | 진행중              | `start()`, `resume()` |
| `PAUSE`   | 일시중지             | `pause()`             |
| `END`     | 정상 완료            | `end()`               |
| `ABORTED` | 비정상 종료           | `abort()`             |

> 상태 전이가 불가능한 호출(예: 이미 `START` 상태에서 `start()` 재호출)은 서버가 OK + `data.success=false` 응답을 보내며, V5 메서드는 `false` 를 반환합니다.

## 11.2 메서드 일람

### CRUD

| 메서드                                | 반환 타입                       | HTTP   | 엔드포인트                       |
| ---------------------------------- | --------------------------- | ------ | --------------------------- |
| `create(OrderRequestV5)`           | `OrderResponseV5` 또는 `null` | POST   | `/api/v5/order`             |
| `update(order_id, OrderRequestV5)` | `OrderResponseV5` 또는 `null` | PUT    | `/api/v5/order/{id}`        |
| `delete(order_id)`                 | `boolean`                   | DELETE | `/api/v5/order/{id}`        |
| `get(order_id)`                    | `OrderResponseV5` 또는 `null` | GET    | `/api/v5/order/{id}`        |
| `list()`                           | `List<OrderResponseV5>`     | GET    | `/api/v5/order`             |
| `exists(order_id)`                 | `boolean`                   | GET    | `/api/v5/order/{id}/exists` |

### 라이프사이클 (모두 `boolean` 반환)

| 메서드                | HTTP | 엔드포인트                       |
| ------------------ | ---- | --------------------------- |
| `start(order_id)`  | POST | `/api/v5/order/{id}/start`  |
| `end(order_id)`    | POST | `/api/v5/order/{id}/end`    |
| `pause(order_id)`  | POST | `/api/v5/order/{id}/pause`  |
| `resume(order_id)` | POST | `/api/v5/order/{id}/resume` |
| `abort(order_id)`  | POST | `/api/v5/order/{id}/abort`  |

## 11.3 OrderRequestV5 / OrderResponseV5

| 필드                    | 타입     | 설명                |
| --------------------- | ------ | ----------------- |
| `order_id`            | String | 작업지시 ID (PK)      |
| `title`               | String | 작업 제목             |
| `description`         | String | 설명                |
| `site_id`             | String | 소속 사이트            |
| `asset_id`            | String | 대상 자산 (Equipment) |
| `customer_id`         | String | 고객 (선택)           |
| `product_id`          | String | 생산할 제품            |
| `employee_id`         | String | 담당 직원 (선택)        |
| `target_units`        | long   | 목표 수량             |
| `unit`                | String | 단위 (EA, KG 등)     |
| `time_per_unit_in_ms` | long   | 단위당 표준 시간         |
| `fix_start_timestamp` | long   | 계획 시작 시각 (밀리초)    |
| `fix_end_timestamp`   | long   | 계획 종료 시각 (밀리초)    |
| `external_order_id`   | String | 외부 시스템 주문 ID      |
| `status`              | String | (응답 전용) 현재 상태     |

## 11.4 사용 예시

### 작업지시 생성

```java
import plantpulse.api.v5.dto.request.OrderRequestV5;
import plantpulse.api.v5.dto.response.OrderResponseV5;

OrderRequestV5 req = new OrderRequestV5();
req.setOrder_id("ORD_20260514_001");
req.setTitle("5월 14일 1교대 생산");
req.setDescription("PROD_A001 500개 생산");
req.setSite_id("SITE_DJ");
req.setAsset_id("ASSET_DJ_M_0001");
req.setProduct_id("PROD_A001");
req.setCustomer_id("CUST_001");
req.setEmployee_id("EMP_E12345");
req.setTarget_units(500);
req.setUnit("EA");
req.setTime_per_unit_in_ms(5_000);

long now = System.currentTimeMillis();
req.setFix_start_timestamp(now);
req.setFix_end_timestamp(now + 4 * 3600_000L);
req.setExternal_order_id("MES-2026-0514-001");

OrderResponseV5 created = client.order().create(req);
```

### 표준 라이프사이클

```java
String orderId = "ORD_20260514_001";

// WAIT → START
if (!client.order().start(orderId)) {
    System.err.println("시작 실패 — fix_start_timestamp가 미래이거나 이미 진행중");
    return;
}

// 잠시 후 — 일시중지
client.order().pause(orderId);

// 재개
client.order().resume(orderId);

// 정상 종료
client.order().end(orderId);
```

### 비정상 종료

```java
// abort 는 별도 인자 없이 호출. 상세 사유는 update() 로 description/notes 에 기록.
if (!client.order().abort("ORD_20260514_001")) {
    System.err.println("중단 실패");
}
```

### 단건 조회 / 존재 확인

```java
OrderResponseV5 o = client.order().get("ORD_20260514_001");
if (o != null) {
    System.out.println("상태: " + o.getStatus());
    System.out.println("목표: " + o.getTarget_units() + " " + o.getUnit());
}

boolean exists = client.order().exists("ORD_20260514_001");
```

### 전체 목록

```java
import java.util.List;

List<OrderResponseV5> all = client.order().list();
for (OrderResponseV5 o : all) {
    System.out.printf("[%s] %s — %s%n",
        o.getStatus(), o.getOrder_id(), o.getTitle());
}
```

### 진행중 작업지시만 (클라이언트 측 필터)

```java
import java.util.stream.Collectors;

List<OrderResponseV5> active = client.order().list().stream()
        .filter(o -> "START".equals(o.getStatus())
                  || "PAUSE".equals(o.getStatus()))
        .collect(Collectors.toList());
```

### 자산별 작업지시

```java
String assetId = "ASSET_DJ_M_0001";
List<OrderResponseV5> assetOrders = client.order().list().stream()
        .filter(o -> assetId.equals(o.getAsset_id()))
        .collect(Collectors.toList());
```

### 상태별 통계

```java
import java.util.Map;

Map<String, Long> byStatus = client.order().list().stream()
        .collect(Collectors.groupingBy(
                OrderResponseV5::getStatus,
                Collectors.counting()));

byStatus.forEach((status, count) ->
    System.out.printf("%s: %d건%n", status, count));
```

### 수정 / 삭제

```java
// 수정 (계획 시간 연장)
OrderRequestV5 update = new OrderRequestV5();
update.setOrder_id("ORD_20260514_001");
update.setTitle("5월 14일 1교대 생산 (연장)");
// ... 모든 필드 다시 세팅 ...
update.setFix_end_timestamp(System.currentTimeMillis() + 8 * 3600_000L);
client.order().update("ORD_20260514_001", update);

// 삭제 (취소 처리)
client.order().delete("ORD_20260514_001");
```

## 11.5 활용 시나리오

### 시나리오 — MES 연동 작업지시 자동 생성

```java
import java.text.SimpleDateFormat;
import java.util.Date;

SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String today = df.format(new Date());

// 외부 MES 큐에서 받은 주문을 V5 Order로 변환
for (MesOrder mes : mesQueue) {
    OrderRequestV5 req = new OrderRequestV5();
    req.setOrder_id("ORD_" + today + "_" + String.format("%03d", mes.seq));
    req.setSite_id("SITE_DJ");
    req.setAsset_id(mes.equipId);
    req.setProduct_id(mes.productId);
    req.setTarget_units(mes.qty);
    req.setExternal_order_id(mes.mesOrderId);
    req.setFix_start_timestamp(mes.plannedStart);
    req.setFix_end_timestamp(mes.plannedEnd);
    client.order().create(req);
}
```

### 시나리오 — 안전한 상태 전이 헬퍼

```java
public static boolean safeStart(APIClient_V5 client, String orderId) {
    OrderResponseV5 o = client.order().get(orderId);
    if (o == null) {
        log.warn("작업지시 없음: " + orderId);
        return false;
    }
    if (!"WAIT".equals(o.getStatus())) {
        log.warn("WAIT 상태가 아님: " + o.getStatus());
        return false;
    }
    return client.order().start(orderId);
}
```

## 다음 단계

* [Customer 서비스](/plantpulse-platform/developer/api-manual/customer-service.md), [Employee 서비스](/plantpulse-platform/developer/api-manual/employee-service.md), [Product 서비스](/plantpulse-platform/developer/api-manual/product-service.md) — Order에 부착되는 마스터 데이터
* [Calendar 서비스](/plantpulse-platform/developer/api-manual/calendar-service.md) — 작업지시와 점검 일정 충돌 검사
