ข้ามไปยังเนื้อหาหลัก
Thinker Docs
ดาวน์โหลด .md/docs/manual.th.md

คู่มือผู้ใช้งาน (USER MANUAL)

แผนภาพระบบ (Service Diagram)#

วงจรชีวิตของเทมเพลต (Template Lifecycle)#

เผยแพร่คือประตูทางเดียว ฉบับร่างที่ตรวจไม่ผ่านจะเผยแพร่ไม่ได้ — และคีย์ API เรนเดอร์ฉบับร่างไม่ได้เลย เพราะฉบับร่างคือเทมเพลตที่ระบบ เชื่ออยู่แล้ว ว่าจะผลิตเอกสารผิด


หน้าออกแบบเทมเพลต — มีอะไรอยู่ตรงไหน#

หน้าออกแบบเทมเพลต: แถบเครื่องมือ 21 ชนิดฟิลด์อยู่ด้านซ้าย, PDF ที่อัปโหลดอยู่ตรงกลางพร้อมฟิลด์ case_id / fullName / loanAmount ที่ลากมาวางทับบนเส้นของเอกสารจริง, หัวท้ายกระดาษและลายน้ำเป็นฟิลด์อ่านอย่างเดียว, พาเนลแก้ไขฟิลด์และผลการตรวจอยู่ด้านขวา และปุ่มเผยแพร่อยู่ล่างขวา

#คืออะไร
1แถบเครื่องมือ — ฟิลด์ 21 ชนิด (ข้อความ ตัวเลข วันที่ พ.ศ. รูป QR บาร์โค้ด ตาราง ช่องติ๊ก … และท้ายสุดคือ หัว/ท้ายกระดาษ · ลายน้ำ · นิพจน์)
2ลากมาวางบน PDF จริงของสถาบันการเงิน — ไม่ได้วาดเอกสารใหม่ เราวางทับของเดิม กรอบประคือฟิลด์ข้อมูลที่ระบบอื่นจะส่งค่ามาให้
3พาเนลแก้ไขฟิลด์ — ชนิด ชื่อ ฟอนต์ ขนาด และ "ย่อขนาดอัตโนมัติเมื่อยาวเกิน"
4หัว/ท้ายกระดาษ — ใส่ {currentPage} / {totalPages} ได้ ปรากฏทุกหน้าเอง
5ลายน้ำ — พิมพ์ข้อความเอง หมุนได้ ปรับความจางได้ เลือกได้ว่าจะแสดงทุกหน้าไหม
6ผลการตรวจ — ต้องผ่านก่อนถึงจะเผยแพร่ได้
7เผยแพร่ — แช่แข็งเวอร์ชันนั้นตลอดกาล

สีของกรอบบอกความหมาย กรอบน้ำเงินประ = ฟิลด์ข้อมูล ระบบอื่นต้องส่งค่ามาให้ (อยู่ในสัญญาฟิลด์) · กรอบเทาประ = อ่านอย่างเดียว เช่นเลขหน้าและลายน้ำ ไม่อยู่ในสัญญา ไม่มีใครต้องส่งค่ามาให้


1. ข้อมูลทั่วไปเกี่ยวกับผลิตภัณฑ์#

Thinker Docs เป็นระบบสร้างเอกสาร PDF ภายในองค์กร (On-Premises) ในรูปแบบของเทมเพลตสำหรับฝ่ายสนับสนุนและธุรการของสถาบันการเงิน บริการนี้ช่วยให้ผู้ใช้งานสายธุรกิจสามารถออกแบบและจัดวางตำแหน่งข้อมูลในสัญญาหรือเอกสารทางการเงินได้ด้วยตนเองผ่านหน้าจอเว็บเบราว์เซอร์อย่างแม่นยำ และยังเปิดให้ระบบภายนอกสามารถเรียกสร้างเอกสารที่กรอกข้อมูลอัตโนมัติได้อย่างรวดเร็วและปลอดภัยภายใต้มาตรฐานการประมวลผลข้อมูลส่วนบุคคล (PDPA) โดยข้อมูลทั้งหมดจะถูกประมวลผลอยู่ภายใต้เครือข่ายภายในของสถาบันการเงินเพื่อความปลอดภัยสูงสุด


2. การออกแบบเทมเพลต (Designing a Template)#

