14 KiB
MIS → CRM 통합 API (crm-rest-api)
MIS 가 CRM 으로 데이터를 밀어 넣을 때 호출하는 통합(integrate) 엔드포인트 명세입니다. 이번 작업으로 추가된 2개 엔드포인트만 다룹니다.
| # | 대상 | Method | Path |
|---|---|---|---|
| 1 | Daily Install (+ Container) 통합 | POST |
/customer-daily-install/integrate |
| 2 | Daily Install 삭제 (자연키) | DELETE |
/customer-daily-install/customer/{accountNo}/date/{installDate}/jobtype/{workType} |
| 3 | Customer Container 통합 | POST |
/customer-container/integrate |
공통 사항
Base URL
http://{host}:8082/crm-rest-api
- 서버 포트
8082, context-path/crm-rest-api포함. - 예:
http://192.168.2.172:8082/crm-rest-api/customer-daily-install/integrate
인증 (필수)
JWT 를 아래 둘 중 하나로 전달합니다. 쿠키 우선, 없으면 헤더.
- 쿠키:
AUTH_TOKEN={jwt} - 헤더:
Authorization: Bearer {jwt}
권한: 두 엔드포인트 모두 CRM 생성 권한(Module=C, Action=C, 또는 ADMIN) 필요.
요청 형식
Content-Type: application/json- 문자 인코딩 UTF-8
날짜/시간 포맷 (JSON 문자열)
| 타입 | 포맷 | 예 |
|---|---|---|
| date | yyyy-MM-dd |
"2026-07-08" |
| time | HH:mm:ss |
"14:30:00" |
| datetime | yyyy-MM-dd'T'HH:mm:ss |
"2026-07-08T14:30:00" |
| 문자열 타임스탬프(14자리) | yyyyMMddHHmmss |
"20260708143000" |
createddate/updateddate처럼 varchar(14) 로 저장되는 필드는 위 14자리 문자열 그대로 전달합니다.
공통 응답 코드
| 코드 | 의미 | 본문 예 |
|---|---|---|
200 OK |
성공 | (엔드포인트별 응답 참조) |
400 Bad Request |
업무 오류 (예: accountNo 로 고객 못 찾음) | { "error": "Customer not found by accountNo: 12345", "timestamp": "...", "status": 400 } |
401 Unauthorized |
토큰 만료/무효 | { "error": "Session has expired." } 또는 { "error": "Invalid login information." } |
403 Forbidden |
권한 없음 | { "error": "Forbidden", "message": "No permission", "status": 403, "timestamp": "..." } |
500 Internal Server Error |
서버 오류 | { "error": "Internal Server Error", "message": "...", "status": 500 } |
내부 ID 자동 변환 규칙 (중요)
MIS 는 CRM 내부 PK 를 모르므로, CRM 이 아래 규칙으로 변환합니다. MIS 는 내부 id 대신 아래 값을 보냅니다.
| 논리 대상 | MIS 가 보내는 값 | CRM 변환 방식 |
|---|---|---|
| 고객 | accountNo (= MIS di_accountno / cc_accountno) |
customer.cus_no 로 조회 → 내부 cusId |
| 기사(driver) | externalDriverId (= MIS di_driver_uid) |
HCM employee_external_map(MIS_DRIVER) 조회 → 내부 empId |
| 생성자 | externalCreatedBy (= MIS member id) |
HCM employee_external_map(MIS_MEMBER) 조회 → empNo |
accountNo == cus_no전제. 해당 고객이 CRM 에 없으면400(Customer not found by accountNo).
1. Daily Install 통합
POST /customer-daily-install/integrate
tbl_daily_install 1건과 그에 딸린 tbl_daily_install_container N건을 한 번의 호출·한 트랜잭션으로 반영합니다.
동작
- install 은 자연키로 upsert:
(accountNo, workType, installDate)— MIStbl_daily_install의UNIQUE KEY (di_accountno, di_work_type, di_install_date)와 동일.- 기존 있으면 update, 없으면 insert.
- containers 는 install 단위로 replace-all: 컨테이너 행에는 자연키가 없으므로, 해당 install 의 기존 컨테이너를 전부 삭제 후 재삽입.
containers필드 생략/null→ 컨테이너 손대지 않음.containers: [](빈 배열) → 해당 install 의 컨테이너 전부 삭제.containers: [ ... ]→ 통째로 교체.
- 컨테이너의 부모 연결(
cdicDailyInstallId)은 저장된 install 의 내부 id 로 서버가 자동 세팅하므로 요청에 넣을 필요 없음(넣어도 무시). - 타임스탬프(
cdiCreatedAt/cdiUpdatedAt)는 서버 auditing 이 자동 기록.
요청 본문
{
"install": {
// ── 자연키 (필수) ──
"cdiAccountNo": "A00123", // = di_accountno (= cus_no)
"cdiWorkType": "INSTALL", // = di_work_type
"cdiInstallDate": "2026-07-08", // = di_install_date
// ── 필수(NOT NULL) ──
"cdiOrderSeq": 1, // = di_order_seq
"cdiExternalDriverId": "88", // = di_driver_uid → 내부 empId 로 변환
// ── 선택 ──
"cdiInstallTime": "14:30:00",
"cdiCustomerName": "홍길동 식당",
"cdiPhone": "416-555-1234",
"cdiAddress": "123 Main St",
"cdiCity": "Toronto",
"cdiPostalCode": "M1A1A1",
"cdiGeoLat": 43.6532100,
"cdiGeoLon": -79.3831800,
"cdiExternalCustomerDriverId": "88", // = di_customer_driver_uid → 내부 empId (선택)
"cdiOilPickup": 1,
"cdiSludge": 0,
"cdiRequestNote": "...",
"cdiWorkNote": "...",
"cdiInstallNote": "...",
"cdiCsNote": "...",
"cdiStatus": "A",
"cdiExternalCreatedBy": "1002" // = MIS member id → empNo 로 변환
},
"containers": [
{
"cdicAction": "IN", // 필수
"cdicType": "T20", // 필수
"cdicCapacity": 20,
"cdicIsUsed": "N",
"cdicHasLock": "N",
"cdicHasWheel": "N",
"cdicHasWoodframe":"N",
"cdicOwnerType": "C"
}
]
}
install 필드 (MIS tbl_daily_install 매핑)
| 필드 | 타입 | 필수 | MIS 컬럼 | 비고 |
|---|---|---|---|---|
cdiAccountNo |
string | ✅ | di_accountno |
자연키 · 고객 해석(=cus_no) |
cdiWorkType |
string(10) | ✅ | di_work_type |
자연키 |
cdiInstallDate |
date | ✅ | di_install_date |
자연키 |
cdiOrderSeq |
int | ✅ | di_order_seq |
|
cdiExternalDriverId |
string | ✅* | di_driver_uid |
내부 driver empId 로 변환. cdiDriverId(내부 Long) 직접 전달도 가능 |
cdiInstallTime |
time | di_install_time |
||
cdiCustomerName |
string(100) | di_customer_name |
||
cdiPhone |
string(50) | di_phone |
||
cdiAddress |
string(200) | di_address |
||
cdiCity |
string(50) | di_city |
||
cdiPostalCode |
string(20) | di_postal |
||
cdiGeoLat |
number | di_lat |
||
cdiGeoLon |
number | di_lng |
||
cdiExternalCustomerDriverId |
string | di_customer_driver_uid |
내부 empId 로 변환(선택) | |
cdiWorkType(위 자연키와 동일) |
||||
cdiOilPickup |
int | di_oil_pickup |
||
cdiSludge |
int | di_sludge |
||
cdiRequestNote |
string | di_request_note |
||
cdiWorkNote |
string | di_work_note |
||
cdiInstallNote |
string | di_install_note |
||
cdiCsNote |
string | di_cs_note |
||
cdiStatus |
string(1) | di_status |
기본 'A' | |
cdiExternalCreatedBy |
string | di_created_by(member id) |
empNo 로 변환 |
|
cdiLoginUser |
string | — | ERP 내부 호출용(생성/수정자 직접 지정). MIS 는 보통 미사용 |
* 내부 cdiDriverId(Long) 를 직접 주면 그 값을 우선 사용. MIS 는 cdiExternalDriverId 로 전달.
MIS 의
di_wait_uid,di_lock_date,di_container_applied는 CRM 에 대응 컬럼이 없어 저장하지 않습니다.
containers[] 필드 (MIS tbl_daily_install_container 매핑)
| 필드 | 타입 | 필수 | MIS 컬럼 |
|---|---|---|---|
cdicAction |
string(10) | ✅ | dic_action |
cdicType |
string(10) | ✅ | dic_type |
cdicCapacity |
int | dic_capacity |
|
cdicIsUsed |
string(1) | dic_is_used |
|
cdicHasLock |
string(1) | dic_has_lock |
|
cdicHasWheel |
string(1) | dic_has_wheel |
|
cdicHasWoodframe |
string(1) | dic_has_woodframe |
|
cdicOwnerType |
string(1) | dic_owner_type |
dic_di_uid(부모 install FK)는 서버가 자동 세팅하므로 보내지 않습니다.
응답 200 OK
{
"install": {
"cdiUuid": "0f9c...-...",
"cdiOrderSeq": 1,
"cdiDriverId": 57,
"cdiInstallDate": "2026-07-08",
"cdiInstallTime": "14:30:00",
"cdiCustomerId": 1024,
"cdiAccountNo": "A00123",
"cdiCustomerName": "홍길동 식당",
"cdiWorkType": "INSTALL",
"cdiStatus": "A",
"cdiCreatedBy": "E1002",
"cdiCreatedAt": "2026-07-08T14:31:05",
"cdiUpdatedAt": "2026-07-08T14:31:05"
// ... 나머지 필드 포함
},
"containers": [
{
"cdicId": 3037,
"cdicDailyInstallId": 1235,
"cdicAction": "IN",
"cdicType": "T20",
"cdicCapacity": 20,
"cdicIsUsed": "N",
"cdicHasLock": "N",
"cdicHasWheel": "N",
"cdicHasWoodframe": "N",
"cdicOwnerType": "C"
}
]
}
2. Daily Install 삭제 (자연키)
DELETE /customer-daily-install/customer/{accountNo}/date/{installDate}/jobtype/{workType}
MIS 에서 install 이 삭제/이동되었을 때, 자연키로 CRM 의 install 을 삭제합니다.
경로 변수
| 변수 | 타입 | MIS 컬럼 | 예 |
|---|---|---|---|
accountNo |
string | di_accountno (= cus_no) |
A00123 |
installDate |
date (yyyy-MM-dd) |
di_install_date |
2026-07-08 |
workType |
string | di_work_type |
INSTALL |
동작
- 자연키
(accountNo, workType, installDate)로 install 을 찾음. - 딸린 컨테이너(
tbl_daily_install_container)를 먼저 전부 삭제한 뒤 install 을 삭제(고아 방지). - 대상이 없어도 멱등: 그대로
204반환(에러 아님). 재시도/중복 호출 안전. - 요청 본문 없음.
응답
| 코드 | 의미 |
|---|---|
204 No Content |
삭제 완료 또는 대상 없음(멱등) |
403 Forbidden |
권한 없음 |
권한: CRM 삭제 권한(
Module=C, Action=D) 필요. (통합 POST 는 생성 권한C이지만, 이 삭제는 삭제 권한D를 확인함.)
호출 예시
curl -X DELETE \
"http://192.168.2.172:8082/crm-rest-api/customer-daily-install/customer/A00123/date/2026-07-08/jobtype/INSTALL" \
-H "Authorization: Bearer $JWT"
3. Customer Container 통합
POST /customer-container/integrate
특정 고객의 컨테이너 목록을 통째로 교체(replace-all) 합니다. CRM 에서는 고객 컨테이너를 수정하지 않고 MIS 를 단일 소스로 보기 때문에 전량 동기화합니다.
동작
accountNo(=cc_accountno=cus_no) 로 고객을 찾아 내부cusId로 변환.- 그 고객의 기존 컨테이너 전부 삭제 후 재삽입.
containers생략/null→ 아무것도 건드리지 않음(현재 목록 반환).containers: []→ 해당 고객 컨테이너 전부 삭제.containers: [ ... ]→ 통째로 교체.
ccoModifiedAt은 서버 auditing 이 자동 기록.
요청 본문
{
"accountNo": "A00123", // = cc_accountno (= cus_no)
"containers": [
{
"ccoType": "T20", // 필수
"ccoCapacity": 20,
"ccoOwnerType": "C",
"ccoIsUsed": "Y",
"ccoHasLock": "N",
"ccoHasWheel": "N",
"ccoHasWoodframe":"N",
"ccoStatus": "I",
"ccoCreateddate": "20260708143000", // varchar(14)
"ccoUpdateddate": "20260708143000", // varchar(14)
"ccoInstallNote": "...",
"ccoPickupNote": "...",
"ccoInstallDate": "2026-07-08",
"ccoPickupDate": null,
"ccoInstallAt": "2026-07-08T14:30:00",
"ccoPickupAt": null
}
]
}
최상위 필드
| 필드 | 타입 | 필수 | 비고 |
|---|---|---|---|
accountNo |
string | ✅ | cc_accountno (= cus_no). 고객 해석용 |
containers |
array | 아래 표. 교체 규칙은 위 "동작" 참조 |
containers[] 필드 (MIS tbl_customer_container 매핑)
| 필드 | 타입 | 필수 | MIS 컬럼 |
|---|---|---|---|
ccoType |
string(10) | ✅ | cc_type |
ccoCapacity |
int | cc_capacity |
|
ccoOwnerType |
string(1) | cc_owner_type |
|
ccoIsUsed |
string(1) | cc_is_used |
|
ccoHasLock |
string(1) | cc_has_lock |
|
ccoHasWheel |
string(1) | cc_has_wheel |
|
ccoHasWoodframe |
string(1) | cc_has_woodframe |
|
ccoStatus |
string(1) | cc_status |
|
ccoCreateddate |
string(14) | cc_createddate |
|
ccoUpdateddate |
string(14) | cc_updateddate |
|
ccoInstallNote |
string(500) | cc_install_note |
|
ccoPickupNote |
string(500) | cc_pickup_note |
|
ccoInstallDate |
date | cc_install_date |
|
ccoPickupDate |
date | cc_pickup_date |
|
ccoInstallAt |
datetime | cc_install_at |
|
ccoPickupAt |
datetime | cc_pickup_at |
cc_uid(MIS 내부 PK)는 사용하지 않습니다. 고객 단위 전량 교체이므로 컨테이너 개별 식별자가 필요 없습니다.ccoCustomerId는accountNo로 서버가 채우므로 보내지 않습니다(보내도 무시).
응답 200 OK
교체 후 그 고객의 전체 컨테이너 목록(배열)을 반환합니다.
[
{
"ccoId": 30235,
"ccoCustomerId": 1024,
"ccoType": "T20",
"ccoCapacity": 20,
"ccoOwnerType": "C",
"ccoIsUsed": "Y",
"ccoHasLock": "N",
"ccoHasWheel": "N",
"ccoHasWoodframe": "N",
"ccoStatus": "I",
"ccoCreateddate": "20260708143000",
"ccoUpdateddate": "20260708143000",
"ccoInstallNote": "...",
"ccoPickupNote": "...",
"ccoInstallDate": "2026-07-08",
"ccoPickupDate": null,
"ccoInstallAt": "2026-07-08T14:30:00",
"ccoPickupAt": null,
"ccoModifiedAt": "2026-07-08T14:31:05"
}
]
호출 예시 (curl)
# 1) Daily Install 통합
curl -X POST "http://192.168.2.172:8082/crm-rest-api/customer-daily-install/integrate" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d @install.json
# 2) Customer Container 통합
curl -X POST "http://192.168.2.172:8082/crm-rest-api/customer-container/integrate" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d @container.json