378 lines
14 KiB
Markdown
378 lines
14 KiB
Markdown
# 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_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 이 자동 기록**.
|
|
|
|
### 요청 본문
|
|
|
|
```jsonc
|
|
{
|
|
"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`
|
|
|
|
```jsonc
|
|
{
|
|
"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` 를 확인함.)
|
|
|
|
### 호출 예시
|
|
```bash
|
|
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 이 자동 기록.
|
|
|
|
### 요청 본문
|
|
|
|
```jsonc
|
|
{
|
|
"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`
|
|
교체 후 그 고객의 전체 컨테이너 목록(배열)을 반환합니다.
|
|
|
|
```jsonc
|
|
[
|
|
{
|
|
"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)
|
|
|
|
```bash
|
|
# 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
|
|
```
|