การทำงานเริ่มจากการสร้างเทมเพลตในหน้าจอระบบ โดยมีขั้นตอนดังนี้:

  1. การอัปโหลดเอกสารฐาน (Base PDF): อัปโหลดเอกสาร PDF ต้นฉบับที่ต้องการนำมาใช้เป็นฐานในการกรอกข้อมูล (จำกัดขนาดไฟล์ไม่เกิน 25 MB และหนาไม่เกิน 20 หน้า) ระบบจะตรวจเช็กและปฏิเสธเอกสารฐานที่เปิดใช้งานการเข้ารหัสผ่าน มีสคริปต์ JavaScript หรือมีลายมือชื่อดิจิทัล (Digital Signature) อยู่ก่อน เนื่องจากระบบจะแปลงหน้าเอกสารฐานเป็นรูปแบบกราฟิก ซึ่งทำให้ระบบโต้ตอบเดิมหรือลายเซ็นดิจิทัลถูกลบออกโดยสิ้นเชิง การปฏิเสธตั้งแต่ขั้นตอนอัปโหลดจึงเป็นไปเพื่อความปลอดภัย
  2. การจัดวางฟิลด์ข้อมูล: ลากฟิลด์ข้อมูลจากแถบเครื่องมือด้านซ้ายและวางลงบนตำแหน่งที่ต้องการในเอกสาร โดยตำแหน่ง (X, Y) และขนาดของฟิลด์ทั้งหมดจะถูกวัดและแสดงผลเป็นหน่วย มิลลิเมตร (มม.) เพื่อให้ตรงตามสัดส่วนกระดาษจริงของสถาบันการเงินเมื่อพิมพ์ออกมา โดยห้ามแปลงหน่วยเป็นพิกเซล (px) เพื่อรักษาความแม่นยำ
  3. การเปิดใช้งานตัวเลือกบีบขนาดฟอนต์อัตโนมัติ (dynamicFontSize): ระบบจะตั้งค่าเปิดใช้ dynamicFontSize เป็นค่าเริ่มต้นสำหรับฟิลด์ข้อความ เพื่อป้องกันการตัดคำหรือข้อความล้นกรอบเมื่อข้อมูลชื่อหรือที่อยู่ภาษาไทยของผู้ลงนามมีความยาวแตกต่างกัน

ประเภทฟิลด์ข้อมูลที่ใช้งานได้ (Available Field Types)#

ระบบมีฟิลด์ข้อมูลให้เลือกใช้งานทั้งหมด 21 ประเภท ดังนี้:

  1. กล่องข้อความ (text): ข้อความทั่วไปที่สามารถย่อขนาดตัวอักษรอัตโนมัติได้
  2. ตัวเลข (number): ข้อความตัวเลขสำหรับมูลค่าเงิน รองรับการกำหนดทศนิยม (decimals), ตัวคั่นหลักพัน (thousandsSeparator), สัญลักษณ์นำหน้า (เช่น ฿), สัญลักษณ์ต่อท้าย (เช่น บาท) และการแปลงเป็นเลขไทย (๑๒๓)
  3. วันที่และเวลาไทย (dateTimeTh): แสดงวันที่ปีพุทธศักราช (พ.ศ.) ของไทย (คำนวณปี BE ด้วยการบวก 543 ปีเข้ากับปี ค.ศ. สากลโดยอัตโนมัติ) กำหนดรูปแบบการแสดงผล (pattern) ได้ เช่น แสดงเฉพาะวันที่ (d MMMM yyyy) หรือแสดงพร้อมเวลา (d MMMM yyyy HH:mm)
  4. ข้อความหลายตัวแปร (multiVariableText): กล่องข้อความที่นำคำคงที่และตัวแปรข้อความต่างๆ มารวมกัน
  5. วันที่ (dateTime): วันที่และเวลารูปแบบเกรกอเรียนสากล (ค.ศ.) เหมาะสำหรับบันทึกเวลาของระบบ (Timestamp)
  6. เวลา (time): เวลาในรูปแบบเกรกอเรียนสากล
  7. รูปภาพ (image): วางไฟล์รูปภาพประกอบเอกสาร
  8. ภาพเวกเตอร์ (svg): วางไฟล์รูปภาพรูปแบบเวกเตอร์ SVG
  9. คิวอาร์โค้ด (qrcode): รหัสคิวอาร์สำหรับการสแกนลิงก์หรือข้อมูล
  10. บาร์โค้ด Code 128 (code128): บาร์โค้ดมาตรฐาน 1D
  11. บาร์โค้ด EAN-13 (ean13): บาร์โค้ดมาตรฐานสินค้า
  12. ตาราง (table): แสดงรายการข้อมูลรูปแบบตาราง เช่น ตารางค่างวด
  13. กล่องเครื่องหมาย (checkbox): ช่องเครื่องหมายติ๊กถูก สามารถปรับความหนาเส้นขอบ (borderWidth) เป็น 0 เพื่อให้เหลือเฉพาะเครื่องหมายติ๊กถูก ในกรณีที่เอกสารฐานมีกรอบสี่เหลี่ยมพิมพ์มาอยู่แล้ว และปรับความมนของมุม (borderRadius) ได้
  14. กลุ่มปุ่มตัวเลือก (radioGroup): ปุ่มตัวเลือกที่ผู้ใช้สามารถเลือกได้เพียงข้อเดียว
  15. ตัวเลือกดรอปดาวน์ (select): เมนูตัวเลือกสำหรับการใช้งานบนเว็บเบราว์เซอร์
  16. เส้นตรง (line): สำหรับวาดเส้นแบ่งส่วนบนกระดาษ
  17. กล่องสี่เหลี่ยม (rectangle): กล่องตกแต่งเลย์เอาต์เอกสาร
  18. วงรี (ellipse): วงกลมหรือวงรีสำหรับตกแต่งเอกสาร
  19. หัว/ท้ายกระดาษ (headerFooter): ข้อความพิเศษแสดงในตำแหน่งเดียวกันทุกหน้าโดยอัตโนมัติ เช่น หน้า {currentPage} จาก {totalPages} ระบบจะประมวลผลตัวเลขหน้าอย่างถูกต้องแม้อนาคตจะมีการแนบไฟล์เพิ่มเติม
  20. ลายน้ำ (watermark): ข้อความจางๆ แสดงตรงกลางทุกหน้า (เช่น "ฉบับร่าง" เอียง 45 องศา ระดับความโปร่งใส 15% เป็นตัวอักษรสีเทาเพื่อความเรียบร้อย)
  21. นิพจน์ (expression): ข้อความที่ระบบ คำนวณเอง จากค่าของฟิลด์อื่น เช่น เรียน คุณ{fullName} หรือยอดรวมท้ายตาราง ระบบภายนอกไม่ต้องส่งค่านี้มา เพราะมันคิดจากค่าที่ส่งมาแล้ว — ดูวิธีใช้ฉบับเต็มที่ ข้อ 7 นิพจน์

