Files
wms-app/scripts/sdlc-seed/docs-pm2.mjs
T

410 lines
37 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// PM work products 3–10: Progress Status Record, Correction Register, Acceptance Report,
// Change Report, Meeting Record, Software Configuration, Project Repository (+ Backup).
import { P, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, WPS, TEAM, ORIGIN, BACKUP, TAG, BASELINE_COMMIT, TEST_FROM, TEST_TO, ACCEPT_DATE, OPS_CLOSE_DATE } from './project.mjs';
import { TASKS, taskById, CORRECTIONS, CHANGES, PERIODS, MEETINGS } from './history.mjs';
import { bud, thLong, thShort, thRange, thRangeLong, table, bullets, daysBetween } from './lib.mjs';
import { workProduct, head } from './format.mjs';
const ids = (arr) => arr.map((i) => P[i].id).join(', ');
const dir3 = '1-PM Process (10 Work Product)/3.Progress Status Record';
/** 3. Progress Status Record — one per reporting period. */
export function progressStatusRecords() {
return PERIODS.map(([date, from, to, weekNo, taskRows, issueIds, changeIds, next]) => {
const tasks = taskRows.map(([tid, status, pct, remark]) => {
const t = taskById(tid);
const icon = status === 'Completed' ? 'Completed' : status === 'In Progress' ? 'In Progress' : 'Planned';
return [tid, t[2], thRange(t[5], t[6]), ids(t[4]), icon, pct, remark];
});
const issues = issueIds.map((iid, n) => {
const c = CORRECTIONS.find((x) => x[0] === iid);
return [n + 1, `${c[0]} (${thShort(c[1])})`, c[3], c[4], c[5], `${c[6]} (${thShort(c[2])})`, P[c[7]].id, 'เสร็จสิ้น'];
});
const changes = changeIds.map((cid) => {
const c = CHANGES.find((x) => x.id === cid);
return [c.id, c.titleTh, 'อนุมัติ', c.impact[0][1]];
});
return {
dir: dir3,
name: `${CODE} Progress Status Record ${bud(date)} V1.0 ApS`,
md: workProduct({
type: 'Progress Status Record', footer: 'PR',
rows: head('Progress Status Record', `${bud(date)} V1.0 ApS`, [
['Title', `รายงานสรุปงวดที่ ${weekNo} : ${thLong(date)}`],
['Reporting Period', thRangeLong(from, to)],
['Organizer', ORGANIZER_PM],
['Recorder', RECORDER],
]),
body: [
'## สรุปสถานะโครงการ',
table(['Task ID', 'Task Name', 'Period', 'Responsible', 'Status', 'Progress (%)', 'Remarks'], tasks, ['c', '', 'c', 'c', 'c', 'r', '']),
'## ความเสี่ยง/อุปสรรค',
issues.length
? table(['ลำดับ', 'Issue ID', 'ประเด็น', 'รายละเอียด', 'สาเหตุที่พบ', 'แนวทางการแก้ไข', 'ผู้รับผิดชอบ', 'สถานะ'], issues, ['c', '', '', '', '', '', 'c', 'c'])
: table(['ลำดับ', 'Issue ID', 'ประเด็น', 'รายละเอียด', 'สาเหตุที่พบ', 'แนวทางการแก้ไข', 'ผู้รับผิดชอบ', 'สถานะ'], [['-', '-', 'ไม่พบประเด็นความเสี่ยงหรืออุปสรรคในงวดนี้', '-', '-', '-', '-', '-']], ['c', '', '', '', '', '', 'c', 'c']),
'## สรุป CR/Change (ถ้ามี)',
changes.length
? table(['CR ID', 'หัวข้อ', 'สถานะ', 'ผลกระทบ'], changes, ['c', '', 'c', ''])
: table(['CR ID', 'หัวข้อ', 'สถานะ', 'ผลกระทบ'], [['-', 'ไม่มีคำขอเปลี่ยนแปลงในงวดนี้', '-', '-']], ['c', '', 'c', '']),
'## Next Meeting',
next
? table(['รายละเอียด', 'วันที่', 'ผู้จัดประชุม', 'หัวข้อหลัก'], [[`ประชุมติดตามงวดที่ ${weekNo + 1}`, thLong(next.date), `${P.ApS.th} (${P.ApS.id})`, next.topic]], ['', 'c', '', ''])
: table(['รายละเอียด', 'วันที่', 'ผู้จัดประชุม', 'หัวข้อหลัก'], [['ไม่มีการประชุมถัดไป โครงการปิดอย่างเป็นทางการแล้ว', '-', '-', '-']], ['', 'c', '', '']),
].join('\n\n'),
secretary: P.YaB, reviewer: P.ApS, approver: P.SeV,
}),
};
});
}
/** 4. Correction Register — all issues found during the project. */
export function correctionRegister() {
const date = '2026-08-17';
const rows = CORRECTIONS.map((c, i) => [i + 1, c[0], thShort(c[1]), c[4], c[5], c[6], P[c[7]].id, thShort(c[2]), 'เสร็จสิ้น']);
const bySev = (s) => CORRECTIONS.filter((c) => c[3] === s).length;
return {
dir: '1-PM Process (10 Work Product)/4.Correction Register',
name: `${CODE} Correction Register ${bud(date)} V1.0 YaB`,
md: workProduct({
type: 'Correction Register', footer: 'CoR',
rows: head('Correction Register', `${bud(date)} V1.0 YaB`, [
['Title', 'เอกสารสรุปปัญหาที่พบระหว่างดำเนินโครงการ'],
['Date', thLong(date)],
['Organizer', ORGANIZER],
['Recorder', RECORDER],
]),
body: [
'## ตารางแสดงปัญหาที่พบระหว่างดำเนินโครงการ',
table(['No', 'เลขที่', 'วันที่พบปัญหา', 'คำอธิบายปัญหา', 'สาเหตุที่พบ', 'การดำเนินการแก้ไข', 'ผู้รับผิดชอบ', 'วันแก้ไขแล้วเสร็จ', 'สถานะ'], rows, ['c', 'c', 'c', '', '', '', 'c', 'c', 'c']),
'## สรุปผลการแก้ไข',
table(['รายการ', 'จำนวน'], [
['ปัญหาที่บันทึกทั้งหมด', CORRECTIONS.length],
['ระดับความรุนแรงสูง', bySev('สูง')],
['ระดับความรุนแรงกลาง', bySev('กลาง')],
['ระดับความรุนแรงต่ำ', bySev('ต่ำ')],
['แก้ไขแล้วเสร็จและตรวจสอบผลแล้ว', CORRECTIONS.length],
['คงค้าง', 0],
], ['', 'r']),
`ปัญหาทุกรายการได้รับการแก้ไขและตรวจสอบผลด้วย Test Case ที่เกี่ยวข้อง โดยผลการทดสอบผ่านทั้งหมดในรอบทดสอบระหว่าง ${thRangeLong(TEST_FROM, TEST_TO)} ไม่มีข้อบกพร่องระดับวิกฤตคงค้าง ณ วันตรวจรับ`,
'## การเชื่อมโยงกับหลักฐานการทดสอบ',
table(['เลขที่', 'การแก้ไขอ้างอิง (Commit)', 'Test Case ที่ใช้ตรวจสอบ', 'ผลการทดสอบ'], CORRECTIONS.map((c) => [c[0], `\`${c[8]}\``, c[9], 'Passed']), ['c', '', 'c', 'c']),
].join('\n\n'),
secretary: P.YaB, reviewer: P.ApS, approver: P.SeV,
}),
};
}
/** 5. Acceptance Report — the sign-off record. */
export function acceptanceReport() {
return {
dir: '1-PM Process (10 Work Product)/5.Acceptance Report',
name: `${CODE} Acceptance Report ${bud(ACCEPT_DATE)} V1.0 ApS`,
md: workProduct({
type: 'Acceptance Report', footer: 'AR',
rows: head('Acceptance Report', `${bud(ACCEPT_DATE)} V1.0 ApS`, [
['Title', 'บันทึกการส่งมอบงาน (Acceptance Record)'],
['ระยะเวลา', thRangeLong(START, END)],
['ผู้ส่งมอบ', `${P.ApS.th} (Project Manager)`],
['ผู้รับมอบ', `${P.SeV.th} (Project Sponsor)`],
['วันที่บันทึกส่งมอบ', thLong(ACCEPT_DATE)],
]),
body: [
'## รายการสิ่งส่งมอบ (Work Products)',
table(['ลำดับ', 'รหัส', 'สิ่งส่งมอบ (Work Product)', 'รายละเอียด', 'ผลการตรวจรับ (Accepted / Not Accepted)'],
WPS.map((w, i) => [i + 1, w[0], w[1], w[2], 'Accepted']), ['c', 'c', '', '', 'c']),
'## ผลการตรวจสอบตามเกณฑ์การตรวจรับ',
table(['ลำดับ', 'เกณฑ์การตรวจรับ', 'หลักฐานอ้างอิง', 'ผลการตรวจสอบ'], [
[1, 'ฟังก์ชันในขอบเขตทำงานตรงตาม Customer Requirements ครบทุกรายการ', 'Traceability Record, Test Report', 'ผ่าน'],
[2, 'ทดสอบระบบตาม Test Case ครบถ้วน', `Test Report 45 Test Case ผ่านทั้งหมด (${thRangeLong(TEST_FROM, TEST_TO)})`, 'ผ่าน'],
[3, 'ทดสอบการยอมรับโดยผู้ใช้ (UAT)', 'Validation Results 12 สถานการณ์ ผ่านทั้งหมด', 'ผ่าน'],
[4, 'ไม่มีข้อบกพร่องระดับวิกฤตคงค้าง', `Correction Register ${CORRECTIONS.length} รายการ แก้ไขและตรวจสอบครบ`, 'ผ่าน'],
[5, 'เอกสารคู่มือผู้ใช้ ผู้ดูแลระบบ และการบำรุงรักษาครบถ้วน', 'Software User Document, Product Operation Guide, Maintenance Document', 'ผ่าน'],
[6, 'Source Code และเอกสารจัดเก็บใน Repository พร้อมชุดสำรอง', 'Project Repository, Project Repository (Backup)', 'ผ่าน'],
[7, 'ผลการตรวจสอบ Work Products โดยผู้ตรวจสอบอิสระ', 'Verification Results รอบที่ 4', 'ผ่าน'],
], ['c', '', '', 'c']),
'## สรุปผลการตรวจรับ',
table(['รายการ', 'ผลสรุป'], [
['จำนวนสิ่งส่งมอบทั้งหมด', `${WPS.length} รายการ`],
['ตรวจรับแล้ว (Accepted)', `${WPS.length} รายการ`],
['ไม่ผ่านการตรวจรับ (Not Accepted)', '0 รายการ'],
['ข้อบกพร่องคงค้าง', 'ไม่มี'],
['ผลการตรวจรับโดยรวม', '**Accepted** — ตรวจรับส่งมอบระบบครบถ้วน'],
]),
`ผู้รับมอบได้ตรวจสอบสิ่งส่งมอบทั้งหมดตามเกณฑ์การตรวจรับที่กำหนดในเอกสาร Statement of Work และ Software Project Plan แล้วเห็นว่าครบถ้วนถูกต้อง จึงมีมติตรวจรับส่งมอบระบบเมื่อวันที่ ${thLong(ACCEPT_DATE)} โดยงานอบรมผู้ใช้งานและการปิดงานควบคุมปฏิบัติการดำเนินการแล้วเสร็จภายในวันที่ ${thLong(END)} ตามแผนปิดโครงการ`,
].join('\n\n'),
secretary: P.ApS, reviewer: P.YaB, approver: P.SeV,
}),
};
}
/** 6. Change Report — one document per change request. */
export function changeReports() {
return CHANGES.map((c) => ({
dir: '1-PM Process (10 Work Product)/6.Change Report',
name: `${CODE} Change Report ${bud(c.date)} V1.0 ${c.requester.id}`,
md: workProduct({
type: 'Change Report', footer: 'CR',
rows: head('Change Report', `${bud(c.date)} V1.0 ${c.requester.id}`, [
['Change ID', c.id, 'วันที่ร้องขอ', thLong(c.date)],
['ผู้ร้องขอ (Requester)', `${c.requester.th} (${c.requester.role})`],
['หน่วยงาน/ฝ่าย', c.unit],
['ประเภทการเปลี่ยนแปลง', `☒ ${c.type} ☒ ${c.typeChecked} ☐ Schedule ☐ Cost ☐ Resource ☐ Other`],
['ความเร่งด่วน (Priority)', `☐ High ☒ ${c.priority} ☐ Low`],
]),
body: [
'## ส่วนที่ 1 : ข้อมูลทั่วไป',
table(['หัวข้อ', 'รายละเอียด'], [
['ชื่อการเปลี่ยนแปลง', c.titleTh],
['โครงการ', PROJECT_TH],
['ระยะที่เกิดการเปลี่ยนแปลง', `ช่วงดำเนินโครงการ (${thLong(c.date)})`],
['เอกสารที่เกี่ยวข้อง', 'Customer Requirements, Software Project Plan, Traceability Record'],
]),
'## ส่วนที่ 2 : รายละเอียดการเปลี่ยนแปลง',
table(['ลำดับ', 'รายการ', 'รายละเอียด'], [
[1, 'คำอธิบายการเปลี่ยนแปลงที่ต้องการ (Description)', c.description],
[2, 'เหตุผลของการเปลี่ยนแปลง (Justification)', c.justification],
], ['c']),
'## ส่วนที่ 3 : การประเมินผลกระทบ (Impact Analysis)',
table(['ลำดับ', 'รายการ', 'รายละเอียด'], c.impact.map((r, i) => [i + 1, r[0], r[1]]), ['c']),
'## ส่วนที่ 4 : การพิจารณาอนุมัติ (Change Advisory Board Approval)',
table(['ลำดับ', 'ชื่อผู้พิจารณา', 'ตำแหน่ง', 'ความคิดเห็น / ลงนาม'], [
[1, P.ApS.th, 'Project Manager', 'เห็นควรอนุมัติ ผลกระทบอยู่ในวิสัยที่ควบคุมได้'],
[2, P.NoC.th, 'System Analyst', 'สอดคล้องกับความต้องการและการออกแบบ'],
[3, P.PaNg.th, 'QA / Tester', 'ต้องทดสอบซ้ำในส่วนที่ได้รับผลกระทบ'],
[4, P.SeV.th, 'Project Sponsor', 'อนุมัติ'],
], ['c']),
'## ส่วนที่ 5 : สรุปผลการพิจารณา',
table(['หัวข้อ', 'รายละเอียด'], [
['สถานะ', '☒ อนุมัติ (Approved) ☐ ไม่อนุมัติ (Rejected)'],
['การอ้างอิงการดำเนินการ', `Commit \`${c.commit}\``],
['ผลการดำเนินการ', c.result],
]),
'## ภาคผนวก A : เกณฑ์การประเมินระดับความรุนแรงของการเปลี่ยนแปลง (Change Criteria)',
'ตารางนี้ใช้เป็นแนวทางประเมินระดับความรุนแรง (Priority Level) ของการเปลี่ยนแปลง เพื่อให้คณะพิจารณา (Change Advisory Board – CAB) กำหนดระดับผลกระทบและความเร่งด่วนได้ชัดเจนตามมาตรฐาน ISO/IEC 29110',
table(['ระดับ (Level)', 'คำจำกัดความ (Definition)', 'ตัวอย่างสถานการณ์ (Examples)', 'ผลกระทบ (Impact Scope)'], [
['High', 'การเปลี่ยนแปลงที่มีผลกระทบอย่างมีนัยสำคัญต่อระบบหลัก งบประมาณ หรือแผนโครงการ ซึ่งต้องได้รับอนุมัติจาก Project Manager และ Project Sponsor', 'ปรับสถาปัตยกรรมหลักของระบบ / เพิ่มฟังก์ชันหลักใหม่ / ต้องปรับงบประมาณหรือระยะเวลาโครงการ', 'กระทบ Scope, Schedule และ Cost โดยตรง'],
['Medium', 'การเปลี่ยนแปลงที่มีผลกระทบต่อส่วนติดต่อผู้ใช้งานหรือฟังก์ชันย่อยบางส่วน แต่ไม่กระทบโครงสร้างหลัก', 'ปรับปรุงหน้าจอ / ปรับข้อความหรือตรรกะบางส่วน / เพิ่มการตรวจสอบข้อมูล', 'กระทบ Quality หรือ Deliverables บางส่วน'],
['Low', 'การเปลี่ยนแปลงขนาดเล็กที่ไม่ส่งผลต่อการทำงานหลักของระบบ', 'แก้ไขคำสะกดหรือข้อความแสดงผล / เปลี่ยนโลโก้หรือรูปภาพ / ปรับฟอนต์หรือสี', 'ผลกระทบเล็กน้อยต่อ Deliverables'],
]),
'**แนวทางการใช้งาน**',
bullets([
'ผู้ร้องขอ (Requester) ระบุระดับความรุนแรงตามตารางนี้ในส่วน "ความเร่งด่วน (Priority)"',
'คณะพิจารณา (CAB) ใช้ตารางนี้ประกอบการพิจารณาในส่วน "การประเมินผลกระทบ (Impact Analysis)"',
'หากมีข้อสงสัยในระดับผลกระทบ ให้ใช้ระดับที่สูงกว่าเพื่อความปลอดภัยของโครงการ',
]),
].join('\n\n'),
secretary: c.requester, reviewer: P.ApS, approver: P.SeV,
}),
}));
}
/** 7. Meeting Record — Minutes of Meeting per checkpoint. */
export function meetingRecords() {
return MEETINGS.map((m) => {
const t = taskById(m.taskId);
const participants = TEAM.map((p) => [p.th, p.id, p.role, '✔']);
return {
dir: '1-PM Process (10 Work Product)/7.Meeting Record',
name: `${CODE} Minutes Of Meeting ${bud(m.date)} V1.0 YaB`,
md: workProduct({
type: 'Minutes Of Meeting', footer: 'MoM',
rows: head('Minutes Of Meeting', `${bud(m.date)} V1.0 YaB`, [
['Phase', m.phase],
['Task ID / Task Name', `${m.taskId} ${m.taskName}`],
['Work Period of Task', thRangeLong(t[5], t[6])],
['Meeting Date', thLong(m.date), 'Time', m.time],
['สิ่งแนบ', m.attachment],
['Location', `ห้องประชุม ${COMPANY_TH}`],
['Organizer', ORGANIZER],
['Recorder', RECORDER],
['สำเนาถึง (CC)', `${P.SeV.th} (Project Sponsor)`],
]),
body: [
'## ผู้เข้าร่วมประชุม (Participants)',
table(['ชื่อ - สกุล', 'ชื่อย่อ', 'ตำแหน่ง/บทบาท', '(✔ เข้าร่วม / ✘ ไม่เข้าร่วม)'], participants, ['', 'c', '', 'c']),
'## ความคืบหน้ารวมของโครงการ (Project Progress)',
table(['หมวดงาน', 'สถานะ', 'ความคืบหน้า (%)', 'รายละเอียด'], m.progress, ['', 'c', 'r', '']),
'## หัวข้อการประชุม (Agenda)',
table(['ลำดับ', 'หัวข้อการประชุม', 'วัตถุประสงค์'], m.agenda.map((a, i) => [i + 1, a[0], a[1]]), ['c']),
'## สรุปรายงานการประชุม (Discussion Summary)',
table(['ลำดับ', 'ประเด็น', 'รายละเอียด / มติที่ได้', 'ผู้รับผิดชอบ', 'สถานะ'], m.discussion.map((d, i) => [i + 1, d[0], d[1], ids(d[2]), d[3]]), ['c', '', '', 'c', 'c']),
'## งานที่ต้องติดตาม (Action Item)',
table(['ลำดับ', 'งาน', 'ผู้รับผิดชอบ', 'วันครบกำหนด', 'สถานะ'], m.actions.map((a, i) => [i + 1, a[0], ids(a[1]), thLong(a[2]), a[3]]), ['c', '', 'c', 'c', 'c']),
'## งานประชุมครั้งถัดไป (Next Meeting)',
m.next
? table(['หัวข้อการประชุมถัดไป', 'วันที่', 'ผู้จัดประชุม', 'วัตถุประสงค์'], [[m.next.topic, thLong(m.next.date), `${P.ApS.th} (${P.ApS.id})`, 'ติดตามความคืบหน้าและมติที่เกี่ยวข้อง']], ['', 'c', '', ''])
: table(['หัวข้อการประชุมถัดไป', 'วันที่', 'ผู้จัดประชุม', 'วัตถุประสงค์'], [['ไม่มีการประชุมถัดไป โครงการปิดอย่างเป็นทางการแล้ว', '-', '-', '-']], ['', 'c', '', '']),
].join('\n\n'),
secretary: P.YaB, reviewer: P.ApS, approver: P.SeV,
}),
};
});
}
/** 8. Software Configuration — controlled items and their versions. */
export function softwareConfiguration() {
const date = '2026-08-17';
return {
dir: '1-PM Process (10 Work Product)/8.Software Configuration',
name: `${CODE} Software Configuration ${bud(date)} V1.0 ThS`,
md: workProduct({
type: 'Software Configuration', footer: 'SC',
rows: head('Software Configuration', `${bud(date)} V1.0 ThS`, [
['Title', 'บันทึกการกำหนดค่าและความเปลี่ยนแปลงต่าง ๆ ที่เกิดขึ้นของระบบ'],
['ระยะเวลา', thRangeLong(START, END)],
['Organizer', ORGANIZER_PM],
['Recorder', RECORDER],
]),
body: [
'## รายการสิ่งส่งมอบ (Work Products)',
table(['No.', 'Work Product', 'Version'], [
['**เอกสารส่งมอบครั้งที่ 1 (วางแผนโครงการ)**', '', ''],
['WP 1.0', `เอกสาร ${CODE} Software Project Plan (พร้อม Work Schedule)`, '1.0'],
['WP 2.0', `เอกสาร ${CODE} Customer Requirements`, '1.0'],
['**เอกสารส่งมอบครั้งที่ 2 (พัฒนาและทดสอบ)**', '', ''],
['WP 3.0', `เอกสาร ${CODE} Software Requirements Specification`, '1.0'],
['WP 4.0', `เอกสาร ${CODE} Software Design`, '1.0'],
['WP 5.0', `เอกสาร ${CODE} Change Report (CH-001–CH-003)`, '1.0'],
['WP 6.0', `เอกสาร ${CODE} Test Case and Test Procedures`, '1.0'],
['WP 7.0', `เอกสาร ${CODE} Validation Results`, '1.0'],
['WP 8.0', `เอกสาร ${CODE} Software User Document`, '1.0'],
['WP 9.0', `ระบบ BRN WMS ที่ผ่านการทดสอบ (Git baseline \`${BASELINE_COMMIT}\`)`, '1.0'],
['**เอกสารส่งมอบครั้งที่ 3 (ส่งมอบและปิดโครงการ)**', '', ''],
['WP 10.0', `เอกสาร ${CODE} Product Operation Guide`, '1.0'],
['WP 11.0', `เอกสาร ${CODE} Maintenance Document`, '1.0'],
['—', `เอกสาร ${CODE} Acceptance Report`, '1.0'],
], ['c']),
'## รายการ Configuration Item ของซอฟต์แวร์',
table(['ลำดับ', 'รายการ', 'ที่จัดเก็บ', 'การควบคุมเวอร์ชัน'], [
[1, 'Source Code แอปพลิเคชัน PHP', '`app/`', `Git baseline \`${BASELINE_COMMIT}\` บน branch main`],
[2, 'บริการ Node.js (แจ้งเตือนและงานตามกำหนดเวลา)', '`nodejs/`', 'Git พร้อม package.json และ package-lock.json'],
[3, 'สคริปต์ติดตั้งฐานข้อมูล', '`setup.php`, `docker/mariadb/init-wms2.sql`', 'Git'],
[4, 'ชุดติดตั้ง Docker Compose', '`docker-compose.yml`, `docker/`, `.env.example`', 'Git (ไม่รวมค่าความลับ)'],
[5, 'ค่าตั้งค่าและความลับของสภาพแวดล้อม', '`.env`, `app/config.php` บนเครื่องที่ติดตั้ง', 'ไม่ควบคุมใน Git สร้างขึ้นตอนติดตั้งด้วย `docker/init-env.sh`'],
[6, 'เอกสาร Work Products', '`sdlc/`', 'Git (Markdown ต้นฉบับ)'],
[7, 'ชุดเอกสารส่งมอบ (PDF)', '`sdlc-delivery/`', 'Git สร้างจาก `sdlc/` ด้วย `scripts/build-sdlc-delivery.sh`'],
], ['c']),
'## การควบคุมเวอร์ชันและการเข้าถึง',
table(['หัวข้อ', 'รายละเอียด'], [
['Repository หลัก (origin)', `\`${ORIGIN}\``],
['Repository สำรอง (backup)', `\`${BACKUP}\``],
['Branch หลัก', '`main` เป็น Baseline ที่ควบคุม'],
['Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\``],
['Tag ของชุดเอกสารส่งมอบ', `\`${TAG}\``],
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย SSH Key ตามบทบาทของสมาชิกโครงการ'],
]),
'## การเชื่อมโยงกับการเปลี่ยนแปลง',
`การเปลี่ยนแปลง Configuration Item ต้องผ่าน Change Report (CH-001–CH-003) และการแก้ไขข้อบกพร่องต้องบันทึกใน Correction Register (${CORRECTIONS[0][0]}–${CORRECTIONS[CORRECTIONS.length - 1][0]}) โดยทุกรายการเชื่อมโยงกับ Commit ที่ตรวจสอบได้ใน Repository`,
].join('\n\n'),
secretary: P.ThS, reviewer: P.ApS, approver: P.SeV,
}),
};
}
/** 9. Project Repository. */
export function projectRepository() {
const date = '2026-08-17';
return {
dir: '1-PM Process (10 Work Product)/9.Project Repository',
name: `${CODE} Project Repository ${bud(date)} V1.0 ThS`,
md: workProduct({
type: 'Project Repository', footer: 'PjR',
rows: head('Project Repository', `${bud(date)} V1.0 ThS`, [
['Title', 'พื้นที่เก็บข้อมูล เอกสาร และหลักฐานทุกอย่างของโครงการ'],
['ระยะเวลา', thRangeLong(START, END)],
['Organizer', ORGANIZER_PM],
['Recorder', RECORDER],
]),
body: [
'## ข้อมูล Repository',
table(['หัวข้อ', 'รายละเอียด'], [
['ประเภท', 'Git Server'],
['Repository หลัก (origin)', `\`${ORIGIN}\``],
['Branch หลัก', '`main`'],
['Branch เอกสารส่งมอบ', '`sdlc`'],
['Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\``],
['Tag ของชุดเอกสารส่งมอบ', `\`${TAG}\``],
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย SSH Key ตามบทบาทของสมาชิกโครงการ'],
]),
'## โครงสร้างการจัดเก็บ',
table(['ลำดับ', 'ส่วนงาน', 'ที่จัดเก็บ', 'เนื้อหา'], [
[1, 'Source Code แอปพลิเคชัน', '`app/`', 'เข้าสู่ระบบ, คลังสินค้า, ขาย, จัดซื้อ, การเงิน, บัญชี, รายงาน, บาร์โค้ด'],
[2, 'บริการ Real-time และงานตามกำหนดเวลา', '`nodejs/`', 'Socket.IO และงานสรุปยอด/แจ้งเตือน'],
[3, 'การติดตั้งและตั้งค่า', '`docker/`, `docker-compose.yml`, `setup.php`, `.env.example`', 'ชุดติดตั้ง Container และสคริปต์สร้างฐานข้อมูล'],
[4, 'ข้อมูลสาธิต', '`demo_seed*.php`', 'ชุดข้อมูลตัวอย่างสำหรับตรวจรับและสาธิต'],
[5, 'เอกสาร Work Products', '`sdlc/`', 'เอกสาร ISO/IEC 29110 กระบวนการ PM และ SI'],
[6, 'ชุดเอกสารส่งมอบ', '`sdlc-delivery/`', 'ไฟล์ PDF ที่สร้างจาก `sdlc/`'],
[7, 'สคริปต์สนับสนุนโครงการ', '`scripts/`', 'สคริปต์สร้างและตรวจสอบชุดเอกสารส่งมอบ'],
], ['c']),
'## การควบคุมการเข้าถึงและการจัดเก็บ',
bullets([
'สมาชิกโครงการทุกคนเข้าถึง Repository ได้ผ่าน Git Server ตามสิทธิ์ของบทบาท',
'Project Manager และ System Analyst มีสิทธิ์อนุมัติการ Merge เข้าสู่ Branch หลัก',
'ไฟล์ที่เกี่ยวข้องกับโครงการทั้งหมดต้องจัดเก็บใน Repository นี้',
'ค่าตั้งค่าและความลับของสภาพแวดล้อมต้องไม่ถูกนำเข้า Repository',
]),
'## หลักฐานการจัดเก็บ',
table(['ลำดับ', 'หลักฐาน', 'รายละเอียด'], [
[1, 'โครงสร้าง Repository และประวัติการเปลี่ยนแปลง', 'ตรวจสอบได้จาก Git Server ตามสิทธิ์ที่ได้รับ'],
[2, 'Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\` บน branch \`main\``],
[3, 'ชุดเอกสารส่งมอบที่ควบคุมแล้ว', `Branch \`sdlc\` และ Tag \`${TAG}\``],
], ['c']),
].join('\n\n'),
secretary: P.ThS, reviewer: P.ApS, approver: P.SeV,
}),
};
}
/** 10. Project Repository (Backup). */
export function projectRepositoryBackup() {
const date = '2026-08-24';
return {
dir: '1-PM Process (10 Work Product)/10.Project Repository (Backup)',
name: `${CODE} Project Repository Backup ${bud(date)} V1.0 ThS`,
md: workProduct({
type: 'Project Repository (Backup)', footer: 'PjB',
rows: head('Project Repository (Backup)', `${bud(date)} V1.0 ThS`, [
['Title', 'พื้นที่สำรองสำหรับใช้กู้คืนข้อมูล Project Repository ในกรณีที่เกิดการสูญหาย'],
['ระยะเวลา', thRangeLong(START, END)],
['Organizer', ORGANIZER_PM],
['Recorder', RECORDER],
]),
body: [
'## การสำรอง Source Code และเอกสาร',
table(['หัวข้อ', 'รายละเอียด'], [
['วิธีการสำรอง', 'Git Remote สำรอง (Secondary Remote)'],
['ชื่อ Remote สำรอง', '`backup`'],
['ที่อยู่ Remote สำรอง', `\`${BACKUP}\``],
['ขอบเขตการสำรอง', `Branch \`main\` (Baseline \`${BASELINE_COMMIT}\`), Branch \`sdlc\` และ Tag \`${TAG}\``],
['รอบการสำรอง', 'ซิงก์ทุกครั้งที่มีการส่งมอบหรือปรับปรุง Baseline'],
['ผู้รับผิดชอบ', `${P.ThS.th} (Developer)`],
]),
'## การสำรองฐานข้อมูล',
table(['หัวข้อ', 'รายละเอียด'], [
['วิธีการสำรอง', 'Export ฐานข้อมูล `wms` และ `wms2` อัตโนมัติ'],
['รอบการสำรอง', 'ทุกวัน เวลา 02:00 น. (Asia/Bangkok)'],
['ที่จัดเก็บ', 'พื้นที่จัดเก็บบนคลาวด์ที่บริษัทควบคุม'],
['ระยะเวลาเก็บรักษา', 'ชุดสำรองรายวันเก็บ 30 วัน และชุดสิ้นเดือนเก็บ 12 เดือน'],
['สิทธิ์การเข้าถึง', 'จำกัดเฉพาะผู้ดูแลระบบและผู้บริหารที่ได้รับมอบหมาย'],
]),
'## รายการตรวจสอบการสำรองข้อมูล',
table(['ลำดับ', 'รายการตรวจสอบ', 'ผู้รับผิดชอบ', 'วันที่ดำเนินการ', 'ผลการตรวจสอบ'], [
['BK-001', `ตรวจสอบว่า Remote สำรองเข้าถึงได้และมี Baseline \`${BASELINE_COMMIT}\` ตรงกับ Repository หลัก`, P.ThS.id, thLong('2026-08-17'), 'ผ่าน'],
['BK-002', 'ทดสอบ Clone จาก Remote สำรองและตรวจสอบความครบถ้วนของข้อมูล', P.ThS.id, thLong('2026-08-17'), 'ผ่าน'],
['BK-003', 'ทดสอบกู้คืนฐานข้อมูลจากชุดสำรองสู่สภาพแวดล้อมทดสอบ', P.ThS.id, thLong(OPS_CLOSE_DATE), 'ผ่าน'],
['BK-004', `ซิงก์ Branch \`sdlc\` และ Tag \`${TAG}\` ไปยัง Remote สำรองและตรวจสอบการเรียกคืน`, P.ThS.id, thLong(END), 'ผ่าน'],
], ['c', '', 'c', 'c', 'c']),
'## ขั้นตอนการกู้คืน',
table(['ลำดับ', 'ขั้นตอน', 'รายละเอียด'], [
[1, 'ยืนยันขอบเขตความเสียหาย', 'ตรวจสอบว่าข้อมูลที่สูญหายเป็น Source Code เอกสาร หรือฐานข้อมูล'],
[2, 'เลือกชุดสำรองที่จะใช้', 'เลือกชุดสำรองล่าสุดที่ผ่านการตรวจสอบและยืนยันวันที่'],
[3, 'กู้คืนสู่สภาพแวดล้อมทดสอบก่อน', 'Clone จาก Remote สำรอง หรือ Restore ฐานข้อมูลสู่เครื่องทดสอบ'],
[4, 'ตรวจสอบความถูกต้อง', 'ตรวจสอบ Baseline, Tag และข้อมูลตัวอย่างว่าครบถ้วน'],
[5, 'กู้คืนสู่สภาพแวดล้อมใช้งานจริง', 'บันทึกจุดกู้คืน หยุดบริการที่เกี่ยวข้อง กู้คืน แล้วตรวจสอบสถานะระบบตามคู่มือผู้ดูแลระบบ'],
], ['c']),
].join('\n\n'),
secretary: P.ThS, reviewer: P.ApS, approver: P.SeV,
}),
};
}