Seed Demo Data - Rebranding

This commit is contained in:
Thanakorn
2026-08-17 13:12:07 +07:00
parent dd48a8b96d
commit 63cea23dc4
51 changed files with 1036 additions and 87 deletions
+1
View File
@@ -14,3 +14,4 @@ notes/
docs/
.claude/
SESSION.php
sdlc/
+6 -7
View File
@@ -98,15 +98,14 @@ html, body { overflow-x: hidden; }
.sidebar { padding-top: 0; }
.sidebar .logo-area { position: sticky; top: 0; z-index: 1; }
.logo-area > a > img:first-child {
width: 40px;
height: 40px;
transition: width 0.2s ease, height 0.2s ease;
.logo-area img {
height: 48px;
width: auto;
transition: height 0.2s ease;
}
.sidebar.collapsed .logo-area > a > img:first-child {
width: 24px !important;
height: 24px !important;
.sidebar.collapsed .logo-area img {
height: 28px !important;
}
@media (max-width: 991.98px) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
<div class="row">
<div class="col-12">
<footer class="text-center py-2 mt-6 text-secondary ">
<p class="mb-0">Copyright © 2026 MN3 WMS. All rights reserved.</p>
<p class="mb-0">Copyright © 2026 BRN WMS. All rights reserved.</p>
</footer>
</div>
+1 -1
View File
@@ -9,7 +9,7 @@ header('Referrer-Policy: strict-origin-when-cross-origin');
<head>
<meta charset="UTF-8" />
<title>MN3 WMS</title>
<title>BRN WMS</title>
<meta name="csrf-token" content="<?= htmlspecialchars($_SESSION['csrf_token'] ?? '') ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="180x180" href="<?php echo $server_url?>assets/images/favicon.png">
+2 -3
View File
@@ -21,9 +21,8 @@
<aside id="sidebar" class="sidebar overflow-y-auto d-flex flex-column">
<div class="logo-area">
<a href="<?php echo $server_url?>index.php" class="d-inline-flex">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="logo-text ms-2">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="40" style="padding: 5px 0;"/>
<span class="logo-text">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="48"/>
</span>
</a>
</div>
+2 -3
View File
@@ -7,9 +7,8 @@
<aside id="sidebar" class="sidebar overflow-y-auto d-flex flex-column">
<div class="logo-area">
<a href="<?php echo $server_url?>index.php" class="d-inline-flex">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="logo-text ms-2">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="40" style="padding: 5px 0;"/>
<span class="logo-text">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="48"/>
</span>
</a>
</div>
+2 -3
View File
@@ -12,9 +12,8 @@
<aside id="sidebar" class="sidebar overflow-y-auto">
<div class="logo-area">
<a href="<?php echo $server_url?>dashboard/index.php" class="d-inline-flex">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="logo-text ms-2">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="40" style="padding: 5px 0;"/>
<span class="logo-text">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="48"/>
</span>
</a>
</div>
+2 -3
View File
@@ -23,9 +23,8 @@
<aside id="sidebar" class="sidebar overflow-y-auto">
<div class="logo-area">
<a href="<?php echo $server_url?>ac_dashboard/index.php" class="d-inline-flex">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="logo-text ms-2">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="40" style="padding: 5px 0;"/>
<span class="logo-text">
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="48"/>
</span>
</a>
</div>
+3 -6
View File
@@ -19,8 +19,7 @@
<div class="container py-5" style="max-width:480px;">
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
</div>
<div class="card text-center">
@@ -71,8 +70,7 @@
<div class="container py-5" style="max-width:480px;">
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
</div>
<div class="card text-center">
@@ -109,8 +107,7 @@
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="h4 mb-1">You've been invited!</h1>
<p class="text-muted">Accept the invitation to join <strong><?php echo $company_name ?></strong>.</p>
+1 -1
View File
@@ -24,7 +24,7 @@
<div class="text-center mb-4">
<a href="<?php echo $server_url?>login/index.php" class="mb-4 d-inline-block">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40">
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="h5 mb-1">Reset your password</h1>
<p class="text-muted small mb-0" id="subtitle">Enter your username or email to receive an OTP.</p>
+1 -4
View File
@@ -26,10 +26,7 @@
</div>
<?php endif; ?>
<a href="index.html" class="mb-5 d-inline-block">
<img
src="<?php echo $server_url?>assets/images/favicon.png"
alt="" width="40">
<span class=" ms-2"> <img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="card-title mb-5 h5">Sign in to your account</h1>
</div>
+3 -6
View File
@@ -19,8 +19,7 @@
<div class="container py-5" style="max-width:480px;">
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
</div>
<div class="card text-center">
@@ -72,8 +71,7 @@
<div class="container py-5" style="max-width:480px;">
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
</div>
<div class="card text-center">
@@ -112,8 +110,7 @@
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="h4 mb-1">You've been invited!</h1>
<p class="text-muted">Complete your account setup to join <strong><?php echo $company_name ?></strong>.</p>
+1 -2
View File
@@ -25,8 +25,7 @@
<!-- Header -->
<div class="text-center mb-5">
<a href="<?php echo $server_url?>login/index.php" class="d-inline-block mb-4">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="h4 mb-1">Welcome, <?php echo $user_name ?>! 👋</h1>
<p class="text-muted">Set up your company and email before you get started.</p>
+1 -2
View File
@@ -27,8 +27,7 @@
<div class="text-center mb-4">
<a href="<?php echo $server_url?>login/index.php" class="mb-4 d-inline-block">
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
<span class="ms-2"><img src="<?php echo $server_url?>assets/images/logo.svg" alt=""></span>
<img src="<?php echo $server_url?>assets/images/logo.svg" alt="">
</a>
<h1 class="h5 mb-1">Create your account</h1>
<p class="text-muted small mb-0">Fill in the details below to get started</p>
+2 -2
View File
@@ -1,4 +1,4 @@
{
"name": "wms/mn3wms",
"description": "MN3 WMS"
"name": "wms/brnwms",
"description": "BRN WMS"
}
+1 -1
View File
@@ -82,7 +82,7 @@ function findFreeBin(PDO $pdo2, int $company_id, int $warehouse_id): string
return $bin;
}
echo "\n\033[1m=== mn3wms Demo Data Seeder ===\033[0m\n\n";
echo "\n\033[1m=== brnwms Demo Data Seeder ===\033[0m\n\n";
// ─────────────────────────────────────────────────────────────────────────────
// 1. Owner user + company (pdo1 = wms auth/identity db)
+1 -1
View File
@@ -48,7 +48,7 @@ function buildLineItem(array $products, string $sku, float $qty): array {
];
}
echo "\n\033[1m=== mn3wms Demo Data Seeder — more sales orders ===\033[0m\n\n";
echo "\n\033[1m=== brnwms Demo Data Seeder — more sales orders ===\033[0m\n\n";
// ── Resolve shared context (company, warehouse, products, customers, dept) ────
$sth = $pdo1->prepare("SELECT company_id FROM company_list WHERE channel_name = 'demo' LIMIT 1");
+1 -1
View File
@@ -74,7 +74,7 @@ function ok(string $msg): void { echo "\033[32m ✓\033[0m {$msg}\n"; }
function skip(string $msg): void { echo "\033[33m –\033[0m {$msg}\n"; }
function warn(string $msg): void { echo "\033[31m !\033[0m {$msg}\n"; }
echo "\n\033[1m=== mn3wms Demo Transaction Seeder ===\033[0m\n\n";
echo "\n\033[1m=== brnwms Demo Transaction Seeder ===\033[0m\n\n";
// ─────────────────────────────────────────────────────────────────────────────
// 0. Resolve company / owner / warehouses / contacts seeded by demo_seed.php
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

+5 -5
View File
@@ -67,7 +67,7 @@
<path d="M310,300 L310,290 M310,300 L320,300" stroke="#f25c05" stroke-width="2.5" fill="none" stroke-linecap="round"/>
<!-- Barcode text below scan area -->
<text x="220" y="323" font-family="monospace" font-size="10" fill="#888" text-anchor="middle">SKU|MN3-0042|LOT-B|001</text>
<text x="220" y="323" font-family="monospace" font-size="10" fill="#888" text-anchor="middle">SKU|BRN-0042|LOT-B|001</text>
<!-- Section label: Recent Scans -->
<text x="118" y="350" font-family="sans-serif" font-size="11" font-weight="bold" fill="#272556">Recent Scans</text>
@@ -77,28 +77,28 @@
<rect x="112" y="358" width="216" height="38" rx="7" ry="7" fill="#fff" stroke="#e4e6f0" stroke-width="1"/>
<!-- green dot -->
<circle cx="127" cy="377" r="5" fill="#00d563"/>
<text x="140" y="372" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">MN3-0042 · Lot B</text>
<text x="140" y="372" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">BRN-0042 · Lot B</text>
<text x="140" y="385" font-family="sans-serif" font-size="9" fill="#888">Loc: A1-03 · Qty: 50</text>
<text x="316" y="372" font-family="sans-serif" font-size="9" fill="#465291" text-anchor="end">In</text>
<!-- Row 2 -->
<rect x="112" y="402" width="216" height="38" rx="7" ry="7" fill="#fff" stroke="#e4e6f0" stroke-width="1"/>
<circle cx="127" cy="421" r="5" fill="#f25c05"/>
<text x="140" y="416" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">MN3-0017 · Lot A</text>
<text x="140" y="416" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">BRN-0017 · Lot A</text>
<text x="140" y="429" font-family="sans-serif" font-size="9" fill="#888">Loc: B2-01 · Qty: 12</text>
<text x="316" y="416" font-family="sans-serif" font-size="9" fill="#f25c05" text-anchor="end">Out</text>
<!-- Row 3 -->
<rect x="112" y="446" width="216" height="38" rx="7" ry="7" fill="#fff" stroke="#e4e6f0" stroke-width="1"/>
<circle cx="127" cy="465" r="5" fill="#00d563"/>
<text x="140" y="460" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">MN3-0055 · Lot C</text>
<text x="140" y="460" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556">BRN-0055 · Lot C</text>
<text x="140" y="473" font-family="sans-serif" font-size="9" fill="#888">Loc: A3-07 · Qty: 200</text>
<text x="316" y="460" font-family="sans-serif" font-size="9" fill="#465291" text-anchor="end">In</text>
<!-- Row 4 (partial, fades at bottom) -->
<rect x="112" y="490" width="216" height="38" rx="7" ry="7" fill="#fff" stroke="#e4e6f0" stroke-width="1" opacity="0.5"/>
<circle cx="127" cy="509" r="5" fill="#7177b5" opacity="0.5"/>
<text x="140" y="504" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556" opacity="0.5">MN3-0031 · Lot A</text>
<text x="140" y="504" font-family="sans-serif" font-size="10" font-weight="bold" fill="#272556" opacity="0.5">BRN-0031 · Lot A</text>
<text x="140" y="517" font-family="sans-serif" font-size="9" fill="#888" opacity="0.5">Loc: C1-02 · Qty: 80</text>
<!-- Bottom navigation bar -->

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