3. การตรวจก่อนเผยแพร่ — กฎที่บล็อกเทมเพลตที่จะผลิตเอกสารผิด#

เมื่อผู้ใช้ออกแบบเสร็จและสั่ง "เผยแพร่" (Publish) ระบบจะนำเทมเพลตเข้าสู่ขั้นตอนการตรวจลินต์อย่างละเอียด หากตรวจพบข้อผิดพลาดระดับ error ระบบจะระงับและบล็อกการเผยแพร่ทันทีเพื่อป้องกันเอกสารที่ใช้งานจริงเกิดการชำรุด โดยกฎการตรวจสอบมีดังนี้:

  • field.unnamed: พบฟิลด์ที่ไม่มีการระบุชื่อ
  • field.duplicate_name: พบฟิลด์ที่มีชื่อซ้ำกันในหน้าเดียวกัน
  • field.bad_page_index: หน้าเอกสารที่ระบุให้กับฟิลด์ไม่มีอยู่จริงในเอกสารฐาน (เช่น ระบุหน้า 5 ในขณะที่เอกสารฐานมีเพียง 4 หน้า)
  • field.off_page: มีขอบเขตของฟิลด์บางส่วนหลุดหรือยื่นออกนอกกระดาษ
  • field.overlap: พบฟิลด์สองฟิลด์วางตำแหน่งทับซ้อนกัน (ยกเว้นฟิลด์ประเภทลายน้ำ/watermark)
  • format.invalid: การจัดรูปแบบทศนิยม ตัวคั่น หรือรูปแบบวันที่ในฟิลด์ number/date ไม่ถูกต้อง
  • sample.missing_required: ฟิลด์ที่ทำเครื่องหมายว่าจำเป็น (required) ไม่มีข้อมูลตัวอย่างสำหรับใช้ทดสอบการกรอก
  • font.unknown: ใช้ฟอนต์ที่ระบบไม่รู้จัก
  • font.missing_glyphs: ตัวอักษรภาษาไทยหรือสัญลักษณ์ในข้อมูลตัวอย่างไม่มีอยู่ในชุดแบบอักษร (Font Glyphs) ของฟอนต์ที่เลือก (ระบบมีระบบเช็กวรรณยุกต์และสระลอยของไทยอย่างละเอียดก่อนเผยแพร่)
  • text.hits_font_floor: เมื่อกรอกข้อความตัวอย่างที่ยาวที่สุด ขนาดตัวอักษรถูกปรับย่อต่ำลงจนชนขอบล่างของเกณฑ์การอ่านออกขั้นต่ำ (Legibility Floor) ที่ 7pt สำหรับอักษรภาษาไทย (อักษรไทยมีสระและวรรณยุกต์ซ้อนระดับ จึงมีขนาดขั้นต่ำเพื่อการอ่านออกจริงที่สูงกว่าภาษาอังกฤษ) ระบบจะบล็อกเพื่อให้ผู้ใช้ออกแบบขยายขนาดกรอบข้อความแทนการฝืนบีบอักษรให้เล็กลงจนอ่านไม่ออกบนเอกสารที่เซ็นสัญญาจริง
  • text.overflows_height: ข้อความยาวเกินกว่าความสูงของกล่อง ทำให้ล้นขอบกล่องที่กำหนด
  • expression.unresolved: ชื่อที่อยู่ในวงเล็บปีกกา {} ซึ่ง จะหาค่าไม่เจอตอนสร้างเอกสารจริง ไม่ว่าจะเป็นฟิลด์ที่สะกดผิด ({loanAmont}) หรือตัวแปรที่ระบบไม่รองรับ (Array, Date, Intl) ทั้งสองกรณีจะพิมพ์ข้อความ {...} ดิบๆ ลงบนเอกสารที่ส่งให้ลูกค้า จึงบล็อกการเผยแพร่ไว้ — ส่วนชื่อที่หาค่าเจอจริง (ฟิลด์ในสัญญา, currentPage, totalPages, date, dateTime, Math, Number, String, Boolean, Object, JSON, parseInt, parseFloat, isNaN และตัวแปรใน arrow function ที่คุณประกาศเอง) จะไม่ถูกจับ ดูข้อ 7
  • expression.on_editable_field: ฟิลด์ที่มีนิพจน์อยู่ แต่ถูกติ๊ก “แก้ไขได้” — ระบบจะไม่คำนวณนิพจน์นั้นเลย มันจะไปรอค่าจากระบบภายนอกแทน ซึ่งไม่มีใครถูกขอให้ส่งมา นิพจน์จึงตายเงียบๆ โดยเอกสารยังพิมพ์ออกมาได้ตามปกติ (แต่ผิด) — เอาเครื่องหมายถูกออกจาก “แก้ไขได้” เพื่อให้ฟิลด์กลับมาคำนวณค่าเอง ดูตัวอย่างช่องติ๊กในข้อ 7
  • expression.optional_field: นิพจน์เรียกใช้ฟังก์ชันของระบบ (เช่น bahtText) กับฟิลด์ที่ไม่ได้ตั้งเป็น “ที่จำเป็น” — ถ้าระบบภายนอกไม่ส่งค่านั้นมา เอกสารจะพิมพ์สูตรดิบๆ ลงบนสัญญาตรงช่องจำนวนเงิน ให้ติ๊ก “ที่จำเป็น” ที่ฟิลด์นั้น ระบบจะได้ปฏิเสธคำขอที่ไม่ส่งค่ามา (400) ตั้งแต่ก่อนสร้างเอกสาร (การเปรียบเทียบธรรมดา เช่น {allow === "Y" ? …} ไม่ถูกจับ — ฟิลด์ที่ไม่ส่งค่ามาจะกลายเป็นค่าว่าง ซึ่งเทียบได้ตามปกติ)
  • table.overflows_page: ขอบเขตตารางไหลยาวจนเลยขอบกระดาษด้านล่าง เนื่องจากระบบไม่สามารถจัดการเรื่องการเพิ่มหน้ากระดาษและขยับเลย์เอาต์ตารางแบบอัตโนมัติบนเอกสารฐานที่เป็น PDF อัปโหลดภายนอกได้ (การล้นของข้อมูลตารางจะทำให้ข้อมูลแถวด้านล่างหายไปในไฟล์จริงโดยไม่มีการเตือนความผิดพลาดที่ฝั่งเซิร์ฟเวอร์) ลินต์จึงต้องบล็อกการเผยแพร่ทันทีเพื่อให้ผู้ใช้ไปแบ่งตารางหรือขยายกล่องในหน้าการออกแบบแทน

