# ชุดเชื่อมระบบต้นทางกับ Water CAD Studio

ปลายทาง WCS: https://cad-studio.entawisdom.com — สัญญาข้อมูล `wcs.exchange.v1`

ชุดนี้เป็น API/SDK สำหรับทีมต้นทางนำไปเชื่อมตามที่ผู้ใช้ยืนยัน ไม่ติดตั้งระบบต้นทางใหม่ ไม่แจก credential และไม่เปิดคิวส่งข้อความจริงให้เอง ตัวอย่าง TEST ไม่ใช่ผลสำรวจ ราคากลาง หรือการอนุมัติงานจริง

## เริ่มต้น

1. ผู้ดูแล WCS สร้าง connector ตามคู่มือ API กำหนด scope เท่าที่ใช้ และส่ง token/signing key ให้ทีมผ่านช่องทาง secret ขององค์กร เก็บ `WCS_EXCHANGE_TOKEN` ใน environment ของ service ห้ามใส่ใน URL, source code หรือไฟล์ที่จะส่งต่อ
2. ใช้ Python3.10+ สร้าง virtual environment และติดตั้ง `requirements-sdk.txt` ตัวอย่าง callback PostgreSQL ใช้ driver ของระบบต้นทางเอง: `psycopg[binary]` หรือ `psycopg2` ที่ทีมรองรับ ไม่ต้องใช้บัญชีฐานข้อมูล WCS
3. รัน `python -m unittest test_wcs_exchange_client -v` จากโฟลเดอร์ sdk เพื่อตรวจพฤติกรรม client โดยไม่มีการส่งอีเมลหรือสร้างงานจริง
4. อ่าน `docs/WCS_EXCHANGE_API.md` และ `api/route_inventory.json` ส่วน route inventory เป็นรายการ route จากแอปที่ deploy ไม่ใช่ OpenAPI ที่บอก schema body ครบ ข้อกำหนด generation รายชนิดต้องอ่านสดจาก `/requirements/{template}` พร้อมตรวจ SHA

```powershell
python sdk/wcs_exchange_client.py --output templates.json templates
python sdk/wcs_exchange_client.py --output RH-15_requirements.json requirements RH-15
```

SDK อ่าน token จาก environment ที่องค์กรตั้งไว้แล้ว คำสั่ง output ใช้การสร้างไฟล์ใหม่ ไม่เขียนทับไฟล์เดิม ตรวจทั้ง fields, investigations, review gates, หน่วย และ generation_supported ไม่มีการเติม example_defaults เป็นข้อมูลพื้นที่

## ส่งข้อมูลและติดตามงาน

ตัวอย่าง `examples/submission_INCOMPLETE.json` ยังไม่ครบและส่งสร้างแบบไม่ได้ ต้องอ่าน requirements ล่าสุด กรอก hash และค่าจริง/ค่าผู้ออกแบบ/ข้อสมมติพร้อมที่มาตามความหมายของแต่ละช่องเอง ค่า measured ต้องมีวันที่ วิธีวัด และรายการหลักฐานพร้อมไฟล์จริง ห้ามเปลี่ยนชื่อข้อมูลสมมติเป็นข้อมูลวัดจริง

```powershell
python sdk/wcs_exchange_client.py submit provided_submission_R1.json --idempotency-key SOURCE-RECORD-001:R1
python sdk/wcs_exchange_client.py upload-evidence SUBMISSION_UUID survey-report-R1 actual_report.pdf --media-type application/pdf --expected-sha256 ACTUAL_SHA256
python sdk/wcs_exchange_client.py status SUBMISSION_UUID
python sdk/wcs_exchange_client.py generate SUBMISSION_UUID
python sdk/wcs_exchange_client.py status SUBMISSION_UUID
python sdk/wcs_exchange_client.py prepare-review SUBMISSION_UUID
```

key ต้องคงเดิมสำหรับ record/revision/data เดิม ข้อมูลเปลี่ยนให้ออก source revision ใหม่ ส่งไฟล์ evidence ที่ประกาศให้ครบก่อน generate เมื่อไม่ทราบผล POST ให้ติดตาม submission เดิมและกระทบยอด ไม่สร้าง key ใหม่เพื่อหลบสถานะ ไม่วนส่ง generate ซ้ำเมื่อ observation timeout การขอสร้างได้อาจยังรอ worker ที่กำลังทำงานอื่นอยู่

