Skip to main content

12. Order Service

Access via client.order(). It manages the entire lifecycle of a Work Order—creation → start → stop/resume → end—using an ISA-88 based state machine.

11.1 Work Order Status (ISA-88 Based)​

CREATE → WAIT ──start()──► START ──pause()──► PAUSED
│ │
│ resume()
│ │
▼ ▼
END ◄──end()──── START
│
└─ (또는) ──abort()──► ABORTED
Status (status)MeaningTransition MethodISA-95 Status (isa95_state)
WAITWaiting (Initial state after creation)create() resultReady
STARTIn Progressstart(), resume()Running
PAUSEDPausedpause()Held
ENDNormal Completionend()Completed
ABORTEDAbnormal Terminationabort()Aborted

status is stored exactly as the value and consists of only these five. A work order canceled without starting is not a new state, but ABORTED + abort_code=CANCELLED. Work order rows (/order·List·/order/query), performance documents, and plantpulse.order.* events append the ISA-95 Part 4 work order status isa95_state—the mapping is the single table above, and all paths yield the same value.

For calls where state transition is impossible (e.g., calling start() again while already in START state), the server sends an OK + data.success=false response, and the V5 method returns false.

11.2 Method List​

CRUD​

MethodReturn TypeHTTPEndpoint
create(OrderRequestV5)OrderResponseV5 or nullPOST/api/v5/order
update(order_id, OrderRequestV5)OrderResponseV5 or nullPUT/api/v5/order/{id}
delete(order_id)booleanDELETE/api/v5/order/{id}
get(order_id)OrderResponseV5 or nullGET/api/v5/order/{id}
list()List<OrderResponseV5>GET/api/v5/order
exists(order_id)booleanGET/api/v5/order/{id}/exists

Lifecycle (All return boolean)​

MethodHTTPEndpoint
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​

FieldTypeDescription
order_idStringWork Order ID (PK)
order_nameStringWork Order Name — Required, cannot be empty (422 order.name.required). Name of B2MML JobOrder
descriptionStringDescription
site_idStringAffiliated Site
asset_idStringTarget equipment
customer_idStringCustomer (Optional)
product_idStringProduct to produce
employee_idStringAssigned employee (Optional)
planned_quantityQuantityPlanned quantity — ISA-95 / B2MML QuantityValueType. See 11.5 Quantity below
time_per_unit_in_mslongStandard time per unit
planned_start_timestamplongPlanned start time (milliseconds)
planned_end_timestamplongPlanned end time (milliseconds)
external_idStringWork order number of external system (MES/ERP) (up to 100 chars). Identified as a pair with external_system — 11.6
external_systemStringName of the external system that issued the work order (up to 50 chars) — other half of the external identification pair
customer_po_idStringCustomer Purchase Order (PO) number corresponding to this work order (Optional)
statusString(Response only) Current status — stored values WAIT·START·PAUSED·END·ABORTED
isa95_stateString(Response only) ISA-95 work order status — Ready·Running·Held·Completed·Aborted (11.1)
Name changed in 2026.11 — No alias

title → order_name, fix_start_timestamp·fix_end_timestamp → planned_start_timestamp·planned_end_timestamp, external_order_id → external_id, external_po_id → customer_po_id. Old names are not read in requests and do not appear in responses — Upgrade Notes.

11.4 Usage Examples​

Work Order Creation​

import plantpulse.api.v5.dto.Quantity;
import plantpulse.api.v5.dto.request.OrderRequestV5;
import plantpulse.api.v5.dto.response.OrderResponseV5;

OrderRequestV5 req = new OrderRequestV5();
req.setOrder_id("ORDER_000001");
req.setOrder_name("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.setPlanned_quantity(Quantity.of("500", "EA")); // 값은 10진 «문자열», 단위는 UN/ECE Rec 20 코드
req.setTime_per_unit_in_ms(5_000);

long now = System.currentTimeMillis();
req.setPlanned_start_timestamp(now);
req.setPlanned_end_timestamp(now + 4 * 3600_000L);
req.setExternal_id("MES-2026-0514-001");
req.setExternal_system("MES");

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

Standard Lifecycle​

String orderId = "ORDER_000001";

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

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

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

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

Abnormal Termination​

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

Single Inquiry / Existence Check​

OrderResponseV5 o = client.order().get("ORDER_000001");
if (o != null) {
System.out.println("상태: " + o.getStatus());
Quantity planned = o.getPlanned_quantity(); // {quantity_string, data_type, unit_of_measure}
System.out.println("계획: " + planned.getQuantity_string() + " " + planned.getUnit_of_measure());
java.math.BigDecimal value = planned.toBigDecimal(); // 계산은 BigDecimal 로 — double 로 바꾸지 않는다
}

boolean exists = client.order().exists("ORDER_000001");

Full List​

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.getOrder_name());
}