4. การเผยแพร่และการจัดเก็บเวอร์ชันเอกสาร (Publishing and Versioning)#

  • สถานะฉบับร่าง (Draft): เทมเพลตที่กำลังปรับแต่งหรืออัปโหลดเอกสารฐานใหม่จะมีสถานะเป็นฉบับร่าง ซึ่งสามารถแก้ไขบันทึกได้ตลอดเวลา แต่ระบบภายนอกจะไม่สามารถดึงหรือสั่งสร้างเอกสารจากฉบับร่างนี้ได้
  • การเผยแพร่ (Publishing): การส่งแบบร่างเข้ารับการตรวจสอบลินต์ หากผ่าน ระบบจะบันทึกเป็นเวอร์ชันที่เผยแพร่แล้ว (เช่น เวอร์ชัน 1, 2) ซึ่งจะถูกแช่แข็ง (Frozen) ทันที ไม่สามารถเข้าไปแก้ไขโครงสร้างของเวอร์ชันนี้ได้อีก หากต้องการเปลี่ยนเลย์เอาต์ ระบบจะบังคับสร้างแบบร่างตัวใหม่ขึ้นมาแทน
  • การยกเลิกเผยแพร่ (Unpublish) และการลบ: การสั่งลบ (DELETE) เทมเพลตที่กำลังเผยแพร่อยู่ทำไม่ได้ (ได้รหัส 409 published_cannot_delete) ต้อง "ยกเลิกเผยแพร่" (Unpublish) ก่อน ซึ่งมีผลดังนี้:
    • เทมเพลตหายจากหน้ารายการ แก้ไขต่อไม่ได้ สร้างฉบับร่างใหม่จากมันไม่ได้
    • POST /v1/generate จะตอบ 404 ทันที — นี่คือการ หยุดให้บริการอย่างแท้จริง ไม่ใช่แค่การจัดระเบียบรายการ ระบบภายนอกที่ยังเรียกเทมเพลตนี้อยู่จะทำงานล้มเหลวทันทีที่คุณกดปุ่ม ต้องแน่ใจก่อนว่าไม่มีใครเรียกมันแล้ว
    • เพราะมันคือการตัดบริการจริง ปุ่มนี้จึงไม่ได้วางไว้ให้กดได้โดยง่าย — มันซ่อนอยู่หลังลูกศร ▾ ข้างปุ่ม "เผยแพร่" และกล่องยืนยันจะบอกผลลัพธ์เป็นประโยคเดียวก่อนคุณกดยืนยัน
    • เทมเพลตที่ยกเลิกเผยแพร่แล้ว ลบทิ้งได้ — ยกเว้นกรณีที่เคยมีเอกสารถูกสร้างจากมัน เพราะเอกสารเหล่านั้นคือหลักฐานว่าลูกค้าได้รับอะไรไป เทมเพลตที่เอกสารอ้างถึงอยู่จึงลบไม่ได้ (409)

