- SOW, plan and design no longer cite the Aug baseline commit or the package tag - Work Schedule status derived from its date; Test Case has no results yet - build fails when a document cites a later commit, tag, ISS or ID (timeline.mjs) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
397 lines
37 KiB
JavaScript
397 lines
37 KiB
JavaScript
// PM work products 3–10: Progress Status Record, Correction Register, Acceptance Report,
|
||
// Change Report, Meeting Record, Software Configuration, Project Repository (+ Backup).
|
||
import { P, CRS, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, WPS, TEAM, ORIGIN, BACKUP, BASELINE_COMMIT, TEST_FROM, TEST_TO, ACCEPT_DATE, OPS_CLOSE_DATE } from './project.mjs';
|
||
import { TASKS, taskById, CORRECTIONS, CHANGE_CRITERIA, CHANGE_REVIEW, LESSONS, 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];
|
||
});
|
||
// The reference package records risks in five columns: number, issue,
|
||
// detail, owner and status.
|
||
const issues = issueIds.map((iid, n) => {
|
||
const c = CORRECTIONS.find((x) => x[0] === iid);
|
||
return [n + 1, `${c[0]} : ${c[4]}`, `${c[5]} จึงดำเนินการ${c[6]} แล้วเสร็จ ${thShort(c[2])}`, P[c[7]].id, 'เสร็จสิ้น'];
|
||
});
|
||
if (changeIds.length) throw new Error(`Progress record ${date} lists change requests, but the project has none.`);
|
||
const changes = [];
|
||
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', '']),
|
||
'## ความเสี่ยง/อุปสรรค',
|
||
table(['ลำดับ', 'ประเด็น / ความเสี่ยง', 'รายละเอียด', 'ผู้รับผิดชอบ', 'สถานะ'],
|
||
issues.length ? issues : [['-', 'ไม่พบประเด็นความเสี่ยงหรืออุปสรรคในงวดนี้', '-', '-', '-']],
|
||
['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)} ไม่มีข้อบกพร่องระดับวิกฤตคงค้าง ณ วันตรวจรับ`,
|
||
'## บทเรียนและแนวทางป้องกันสำหรับโครงการถัดไป (Lessons Learned)',
|
||
'เมื่อปิดโครงการ ทีมงานทบทวนปัญหาทั้งหมดในทะเบียนนี้ จัดกลุ่มตามสาเหตุ และกำหนดแนวทางป้องกันเพื่อไม่ให้ปัญหาลักษณะเดียวกันเกิดซ้ำในโครงการถัดไป',
|
||
table(['ลำดับ', 'กลุ่มปัญหา', 'เลขที่ปัญหา', 'จำนวน', 'สิ่งที่เกิดขึ้น', 'แนวทางป้องกัน (Preventive Action)'],
|
||
LESSONS.map(([theme, issueIds, what, action], i) => [i + 1, theme, issueIds.join(', '), issueIds.length, what, action]), ['c', '', '', 'r', '', '']),
|
||
'## การเชื่อมโยงกับหลักฐานการทดสอบ',
|
||
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[0] === 'WP 9.0' ? `${w[2]} (Git baseline \`${BASELINE_COMMIT}\`)` : 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 ครบ 14 หมวดความต้องการ (${CRS.length} รายการ) ผ่านทั้งหมด`, 'ผ่าน'],
|
||
[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 — the change-control register. No adjustment in this project met the criteria. */
|
||
export function changeReports() {
|
||
const date = ACCEPT_DATE;
|
||
return [{
|
||
dir: '1-PM Process (10 Work Product)/6.Change Report',
|
||
name: `${CODE} Change Report ${bud(date)} V1.0 ApS`,
|
||
md: workProduct({
|
||
type: 'Change Report', footer: 'CR',
|
||
rows: head('Change Report', `${bud(date)} V1.0 ApS`, [
|
||
['Title', 'ทะเบียนการควบคุมการเปลี่ยนแปลง (Change Control Register)'],
|
||
['Project Period', thRangeLong(START, END)],
|
||
['Organizer', ORGANIZER_PM],
|
||
['Recorder', RECORDER],
|
||
]),
|
||
body: [
|
||
'## วัตถุประสงค์ (Objective)',
|
||
'เอกสารนี้บันทึกการควบคุมการเปลี่ยนแปลงของโครงการ ตั้งแต่ตั้ง Baseline ความต้องการเมื่อ 18 กุมภาพันธ์ 2569 จนถึงการตรวจรับส่งมอบ โดยพิจารณาทุกรายการปรับปรุงที่มีการเสนอตามเกณฑ์ในส่วนที่ 1 รายการที่เข้าเกณฑ์ต้องจัดทำคำขอเปลี่ยนแปลง ประเมินผลกระทบ และขออนุมัติก่อนดำเนินการ ส่วนรายการที่ไม่เข้าเกณฑ์ดำเนินการเป็นมติที่ประชุมหรืองานในแผน',
|
||
'## ส่วนที่ 1 : เกณฑ์การพิจารณาคำขอเปลี่ยนแปลง (Change Criteria)',
|
||
'รายการใดจะถือเป็นคำขอเปลี่ยนแปลงเมื่อเข้าเกณฑ์ทั้ง 3 ข้อ หากตัดรายการนั้นออกแล้วยังส่งมอบงานได้ตามที่ตกลงไว้ จะไม่ถือเป็นคำขอเปลี่ยนแปลง',
|
||
table(['ลำดับ', 'เกณฑ์', 'คำอธิบาย'], CHANGE_CRITERIA.map(([name, detail], i) => [i + 1, name, detail]), ['c']),
|
||
'## ส่วนที่ 2 : ทะเบียนคำขอเปลี่ยนแปลง (Change Request Log)',
|
||
table(['Change ID', 'หัวข้อ', 'ผู้ร้องขอ', 'ผลกระทบ', 'สถานะ'], [['-', 'ไม่มีคำขอเปลี่ยนแปลงที่เข้าเกณฑ์ตลอดโครงการ', '-', '-', '-']], ['c', '', 'c', '', 'c']),
|
||
'## ส่วนที่ 3 : รายการปรับปรุงที่พิจารณาแล้วไม่เข้าเกณฑ์',
|
||
table(['ลำดับ', 'วันที่พิจารณา', 'รายการ', 'ผู้เสนอ', 'ผลการพิจารณา', 'การดำเนินการ', 'อ้างอิง'],
|
||
CHANGE_REVIEW.map(([when, topic, who, assessment, handled, ref], i) => [i + 1, thShort(when), topic, P[who].id, `ไม่เข้าเกณฑ์ — ${assessment}`, handled, ref]), ['c', 'c', '', 'c', '', '', '']),
|
||
'## ส่วนที่ 4 : สรุปผลการควบคุมการเปลี่ยนแปลง',
|
||
table(['รายการ', 'จำนวน'], [
|
||
['รายการปรับปรุงที่เสนอและพิจารณา', CHANGE_REVIEW.length],
|
||
['คำขอเปลี่ยนแปลงที่เข้าเกณฑ์', 0],
|
||
['คำขอเปลี่ยนแปลงที่อนุมัติ', 0],
|
||
['ผลกระทบต่อ Man-day กำหนดส่งมอบ และค่าใช้จ่าย', 'ไม่มี'],
|
||
], ['', 'r']),
|
||
'ขอบเขตงาน สิ่งส่งมอบ และ Customer Requirements ที่ตั้ง Baseline ไม่มีการเปลี่ยนแปลงตลอดโครงการ รายการปรับปรุงที่พิจารณาทั้งหมดดำเนินการภายในแผนงานเดิมโดยไม่กระทบ Man-day และกำหนดส่งมอบ',
|
||
'## ภาคผนวก A : เกณฑ์การประเมินระดับความรุนแรงของการเปลี่ยนแปลง',
|
||
'ใช้ประเมินระดับความรุนแรง (Priority Level) เมื่อมีคำขอเปลี่ยนแปลงที่เข้าเกณฑ์ในส่วนที่ 1',
|
||
table(['ระดับ (Level)', 'คำจำกัดความ (Definition)', 'ตัวอย่างสถานการณ์ (Examples)'], [
|
||
['High', 'กระทบระบบหลัก งบประมาณ หรือแผนงานอย่างมีนัยสำคัญ', 'เพิ่มโมดูลใหม่ เปลี่ยนโครงสร้างฐานข้อมูลหลัก'],
|
||
['Medium', 'กระทบฟังก์ชันย่อยหรือส่วนติดต่อผู้ใช้งานบางส่วน และกระทบ Man-day', 'รองรับ 2 ภาษา เพิ่มรายงานใหม่'],
|
||
['Low', 'กระทบเล็กน้อยต่อ Man-day ไม่เปลี่ยนการทำงานหลัก', 'เพิ่มฟิลด์ในเอกสาร'],
|
||
]),
|
||
].join('\n\n'),
|
||
secretary: P.ApS, reviewer: P.YaB, 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`, '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}\``],
|
||
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย SSH Key ตามบทบาทของสมาชิกโครงการ'],
|
||
]),
|
||
'## การเชื่อมโยงกับการเปลี่ยนแปลง',
|
||
`การเปลี่ยนแปลง Configuration Item ต้องพิจารณาตามเกณฑ์ใน Change Report และการแก้ไขข้อบกพร่องต้องบันทึกใน 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}\``],
|
||
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย 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`'],
|
||
], ['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\``],
|
||
['รอบการสำรอง', 'ซิงก์ทุกครั้งที่มีการส่งมอบหรือปรับปรุง 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` ไปยัง Remote สำรองและตรวจสอบการเรียกคืน', P.ThS.id, thLong(END), 'ผ่าน'],
|
||
], ['c', '', 'c', 'c', 'c']),
|
||
'## ขั้นตอนการกู้คืน',
|
||
table(['ลำดับ', 'ขั้นตอน', 'รายละเอียด'], [
|
||
[1, 'ยืนยันขอบเขตความเสียหาย', 'ตรวจสอบว่าข้อมูลที่สูญหายเป็น Source Code เอกสาร หรือฐานข้อมูล'],
|
||
[2, 'เลือกชุดสำรองที่จะใช้', 'เลือกชุดสำรองล่าสุดที่ผ่านการตรวจสอบและยืนยันวันที่'],
|
||
[3, 'กู้คืนสู่สภาพแวดล้อมทดสอบก่อน', 'Clone จาก Remote สำรอง หรือ Restore ฐานข้อมูลสู่เครื่องทดสอบ'],
|
||
[4, 'ตรวจสอบความถูกต้อง', 'ตรวจสอบ Baseline และข้อมูลตัวอย่างว่าครบถ้วน'],
|
||
[5, 'กู้คืนสู่สภาพแวดล้อมใช้งานจริง', 'บันทึกจุดกู้คืน หยุดบริการที่เกี่ยวข้อง กู้คืน แล้วตรวจสอบสถานะระบบตามคู่มือผู้ดูแลระบบ'],
|
||
], ['c']),
|
||
].join('\n\n'),
|
||
secretary: P.ThS, reviewer: P.ApS, approver: P.SeV,
|
||
}),
|
||
};
|
||
}
|