โหมดมืด
แนวทางขยายระบบ
ทุกการปรับแต่งมีต้นทุนตอนอัปเกรด หน้านี้เรียงทางเลือกจากต้นทุนต่ำไปสูง ให้เลือกทางที่ต่ำที่สุดที่แก้ปัญหาได้จริง
การเพิ่มฟิลด์ผ่านเครื่องมือในระบบมีต้นทุนต่ำสุด เพราะระบบรู้จักฟิลด์นั้นและจะพาไปกับการอัปเกรดเอง ถัดมาคือการเขียนสคริปต์ที่จุดต่อที่กำหนดไว้ ซึ่งจะทำงานเมื่อเกิดเหตุการณ์บนเอกสาร ต้นทุนสูงขึ้นเพราะต้องทดสอบซ้ำทุกครั้งที่อัปเกรด แต่ยังอยู่ในขอบเขตที่รองรับ ทางที่แพงที่สุดคือการเขียนบริการภายนอกที่ถือ state ของตัวเองขนานไปกับระบบ ทางนี้ควรใช้เมื่อจำเป็นจริงเท่านั้น และต้องมีเจ้าของดูแลระยะยาว
กฎข้อแรก — แกนต้องสะอาด
โค้ดแกน (frappe / erpnext / hrms) ห้ามแก้ในที่ ทุกการเปลี่ยนพฤติกรรมของแกน ต้องอยู่ในแอปของเรา เหตุผลไม่ใช่ความสวยงาม แต่คือการอัปเกรด — แกนที่ถูกแก้ในที่ จะอัปเกรดไม่ได้โดยไม่เสียงาน และไม่มีวิธีรู้ว่าถูกแก้อะไรไปบ้างนอกจากไล่เทียบทีละไฟล์
ด้วยเหตุผลเดียวกัน custom field ควรสร้างด้วยโค้ด ไม่ใช่ export เป็น fixture มือ ๆ — คำสั่ง export จะเขียนทับไฟล์ด้วยเฉพาะสิ่งที่มีในไซต์ที่คุณรันอยู่ ฟิลด์ของไซต์อื่น ที่ใช้แอปเดียวกันจะหายไปเงียบ ๆ (เคยเกิดมาแล้ว)
จุดต่อที่มี เรียงจากต้นทุนต่ำไปสูง
1. เพิ่มฟิลด์และปรับหน้าจอผ่านเครื่องมือในระบบ
Custom Field เพิ่มช่องใหม่บนเอกสารมาตรฐาน · Property Setter เปลี่ยนคุณสมบัติ ของฟิลด์เดิม เช่นซ่อน บังคับกรอก เปลี่ยนป้าย · Client Script ใส่ตรรกะฝั่งหน้าจอ เช่นคำนวณช่องหนึ่งจากอีกช่อง
ต้นทุนต่ำสุดเพราะระบบรู้จักของพวกนี้และพาไปกับการอัปเกรดเอง ในโครงของ KlevIQ ทั้งสามอย่างถูกผูกเข้ากับโมดูล ของแอปปรับแต่ง เพื่อให้ export ออกมาเป็นชั้น ๆ แยกกันได้
2. เกาะเหตุการณ์บนเอกสาร — doc_events
ประกาศในไฟล์ hooks.py ของแอปว่า เมื่อเอกสารประเภทนี้เกิดเหตุการณ์นี้ ให้เรียกฟังก์ชันไหน เหตุการณ์ที่ใช้บ่อยคือ validate, before_insert, after_insert, on_submit, on_cancel, on_update_after_submit
ตัวอย่างจริงในระบบ — Flow เกาะ on_update / on_submit / on_cancel ของ เอกสารทุกประเภท เพื่อส่งการแจ้งเตือน · ชั้นปรับแต่งของลูกค้าเกาะ validate ของใบแจ้งหนี้ขายเพื่อคัดลอกหมายเหตุจากใบสั่งขายมาให้
เกาะ "*" มีราคา
การเกาะทุก doctype แปลว่าโค้ดของคุณจะทำงานทุกครั้งที่มีการบันทึกอะไรก็ตามในระบบ รวมทั้ง log และตารางเบื้องหลัง ต้องตัดออกให้เร็วที่สุดในบรรทัดแรก ๆ และห้าม throw มิฉะนั้นคุณจะทำให้ทั้งระบบบันทึกอะไรไม่ได้เลย
3. แทนที่ฟังก์ชันของแกน — override_whitelisted_methods
ใช้เมื่อต้องเปลี่ยนพฤติกรรมของปุ่มมาตรฐาน แผนที่ใน hooks.py จะชี้ชื่อฟังก์ชันเดิม ของ ERPNext ไปยังฟังก์ชันของเรา ตัวอย่างจริง — ERPNext ยอมให้สร้างใบจัดของ จากใบส่งของที่ยังเป็นร่างเท่านั้น ลูกค้ารายหนึ่งต้องการให้สร้างจากใบที่ยืนยันแล้วได้ จึงแทนที่ฟังก์ชันนั้นทั้งตัว
การคัดลอกโค้ดแกนมาแก้ = ผูกกับเวอร์ชัน
วิธีนี้คือการก๊อปโค้ดของแกนมาแล้วแก้บางบรรทัด พออัปเกรดแกน โค้ดที่ก๊อปมาจะกลายเป็น ของเก่าทันทีโดยไม่มีอะไรเตือน
ทางแก้ที่ใช้อยู่คือ ตัวกั้นเวอร์ชัน — บันทึกไว้ว่าโค้ดชุดนี้ก๊อปมาจากเวอร์ชันไหน แล้วตอนติดตั้ง patch ให้เทียบกับเวอร์ชันที่รันอยู่จริง ถ้าไม่ตรงให้ข้ามและบันทึกไว้ ไม่ใช่ฝืนทำงานต่อ ผลคือหลังอัปเกรดใหญ่ ไซต์นั้นจะรันแกนสะอาด — ฟีเจอร์ปิด ไม่ใช่พัง จนกว่าจะมีคนไปทำใหม่ให้ตรงเวอร์ชัน
4. งานตามเวลาและงานหลังอัปเกรด
scheduler_events สำหรับงานตามรอบ · after_migrate สำหรับสิ่งที่ต้องสร้างใหม่ ทุกครั้งหลังอัปเกรด
after_migrate สำคัญกว่าที่คิด เพราะการอัปเกรดจะโหลดนิยาม doctype จากไฟล์ทับของเดิม สิ่งที่เราตั้งไว้ในฐานข้อมูลจะถูกล้าง ของที่ต้อง "ประกาศใหม่ทุกครั้ง" จึงต้องอยู่ตรงนี้ — ในระบบจริงใช้กับการเปิด/ปิดโมดูลรายไซต์ และการติดตั้งฟิลด์กับรูปแบบใบพิมพ์ของระบบขายหน้าร้าน
5. เพิ่มของให้ใบพิมพ์ — jinja.methods
ลงทะเบียนฟังก์ชันให้เรียกใช้ได้จากรูปแบบใบพิมพ์ ตัวอย่างจริงคือฟังก์ชันแปลงจำนวนเงิน เป็นตัวหนังสือไทย ซึ่งใบสำคัญจ่ายและเช็คต้องใช้
6. เขียนแอปใหม่ทั้งตัว
เมื่อสิ่งที่จะทำมี doctype เป็นของตัวเอง มีหน้าจอของตัวเอง และมีอายุยืนกว่าโครงการหนึ่งโครงการ ให้แยกเป็นแอปต่างหากแทนที่จะยัดลงแอปเดิม แล้วประกาศ required_apps ให้ชัดว่าพึ่งอะไรอยู่
7. บริการนอก bench
แพงที่สุด ใช้เมื่อจำเป็นจริง — ฝั่งนอกคุยกับ ERP ผ่าน API เท่านั้น และต้องไม่ถือ state ที่ซ้ำกับ ERP ถ้าเลี่ยงไม่ได้ ต้องมีคำตอบไว้ล่วงหน้าว่า ฝั่งไหนถูกเมื่อสองฝั่งไม่ตรงกัน
ข้อควรระวังที่ใช้ได้กับทุกจุดต่อ
อย่าอ้างถึงฟิลด์ที่มีเฉพาะบางเวอร์ชัน ในแอปที่ใช้ร่วมกันหลายลูกค้า ฟลีตรัน Frappe/ERPNext คนละเวอร์ชันกัน ไซต์ที่ไม่มีฟิลด์นั้นจะพังทันทีที่โค้ดวิ่งถึง
อย่าเดาชื่อฟิลด์ ชื่อที่ใกล้เคียงกันมีจริงและไม่เหมือนกัน การเดาผิดมักไม่ทำให้เกิด error แต่ทำให้เงื่อนไขไม่แมตช์อะไรเลย แล้วผลลัพธ์ออกมาเป็นศูนย์อย่างเงียบ ๆ ซึ่งหาสาเหตุยากกว่าพังตรง ๆ มาก — เปิดนิยาม doctype ดูทุกครั้ง
แยกให้ออกระหว่าง "ของที่ใช้ร่วมกัน" กับ "ของลูกค้ารายนี้" การปรับแต่งที่เขียนไว้ผิดชั้น จะไปโผล่ที่ลูกค้าอื่นตอนอัปเดตครั้งถัดไป
สิ่งที่ไม่รองรับ
การเขียนลงตารางฐานข้อมูลตรง การแก้ไฟล์ในแกนระบบ และการพึ่งพารูปแบบภายในที่ไม่ปรากฏในเอกสารอ้างอิง สามอย่างนี้ไม่รองรับและไม่มีการแจ้งเตือนเมื่อเปลี่ยนแปลง