5. การกรอกข้อมูลผ่านเว็บเบราว์เซอร์ (Browser Filling)#

สำหรับเจ้าหน้าที่ปฏิบัติการสายธุรกิจที่ต้องการสร้างเอกสารรายฉบับด้วยตนเอง สามารถทำได้ผ่านหน้าจอการกรอกข้อมูล:

  1. เข้าลิงก์ของหน้ากรอกข้อมูลที่ /fill/:id (โดย id คือรหัสของเทมเพลต)
  2. เจ้าหน้าที่ล็อกอินเข้าระบบด้วยบัญชีตนเองผ่านระบบคุกกี้เซสชันขององค์กร ไม่ต้องคัดลอกหรือใช้งานกุญแจ API (API Key) ซึ่งมีไว้สำหรับโปรแกรมหรือเครื่องอัตโนมัติเท่านั้น
  3. หน้าจอจะโหลดหน้าเอกสารต้นฉบับจริงพร้อมฟิลด์กรอกข้อมูลแสดงทับในตำแหน่งที่ออกแบบไว้ (ผ่านระบบ Form ของระบบ)
  4. เจ้าหน้าที่สามารถพิมพ์ข้อมูลลงบนหน้านั้นโดยตรง ซึ่งจะได้เห็นฟอนต์และการตัดวรรณยุกต์ภาษาไทยแบบเรียลไทม์ ทำให้มั่นใจว่าเอกสารจะออกมาถูกต้อง สวยงาม และไม่มีการซ้อนทับกัน
  5. เมื่อกรอกข้อมูลเรียบร้อยแล้ว คลิกปุ่ม "สร้างเอกสาร" (Generate) ระบบจะเรียกใช้ API ซิงค์ประมวลผลเอกสาร PDF และส่งกลับมาให้เบราว์เซอร์ดาวน์โหลดไฟล์เก็บไว้ในคอมพิวเตอร์ทันทีโดยใช้ชื่อเดียวกับชื่อของเทมเพลตนั้นๆ

6. ระบบเอกสาร ระยะเวลาเก็บรักษา และบันทึกการตรวจสอบย้อนกลับ (Audit Log)#

  • ระยะเวลาการเก็บรักษาเอกสาร (Retention Policy): เอกสาร PDF ที่สร้างผ่านโหมดอัปโหลดแบบไม่ประสานเวลา (Async Mode) จะถูกจัดเก็บไว้ในถังข้อมูลบนระบบออนเพรมของสถาบันการเงินตามกำหนด (ค่าเริ่มต้น 30 วัน กำหนดผ่าน DOCUMENT_TTL_DAYS) และจะถูกลบออกแบบถาวรเมื่อหมดอายุ หากระบบภายนอกพยายามดาวน์โหลดเอกสารที่หมดอายุแล้วจะได้รับรหัสข้อผิดพลาดกลับไปเป็น 410 Gone
  • บันทึกการตรวจสอบย้อนกลับที่ไม่เก็บข้อมูลลูกค้า (Append-Only & No-PII Audit Log): เพื่อความสอดคล้องกับกฎหมายคุ้มครองข้อมูลส่วนบุคคล (PDPA) บันทึกการตรวจสอบย้อนกลับ (Audit Log) ของ Thinker Docs จะ ไม่มีการเก็บข้อมูลส่วนตัวของลูกค้า (PII) เช่น ชื่อ หมายเลขบัตรประชาชน หรือจำนวนเงินใดๆ ทั้งสิ้น และสำหรับโหมดการสร้างเอกสารแบบประสานเวลา (Sync Mode) ที่ไม่ได้เก็บไฟล์ PDF ไว้บนระบบเลย ระบบจะยังคงทำบันทึกระบุประเภทเทมเพลต เวอร์ชัน วันที่ เวลา ผู้ขอสร้าง (API Key ตัวไหน) และเก็บ รหัสแฮช SHA-256 ของไฟล์ PDF ที่ส่งออกไป ประโยชน์คือ เมื่อเกิดกรณีพิพาทในภายหลัง สถาบันการเงินสามารถนำไฟล์ PDF ที่ผู้ใช้ถืออยู่มาทำการเข้ารหัสแฮช SHA-256 แล้วนำไปเปรียบเทียบกับบันทึกการตรวจสอบในระบบเพื่อยืนยันความถูกต้องของเอกสารได้ทันที โดยที่ระหว่างนั้นระบบ Thinker Docs ไม่จำเป็นต้องเก็บไฟล์จริงหรือข้อมูล PII ของลูกค้าไว้ให้เสี่ยงต่อการรั่วไหลแต่อย่างใด ทั้งนี้ บันทึกการตรวจสอบเป็นระบบต่อท้ายข้อมูลเท่านั้น (Append-only) ไม่มีเมนูแก้ไขหรือลบโดยผู้ใช้งาน และบันทึกการถูกปฏิเสธสิทธิ์ข้ามทีม (outcome: denied) จะถูกรวบรวมมาไว้เพื่อใช้ตรวจหาการโจมตีหรือการใช้คีย์รั่วไหลเป็นประจำทุกสัปดาห์
  • การเปิดดู กรอง และส่งออกบันทึก (หน้า บันทึกตรวจสอบ): ผู้ดูแลทีม (admin) เปิดดูบันทึกได้จากเมนู บันทึกตรวจสอบ ในแถบด้านซ้าย หน้านี้แสดงรายการทีละหน้า (เรียงจากใหม่ไปเก่า) พร้อมชุดตัวกรองด้านบน:
    • ช่วงเวลา — เลือกแบบสำเร็จรูป (24 ชั่วโมง / 7 วัน / 30 วัน / 90 วัน) หรือเลือก "กำหนดเอง" เพื่อระบุวันและเวลา เริ่มต้น–สิ้นสุด ได้เอง ผ่านปฏิทินและตัวเลื่อนเลือกเวลา (ชั่วโมง:นาที) — เหมาะเวลาต้องการดูเฉพาะเหตุการณ์ในกรอบเวลาที่เจาะจง เช่น ช่วงที่เกิดปัญหา
    • การกระทำ — กรองตามชนิดเหตุการณ์ (เช่น generate, apikey.create, member.role_change) และปุ่ม "เฉพาะที่ถูกปฏิเสธ" เพื่อดูเฉพาะเหตุการณ์ที่ถูกปฏิเสธสิทธิ์ (outcome: denied) — คือคำถามที่ควรตรวจทุกสัปดาห์นั่นเอง
    • ปุ่ม "ล้างตัวกรอง" จะปรากฏเมื่อมีตัวกรองที่ไม่ใช่ค่าเริ่มต้น กดเพื่อรีเซ็ตทุกตัวกรองกลับเป็น 7 วัน
    • ปุ่ม "ส่งออก CSV" ดาวน์โหลดบันทึก เฉพาะแถวที่ตรงกับตัวกรองที่เลือกอยู่ในขณะนั้น เป็นไฟล์ CSV — ฝังรหัส UTF-8 BOM ให้ Microsoft Excel อ่านภาษาไทยได้ถูกต้อง และใช้เวลารูปแบบ ISO 8601 ที่เรียงลำดับได้ เหมาะสำหรับนำไปตรวจสอบหรือทำรายงานภายนอก การส่งออกครั้งหนึ่งจำกัดสูงสุด 100,000 แถว หากต้องการมากกว่านั้นให้แบ่งช่วงเวลาให้แคบลงแล้วส่งออกทีละช่วง

