crm-rest-api/docs/mis-integration-api.md

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) — MIS tbl_daily_installUNIQUE 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)는 사용하지 않습니다. 고객 단위 전량 교체이므로 컨테이너 개별 식별자가 필요 없습니다. ccoCustomerIdaccountNo 로 서버가 채우므로 보내지 않습니다(보내도 무시).

응답 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