In-Progress Work Orders Only (Client-side filter)​

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());

Work Orders by equipment​

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

Statistics by Status​

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));

Modification / Deletion​

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

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

11.5 Quantity — ISA-95 Quantity​

The quantity of a work order (planned, production, good, scrap, rework ...) is not a single number, but an object containing both value and unit. The format is 1:1 with QuantityValueType of ISA-95 (IEC 62264) / B2MML V0700.

Version described in this section

Based on V5 API (OpenAPI info.version 5.0) · Quantity agreement 2026-10-30 · Name wave 2026-10-31 · Platform 2026.11 version (server-web 974891b4). Events are based on plantpulse.order.*·plantpulse.calendar.* schema major 2, plantpulse.kpi.oee.finalized major 1, DB is based on Flyway V99·V100·V103 and core migration V12V16·V21V23 — Upgrade Notes.

[
{ "quantity_string": "1250.5", "data_type": "decimal", "unit_of_measure": "KGM" },
{ "quantity_string": "1200", "data_type": "integer", "unit_of_measure": "EA" }
]
FieldB2MMLTypeRule
quantity_stringQuantityStringstringDecimal notation exactly — exponential notation forbidden, decimal point is ., up to four decimal places. Counting units must be integers
data_typeDataTypestringinteger (Counting unit) or decimal (Others). Determined by the server from the unit — can be omitted in requests, but must match the unit if provided
unit_of_measureUnitOfMeasurestringUN/ECE Recommendation 20 common code — allowed list below
Why the value is a string

Because B2MML defines it as QuantityString and most JSON parsers read numbers as double, losing decimal accuracy. In the SDK, read as Quantity.toBigDecimal() for calculations. When creating a request, use Quantity.of("1250.5", "KGM") — if data_type is left empty, the server fills it.

Units — Allowed List (UN/ECE Rec 20)​

CodeMeaningdata_typeScreen Symbol
EAeach — Number of individual itemsintegerEA
H87pieceintegerpc
C62one (unit)integerunit
BXbox — Number of boxesintegerbox
SETset — Number of sets/kitsintegerset
KGMkilogramdecimalkg
GRMgramdecimalg
TNEtonnedecimalt
LTRlitredecimalL
MLTmillilitredecimalmL
MTQcubic metredecimalm³
MTRmetredecimalm
MTKsquare metredecimalm²
  • There are 13 allowed units, and the counting units are EA·H87·C62·BX·SET. Codes outside the list are rejected (quantity.unit_invalid). Codes are compared exactly, including case — ea, KG, PCS are not allowed codes.
  • If a code not in the screen symbols is received (e.g., old data), it is displayed exactly as the code.
  • No unit conversion is performed (unit master and conversion are the responsibility of the MES). Quantities with different units are not added together.
  • Screen symbols are independent of language — on Korean screens, «개» is not used, but EA. Symbols are always attached (including count).

Field Names — ISO 22400​

Field names follow the terminology of ISA-95 and ISO 22400-2 (KPI pair of IEC 62264). Old names no longer appear in responses (no compatibility fields).

MeaningField (Quantity)Old FieldBasis
Planned quantityplanned_quantitytarget_units + unitISA-95 MaterialRequirement.Quantity · ISO 22400 POQ
Production quantityproduced_quantitytotal_production_countISO 22400 PQ
Good quantitygood_quantitytotal_good_count · good_countISO 22400 GQ
Scrap quantityscrap_quantitytotal_bad_count · bad_count — Meaning narrowed: old field was total defect, new field is scrap onlyISO 22400 SQ
Rework quantityrework_quantity(None — new field)ISO 22400 RQ
Off-plan good / defectgood_quantity_out_of_plan / scrap_quantity_out_of_planout_of_plan_good_count / out_of_plan_bad_count
Measured good / defect (PLC)measured_good_quantity / measured_scrap_quantitymeasured_good_count / measured_bad_count
Calibration increase/decreasegood_quantity_delta / scrap_quantity_deltagood_delta / bad_delta
Calibration input valueentered_good_quantity / entered_scrap_quantityentered_good / entered_bad
Inspection quantityinspected_quantityinspected_count
Inspection defect totalinspected_scrap_quantityinspected_bad_count
Defect record by dispositionscrap_quantity / rework_quantityscrap_count / rework_countISO 22400 SQ / RQ