7. นิพจน์ (Expression) — ข้อความที่เทมเพลตคำนวณเอง#

นิพจน์ คือข้อความที่ฟิลด์คิดค่าขึ้นมาเอง จากค่าที่ฟิลด์อื่นมีอยู่แล้ว ระบบภายนอกไม่ต้องส่งมา ลากฟิลด์ นิพจน์ (Expression) ลงบนหน้ากระดาษ แล้วพิมพ์ว่า

เรียน คุณ{fullName}

ตอนสร้างเอกสารจริงมันจะพิมพ์ออกมาเป็น เรียน คุณสมชาย — อะไรที่อยู่ในวงเล็บปีกกา { } จะถูกคำนวณ ส่วนที่อยู่นอกวงเล็บพิมพ์ออกมาตามที่เห็น

ทำไมต้องแยกเป็นฟิลด์ชนิดหนึ่งต่างหาก#

ระบบคำนวณ {...} ได้มาโดยตลอด แต่มีเงื่อนไขซ่อนอยู่ 2 ข้อที่ไม่มีอะไรบนหน้าจอบอก คือ (1) ข้อความต้องอยู่ใน content (ข้อความที่ คุณ พิมพ์ลงเทมเพลต ไม่ใช่ช่องข้อมูลตัวอย่าง) และ (2) ฟิลด์นั้นต้องเป็นแบบ อ่านอย่างเดียว (read-only) ก่อน

ถ้าฟิลด์แก้ไขได้ ระบบจะ ไม่สนใจ content เลย แล้วไปพิมพ์ค่าที่ระบบภายนอกส่งมาแทน — แปลว่านิพจน์บนฟิลด์ที่แก้ไขได้จะ เงียบสนิท ไม่ทำงาน และไม่ฟ้องอะไรเลย

พอทำเป็น "ชนิดฟิลด์" เงื่อนไขทั้งสองข้อก็เป็นจริงตั้งแต่วินาทีที่ลากลงมา คุณไม่ต้องรู้กฎพวกนี้เลย

ด้วยเหตุผลเดียวกัน หัว/ท้ายกระดาษ และ ลายน้ำ ก็ใช้นิพจน์ได้ (เพราะมันอ่านอย่างเดียวเหมือนกัน) — พิมพ์ หน้า {currentPage} จาก {totalPages} ในท้ายกระดาษได้เลย ทำงานเองทุกหน้า

สิ่งที่ใส่ใน { } ได้#