`validated` หมายถึงผ่านรูปแบบข้อมูล ไม่ใช่ผลสำรวจผ่านตรวจ; `generated` หมายถึงสร้างไฟล์ ไม่ใช่อนุมัติ การขาดข้อมูลวิศวกรรมหรือราคาอยู่ใน HOLD ต่อไป วิศวกร/ผู้ตรวจราคาที่ได้รับมอบหมายและมีอำนาจตรวจใน WCS ให้ความเห็น ขอแก้ เปิดฉบับใหม่ และลงผลพร้อมเอกสารจริง

## รับ callback และจัดเก็บไฟล์

- ทีมต้นทางจัด HTTPS endpoint พอร์ต443 บน public host ที่องค์กรควบคุม แล้วแจ้งผู้ดูแลเพื่อกำหนดใน connector WCS ตรวจ DNS/TLS และไม่ follow redirect ห้ามใช้ endpoint ส่วนตัวหรือ worker token แทน
- framework ต้องส่ง **raw body bytes** และ headers เดิมเข้า `receive_callback()` ใน `sdk/callback_receiver_example.py` ตรวจ HMAC ก่อน parse/เก็บข้อมูล ห้าม stringify JSON ใหม่ก่อนตรวจลายเซ็น
- `api/callback_inbox.sql` เป็น schema อ้างอิงสำหรับฐานข้อมูลต้นทาง ทีมต้องตรวจและปรับเข้าระบบ migration/สิทธิ์ของตนเอง ตัวอย่าง handler ใช้ connection เฉพาะที่ `autocommit=False` และ commit ก่อนคืน HTTP200
- UNIQUE(connector_id,event_id) เก็บซ้ำได้ครั้งเดียว payloadเดิมตอบ200ซ้ำ ถ้า eventเดิมแต่ payloadต่างให้409และตรวจเหตุการณ์ ไม่เขียนทับแถวเดิม Database error ต้องไม่ตอบ200 ให้ WCS retry ตามคิว
- รับ callback แล้วจัดคิว worker ต้นทางดาวน์โหลดแยกจาก HTTP request ใช้ project/version/fingerprint ใน event และตรวจไฟล์ทั้งหมดก่อนเปลี่ยนเป็น downloaded ต้องมี lease/retry ของ worker ตามระบบต้นทาง และเก็บ event/receipt ไว้เมื่อ restart

```python
from wcs_exchange_client import WCSClient

# event มาจาก callback ที่ตรวจและ commit แล้ว; key/token อ่านจาก secret store
c = WCSClient("https://cad-studio.entawisdom.com", service_token)
receipt = c.download(event["project_id"], event["version_id"],
                     target_directory, event["artifact_fingerprint"])
# SDK ตรวจ CAD/ราคา/หลักฐานตาม SHA แล้วจึงสร้าง RELEASE_COMPLETE.json
# จึงค่อยอัปเดต inbox เป็น downloaded ใน transaction ของระบบต้นทาง
```

การดาวน์โหลดซ้ำใช้ไฟล์ hash เดิมได้ ไม่เขียนทับไฟล์ต่าง hash การรับ callback หรือดาวน์โหลดสำเร็จไม่ใช่การลงนามรับรองเพิ่ม และไม่อนุมัติรายการในระบบราชการอื่นให้อัตโนมัติ

## สิ่งที่ทีมต้นทางต้องตั้งค่าก่อนเปิดจริง

ผู้รับผิดชอบระบบ, connector/scopes, secret store, HTTPS callback, การเทียบ source record/revision กับ WCS submission/project/version, นาฬิกาเครื่องที่ตรงกัน, durable inbox/worker/retry, พื้นที่เก็บไฟล์/สิทธิ์/สำรองข้อมูล และผู้รับ/ผู้ตรวจที่มีอำนาจจริง ผู้ดูแล WCS ต้องตรวจผู้รับและคิวก่อนเปิด dispatcher การทดสอบ SMTP เดิมผ่าน Inbox แล้ว แต่ชุดส่งมอบนี้ไม่เปิดส่งคิวจริง

## ผลทดสอบและขอบเขต

อ่าน `HANDOFF_MANIFEST.json` และ `HANDOFF_QA.json` เพื่อตรวจ SHA และผลตรวจจากชุดที่แตก ZIP แล้ว ภาคพัฒนาตรวจ regression70กรณี, SDK7กรณี, RH-15จริง37QTO/HOLD9ข้อ และ native Excel ตามแต่ละ profile; ผลบวกอนุมัติและ callback ใช้ TEST/transportจำลอง การรับส่งกับ endpoint ของทีมต้นทางยังต้องทำร่วมกันเมื่อทีมติดตั้ง ไม่ถือว่าทดสอบ endpoint องค์กรที่ยังไม่ระบุแล้ว