+3 -3
View File
@@ -8,7 +8,7 @@ $app_url = $app_url ?? '/app';
<div class="row">
<div class="col-lg-12">
<div class="footer-col first">
<h6>MN3 WMS</h6>
<h6>BRN WMS</h6>
<p class="p-small">A modern Warehouse Management System for stock control, order processing, and barcode-driven operations.</p>
</div>
<div class="footer-col second">
@@ -23,7 +23,7 @@ $app_url = $app_url ?? '/app';
</ul>
</div>
<div class="footer-col third">
<p class="p-small">Questions? <a href="mailto:contact@mn3wms.com"><strong>contact@mn3wms.com</strong></a></p>
<p class="p-small">Questions? <a href="mailto:contact@brnwms.com"><strong>contact@brnwms.com</strong></a></p>
</div>
</div>
</div>
@@ -36,7 +36,7 @@ $app_url = $app_url ?? '/app';
<div class="container">
<div class="row">
<div class="col-lg-12">
<p class="p-small">Copyright &copy; MN3 WMS. All rights reserved.</p>
<p class="p-small">Copyright &copy; BRN WMS. All rights reserved.</p>
</div>
</div>
</div>
+3 -3
View File
@@ -1,14 +1,14 @@
<?php
// Usage: require __DIR__ . '/head.php';
// Caller may define $page_title and $page_description before requiring this file.
$page_title = $page_title ?? 'MN3 WMS — Warehouse Management System';
$page_description = $page_description ?? 'MN3 WMS — A cloud-ready Warehouse Management System for stock control, order fulfillment, and barcode labeling.';
$page_title = $page_title ?? 'BRN WMS — Warehouse Management System';
$page_description = $page_description ?? 'BRN WMS — A cloud-ready Warehouse Management System for stock control, order fulfillment, and barcode labeling.';
$base = $base ?? '/landing';
?>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="<?= htmlspecialchars($page_description) ?>">
<meta name="author" content="MN3">
<meta name="author" content="BRN">
<title><?= htmlspecialchars($page_title) ?></title>
<!-- Styles -->
+1 -1
View File
@@ -6,7 +6,7 @@ $app_url = $app_url ?? '/app';
<div class="container">
<a class="navbar-brand logo-image" href="<?= $base ?>/index.php">
<img src="<?= $base ?>/images/logo.png" alt="MN3 WMS" class="logo-wordmark">
<img src="<?= $base ?>/images/logo.png" alt="BRN WMS" class="logo-wordmark">
</a>
<button class="navbar-toggler p-0 border-0" type="button" id="navbarSideCollapse" aria-label="Toggle navigation">
+6 -6
View File
@@ -41,7 +41,7 @@ $active_nav = 'home';
<div class="container">
<div class="row">
<div class="col-lg-12">
<h2 class="h2-heading">MN3 WMS is packed with <span>powerful warehouse tools</span></h2>
<h2 class="h2-heading">BRN WMS is packed with <span>powerful warehouse tools</span></h2>
</div>
</div>
<div class="row">
@@ -91,7 +91,7 @@ $active_nav = 'home';
<div class="col-lg-6 col-xl-5">
<div class="text-container">
<h2>Track every item, every movement, in real time</h2>
<p>MN3 WMS gives you a live stock overview across all your warehouses. Drill into individual lots, view expiry dates, and spot low-stock alerts before they become a problem.</p>
<p>BRN WMS gives you a live stock overview across all your warehouses. Drill into individual lots, view expiry dates, and spot low-stock alerts before they become a problem.</p>
<p>Advanced and simple location modes let you model everything from a single bin to complex zone-aisle-bin layouts.</p>
<a class="btn-solid-reg" href="<?= $app_url ?>/login/index.php">See it in action</a>
</div>
@@ -147,7 +147,7 @@ $active_nav = 'home';
<div class="container">
<div class="row">
<div class="col-lg-12">
<h4>Ready to take control of your warehouse? MN3 WMS is fast to set up and easy to use from day one.</h4>
<h4>Ready to take control of your warehouse? BRN WMS is fast to set up and easy to use from day one.</h4>
<a class="btn-outline-lg" href="<?= $app_url ?>/login/register.php">Create an account</a>
</div>
</div>
@@ -170,10 +170,10 @@ $active_nav = 'home';
<div class="accordion-item">
<h2 class="accordion-header" id="faqOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faqCollapseOne" aria-expanded="true" aria-controls="faqCollapseOne">What is MN3 WMS and who is it for?</button>
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faqCollapseOne" aria-expanded="true" aria-controls="faqCollapseOne">What is BRN WMS and who is it for?</button>
</h2>
<div id="faqCollapseOne" class="accordion-collapse collapse show" aria-labelledby="faqOne" data-bs-parent="#accordionFaq">
<div class="accordion-body">MN3 WMS is a web-based Warehouse Management System designed for businesses that need real-time stock control, multi-warehouse support, and order fulfillment tracking. It works for small warehouses and scales to complex multi-location operations.</div>
<div class="accordion-body">BRN WMS is a web-based Warehouse Management System designed for businesses that need real-time stock control, multi-warehouse support, and order fulfillment tracking. It works for small warehouses and scales to complex multi-location operations.</div>
</div>
</div>
@@ -182,7 +182,7 @@ $active_nav = 'home';
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faqCollapseTwo" aria-expanded="false" aria-controls="faqCollapseTwo">Does it support barcode scanning?</button>
</h2>
<div id="faqCollapseTwo" class="accordion-collapse collapse" aria-labelledby="faqTwo" data-bs-parent="#accordionFaq">
<div class="accordion-body">Yes. MN3 WMS can generate and print SKU product labels and warehouse location labels. The mobile-friendly interface also supports camera-based barcode scanning directly in the browser — no external app required.</div>
<div class="accordion-body">Yes. BRN WMS can generate and print SKU product labels and warehouse location labels. The mobile-friendly interface also supports camera-based barcode scanning directly in the browser — no external app required.</div>
</div>
</div>
+6 -6
View File
@@ -2,8 +2,8 @@
require __DIR__ . '/../app/config.php';
$base = rtrim($base_url, '/') . '/landing';
$app_url = rtrim($base_url, '/') . '/app';
$page_title = 'Privacy Policy — MN3 WMS';
$page_description = 'Privacy Policy for MN3 WMS.';
$page_title = 'Privacy Policy — BRN WMS';
$page_description = 'Privacy Policy for BRN WMS.';
$active_nav = '';
?>
<!DOCTYPE html>
@@ -28,13 +28,13 @@ $active_nav = '';
<h1>Privacy Policy</h1>
<p class="updated">Last updated: May 2026</p>
<p>This Privacy Policy describes how MN3 WMS collects, uses, and protects your information when you use our service.</p>
<p>This Privacy Policy describes how BRN WMS collects, uses, and protects your information when you use our service.</p>
<h2>1. Information We Collect</h2>
<p>We collect information you provide directly — such as your name, email address, and company details when you register — and operational data you enter into the system (products, stock, orders).</p>
<h2>2. How We Use Your Information</h2>
<p>We use your information solely to provide and improve the MN3 WMS service, authenticate your identity, and communicate with you about your account. We do not sell or share your data with third parties for marketing purposes.</p>
<p>We use your information solely to provide and improve the BRN WMS service, authenticate your identity, and communicate with you about your account. We do not sell or share your data with third parties for marketing purposes.</p>
<h2>3. Session and Authentication Data</h2>
<p>Login sessions are protected by OTP-based two-factor authentication. Session tokens are stored securely and expire after inactivity. We log login events for security auditing.</p>
@@ -49,7 +49,7 @@ $active_nav = '';
<p>We use industry-standard security practices including CSRF protection, parameterised queries, and encrypted connections. No system is completely secure — you are also responsible for protecting your credentials.</p>
<h2>7. Cookies</h2>
<p>MN3 WMS uses session cookies for authentication. We do not use third-party tracking or advertising cookies.</p>
<p>BRN WMS uses session cookies for authentication. We do not use third-party tracking or advertising cookies.</p>
<h2>8. Your Rights</h2>
<p>You may request access to, correction of, or deletion of your personal data by contacting us. We will respond within a reasonable timeframe.</p>
@@ -58,7 +58,7 @@ $active_nav = '';
<p>We may update this policy periodically. We will notify users of significant changes via email or an in-app notice.</p>
<h2>10. Contact</h2>
<p>Privacy questions? Email <a href="mailto:contact@mn3wms.com">contact@mn3wms.com</a>.</p>
<p>Privacy questions? Email <a href="mailto:contact@brnwms.com">contact@brnwms.com</a>.</p>
</div>
</div>
</div>
+7 -7
View File
@@ -2,8 +2,8 @@
require __DIR__ . '/../app/config.php';
$base = rtrim($base_url, '/') . '/landing';
$app_url = rtrim($base_url, '/') . '/app';
$page_title = 'Terms of Service — MN3 WMS';
$page_description = 'Terms of Service for MN3 WMS.';
$page_title = 'Terms of Service — BRN WMS';
$page_description = 'Terms of Service for BRN WMS.';
$active_nav = '';
?>
<!DOCTYPE html>
@@ -28,10 +28,10 @@ $active_nav = '';
<h1>Terms of Service</h1>
<p class="updated">Last updated: May 2026</p>
<p>By accessing or using MN3 WMS, you agree to be bound by these Terms of Service. Please read them carefully.</p>
<p>By accessing or using BRN WMS, you agree to be bound by these Terms of Service. Please read them carefully.</p>
<h2>1. Use of the Service</h2>
<p>MN3 WMS is a warehouse management platform. You may use the service only for lawful business purposes and in accordance with these terms. You are responsible for all activity that occurs under your account.</p>
<p>BRN WMS is a warehouse management platform. You may use the service only for lawful business purposes and in accordance with these terms. You are responsible for all activity that occurs under your account.</p>
<h2>2. Account Security</h2>
<p>You are responsible for maintaining the confidentiality of your login credentials. You must notify us immediately of any unauthorized access to your account. Two-factor authentication (OTP) is required on every session.</p>
@@ -40,19 +40,19 @@ $active_nav = '';
<p>You retain ownership of all data you enter into the system. We do not access your operational data except as required to provide or support the service.</p>
<h2>4. Acceptable Use</h2>
<p>You may not use MN3 WMS to store illegal content, attempt to breach system security, or interfere with other tenants. We reserve the right to suspend accounts that violate these terms.</p>
<p>You may not use BRN WMS to store illegal content, attempt to breach system security, or interfere with other tenants. We reserve the right to suspend accounts that violate these terms.</p>
<h2>5. Service Availability</h2>
<p>We aim for high availability but do not guarantee uninterrupted access. Scheduled maintenance will be communicated in advance where possible.</p>
<h2>6. Limitation of Liability</h2>
<p>MN3 WMS is provided "as is." To the maximum extent permitted by law, we are not liable for indirect, incidental, or consequential damages arising from your use of the service.</p>
<p>BRN WMS is provided "as is." To the maximum extent permitted by law, we are not liable for indirect, incidental, or consequential damages arising from your use of the service.</p>
<h2>7. Changes to Terms</h2>
<p>We may update these terms from time to time. Continued use of the service after changes are posted constitutes acceptance of the revised terms.</p>
<h2>8. Contact</h2>
<p>Questions about these terms? Email <a href="mailto:contact@mn3wms.com">contact@mn3wms.com</a>.</p>
<p>Questions about these terms? Email <a href="mailto:contact@brnwms.com">contact@brnwms.com</a>.</p>
</div>
</div>
</div>
+1 -1
View File
@@ -11,4 +11,4 @@ EMIT_SECRET=
# PHP internal base URL (scheduler → PHP cron endpoints)
PHP_BASE_URL=http://127.0.0.1
PHP_WEBROOT=/mn3wms/app
PHP_WEBROOT=/brnwms/app
+2 -2
View File
@@ -1,7 +1,7 @@
module.exports = {
apps: [
{
name: 'mn3-socket',
name: 'brn-socket',
script: 'server.js',
cwd: __dirname,
watch: false,
@@ -12,7 +12,7 @@ module.exports = {
time: true
},
{
name: 'mn3-scheduler',
name: 'brn-scheduler',
script: 'scheduler.js',
cwd: __dirname,
watch: false,
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "mn3wms-realtime",
"name": "brnwms-realtime",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mn3wms-realtime",
"name": "brnwms-realtime",
"version": "1.0.0",
"dependencies": {
"dotenv": "^16.0.0",
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "mn3wms-realtime",
"name": "brnwms-realtime",
"version": "1.0.0",
"description": "Real-time event server for MN3 WMS",
"description": "Real-time event server for BRN WMS",
"main": "server.js",
"scripts": {
"start": "node server.js"
+1 -1
View File
@@ -4,7 +4,7 @@ const cron = require('node-cron');
const http = require('http');
const PHP_BASE = process.env.PHP_BASE_URL || 'http://127.0.0.1';
const PHP_WEBROOT = process.env.PHP_WEBROOT || '/mn3wms/app';
const PHP_WEBROOT = process.env.PHP_WEBROOT || '/brnwms/app';
const SECRET = process.env.EMIT_SECRET || '';
const NODE_PORT = process.env.PORT || 3000;
+143
View File
@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
if ($argc !== 2) {
fwrite(STDERR, "Usage: php adjust_docx_whitespace.php <document.docx>\n");
exit(2);
}
$path = $argv[1];
$wordNs = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
$zip = new ZipArchive();
if ($zip->open($path) !== true) {
throw new RuntimeException("Cannot open DOCX: {$path}");
}
$documentXml = $zip->getFromName('word/document.xml');
if ($documentXml === false) {
throw new RuntimeException('word/document.xml is missing');
}
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = false;
if (!$doc->loadXML($documentXml)) {
throw new RuntimeException('word/document.xml is invalid');
}
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('w', $wordNs);
function attr(DOMElement $element, string $name, string $value, string $ns): void
{
$element->setAttributeNS($ns, 'w:' . $name, $value);
}
function child(DOMDocument $doc, DOMElement $parent, string $localName, string $ns): DOMElement
{
foreach ($parent->childNodes as $node) {
if ($node instanceof DOMElement && $node->namespaceURI === $ns && $node->localName === $localName) {
return $node;
}
}
$element = $doc->createElementNS($ns, 'w:' . $localName);
$parent->appendChild($element);
return $element;
}
foreach ($xpath->query('//w:body/w:p | //w:body/w:tbl//w:p') as $paragraph) {
if (!$paragraph instanceof DOMElement) {
continue;
}
$text = '';
foreach ($xpath->query('.//w:t', $paragraph) as $textNode) {
$text .= $textNode->textContent;
}
$text = trim(preg_replace('/\s+/u', ' ', $text) ?? '');
$pPrNode = $xpath->query('./w:pPr', $paragraph)->item(0);
if (!$pPrNode instanceof DOMElement) {
$pPrNode = $doc->createElementNS($wordNs, 'w:pPr');
$paragraph->insertBefore($pPrNode, $paragraph->firstChild);
}
$spacing = child($doc, $pPrNode, 'spacing', $wordNs);
$before = '0';
$after = '30';
$line = '320';
if ($text === '') {
$after = '20';
$line = '120';
} elseif (preg_match('/^[1-7]\.\s/u', $text)) {
$before = '120';
$after = '45';
$line = '330';
} elseif (preg_match('/^[1-7]\.\d\s/u', $text)) {
$before = '70';
$after = '30';
$line = '320';
} elseif (str_starts_with($text, '•')) {
$after = '15';
$line = '310';
} elseif (str_starts_with($text, 'วันที่ 5 มกราคม')) {
$before = '40';
$after = '100';
$line = '330';
} elseif (str_starts_with($text, 'เรื่อง') || str_starts_with($text, 'เรียน')) {
$after = '55';
$line = '330';
} elseif (str_starts_with($text, 'ลงชื่อ')) {
$before = '160';
$after = '10';
$line = '310';
} elseif (str_starts_with($text, '(') || str_starts_with($text, 'ตำแหน่ง') || str_starts_with($text, 'วันที่ _') || str_starts_with($text, 'ผู้มีอำนาจ')) {
$after = '10';
$line = '310';
} elseif (str_starts_with($text, 'บริษัท บี.อาร์.เอ็น.') || str_starts_with($text, 'เอกสารฉบับนี้') || str_starts_with($text, 'ผลส่งมอบประกอบ') || str_starts_with($text, 'การเปลี่ยนแปลง') || str_starts_with($text, 'จึงจัดทำ')) {
$after = '55';
$line = '330';
}
attr($spacing, 'before', $before, $wordNs);
attr($spacing, 'after', $after, $wordNs);
attr($spacing, 'line', $line, $wordNs);
attr($spacing, 'lineRule', 'auto', $wordNs);
}
foreach ($xpath->query('//w:sectPr/w:pgMar') as $margin) {
if (!$margin instanceof DOMElement) {
continue;
}
attr($margin, 'top', '850', $wordNs);
attr($margin, 'bottom', '850', $wordNs);
attr($margin, 'left', '1080', $wordNs);
attr($margin, 'right', '1080', $wordNs);
attr($margin, 'header', '400', $wordNs);
attr($margin, 'footer', '400', $wordNs);
}
foreach ($xpath->query('//w:tbl/w:tblPr') as $tableProperties) {
if (!$tableProperties instanceof DOMElement) {
continue;
}
$margins = child($doc, $tableProperties, 'tblCellMar', $wordNs);
foreach (['top' => '45', 'left' => '70', 'bottom' => '45', 'right' => '70'] as $side => $width) {
$edge = child($doc, $margins, $side, $wordNs);
attr($edge, 'w', $width, $wordNs);
attr($edge, 'type', 'dxa', $wordNs);
}
}
$updatedXml = $doc->saveXML();
if ($updatedXml === false) {
throw new RuntimeException('Cannot serialize adjusted document XML');
}
if (!$zip->addFromString('word/document.xml', $updatedXml)) {
throw new RuntimeException('Cannot update word/document.xml');
}
$zip->close();
echo $path . PHP_EOL;
+117
View File
@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
/*
* Applies the approved print layout from the first formatted Progress Status
* Record to every generated Progress Status Record HTML file. Markdown source
* files are deliberately not read or changed.
*
* Usage:
* php scripts/format_progress_status_html.php
*/
$root = dirname(__DIR__);
$directory = $root . '/sdlc/1-PM Process (10 Work Product)/3.Progess Status Record';
$templatePath = $directory . '/200-WMS-26-001-00 Progress Status Record 25690123 V1.0 ApS.html';
if (!is_file($templatePath)) {
fwrite(STDERR, "Template Progress Status Record HTML was not found.\n");
exit(1);
}
$template = file_get_contents($templatePath);
if ($template === false || !preg_match('~(/\* BRN Progress Status Record print layout.*?</style>)~s', $template, $match)) {
fwrite(STDERR, "The template does not contain the approved Progress Status Record CSS.\n");
exit(1);
}
// The CSS must work for every record number, not just record 1.
$layoutCss = str_replace(
'#progress-status-record-1-of-13',
'h1[id^="progress-status-record-"]',
$match[1]
);
/** @return string */
function approvalCard(string $id, string $title, string $paragraph): string
{
$lines = preg_split('~<br\s*/?>\s*~i', trim($paragraph)) ?: [];
$items = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') {
continue;
}
if (preg_match('~^(Signature|Date):\s*_*\s*$~i', strip_tags($line), $label)) {
$items[] = '<p class="sign-row"><strong>' . htmlspecialchars($label[1], ENT_QUOTES, 'UTF-8') . ':</strong><span class="write-line"></span></p>';
} else {
$items[] = '<p>' . $line . '</p>';
}
}
return '<div class="approval-card"><h3 id="' . $id . '">' . $title . '</h3>' . implode("\n", $items) . '</div>';
}
$files = glob($directory . '/*.html') ?: [];
$formatted = 0;
$skipped = 0;
foreach ($files as $path) {
$html = file_get_contents($path);
if ($html === false) {
fwrite(STDERR, "Could not read: $path\n");
continue;
}
if (str_contains($html, '/* BRN Progress Status Record print layout')) {
$skipped++;
continue;
}
$html = preg_replace('~</head>~', "<style>\n$layoutCss\n</style>\n</head>", $html, 1, $headCount);
if ($headCount !== 1) {
fwrite(STDERR, "Skipped malformed HTML: " . basename($path) . "\n");
continue;
}
$header = '<header class="psr-letterhead"><img class="document-header-image" src="../../../app/assets/images/brn-document-header-left.png" alt="BRN corporate identity"><div class="psr-title-band">Progress Status Record</div></header>';
$html = preg_replace(
'~(<h1 id="progress-status-record-[^"]+">.*?</h1>\s*)<table>~s',
$header . "\n" . '$1<table class="document-control">',
$html,
1,
$titleCount
);
if ($titleCount !== 1) {
fwrite(STDERR, "Skipped unsupported record structure: " . basename($path) . "\n");
continue;
}
if (preg_match('~<h2 id="8-approval">8\. Approval</h2>(.*?)(?=</body>)~s', $html, $approvalMatch)) {
preg_match_all('~<h3 id="([^"]+)">(.*?)</h3>\s*<p>(.*?)</p>~s', $approvalMatch[1], $blocks, PREG_SET_ORDER);
if (count($blocks) === 3) {
$cards = [];
foreach ($blocks as $block) {
$cards[] = approvalCard($block[1], $block[2], $block[3]);
}
$approval = '<section class="approval-page"><h2 id="8-approval">8. Approval</h2><div class="approval-grid">'
. implode("\n", $cards)
. '</div></section>';
$html = str_replace($approvalMatch[0], $approval, $html);
}
}
$date = 'Progress Status Record';
if (preg_match('~<td>Report date</td>\s*<td>(.*?)</td>~s', $html, $dateMatch)) {
$date = trim(strip_tags($dateMatch[1]));
}
$footer = '<footer class="psr-footer">200-WMS-26-001-00 · BRN WMS · Progress Status Record · '
. htmlspecialchars($date, ENT_QUOTES, 'UTF-8') . ' · V1.0 Final</footer>';
$html = str_replace('</body>', $footer . "\n</body>", $html);
if (file_put_contents($path, $html) === false) {
fwrite(STDERR, "Could not write: $path\n");
continue;
}
$formatted++;
}
printf("Formatted: %d; already formatted: %d\n", $formatted, $skipped);
@@ -0,0 +1,182 @@
<?php
declare(strict_types=1);
$directory = __DIR__ . '/../sdlc/1-PM Process (10 Work Product)/3.Progess Status Record';
$records = [
['25690123','5–23 January 2026','Project initiation','Establish the project need, stakeholders, objectives, scope, and authority.','Formal project start and scope boundary were established retrospectively from the agreed lifecycle.','Agreed reconstructed','Statement of Work; Work Schedule','Not rated — reconstructed','Historical initiation records were not created contemporaneously.','Complete customer requirements and project planning baseline.'],
['25690218','12 January–18 February 2026','Requirements and planning baseline','Collect customer requirements and define schedule, resources, risks, controls, and work-product responsibilities.','Customer Requirements, Work Schedule, and Software Project Plan were reconstructed from agreed scope and implementation evidence.','Document reconstructed','Project Plan work products','Not rated — reconstructed','Exact elicitation dates and contemporaneous approvals are unavailable.','Begin controlled software implementation on 19 February 2026.'],
['25690225','19–25 February 2026','Application initialization','Initialize the repository and establish initial application and file-upload capability.','WMS repository initialized; verification commit and dropzone/file-upload work completed.','Git evidenced','`9a50080`, `42a3876`, `8625652`, `1843308`','Green','No unresolved blocker is evidenced in the repository for this period.','Protect configuration, address security findings, and establish the stock database foundation.'],
['25690317','9–17 March 2026','Security, database, and ICS foundation','Protect local configuration, address initial security findings, design the stock database, and introduce inventory-control modules.','Configuration was removed from tracking, security-audit fixes were applied, stock database design was committed, and ICS modules were introduced.','Git evidenced','`93d903c`, `7cb78d0`, `2e558a5`, `a4f474b`','Green','The period contains a gap in commit activity before the documented security/database work.','Develop stock visibility, warehouse structure, product controls, and operational reports.'],
['25690429','9–29 April 2026','Inventory, warehouse, and access foundation','Implement stock dashboard, products, warehouse capacity, traceability, reports, settings, authentication, and reusable managers.','Dashboard, product files, OOP utilities, capacity/occupancy, lot/serial/expiry, reports, settings, login/onboarding, and manager classes were implemented.','Git evidenced','`3b8f94f` through `db5c47b`','Green','Rapid module growth increased the need for consistent authorization and lifecycle review.','Complete document approval logic, orders, warehouse layers, barcode, and role guards.'],
['25690508','30 April–8 May 2026','Orders, barcode, and security controls','Implement approval logic, customer order flows, flexible warehouse layers, barcode operations, and centralized role guards.','Password scoring, stock approval, orders/returns/invoices, switchable warehouse layers, barcode functions, role guards, security fixes, and naming/session corrections were completed.','Git evidenced','30-Apr through 08-May commits','Green','Security gaps were actively corrected during implementation; formal correction records remain to be linked.','Prepare production setup and introduce accounting capabilities.'],
['25690513','11–13 May 2026','Production preparation and accounting foundation','Prepare deployment, automate setup, correct onboarding/recovery, populate test data, and introduce accounting.','Production preparation, dynamic base URL, automated setup, password recovery, onboarding fixes, test data, dashboard updates, and accounting modules were committed.','Git evidenced','11–13 May commits','Green','Multiple fixes and a revert occurred during integration; results require traceability to tests.','Integrate accounting workflows, document control, access limits, and supporting services.'],
['25690523','20–23 May 2026','Accounting integration and supporting services','Integrate accounting, user invitation/access controls, transaction limits, document flows, Node.js, aggregates, and reports.','Accounting workflows, access controls, setup script, soft delete, Socket service, GL aggregation, accounting reports, journal batching, and GL automation were implemented.','Git evidenced','20–23 May commits','Green','Integration breadth increased regression and tenant-isolation risk.','Complete security hardening, notification control, sequencing, scheduler, tenant scoping, and final review.'],
['25690529','24–29 May 2026','Development baseline','Harden security and integrity, close known implementation gaps, stabilize services, and establish the substantially complete development baseline.','Session controls, aggregates, notifications, security/lifecycle reviews, transaction limits, document sequencing, role guards, scheduler fixes, bin naming, tenant scoping, and final refactoring were completed.','Git evidenced','24–29 May commits; final `a0677d6`','Green','Formal verification, validation, and acceptance evidence remained to be consolidated after development.','Conduct verification, validation, documentation, and delivery preparation.'],
['25690731','30 May–31 July 2026','Verification, validation, and documentation','Verify requirements/design/software, validate intended use, prepare operational documents, and consolidate delivery evidence.','This activity period is part of the agreed timeline, but contemporaneous Git evidence is unavailable; assurance and documentation records are being reconstructed from the delivered system.','Document reconstructed','SRS, Design, Traceability, Test, Guide, Verification, and Validation work products','Amber','Missing contemporaneous evidence creates an audit and acceptance gap.','Complete stabilization corrections, finalize evidence, and obtain stakeholder review.'],
['25690803','3 August 2026','Stabilization correction','Correct login and environment-configuration issues identified after the development baseline.','Login and configuration corrections were committed.','Git evidenced','`b2c4374`','Green','The correction requires linkage to the Correction Register and verification evidence.','Prepare representative demonstration data and complete final delivery checks.'],
['25690814','4–14 August 2026','Demonstration and completion boundary','Prepare controlled demonstration data, complete final checks, and reach the agreed project-completion boundary.','Demonstration data was committed on 14 August 2026 and the agreed formal completion date was reached.','Git evidenced / agreed boundary','`dd48a8b`; agreed completion date','Green','Formal signatures and some controlled ISO/IEC 29110 evidence remained open at completion.','Consolidate remaining work products and obtain Project Sponsor authorization.'],
['25690817','15–17 August 2026','Evidence consolidation and authorization preparation','Complete missing controlled work products, align roles, and prepare records for review and authorization.','PM work products were converted to reviewable Markdown, project identities and roles were aligned, and missing evidence was identified for subsequent completion.','Document reconstructed','Current `sdlc/` repository and Git status','Amber','Verification, validation, repository-backup, and acceptance evidence and signatures remain open.','Complete remaining PM/SI work products and obtain Project Sponsor review and authorization.'],
];
$tasksByDate = [
'25690123' => [
['1.1','Identify project need','09-Jan-2026','09-Jan-2026','100%','Statement of Work','Reconstructed completed'],
['1.2','Identify stakeholders and objectives','16-Jan-2026','16-Jan-2026','100%','Project role confirmation','Reconstructed completed'],
['1.3','Approve project scope','23-Jan-2026','Pending signature','90%','Statement of Work V1.0 Final','Approval pending'],
],
'25690218' => [
['2.1','Collect customer requirements','06-Feb-2026','06-Feb-2026','100%','Customer Requirements V1.0 Final','Reconstructed completed'],
['2.2','Prepare Software Project Plan','13-Feb-2026','13-Feb-2026','100%','Software Project Plan V1.0 Final','Reconstructed completed'],
['2.3','Baseline requirements and schedule','18-Feb-2026','18-Feb-2026','100%','Work Schedule and Project Plan','Reconstructed completed; approval pending'],
],
'25690225' => [
['3.1','Initialize WMS application','25-Feb-2026','25-Feb-2026','100%','Git `9a50080`–`1843308`','Completed'],
],
'25690317' => [
['3.2','Security and database foundation','17-Mar-2026','17-Mar-2026','100%','Git `93d903c`–`a4f474b`','Completed'],
],
'25690429' => [
['3.3','Inventory and warehouse modules','29-Apr-2026','29-Apr-2026','100%','Git `3b8f94f`–`db5c47b`','Completed'],
],
'25690508' => [
['3.4','Authentication and onboarding','12-May-2026','In progress at 08-May','70%','Commits through 08-May-2026','In progress; carried forward'],
['3.5','Order and barcode workflows','08-May-2026','08-May-2026','100%','02–08 May commits','Completed'],
],
'25690513' => [
['3.4','Authentication and onboarding','12-May-2026','12-May-2026','100%','Login/onboarding and recovery commits','Completed'],
['3.6','Production preparation and setup','13-May-2026','13-May-2026','100%','11–13 May commits','Completed'],
['3.7','Accounting and finance workflows','23-May-2026','In progress at 13-May','20%','Accounting foundation commits','In progress; carried forward'],
],
'25690523' => [
['3.7','Accounting and finance workflows','23-May-2026','23-May-2026','100%','13–23 May accounting commits','Completed'],
['3.8','Real-time services and scheduled jobs','27-May-2026','In progress at 23-May','45%','Node.js and GL aggregate commits','In progress; carried forward'],
['3.9','Security hardening and lifecycle review','28-May-2026','In progress at 23-May','35%','Access, flow, and role-guard commits','In progress; carried forward'],
],
'25690529' => [
['3.8','Real-time services and scheduled jobs','27-May-2026','27-May-2026','100%','22–27 May Node.js/scheduler commits','Completed'],
['3.9','Security hardening and lifecycle review','28-May-2026','28-May-2026','100%','21–28 May hardening/review commits','Completed'],
['3.10','Refactor and development baseline','29-May-2026','29-May-2026','100%','Git `a0677d6`','Completed'],
],
'25690731' => [
['4.3','Verify requirements, design, and implementation','31-Jul-2026','Evidence consolidation pending','80%','Reconstructed verification work products','In progress; evidence gap'],
['4.4','Customer-oriented system validation','07-Aug-2026','In progress at 31-Jul','85%','Reconstructed validation activities','In progress; carried forward'],
['4.5','Prepare operational documentation','07-Aug-2026','In progress at 31-Jul','85%','Configuration and draft work products','In progress; carried forward'],
],
'25690803' => [
['4.6','Configuration and login corrections','03-Aug-2026','03-Aug-2026','100%','Git `b2c4374`','Completed; formal correction closure pending'],
],
'25690814' => [
['4.4','Customer-oriented system validation','07-Aug-2026','Evidence consolidation pending','90%','Validation work products','In progress; approval pending'],
['4.5','Prepare operational documentation','07-Aug-2026','Evidence consolidation pending','90%','Operational documentation work products','In progress; approval pending'],
['4.7','Prepare demonstration data','14-Aug-2026','14-Aug-2026','100%','Git `dd48a8b`','Completed'],
['5.1','Final repository and work-product review','14-Aug-2026','In progress','70%','Repository and SDLC gap review','In progress; carried forward'],
['5.2','Acceptance and project closure','14-Aug-2026','Pending signature','75%','Completion boundary reached','Acceptance pending'],
],
'25690817' => [
['5.1','Final repository and work-product review','14-Aug-2026','In progress at 17-Aug','80%','BRN WMS PM work products and gap list','In progress'],
['5.2','Acceptance and project closure','14-Aug-2026','Pending evidence and signature','75%','Acceptance evidence not yet authorized','Acceptance pending'],
],
];
foreach ($records as $index => $record) {
[$date,$period,$milestone,$planned,$completed,$evidenceClass,$evidence,$rag,$issue,$next] = $record;
$number = $index + 1;
$taskRows = '';
foreach ($tasksByDate[$date] as $task) {
$taskRows .= '| ' . implode(' | ', $task) . " |\n";
}
$content = <<<MD
# Progress Status Record {$number} of 13
| Document field | Value |
|---|---|
| Document | Progress Status Record |
| Project | BRN WMS |
| Project code | 200-WMS-26-001-00 |
| Reporting period | {$period} |
| Report date | {$date} |
| Actual preparation date | 17 August 2026 |
| Release | {$date} V1.0 Final |
| Prepared by | คุณอภิรัชต์ สุภัทรประทีป — Project Manager |
| Technical evidence | ธนกร สถิตวิทยากุล — Developer / System Analyst |
| Status | Final — ready for review and authorization |
## 1. Retrospective-record disclosure
This record was prepared retrospectively on 17 August 2026. It covers the historical reporting period shown above and does not claim to have been authored or committed on the historical report date. Statements are limited to the evidence classification and references identified below.
## 2. Period objective
**Milestone:** {$milestone}
**Planned outcome:** {$planned}
## 3. Task progress
**Period summary:** {$completed}
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|---|---|---:|---:|---:|---|---|
{$taskRows}
| Evidence field | Value |
|---|---|
| Evidence classification | {$evidenceClass} |
| Evidence reference | {$evidence} |
| Overall period status | {$rag} |
## 4. Schedule status
Work is assessed against the agreed project boundaries of 5 January–14 August 2026. No unsupported numeric variance is asserted for this period. Where the evidence is reconstructed, schedule status remains explicitly unrated rather than represented as contemporaneously measured.
## 5. Issues, risks, and corrective action
**Issue or risk:** {$issue}
**Control:** Preserve the stated evidence basis, link applicable defects to the Correction Register, update traceability and tests, and obtain the required review or authorization without backdating records or signatures.
## 6. Changes
Technical commits in this period are implementation evidence. They must be classified separately as in-scope implementation, defect correction, or an authorized baseline change before being entered in the Change Report.
## 7. Next-period plan
{$next}
## 8. Approval
### Prepared by
Name: คุณอภิรัชต์ สุภัทรประทีป
Role: Project Manager
Signature: ______________________________________________
Date: ___________________________________________________
### Technical evidence provided by
Name: ธนกร สถิตวิทยากุล
Role: Developer / System Analyst
Signature: ______________________________________________
Date: ___________________________________________________
### Reviewed and authorized by
Name: คุณเสรี วิริยะสกุลธรณ์
Project roles: Project Sponsor / Customer Representative / Authorized Approver
Position: กรรมการผู้จัดการ
Company: บริษัท บี.อาร์.เอ็น. เอ็นเตอร์ไพรส์ จำกัด
Signature: ______________________________________________
Date: ___________________________________________________
MD;
$filename = "200-WMS-26-001-00 Progress Status Record {$date} V1.0 ApS.md";
if (file_put_contents($directory . '/' . $filename, $content . PHP_EOL) === false) {
throw new RuntimeException('Unable to write ' . $filename);
}
}
require __DIR__ . '/normalize_document_dates.php';
echo count($records) . " progress status records generated.\n";
+80
View File
@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
$base = __DIR__ . '/../sdlc/1-PM Process (10 Work Product)/2.Project Plan/';
$w = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
function e(string $s): string { return htmlspecialchars($s, ENT_XML1 | ENT_QUOTES, 'UTF-8'); }
function r(string $s, bool $b = false, int $sz = 28): string {
return '<w:r><w:rPr>' . ($b ? '<w:b/><w:bCs/>' : '') . '<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="'.$sz.'"/><w:szCs w:val="'.$sz.'"/></w:rPr><w:t xml:space="preserve">'.e($s).'</w:t></w:r>';
}
function p(string $s = '', string $a = 'left', bool $b = false, int $sz = 28, int $before = 0, int $after = 40): string {
return '<w:p><w:pPr><w:jc w:val="'.$a.'"/><w:spacing w:before="'.$before.'" w:after="'.$after.'" w:line="320" w:lineRule="auto"/></w:pPr>'.r($s,$b,$sz).'</w:p>';
}
function h(string $no, string $title): string { return p($no.' '.$title,'left',true,31,120,45); }
function b(string $s): string { return '<w:p><w:pPr><w:ind w:left="650" w:hanging="300"/><w:spacing w:after="20" w:line="310" w:lineRule="auto"/></w:pPr>'.r('• '.$s,false,28).'</w:p>'; }
function pb(): string { return '<w:p><w:r><w:br w:type="page"/></w:r></w:p>'; }
function tr(array $cells, bool $head = false, array $widths = []): string {
$x='<w:tr>';
foreach($cells as $i=>$v){$width=$widths[$i]??2200;$x.='<w:tc><w:tcPr><w:tcW w:w="'.$width.'" w:type="dxa"/>'.($head?'<w:shd w:fill="D9EAF7"/>':'').'</w:tcPr>'.p((string)$v,'left',$head,25,0,15).'</w:tc>';}
return $x.'</w:tr>';
}
function tbl(array $rows, array $widths, bool $firstHead = true): string {
$x='<w:tbl><w:tblPr><w:tblW w:w="9000" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:sz="4" w:color="8AA4B5"/><w:left w:val="single" w:sz="4" w:color="8AA4B5"/><w:bottom w:val="single" w:sz="4" w:color="8AA4B5"/><w:right w:val="single" w:sz="4" w:color="8AA4B5"/><w:insideH w:val="single" w:sz="4" w:color="B7C9D6"/><w:insideV w:val="single" w:sz="4" w:color="B7C9D6"/></w:tblBorders></w:tblPr><w:tblGrid>';
foreach($widths as $wd)$x.='<w:gridCol w:w="'.$wd.'"/>';$x.='</w:tblGrid>';
foreach($rows as $i=>$row)$x.=tr($row,$firstHead&&$i===0,$widths);
return $x.'</w:tbl>';
}
function top(string $label): string {
return '<w:tbl><w:tblPr><w:tblW w:w="9000" w:type="dxa"/></w:tblPr><w:tblGrid><w:gridCol w:w="6000"/><w:gridCol w:w="3000"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:w="6000" w:type="dxa"/></w:tcPr>'.p('B.R.N. ENTERPRISE CO., LTD.','left',true,29,0,0).p('1011 Supalai Grand Tower, 7th Floor, Unit 6-7, Rama 3 Road, Bangkok 10120','left',false,18,0,0).'</w:tc><w:tc><w:tcPr><w:tcW w:w="3000" w:type="dxa"/><w:shd w:fill="0798D1"/><w:vAlign w:val="center"/></w:tcPr>'.p($label,'center',false,29,0,0).'</w:tc></w:tr></w:tbl>';
}
function meta(string $doc, string $title): string {
return tbl([
['Document No',$doc,'Release, Version','05/01/26 V1.0 Final'],
['Project Name','โครงการพัฒนาระบบบริหารจัดการคลังสินค้า BRN WMS','Project Code','200-WMS-26-001-00'],
['Title',$title,'Project Period','5 มกราคม – 14 สิงหาคม 2569'],
['Prepared by','คุณอภิรัชต์ สุภัทรประทีป (Project Manager)','Approval','Pending authorized signature'],
],[1600,3500,1600,2300],false);
}
function saveDoc(string $path,string $title,string $body,string $code): void {
$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="850" w:right="1080" w:bottom="850" w:left="1080" w:header="400" w:footer="400"/><w:footerReference w:type="default" r:id="rId1"/></w:sectPr></w:body></w:document>';
$ct='<?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"/></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"/></Relationships>';
$drels='<?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">'.p('ISO/IEC 29110 | 200-WMS-26-001-00 | '.$code.' | V1.0 Final','center',false,18,0,0).'</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>'.e($title).'</dc:title><dc:creator>คุณอภิรัชต์ สุภัทรประทีป (Project Manager)</dc:creator><dc:description>Editable ISO/IEC 29110 controlled final document ready for authorization.</dc:description><dcterms:created xsi:type="dcterms:W3CDTF">2026-01-05T00:00:00+07:00</dcterms:created></cp:coreProperties>';
$z=new ZipArchive();if($z->open($path,ZipArchive::CREATE|ZipArchive::OVERWRITE)!==true)throw new RuntimeException('Cannot create '.$path);
foreach(['[Content_Types].xml'=>$ct,'_rels/.rels'=>$rels,'word/document.xml'=>$document,'word/_rels/document.xml.rels'=>$drels,'word/footer1.xml'=>$footer,'docProps/core.xml'=>$core] as $n=>$v)$z->addFromString($n,$v);$z->close();
}
$plan = top('Software Project Plan').p().meta('Software Project Plan','แผนการดำเนินโครงการ').h('1.','วัตถุประสงค์และภาพรวม')
.p('โครงการ BRN WMS จัดทำระบบเว็บแบบหลายบริษัทและหลายคลัง เพื่อควบคุมข้อมูลหลัก สินค้าคงคลัง เอกสารซื้อขาย การเงินและบัญชี รายงาน และการแจ้งเตือน โดยบริหารงานตามกระบวนการ Project Management และ Software Implementation ของ ISO/IEC 29110 Basic Profile.')
.h('2.','ขอบเขตและผลส่งมอบ').b('ซอฟต์แวร์ BRN WMS และฐานข้อมูลสำหรับ Authentication/Company และ WMS/Accounting').b('โมดูลผู้ใช้ บริษัท ผู้ติดต่อ สินค้า คลัง/ตำแหน่งจัดเก็บ Stock In/Out/Transfer Lot Serial Expiry และ Barcode').b('โมดูล Quotation, Order, Return, Purchase, Invoice, Receipt, Payment, General Ledger และรายงาน').b('Node.js Socket.IO สำหรับแจ้งเตือน และ Scheduler สำหรับ Stock/GL maintenance และ alerts').b('Work Products ISO/IEC 29110 จำนวน 22 รายการและ Project Repository ที่ควบคุมเวอร์ชัน')
.h('3.','แนวทางดำเนินโครงการ').tbl([['Phase','Period','Primary output'],['Initiation and planning','05-Jan–18-Feb-2026','SOW, Customer Requirements, Project Plan'],['Implementation','19-Feb–29-May-2026','Requirements, Design, Components, Tests, Software'],['Verification, validation and documentation','30-May–07-Aug-2026','Test/Verification/Validation results and guides'],['Stabilization and closure','03-Aug–14-Aug-2026','Corrections, demo data, acceptance and repository closure']],[2600,2500,3900])
.h('4.','ทีมงานและความรับผิดชอบ').tbl([['Role','Responsibility'],['Project Sponsor / Customer Representative / Authorized Approver — คุณเสรี วิริยะสกุลธรณ์','Represent customer needs and authorize scope, resources, changes and acceptance.'],['Project Manager — คุณอภิรัชต์ สุภัทรประทีป','Plan, monitor, communicate, control risks/issues and close the project.'],['Developer / System Analyst — ธนกร สถิตวิทยากุล','Analyze, design, implement, configure, correct and maintain technical records aligned with Git evidence.'],['Tester / Reviewer','Prepare and execute tests; verify work products and record results.'],['Document Control','Control identifiers, versions, approvals, repository and evidence.']],[3200,5800])
.h('5.','ทรัพยากรและสภาพแวดล้อม').b('PHP 8+, MySQL/MariaDB, Nginx or compatible web server, Node.js, npm and PM2').b('Git main branch as source baseline; configuration secrets excluded from version control').b('Two logical databases: identity/company data and WMS/accounting operational data').b('Development and deployment in Asia/Bangkok time zone with controlled configuration')
.h('6.','การบริหารความเสี่ยง').tbl([['Risk','Response / Control'],['Reconstructed pre-development evidence','Mark inferred records clearly and obtain retrospective review/approval.'],['Unauthorized access or tenant-data exposure','Role guards, company/warehouse scoping, session controls and security review.'],['Inventory inconsistency','Transactions, validation, locking, approval flow, stock summaries and reconciliation tests.'],['Configuration or secret exposure','Ignored local configuration, templates, deployment guide and restricted web access.'],['Insufficient acceptance evidence','Trace requirements to tests and obtain authorized signatures before baselining.'],['Scope expansion','Record Change Report, assess impact and authorize before implementation.']],[3600,5400])
.h('7.','คุณภาพ การทวนสอบ และการตรวจสอบความใช้ได้').b('Every approved requirement receives a unique ID and forward/backward traceability.').b('Reviews cover requirements, design, code/configuration, test records and operational documentation.').b('Critical failures prevent acceptance; other defects require disposition in the Correction Register.').b('Validation uses representative warehouse, order, finance and role-based workflows with controlled demo data.')
.h('8.','การควบคุมการเปลี่ยนแปลงและการกำหนดค่า').b('Configuration items include source code, database/setup scripts, Node.js services, controlled documents and releases.').b('Changes are requested, analyzed, approved, implemented, tested and closed with linked evidence.').b('Document filenames use project code, work-product name, Buddhist date, version and status/author indicator.')
.h('9.','การสื่อสารและการติดตาม').tbl([['Record','Frequency / trigger','Owner'],['Progress Status Record','Weekly during active work','Project Manager'],['Meeting Record','At project/review/decision meetings','Recorder'],['Correction Register','When an issue or defect is identified','Project Manager / Tester'],['Change Report','When baseline scope/configuration changes','Project Manager'],['Repository review','At baselines and closure','Document Control']],[3400,3300,2300])
.h('10.','เกณฑ์การส่งมอบและปิดโครงการ').b('Scope functions pass their linked test cases and no unresolved critical defect remains.').b('Installation, user, operation and maintenance information is available and reviewed.').b('Software and all required work products are stored in the controlled repository.').b('Verification, validation and acceptance results are reviewed and authorized.').p('Approval signature: ____________________________________','center',false,28,180,20).p('Name/position: _______________________________________','center').p('Date: ________________________________________________','center');
$reqs = [
['FR-001','Identity','Register/onboard company owner and invited users.','Must'],['FR-002','Identity','Authenticate users and apply role-based access for Owner, Admin, Staff and Viewer.','Must'],['FR-003','Identity','Support password recovery, session control and applicable OTP verification.','Must'],['FR-004','Administration','Maintain company profile, SMTP, system settings, users and application access.','Must'],
['FR-005','Master data','Maintain warehouses, storage areas/bins, product categories, products and contacts.','Must'],['FR-006','Master data','Support simple and layered warehouse-location models.','Should'],['FR-007','Inventory','Record stock-in with product, quantity, warehouse/bin and traceability attributes.','Must'],['FR-008','Inventory','Record stock-out with authorization and balance validation.','Must'],['FR-009','Inventory','Transfer stock between controlled warehouse locations.','Must'],['FR-010','Inventory','Track lot, serial number and expiry-date information where applicable.','Must'],['FR-011','Inventory','Display stock overview, movement history, capacity/occupancy and low-stock information.','Must'],['FR-012','Inventory','Generate SKU and location barcode labels and support scanning workflows.','Should'],
['FR-013','Sales','Create and manage quotations, sales orders, invoices, returns and credit notes.','Must'],['FR-014','Purchasing','Create and manage purchase requests, purchase orders, supplier invoices and supplier returns.','Must'],['FR-015','Finance','Create and manage receipt/payment billing, receipts and payments.','Must'],['FR-016','Accounting','Maintain chart of accounts, departments, formulas, journals and GL entries.','Must'],['FR-017','Accounting','Produce trial balance, profit/loss, balance sheet, VAT and GL movement reports.','Must'],['FR-018','Documents','Generate controlled document numbers and manage document lifecycle/status.','Must'],['FR-019','Documents','Attach permitted files to supported records.','Should'],['FR-020','Reports','Filter, view and export operational and management reports.','Must'],
['FR-021','Notifications','Notify authorized users of relevant status transitions and operational alerts.','Should'],['FR-022','Scheduler','Maintain stock/GL summaries and produce low-stock/overdue-invoice alerts.','Should'],['FR-023','Audit','Preserve creator/updater/status and transaction history required for review.','Must'],['FR-024','Isolation','Restrict company and warehouse data to the authorized user context.','Must'],
['NFR-001','Security','Protect secrets from source control and direct web access.','Must'],['NFR-002','Security','Validate input, authorize server-side actions and prevent cross-tenant access.','Must'],['NFR-003','Integrity','Execute related database updates transactionally and prevent invalid negative/duplicate movements.','Must'],['NFR-004','Availability','Provide documented installation, configuration, backup and recovery procedures.','Must'],['NFR-005','Usability','Provide responsive browser UI suitable for desktop and warehouse-floor devices.','Should'],['NFR-006','Performance','Provide practical response times for daily operations and use aggregate tables for dashboards/reports.','Should'],['NFR-007','Maintainability','Use modular managers/APIs, centralized helpers, configuration templates and version control.','Should'],['NFR-008','Compatibility','Run on PHP 8+, MySQL/MariaDB and current standards-based browsers.','Must'],['NFR-009','Traceability','Link each approved requirement to design, component and verification evidence.','Must'],['NFR-010','Time','Use Asia/Bangkok consistently for application and scheduled jobs.','Must'],
];
$cr = top('Customer Requirements').p().meta('Customer Requirements','เอกสารบันทึกและสรุปความต้องการของลูกค้า').h('1.','วัตถุประสงค์').p('เอกสารฉบับนี้กำหนดความต้องการระดับลูกค้าและผู้ใช้ของ BRN WMS เพื่อเป็นฐานสำหรับ Software Requirements Specification, Software Design, Traceability Record, Test Cases และ Acceptance. ข้อมูลถูกรวบรวมย้อนหลังจากขอบเขตที่ตกลง ซอร์สโค้ด ฐานข้อมูล และประวัติ Git โดยธนกร สถิตวิทยากุล ในบทบาท Developer / System Analyst และต้องได้รับการทบทวนและอนุมัติโดยคุณเสรี วิริยะสกุลธรณ์ ในบทบาท Project Sponsor / Customer Representative / Authorized Approver ก่อน Baseline.')
.h('2.','ผู้มีส่วนได้ส่วนเสีย').tbl([['Stakeholder','Interest / role'],['Authorized Sponsor','Approve scope, resources, changes and final acceptance.'],['Warehouse Manager / Staff','Accurate receiving, issuing, transfer, stock status, traceability and reports.'],['Sales and Purchasing Users','Controlled quotation/order/purchase/invoice/return workflows.'],['Finance and Accounting Users','Billing, receipts, payments, journals, GL and financial reports.'],['System Administrator','Company configuration, user access, deployment, monitoring and recovery.'],['Auditor / Management','Traceable transactions, controlled records and management information.']],[3400,5600])
.h('3.','สมมติฐานและข้อจำกัด').b('The solution is a browser-based application deployed on a controlled PHP/MySQL environment.').b('External ERP integration, legacy-data migration, hardware procurement and custom third-party services are outside scope unless changed formally.').b('Customer representatives provide representative data, review requirements and participate in validation and acceptance.').b('Requirements marked Must are acceptance-critical; Should requirements may be accepted with documented disposition.')
.h('4.','รายการความต้องการ');
$reqRows=[['ID','Area','Requirement','Priority']];foreach($reqs as $q)$reqRows[]=$q;$cr.=tbl($reqRows,[1300,1700,5100,900]).h('5.','เกณฑ์การยอมรับความต้องการ').b('Each Must requirement is implemented, traced to one or more test cases and passes verification/validation.').b('No critical security, tenant-isolation, inventory-integrity or transaction defect remains open.').b('Any exception or deferred Should requirement is recorded and authorized.').b('Customer review confirms that representative end-to-end workflows support intended warehouse operations.').h('6.','การอนุมัติ Baseline').p('Customer representative signature: ______________________________','center',false,28,180,20).p('Name/position: ______________________________________________','center').p('Date: ________________________________________________________','center');
saveDoc($base.'2-Software Project Plan/200-WMS-26-001-00 Software Project Plan 25690105 V1.0 Final.docx','200-WMS-26-001-00 Software Project Plan',$plan,'PP');
saveDoc($base.'3-Customer Requirement/200-WMS-26-001-00 Customer Requirements 25690112 V1.0 Final.docx','200-WMS-26-001-00 Customer Requirements',$cr,'CR');
echo "Project Plan documents generated.\n";
require __DIR__ . '/normalize_document_dates.php';
+192
View File
@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
/**
* 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';
+148
View File
@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
$output = __DIR__ . '/../sdlc/1-PM Process (10 Work Product)/2.Project Plan/1-Work Schedule/'
. '200-WMS-26-001-00 Work Schedule 25690105 V1.0 Final.xlsx';
$headers = ['No.', 'Phase', 'Task', 'Details / Basis', 'Responsible Role', 'Start', 'Finish', 'Duration (days)', 'Deliverable / Evidence', 'Status', 'Remarks'];
$rows = [
['1.1', 'Initiation', 'Identify project need', 'Establish need for centralized warehouse, inventory, order, and accounting control.', 'Project Sponsor / Project Manager', '05-Jan-2026', '09-Jan-2026', '5', 'Statement of Work', 'Completed', 'Reconstructed planning activity'],
['1.2', 'Initiation', 'Identify stakeholders and objectives', 'Identify sponsor, operational users, system administrator, development, and approval roles.', 'Project Manager', '05-Jan-2026', '16-Jan-2026', '10', 'Stakeholder and objective records', 'Completed', 'Reconstructed planning activity'],
['1.3', 'Initiation', 'Approve project scope', 'Confirm project boundaries, assumptions, deliverables, and acceptance approach.', 'Project Sponsor', '19-Jan-2026', '23-Jan-2026', '5', 'Approved Statement of Work', 'Pending signature', 'Authority signature required'],
['2.1', 'Planning', 'Collect customer requirements', 'Document functional, data, security, operational, and quality requirements.', 'System Analyst / Customer Representatives', '12-Jan-2026', '06-Feb-2026', '20', 'Customer Requirements', 'Completed', 'Reconstructed from implemented system'],
['2.2', 'Planning', 'Prepare Software Project Plan', 'Define lifecycle, resources, risks, repository, configuration, communication, and controls.', 'Project Manager', '26-Jan-2026', '13-Feb-2026', '15', 'Software Project Plan', 'Completed', 'Reconstructed project plan'],
['2.3', 'Planning', 'Baseline requirements and schedule', 'Review initial requirements, priorities, milestones, and work-product responsibilities.', 'Project Manager / System Analyst', '16-Feb-2026', '18-Feb-2026', '3', 'Baseline plan and requirements', 'Completed', 'Development begins 19-Feb-2026'],
['3.1', 'Execution', 'Initialize WMS application', 'Create initial PHP application repository and baseline structure.', 'System Analyst / Developer', '19-Feb-2026', '25-Feb-2026', '5', 'Git commits 9a50080–1843308', 'Completed', 'Git evidenced'],
['3.2', 'Execution', 'Security and database foundation', 'Configuration protection, security audit actions, stock database design.', 'Developer', '09-Mar-2026', '17-Mar-2026', '7', 'Git commits 93d903c–a4f474b', 'Completed', 'Git evidenced'],
['3.3', 'Execution', 'Inventory and warehouse modules', 'Warehouse capacity, product, storage/bin, lot, serial, expiry, stock movements, and reports.', 'Developer', '09-Apr-2026', '29-Apr-2026', '15', 'Inventory, ICS, dashboard and report modules', 'Completed', 'Git evidenced'],
['3.4', 'Execution', 'Authentication and onboarding', 'Login, registration, onboarding, password controls, and role-based access.', 'Developer', '28-Apr-2026', '12-May-2026', '11', 'Authentication and user-management modules', 'Completed', 'Git evidenced'],
['3.5', 'Execution', 'Order and barcode workflows', 'Orders, returns, invoices, switchable warehouse layers, barcode labels, and scanning.', 'Developer', '02-May-2026', '08-May-2026', '5', 'Order and barcode modules', 'Completed', 'Git evidenced'],
['3.6', 'Execution', 'Production preparation and setup', 'Dynamic base URL, automated database setup, recovery flow, and production preparation.', 'Developer', '11-May-2026', '13-May-2026', '3', 'Setup and configuration implementation', 'Completed', 'Git evidenced'],
['3.7', 'Execution', 'Accounting and finance workflows', 'Chart of accounts, GL, journals, reports, billing, payment and receipt workflows.', 'Developer', '13-May-2026', '23-May-2026', '9', 'Accounting and finance modules', 'Completed', 'Git evidenced'],
['3.8', 'Execution', 'Real-time services and scheduled jobs', 'Socket notifications, stock/GL aggregates, low-stock and overdue-invoice schedules.', 'Developer', '22-May-2026', '27-May-2026', '4', 'Node.js service and cron jobs', 'Completed', 'Git evidenced'],
['3.9', 'Execution', 'Security hardening and lifecycle review', 'Role guards, tenant scoping, document flows, transaction limits, and correction closure.', 'Developer / Reviewer', '21-May-2026', '28-May-2026', '6', 'Security and lifecycle review commits', 'Completed', 'Git evidenced'],
['3.10', 'Execution', 'Refactor and development baseline', 'Remove redundancy and establish substantially complete development baseline.', 'Developer', '29-May-2026', '29-May-2026', '1', 'Git commit a0677d6', 'Completed', 'Development substantially complete'],
['4.1', 'Control', 'Maintain progress and meeting records', 'Track progress, issues, decisions, risks, and corrective actions throughout the project.', 'Project Manager / Document Control', '05-Jan-2026', '14-Aug-2026', '160', 'Progress Status, Meeting Records, Correction Register', 'Completed', 'Records require evidence review'],
['4.2', 'Control', 'Configuration and repository control', 'Control source, baselines, document versions, configuration, and backups.', 'Configuration Manager', '19-Feb-2026', '14-Aug-2026', '127', 'Git repository and Software Configuration', 'Completed', 'Git repository evidenced'],
['4.3', 'Verification', 'Verify requirements, design, and implementation', 'Conduct reviews and tests; link requirements, design, code, and test evidence.', 'Reviewer / Tester', '30-May-2026', '31-Jul-2026', '45', 'Verification Results and Traceability Record', 'Completed', 'Formal approval pending'],
['4.4', 'Validation', 'Customer-oriented system validation', 'Validate operational workflows and quality attributes against intended use.', 'Customer Representatives / Tester', '01-Jun-2026', '07-Aug-2026', '50', 'Validation Results and Test Report', 'Completed', 'Formal approval pending'],
['4.5', 'Documentation', 'Prepare operational documentation', 'Prepare user, operation, configuration, deployment, and maintenance guidance.', 'Developer / Document Control', '01-Jun-2026', '07-Aug-2026', '50', 'User Documentation, Operation Guide, Maintenance Documentation', 'Completed', 'Reconstructed documentation period'],
['4.6', 'Stabilization', 'Configuration and login corrections', 'Correct login and environment configuration issues identified after baseline.', 'Developer', '03-Aug-2026', '03-Aug-2026', '1', 'Git commit b2c4374', 'Completed', 'Git evidenced'],
['4.7', 'Stabilization', 'Prepare demonstration data', 'Populate controlled demonstration data for validation and handover support.', 'Developer / Tester', '14-Aug-2026', '14-Aug-2026', '1', 'Git commit dd48a8b', 'Completed', 'Git evidenced'],
['5.1', 'Closure', 'Final repository and work-product review', 'Confirm required work products, traceability, configuration items, and unresolved actions.', 'Project Manager / Document Control', '10-Aug-2026', '14-Aug-2026', '5', 'Repository review and List of Evidence', 'Completed', 'Approval records pending'],
['5.2', 'Closure', 'Acceptance and project closure', 'Obtain authorized acceptance and record project closure and follow-up actions.', 'Project Sponsor / Project Manager', '14-Aug-2026', '14-Aug-2026', '1', 'Acceptance Report and closure record', 'Pending signature', 'Authority signature required'],
];
function esc(string $value): string
{
return htmlspecialchars($value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
}
function colName(int $number): string
{
$name = '';
while ($number > 0) {
$number--;
$name = chr(65 + ($number % 26)) . $name;
$number = intdiv($number, 26);
}
return $name;
}
function cell(string $reference, string $value, int $style): string
{
return '<c r="' . $reference . '" t="inlineStr" s="' . $style . '"><is><t xml:space="preserve">' . esc($value) . '</t></is></c>';
}
$sheetRows = [];
$sheetRows[] = '<row r="1" ht="30" customHeight="1">'
. cell('A1', 'Document No: Work Schedule', 1)
. cell('B1', 'Project: BRN WMS', 1)
. cell('C1', 'Project Code: 200-WMS-26-001-00', 1)
. cell('D1', 'Period: 05-Jan-2026 to 14-Aug-2026', 1)
. cell('E1', 'Release: 05/01/26 V1.0 Final', 1)
. '</row>';
$sheetRows[] = '<row r="2" ht="26" customHeight="1">';
foreach ($headers as $index => $header) {
$sheetRows[1] .= cell(colName($index + 1) . '2', $header, 2);
}
$sheetRows[1] .= '</row>';
foreach ($rows as $rowIndex => $row) {
$excelRow = $rowIndex + 3;
$xml = '<row r="' . $excelRow . '" ht="42" customHeight="1">';
foreach ($row as $columnIndex => $value) {
$style = $columnIndex === 9
? ($value === 'Completed' ? 4 : 5)
: ($columnIndex === 0 || $columnIndex >= 5 && $columnIndex <= 7 ? 3 : 6);
$xml .= cell(colName($columnIndex + 1) . $excelRow, $value, $style);
}
$sheetRows[] = $xml . '</row>';
}
$lastRow = count($rows) + 2;
$sheet = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
. '<sheetPr><pageSetUpPr fitToPage="1"/></sheetPr><dimension ref="A1:K' . $lastRow . '"/>'
. '<sheetViews><sheetView workbookViewId="0"><pane ySplit="2" topLeftCell="A3" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews>'
. '<cols><col min="1" max="1" width="7" customWidth="1"/><col min="2" max="2" width="14" customWidth="1"/>'
. '<col min="3" max="3" width="29" customWidth="1"/><col min="4" max="4" width="48" customWidth="1"/>'
. '<col min="5" max="5" width="28" customWidth="1"/><col min="6" max="7" width="14" customWidth="1"/>'
. '<col min="8" max="8" width="14" customWidth="1"/><col min="9" max="9" width="34" customWidth="1"/>'
. '<col min="10" max="10" width="18" customWidth="1"/><col min="11" max="11" width="28" customWidth="1"/></cols>'
. '<sheetData>' . implode('', $sheetRows) . '</sheetData>'
. '<autoFilter ref="A2:K' . $lastRow . '"/><mergeCells count="5"><mergeCell ref="A1:A1"/><mergeCell ref="B1:B1"/><mergeCell ref="C1:C1"/><mergeCell ref="D1:D1"/><mergeCell ref="E1:K1"/></mergeCells>'
. '<pageMargins left="0.25" right="0.25" top="0.4" bottom="0.4" header="0.2" footer="0.2"/>'
. '<pageSetup orientation="landscape" paperSize="9" fitToWidth="1" fitToHeight="0"/>'
. '<printOptions horizontalCentered="1"/><headerFooter><oddFooter>&amp;LISO/IEC 29110&amp;C200-WMS-26-001-00&amp;RPage &amp;P of &amp;N</oddFooter></headerFooter>'
. '</worksheet>';
$styles = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
. '<fonts count="4"><font><sz val="10"/><name val="Arial"/></font><font><b/><color rgb="FFFFFFFF"/><sz val="11"/><name val="Arial"/></font>'
. '<font><b/><color rgb="FFFFFFFF"/><sz val="10"/><name val="Arial"/></font><font><sz val="9"/><name val="Arial"/></font></fonts>'
. '<fills count="5"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill>'
. '<fill><patternFill patternType="solid"><fgColor rgb="FF075985"/></patternFill></fill>'
. '<fill><patternFill patternType="solid"><fgColor rgb="FF0E7490"/></patternFill></fill>'
. '<fill><patternFill patternType="solid"><fgColor rgb="FFFEF3C7"/></patternFill></fill></fills>'
. '<borders count="2"><border/><border><left style="thin"><color rgb="FFB7C9D6"/></left><right style="thin"><color rgb="FFB7C9D6"/></right><top style="thin"><color rgb="FFB7C9D6"/></top><bottom style="thin"><color rgb="FFB7C9D6"/></bottom></border></borders>'
. '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>'
. '<cellXfs count="7"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>'
. '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" xfId="0" applyAlignment="1"><alignment vertical="center" wrapText="1"/></xf>'
. '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" xfId="0" applyAlignment="1"><alignment horizontal="center" vertical="center" wrapText="1"/></xf>'
. '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" xfId="0" applyAlignment="1"><alignment horizontal="center" vertical="top" wrapText="1"/></xf>'
. '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" xfId="0" applyAlignment="1"><alignment horizontal="center" vertical="top" wrapText="1"/></xf>'
. '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" xfId="0" applyAlignment="1"><alignment horizontal="center" vertical="top" wrapText="1"/></xf>'
. '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" xfId="0" applyAlignment="1"><alignment vertical="top" wrapText="1"/></xf></cellXfs>'
. '<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>';
$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="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
. '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
. '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+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>';
$rootRels = '<?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="xl/workbook.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>';
$workbook = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
. '<sheets><sheet name="Work Schedule" sheetId="1" r:id="rId1"/></sheets><definedNames><definedName name="_xlnm.Print_Area" localSheetId="0">\'Work Schedule\'!$A$1:$K$' . $lastRow . '</definedName></definedNames></workbook>';
$workbookRels = '<?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/worksheet" Target="worksheets/sheet1.xml"/>'
. '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>';
$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 Work Schedule</dc:title><dc:creator>BRN WMS Project Team</dc:creator><dc:description>ISO/IEC 29110 reconstructed final project schedule ready for authorization.</dc:description><dcterms:created xsi:type="dcterms:W3CDTF">2026-01-05T00:00:00+07:00</dcterms:created></cp:coreProperties>';
$app = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>Microsoft Excel</Application><Company>B.R.N. Enterprise Co., Ltd.</Company></Properties>';
$zip = new ZipArchive();
if ($zip->open($output, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException('Cannot create ' . $output);
}
foreach (['[Content_Types].xml' => $contentTypes, '_rels/.rels' => $rootRels, 'xl/workbook.xml' => $workbook, 'xl/_rels/workbook.xml.rels' => $workbookRels, 'xl/worksheets/sheet1.xml' => $sheet, 'xl/styles.xml' => $styles, 'docProps/core.xml' => $core, 'docProps/app.xml' => $app] as $name => $contents) {
$zip->addFromString($name, $contents);
}
$zip->close();
echo $output . PHP_EOL;
require __DIR__ . '/normalize_document_dates.php';
+99
View File
@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
$root = __DIR__ . '/../sdlc';
$english = [
'January'=>'01','February'=>'02','March'=>'03','April'=>'04','May'=>'05','June'=>'06',
'July'=>'07','August'=>'08','September'=>'09','October'=>'10','November'=>'11','December'=>'12',
'Jan'=>'01','Feb'=>'02','Mar'=>'03','Apr'=>'04','Jun'=>'06','Jul'=>'07','Aug'=>'08','Sep'=>'09','Oct'=>'10','Nov'=>'11','Dec'=>'12',
];
$thai = ['มกราคม'=>'01','กุมภาพันธ์'=>'02','มีนาคม'=>'03','เมษายน'=>'04','พฤษภาคม'=>'05','มิถุนายน'=>'06','กรกฎาคม'=>'07','สิงหาคม'=>'08','กันยายน'=>'09','ตุลาคม'=>'10','พฤศจิกายน'=>'11','ธันวาคม'=>'12'];
function normalDate(int|string $day, string $month, int|string $year, array $english, array $thai): string
{
$monthNo = $english[$month] ?? $thai[$month] ?? null;
if ($monthNo === null) return (string)$day . ' ' . $month . ' ' . (string)$year;
$yearNo = (int)$year;
if ($yearNo >= 2400) $yearNo -= 543;
return sprintf('%02d/%s/%02d', (int)$day, $monthNo, $yearNo % 100);
}
function normalizeDates(string $text, array $english, array $thai): string
{
$months = implode('|', array_map(static fn($v) => preg_quote($v, '/'), array_keys($english + $thai)));
// Compact Buddhist document/report dates, e.g. 25690817.
$text = preg_replace_callback('/\b(25\d{2})(\d{2})(\d{2})\b/', static function ($m) {
return sprintf('%02d/%02d/%02d', (int)$m[3], (int)$m[2], ((int)$m[1] - 543) % 100);
}, $text) ?? $text;
// Shared-month range: 11–13 May 2026.
$text = preg_replace_callback('/\b(\d{1,2})\s*[–-]\s*(\d{1,2})\s+('.$months.')\s+(20\d{2}|25\d{2})\b/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[3],$m[4],$english,$thai).'–'.normalDate($m[2],$m[3],$m[4],$english,$thai);
}, $text) ?? $text;
// Two-month range with a shared year: 5 January–14 August 2026.
$text = preg_replace_callback('/\b(\d{1,2})\s+('.$months.')\s*[–-]\s*(\d{1,2})\s+('.$months.')\s+(20\d{2}|25\d{2})\b/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[2],$m[5],$english,$thai).'–'.normalDate($m[3],$m[4],$m[5],$english,$thai);
}, $text) ?? $text;
// Hyphenated two-month range: 19-Feb–29-May-2026.
$text = preg_replace_callback('/\b(\d{1,2})-('.$months.')\s*[–-]\s*(\d{1,2})-('.$months.')-(20\d{2}|25\d{2})\b/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[2],$m[5],$english,$thai).'–'.normalDate($m[3],$m[4],$m[5],$english,$thai);
}, $text) ?? $text;
// A full single date using a month name or abbreviation.
$text = preg_replace_callback('/\b(\d{1,2})(?:-|\s+)('.$months.')(?:-|\s+)(20\d{2}|25\d{2})\b/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[2],$m[3],$english,$thai);
}, $text) ?? $text;
// Period descriptions without a repeated year, all known to be in 2026.
$text = preg_replace_callback('/\b(\d{1,2})\s*[–-]\s*(\d{1,2})\s+('.$months.')(?=\s+(?:commits|accounting|May|April|January|February|March|June|July|August))/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[3],2026,$english,$thai).'–'.normalDate($m[2],$m[3],2026,$english,$thai);
}, $text) ?? $text;
// Commit-period shorthand: 30-Apr through 08-May.
$text = preg_replace_callback('/\b(\d{1,2})-('.$months.')\s+through\s+(\d{1,2})-('.$months.')\b/u', static function ($m) use ($english,$thai) {
return normalDate($m[1],$m[2],2026,$english,$thai).'–'.normalDate($m[3],$m[4],2026,$english,$thai);
}, $text) ?? $text;
return $text;
}
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS));
$updated = 0;
foreach ($iterator as $file) {
if (!$file->isFile() || !str_starts_with($file->getFilename(), '200-WMS-')) continue;
$path = $file->getPathname();
$extension = strtolower($file->getExtension());
if ($extension === 'md') {
$before = file_get_contents($path);
if ($before === false) continue;
$after = normalizeDates($before, $english, $thai);
if ($after !== $before) {
file_put_contents($path, $after);
$updated++;
}
continue;
}
if (!in_array($extension, ['docx','xlsx'], true)) continue;
$zip = new ZipArchive();
if ($zip->open($path) !== true) continue;
$changed = false;
for ($i=0; $i<$zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
if (!str_ends_with($name, '.xml')) continue;
$before = $zip->getFromIndex($i);
$after = normalizeDates($before, $english, $thai);
if ($after === $before) continue;
$dom = new DOMDocument();
if (!$dom->loadXML($after)) throw new RuntimeException("Invalid XML after date conversion: {$path}::{$name}");
$zip->addFromString($name, $after);
$changed = true;
}
$zip->close();
if ($changed) $updated++;
}
echo "Updated {$updated} BRN WMS document(s).\n";
+1 -1
View File
@@ -56,7 +56,7 @@ function run(PDO $pdo, string $sql, string $label): void {
}
// ── Connect (no DB selected) ──────────────────────────────────────────────────
echo "\n\033[1m=== mn3wms Production Setup ===\033[0m\n\n";
echo "\n\033[1m=== brnwms Production Setup ===\033[0m\n\n";
echo "Host : {$host}\n";
echo "DB1 : {$db1} (auth / identity)\n";
echo "DB2 : {$db2} (business data)\n\n";