ฟิลด์ของคุณเองชื่อฟิลด์ใดก็ได้ที่อยู่ในสัญญาของเทมเพลต — {loanAmount}
เลขหน้า{currentPage}, {totalPages} — นับถูกทุกหน้า แม้จะมีการแนบสแกนเพิ่มท้ายเล่ม
วันที่วันนี้{date}2026/07/14 · {dateTime}2026/07/14 13:39
คณิตศาสตร์และเงื่อนไขMath, Number, String, Boolean, Object, JSON, parseInt, parseFloat, isNaN, + - * / และ ? :
แถวในตารางฟิลด์ตารางส่งมาเป็นอาเรย์ของแถว ใช้ map / reduce / join กับ arrow function ได้
ฟิลด์นิพจน์อื่นนิพจน์อ่านค่าที่นิพจน์ตัวอื่นคำนวณไว้แล้วได้ (ดู "ต่อกันเป็นทอด" ข้างล่าง)

ต่อกันเป็นทอด — วิธีทำยอดรวมท้ายใบแจ้งหนี้#

นิพจน์อ่านค่าที่ นิพจน์ตัวอื่นคำนวณเสร็จแล้ว ได้ ทำให้แตกยอดเป็นขั้นๆ ได้ ไม่ต้องยัดทุกอย่างไว้ในบรรทัดเดียว

ฟิลด์ subtotal :  {orders.reduce((sum, item) => sum + Number(item[1]) * Number(item[2]), 0)}   → 2000
ฟิลด์ tax      :  {Number(subtotal) * 0.07}                                                    → 140
ฟิลด์ total    :  {Number(subtotal) + Number(tax)}                                             → 2140

ลำดับการวางฟิลด์ไม่สำคัญtotal อ้างถึง tax ที่วางไว้ทีหลังก็ได้ ระบบคำนวณให้ถูกเอง

ข้อแม้เดียวคือฟิลด์ที่ถูกอ้างถึงต้องเป็นแบบ อ่านอย่างเดียว (นิพจน์ · หัว/ท้ายกระดาษ · ลายน้ำ · ข้อความที่ตั้งเป็นอ่านอย่างเดียว) ซึ่งฟิลด์นิพจน์เป็นอยู่แล้วโดยธรรมชาติ

กฎข้อเดียวที่จะทำให้คุณเจ็บตัว#

ต้องห่อฟิลด์ด้วย String(...) หรือ Number(...) ก่อน ถึงจะเรียก method ต่อท้ายได้

{loanAmount.padStart(9, "0")}          ✗ พิมพ์ดิบออกมา: {loanAmount.padStart(9, "0")}
{String(loanAmount).padStart(9, "0")}  ✓ พิมพ์ 001250000

ค่าฟิลด์ที่ว่างเปล่ายังไม่ใช่ string จริงในสายตาระบบ การเรียก method โดยตรงจึงล้มเหลว แต่เมื่อห่อด้วยฟังก์ชันแล้วจึงใช้งานได้ — นี่คือสาเหตุอันดับหนึ่งที่นิพจน์ซึ่งดูถูกต้องกลับพิมพ์ออกมาเป็นข้อความที่ผิดเพี้ยน

สูตรสำเร็จ (ทดสอบกับไลบรารีตัวจริงมาแล้วทุกบรรทัด)#

เรียน คุณ{fullName}                                              เรียน คุณสมชาย
หน้า {currentPage} จาก {totalPages}                              หน้า 1 จาก 2

{String(loanAmount).replace(/\B(?=(\d{3})+(?!\d))/g, ",")} บาท   1,250,000 บาท    ← เงินบาท ใส่ลูกน้ำ
{Number(loanAmount).toFixed(2)}                                  1250000.00       ← ทศนิยม 2 ตำแหน่ง
{Math.round(loanAmount / 12)} บาท/เดือน                          104167 บาท/เดือน
{Number(amount) > 1000000 ? "วงเงินสูง" : "วงเงินปกติ"}            วงเงินสูง         ← ข้อความตามเงื่อนไข

{orders.reduce((sum, item) => sum + Number(item[1]) * Number(item[2]), 0)}
                                                                 2000             ← ยอดรวมท้ายตาราง

ระบบ ไม่มี toLocaleString และ ไม่มี Intl (อ่านหัวข้อถัดไป) ดังนั้นสูตร regex ข้างบน คือ วิธีใส่ลูกน้ำในจำนวนเงินบาท คัดลอกไปใช้ได้เลยตามนั้น

ตัวอย่างจริง — ช่องติ๊ก "อนุญาต / ไม่อนุญาต" ที่ตรงข้ามกันเสมอ#

ระบบภายนอกส่งมาแค่ค่าเดียว ("Y" หรือ "N") แล้วให้เอกสารติ๊กช่องให้ถูกเอง ใช้ 3 ฟิลด์:

ฟิลด์ชนิดแก้ไขได้ข้อความในฟิลด์
allowข้อความ✅ ติ๊ก(ว่างไว้) — API ส่ง "Y" / "N" มาลงตรงนี้
boxYesช่องติ๊กไม่ติ๊ก{allow === "Y" ? "true" : "false"}
boxNoช่องติ๊กไม่ติ๊ก{allow === "N" ? "true" : "false"}

