- .htaccess: refuse .git, .env, docker, nodejs, sdlc*, scripts, archives and app internals; security headers, HSTS over TLS, optional HTTPS redirect - uploads served through app/file.php to signed-in users only - Apache/PHP hardening config for the container (ServerTokens, expose_php) - least-privilege DB account and APP_SECRET_KEY via docker/php/provision.php; SMTP passwords re-encrypted with a random IV (secret_box.php) - Socket.IO rooms from a PHP-signed token; /emit and cron refuse empty secrets - escape notification text; CLI guards on build scripts; no fixed demo password
199 lines
20 KiB
PHP
199 lines
20 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
// Build script, CLI only — the repository sits under the web root.
|
||
if (PHP_SAPI !== 'cli') {
|
||
http_response_code(404);
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Generate the ISO/IEC 29110 Statement of Work for BRN WMS.
|
||
*
|
||
* The document is generated as native OOXML so it remains editable in Word.
|
||
*/
|
||
|
||
$output = __DIR__ . '/../sdlc/1-PM Process (10 Work Product)/1.Statement of Work/'
|
||
. '200-WMS-26-001-00 Statement of Work 25690105 V1.0 Final.docx';
|
||
|
||
function xml(string $value): string
|
||
{
|
||
return htmlspecialchars($value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
|
||
}
|
||
|
||
function run(string $text, bool $bold = false, int $size = 30): string
|
||
{
|
||
$boldXml = $bold ? '<w:b/><w:bCs/>' : '';
|
||
return '<w:r><w:rPr>' . $boldXml
|
||
. '<w:rFonts w:ascii="TH Sarabun New" w:hAnsi="TH Sarabun New" w:eastAsia="TH Sarabun New" w:cs="TH Sarabun New"/>'
|
||
. '<w:sz w:val="' . $size . '"/><w:szCs w:val="' . $size . '"/>'
|
||
. '<w:lang w:val="th-TH" w:eastAsia="th-TH" w:bidi="th-TH"/>'
|
||
. '</w:rPr><w:t xml:space="preserve">' . xml($text) . '</w:t></w:r>';
|
||
}
|
||
|
||
function para(string $text = '', string $align = 'left', bool $bold = false, int $size = 30, int $before = 0, int $after = 80, int $indent = 0): string
|
||
{
|
||
return '<w:p><w:pPr><w:jc w:val="' . $align . '"/>'
|
||
. '<w:spacing w:before="' . $before . '" w:after="' . $after . '" w:line="360" w:lineRule="auto"/>'
|
||
. ($indent > 0 ? '<w:ind w:firstLine="' . $indent . '"/>' : '')
|
||
. '</w:pPr>' . run($text, $bold, $size) . '</w:p>';
|
||
}
|
||
|
||
function bullet(string $text): string
|
||
{
|
||
return '<w:p><w:pPr><w:ind w:left="720" w:hanging="360"/><w:spacing w:after="50" w:line="340" w:lineRule="auto"/></w:pPr>'
|
||
. run('• ' . $text, false, 29) . '</w:p>';
|
||
}
|
||
|
||
function tableRow(string $label, string $value): string
|
||
{
|
||
return '<w:tr>'
|
||
. '<w:tc><w:tcPr><w:tcW w:w="2200" w:type="dxa"/><w:shd w:fill="D9EAF7"/></w:tcPr>'
|
||
. para($label, 'left', true, 27, 0, 20) . '</w:tc>'
|
||
. '<w:tc><w:tcPr><w:tcW w:w="6800" w:type="dxa"/></w:tcPr>'
|
||
. para($value, 'left', false, 27, 0, 20) . '</w:tc>'
|
||
. '</w:tr>';
|
||
}
|
||
|
||
$header = '<w:tbl><w:tblPr><w:tblW w:w="9000" w:type="dxa"/><w:tblLayout w:type="fixed"/></w:tblPr><w:tblGrid><w:gridCol w:w="6100"/><w:gridCol w:w="2900"/></w:tblGrid>'
|
||
. '<w:tr><w:tc><w:tcPr><w:tcW w:w="6100" w:type="dxa"/></w:tcPr>'
|
||
. para('B.R.N. ENTERPRISE CO., LTD.', 'left', true, 29, 0, 0)
|
||
. para('1011 Supalai Grand Tower, 7th Floor, Unit 6-7, Rama 3 Road,', 'left', false, 19, 0, 0)
|
||
. para('Chongnonsi, Yannawa, Bangkok 10120', 'left', false, 19, 0, 0)
|
||
. '</w:tc><w:tc><w:tcPr><w:tcW w:w="2900" w:type="dxa"/><w:shd w:fill="0798D1"/><w:vAlign w:val="center"/></w:tcPr>'
|
||
. para('Statement of Work', 'center', false, 29, 0, 0)
|
||
. '</w:tc></w:tr></w:tbl>';
|
||
|
||
$meta = '<w:tbl><w:tblPr><w:tblW w:w="9000" w:type="dxa"/><w:tblBorders>'
|
||
. '<w:top w:val="single" w:sz="4" w:color="9E9E9E"/><w:left w:val="single" w:sz="4" w:color="9E9E9E"/>'
|
||
. '<w:bottom w:val="single" w:sz="4" w:color="9E9E9E"/><w:right w:val="single" w:sz="4" w:color="9E9E9E"/>'
|
||
. '<w:insideH w:val="single" w:sz="4" w:color="9E9E9E"/><w:insideV w:val="single" w:sz="4" w:color="9E9E9E"/>'
|
||
. '</w:tblBorders></w:tblPr><w:tblGrid><w:gridCol w:w="2200"/><w:gridCol w:w="6800"/></w:tblGrid>'
|
||
. tableRow('รหัสโครงการ', '200-WMS-26-001-00')
|
||
. tableRow('ชื่อโครงการ', 'โครงการพัฒนาระบบบริหารจัดการคลังสินค้า BRN WMS')
|
||
. tableRow('ระยะเวลาโครงการ', '5 มกราคม 2569 – 14 สิงหาคม 2569')
|
||
. tableRow('เอกสาร', 'Statement of Work · Version 1.0 · สถานะ Final')
|
||
. '</w:tbl>';
|
||
|
||
$body = $header
|
||
. para('', 'left', false, 10, 0, 80)
|
||
. para('วันที่ 5 มกราคม 2569', 'center', false, 30, 80, 180)
|
||
. para('เรื่อง ขอบเขตการดำเนินงานโครงการพัฒนาระบบบริหารจัดการคลังสินค้า BRN WMS', 'left', true, 30, 0, 100)
|
||
. para('เรียน ผู้บริหารและผู้มีส่วนเกี่ยวข้องในโครงการ', 'left', true, 30, 0, 180)
|
||
. para('บริษัท บี.อาร์.เอ็น. เอ็นเตอร์ไพรส์ จำกัด มีความประสงค์ดำเนินโครงการพัฒนาระบบบริหารจัดการคลังสินค้า BRN WMS เพื่อเพิ่มความถูกต้องและความรวดเร็วของงานคลังสินค้า ทำให้สามารถติดตามสินค้าคงคลัง การเคลื่อนไหวสินค้า คำสั่งซื้อ เอกสารทางธุรกิจ และข้อมูลบัญชีที่เกี่ยวข้องได้อย่างเป็นระบบ โดยใช้ข้อมูลแบบรวมศูนย์และกำหนดสิทธิ์การเข้าถึงตามบทบาทผู้ใช้งาน', 'both', false, 30, 0, 120, 720)
|
||
. para('เอกสารฉบับนี้กำหนดขอบเขต ผลส่งมอบ เกณฑ์การยอมรับ หน้าที่ความรับผิดชอบ และกรอบระยะเวลาของโครงการตามแนวทาง ISO/IEC 29110 โดยมีรายละเอียดดังต่อไปนี้', 'both', false, 30, 0, 180, 720)
|
||
. $meta
|
||
. para('1. วัตถุประสงค์', 'left', true, 32, 180, 80)
|
||
. bullet('พัฒนาระบบเว็บสำหรับควบคุมสินค้าคงคลังและการปฏิบัติงานคลังสินค้าแบบหลายคลัง')
|
||
. bullet('สนับสนุนการรับเข้า เบิกจ่าย โอนย้าย ปรับปรุง และตรวจสอบยอดคงเหลือ พร้อมการติดตาม Lot, Serial Number และวันหมดอายุ')
|
||
. bullet('ลดความผิดพลาดจากการทำงานด้วยมือและเพิ่มความสามารถในการตรวจสอบย้อนหลัง')
|
||
. bullet('สนับสนุนการบริหารคำสั่งซื้อ จัดซื้อ คืนสินค้า ใบแจ้งหนี้ รายงาน และรายการบัญชีที่เกี่ยวข้อง')
|
||
. bullet('จัดให้มีการรักษาความมั่นคงปลอดภัย การแยกข้อมูลรายบริษัท การกำหนดสิทธิ์ และการแจ้งเตือนแบบเรียลไทม์')
|
||
. para('2. ขอบเขตงาน', 'left', true, 32, 150, 80)
|
||
. para('2.1 งานวิเคราะห์และออกแบบ', 'left', true, 29, 0, 50)
|
||
. bullet('รวบรวมและวิเคราะห์ความต้องการของผู้ใช้ กำหนดกระบวนการทำงาน ข้อมูล และกฎทางธุรกิจ')
|
||
. bullet('ออกแบบสถาปัตยกรรมระบบ ฐานข้อมูล ส่วนติดต่อผู้ใช้ การเชื่อมต่อบริการ และมาตรการความมั่นคงปลอดภัย')
|
||
. para('2.2 งานพัฒนาระบบ', 'left', true, 29, 80, 50)
|
||
. bullet('ข้อมูลหลัก: บริษัท ผู้ใช้ ผู้ติดต่อ สินค้า คลังสินค้า โซน ทางเดิน ตำแหน่งจัดเก็บ และหน่วยนับ')
|
||
. bullet('สินค้าคงคลัง: รับเข้า จ่ายออก โอนย้าย ปรับปรุงยอด ตรวจนับ ยอดคงเหลือ และประวัติการเคลื่อนไหว')
|
||
. bullet('เอกสารธุรกิจ: Sales Order, Purchase Order, Return, Invoice และลำดับเลขที่เอกสาร')
|
||
. bullet('การเงินและบัญชี: รายรับ รายจ่าย สมุดรายวัน รายการบัญชี และรายงานที่ระบบรองรับ')
|
||
. bullet('Dashboard, รายงาน, การส่งออกข้อมูล, Barcode/Label และการแนบไฟล์')
|
||
. bullet('การยืนยันตัวตน การกำหนดบทบาท Owner/Admin/Staff/Viewer การจำกัดข้อมูลตามบริษัท และการควบคุม session')
|
||
. bullet('บริการแจ้งเตือนแบบเรียลไทม์และงานตามกำหนดเวลาด้วย Node.js/Socket.IO')
|
||
. para('2.3 งานทดสอบและส่งมอบ', 'left', true, 29, 80, 50)
|
||
. bullet('จัดทำและดำเนินการทดสอบตามความต้องการ บันทึกผล แก้ไขข้อบกพร่อง และทดสอบยืนยันผล')
|
||
. bullet('จัดเตรียมคู่มือผู้ใช้ คู่มือการติดตั้ง/ปฏิบัติการ และเอกสารบำรุงรักษา')
|
||
. bullet('จัดเตรียมหลักฐานการทวนสอบ การตรวจสอบความใช้ได้ และการยอมรับระบบ')
|
||
. para('3. ผลส่งมอบ', 'left', true, 32, 150, 80)
|
||
. para('ผลส่งมอบประกอบด้วยซอฟต์แวร์ BRN WMS ซอร์สโค้ด สคริปต์การติดตั้งและฐานข้อมูล การกำหนดค่า คู่มือ และ Work Products ของกระบวนการ Project Management และ Software Implementation ตาม ISO/IEC 29110 รวม 22 รายการ โดยจัดเก็บใน Project Repository ภายใต้การควบคุมเวอร์ชัน', 'both', false, 30, 0, 100, 720)
|
||
. para('4. ข้อยกเว้นและข้อสมมติ', 'left', true, 32, 120, 80)
|
||
. bullet('ไม่รวมการจัดหาเครื่องแม่ข่าย อุปกรณ์เครือข่าย เครื่องสแกน Barcode หรือบริการจากบุคคลภายนอก เว้นแต่ได้รับอนุมัติเพิ่มเติม')
|
||
. bullet('การย้ายข้อมูลเดิม การเชื่อมต่อ ERP/บริการภายนอก และการปรับแต่งนอกขอบเขตต้องผ่านกระบวนการ Change Request')
|
||
. bullet('ผู้มีส่วนเกี่ยวข้องต้องให้ข้อมูล ทบทวนเอกสาร และเข้าร่วมการทดสอบ/ยอมรับตามกำหนด')
|
||
. para('5. แผนงานและจุดควบคุม', 'left', true, 32, 150, 80)
|
||
. bullet('เริ่มต้นและวางแผนโครงการ: 5 มกราคม – 18 กุมภาพันธ์ 2569')
|
||
. bullet('พัฒนาและทดสอบภายใน: 19 กุมภาพันธ์ – 29 พฤษภาคม 2569')
|
||
. bullet('ทดสอบการยอมรับ จัดทำเอกสาร ส่งมอบ และปรับเสถียรภาพ: 30 พฤษภาคม – 14 สิงหาคม 2569')
|
||
. bullet('วันเสร็จสิ้นโครงการอย่างเป็นทางการ: 14 สิงหาคม 2569')
|
||
. para('6. เกณฑ์การยอมรับ', 'left', true, 32, 150, 80)
|
||
. bullet('ฟังก์ชันที่อยู่ในขอบเขตผ่าน Test Cases และเชื่อมโยงกับความต้องการใน Traceability Record')
|
||
. bullet('ข้อบกพร่องระดับร้ายแรงที่ขัดขวางการใช้งานได้รับการแก้ไขหรือมีแนวทางที่ผู้มีอำนาจยอมรับ')
|
||
. bullet('เอกสารการติดตั้ง การใช้งาน การปฏิบัติการ และการบำรุงรักษาพร้อมใช้งาน')
|
||
. bullet('ผลการ Verification, Validation และ Acceptance ได้รับการทบทวนและลงนามโดยผู้มีอำนาจ')
|
||
. para('7. การบริหารการเปลี่ยนแปลง', 'left', true, 32, 150, 80)
|
||
. para('การเปลี่ยนแปลงขอบเขต กำหนดการ หรือผลส่งมอบต้องบันทึกใน Change Report ประเมินผลกระทบ และได้รับอนุมัติก่อนดำเนินการ การแก้ไขข้อบกพร่องให้บันทึกใน Correction Register และเชื่อมโยงกับหลักฐานการทดสอบที่เกี่ยวข้อง', 'both', false, 30, 0, 120, 720)
|
||
. para('จึงจัดทำ Statement of Work ฉบับนี้เพื่อใช้เป็นกรอบดำเนินงานและขอให้ผู้เกี่ยวข้องพิจารณาอนุมัติตามอำนาจหน้าที่', 'both', false, 30, 80, 240, 720)
|
||
. para('ลงชื่อ ____________________________________', 'center', false, 30, 240, 20)
|
||
. para('(คุณเสรี วิริยะสกุลธรณ์)', 'center', false, 30, 0, 20)
|
||
. para('กรรมการผู้จัดการ · Project Sponsor / Customer Representative / Authorized Approver', 'center', false, 26, 0, 20)
|
||
. para('บริษัท บี.อาร์.เอ็น. เอ็นเตอร์ไพรส์ จำกัด', 'center', false, 30, 0, 20)
|
||
. para('วันที่ ____________________________________', 'center', false, 30, 0, 80);
|
||
|
||
$document = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
|
||
. '<w:body>' . $body
|
||
. '<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1080" w:right="1080" w:bottom="1080" w:left="1260" w:header="500" w:footer="500"/>'
|
||
. '<w:footerReference w:type="default" r:id="rId1"/></w:sectPr></w:body></w:document>';
|
||
|
||
$contentTypes = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
||
. '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
|
||
. '<Default Extension="xml" ContentType="application/xml"/>'
|
||
. '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
|
||
. '<Override PartName="/word/footer1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>'
|
||
. '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>'
|
||
. '<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>'
|
||
. '</Types>';
|
||
|
||
$rels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
||
. '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>'
|
||
. '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>'
|
||
. '<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>'
|
||
. '</Relationships>';
|
||
|
||
$documentRels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
||
. '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer1.xml"/>'
|
||
. '</Relationships>';
|
||
|
||
$footer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<w:ftr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
|
||
. '<w:p><w:pPr><w:jc w:val="center"/></w:pPr>'
|
||
. run('200-WMS-26-001-00 · Statement of Work · V1.0 · Final', false, 18)
|
||
. '</w:p></w:ftr>';
|
||
|
||
$core = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" '
|
||
. 'xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
|
||
. '<dc:title>200-WMS-26-001-00 Statement of Work</dc:title><dc:subject>ISO/IEC 29110 Project Management Work Product</dc:subject>'
|
||
. '<dc:creator>BRN WMS Project Team</dc:creator><cp:keywords>ISO/IEC 29110; Statement of Work; BRN WMS</cp:keywords>'
|
||
. '<dc:description>Editable final document ready for authorized approval and signature.</dc:description>'
|
||
. '<dcterms:created xsi:type="dcterms:W3CDTF">2026-01-05T00:00:00+07:00</dcterms:created>'
|
||
. '<dcterms:modified xsi:type="dcterms:W3CDTF">2026-08-17T00:00:00+07:00</dcterms:modified>'
|
||
. '</cp:coreProperties>';
|
||
|
||
$app = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||
. '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" '
|
||
. 'xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">'
|
||
. '<Application>Microsoft Office Word</Application><Company>B.R.N. Enterprise Co., Ltd.</Company><AppVersion>16.0000</AppVersion>'
|
||
. '</Properties>';
|
||
|
||
$zip = new ZipArchive();
|
||
if ($zip->open($output, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
||
throw new RuntimeException('Unable to create ' . $output);
|
||
}
|
||
$zip->addFromString('[Content_Types].xml', $contentTypes);
|
||
$zip->addFromString('_rels/.rels', $rels);
|
||
$zip->addFromString('word/document.xml', $document);
|
||
$zip->addFromString('word/_rels/document.xml.rels', $documentRels);
|
||
$zip->addFromString('word/footer1.xml', $footer);
|
||
$zip->addFromString('docProps/core.xml', $core);
|
||
$zip->addFromString('docProps/app.xml', $app);
|
||
$zip->close();
|
||
|
||
echo $output . PHP_EOL;
|
||
require __DIR__ . '/normalize_document_dates.php';
|