Changing responses: /order, /order/query(include=oee), /order/{id}/oee, /order/{id}/quantity(including revision history), /defects, /order/performance, /kpi/oee/history, /asset/{id}/status, MCP work order tool, production event OEE_CALCULATED.

  • /asset/{id}/status's planned_quantity·good_quantity·scrap_quantity appear as the unit of the in-progress work order. If there is no in-progress work order, it is null.
  • Ratio fields (total_good_rate, production_rate, availability, performance, quality, OEE) are dimensionless and thus remain as numbers.
  • /asset/{id}/status also has rework_quantity (rework of the in-progress work order).
  • PLC counter records (GET /api/v5/kpi/oee/count's good_quantity·scrap_quantity) and cause-based scrap in MCP get_asset_kpi (scrap_quantity) are also the Quantity of the work order unit at that time. Since counters count integers, count 1 = work order unit 1 — if an integer counter is attached to a kg work order, it counts in 1 kg units. Decimal performance is entered via quantity correction or performance input, not the counter.

Scrap and Rework — «Defect» is the sum of the two​

Defects are divided into scrap (SQ) and rework (RQ) according to ISO 22400.

  • Defect = Scrap + Rework, Production = Good + Scrap + Rework(produced_quantity = good_quantity + scrap_quantity + rework_quantity).
  • scrap_quantity is scrap only. Defects counted by PLC are treated as scrap as the disposition is unknown. Rework only occurs in defect records where the disposition is REWORK.
  • Since Quality Q is Good ÷ Production, rework also reduces quality — it is simply recorded separately and does not change the OEE value.
  • The screen maintains this distinction verbally: areas referring only to scrap (production history column, quantity correction field) are labeled «Scrap», while areas showing the total are labeled «Defect». If rework exists, the detail is appended as «14 kg (Scrap 12.75 · Rework 1.25)» — Production Management — Quantity and Units.
  • If integration requires «Defect Total», add scrap_quantity + rework_quantity as decimals of the same unit. Do not read scrap_quantity as the defect total.

Which field carries which quantity — Counter Table​

Source (Stored)V5 Field / Event FieldQuantity Carried
PLC counter ledger tm_kpi_oee_count/kpi/oee/count's good_quantity · scrap_quantityGood/Defect increments counted by PLC. Defect is scrap (SQ) since disposition is unknown
Work order OEE row mm_order_oee.total_production_countproduced_quantity · Event pq/pq_quantityProduction(PQ) = Good + Scrap + Rework
mm_order_oee.total_good_countgood_quantity · gq/gq_quantityGood(GQ)
mm_order_oee.total_bad_count − total_rework_countscrap_quantity · sq/sq_quantityScrap(SQ). total_bad_count itself (defect total) is not sent over the wire
mm_order_oee.total_rework_countrework_quantity · rq/rq_quantityRework(RQ)
mm_order_oee.out_of_plan_good_count / out_of_plan_bad_countgood_quantity_out_of_plan / scrap_quantity_out_of_plan · gq_out_of_plan/sq_out_of_plan(+_quantity)Volume produced during planned stop — included in above good/scrap, but subtracted in efficiency calculation
Quantity correction mm_order_quantity_correction/order/{id}/quantity's measured_*_quantity · good_quantity_delta/scrap_quantity_delta · (History) entered_*_quantityPLC measured value, correction increase/decrease, REPLACE input value. Scrap field is scrap only
Defect record mm_defect_record (Disposition SCRAP/REWORK)/defects's quantity·inspected_quantity, Performance document defects.scrap_quantity / rework_quantity / inspected_scrap_quantityScrap/Rework by disposition, inspection defect total(SCRAP + REWORK)
mm_order.planned_quantityplanned_quantity · Event planned_quantityPlanned(POQ)

Request Rules and Rejections​

The quantity carried in performance, calibration, or defect records must be in the same unit as work order planned_quantity. The server rejects it without conversion. Value/unit rejections are 422 E1002 (not 400), and in the response (application/problem+json), code is E1002, and the reason field contains the reason code below. Since detail is a sentence in the caller's language, use reason for branching.

Reason CodeWhen
quantity.unit_mismatchUnit different from the work order unit was sent
quantity.not_integerDecimal sent for a counting unit(EA·H87·C62·BX·SET)
quantity.unit_invalidUnit is missing or outside the allowed list of 13
quantity.formatquantity_string is not a decimal notation string (exponential notation, leading zero, + sign, numeric type), or data_type does not match the unit
quantity.scaleFifth decimal place or more
quantity.negativeNegative number (only calibration increase/decrease fields accept negatives)
quantity.rangeAbsolute value is 1014 or more — outside the range of stored numeric(18,4)
  • Providing a value (such as number 500) instead of an object in the quantity field or using keys outside quantity_string·data_type·unit_of_measure results in 400 E1000 (format error).
  • The planned quantity of the work order must be greater than 0.

Period KPI — Do not sum if units are mixed​

Responses that aggregate multiple work orders, such as /kpi/period·/kpi/daily, only calculate totals when there is a single unit.

Quantity total fields are good_count·total_count·in_plan_count inside the oee object of /kpi/period, and good_count·bad_count·planned_count of /kpi/daily. bad_count of /kpi/daily is Production − Good, which is the Defect Total (Scrap + Rework).

  • Single unit — Quantity total fields are numbers as they are, with the unit unit_of_measure attached.
  • Mixed units — Quantity total fields and unit_of_measure are null; instead, good/production quantities per unit are listed as Quantity in quantities_by_unit.
{
"good_count": null,
"total_count": null,
"in_plan_count": null,
"unit_of_measure": null,
"quantities_by_unit": [
{ "unit_of_measure": "EA",
"good_quantity": { "quantity_string": "2671", "data_type": "integer", "unit_of_measure": "EA" },
"produced_quantity": { "quantity_string": "2700", "data_type": "integer", "unit_of_measure": "EA" } },
{ "unit_of_measure": "KGM",
"good_quantity": { "quantity_string": "1250.5", "data_type": "decimal", "unit_of_measure": "KGM" },
"produced_quantity": { "quantity_string": "1302", "data_type": "decimal", "unit_of_measure": "KGM" } }
]
}

Ratios (availability, performance, quality, OEE) are dimensionless, so they are displayed as is even if units are mixed.

EMS Value per Unit — Unit of Denominator​

The denominator of energy_per_unit_kwh·co2_per_unit_kg is the good quantity of the work order, so that unit unit_of_measure appears in the response (/order/{id}/ems, /order/ems/*, /order/query?include=ems). The screen displays it as «kWh/EA», «kWh/kg». In aggregations of multiple work orders, if units are mixed, the value per unit and the unit are null.

Events​

Since event schemas only allow «appending to the end», fields were added without changing types.

EventQuantity Field
plantpulse.order.*planned_quantity(number) + planned_quantity_unit_of_measure(Rec 20 code)
plantpulse.kpi.oee.finalizedThe original is gq_quantity·sq_quantity·rq_quantity·pq_quantity·gq_out_of_plan_quantity·sq_out_of_plan_quantity(number) + unit_of_measure. sq is scrap only, rework is rq(pq = gq + sq + rq). Integer fields gq·sq·rq·pq·… are only filled for counting units and are absent for decimal units — will be removed in the next major version
Production event OEE_CALCULATEDgood_quantity·scrap_quantity(scrap only)·rework_quantity = Quantity object

11.6 MES Integration — Performance Feed and B2MML Mapping​

External Work Order Identification — (external_system, external_id) Pair​

Work orders from customer systems (MES/ERP) are identified by a pair of two fields: the issuing system name and the number within that system — same as the external identification for employee or maintenance orders.

  • The pair is present both on the incoming and outgoing paths. V5 POST·PUT·PATCH /api/v5/order receive external_system(50 chars)·external_id(100 chars), and GET /order/{id}·List·/order/query·performance documents always carry both (null if absent). Since the console form does not have external_system fields, enter them via V5.
  • Values that are empty or whitespace only are stored as null. If the length is exceeded or control characters are present, it is 422 order.external_ref.invalid.
  • The pair is unique when both fields are not empty. Assigning the same pair to a second work order results in 409 order.external.duplicate.
  • PUT replaces the entire request field — missing external_system·external_id are deleted. To change only a part, use PATCH.
  • Work orders with external_id but no external_system are still accepted, but since they are not a pair, they cannot be found using the «Find by Pair» method below.
  • Events also carry the pair: created·updated·startedaborted of plantpulse.order.* (schema major 2) carry external_id·external_system, and transition events (startedaborted) also carry customer_po_id.
  • To read a single work order by pair, use GET /api/v5/order/by-external/{system}/{external_id} — 404 if not found, 422 order.external_ref.invalid if one side is empty. The filter for /order/query is also external_id + external_system. Sending the old parameter external_order_id is not ignored but rejected as 422.

Performance Feed Path​

HTTPPathAction
GET/api/v5/order/performancePerformance feed — cursor·site_id·limit. Work orders with changed performance have performance_revision incremented by one and appear again at the end
GET/api/v5/order/{order_id}/performancePerformance document of a single work order
GET/api/v5/order/performance/by-external/{external_system}/{external_id}Find by pair — array (at most one since pair is unique), empty array if not found
Old paths are gone

/api/v5/order/performance/by-external/{external_order_id} (number only without system) has been removed. There are no compatibility paths.

Quantity Keys in Performance Document​

LocationKey (All Quantity)Old Key
planplanned_quantitytarget_units
quantitymeasured_good_quantity / measured_scrap_quantitymeasured_good / measured_bad
quantitygood_quantity / scrap_quantitygood / bad
quantity.correctiongood_quantity_delta / scrap_quantity_deltagood_delta / bad_delta
oeegood_quantity / scrap_quantitytotal_good_count / total_bad_count
defectsinspected_scrap_quantity / scrap_quantity / rework_quantityinspected_bad_count / scrap_count / rework_count

quantity.correction_version, progress.expected_now_units·pace_units names remain the same (decimal number if decimal unit).

quantity.scrap_quantity is scrap only (PLC defect + SCRAP record) and has the same meaning as defects.scrap_quantity. quantity.rework_quantity has the same value as defects.rework_quantity. defects.inspected_scrap_quantity is the total inspection defect(SCRAP + REWORK).

Status of Performance Document — How to read what changed​

A single performance document contains the current status of one work order, and the version increases whenever the status changes.

FieldMeaning
actual.statusStored status WAIT·START·PAUSED·END·ABORTED — 11.1. Cancellation is ABORTED + actual.abort_code=CANCELLED
actual.isa95_stateISA-95 work order status Ready·Running·Held·Completed·Aborted
performance_revisionNumber of times performance has changed. Increases by 1 each time, and the feed places that work order at the end. 0 is a work order not yet finished — not in the feed and only read via /order/{id}/performance
quantity.correction_versionQuantity correction version. If no correction, it is the measured value; increases per correction — history is in the revision history of /order/{id}/quantity
changed_at · change_causeTime and cause of last change — ENDED·QUANTITY(quantity correction)·STOP_REASON·OEE_FINAL·DEFECT(defect record)·PROGRESS·BASELINE

Quantities are carried only as «value + unit», and fields that are not calculated are null rather than 0. The MES can replace the entire previous document with a newer document where the same order_id's performance_revision is larger.

B2MML Mapping Table​

A single performance document corresponds to the Job Response (Segment Response) of ISA-95 Part 4, and its quantity corresponds to the Quantity of Material Actual (Produced).

ISA-95 / B2MMLPlantPulse V5Note
QuantityValueType — QuantityString · DataType · UnitOfMeasureQuantity — quantity_string · data_type · unit_of_measure1:1. Optional B2MML field Key is not used
UnitOfMeasure ValueUN/ECE Rec 20 CodeAllowed list is in 11.5
Job Order IDexternal_idIssuing system name is external_system — the two are a pair
Job Order · MaterialRequirement Quantityplanned_quantityISO 22400 POQ
Job Order Nameorder_name
Job Order StartTime · EndTimeplanned_start_timestamp · planned_end_timestampPlanned time. Actual time is actual_start_timestamp · actual_end_timestamp
Job ResponseSingle performance document (/order/{id}/performance)
MaterialActual(Produced) Quantityproduced_quantityISO 22400 PQ
Good portion among thosegood_quantityISO 22400 GQ
Scrap portion among thosescrap_quantityISO 22400 SQ
Rework portion among thosedefects.rework_quantityISO 22400 RQ

11.7 Application Scenarios​

Scenario — Automatic Work Order Creation via MES Integration​

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("ORDER_" + today + "_" + String.format("%03d", mes.seq));
req.setSite_id("SITE_DJ");
req.setAsset_id(mes.equipId);
req.setProduct_id(mes.productId);
// MES 수량을 그대로 — 단위 환산은 하지 않는다. 단위는 허용 목록(11.5) 안의 Rec 20 코드여야 한다
req.setPlanned_quantity(Quantity.of(mes.qty.toPlainString(), mes.unitCode));
req.setExternal_id(mes.mesOrderId);
req.setExternal_system("MES");
req.setPlanned_start_timestamp(mes.plannedStart);
req.setPlanned_end_timestamp(mes.plannedEnd);
client.order().create(req);
}

Scenario — Safe State Transition Helper​

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);
}

Next Steps​