ผลลัพธ์บนเอกสารจริง:

API ส่ง  allow: "Y"   →   ☑ อนุญาต      ☐ ไม่อนุญาต
API ส่ง  allow: "N"   →   ☐ อนุญาต      ☑ ไม่อนุญาต
API ไม่ส่ง allow มาเลย →   ☐ อนุญาต      ☐ ไม่อนุญาต     ← ยังไม่ได้ตอบ ก็ต้องไม่ติ๊กอะไรเลย

สองช่องนี้ตรงข้ามกันโดยโครงสร้าง ติ๊กพร้อมกันทั้งคู่ไม่ได้ และเว้นว่างทั้งคู่ได้เฉพาะตอนที่ยังไม่มีคำตอบ บนหนังสือยินยอม ช่องที่ติ๊กทั้งสองอันคือเอกสารที่บังคับใช้ไม่ได้ — ตรงนี้จึงเป็นไปไม่ได้ตั้งแต่แรก

สามเรื่องที่คนทำพลาดบ่อยที่สุด — และระบบจะบล็อกให้ทั้งหมด:

  1. เอานิพจน์ไปใส่ที่ฟิลด์ allow เอง (ให้มันอ้างถึงตัวเอง) — ไม่ได้ allow คือข้อมูลดิบที่ระบบภายนอกส่งมา ส่วนช่องติ๊กคือผลลัพธ์ที่คิดจากข้อมูลนั้น คนละหน้าที่กัน ต้องแยกฟิลด์
  2. ติ๊ก "แก้ไขได้" ที่ช่องติ๊กสองอันนั้น — นิพจน์จะตายทันที ระบบจะเลิกคำนวณแล้วไปรอค่าจากระบบภายนอก ซึ่งไม่มีใครส่งมาให้ (ระบบตรวจจะฟ้อง expression.on_editable_field)
  3. ใช้ "ช่องติ๊ก" เป็นตัวรับค่า allow — ช่องติ๊กรับได้แค่ "true" / "false" เท่านั้น รับ "Y" / "N" ไม่ได้ allow จึงต้องเป็นฟิลด์ ข้อความ

ไม่อยากให้ตัว "Y" ปรากฏบนกระดาษ? ตั้ง ความทึบ (opacity) = 0 ให้ฟิลด์ allow แล้ววางไว้มุมกระดาษ — มันจะยังอยู่ในสัญญาฟิลด์ให้ระบบภายนอกส่งค่ามาได้ แต่ไม่ปรากฏบนเอกสารที่พิมพ์ออกมา

สิ่งที่ใช้ไม่ได้ — และทำไมระบบถึงต้องบล็อกคุณ#

ระบบไม่รองรับการใช้งาน Array, Date และตระกูล encodeURI/decodeURI ซึ่งหากนำมาใช้งาน ระบบจะไม่แจ้งข้อผิดพลาด (error) ใดๆ แต่จะพิมพ์ข้อความดิบ {...} ลงบนเอกสารจริงโดยตรง:

{Array.isArray(orders)}             → พิมพ์ "{Array.isArray(orders)}" ลงบน PDF
{new Date().getFullYear() + 543}    → พิมพ์ "{new Date().getFullYear() + 543}"
{new Intl.NumberFormat(...)}        → พิมพ์ "{new Intl.NumberFormat(...)}"
{loanAmont}                         → พิมพ์ "{loanAmont}"  ← สะกดผิดตัวเดียว

นี่คืออันตรายทั้งหมดของนิพจน์: นิพจน์ที่ทำงานผิดพลาดไม่ใช่ error แต่คือเอกสารที่ผิด — พิมพ์เสร็จแล้ว เซ็นแล้ว ส่งถึงมือลูกค้าแล้ว กว่าจะรู้ตัว

expression.unresolved จึงบล็อกการเผยแพร่ทุกกรณีข้างบน รวมถึงคำที่สะกดผิดด้วย ถ้าระบบทักชื่อตัวแปรที่คุณมั่นใจว่าถูก มันกำลังบอกคุณว่าตัวนั้นจะพิมพ์ดิบลงเอกสาร — เชื่อมันไว้ก่อน

ด้วยหลักการเดียวกัน Math.round ใช้ได้ แต่ eval, constructor และ process ถูกกล่องทรายของระบบปฏิเสธทิ้งทั้งหมด — นิพจน์เอื้อมไปแตะเซิร์ฟเวอร์ที่มันรันอยู่ไม่ได้

ค่ามาจากไหน#

ฟิลด์นิพจน์ ไม่อยู่ใน API contract และไม่มีการขอให้ระบบภายนอกส่งค่านี้มา เพราะมันอ่านค่าจากฟิลด์ที่เขาส่งมาอยู่แล้ว การเพิ่มนิพจน์ลงเทมเพลตจึง ไม่กระทบ JSON ที่ผู้เรียกส่งเข้ามาเลย ค่าจะถูกคำนวณตอนสร้างเอกสาร ทุกหน้าที่มันปรากฏ

/docs/manual.th.md — this page renders that file. It does not keep a second copy, so the two can never disagree.