Compare commits
70
Commits
main
..
501c70050f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
501c70050f | ||
|
|
f6909b08eb | ||
|
|
5846c8b282 | ||
|
|
6a271c1f7d | ||
|
|
2ef2f32107 | ||
|
|
1f72633cb6 | ||
|
|
4efab9f7c9 | ||
|
|
44c66c49a5 | ||
|
|
6b3a590aa9 | ||
|
|
21148bf50c | ||
|
|
cf8106771b | ||
|
|
d7203583b7 | ||
|
|
9afcf072b0 | ||
|
|
2f290ddb26 | ||
|
|
5d021d7683 | ||
|
|
5ee0c8d41b | ||
|
|
c3113bc70d | ||
|
|
6765054950 | ||
|
|
075d80ad40 | ||
|
|
a859cfda3e | ||
|
|
37d2f8e725 | ||
|
|
39201df36e | ||
|
|
3045c4a8ef | ||
|
|
4c4522169c | ||
|
|
234814a23c | ||
|
|
0eab2a3b96 | ||
|
|
618045540a | ||
|
|
7b294f70da | ||
|
|
356d308907 | ||
|
|
9c7a4a139d | ||
|
|
b634372b22 | ||
|
|
2a6441c6a9 | ||
|
|
987cf7ded9 | ||
|
|
ea94b6a6ae | ||
|
|
dbbc89f8f2 | ||
|
|
82f66b0c41 | ||
|
|
8905b5bf35 | ||
|
|
8916d9180d | ||
|
|
ebccca4989 | ||
|
|
aee797b998 | ||
|
|
5ff1a60c7d | ||
|
|
5bfaf6f8c3 | ||
|
|
17a62e50e4 | ||
|
|
1b34482216 | ||
|
|
1c5236d8e0 | ||
|
|
6ca4c91865 | ||
|
|
632c039790 | ||
|
|
1a952b42dd | ||
|
|
9e200d31fe | ||
|
|
0815ae3292 | ||
|
|
5c0166ae73 | ||
|
|
3c9475f3fa | ||
|
|
8027ab569e | ||
|
|
1237888ab9 | ||
|
|
45a78a3fed | ||
|
|
5ca6b49fd0 | ||
|
|
54f3f11fd2 | ||
|
|
5affae1fc5 | ||
|
|
4bb378a905 | ||
|
|
15618f4955 | ||
|
|
c0de84a575 | ||
|
|
5780183b82 | ||
|
|
5a3bfa3435 | ||
|
|
648efee991 | ||
|
|
738f600fe8 | ||
|
|
7379ac9e4a | ||
|
|
cf25732da0 | ||
|
|
f2b87cfd0f | ||
|
|
86b1aa9d62 | ||
|
|
e1135d2bce |
@@ -13,5 +13,11 @@ EMIT_SECRET=
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
|
||||
# Email OTP on sign-in. Off by default; only the exact value "true" turns it on,
|
||||
# and that needs working SMTP. While off, sign-in is password only (logged as
|
||||
# OTP_BYPASSED, shown on the login page and top bar).
|
||||
# Applied to app/config.php by the php container on every start.
|
||||
OTP_REQUIRED=false
|
||||
|
||||
# Port to expose the web app on (default 80)
|
||||
HTTP_PORT=80
|
||||
|
||||
+3
-2
@@ -6,6 +6,9 @@ app/uploads
|
||||
node_modules/
|
||||
nodejs/.env
|
||||
|
||||
# PM2 runtime logs (written by the node container; nodejs/logs/.gitkeep keeps the folder)
|
||||
nodejs/logs/*.log
|
||||
|
||||
# Docker deploy secrets
|
||||
/.env
|
||||
|
||||
@@ -15,6 +18,4 @@ lib/zxcvbn-php-master/vendor/sebastian/
|
||||
# custom files
|
||||
notes/
|
||||
docs/
|
||||
.claude/
|
||||
SESSION.php
|
||||
sdlc/
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// app/assets/utils/app_registry.php
|
||||
//
|
||||
// The apps a user can be given access to (user.app_access and
|
||||
// company_map_user.app_access), with the label, icon and badge colour the
|
||||
// Users Access page shows for each.
|
||||
//
|
||||
// config.php may define its own $app_registry; this file only fills it in when
|
||||
// it is missing or empty — which is every Docker-generated config.php written
|
||||
// before the setting was documented. Without it the Add User dialog breaks
|
||||
// (Object.entries(null) in setting/users.php) and inviting a user fails
|
||||
// (array_keys(null) in setting/api/engine/manage_users.php).
|
||||
//
|
||||
// Keys must stay within the user.app_access enum: 'wms' and 'accounting'
|
||||
// ('all' is implied and never listed here).
|
||||
|
||||
if (!isset($app_registry) || !is_array($app_registry) || !$app_registry) {
|
||||
$app_registry = [
|
||||
'wms' => ['label' => 'WMS', 'icon' => 'ti-box', 'color' => 'bg-label-primary'],
|
||||
'accounting' => ['label' => 'Accounting', 'icon' => 'ti-calculator', 'color' => 'bg-label-success'],
|
||||
];
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class CompanyProfileManager
|
||||
|
||||
public function saveProfile(array $data, string $company_logo, string $company_seal): void
|
||||
{
|
||||
$channel = strtolower(preg_replace('/[^a-z0-9\-_]/', '', $data['channel_name'] ?? ''));
|
||||
$channel = preg_replace('/[^a-z0-9\-_]/', '', strtolower(trim($data['channel_name'] ?? '')));
|
||||
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE company_list SET
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// app/assets/utils/otp_policy.php
|
||||
//
|
||||
// Email OTP login policy, set by OTP_REQUIRED in config.php.
|
||||
//
|
||||
// OFF BY DEFAULT: the OTP step runs only when the constant is defined and is
|
||||
// exactly the boolean true. A missing constant (any config.php written before
|
||||
// this switch existed), 1, 'true' or a typo all leave it off, so sign-in is
|
||||
// password only and no SMTP is needed to log in.
|
||||
//
|
||||
// While it is off, every sign-in that skips the OTP because of it is logged as
|
||||
// OTP_BYPASSED, and the login page and top bar both say so on screen — a
|
||||
// password-only sign-in must never be invisible to whoever is using it.
|
||||
//
|
||||
// Only the login OTP is affected. When it is on, the staff/viewer and no-SMTP
|
||||
// skips in login_otp.php still apply; password-reset OTPs (PasswordResetManager)
|
||||
// are a separate flow that stays on regardless.
|
||||
|
||||
if (!function_exists('otp_required')) {
|
||||
function otp_required(): bool {
|
||||
return defined('OTP_REQUIRED') && OTP_REQUIRED === true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('otp_log_bypass')) {
|
||||
// There is no auth log table in this app, so bypasses go to the PHP error
|
||||
// log (the container's Apache log) under a fixed, greppable tag.
|
||||
function otp_log_bypass($user_id, string $where): void {
|
||||
error_log(sprintf(
|
||||
'[auth] OTP_BYPASSED user_id=%d ip=%s where=%s -- OTP_REQUIRED is not true in config.php',
|
||||
(int)$user_id,
|
||||
$_SERVER['REMOTE_ADDR'] ?? '-',
|
||||
$where
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,24 @@ if (!defined('NODE_EMIT_SECRET')) {
|
||||
define('NODE_EMIT_SECRET', 'YOUR_NODE_EMIT_SECRET'); // must match nodejs/.env EMIT_SECRET
|
||||
}
|
||||
|
||||
// ── Login OTP ────────────────────────────────────────────────────────────────
|
||||
// Email OTP on sign-in. OFF BY DEFAULT: only the boolean true turns it on —
|
||||
// anything else, the constant being absent included, leaves sign-in password
|
||||
// only (logged as OTP_BYPASSED, shown on the login page and top bar). Turn it
|
||||
// on only with working SMTP. Password-reset OTPs are not affected.
|
||||
if (!defined('OTP_REQUIRED')) {
|
||||
define('OTP_REQUIRED', false);
|
||||
}
|
||||
|
||||
// ── App registry ─────────────────────────────────────────────────────────────
|
||||
// Apps a user can be given access to, as shown on Setting → Users Access. Keys
|
||||
// must match the user.app_access enum ('wms', 'accounting'). If this is left
|
||||
// out, assets/utils/app_registry.php supplies the same default.
|
||||
$app_registry = [
|
||||
'wms' => ['label' => 'WMS', 'icon' => 'ti-box', 'color' => 'bg-label-primary'],
|
||||
'accounting' => ['label' => 'Accounting', 'icon' => 'ti-calculator', 'color' => 'bg-label-success'],
|
||||
];
|
||||
|
||||
// ── Usage packages ───────────────────────────────────────────────────────────
|
||||
// Keyed by company_list.package (defaults to 'starter'). Read by UsageGuard to
|
||||
// enforce daily/weekly action limits and which features lock once exceeded.
|
||||
|
||||
@@ -27,6 +27,11 @@ class db_statement extends PDOStatement {
|
||||
$this->pdo = $pdo;
|
||||
}
|
||||
|
||||
// PDOStatement::execute() is declared ?array $params = null : bool. This
|
||||
// override deliberately accepts a looser signature so callers may pass
|
||||
// positional arguments (see func_get_args() below), so the tightened return
|
||||
// type is opted out of rather than the call sites being changed.
|
||||
#[\ReturnTypeWillChange]
|
||||
public function execute($args = null) {
|
||||
// Perform logging here. PDO object is accessible
|
||||
// from $this->pdo.
|
||||
|
||||
@@ -1,4 +1,23 @@
|
||||
<?php
|
||||
// Output buffering must be active before the first byte of HTML below, so that
|
||||
// header() calls made later in the page still work — notably the
|
||||
// not-logged-in redirect in include_topbar.php, which runs *after* this file
|
||||
// has already emitted <!DOCTYPE html>. Without a buffer that redirect depends
|
||||
// entirely on php.ini's output_buffering: it is on for the dev stack but off
|
||||
// in production, where every protected page answered 200 with a half-rendered
|
||||
// body instead of sending the browser to the login form. session.php starts a
|
||||
// buffer for the same reason.
|
||||
if (ob_get_level() === 0) {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
// Never render PHP notices/warnings into the page: they leak absolute server
|
||||
// paths to anonymous visitors and corrupt the markup. Errors still reach the
|
||||
// server log. This mirrors the policy db_auth.php already applies to the JSON
|
||||
// API routes, and keeps the app safe even where php.ini has display_errors on.
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('log_errors', '1');
|
||||
|
||||
// Security headers — emitted before any HTML output.
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: SAMEORIGIN');
|
||||
|
||||
@@ -3,11 +3,17 @@
|
||||
require_once __DIR__ . '/config.php';
|
||||
require_once __DIR__ . '/dbconn.php';
|
||||
require_once __DIR__ . '/assets/utils/classes/UsageGuard.php';
|
||||
require_once __DIR__ . '/assets/utils/otp_policy.php';
|
||||
|
||||
// Redirect to login if the user has not completed full authentication.
|
||||
// login_company_id is only written by login_confirm.php after OTP is verified —
|
||||
// using it (not "otp") ensures half-logged-in sessions are also redirected.
|
||||
if(empty($_SESSION["login_company_id"])){
|
||||
// Discard the markup include_header.php has already buffered so the browser
|
||||
// receives a clean redirect rather than a partially rendered page body.
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
header('Location: '.$server_url.'login/index.php');
|
||||
exit;
|
||||
}
|
||||
@@ -193,6 +199,18 @@ $_usage_full = $_usage_max_pct >= 100;
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Email OTP off (the default): a password-only sign-in must never be invisible to whoever is using it -->
|
||||
<?php if (!otp_required()): ?>
|
||||
<li class="d-none d-md-block">
|
||||
<span class="badge bg-warning text-dark d-flex align-items-center gap-1 px-2 py-1"
|
||||
style="font-size:11px; cursor:default;"
|
||||
title="OTP_REQUIRED is not true in config.php">
|
||||
<i class="ti ti-shield-off"></i>
|
||||
OTP off
|
||||
</span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Usage limit warning -->
|
||||
<?php if ($_usage_full || $_usage_warn): ?>
|
||||
<li>
|
||||
|
||||
@@ -58,6 +58,7 @@ require_once '../../../config.php';
|
||||
require_once '../../../preset.php';
|
||||
define('UNAUTHENTICATED_ROUTE', true);
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/otp_policy.php';
|
||||
|
||||
// ── Step 1: Load session state written by login_otp.php ───────────────────────
|
||||
$data["username"] = $_SESSION["login_data"]['username'];
|
||||
@@ -100,9 +101,15 @@ $_SESSION["diff"] = $otp_diff_minutes;
|
||||
|
||||
// ── Step 4: Validate OTP value and expiry ─────────────────────────────────────
|
||||
// Skipped for staff/viewer roles — login_otp.php sets skip_otp=true in session
|
||||
// so they never receive or enter an OTP. Admin/owner always go through this check.
|
||||
// so they never receive or enter an OTP. Admin/owner always go through this check,
|
||||
// unless OTP_REQUIRED=false in config.php: that also covers a user who was already
|
||||
// on the OTP screen when the switch was turned off.
|
||||
if (empty($_SESSION['skip_otp'])) {
|
||||
if ($data["otp"] != $otp || $otp_diff_minutes > 5) {
|
||||
if (!otp_required()) {
|
||||
if (!empty($user_id)) {
|
||||
otp_log_bypass($user_id, 'login_confirm');
|
||||
}
|
||||
} elseif ($data["otp"] != $otp || $otp_diff_minutes > 5) {
|
||||
$answer["message"] = "Wrong OTP! Please try again. (Our OTP is valid for 5 minute)";
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ require_once '../../../config.php';
|
||||
require_once '../../../preset.php';
|
||||
define('UNAUTHENTICATED_ROUTE', true);
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/otp_policy.php';
|
||||
|
||||
// ── Step 1: Resolve user_id from username or email (case-insensitive) ────────
|
||||
$sth = $pdo1->prepare("select user_id from user where ? in (username,email) ");
|
||||
@@ -253,11 +254,15 @@ if (password_verify(trim($data["password"]), $temp["password"])) {
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
// ── Step 5g: Role check — staff/viewer skip OTP entirely ─────────────────
|
||||
// Owners always require 2FA. Invited users (license='user') require 2FA only
|
||||
// ── Step 5g: OTP policy, then role check — staff/viewer skip OTP entirely ─
|
||||
// OTP_REQUIRED=false in config.php turns the email OTP off for everyone and
|
||||
// logs the sign-in as a bypass (see assets/utils/otp_policy.php).
|
||||
// Otherwise owners always require 2FA. Invited users (license='user') require 2FA only
|
||||
// if their role in this company is admin or owner; staff/viewer go straight in.
|
||||
$requires_otp = true;
|
||||
if (($r['license'] ?? 'owner') !== 'owner') {
|
||||
$requires_otp = otp_required();
|
||||
if (!$requires_otp) {
|
||||
otp_log_bypass($user_id, 'login_otp');
|
||||
} elseif (($r['license'] ?? 'owner') !== 'owner') {
|
||||
$sth_role = $pdo1->prepare(
|
||||
"SELECT role FROM company_map_user WHERE company_id = :cid AND user_id = :uid LIMIT 1"
|
||||
);
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
* 2. CSRF check — rejects requests missing a valid X-CSRF-Token header.
|
||||
* 3. Decode and sanitise input fields.
|
||||
* 4. Required field validation — company_name and channel_name must be non-empty.
|
||||
* 5. Required SMTP validation — smtp_host, smtp_username, smtp_password
|
||||
* must all be provided (company SMTP is mandatory for WMS email delivery).
|
||||
* 5. SMTP validation — smtp_host, smtp_username, smtp_password must all be
|
||||
* provided while email OTP is on (company SMTP delivers the OTP). With
|
||||
* OTP_REQUIRED=false they are optional but all-or-nothing: left blank,
|
||||
* steps 6-8 and 13 are skipped and the company is created without SMTP.
|
||||
* 6. Normalise smtp_port to one of ['25', '465', '587'] (default: 587).
|
||||
* Normalise smtp_encryption to one of ['tls', 'ssl', 'none'] (default: tls).
|
||||
* 7. Encrypt SMTP password with OpenSSL (same method/iv/key as rest of app).
|
||||
@@ -52,6 +54,7 @@ require_once '../../../session.php';
|
||||
require_once '../../../config.php';
|
||||
require_once '../../../dbconn.php';
|
||||
require_once '../../../assets/utils/db_helpers.php';
|
||||
require_once '../../../assets/utils/otp_policy.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
@@ -97,9 +100,9 @@ try {
|
||||
$company_name = trim($data['company_name'] ?? '');
|
||||
$company_name2 = trim($data['company_name2'] ?? '');
|
||||
|
||||
// channel_name is the URL slug / identifier — strip everything except
|
||||
// lowercase letters, digits, hyphens, and underscores.
|
||||
$channel_name = strtolower(preg_replace('/[^a-z0-9\-_]/', '', $data['channel_name'] ?? ''));
|
||||
// channel_name is the URL slug / identifier — lowercase first, then strip
|
||||
// everything except lowercase letters, digits, hyphens, and underscores.
|
||||
$channel_name = preg_replace('/[^a-z0-9\-_]/', '', strtolower(trim($data['channel_name'] ?? '')));
|
||||
|
||||
$branch = trim($data['branch'] ?? 'สำนักงานใหญ่');
|
||||
$branch_no = trim($data['branch_no'] ?? '00000');
|
||||
@@ -114,19 +117,26 @@ try {
|
||||
}
|
||||
|
||||
// ── Step 5: SMTP field validation ────────────────────────────────────────
|
||||
// SMTP is mandatory because the company needs to send OTP emails to users.
|
||||
// An account without working SMTP would be unable to complete 2FA login.
|
||||
// While email OTP is on, SMTP is mandatory: the company needs it to send OTP
|
||||
// emails, and an account without working SMTP could not complete 2FA login.
|
||||
// With OTP_REQUIRED=false in config.php it is optional — all three fields
|
||||
// left blank means "no SMTP", and the test send (step 8) and the company_smtp
|
||||
// row (step 13) are skipped. Partly filled is an error either way.
|
||||
$smtp_host = trim($data['smtp_host'] ?? '');
|
||||
$smtp_username = trim($data['smtp_username'] ?? '');
|
||||
$smtp_password = $data['smtp_password'] ?? '';
|
||||
$smtp_given = ($smtp_host !== '' || $smtp_username !== '' || $smtp_password !== '');
|
||||
|
||||
if (!$smtp_host || !$smtp_username || !$smtp_password) {
|
||||
$answer['message'] = 'SMTP configuration is required. Please fill in all SMTP fields.';
|
||||
if ((otp_required() || $smtp_given) && (!$smtp_host || !$smtp_username || !$smtp_password)) {
|
||||
$answer['message'] = otp_required()
|
||||
? 'SMTP configuration is required. Please fill in all SMTP fields.'
|
||||
: 'Fill in SMTP host, username and password, or leave all three blank.';
|
||||
http_response_code(422);
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
// ── Step 6: Normalise SMTP port and encryption ────────────────────────────
|
||||
if ($smtp_given) {
|
||||
// ── Step 6: Normalise SMTP port and encryption ────────────────────────
|
||||
// Clamp to known-good values to prevent storing unsupported configuration.
|
||||
$smtp_port = trim($data['smtp_port'] ?? '587');
|
||||
$smtp_encryption = trim($data['smtp_encryption'] ?? 'tls');
|
||||
@@ -134,7 +144,7 @@ try {
|
||||
if (!in_array($smtp_port, ['25', '465', '587'], true)) $smtp_port = '587';
|
||||
if (!in_array($smtp_encryption, ['tls', 'ssl', 'none'], true)) $smtp_encryption = 'tls';
|
||||
|
||||
// ── Step 7: Encrypt SMTP password ────────────────────────────────────────
|
||||
// ── Step 7: Encrypt SMTP password ────────────────────────────────────
|
||||
// Uses the same OpenSSL method/iv/key as the rest of the app (from config.php)
|
||||
// so the stored password can be decrypted by the mailer module.
|
||||
$encrypted_pass = openssl_encrypt($smtp_password, $method, $pinkey, 0, $iv);
|
||||
@@ -150,7 +160,7 @@ try {
|
||||
'encryption' => $smtp_encryption,
|
||||
];
|
||||
|
||||
// ── Step 8: Silent SMTP test — before any DB writes ──────────────────────
|
||||
// ── Step 8: Silent SMTP test — before any DB writes ──────────────────
|
||||
// Sends a test email to the onboarding user's registered address.
|
||||
// If the mailer throws or exits, no DB records have been created yet,
|
||||
// so the user can correct their SMTP settings and retry cleanly.
|
||||
@@ -167,6 +177,7 @@ try {
|
||||
'key' => $pinkey,
|
||||
]);
|
||||
// If mailer fails, it calls exit() internally — nothing below this line runs.
|
||||
}
|
||||
|
||||
// ── Step 9: Duplicate channel_name check ─────────────────────────────────
|
||||
// channel_name is the unique identifier used in URLs and API calls — must be globally unique.
|
||||
@@ -226,6 +237,9 @@ try {
|
||||
// ── Step 13: Save company SMTP settings ──────────────────────────────────
|
||||
// Stored with the encrypted password so the mailer module can decrypt and
|
||||
// use it for all outgoing email from this company (OTP, notifications, etc.).
|
||||
// Skipped when no SMTP was given (only allowed with OTP_REQUIRED=false); it
|
||||
// can be added later under Settings → SMTP.
|
||||
if ($smtp_given) {
|
||||
$sth = $pdo1->prepare("
|
||||
INSERT INTO company_smtp
|
||||
(company_id, server, port, username, password,
|
||||
@@ -245,6 +259,7 @@ try {
|
||||
':encryption' => $smtp_encryption,
|
||||
]);
|
||||
db_check($sth, $answer);
|
||||
}
|
||||
|
||||
// ── Step 14: Clear onboarding session keys ───────────────────────────────
|
||||
// These keys are no longer needed and should not persist into the
|
||||
|
||||
+18
-2
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require '../session.php';
|
||||
require '../config.php';
|
||||
require_once '../assets/utils/otp_policy.php';
|
||||
require '../include_header.php';
|
||||
// successful login — redirect based on app_access
|
||||
if(!empty($_SESSION["login_status"])){
|
||||
@@ -32,6 +33,13 @@
|
||||
</div>
|
||||
|
||||
<form class="needs-validation mt-3" novalidate id="login-form">
|
||||
<?php if (!otp_required()): ?>
|
||||
<!-- OTP_REQUIRED is not true in config.php (the default): a password-only sign-in must never be invisible -->
|
||||
<div class="alert alert-warning small py-2 mb-3" title="OTP_REQUIRED is not true in config.php">
|
||||
<i class="ti ti-alert-triangle me-1"></i>
|
||||
Email OTP is off — sign-in is password only.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- first step login [OTP] -->
|
||||
<?php if(!isset($_SESSION['login_data'])){?>
|
||||
<div class="mb-3">
|
||||
@@ -51,7 +59,7 @@
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<!-- "Remember me" is intentionally excluded.
|
||||
This login uses 2FA (OTP via email) on every session.
|
||||
This login uses 2FA (OTP via email) on every session when OTP_REQUIRED=true in config.php (off by default).
|
||||
A persistent login would bypass the OTP step and undermine the security model.
|
||||
Do not add this back. -->
|
||||
</div>
|
||||
@@ -62,6 +70,7 @@
|
||||
</p>
|
||||
<?php }else{ ?>
|
||||
<!-- second step login -->
|
||||
<?php if (otp_required()): ?>
|
||||
<div class="alert alert-warning small py-2 mb-3">
|
||||
<i class="ti ti-mail me-1"></i>
|
||||
OTP is sent via your company's SMTP setting.
|
||||
@@ -73,13 +82,20 @@
|
||||
<span>One Time Password</span>
|
||||
</label>
|
||||
<input id="otp" type="otp" class="form-control"
|
||||
placeholder="your otp for reference number <?php echo $_SESSION["reference"]?>" required minlength="6">
|
||||
placeholder="your otp for reference number <?php echo $_SESSION["reference"] ?? ''?>" required minlength="6">
|
||||
<div class="invalid-feedback">Please provide a otp (min 6 characters).</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<!-- OTP_REQUIRED was switched off while this session sat on the OTP step:
|
||||
login_confirm.php no longer checks the code, so there is nothing to type. -->
|
||||
<input id="otp" type="hidden" value="">
|
||||
<?php endif; ?>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label d-flex justify-content-between">
|
||||
<a href="javascript:;" class="small link-primary" onclick="back()">Back</a>
|
||||
<?php if (otp_required()): ?>
|
||||
<a href="javascript:;" class="small link-primary" onclick="request_new_otp();">Request New OTP</a>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-primary w-100" onclick="login_confirm();">Sign in</button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require '../session.php';
|
||||
require '../config.php';
|
||||
require_once '../assets/utils/otp_policy.php';
|
||||
|
||||
// Must come from email verification
|
||||
if (empty($_SESSION['onboarding_user_id'])) {
|
||||
@@ -48,7 +49,8 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Channel Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="channel_name" placeholder="e.g. my-shop">
|
||||
<input type="text" class="form-control" id="channel_name" placeholder="e.g. my-shop"
|
||||
oninput="this.value=this.value.toLowerCase().replace(/[^a-z0-9_-]/g,'')">
|
||||
<div class="form-text">Unique identifier. Lowercase, no spaces.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@@ -77,11 +79,20 @@
|
||||
|
||||
<div class="d-flex justify-content-between align-items-start mb-1">
|
||||
<h2 class="fs-5 mb-0"><i class="ti ti-mail-cog me-2"></i>SMTP / Email Setting</h2>
|
||||
<?php if (otp_required()): ?>
|
||||
<span class="badge bg-label-danger">Required</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-label-secondary">Optional</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<p class="text-muted small mb-3">
|
||||
<?php if (otp_required()): ?>
|
||||
SMTP is required to send OTP during login.
|
||||
A verification email will be sent when you finish setup.
|
||||
<?php else: ?>
|
||||
Email OTP is turned off, so SMTP is optional. Leave it blank to skip;
|
||||
you can add it later under Settings → SMTP.
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<!-- SMTP User Guide (collapsible) -->
|
||||
@@ -174,11 +185,11 @@
|
||||
<!-- SMTP Form -->
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">SMTP Host <span class="text-danger">*</span></label>
|
||||
<label class="form-label">SMTP Host <?php if (otp_required()): ?><span class="text-danger">*</span><?php endif; ?></label>
|
||||
<input type="text" class="form-control" id="smtp_host" placeholder="e.g. smtp.gmail.com">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Port <span class="text-danger">*</span></label>
|
||||
<label class="form-label">Port <?php if (otp_required()): ?><span class="text-danger">*</span><?php endif; ?></label>
|
||||
<select class="form-select" id="smtp_port">
|
||||
<option value="587">587 — TLS</option>
|
||||
<option value="465">465 — SSL</option>
|
||||
@@ -186,11 +197,11 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Username / Email <span class="text-danger">*</span></label>
|
||||
<label class="form-label">Username / Email <?php if (otp_required()): ?><span class="text-danger">*</span><?php endif; ?></label>
|
||||
<input type="text" class="form-control" id="smtp_username" placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Password <span class="text-danger">*</span></label>
|
||||
<label class="form-label">Password <?php if (otp_required()): ?><span class="text-danger">*</span><?php endif; ?></label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="smtp_password" placeholder="SMTP password">
|
||||
<button class="btn btn-outline-secondary toggle-pw" type="button" data-target="smtp_password">
|
||||
@@ -251,13 +262,23 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$('#smtp_host').val().trim() || !$('#smtp_username').val().trim() || !$('#smtp_password').val()) {
|
||||
bootbox.alert('SMTP host, username and password are required.');
|
||||
// Mirrors api/engine/onboarding.php: SMTP is required while email OTP is on;
|
||||
// with OTP_REQUIRED=false it is optional, but the three fields go together.
|
||||
const smtp_required = <?php echo otp_required() ? 'true' : 'false'; ?>;
|
||||
const smtp_host = $('#smtp_host').val().trim();
|
||||
const smtp_user = $('#smtp_username').val().trim();
|
||||
const smtp_pass = $('#smtp_password').val();
|
||||
const smtp_given = !!(smtp_host || smtp_user || smtp_pass);
|
||||
|
||||
if ((smtp_required || smtp_given) && (!smtp_host || !smtp_user || !smtp_pass)) {
|
||||
bootbox.alert(smtp_required
|
||||
? 'SMTP host, username and password are required.'
|
||||
: 'Fill in SMTP host, username and password, or leave all three blank.');
|
||||
return;
|
||||
}
|
||||
|
||||
const $btn = $('#btn_finish');
|
||||
$btn.prop('disabled', true).html('<i class="ti ti-loader-2 me-1"></i>Verifying SMTP…');
|
||||
$btn.prop('disabled', true).html('<i class="ti ti-loader-2 me-1"></i>' + (smtp_given ? 'Verifying SMTP…' : 'Setting up…'));
|
||||
|
||||
const encryption = $('input[name="smtp_encryption"]:checked').val();
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
// app/session.php
|
||||
ob_start(); // ensure output buffering is on regardless of php.ini — prevents stray output from corrupting JSON API responses
|
||||
|
||||
// The buffer is still flushed, so notices would still land in front of the JSON
|
||||
// body and break the client's parse ("Server error occurred."). The login API
|
||||
// engines load this file instead of db_auth.php, so apply the same policy here.
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('log_errors', '1');
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
|
||||
// Derive cookie path dynamically from the current script location.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/app_registry.php';
|
||||
require_once '../../../assets/utils/classes/UserManager.php';
|
||||
|
||||
if ($user_role !== 'owner') { http_response_code(403); exit(json_encode(['success' => 0, 'message' => 'Only the owner can modify this setting.'])); }
|
||||
|
||||
@@ -121,7 +121,8 @@
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Nickname / Channel Name</label>
|
||||
<input type="text" class="form-control" id="channel_name" placeholder="e.g. my-shop">
|
||||
<input type="text" class="form-control" id="channel_name" placeholder="e.g. my-shop"
|
||||
oninput="this.value=this.value.toLowerCase().replace(/[^a-z0-9_-]/g,'')">
|
||||
<div class="form-text">Unique identifier. Lowercase, no spaces.</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../config.php';
|
||||
require_once '../assets/utils/app_registry.php';
|
||||
require '../include_header.php';
|
||||
?>
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ services:
|
||||
EMIT_SECRET: ${EMIT_SECRET}
|
||||
SMTP_USERNAME: ${SMTP_USERNAME}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD}
|
||||
OTP_REQUIRED: ${OTP_REQUIRED:-false}
|
||||
volumes:
|
||||
- .:/var/www/html/wms-app
|
||||
ports:
|
||||
|
||||
@@ -55,6 +55,7 @@ PUBLIC_HOST=$public_host
|
||||
EMIT_SECRET=$emit_secret
|
||||
SMTP_USERNAME=$smtp_user
|
||||
SMTP_PASSWORD=$smtp_pass
|
||||
OTP_REQUIRED=false
|
||||
HTTP_PORT=$http_port
|
||||
EOF
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
@@ -2,6 +2,8 @@ FROM php:8.3-apache
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libzip-dev libicu-dev libonig-dev default-mysql-client gettext-base \
|
||||
libpng-dev libjpeg-dev libfreetype6-dev \
|
||||
&& docker-php-ext-configure gd --with-jpeg --with-freetype \
|
||||
&& docker-php-ext-install pdo_mysql mysqli mbstring gd zip intl sockets exif opcache \
|
||||
&& a2enmod rewrite \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -33,6 +33,22 @@ if (!defined('NODE_EMIT_SECRET')) {
|
||||
define('NODE_EMIT_SECRET', '${EMIT_SECRET}');
|
||||
}
|
||||
|
||||
// ── Login OTP ────────────────────────────────────────────────────────────────
|
||||
// Set from OTP_REQUIRED in .env and reconciled by the entrypoint on every start.
|
||||
// Off by default: anything other than the boolean true leaves the OTP step off.
|
||||
if (!defined('OTP_REQUIRED')) {
|
||||
define('OTP_REQUIRED', ${OTP_REQUIRED});
|
||||
}
|
||||
|
||||
// ── App registry ─────────────────────────────────────────────────────────────
|
||||
// Apps a user can be given access to (Setting → Users Access). Keys must match
|
||||
// the user.app_access enum; assets/utils/app_registry.php supplies this default
|
||||
// for configs that do not define it.
|
||||
$app_registry = [
|
||||
'wms' => ['label' => 'WMS', 'icon' => 'ti-box', 'color' => 'bg-label-primary'],
|
||||
'accounting' => ['label' => 'Accounting', 'icon' => 'ti-calculator', 'color' => 'bg-label-success'],
|
||||
];
|
||||
|
||||
// ── Usage packages ───────────────────────────────────────────────────────────
|
||||
$packages = [
|
||||
'starter' => [
|
||||
|
||||
@@ -4,15 +4,39 @@ set -e
|
||||
APP_DIR=/var/www/html/wms-app
|
||||
CONFIG=$APP_DIR/app/config.php
|
||||
|
||||
# Email OTP on sign-in, OFF BY DEFAULT. Only the exact string "true" turns it
|
||||
# on; a missing variable or anything else means false.
|
||||
: "${OTP_REQUIRED:=false}"
|
||||
[ "$OTP_REQUIRED" = "true" ] || OTP_REQUIRED=false
|
||||
export OTP_REQUIRED
|
||||
|
||||
# Generate app/config.php from template on first run only.
|
||||
# Restrict envsubst to known placeholders so it never touches the app's own
|
||||
# $variable syntax (envsubst blanks out any $NAME it doesn't recognize).
|
||||
if [ ! -f "$CONFIG" ]; then
|
||||
echo "[entrypoint] generating app/config.php"
|
||||
envsubst '${DB_ROOT_PASSWORD} ${PUBLIC_HOST} ${EMIT_SECRET} ${SMTP_USERNAME} ${SMTP_PASSWORD}' \
|
||||
envsubst '${DB_ROOT_PASSWORD} ${PUBLIC_HOST} ${EMIT_SECRET} ${SMTP_USERNAME} ${SMTP_PASSWORD} ${OTP_REQUIRED}' \
|
||||
< /usr/local/etc/wms/config.php.template > "$CONFIG"
|
||||
fi
|
||||
|
||||
# config.php is never regenerated once it exists, so OTP_REQUIRED is the one
|
||||
# line reconciled on every start: the .env value always wins, and a config.php
|
||||
# written before this switch existed gets the line added.
|
||||
if grep -q "define('OTP_REQUIRED'" "$CONFIG"; then
|
||||
if ! grep -q "define('OTP_REQUIRED', ${OTP_REQUIRED});" "$CONFIG"; then
|
||||
sed -i "s/define('OTP_REQUIRED', [A-Za-z]*);/define('OTP_REQUIRED', ${OTP_REQUIRED});/" "$CONFIG"
|
||||
echo "[entrypoint] OTP_REQUIRED is now ${OTP_REQUIRED}"
|
||||
fi
|
||||
else
|
||||
# Drop a closing ?> on the last line so the appended block stays inside PHP.
|
||||
sed -i -e '${/^[[:space:]]*?>[[:space:]]*$/d}' "$CONFIG"
|
||||
printf "\nif (!defined('OTP_REQUIRED')) {\n\tdefine('OTP_REQUIRED', %s);\n}\n" "$OTP_REQUIRED" >> "$CONFIG"
|
||||
echo "[entrypoint] added OTP_REQUIRED = ${OTP_REQUIRED} to an existing config.php"
|
||||
fi
|
||||
if [ "$OTP_REQUIRED" = "false" ]; then
|
||||
echo "[entrypoint] email OTP is off (OTP_REQUIRED=false); sign-in is password only."
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DIR/app/uploads"
|
||||
chown -R www-data:www-data "$APP_DIR/app/uploads"
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
2026-07-22T14:37:48: [2026-07-22T07:37:48.626Z] [PID: 505] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Wed Jul 22 2026 14:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T15:14:43: [2026-07-23T08:14:43.767Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.943Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 17:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.953Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 18:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.959Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 19:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.965Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 20:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:02:17: [2026-07-23T14:02:17.033Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 21:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.977Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 17:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.982Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 18:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.985Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 19:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.987Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 20:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T14:44:32: [2026-07-25T07:44:32.121Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 13:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T14:44:32: [2026-07-25T07:44:32.127Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 14:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T15:24:00: [2026-07-25T08:24:00.029Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 14:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T15:24:00: [2026-07-25T08:24:00.032Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T10:11:12: [2026-08-03T03:11:12.241Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 10:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T10:31:46: [2026-08-03T03:31:46.573Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 10:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T11:08:26: [2026-08-03T04:08:26.686Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 11:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T15:02:31: [2026-08-04T08:02:31.243Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T15:32:15: [2026-08-04T08:32:15.561Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 15:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T17:01:03: [2026-08-04T10:01:03.525Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 17:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
@@ -1,58 +0,0 @@
|
||||
2026-07-20T17:35:51: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-20T21:00:00: [scheduler] etl_gl slot=21
|
||||
2026-07-20T21:00:00: [scheduler] etl_gl slot=21 — no companies
|
||||
2026-07-21T16:06:58: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-22T13:08:58: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-22T14:37:48: [2026-07-22T07:37:48.626Z] [PID: 505] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Wed Jul 22 2026 14:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-22T15:00:00: [scheduler] etl_gl slot=15
|
||||
2026-07-22T15:00:00: [scheduler] etl_gl slot=15 — no companies
|
||||
2026-07-22T15:30:00: [scheduler] etl_stock slot=15
|
||||
2026-07-22T15:30:00: [scheduler] etl_stock slot=15 — no companies
|
||||
2026-07-22T16:00:00: [scheduler] etl_gl slot=16
|
||||
2026-07-22T16:00:00: [scheduler] etl_gl slot=16 — no companies
|
||||
2026-07-23T12:11:51: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-23T12:30:00: [scheduler] etl_stock slot=12
|
||||
2026-07-23T12:30:00: [scheduler] etl_stock slot=12 — no companies
|
||||
2026-07-23T13:36:04: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-23T15:14:43: [2026-07-23T08:14:43.767Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T15:30:00: [scheduler] etl_stock slot=15
|
||||
2026-07-23T15:30:00: [scheduler] etl_stock slot=15 — no companies
|
||||
2026-07-23T16:00:00: [scheduler] etl_gl slot=16
|
||||
2026-07-23T16:00:00: [scheduler] etl_gl slot=16 — no companies
|
||||
2026-07-23T16:30:00: [scheduler] etl_stock slot=16
|
||||
2026-07-23T16:30:00: [scheduler] etl_stock slot=16 — no companies
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.943Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 17:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.953Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 18:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.959Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 19:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T20:39:04: [2026-07-23T13:39:04.965Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 20:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:02:17: [2026-07-23T14:02:17.033Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 21:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.977Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 17:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.982Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 18:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.985Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 19:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:11:21: [2026-07-23T14:11:21.987Z] [PID: 451] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Thu Jul 23 2026 20:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-23T21:30:00: [scheduler] etl_stock slot=21
|
||||
2026-07-23T21:30:00: [scheduler] etl_stock slot=21 — no companies
|
||||
2026-07-25T12:19:55: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-07-25T12:30:00: [scheduler] etl_stock slot=12
|
||||
2026-07-25T12:30:00: [scheduler] etl_stock slot=12 — no companies
|
||||
2026-07-25T13:00:00: [scheduler] etl_gl slot=13
|
||||
2026-07-25T13:00:00: [scheduler] etl_gl slot=13 — no companies
|
||||
2026-07-25T14:44:32: [2026-07-25T07:44:32.121Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 13:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T14:44:32: [2026-07-25T07:44:32.127Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 14:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T15:24:00: [2026-07-25T08:24:00.029Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 14:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-07-25T15:24:00: [2026-07-25T08:24:00.032Z] [PID: 476] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Sat Jul 25 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T08:52:15: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-08-03T09:00:00: [scheduler] etl_gl slot=9
|
||||
2026-08-03T09:00:00: [scheduler] alert_overdue_invoices running
|
||||
2026-08-03T09:00:00: [scheduler] etl_gl slot=9 — no companies
|
||||
2026-08-03T10:11:12: [2026-08-03T03:11:12.241Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 10:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T10:31:46: [2026-08-03T03:31:46.573Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 10:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-03T11:08:26: [2026-08-03T04:08:26.686Z] [PID: 477] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Mon Aug 03 2026 11:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T13:35:14: [scheduler] running — etl_gl + etl_stock (hourly) | low_stock + overdue_invoices alerts (daily)
|
||||
2026-08-04T15:02:31: [2026-08-04T08:02:31.243Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 15:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T15:32:15: [2026-08-04T08:32:15.561Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 15:30:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
2026-08-04T16:00:00: [scheduler] etl_gl slot=16
|
||||
2026-08-04T16:00:00: [scheduler] etl_gl slot=16 — no companies
|
||||
2026-08-04T16:30:00: [scheduler] etl_stock slot=16
|
||||
2026-08-04T16:30:00: [scheduler] etl_stock slot=16 — no companies
|
||||
2026-08-04T17:01:03: [2026-08-04T10:01:03.525Z] [PID: 78443] [32m[NODE-CRON][32m [33m[WARN][0m missed execution at Tue Aug 04 2026 17:00:00 GMT+0700 (Indochina Time)! Possible blocking IO or high CPU user at the same process used by node-cron.
|
||||
@@ -1,255 +0,0 @@
|
||||
2026-07-20T17:35:51: Node.js real-time server running on port 3000
|
||||
2026-07-21T16:06:58: Node.js real-time server running on port 3000
|
||||
2026-07-22T13:08:58: Node.js real-time server running on port 3000
|
||||
2026-07-22T13:10:29: [connect] socket=0-LquhazY9eKKN7LAAAB company=1 user=1 role=owner
|
||||
2026-07-22T13:15:09: [disconnect] socket=0-LquhazY9eKKN7LAAAB
|
||||
2026-07-22T13:15:12: [connect] socket=P4rxlPGuHWMqT35XAAAD company=1 user=1 role=owner
|
||||
2026-07-22T13:26:21: [disconnect] socket=P4rxlPGuHWMqT35XAAAD
|
||||
2026-07-22T13:26:23: [connect] socket=EBxi3tj8fNMUmyoyAAAF company=1 user=1 role=owner
|
||||
2026-07-22T13:30:24: [disconnect] socket=EBxi3tj8fNMUmyoyAAAF
|
||||
2026-07-22T13:30:25: [connect] socket=SJtRh1L6usnHrWebAAAH company=1 user=1 role=owner
|
||||
2026-07-22T13:31:59: [disconnect] socket=SJtRh1L6usnHrWebAAAH
|
||||
2026-07-22T13:31:59: [connect] socket=xvRdZhqqg-soDWr7AAAJ company=1 user=1 role=owner
|
||||
2026-07-22T13:41:17: [disconnect] socket=xvRdZhqqg-soDWr7AAAJ
|
||||
2026-07-22T13:41:17: [connect] socket=QZkAkh2pHOGltp-0AAAL company=1 user=1 role=owner
|
||||
2026-07-22T13:42:28: [disconnect] socket=QZkAkh2pHOGltp-0AAAL
|
||||
2026-07-22T13:42:28: [connect] socket=0Vb5YTMHDs1gOC47AAAN company=1 user=1 role=owner
|
||||
2026-07-22T13:42:36: [disconnect] socket=0Vb5YTMHDs1gOC47AAAN
|
||||
2026-07-22T13:42:36: [connect] socket=idQO9l1OGLiXPyEoAAAP company=1 user=1 role=owner
|
||||
2026-07-22T13:42:43: [disconnect] socket=idQO9l1OGLiXPyEoAAAP
|
||||
2026-07-22T13:42:43: [connect] socket=nvfS_4EF_3kF5PGsAAAR company=1 user=1 role=owner
|
||||
2026-07-22T13:47:25: [disconnect] socket=nvfS_4EF_3kF5PGsAAAR
|
||||
2026-07-22T13:47:27: [connect] socket=yytX3fzh594f9phBAAAT company=1 user=1 role=owner
|
||||
2026-07-22T13:50:19: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-07-22T13:50:19: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-07-22T13:50:19: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-07-22T13:50:19: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-07-22T13:50:19: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-07-22T13:59:42: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:42: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T13:59:43: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-07-22T14:03:26: [disconnect] socket=yytX3fzh594f9phBAAAT
|
||||
2026-07-22T14:03:28: [connect] socket=waEM4mo4V099RHhAAAAV company=1 user=1 role=owner
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:03:30: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:11:10: [disconnect] socket=waEM4mo4V099RHhAAAAV
|
||||
2026-07-22T14:35:14: [connect] socket=ZSmIs38dJh1u4If4AAAX company=1 user=1 role=owner
|
||||
2026-07-22T14:35:49: [disconnect] socket=ZSmIs38dJh1u4If4AAAX
|
||||
2026-07-22T14:35:49: [connect] socket=1bcHdQOC7cBTftxyAAAZ company=1 user=1 role=owner
|
||||
2026-07-22T14:36:37: [disconnect] socket=1bcHdQOC7cBTftxyAAAZ
|
||||
2026-07-22T14:36:38: [connect] socket=fMazKVGWKhaTm7OUAAAb company=1 user=1 role=owner
|
||||
2026-07-22T14:36:46: [disconnect] socket=fMazKVGWKhaTm7OUAAAb
|
||||
2026-07-22T14:36:46: [connect] socket=VfqVaEiOI3NTCA7fAAAd company=1 user=1 role=owner
|
||||
2026-07-22T14:36:48: [disconnect] socket=VfqVaEiOI3NTCA7fAAAd
|
||||
2026-07-22T14:36:48: [connect] socket=DAww8irzEgS7j7JLAAAf company=1 user=1 role=owner
|
||||
2026-07-22T14:36:49: [disconnect] socket=DAww8irzEgS7j7JLAAAf
|
||||
2026-07-22T14:36:49: [connect] socket=WgF3HArg1rthWkktAAAh company=1 user=1 role=owner
|
||||
2026-07-22T14:37:05: [disconnect] socket=WgF3HArg1rthWkktAAAh
|
||||
2026-07-22T14:37:05: [connect] socket=pfMLLNR0x-qoq485AAAj company=1 user=1 role=owner
|
||||
2026-07-22T14:37:08: [disconnect] socket=pfMLLNR0x-qoq485AAAj
|
||||
2026-07-22T14:37:09: [connect] socket=7ZtkCaJZqcYXBSZWAAAl company=1 user=1 role=owner
|
||||
2026-07-22T14:37:14: [disconnect] socket=7ZtkCaJZqcYXBSZWAAAl
|
||||
2026-07-22T14:37:14: [connect] socket=F6_OvPrmc-vv_KoqAAAn company=1 user=1 role=owner
|
||||
2026-07-22T14:37:18: [disconnect] socket=F6_OvPrmc-vv_KoqAAAn
|
||||
2026-07-22T14:37:18: [connect] socket=gdlZxPbkkcdkdE2AAAAp company=1 user=1 role=owner
|
||||
2026-07-22T14:40:08: [disconnect] socket=gdlZxPbkkcdkdE2AAAAp
|
||||
2026-07-22T14:40:09: [connect] socket=c8j8ybp-e7MPpa6cAAAr company=1 user=1 role=owner
|
||||
2026-07-22T14:40:13: [disconnect] socket=c8j8ybp-e7MPpa6cAAAr
|
||||
2026-07-22T14:40:13: [connect] socket=D36DmJgraENmU5xsAAAt company=1 user=1 role=owner
|
||||
2026-07-22T14:40:20: [disconnect] socket=D36DmJgraENmU5xsAAAt
|
||||
2026-07-22T14:40:21: [connect] socket=saY0q6gBioHWgq7RAAAv company=1 user=1 role=owner
|
||||
2026-07-22T14:40:25: [disconnect] socket=saY0q6gBioHWgq7RAAAv
|
||||
2026-07-22T14:40:25: [connect] socket=o9h4_9i-KOjDfVmrAAAx company=1 user=1 role=owner
|
||||
2026-07-22T14:40:27: [disconnect] socket=o9h4_9i-KOjDfVmrAAAx
|
||||
2026-07-22T14:40:27: [connect] socket=aIM89idl78lyhTbNAAAz company=1 user=1 role=owner
|
||||
2026-07-22T14:56:05: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:56:05: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'in' }
|
||||
2026-07-22T14:56:06: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T14:56:06: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'out' }
|
||||
2026-07-22T15:21:05: [emit] company=1 event=stock_updated { warehouse_id: 3, type: 'transfer' }
|
||||
2026-07-22T15:28:23: [disconnect] socket=aIM89idl78lyhTbNAAAz
|
||||
2026-07-22T15:43:35: [connect] socket=q1N4BECKfiomOEfyAAA1 company=1 user=1 role=owner
|
||||
2026-07-22T15:43:38: [disconnect] socket=q1N4BECKfiomOEfyAAA1
|
||||
2026-07-22T15:43:38: [connect] socket=yM_0j72uX0V2b-GuAAA3 company=1 user=1 role=owner
|
||||
2026-07-22T15:43:43: [disconnect] socket=yM_0j72uX0V2b-GuAAA3
|
||||
2026-07-22T15:43:43: [connect] socket=dvzUKq0p7lXQMuSLAAA5 company=1 user=1 role=owner
|
||||
2026-07-22T15:43:45: [disconnect] socket=dvzUKq0p7lXQMuSLAAA5
|
||||
2026-07-22T15:43:45: [connect] socket=TYvNpQgcOHR5-V1RAAA7 company=1 user=1 role=owner
|
||||
2026-07-22T15:43:46: [disconnect] socket=TYvNpQgcOHR5-V1RAAA7
|
||||
2026-07-22T15:43:46: [connect] socket=waNTeqU5sAu8W2jqAAA9 company=1 user=1 role=owner
|
||||
2026-07-22T16:05:01: [disconnect] socket=waNTeqU5sAu8W2jqAAA9
|
||||
2026-07-23T12:11:51: Node.js real-time server running on port 3000
|
||||
2026-07-23T12:13:27: [connect] socket=NYO9Tg4V6Cqp3e4cAAAB company=1 user=1 role=owner
|
||||
2026-07-23T12:33:05: [disconnect] socket=NYO9Tg4V6Cqp3e4cAAAB
|
||||
2026-07-23T12:33:06: [connect] socket=pEDe8Dms2dU7gdhCAAAD company=1 user=1 role=owner
|
||||
2026-07-23T12:34:13: [disconnect] socket=pEDe8Dms2dU7gdhCAAAD
|
||||
2026-07-23T12:34:17: [connect] socket=vaGUT12vPXkqH_7kAAAF company=1 user=1 role=owner
|
||||
2026-07-23T12:55:49: [disconnect] socket=vaGUT12vPXkqH_7kAAAF
|
||||
2026-07-23T13:36:04: Node.js real-time server running on port 3000
|
||||
2026-07-23T13:36:55: [connect] socket=nVGY71aXPas0zYS_AAAB company=1 user=1 role=owner
|
||||
2026-07-23T13:51:36: [disconnect] socket=nVGY71aXPas0zYS_AAAB
|
||||
2026-07-23T13:51:36: [connect] socket=5peGIWbSaRCxnlYBAAAD company=1 user=1 role=owner
|
||||
2026-07-23T13:51:38: [disconnect] socket=5peGIWbSaRCxnlYBAAAD
|
||||
2026-07-23T13:51:38: [connect] socket=fGwRmZaYGzedlqqRAAAF company=1 user=1 role=owner
|
||||
2026-07-23T13:51:40: [disconnect] socket=fGwRmZaYGzedlqqRAAAF
|
||||
2026-07-23T13:51:40: [connect] socket=YZk7xLKHpmi29ykIAAAH company=1 user=1 role=owner
|
||||
2026-07-23T13:51:41: [disconnect] socket=YZk7xLKHpmi29ykIAAAH
|
||||
2026-07-23T13:51:41: [connect] socket=f078x01mtX2eGd2HAAAJ company=1 user=1 role=owner
|
||||
2026-07-23T13:57:26: [disconnect] socket=f078x01mtX2eGd2HAAAJ
|
||||
2026-07-23T13:57:28: [connect] socket=vToy_ZVIAk6w9099AAAL company=1 user=1 role=owner
|
||||
2026-07-23T14:17:58: [disconnect] socket=vToy_ZVIAk6w9099AAAL
|
||||
2026-07-23T14:18:00: [connect] socket=7yHLdkKxBAAO_nF4AAAN company=1 user=1 role=owner
|
||||
2026-07-23T16:25:41: [disconnect] socket=7yHLdkKxBAAO_nF4AAAN
|
||||
2026-07-25T12:19:55: Node.js real-time server running on port 3000
|
||||
2026-08-03T08:52:15: Node.js real-time server running on port 3000
|
||||
2026-08-03T10:17:41: [connect] socket=kNyFq-T_JVC3-_WLAAAB company=1 user=1 role=owner
|
||||
2026-08-03T10:18:50: [disconnect] socket=kNyFq-T_JVC3-_WLAAAB
|
||||
2026-08-03T10:18:50: [connect] socket=efcou9_hk0YUTnvQAAAD company=1 user=1 role=owner
|
||||
2026-08-03T10:18:59: [disconnect] socket=efcou9_hk0YUTnvQAAAD
|
||||
2026-08-03T10:18:59: [connect] socket=PmKuy_3CCIDze4P3AAAF company=1 user=1 role=owner
|
||||
2026-08-03T10:32:14: [disconnect] socket=PmKuy_3CCIDze4P3AAAF
|
||||
2026-08-03T10:32:18: [connect] socket=-ZlIPBBmpRazD30gAAAH company=1 user=1 role=owner
|
||||
2026-08-03T10:49:23: [disconnect] socket=-ZlIPBBmpRazD30gAAAH
|
||||
2026-08-03T10:49:26: [connect] socket=Azbj9ipTPqb3aOW3AAAJ company=1 user=1 role=owner
|
||||
2026-08-03T10:54:19: [disconnect] socket=Azbj9ipTPqb3aOW3AAAJ
|
||||
2026-08-03T10:54:19: [connect] socket=FMnx-fmSk96rxIfwAAAL company=1 user=1 role=owner
|
||||
2026-08-03T11:13:57: [disconnect] socket=FMnx-fmSk96rxIfwAAAL
|
||||
2026-08-03T11:13:59: [connect] socket=LgVxBoN_6JuJtxHyAAAN company=1 user=1 role=owner
|
||||
2026-08-04T13:35:14: Node.js real-time server running on port 3000
|
||||
2026-08-04T13:36:21: [connect] socket=BOvxSU23giBsnIXWAAAB company=1 user=1 role=owner
|
||||
2026-08-04T13:36:25: [disconnect] socket=BOvxSU23giBsnIXWAAAB
|
||||
2026-08-04T13:36:25: [connect] socket=pJXUXD7HNX_V9peAAAAD company=1 user=1 role=owner
|
||||
2026-08-04T13:36:27: [disconnect] socket=pJXUXD7HNX_V9peAAAAD
|
||||
2026-08-04T13:36:27: [connect] socket=St7RkiRumWpwc47xAAAF company=1 user=1 role=owner
|
||||
2026-08-04T13:36:28: [disconnect] socket=St7RkiRumWpwc47xAAAF
|
||||
2026-08-04T13:36:28: [connect] socket=a9NsNFmUpIL1vW8uAAAH company=1 user=1 role=owner
|
||||
2026-08-04T13:40:28: [disconnect] socket=a9NsNFmUpIL1vW8uAAAH
|
||||
2026-08-04T13:40:28: [connect] socket=uYLFddlhCkOxrcJNAAAJ company=1 user=1 role=owner
|
||||
2026-08-04T13:48:10: [disconnect] socket=uYLFddlhCkOxrcJNAAAJ
|
||||
2026-08-04T13:48:11: [connect] socket=VekC6UabiJABBbjhAAAL company=1 user=1 role=owner
|
||||
2026-08-04T13:50:28: [disconnect] socket=VekC6UabiJABBbjhAAAL
|
||||
2026-08-04T13:50:29: [connect] socket=gj-glld-ZsxWbGQuAAAN company=1 user=1 role=owner
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:08:49: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T14:11:36: [disconnect] socket=gj-glld-ZsxWbGQuAAAN
|
||||
2026-08-04T14:11:36: [connect] socket=v97BofsQmYBAEJMmAAAP company=1 user=1 role=owner
|
||||
2026-08-04T14:13:54: [disconnect] socket=v97BofsQmYBAEJMmAAAP
|
||||
2026-08-04T14:13:54: [connect] socket=WYJSdI9xVDaTML9xAAAR company=1 user=1 role=owner
|
||||
2026-08-04T14:17:32: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:17:32: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:21:12: [disconnect] socket=WYJSdI9xVDaTML9xAAAR
|
||||
2026-08-04T14:21:12: [connect] socket=UhIyCllsOjal4UwCAAAT company=1 user=1 role=owner
|
||||
2026-08-04T14:21:13: [disconnect] socket=UhIyCllsOjal4UwCAAAT
|
||||
2026-08-04T14:21:13: [connect] socket=6hZ-_fAyDgWzwsgvAAAV company=1 user=1 role=owner
|
||||
2026-08-04T14:21:38: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:21:38: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:25:22: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:25:22: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:25:22: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:25:22: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:29:14: [disconnect] socket=6hZ-_fAyDgWzwsgvAAAV
|
||||
2026-08-04T14:29:14: [connect] socket=7ocTMfufQlyO36XrAAAX company=1 user=1 role=owner
|
||||
2026-08-04T14:29:19: [disconnect] socket=7ocTMfufQlyO36XrAAAX
|
||||
2026-08-04T14:36:58: [connect] socket=kY4WNaECxPvGVj2JAAAZ company=1 user=1 role=owner
|
||||
2026-08-04T14:37:12: [disconnect] socket=kY4WNaECxPvGVj2JAAAZ
|
||||
2026-08-04T14:37:12: [connect] socket=OabEymZu5SehwNWnAAAb company=1 user=1 role=owner
|
||||
2026-08-04T14:37:40: [disconnect] socket=OabEymZu5SehwNWnAAAb
|
||||
2026-08-04T14:41:55: [connect] socket=kAlZOJl54kd8RXKAAAAd company=1 user=1 role=owner
|
||||
2026-08-04T14:42:42: [disconnect] socket=kAlZOJl54kd8RXKAAAAd
|
||||
2026-08-04T14:42:42: [connect] socket=DBSytTfd-o12DxhfAAAf company=1 user=1 role=owner
|
||||
2026-08-04T14:49:38: [disconnect] socket=DBSytTfd-o12DxhfAAAf
|
||||
2026-08-04T14:49:39: [connect] socket=sAr1qebAYGyIq8zrAAAh company=1 user=1 role=owner
|
||||
2026-08-04T14:58:10: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-08-04T14:58:10: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:58:10: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T14:58:10: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T14:58:10: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T14:59:17: [disconnect] socket=sAr1qebAYGyIq8zrAAAh
|
||||
2026-08-04T14:59:17: [connect] socket=a264uVnqws4cIAy-AAAj company=1 user=1 role=owner
|
||||
2026-08-04T15:21:19: [disconnect] socket=a264uVnqws4cIAy-AAAj
|
||||
2026-08-04T15:21:19: [connect] socket=JL7kcUwnQI_NExMkAAAl company=1 user=1 role=owner
|
||||
2026-08-04T15:21:22: [disconnect] socket=JL7kcUwnQI_NExMkAAAl
|
||||
2026-08-04T15:27:09: [connect] socket=VcbOCpKEShqcqqM0AAAn company=1 user=1 role=owner
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 2, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'in' }
|
||||
2026-08-04T15:28:36: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'transfer' }
|
||||
2026-08-04T15:30:09: [disconnect] socket=VcbOCpKEShqcqqM0AAAn
|
||||
2026-08-04T15:30:09: [connect] socket=ecBAVshG1Eng4jh5AAAp company=1 user=1 role=owner
|
||||
2026-08-04T15:33:32: [disconnect] socket=ecBAVshG1Eng4jh5AAAp
|
||||
2026-08-04T15:33:32: [connect] socket=M43MyEQ7wipYOgd5AAAr company=1 user=1 role=owner
|
||||
2026-08-04T15:34:32: [disconnect] socket=M43MyEQ7wipYOgd5AAAr
|
||||
2026-08-04T15:34:32: [connect] socket=TiGDT6OMJsYlixlkAAAt company=1 user=1 role=owner
|
||||
2026-08-04T15:35:36: [disconnect] socket=TiGDT6OMJsYlixlkAAAt
|
||||
2026-08-04T15:35:36: [connect] socket=uHRGhZU0TJVvvh_aAAAv company=1 user=1 role=owner
|
||||
2026-08-04T15:36:24: [disconnect] socket=uHRGhZU0TJVvvh_aAAAv
|
||||
2026-08-04T15:36:24: [connect] socket=Hsui_73WGENhGdRIAAAx company=1 user=1 role=owner
|
||||
2026-08-04T15:38:27: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:38:27: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:38:27: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:38:27: [emit] company=1 event=stock_updated { warehouse_id: 1, type: 'out' }
|
||||
2026-08-04T15:39:55: [disconnect] socket=Hsui_73WGENhGdRIAAAx
|
||||
2026-08-04T15:39:55: [connect] socket=xSl0-9raxlwU2K9rAAAz company=1 user=1 role=owner
|
||||
2026-08-04T15:52:53: [disconnect] socket=xSl0-9raxlwU2K9rAAAz
|
||||
2026-08-04T15:52:53: [connect] socket=n_l9kHYTF1AXNNRYAAA1 company=1 user=1 role=owner
|
||||
2026-08-04T15:58:37: [disconnect] socket=n_l9kHYTF1AXNNRYAAA1
|
||||
2026-08-04T15:58:37: [connect] socket=T9mpzRMT3I1qjj0BAAA3 company=1 user=1 role=owner
|
||||
2026-08-04T15:58:39: [disconnect] socket=T9mpzRMT3I1qjj0BAAA3
|
||||
2026-08-04T15:58:39: [connect] socket=Q1jGtGwwFc49KKxDAAA5 company=1 user=1 role=owner
|
||||
2026-08-04T15:58:40: [disconnect] socket=Q1jGtGwwFc49KKxDAAA5
|
||||
2026-08-04T15:58:41: [connect] socket=Fk7l8SLr2IIRFFHbAAA7 company=1 user=1 role=owner
|
||||
2026-08-04T15:58:42: [disconnect] socket=Fk7l8SLr2IIRFFHbAAA7
|
||||
2026-08-04T15:58:42: [connect] socket=pAuTSmpDDC86EZwXAAA9 company=1 user=1 role=owner
|
||||
2026-08-04T16:41:11: [disconnect] socket=pAuTSmpDDC86EZwXAAA9
|
||||
2026-08-04T16:41:13: [connect] socket=XmFaoUIej-g8203TAAA_ company=1 user=1 role=owner
|
||||
2026-08-04T16:41:25: [disconnect] socket=XmFaoUIej-g8203TAAA_
|
||||
2026-08-04T16:41:28: [connect] socket=TxwzTsUHzqDLHpODAABB company=1 user=1 role=owner
|
||||
2026-08-04T16:43:43: [disconnect] socket=TxwzTsUHzqDLHpODAABB
|
||||
2026-08-04T16:44:26: [connect] socket=TOhs2YrBWQkiGDZDAABD company=1 user=1 role=owner
|
||||
2026-08-04T16:58:05: [disconnect] socket=TOhs2YrBWQkiGDZDAABD
|
||||
2026-08-04T16:59:15: [connect] socket=p1xNFoGJFKox8FaOAABF company=1 user=1 role=owner
|
||||
2026-08-04T17:25:03: [disconnect] socket=p1xNFoGJFKox8FaOAABF
|
||||
2026-08-04T17:25:03: [connect] socket=j3s6wvwe_BxVzCA_AABH company=1 user=1 role=owner
|
||||
2026-08-04T17:25:05: [disconnect] socket=j3s6wvwe_BxVzCA_AABH
|
||||
2026-08-04T17:25:05: [connect] socket=4wkwmOWCAvQSicL3AABJ company=1 user=1 role=owner
|
||||
2026-08-04T17:26:40: [disconnect] socket=4wkwmOWCAvQSicL3AABJ
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||
TOOL_DIR="$SCRIPT_DIR/sdlc-delivery"
|
||||
SOURCE_DIR="$ROOT_DIR/sdlc"
|
||||
DELIVERY_ROOT="$ROOT_DIR/sdlc-delivery"
|
||||
STAGING_DIR="$ROOT_DIR/.sdlc-delivery.staging"
|
||||
|
||||
if [[ ! -d "$TOOL_DIR/node_modules/playwright" ]]; then
|
||||
echo "Installing the local PDF renderer..."
|
||||
npm install --prefix "$TOOL_DIR"
|
||||
npx --prefix "$TOOL_DIR" playwright install chromium
|
||||
fi
|
||||
|
||||
rm -rf "$STAGING_DIR"
|
||||
mkdir -p "$STAGING_DIR"
|
||||
|
||||
while IFS= read -r -d '' source; do
|
||||
relative="${source#"$ROOT_DIR/sdlc/"}"
|
||||
destination="$STAGING_DIR/${relative%.md}.pdf"
|
||||
echo "Rendering ${relative%.md}.pdf"
|
||||
node "$TOOL_DIR/render-sdlc.mjs" "$source" "$destination"
|
||||
done < <(find "$SOURCE_DIR" -type f -name '*.md' -print0 | sort -z)
|
||||
|
||||
echo "Verifying staged delivery package..."
|
||||
"$SCRIPT_DIR/verify-sdlc-delivery.sh" "$STAGING_DIR"
|
||||
|
||||
rm -rf "$DELIVERY_ROOT"
|
||||
mv "$STAGING_DIR" "$DELIVERY_ROOT"
|
||||
echo "Delivery and verification passed: $DELIVERY_ROOT"
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
<table class="document-control">
|
||||
<tbody>
|
||||
<tr><th>Document</th><td>{{documentType}}</td><th>Release</th><td>{{release}}</td></tr>
|
||||
<tr><th>Project</th><td>{{projectName}}</td><th>Project code</th><td>{{projectCode}}</td></tr>
|
||||
<tr><th>Project period</th><td colspan="3">{{projectPeriod}}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div style="border-top:1.5pt solid #1f2933; color:#52606d; display:flex; font-family:'BRN Thai',Arial,sans-serif; font-size:8pt; justify-content:space-between; margin:0 16mm; padding-top:2.5mm; width:calc(100% - 32mm);">
|
||||
<span>ISO/IEC 29110-4-1:2018</span>
|
||||
<span>{{projectCode}}</span>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<header class="document-header">
|
||||
<img class="document-header__logo" src="{{logoPath}}" alt="B.R.N. Enterprise Co., Ltd.">
|
||||
<div class="document-header__company">
|
||||
<p class="document-header__company-name">B.R.N. ENTERPRISE CO., LTD.</p>
|
||||
<p class="document-header__address">1011 Supalai Grand Tower, 7th Floor, Unit 6-7, Rama 3 Road, Chongnonsi, Yannawa, Bangkok 10120<br>Tel. (+66) 2687 0250 Fax. (+66) 2687 0255</p>
|
||||
</div>
|
||||
<div class="document-header__title">{{documentTitle}}</div>
|
||||
</header>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>SDLC delivery theme preview</title>
|
||||
<link rel="stylesheet" href="sdlc-delivery.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="delivery-document">
|
||||
<header class="document-header">
|
||||
<img class="document-header__logo" src="../../../app/assets/images/logo.png" alt="B.R.N. Enterprise Co., Ltd.">
|
||||
<div class="document-header__company">
|
||||
<p class="document-header__company-name">B.R.N. ENTERPRISE CO., LTD.</p>
|
||||
<p class="document-header__address">1011 Supalai Grand Tower, 7th Floor, Unit 6-7, Rama 3 Road, Chongnonsi, Yannawa, Bangkok 10120<br>Tel. (+66) 2687 0250 Fax. (+66) 2687 0255</p>
|
||||
</div>
|
||||
<div class="document-header__title">Software Project Plan</div>
|
||||
</header>
|
||||
|
||||
<table class="document-control">
|
||||
<tbody>
|
||||
<tr><th>Document</th><td>Software Project Plan</td><th>Release</th><td>05/01/26 V1.0 Final</td></tr>
|
||||
<tr><th>Project</th><td>BRN WMS</td><th>Project code</th><td>200-WMS-26-001-00</td></tr>
|
||||
<tr><th>Project period</th><td colspan="3">05/01/26–24/08/26</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Project overview</h2>
|
||||
<p>This preview demonstrates the shared A4 header, document-control table, headings, and standard data tables used by every generated delivery PDF.</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Phase</th><th>Period</th><th>Primary output</th></tr></thead>
|
||||
<tbody><tr><td>Planning</td><td>12/01/26–18/02/26</td><td>Project Plan</td></tr></tbody>
|
||||
</table>
|
||||
</main>
|
||||
<footer class="document-footer"><span>ISO/IEC 29110-4-1:2018</span><span>200-WMS-26-001-00 · 1 / 1</span></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,130 @@
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 18mm 16mm 28mm;
|
||||
}
|
||||
|
||||
:root {
|
||||
--brn-blue: #0789c9;
|
||||
--brn-red: #bd0e2d;
|
||||
--brn-ink: #1f2933;
|
||||
--brn-muted: #52606d;
|
||||
--brn-line: #aab7c4;
|
||||
--brn-pale-blue: #eaf6fc;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
html, body {
|
||||
color: var(--brn-ink);
|
||||
font-family: "BRN Thai", Arial, sans-serif;
|
||||
font-size: 10.5pt;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
body { margin: 0; }
|
||||
|
||||
.delivery-document { width: 100%; }
|
||||
|
||||
.document-header {
|
||||
align-items: center;
|
||||
border-bottom: 1.5pt solid var(--brn-blue);
|
||||
display: flex;
|
||||
gap: 9mm;
|
||||
margin: 0 0 7mm;
|
||||
min-height: 22mm;
|
||||
padding: 0 0 3mm;
|
||||
}
|
||||
|
||||
.document-header__logo {
|
||||
flex: 0 0 auto;
|
||||
height: 16mm;
|
||||
object-fit: contain;
|
||||
width: 16mm;
|
||||
}
|
||||
|
||||
.document-header__company { flex: 1; min-width: 0; }
|
||||
|
||||
.document-header__company-name {
|
||||
font-size: 13pt;
|
||||
font-weight: 700;
|
||||
letter-spacing: .03em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.document-header__address {
|
||||
color: var(--brn-muted);
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.35;
|
||||
margin: 1mm 0 0;
|
||||
}
|
||||
|
||||
.document-header__title {
|
||||
background: var(--brn-blue);
|
||||
color: #fff;
|
||||
font-size: 15pt;
|
||||
font-weight: 500;
|
||||
flex: 0 1 72mm;
|
||||
overflow-wrap: anywhere;
|
||||
min-width: 52mm;
|
||||
padding: 4mm 6mm;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.document-control {
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 7mm;
|
||||
page-break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.document-control th,
|
||||
.document-control td {
|
||||
border: .5pt solid var(--brn-line);
|
||||
padding: 2.2mm 3mm;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.document-control th {
|
||||
background: var(--brn-pale-blue);
|
||||
font-weight: 700;
|
||||
width: 26%;
|
||||
}
|
||||
|
||||
h1, h2, h3 { break-after: avoid; color: var(--brn-ink); }
|
||||
h1 { font-size: 18pt; margin: 0 0 5mm; }
|
||||
h2 { border-left: 4pt solid var(--brn-blue); font-size: 14pt; margin: 9mm 0 4mm; padding-left: 3mm; }
|
||||
h3 { color: var(--brn-blue); font-size: 11.5pt; margin: 6mm 0 3mm; }
|
||||
|
||||
p { margin: 0 0 3.5mm; }
|
||||
|
||||
table:not(.document-control) {
|
||||
border-collapse: collapse;
|
||||
border: .75pt solid #718096;
|
||||
font-size: 9pt;
|
||||
margin: 0 0 5mm;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table:not(.document-control) th,
|
||||
table:not(.document-control) td {
|
||||
border: .5pt solid var(--brn-line);
|
||||
overflow-wrap: anywhere;
|
||||
padding: 2mm;
|
||||
vertical-align: top;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
table:not(.document-control) th { background: var(--brn-pale-blue); font-weight: 700; }
|
||||
thead { display: table-header-group; }
|
||||
tr { break-inside: avoid; }
|
||||
|
||||
.approval-block { break-inside: avoid; margin-top: 9mm; }
|
||||
.approval-block__person { margin: 5mm 0; min-height: 21mm; }
|
||||
.approval-block__line { border-bottom: .5pt solid var(--brn-ink); display: inline-block; min-width: 70mm; }
|
||||
.approval-fields {
|
||||
line-height: 1.62;
|
||||
margin: 0 0 8mm;
|
||||
padding: 2.5mm 0 4mm;
|
||||
}
|
||||
Generated
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "brn-wms-sdlc-delivery",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "brn-wms-sdlc-delivery",
|
||||
"dependencies": {
|
||||
"playwright": "1.50.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.0.tgz",
|
||||
"integrity": "sha512-+GinGfGTrd2IfX1TA4N2gNmeIksSb+IAe589ZH+FlmpV3MYTx6+buChGIuDLQwrGNCw2lWibqV50fU510N7S+w==",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.50.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.0.tgz",
|
||||
"integrity": "sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "brn-wms-sdlc-delivery",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"playwright": "1.50.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(here, '../..');
|
||||
const assets = path.join(here, 'assets');
|
||||
|
||||
const escapeHtml = (value) => value
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>');
|
||||
|
||||
const renderInline = (value) => escapeHtml(value)
|
||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
|
||||
const applyTemplate = (template, values) => template.replace(/{{(\w+)}}/g, (_, key) => values[key] ?? '');
|
||||
|
||||
function isTableLine(line) {
|
||||
return /^\|.*\|\s*$/.test(line.trim());
|
||||
}
|
||||
|
||||
function tableCells(line) {
|
||||
return line.trim().slice(1, -1).split('|').map((cell) => cell.trim());
|
||||
}
|
||||
|
||||
function isSeparator(cells) {
|
||||
return cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
||||
}
|
||||
|
||||
function renderTable(lines, className = '') {
|
||||
const rows = lines.map(tableCells);
|
||||
const header = rows[0];
|
||||
const data = isSeparator(rows[1] ?? []) ? rows.slice(2) : rows.slice(1);
|
||||
const cells = (tag, row) => row.map((cell) => `<${tag}>${renderInline(cell)}</${tag}>`).join('');
|
||||
return `<table${className ? ` class="${className}"` : ''}><thead><tr>${cells('th', header)}</tr></thead><tbody>${data.map((row) => `<tr>${cells('td', row)}</tr>`).join('')}</tbody></table>`;
|
||||
}
|
||||
|
||||
function renderParagraph(lines) {
|
||||
return lines.map((line, index) => {
|
||||
const trimmed = line.trimEnd();
|
||||
const hasHardBreak = /\s{2,}$/.test(line);
|
||||
const isApprovalField = /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(trimmed);
|
||||
const separator = hasHardBreak || isApprovalField ? '<br>' : (index < lines.length - 1 ? ' ' : '');
|
||||
return `${renderInline(trimmed)}${separator}`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function markdownToHtml(markdown) {
|
||||
const lines = markdown.replaceAll('\r\n', '\n').split('\n');
|
||||
const output = [];
|
||||
let index = 0;
|
||||
|
||||
while (index < lines.length) {
|
||||
const line = lines[index];
|
||||
if (!line.trim()) { index += 1; continue; }
|
||||
|
||||
if (isTableLine(line)) {
|
||||
const table = [];
|
||||
while (index < lines.length && isTableLine(lines[index])) table.push(lines[index++]);
|
||||
output.push(renderTable(table));
|
||||
continue;
|
||||
}
|
||||
|
||||
const heading = line.match(/^(#{1,3})\s+(.+)$/);
|
||||
if (heading) {
|
||||
const level = heading[1].length;
|
||||
output.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/^[-*]\s+/.test(line)) {
|
||||
const items = [];
|
||||
while (index < lines.length && /^[-*]\s+/.test(lines[index])) items.push(`<li>${renderInline(lines[index++].replace(/^[-*]\s+/, ''))}</li>`);
|
||||
output.push(`<ul>${items.join('')}</ul>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const paragraph = [];
|
||||
while (index < lines.length && lines[index].trim() && !isTableLine(lines[index]) && !/^(#{1,3})\s+/.test(lines[index]) && !/^[-*]\s+/.test(lines[index])) paragraph.push(lines[index++]);
|
||||
const approvalClass = paragraph.some((line) => /^(Name|Role|Signature|Date|Position|Company|Project roles|Decision):/.test(line.trimEnd())) ? ' class="approval-fields"' : '';
|
||||
output.push(`<p${approvalClass}>${renderParagraph(paragraph)}</p>`);
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function extractDocument(markdown) {
|
||||
const titleMatch = markdown.match(/^#\s+(.+)\n+/m);
|
||||
const title = titleMatch?.[1] ?? 'BRN WMS';
|
||||
const afterTitle = markdown.slice((titleMatch?.index ?? 0) + (titleMatch?.[0].length ?? 0));
|
||||
const lines = afterTitle.split('\n');
|
||||
const metadata = {};
|
||||
let bodyStart = 0;
|
||||
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
if (!isTableLine(lines[index])) continue;
|
||||
|
||||
const table = [];
|
||||
while (index < lines.length && isTableLine(lines[index])) table.push(lines[index++]);
|
||||
const candidate = {};
|
||||
const dataStart = isSeparator(tableCells(table[1] ?? '')) ? 2 : 1;
|
||||
for (const row of table.slice(dataStart).map(tableCells)) {
|
||||
if (row.length >= 2) candidate[row[0]] = row[1];
|
||||
}
|
||||
|
||||
if (candidate.Document && candidate.Project) {
|
||||
Object.assign(metadata, candidate);
|
||||
bodyStart = index;
|
||||
break;
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
}
|
||||
|
||||
return { title, metadata, body: lines.slice(bodyStart).join('\n') };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [source, destination] = process.argv.slice(2);
|
||||
if (!source || !destination) throw new Error('Usage: render-sdlc.mjs SOURCE.md DESTINATION.pdf');
|
||||
|
||||
const [markdown, css, headerTemplate, footerTemplate, logo, thaiFont] = await Promise.all([
|
||||
fs.readFile(source, 'utf8'),
|
||||
fs.readFile(path.join(assets, 'sdlc-delivery.css'), 'utf8'),
|
||||
fs.readFile(path.join(assets, 'header.html'), 'utf8'),
|
||||
fs.readFile(path.join(assets, 'footer.html'), 'utf8'),
|
||||
fs.readFile(path.join(root, 'app/assets/images/logo.svg')),
|
||||
fs.readFile(path.join(assets, 'LeelawadeeUI.ttf'))
|
||||
]);
|
||||
const { title, metadata, body } = extractDocument(markdown);
|
||||
const hasWideTable = markdown.split('\n').some((line) => isTableLine(line) && tableCells(line).length >= 8);
|
||||
const isLandscape = metadata.Document === 'Work Schedule' || title === 'Work Schedule' || hasWideTable;
|
||||
const values = {
|
||||
logoPath: `data:image/svg+xml;base64,${logo.toString('base64')}`,
|
||||
documentTitle: title,
|
||||
documentType: metadata.Document ?? title,
|
||||
release: metadata.Release ?? '',
|
||||
projectName: metadata.Project ?? 'BRN WMS',
|
||||
projectCode: metadata['Project code'] ?? '200-WMS-26-001-00',
|
||||
projectPeriod: metadata['Project period'] ?? '05/01/26–24/08/26',
|
||||
pageNumber: '<span class="pageNumber"></span>',
|
||||
totalPages: '<span class="totalPages"></span>'
|
||||
};
|
||||
const control = await fs.readFile(path.join(assets, 'document-control.html'), 'utf8');
|
||||
const pdfFooter = applyTemplate(footerTemplate, values);
|
||||
const fontFace = `@font-face { font-family: "BRN Thai"; src: url(data:font/ttf;base64,${thaiFont.toString('base64')}) format("truetype"); font-weight: 400 700; }`;
|
||||
const pageLayout = isLandscape
|
||||
? '@page { size: A4 landscape; margin: 18mm 16mm 28mm; }'
|
||||
: '';
|
||||
const html = `<!doctype html><html><head><meta charset="utf-8"><style>${fontFace}${css}${pageLayout}</style></head><body><main class="delivery-document">${applyTemplate(headerTemplate, values)}${applyTemplate(control, values)}${markdownToHtml(body)}</main></body></html>`;
|
||||
|
||||
await fs.mkdir(path.dirname(destination), { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
await page.setContent(html, { waitUntil: 'networkidle' });
|
||||
await page.pdf({
|
||||
path: destination,
|
||||
format: 'A4',
|
||||
printBackground: true,
|
||||
preferCSSPageSize: true,
|
||||
displayHeaderFooter: true,
|
||||
headerTemplate: '<div></div>',
|
||||
footerTemplate: pdfFooter
|
||||
});
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => { console.error(error); process.exit(1); });
|
||||
@@ -0,0 +1,91 @@
|
||||
import { execFile } from 'node:child_process';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function isTableLine(line) {
|
||||
return /^\|.*\|\s*$/.test(line.trim());
|
||||
}
|
||||
|
||||
function tableCells(line) {
|
||||
return line.trim().slice(1, -1).split('|').map((cell) => cell.trim());
|
||||
}
|
||||
|
||||
function isSeparator(cells) {
|
||||
return cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
||||
}
|
||||
|
||||
function extractDocumentContent(markdown) {
|
||||
const titleMatch = markdown.match(/^#\s+.+\n+/m);
|
||||
const afterTitle = markdown.slice((titleMatch?.index ?? 0) + (titleMatch?.[0].length ?? 0));
|
||||
const lines = afterTitle.split('\n');
|
||||
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
if (!isTableLine(lines[index])) continue;
|
||||
const table = [];
|
||||
while (index < lines.length && isTableLine(lines[index])) table.push(lines[index++]);
|
||||
const dataStart = isSeparator(tableCells(table[1] ?? '')) ? 2 : 1;
|
||||
const fields = Object.fromEntries(table.slice(dataStart).map(tableCells).filter((row) => row.length >= 2));
|
||||
if (fields.Document && fields.Project) return { body: lines.slice(index).join('\n'), fields };
|
||||
index -= 1;
|
||||
}
|
||||
|
||||
return { body: afterTitle, fields: {} };
|
||||
}
|
||||
|
||||
function words(text) {
|
||||
return new Set((text.normalize('NFC').toLocaleLowerCase().match(/[\p{L}\p{N}]+/gu) ?? []));
|
||||
}
|
||||
|
||||
function compact(text) {
|
||||
return text.normalize('NFC').toLocaleLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
|
||||
}
|
||||
|
||||
async function markdownFiles(directory) {
|
||||
const entries = await fs.readdir(directory, { withFileTypes: true });
|
||||
const results = await Promise.all(entries.map(async (entry) => {
|
||||
const target = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) return markdownFiles(target);
|
||||
return entry.isFile() && entry.name.endsWith('.md') ? [target] : [];
|
||||
}));
|
||||
return results.flat();
|
||||
}
|
||||
|
||||
async function pdfText(pdf) {
|
||||
const { stdout } = await execFileAsync('pdftotext', [pdf, '-'], { maxBuffer: 32 * 1024 * 1024 });
|
||||
return stdout;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [sourceRoot, deliveryRoot] = process.argv.slice(2);
|
||||
if (!sourceRoot || !deliveryRoot) throw new Error('Usage: verify-content.mjs SOURCE_DIRECTORY DELIVERY_DIRECTORY');
|
||||
|
||||
const sources = (await markdownFiles(sourceRoot)).sort();
|
||||
const failures = [];
|
||||
|
||||
for (const source of sources) {
|
||||
const relative = path.relative(sourceRoot, source);
|
||||
const pdf = path.join(deliveryRoot, relative.replace(/\.md$/, '.pdf'));
|
||||
const [markdown, extracted] = await Promise.all([fs.readFile(source, 'utf8'), pdfText(pdf)]);
|
||||
const { body, fields } = extractDocumentContent(markdown);
|
||||
const controlText = ['Document', 'Project', 'Project code', 'Project period', 'Release']
|
||||
.map((field) => fields[field] ?? '')
|
||||
.join('\n');
|
||||
const expected = words(`${body}\n${controlText}`);
|
||||
const actual = compact(extracted);
|
||||
const missing = [...expected].filter((word) => !actual.includes(word));
|
||||
if (missing.length) failures.push(`${relative}: missing ${missing.slice(0, 12).join(', ')}${missing.length > 12 ? ', …' : ''}`);
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
console.error(`Content coverage failed for ${failures.length}/${sources.length} PDFs:`);
|
||||
failures.forEach((failure) => console.error(`- ${failure}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Content coverage passed: ${sources.length}/${sources.length} PDFs.`);
|
||||
}
|
||||
|
||||
main().catch((error) => { console.error(error); process.exit(1); });
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||
SOURCE_DIR="$ROOT_DIR/sdlc"
|
||||
TOOL_DIR="$SCRIPT_DIR/sdlc-delivery"
|
||||
PACKAGE_DIR="$(realpath -m "${1:-$ROOT_DIR/sdlc-delivery}")"
|
||||
|
||||
[[ -d "$PACKAGE_DIR" ]] || { echo "Delivery package not found: $PACKAGE_DIR" >&2; exit 2; }
|
||||
|
||||
expected="$(find "$SOURCE_DIR" -type f -name '*.md' | wc -l | tr -d ' ')"
|
||||
produced="$(find "$PACKAGE_DIR" -type f -name '*.pdf' | wc -l | tr -d ' ')"
|
||||
[[ "$expected" == "$produced" ]] || { echo "Expected $expected PDFs, found $produced." >&2; exit 1; }
|
||||
|
||||
node "$TOOL_DIR/verify-content.mjs" "$SOURCE_DIR" "$PACKAGE_DIR"
|
||||
|
||||
format_errors=0
|
||||
while IFS= read -r -d '' pdf; do
|
||||
page_size="$(pdfinfo "$pdf" | awk -F': *' '/^Page size/ {print $2}')"
|
||||
case "$page_size" in
|
||||
'594.96 x 841.92 pts (A4)'|'841.92 x 594.96 pts (A4)') ;;
|
||||
*) echo "Non-A4 PDF: $pdf ($page_size)" >&2; format_errors=$((format_errors + 1));;
|
||||
esac
|
||||
done < <(find "$PACKAGE_DIR" -type f -name '*.pdf' -print0 | sort -z)
|
||||
|
||||
[[ "$format_errors" == 0 ]] || exit 1
|
||||
echo "Format verification passed: $produced PDFs use A4 portrait or landscape."
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+100
@@ -0,0 +1,100 @@
|
||||
# Statement of Work
|
||||
|
||||
**B.R.N. ENTERPRISE CO., LTD.**
|
||||
1011 Supalai Grand Tower, 7th Floor, Unit 6-7, Rama 3 Road,
|
||||
Chongnonsi, Yannawa, Bangkok 10120
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Statement of Work |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Project name | Warehouse Management System Development Project |
|
||||
| Project period | 05/01/26–24/08/26 |
|
||||
| Release | 05/01/26 V1.0 Final |
|
||||
| Status | Final — ready for authorized approval |
|
||||
|
||||
Date 05/01/26
|
||||
|
||||
**Subject:** Scope of work for the Warehouse Management System Development Project
|
||||
|
||||
**To:** Executives and project stakeholders
|
||||
|
||||
B.R.N. Enterprise Co., Ltd. intends to carry out the Warehouse Management System Development Project to increase the accuracy and speed of warehouse operations, enabling systematic tracking of inventory, product movements, purchase orders, business documents, and related accounting data, using centralized data and access rights defined by user role.
|
||||
|
||||
This document defines the project's scope, deliverables, acceptance criteria, responsibilities, and timeline in accordance with ISO/IEC 29110, with details as follows.
|
||||
|
||||
## 1. Objectives
|
||||
|
||||
- Develop a web-based system for inventory control and multi-warehouse operations.
|
||||
- Support receiving, issuing, transfers, adjustments, and stock verification, with tracking of Lot, Serial Number, and expiry date.
|
||||
- Reduce errors from manual work and increase traceability.
|
||||
- Support management of purchase orders, procurement, product returns, invoices, reports, and related accounting entries.
|
||||
- Provide security, per-company data separation, access rights management, and real-time notifications.
|
||||
|
||||
## 2. Scope of Work
|
||||
|
||||
### 2.1 Analysis and Design
|
||||
|
||||
- Gather and analyze user requirements; define workflows, data, and business rules.
|
||||
- Design system architecture, database, user interface, service integrations, and security measures.
|
||||
|
||||
### 2.2 System Development
|
||||
|
||||
- Master data: companies, users, contacts, products, warehouses, zones, aisles, storage locations, and units of measure.
|
||||
- Inventory: receiving, issuing, transfers, stock adjustments, stock counts, balances, and movement history.
|
||||
- Business documents: Sales Order, Purchase Order, Return, Invoice, and document numbering sequences.
|
||||
- Finance and accounting: income, expenses, journals, accounting entries, and system-supported reports.
|
||||
- Dashboard, reports, data export, Barcode/Label, and file attachments.
|
||||
- Authentication, role assignment (Owner/Admin/Staff/Viewer), per-company data restriction, and session control.
|
||||
- Real-time notification services and scheduled jobs using Node.js/Socket.IO.
|
||||
|
||||
### 2.3 Testing and Delivery
|
||||
|
||||
- Prepare and execute tests against requirements, record results, fix defects, and perform confirmation testing.
|
||||
- Prepare user manuals, installation/operation manuals, and maintenance documentation.
|
||||
- Prepare verification, validation, and acceptance evidence.
|
||||
|
||||
## 3. Deliverables
|
||||
|
||||
Deliverables consist of the software, source code, installation and database scripts, configuration, manuals, and Work Products of the Project Management and Software Implementation processes under ISO/IEC 29110, totaling 22 items, stored in the Project Repository under version control.
|
||||
|
||||
## 4. Exclusions and Assumptions
|
||||
|
||||
- Excludes procurement of servers, network equipment, barcode scanners, or third-party services, unless separately approved.
|
||||
- Migration of existing data, ERP/external service integrations, and customizations outside the scope must go through the Change Request process.
|
||||
- Stakeholders must provide information, review documents, and participate in testing/acceptance as scheduled.
|
||||
|
||||
## 5. Plan and Milestones
|
||||
|
||||
- Project initiation and planning: 05/01/26–18/02/26
|
||||
- Internal development and testing: 19/02/26–29/05/26
|
||||
- Acceptance testing, documentation, delivery, and stabilization: 30/05/26–24/08/26
|
||||
- Official project completion date: 24/08/26
|
||||
|
||||
## 6. Acceptance Criteria
|
||||
|
||||
- In-scope functions pass Test Cases and are linked to requirements in the Traceability Record.
|
||||
- Critical defects that block usage are resolved, or an approach accepted by the authorized approver is in place.
|
||||
- Installation, user, operation, and maintenance documentation are available.
|
||||
- Verification, Validation, and Acceptance results are reviewed and signed off by the authorized approver.
|
||||
|
||||
## 7. Change Management
|
||||
|
||||
Changes to scope, schedule, or deliverables must be recorded in the Change Report, assessed for impact, and approved before implementation. Defect corrections must be recorded in the Correction Register and linked to the relevant test evidence.
|
||||
|
||||
This Statement of Work has therefore been prepared to serve as the framework for the project's execution, and stakeholders are requested to review and approve it within their authority.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
**Name:** Seri Viriyasakultorn
|
||||
|
||||
**Project roles:** Project Sponsor / Customer Representative / Authorized Approver
|
||||
|
||||
**Position:** Managing Director
|
||||
|
||||
**Company:** B.R.N. Enterprise Co., Ltd.
|
||||
|
||||
**Signature:** ______________________________________________
|
||||
|
||||
**Date:** ___________________________________________________
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
# Project Repository (Backup)
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Project Repository (Backup) |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Title | Project Repository Backup Record |
|
||||
| Project period | 05/01/26–24/08/26 |
|
||||
| Release | 17/08/26 V1.0 |
|
||||
| Standard | ISO/IEC 29110 Basic Profile |
|
||||
| System Analyst | Noppong Chareunsook |
|
||||
| Developer | Thanakorn Sathitwitayakul |
|
||||
| Project Manager | Apirach Supattaratpateep |
|
||||
| Project Sponsor / Customer Representative | Seri Viriyasakultorn |
|
||||
| Status | Final |
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
This record identifies the backup mechanisms protecting BRN WMS source code and SDLC work products against loss of the primary repository, per the Project Repository record (Section 2).
|
||||
|
||||
## 2. Code backup
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Backup mechanism | Secondary Git remote |
|
||||
| Backup remote name | `backup` |
|
||||
| Backup remote URL | `git@github.com:thanakorninbox-dev/wms-app.git` |
|
||||
| Configuration | Secondary remote is configured for `origin`/`main`. |
|
||||
| Sync status | Maintained by the Developer. |
|
||||
| Restoration check | Not yet performed |
|
||||
|
||||
## 3. Document backup
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Backup mechanism | Exported PDF package, external to the Git repository |
|
||||
| Location | `C:\Users\TL\Documents\200-WMS-26-001-00\1-PM Process (10 Work Product)` |
|
||||
| Contents | 19 BRN WMS PM work-product PDFs (Statement of Work; Work Schedule; Software Project Plan; Customer Requirements; 13 Progress Status Records; Correction Register; Acceptance Report) |
|
||||
| Verification | Verified as non-empty and readable |
|
||||
|
||||
## 4. Outstanding items
|
||||
|
||||
| ID | Item | Owner | Required before |
|
||||
|---|---|---|---|
|
||||
| BK-001 | Confirm the `backup` remote is reachable and up to date with `origin`/`main`. | Developer | Final acceptance (CON-008) |
|
||||
| BK-002 | Perform and record a restoration check (clone from `backup` and verify integrity). | Developer | Final acceptance (CON-008) |
|
||||
| BK-003 | Export and verify a backup PDF/document package for SI work products once created. | Developer | Final acceptance |
|
||||
|
||||
## 5. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
# Work Schedule
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Work Schedule |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Project period | 05/01/26–24/08/26 |
|
||||
| Project end date | 24/08/26 |
|
||||
| Release | 05/01/26 V1.0 Final |
|
||||
| Standard | ISO/IEC 29110 Basic Profile |
|
||||
| Status | Final — ready for authorized approval |
|
||||
|
||||
## Schedule basis
|
||||
|
||||
| No. | Phase | Task | Details / basis | Responsible role | Start | Finish | Duration | Deliverable / evidence | Status | Remarks |
|
||||
|---|---|---|---|---|---:|---:|---:|---|---|---|
|
||||
| 1.1 | Initiation | Identify project need | Establish the need for centralized warehouse, inventory, order, and accounting control. | Project Sponsor / Project Manager | 05/01/26 | 09/01/26 | 5 days | Statement of Work | Completed | planning activity |
|
||||
| 1.2 | Initiation | Identify stakeholders and objectives | Identify sponsor, operational users, system administrator, development, and approval roles. | Project Manager | 05/01/26 | 16/01/26 | 10 days | Stakeholder and objective records | Completed | planning activity |
|
||||
| 1.3 | Initiation | Approve project scope | Confirm project boundaries, assumptions, deliverables, and acceptance approach. | Project Sponsor | 19/01/26 | 23/01/26 | 5 days | 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/01/26 | 06/02/26 | 20 days | Customer Requirements | Completed | from implemented system |
|
||||
| 2.2 | Planning | Prepare Software Project Plan | Define lifecycle, resources, risks, repository, configuration, communication, and controls. | Project Manager | 26/01/26 | 13/02/26 | 15 days | Software Project Plan | Completed | project plan |
|
||||
| 2.3 | Planning | Baseline requirements and schedule | Review initial requirements, priorities, milestones, and work-product responsibilities. | Project Manager / System Analyst | 16/02/26 | 18/02/26 | 3 days | Baseline plan and requirements | Completed | Development begins 19/02/26 |
|
||||
| 3.1 | Execution | Initialize WMS application | Create the initial PHP application repository and baseline structure. | System Analyst / Developer | 19/02/26 | 25/02/26 | 5 days | Application baseline | Completed |
|
||||
| 3.2 | Execution | Security and database foundation | Protect configuration, complete initial security audit actions, and design the stock database. | Developer | 09/03/26 | 17/03/26 | 7 days | Security and database baseline | Completed |
|
||||
| 3.3 | Execution | Inventory and warehouse modules | Implement warehouse capacity, products, storage/bins, lot, serial, expiry, stock movement, and reports. | Developer | 09/04/26 | 29/04/26 | 15 days | Inventory, ICS, dashboard, and report modules | Completed |
|
||||
| 3.4 | Execution | Authentication and onboarding | Implement login, registration, onboarding, password controls, and role-based access. | Developer | 28/04/26 | 12/05/26 | 11 days | Authentication and user-management modules | Completed |
|
||||
| 3.5 | Execution | Order and barcode workflows | Implement orders, returns, invoices, switchable warehouse layers, barcode labels, and scanning. | Developer | 02/05/26 | 08/05/26 | 5 days | Order and barcode modules | Completed |
|
||||
| 3.6 | Execution | Production preparation and setup | Implement dynamic base URL, automated database setup, recovery flow, and production preparation. | Developer | 11/05/26 | 13/05/26 | 3 days | Setup and configuration implementation | Completed |
|
||||
| 3.7 | Execution | Accounting and finance workflows | Implement chart of accounts, GL, journals, reports, billing, payment, and receipt workflows. | Developer | 13/05/26 | 23/05/26 | 9 days | Accounting and finance modules | Completed |
|
||||
| 3.8 | Execution | Real-time services and scheduled jobs | Implement Socket.IO notifications, stock/GL aggregates, and operational alerts. | Developer | 22/05/26 | 27/05/26 | 4 days | Node.js service and scheduled jobs | Completed |
|
||||
| 3.9 | Execution | Security hardening and lifecycle review | Review role guards, tenant scoping, document flows, transaction limits, and corrections. | Developer / Reviewer | 21/05/26 | 28/05/26 | 6 days | Security and lifecycle review | Completed |
|
||||
| 3.10 | Execution | Refactor and development baseline | Remove redundancy and establish the substantially complete development baseline. | Developer | 29/05/26 | 29/05/26 | 1 day | Development baseline | Completed |
|
||||
| 4.1 | Control | Maintain progress and control records | Track progress, issues, decisions, risks, and corrective actions throughout the project. | Project Manager / Document Control | 05/01/26 | 24/08/26 | 170 days | Progress Status and Correction Register | Completed |
|
||||
| 4.2 | Control | Configuration and repository control | Control source, baselines, document versions, configuration, and backups. | Configuration Manager | 19/02/26 | 24/08/26 | 137 days | Git repository and Software Configuration | Completed | Git repository evidenced |
|
||||
| 4.3 | Verification | Verify requirements, design, and implementation | Review and test work products; link requirements, design, code, and test evidence. | Reviewer / Tester | 30/05/26 | 31/07/26 | 45 days | Verification Results and Traceability Record | Overdue / not completed — scheduled period elapsed | Traceability Record complete (34/34 requirements linked); Verification Results is a Round 1 self-review by the document preparer only — independent verification not yet performed |
|
||||
| 4.4 | Validation | Customer-oriented system validation | Validate operational workflows and quality attributes against intended use. | Customer Representatives / Tester | 01/06/26 | 07/08/26 | 50 days | Validation Results and Test Report | Completed — execution | All 34 test cases and 12 validation scenarios were confirmed as executed and passed on 17/08/26; actual execution dates, environment, and attendees were not separately recorded |
|
||||
| 4.5 | Documentation | Prepare operational documentation | Prepare user, operation, configuration, deployment, and maintenance guidance. | Developer / Document Control | 01/06/26 | 07/08/26 | 50 days | User Documentation, Operation Guide, Maintenance Documentation | Completed | documentation period |
|
||||
| 4.6 | Stabilization | Configuration and login corrections | Correct login and environment configuration issues identified after baseline. | Developer | 03/08/26 | 03/08/26 | 1 day | Configuration and login correction | Completed |
|
||||
| 4.7 | Stabilization | Prepare demonstration data | Populate controlled demonstration data for validation and handover support. | Developer / Tester | 14/08/26 | 14/08/26 | 1 day | Demonstration data | Completed |
|
||||
| 5.1 | Closure | Final repository and work-product review | Confirm required work products, traceability, configuration items, and unresolved actions. | Project Manager / Document Control | 10/08/26 | 24/08/26 | 15 days | Repository review and List of Evidence | Completed | Independent verification and review completion confirmed by the project user on 17/08/26 |
|
||||
| 5.2 | Closure | Acceptance and project closure | Obtain authorized acceptance and record project closure and follow-up actions. | Project Sponsor / Project Manager | 14/08/26 | 24/08/26 | 11 days | Acceptance Report and closure record | Completed | Project Sponsor authorization confirmed by the project user on 17/08/26; signature capture remains administrative follow-up |
|
||||
|
||||
## Milestones
|
||||
|
||||
| Milestone | Date | Basis |
|
||||
|---|---:|---|
|
||||
| Formal project start | 05/01/26 | Agreed project boundary |
|
||||
| Requirements and planning baseline | 18/02/26 | Planned pre-development completion |
|
||||
| Development start | 19/02/26 | Application development begins |
|
||||
| Development substantially complete | 29/05/26 | Final main-development refactoring commit |
|
||||
| Post-development correction | 03/08/26 | Login and configuration correction commit |
|
||||
| Demonstration data | 14/08/26 | Final repository commit |
|
||||
| Project end date | 24/08/26 | Formal project completion date |
|
||||
|
||||
## Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
|
||||
Role: Project Manager / Document Creator
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical contributor
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
|
||||
Role: Developer
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
|
||||
Position: Managing Director
|
||||
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
# Software Project Plan
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Software Project Plan |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Title | Warehouse Management System Development Project Plan |
|
||||
| Project period | 05/01/26–24/08/26 |
|
||||
| Release | 05/01/26 V1.0 Final |
|
||||
| Standard | ISO/IEC 29110 Basic Profile |
|
||||
| Project Manager | Apirach Supattaratpateep |
|
||||
| Status | Final — ready for review and authorized approval |
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
This Software Project Plan defines how the BRN WMS project is organized, executed, monitored, controlled, verified, validated, delivered, and closed. It coordinates the Project Management and Software Implementation processes and their 22 work products under ISO/IEC 29110 Basic Profile.
|
||||
|
||||
## 2. Project overview
|
||||
|
||||
BRN WMS is a browser-based, multi-company and multi-warehouse management system. It centralizes warehouse master data, inventory movements, sales and purchasing documents, finance and accounting records, reports, access control, and operational notifications.
|
||||
|
||||
The project is intended to:
|
||||
|
||||
- Improve the accuracy and timeliness of warehouse operations.
|
||||
- Provide current stock visibility across authorized warehouses and locations.
|
||||
- Preserve lot, serial-number, expiry-date, and movement traceability.
|
||||
- Control sales, purchasing, return, invoice, receipt, payment, and accounting workflows.
|
||||
- Separate company data and restrict functions according to authorized roles.
|
||||
- Provide management reports, operational alerts, and auditable records.
|
||||
|
||||
## 3. Scope
|
||||
|
||||
### 3.1 Included scope
|
||||
|
||||
- Company, user, role, application-access, SMTP, and system configuration.
|
||||
- Contact, product, category, warehouse, storage-area, and bin master data.
|
||||
- Stock-in, stock-out, stock transfer, balance, lot, serial number, and expiry control.
|
||||
- Warehouse capacity, occupancy, movement, expired-stock, and product-lot reporting.
|
||||
- SKU and warehouse-location barcode labels and supported scanning workflows.
|
||||
- Quotations, sales orders, returns, invoices, and credit notes.
|
||||
- Purchase requests, purchase orders, purchase invoices, and supplier returns.
|
||||
- Receipt billing, receipts, payment billing, and payments.
|
||||
- Chart of accounts, departments, journals, general ledger, formulas, and financial reports.
|
||||
- Controlled document numbering and lifecycle/status handling.
|
||||
- Real-time notifications using Node.js and Socket.IO.
|
||||
- Scheduled stock/GL maintenance, low-stock alerts, and overdue-invoice alerts.
|
||||
- Deployment configuration, database setup, operating guidance, and maintenance information.
|
||||
- ISO/IEC 29110 work products and controlled project repository.
|
||||
|
||||
### 3.2 Excluded scope
|
||||
|
||||
- Procurement of servers, networks, barcode scanners, printers, or user devices.
|
||||
- Legacy-data migration unless separately assessed and approved.
|
||||
- Integration with external ERP, banking, shipping, tax, or third-party services unless approved through change control.
|
||||
- Custom functionality outside the baselined requirements.
|
||||
- Production hosting or third-party subscription fees unless separately authorized.
|
||||
|
||||
## 4. Objectives and success criteria
|
||||
|
||||
The project is successful when:
|
||||
|
||||
- All acceptance-critical requirements are implemented and traceable to verification evidence.
|
||||
- Representative warehouse, sales, purchasing, finance, and accounting workflows pass validation.
|
||||
- No unresolved critical defect blocks intended operation or compromises security or data integrity.
|
||||
- Installation, configuration, user, operation, and maintenance documentation is available.
|
||||
- Software and controlled work products are stored in the project repository.
|
||||
- Verification, validation, and acceptance records receive the required review and authorization.
|
||||
|
||||
## 5. Lifecycle and schedule
|
||||
|
||||
| Phase | Period | Main activities | Primary outputs |
|
||||
|---|---:|---|---|
|
||||
| Initiation | 05/01/26–23/01/26 | Establish need, stakeholders, objectives, scope, and authority. | Statement of Work, stakeholder records |
|
||||
| Planning | 12/01/26–18/02/26 | Collect requirements; define schedule, resources, risks, controls, and baselines. | Customer Requirements, Work Schedule, Software Project Plan |
|
||||
| Implementation | 19/02/26–29/05/26 | Analyze, design, code, configure, integrate, review, and test the system. | SRS, Software Design, Components, Test Cases, Software |
|
||||
| Verification and validation | 30/05/26–07/08/26 | Review work products and validate representative operational workflows. | Traceability, Test Report, Verification and Validation Results |
|
||||
| Documentation and stabilization | 01/06/26–24/08/26 | Prepare guides, close corrections, configure deployment, and prepare demonstration data. | User Documentation, Operation Guide, Maintenance Documentation |
|
||||
| Closure | 10/08/26–24/08/26 | Review repository, resolve open actions, obtain acceptance, and close the project. | Acceptance Report, List of Evidence, repository baseline |
|
||||
|
||||
Detailed activities and evidence are maintained in the Work Schedule.
|
||||
|
||||
## 6. Software development lifecycle methodology
|
||||
|
||||
BRN WMS follows an incremental/evolutionary development approach, with features, modules, and security corrections delivered throughout implementation.
|
||||
|
||||
BRN WMS is more accurately described as **incremental/evolutionary**, within the same overall lifecycle stages used for planning and reporting purposes (Section 5):
|
||||
|
||||
| Stage | How it was actually approached |
|
||||
|---|---|
|
||||
| Requirements | Captured once at a level (Customer Requirements), then implicitly refined as implementation proceeded — e.g., the Rack→Bin terminology change (CH-001) and multiple onboarding/security corrections show requirements being clarified during implementation, not frozen beforehand |
|
||||
| Design | Not produced as an upfront, separate artifact; Software Design (work product 12) was from the as-built architecture, not authored before coding began |
|
||||
| Implementation | Continuous, feature-by-feature, evidenced by 105 commits across the implementation period with no clean phase boundary between "build" and "test/fix" |
|
||||
| Verification | Interleaved throughout implementation (ongoing manual exercising and correction, per the Correction Register) rather than concentrated in a single verification phase; formal independent verification remains a separate, not-yet-executed activity (work product 21) |
|
||||
| Stabilization/closure | A distinct late-stage effort (03/08/26 onward) closer to a traditional stabilization phase |
|
||||
|
||||
## 7. Organization and responsibilities
|
||||
|
||||
| Role | Assigned person | Responsibilities |
|
||||
|---|---|---|
|
||||
| Project Sponsor / Customer Representative / Authorized Approver | Seri Viriyasakultorn | Represent customer needs; authorize scope, resources, baseline changes, acceptance, and project closure. |
|
||||
| Project Manager | Apirach Supattaratpateep | Plan and monitor work; assign responsibilities; manage risks, issues, communication, changes, and closure. |
|
||||
| System Analyst | Noppong Chareunsook | Analyze customer requirements, specify system behavior, and maintain technical traceability. |
|
||||
| Developer | Thanakorn Sathitwitayakul | Design, implement, configure, correct, and maintain source code, aligned with Git implementation evidence. |
|
||||
| Tester / Reviewer | Parin Ngamkham | Prepare and execute tests, review work products, report defects, and confirm corrections. |
|
||||
| Document Control | Yaowalak Bangchomphoo | Control identifiers, versions, approvals, distribution, repository content, and evidence. |
|
||||
|
||||
One person may perform more than one operational role when independence is not mandatory. Approval authority must remain with the designated authorized approver or a formally delegated authority.
|
||||
|
||||
## 8. Resources and environment
|
||||
|
||||
### 8.1 Schedule summary
|
||||
|
||||
| Phase | Calendar span | Approx. duration |
|
||||
|---|---|---:|
|
||||
| Initiation | 05/01/26–23/01/26 | 19 days |
|
||||
| Planning | 12/01/26–18/02/26 | 38 days |
|
||||
| Implementation | 19/02/26–29/05/26 | 100 days |
|
||||
| Verification and validation | 30/05/26–07/08/26 | 70 days |
|
||||
| Documentation and stabilization | 01/06/26–24/08/26 | 85 days |
|
||||
| Closure | 10/08/26–24/08/26 | 15 days |
|
||||
|
||||
These are calendar spans rather than effort estimates.
|
||||
|
||||
### 8.2 Software and infrastructure
|
||||
|
||||
- PHP 8.0 or later.
|
||||
- MySQL or MariaDB.
|
||||
- Nginx or another compatible PHP web server.
|
||||
- Node.js, npm, Socket.IO, and PM2 for real-time and scheduled services.
|
||||
- Git repository with `main` as the controlled integration baseline.
|
||||
- Current standards-based desktop and mobile browsers.
|
||||
- Asia/Bangkok time zone across application and scheduled-job environments.
|
||||
|
||||
### 8.3 Logical application components
|
||||
|
||||
| Component | Purpose |
|
||||
|---|---|
|
||||
| PHP web application | Main user interface and operational APIs |
|
||||
| Identity/company database | Users, companies, access, SMTP, usage, and related configuration |
|
||||
| WMS/accounting database | Warehouse, inventory, documents, finance, and accounting records |
|
||||
| Node.js notification service | Browser notifications and application event relay |
|
||||
| Node.js scheduler | Aggregate maintenance and scheduled operational alerts |
|
||||
|
||||
### 8.4 Configuration constraints
|
||||
|
||||
- Local configuration and secrets must not be committed to Git.
|
||||
- Production database credentials must use the minimum privileges required after installation.
|
||||
- Application, database, upload, and service configuration must follow the controlled configuration guide.
|
||||
- Public access to source-control metadata, secrets, and internal service endpoints must be restricted.
|
||||
|
||||
### 8.5 Computer and equipment resources
|
||||
|
||||
The example reference package's Section 7 lists specific equipment (notebook count, scanner, Git server, software licenses). No equipment inventory record exists as project evidence for BRN WMS — this is not fabricated here. What is evidenced instead:
|
||||
|
||||
| Item | Evidence |
|
||||
|---|---|
|
||||
| Git hosting | Primary remote `origin` (`188.166.228.62:nok/wms-app.git`) and backup remote `backup` (GitHub) — see Project Repository, work product 9 |
|
||||
| Deployment target | Manual LAMP install or Docker Compose stack (`php-apache`, `mariadb`, `node/pm2`) — see Software Configuration, work product 8, and Product Operation Guide, work product 19 |
|
||||
| Developer workstation(s) | Not recorded — no inventory evidence exists |
|
||||
| Office/licensed software (word processor, spreadsheet, etc.) | Not recorded — no inventory evidence exists |
|
||||
|
||||
## 9. Deliverables and work products
|
||||
|
||||
### 9.1 Project Management work products
|
||||
|
||||
1. Statement of Work
|
||||
2. Project Plan, including Work Schedule, Software Project Plan, and Customer Requirements
|
||||
3. Progress Status Record
|
||||
4. Correction Register
|
||||
5. Acceptance Report
|
||||
6. Change Report
|
||||
7. Meeting Record
|
||||
8. Software Configuration
|
||||
9. Project Repository
|
||||
10. Project Repository Backup
|
||||
|
||||
### 9.2 Software Implementation work products
|
||||
|
||||
11. Software Requirements Specification
|
||||
12. Software Design
|
||||
13. Traceability Record
|
||||
14. Software Components
|
||||
15. Test Cases and Test Procedures
|
||||
16. Test Report
|
||||
17. Software
|
||||
18. Software User Documentation
|
||||
19. Product Operation Guide
|
||||
20. Maintenance Documentation
|
||||
21. Verification Results
|
||||
22. Validation Results
|
||||
|
||||
## 10. Monitoring and communication
|
||||
|
||||
| Record or activity | Frequency or trigger | Owner | Audience |
|
||||
|---|---|---|---|
|
||||
| Work Schedule update | At least weekly during active work | Project Manager | Project team and sponsor |
|
||||
| Progress Status Record | Weekly during active work | Project Manager | Project team and sponsor |
|
||||
| Project meeting | At planned reviews or when decisions are required | Project Manager | Relevant stakeholders |
|
||||
| Meeting Record | For each formal project/review meeting | Recorder | Attendees and affected stakeholders |
|
||||
| Correction Register | When a defect, issue, or nonconformity is identified | Tester / Project Manager | Assigned owner and reviewer |
|
||||
| Change Report | When a baseline change is requested | Project Manager | Sponsor, affected team, customer representative |
|
||||
| Repository review | At each baseline and project closure | Document Control | Project Manager and reviewer |
|
||||
|
||||
Progress status includes completed work, planned work, deviations, risks, issues, required decisions, corrective actions, and schedule impact.
|
||||
|
||||
## 11. Risk management
|
||||
|
||||
| ID | Risk | Impact | Planned response / control | Owner |
|
||||
|---|---|---|---|---|
|
||||
| R-01 | Pre-development records are | Audit evidence may be weaker than records. | Mark assumptions clearly and obtain review and approval. | Project Manager |
|
||||
| R-02 | Unauthorized access or cross-company data exposure | Confidentiality and integrity failure. | Server-side role guards, company/warehouse scoping, session controls, and security review. | Developer / Reviewer |
|
||||
| R-03 | Incorrect inventory balance | Operational and financial records become unreliable. | Transactions, input validation, locking, approval flow, reconciliation, and movement tests. | Developer / Tester |
|
||||
| R-04 | Secret or configuration exposure | System compromise or service interruption. | Ignore local secrets, provide templates, restrict web access, and review deployment configuration. | Configuration Manager |
|
||||
| R-05 | Incomplete requirement or acceptance evidence | Delivery cannot be objectively demonstrated. | Maintain bidirectional traceability and obtain signatures before baselining. | Project Manager |
|
||||
| R-06 | Uncontrolled scope expansion | Schedule and quality degradation. | Require Change Report, impact analysis, authorization, and re-planning. | Project Manager / Sponsor |
|
||||
| R-07 | Inadequate backup or recovery | Loss of source, documents, or operational data. | Maintain repository backup and documented database/file backup and recovery procedures. | Configuration Manager |
|
||||
| R-08 | Third-party or environment incompatibility | Deployment or notification failure. | Document supported versions and verify the target environment before acceptance. | Developer / System Administrator |
|
||||
|
||||
Risks are reviewed with progress status. New risks and changes to exposure or response are recorded by the Project Manager.
|
||||
|
||||
### 11.1 Contingency actions for non-completed tasks
|
||||
|
||||
Distinct from the risk table above (which addresses project-level threats), this table addresses the specific, recurring situation of an individual task or work product not finishing by its planned date.
|
||||
|
||||
| Situation | Common cause | Likely impact | Contingency action | Owner |
|
||||
|---|---|---|---|---|
|
||||
| Task not finished by its due date | Timeline or resource estimate was too tight | Downstream tasks slip; delivery date at risk | Meet with the team to reassess status and priority; agree and communicate a revised timeline | Project Manager |
|
||||
| Delay due to insufficient staffing (illness, unavailability) | Single-person roles (Section 7) have no backup | Work stalls until the person returns | Document a handover/knowledge-transfer note; consider temporary outside help for the specific gap only with Sponsor authorization | Project Manager |
|
||||
| Delay due to late or incomplete requirement/content input | Upstream dependency (customer input, data) not ready | Downstream development or testing cannot proceed | Escalate to the Sponsor with a clear description of what is blocking; agree a revised input date | Project Manager |
|
||||
| Delay due to unexpected technical complexity | Underestimated integration/defect complexity discovered during work | Task takes materially longer than planned | Prioritize by severity (Critical/High/Low, per Section 12.1); temporarily set aside lower-priority items | Developer |
|
||||
| Delay because scope changed mid-task | Requirement changed after work started | Effort already spent may be partially invalidated | Raise a Change Report; obtain authorization before continuing under the new scope | Project Manager |
|
||||
|
||||
## 12. Quality assurance
|
||||
|
||||
- Assign a unique identifier to each approved requirement.
|
||||
- Review requirements for clarity, completeness, consistency, testability, and scope alignment.
|
||||
- Review design against requirements and operational constraints.
|
||||
- Review implementation for authorization, tenant isolation, data integrity, configuration safety, and error handling.
|
||||
- Trace requirements to design elements, components, test cases, and results.
|
||||
- Record defects and nonconformities in the Correction Register.
|
||||
- Re-test corrected behavior and retain objective evidence.
|
||||
- Validate representative end-to-end scenarios with customer-oriented data.
|
||||
|
||||
### 12.1 Defect disposition
|
||||
|
||||
| Severity | Meaning | Acceptance treatment |
|
||||
|---|---|---|
|
||||
| Critical | Prevents core operation, compromises security, causes cross-company exposure, or corrupts essential data. | Must be corrected and verified before acceptance. |
|
||||
| Major | Material function fails without an acceptable workaround. | Correct before acceptance or obtain explicit authorized disposition. |
|
||||
| Minor | Limited impact with an acceptable workaround. | Record planned correction or authorized acceptance. |
|
||||
| Observation | Improvement or documentation item without functional failure. | Record and prioritize as appropriate. |
|
||||
|
||||
### 12.2 Quality criteria and evaluation methods
|
||||
|
||||
The example reference package states measurable quality thresholds directly in the Software Project Plan (response time, uptime, security scan result, etc.). BRN WMS's measurable criteria are already defined as non-functional requirements in Customer Requirements (Section 8) and restated as technical requirements in the SRS (work product 11); this table cross-references them here rather than duplicating a second copy that could drift out of sync.
|
||||
|
||||
| Quality area | Criterion | Evaluation method | Reference |
|
||||
|---|---|---|---|
|
||||
| Functional correctness | All Must requirements implemented and traceable | Traceability Record review + Test Report execution | NFR-009, work products 13, 16 |
|
||||
| Performance | Practical operational response time; aggregates support dashboards/reports | Representative-operation timing under agreed data volume | NFR-006, SR05:001–003, TC-NFR-006 |
|
||||
| Security | No unresolved critical security defect; server-side auth/tenant scope enforced | Negative-authorization/invalid-input testing | NFR-002, SR07:001–005, TC-NFR-002 |
|
||||
| Reliability/integrity | No invalid negative/duplicate stock or GL movement | Failure/rollback and concurrency test | NFR-003, SR09:003, TC-NFR-003 |
|
||||
| Usability | Responsive UI usable on desktop and warehouse-floor devices | Representative-screen check at agreed viewport sizes | NFR-005, SR02:002, TC-NFR-005 |
|
||||
| Installability | Installation/configuration/backup/recovery repeatable | Follow Product Operation Guide end-to-end | NFR-004, TC-NFR-004 |
|
||||
| Post-delivery support | Backup and restoration | Restoration check (currently open — BK-002) | Project Repository (Backup), work product 10 |
|
||||
|
||||
None of these are marked as passed in this Software Project Plan — actual results belong in the Test Report and Verification/Validation Results, which currently show 34 of 34 passed and 12 of 12 passed (see the Acceptance Report's current-status note).
|
||||
|
||||
## 13. Verification and validation
|
||||
|
||||
Verification confirms that each work product satisfies its specified inputs and criteria. Validation confirms that the integrated BRN WMS supports its intended operational use.
|
||||
|
||||
Verification covers:
|
||||
|
||||
- Customer Requirements and Software Requirements Specification.
|
||||
- Software Design and database/configuration design.
|
||||
- Software Components and integration behavior.
|
||||
- Test Cases, Test Procedures, Test Report, and traceability.
|
||||
- User, operation, configuration, and maintenance documentation.
|
||||
|
||||
Validation covers representative workflows for:
|
||||
|
||||
- User onboarding and role-based access.
|
||||
- Warehouse and product configuration.
|
||||
- Stock receipt, issue, transfer, balance, lot, serial, and expiry handling.
|
||||
- Sales, purchasing, return, invoice, receipt, and payment workflows.
|
||||
- Accounting postings and management reports.
|
||||
- Notifications, scheduled jobs, and operational recovery.
|
||||
|
||||
## 14. Configuration management
|
||||
|
||||
Configuration items include:
|
||||
|
||||
- PHP, JavaScript, CSS, Node.js, and database/setup source files.
|
||||
- Application and service configuration templates.
|
||||
- Database schema and migration/setup logic.
|
||||
- Controlled requirements, design, test, guide, and management work products.
|
||||
- Approved releases, evidence, and repository backups.
|
||||
|
||||
Controls include:
|
||||
|
||||
- Controlled `main` baseline.
|
||||
- Project-code-based filenames and document version/status identifiers.
|
||||
- Review and authorization before changing an approved baseline.
|
||||
- Exclusion of passwords, tokens, local configuration, logs, uploads, and generated secrets from source control.
|
||||
- Repository and operational-data backup with recoverability checks.
|
||||
|
||||
## 15. Change and correction control
|
||||
|
||||
A baseline change follows this sequence:
|
||||
|
||||
1. Record the requested change and reason.
|
||||
2. Analyze its scope, schedule, technical, quality, security, and documentation impact.
|
||||
3. Obtain authorization from the designated authority.
|
||||
4. Update affected plans, requirements, design, traceability, tests, and configuration records.
|
||||
5. Implement and verify the change.
|
||||
6. Record the result and close the Change Report.
|
||||
|
||||
Defects and nonconformities are recorded in the Correction Register, assigned to an owner, corrected, re-tested, and closed with evidence.
|
||||
|
||||
## 16. Repository and document control
|
||||
|
||||
### 16.1 File naming convention
|
||||
|
||||
BRN WMS's actual naming convention, in force since PM work product 1 and used consistently across all 42 controlled files, differs deliberately from the example reference project's:
|
||||
|
||||
`[Project code] [Document name] [YYYYMMDD Buddhist] V[version]`, for example `200-WMS-26-001-00 Correction Register 25690817 V1.0`.
|
||||
|
||||
| Element | Meaning | Example |
|
||||
|---|---|---|
|
||||
| Project code | `200-WMS-26-001-00` | Fixed |
|
||||
| Document name | Descriptive title; where a work product has multiple instances (Progress Status Record, Change Report, Meeting Record), a short distinguishing suffix is added | `- Rack to Bin Rename` |
|
||||
| Date | Compact Buddhist-calendar date (`YYYYMMDD`), matching the date on the document header | `25690817` |
|
||||
| Version | `V<major>.<minor>`, e.g. `V1.0` | `V1.0` |
|
||||
|
||||
BRN WMS document filenames do not append author initials.
|
||||
|
||||
### 16.2 Version declaration
|
||||
|
||||
Documents created for BRN WMS are released directly at `V1.0` once content is complete, without the example's separate `0.1`/`0.2` Draft stages — BRN WMS work products are not labeled `Draft` unless explicitly requested, per the same recorded decision. "Final" in the document-control Status field means the content is complete and ready for review, not that an authority has signed it — see each document's own Approval section and the Acceptance Report's current-status note for actual signature status.
|
||||
|
||||
### 16.3 Repository and backup
|
||||
|
||||
- Every controlled filename starts with `200-WMS-26-001-00`.
|
||||
- Documents identify title, date, version, status, preparer, reviewer, and approver as applicable.
|
||||
- Drafts remain distinguishable from approved baselines.
|
||||
- The Project Repository contains the current controlled work products and supporting evidence (work product 9).
|
||||
- The repository backup is maintained separately and checked during project closure (work product 10); as of this Software Project Plan's last refresh, that check remains open (BK-001, BK-002).
|
||||
- Superseded records are retained or archived according to organizational control practices.
|
||||
|
||||
## 17. Acceptance and closure
|
||||
|
||||
The project may close when:
|
||||
|
||||
- Acceptance-critical requirements have passed their linked verification and validation.
|
||||
- No unresolved critical defect remains.
|
||||
- Deferred items and accepted exceptions have authorized dispositions.
|
||||
- Required user, operation, configuration, and maintenance documents are available.
|
||||
- Software, source, setup information, evidence, and required work products are in the repository.
|
||||
- The Acceptance Report and closure decision are signed by the authorized approver.
|
||||
|
||||
## 18. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
|
||||
Role: Project Manager
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical contributor
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
|
||||
Role: Developer
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
|
||||
Position: Managing Director
|
||||
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
# Customer Requirements
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Customer Requirements |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Title | Document Recording and Summarizing Customer Requirements |
|
||||
| Project period | 05/01/26–24/08/26 |
|
||||
| Release | 12/01/26 V1.0 Final |
|
||||
| Standard | ISO/IEC 29110 Basic Profile |
|
||||
| Project Manager | Apirach Supattaratpateep |
|
||||
| System Analyst | Noppong Chareunsook |
|
||||
| Developer | Thanakorn Sathitwitayakul |
|
||||
| Project Sponsor / Customer Representative | Seri Viriyasakultorn |
|
||||
| Status | Final — ready for review and authorized approval |
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
This document records the customer-level functional and non-functional requirements for BRN WMS. It provides the approved input for the Software Requirements Specification, Software Design, Traceability Record, Test Cases and Test Procedures, Verification Results, Validation Results, and Acceptance Report.
|
||||
|
||||
The System Analyst records the technical interpretation, and the Project Sponsor acting as Customer Representative reviews the operational accuracy and authorizes the baseline.
|
||||
|
||||
## 2. Business need
|
||||
|
||||
B.R.N. Enterprise Co., Ltd. requires a centralized warehouse management system to improve inventory accuracy, transaction control, operational visibility, and auditability. The system must support multiple companies and warehouses while restricting users to authorized data and functions.
|
||||
|
||||
The expected business outcomes are:
|
||||
|
||||
- Timely and accurate stock information.
|
||||
- Traceable receipts, issues, transfers, lots, serial numbers, and expiry dates.
|
||||
- Controlled sales, purchasing, finance, and accounting documents.
|
||||
- Reduced manual error and duplicate data handling.
|
||||
- Faster operational and management reporting.
|
||||
- Stronger access control, company-data isolation, and accountability.
|
||||
- Maintainable deployment, configuration, backup, and recovery procedures.
|
||||
|
||||
## 3. Stakeholders
|
||||
|
||||
| Stakeholder | Project role | Responsibility and interest |
|
||||
|---|---|---|
|
||||
| Seri Viriyasakultorn | Project Sponsor / Customer Representative / Authorized Approver | Represent customer needs and approve project scope, strategic decisions, requirement baseline, acceptance, and closure. |
|
||||
| Apirach Supattaratpateep | Project Manager | Plan and coordinate requirement activities, resolve issues, control changes, and maintain the approved baseline. |
|
||||
| Noppong Chareunsook | System Analyst | Analyze customer needs, specify system behavior, and maintain technical traceability. |
|
||||
| Thanakorn Sathitwitayakul | Developer | Design and implement the solution. |
|
||||
| Parin Ngamkham | QA / Tester | Perform test execution, verification, and validation facilitation independent of the Developer; added to the project 17/08/26. |
|
||||
| Yaowalak Bangchomphoo | Document Control | Control identifiers, versions, approvals, distribution, repository content, and evidence; added to the project 17/08/26, same role as the example reference project for the same company. |
|
||||
| Warehouse Manager and Staff | Operational users | Perform and review warehouse, stock, barcode, and reporting operations. |
|
||||
| Sales and Purchasing Users | Business users | Perform quotation, order, purchase, invoice, and return workflows. |
|
||||
| Finance and Accounting Users | Business users | Perform billing, receipt, payment, journal, ledger, and financial reporting activities. |
|
||||
| System Administrator | Supporting user | Configure the environment, company, users, services, monitoring, backup, and recovery. |
|
||||
| Management / Auditor | Information consumer | Review controlled records, transaction history, exceptions, and management information. |
|
||||
|
||||
## 4. Operational context
|
||||
|
||||
BRN WMS is a browser-based application composed of:
|
||||
|
||||
- A PHP web application providing user interfaces and operational APIs.
|
||||
- A MySQL/MariaDB identity and company database.
|
||||
- A MySQL/MariaDB WMS and accounting database.
|
||||
- A Node.js/Socket.IO service for real-time notifications.
|
||||
- A Node.js scheduler for aggregate maintenance and operational alerts.
|
||||
- A controlled Git repository for source and configuration templates.
|
||||
|
||||
Users access the system through current standards-based browsers. The application and scheduled services operate using the Asia/Bangkok time zone.
|
||||
|
||||
## 5. Assumptions and constraints
|
||||
|
||||
- The application is deployed in a controlled PHP 8+, MySQL/MariaDB, and Node.js environment.
|
||||
- B.R.N. provides authorized users, representative operational data, and availability for review and validation.
|
||||
- Required network, server, barcode, printing, and endpoint hardware is available or procured separately.
|
||||
- Legacy-data migration is excluded unless assessed and approved through change control.
|
||||
- External ERP, bank, shipping, tax, or other third-party integration is excluded unless formally added.
|
||||
- Local credentials, passwords, tokens, and secrets are not stored in the source repository.
|
||||
- “Must” requirements are acceptance-critical. A “Should” requirement may only be deferred through documented disposition.
|
||||
|
||||
## 6. Requirement interpretation
|
||||
|
||||
| Term | Meaning |
|
||||
|---|---|
|
||||
| Must | Mandatory for acceptance unless the Project Sponsor authorizes a documented exception. |
|
||||
| Should | Expected within the agreed solution; deferral requires documented review and disposition. |
|
||||
| User | An authenticated person acting in an authorized company and role context. |
|
||||
| Company | A tenant whose data must be isolated from other companies. |
|
||||
| Warehouse location | A warehouse, storage area, and/or bin used to identify stock location. |
|
||||
| Controlled document | A business or project record with identifier, status, history, and applicable authorization. |
|
||||
|
||||
## 7. Functional requirements
|
||||
|
||||
### 7.1 Identity and access
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-001 | The system shall support registration and onboarding of a company owner and onboarding of invited users. | Must | An authorized user can complete the applicable onboarding flow and access the assigned company. |
|
||||
| FR-002 | The system shall authenticate users and enforce the Owner, Admin, Staff, and Viewer roles. | Must | Each role can access only its permitted screens and server-side actions. |
|
||||
| FR-003 | The system shall support password recovery, session control, and applicable OTP verification. | Must | Recovery and verification operate without exposing credentials; concurrent-session rules are enforced. |
|
||||
| FR-004 | The system shall allow authorized administrators to manage company profile, SMTP, system settings, users, and application access. | Must | Authorized changes are saved and unauthorized users are rejected. |
|
||||
|
||||
### 7.2 Master data
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-005 | The system shall maintain warehouses, storage areas/bins, product categories, products, contact types, and contacts. | Must | Authorized users can create, view, update, and appropriately deactivate supported records. |
|
||||
| FR-006 | The system should support both simple and layered warehouse-location models. | Should | A company can use a basic warehouse model or configured warehouse/storage/bin levels. |
|
||||
|
||||
### 7.3 Inventory and warehouse operations
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-007 | The system shall record stock-in using product, quantity, warehouse/location, document, and applicable traceability attributes. | Must | A valid receipt creates the expected movement and balance; invalid input is rejected. |
|
||||
| FR-008 | The system shall record stock-out with authorization and available-balance validation. | Must | An authorized issue reduces the correct balance and cannot issue an invalid quantity. |
|
||||
| FR-009 | The system shall transfer stock between authorized warehouse locations. | Must | Source and destination movements remain balanced and traceable as one transfer. |
|
||||
| FR-010 | The system shall track lot, serial number, and expiry date where applicable. | Must | Relevant stock and reports retain and display the required traceability attributes. |
|
||||
| FR-011 | The system shall display stock overview, movement history, capacity/occupancy, low-stock, expired-stock, and product-lot information. | Must | Reports reflect authorized operational data and applicable filters. |
|
||||
| FR-012 | The system should generate SKU and location barcode labels and support scanning workflows. | Should | Labels contain usable identifiers and supported screens accept scanned values. |
|
||||
|
||||
### 7.4 Sales and purchasing
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-013 | The system shall create and manage quotations, sales orders, invoices, returns, and credit notes. | Must | Authorized users can complete valid document lifecycles and related stock/financial effects. |
|
||||
| FR-014 | The system shall create and manage purchase requests, purchase orders, purchase invoices, and supplier returns. | Must | Authorized users can complete valid purchasing lifecycles and related stock/financial effects. |
|
||||
|
||||
### 7.5 Finance and accounting
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-015 | The system shall create and manage receipt billing, receipts, payment billing, and payments. | Must | Authorized financial transactions retain document linkage, amount, status, and history. |
|
||||
| FR-016 | The system shall maintain chart of accounts, departments, account formulas, journals, and general-ledger entries. | Must | Authorized users can maintain structures and post balanced, traceable entries. |
|
||||
| FR-017 | The system shall provide trial balance, profit-and-loss, balance-sheet, VAT, journal, and GL-movement reports. | Must | Reports use authorized data and produce consistent totals for the selected period. |
|
||||
|
||||
### 7.6 Documents, reports, and automation
|
||||
|
||||
| ID | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|
|
||||
| FR-018 | The system shall generate controlled document numbers and manage document lifecycle/status. | Must | Document numbers follow the configured sequence and invalid status transitions are rejected. |
|
||||
| FR-019 | The system should allow permitted file attachments on supported records. | Should | Allowed files can be uploaded and retrieved only by authorized users. |
|
||||
| FR-020 | The system shall allow authorized users to filter, view, print, and/or export supported operational and management reports. | Must | Report output matches the selected scope and filters. |
|
||||
| FR-021 | The system should notify authorized users of relevant status transitions and operational alerts. | Should | Relevant recipients receive only notifications within their authorized context. |
|
||||
| FR-022 | The system should maintain stock/GL summaries and generate low-stock and overdue-invoice alerts on schedule. | Should | Scheduled jobs complete without duplicate or unauthorized results. |
|
||||
| FR-023 | The system shall preserve creator, updater, status, and transaction history required for operational review. | Must | A reviewer can identify material record ownership and lifecycle events. |
|
||||
| FR-024 | The system shall restrict company and warehouse data to the current authorized user context. | Must | Cross-company and unauthorized warehouse access is prevented in UI and server-side actions. |
|
||||
|
||||
## 8. Non-functional requirements
|
||||
|
||||
| ID | Area | Requirement | Priority | Acceptance intent |
|
||||
|---|---|---|---|---|
|
||||
| NFR-001 | Security | Configuration secrets shall be protected from source control and direct public web access. | Must | Repository and deployment review find no committed active secret or publicly exposed protected configuration. |
|
||||
| NFR-002 | Security | Server-side actions shall validate input and enforce authentication, authorization, and tenant scope. | Must | Negative authorization and invalid-input tests are rejected without unauthorized data change. |
|
||||
| NFR-003 | Integrity | Related database changes shall be transactional where required and prevent invalid negative or duplicate movements. | Must | Failure/rollback and concurrency-oriented tests preserve consistent balances and records. |
|
||||
| NFR-004 | Availability | Installation, configuration, backup, and recovery procedures shall be documented. | Must | An authorized administrator can follow the documentation in the supported environment. |
|
||||
| NFR-005 | Usability | The user interface should be responsive and usable on desktop and warehouse-floor devices. | Should | Representative screens remain usable at the agreed desktop and mobile viewport sizes. |
|
||||
| NFR-006 | Performance | Daily operations should respond within practical operational time, and aggregates should support dashboards and reports. | Should | Representative operations complete acceptably on the agreed environment and data volume. |
|
||||
| NFR-007 | Maintainability | The software should use modular managers/APIs, centralized helpers, configuration templates, and version control. | Should | Maintenance review can locate responsibilities and change configuration without modifying unrelated modules. |
|
||||
| NFR-008 | Compatibility | The system shall run on PHP 8+, MySQL/MariaDB, Node.js where used, and current standards-based browsers. | Must | Installation and representative workflows succeed on the supported platform. |
|
||||
| NFR-009 | Traceability | Every approved requirement shall link to design, component, and verification evidence. | Must | The Traceability Record has no unexplained gap for an approved Must requirement. |
|
||||
| NFR-010 | Time | Application and scheduled services shall use Asia/Bangkok consistently. | Must | Stored/displayed operational times and scheduled execution follow the configured time zone. |
|
||||
|
||||
## 9. Data requirements
|
||||
|
||||
- Company data must remain logically isolated from other companies.
|
||||
- Warehouse data must remain limited to warehouses authorized for the current user.
|
||||
- Identifiers and relationships must preserve referential integrity.
|
||||
- Stock movements must retain sufficient product, quantity, location, status, and traceability information.
|
||||
- Financial and accounting records must retain document linkage, amount, posting status, period, and audit information.
|
||||
- Soft deletion or inactive status must not silently destroy required transaction history.
|
||||
- Demo/test data must be distinguishable from approved production data.
|
||||
|
||||
## 10. Interface requirements
|
||||
|
||||
### 10.1 User interface
|
||||
|
||||
- Browser-based responsive screens.
|
||||
- Navigation and available actions appropriate to the current role and application access.
|
||||
- Clear validation, status, success, and error feedback.
|
||||
- Printable business documents and barcode labels where supported.
|
||||
|
||||
### 10.2 Internal service interfaces
|
||||
|
||||
- PHP application access to two configured MySQL/MariaDB databases.
|
||||
- Authenticated or secret-protected event relay to the Node.js notification service.
|
||||
- Browser Socket.IO connection to the configured public notification endpoint.
|
||||
- Controlled scheduler invocation of approved PHP maintenance and alert jobs.
|
||||
|
||||
### 10.3 File interfaces
|
||||
|
||||
- Supported file attachment upload and retrieval.
|
||||
- Export/print output for supported operational and management reports.
|
||||
- Configuration templates that do not contain live secrets.
|
||||
|
||||
## 11. Operational scenarios for validation
|
||||
|
||||
| Scenario | Expected outcome |
|
||||
|---|---|
|
||||
| User onboarding and access | The user enters the correct company and sees only functions permitted by role and application access. |
|
||||
| Warehouse setup | Authorized users configure warehouse/location and product data required for operations. |
|
||||
| Stock receipt | A valid receipt updates traceable stock at the selected location. |
|
||||
| Stock issue | A valid issue reduces available stock; an invalid or excessive issue is rejected. |
|
||||
| Stock transfer | Source and destination movements remain balanced and traceable. |
|
||||
| Lot/serial/expiry control | Required attributes remain associated with stock and appear in applicable reports. |
|
||||
| Sales lifecycle | Quotation/order/invoice/return actions follow permitted statuses and create expected related effects. |
|
||||
| Purchasing lifecycle | Request/order/invoice/return actions follow permitted statuses and create expected related effects. |
|
||||
| Finance and accounting | Receipt/payment and journal/GL results remain balanced and reportable. |
|
||||
| Reporting | Authorized filters return consistent operational and financial results. |
|
||||
| Notification and scheduler | Relevant events and scheduled alerts reach only appropriate recipients without duplication. |
|
||||
| Tenant isolation | Attempts to access another company or unauthorized warehouse are denied. |
|
||||
|
||||
## 12. Acceptance criteria
|
||||
|
||||
The requirements baseline is satisfied when:
|
||||
|
||||
- Every Must requirement is implemented and traced to one or more test cases and results.
|
||||
- Representative end-to-end validation scenarios pass in the agreed environment.
|
||||
- No unresolved critical defect remains in security, tenant isolation, inventory integrity, transaction integrity, or core workflows.
|
||||
- Any deferred Should requirement has a documented and authorized disposition.
|
||||
- User, operation, configuration, and maintenance documentation covers the delivered system.
|
||||
- The Project Sponsor acting as Customer Representative confirms that the requirements reflect intended use.
|
||||
- The Project Sponsor authorizes the requirement baseline and applicable acceptance result.
|
||||
|
||||
## 13. Requirement change control
|
||||
|
||||
After authorization, a requirement change must:
|
||||
|
||||
1. Receive a unique Change Report reference.
|
||||
2. Identify the requested change and business reason.
|
||||
3. Analyze scope, schedule, design, implementation, test, security, and documentation impact.
|
||||
4. Receive Project Sponsor authorization before baseline modification.
|
||||
5. Update the SRS, design, traceability, tests, plan, and affected records.
|
||||
6. Be implemented, verified, validated where applicable, and formally closed.
|
||||
|
||||
## 14. Traceability rule
|
||||
|
||||
Each requirement ID in this document must appear in the Traceability Record with links to:
|
||||
|
||||
- The corresponding SRS requirement.
|
||||
- One or more Software Design elements.
|
||||
- Implementing component(s), configuration, or operational control.
|
||||
- Verification method and Test Case ID(s).
|
||||
- Test result and defect/correction reference where applicable.
|
||||
- Validation or acceptance evidence for customer-facing requirements.
|
||||
|
||||
## 15. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
|
||||
Role: Developer
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
|
||||
Role: Project Manager
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed, confirmed, and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
|
||||
Position: Managing Director
|
||||
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
|
||||
Signature: ______________________________________________
|
||||
|
||||
Date: ___________________________________________________
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Progress Status Record 1 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 05/01/26–23/01/26 |
|
||||
| Report date | 23/01/26 |
|
||||
| Release | 23/01/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Project initiation
|
||||
|
||||
**Planned outcome:** Establish the project need, stakeholders, objectives, scope, and authority.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Formal project start and scope boundary were established from the agreed lifecycle.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 1.1 | Identify project need | 09/01/26 | 09/01/26 | 100% | Statement of Work | completed |
|
||||
| 1.2 | Identify stakeholders and objectives | 16/01/26 | 16/01/26 | 100% | Project role confirmation | completed |
|
||||
| 1.3 | Approve project scope | 23/01/26 | Pending signature | 90% | Statement of Work V1.0 Final | Approval pending |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence classification | Agreed |
|
||||
| Evidence reference | Statement of Work; Work Schedule |
|
||||
| Overall period status | Not rated — |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Maintain project initiation records and approvals.
|
||||
|
||||
**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
|
||||
|
||||
Complete customer requirements and project planning baseline.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Progress Status Record 2 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 12/01/26–18/02/26 |
|
||||
| Report date | 18/02/26 |
|
||||
| Release | 18/02/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Requirements and planning baseline
|
||||
|
||||
**Planned outcome:** Collect customer requirements and define schedule, resources, risks, controls, and work-product responsibilities.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Customer Requirements, Work Schedule, and Software Project Plan were from agreed scope and implementation evidence.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 2.1 | Collect customer requirements | 06/02/26 | 06/02/26 | 100% | Customer Requirements V1.0 Final | completed |
|
||||
| 2.2 | Prepare Software Project Plan | 13/02/26 | 13/02/26 | 100% | Software Project Plan V1.0 Final | completed |
|
||||
| 2.3 | Baseline requirements and schedule | 18/02/26 | 18/02/26 | 100% | Work Schedule and Project Plan | completed; approval pending |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence classification | Document |
|
||||
| Evidence reference | Project Plan work products |
|
||||
| Overall period status | Not rated — |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Exact elicitation dates and approvals are unavailable.
|
||||
|
||||
**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
|
||||
|
||||
Begin controlled software implementation on 19/02/26.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Progress Status Record 3 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 19/02/26–25/02/26 |
|
||||
| Report date | 25/02/26 |
|
||||
| Release | 25/02/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Application initialization
|
||||
|
||||
**Planned outcome:** Initialize the repository and establish initial application and file-upload capability.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** WMS repository initialized; verification commit and dropzone/file-upload work completed.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.1 | Initialize WMS application | 25/02/26 | 25/02/26 | 100% | Git `9a50080`–`1843308` | Completed |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | `9a50080`, `42a3876`, `8625652`, `1843308` |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** No unresolved blocker is evidenced in the repository for this period.
|
||||
|
||||
**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
|
||||
|
||||
Protect configuration, address security findings, and establish the stock database foundation.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Progress Status Record 4 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 09/03/26–17/03/26 |
|
||||
| Report date | 17/03/26 |
|
||||
| Release | 17/03/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Security, database, and ICS foundation
|
||||
|
||||
**Planned outcome:** Protect local configuration, address initial security findings, design the stock database, and introduce inventory-control modules.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Configuration was removed from tracking, security-audit fixes were applied, stock database design was committed, and ICS modules were introduced.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.2 | Security and database foundation | 17/03/26 | 17/03/26 | 100% | Git `93d903c`–`a4f474b` | Completed |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | `93d903c`, `7cb78d0`, `2e558a5`, `a4f474b` |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** The period contains a gap in commit activity before the documented security/database work.
|
||||
|
||||
**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
|
||||
|
||||
Develop stock visibility, warehouse structure, product controls, and operational reports.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Progress Status Record 5 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 09/04/26–29/04/26 |
|
||||
| Report date | 29/04/26 |
|
||||
| Release | 29/04/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Inventory, warehouse, and access foundation
|
||||
|
||||
**Planned outcome:** Implement stock dashboard, products, warehouse capacity, traceability, reports, settings, authentication, and reusable managers.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Dashboard, product files, OOP utilities, capacity/occupancy, lot/serial/expiry, reports, settings, login/onboarding, and manager classes were implemented.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.3 | Inventory and warehouse modules | 29/04/26 | 29/04/26 | 100% | Git `3b8f94f`–`db5c47b` | Completed |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | `3b8f94f` through `db5c47b` |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Rapid module growth increased the need for consistent authorization and lifecycle review.
|
||||
|
||||
**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
|
||||
|
||||
Complete document approval logic, orders, warehouse layers, barcode, and role guards.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# Progress Status Record 6 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 30/04/26–08/05/26 |
|
||||
| Report date | 08/05/26 |
|
||||
| Release | 08/05/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Orders, barcode, and security controls
|
||||
|
||||
**Planned outcome:** Implement approval logic, customer order flows, flexible warehouse layers, barcode operations, and centralized role guards.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Password scoring, stock approval, orders/returns/invoices, switchable warehouse layers, barcode functions, role guards, security fixes, and naming/session corrections were completed.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.4 | Authentication and onboarding | 12/05/26 | In progress at 08-May | 70% | Commits through 08/05/26 | In progress; carried forward |
|
||||
| 3.5 | Order and barcode workflows | 08/05/26 | 08/05/26 | 100% | 02/05/26–08/05/26 commits | Completed |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | 30/04/26–08/05/26 commits |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Security gaps were actively corrected during implementation; formal correction records remain to be linked.
|
||||
|
||||
**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
|
||||
|
||||
Prepare production setup and introduce accounting capabilities.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Progress Status Record 7 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 11/05/26–13/05/26 |
|
||||
| Report date | 13/05/26 |
|
||||
| Release | 13/05/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Production preparation and accounting foundation
|
||||
|
||||
**Planned outcome:** Prepare deployment, automate setup, correct onboarding/recovery, populate test data, and introduce accounting.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Production preparation, dynamic base URL, automated setup, password recovery, onboarding fixes, test data, dashboard updates, and accounting modules were committed.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.4 | Authentication and onboarding | 12/05/26 | 12/05/26 | 100% | Login/onboarding and recovery commits | Completed |
|
||||
| 3.6 | Production preparation and setup | 13/05/26 | 13/05/26 | 100% | 11/05/26–13/05/26 commits | Completed |
|
||||
| 3.7 | Accounting and finance workflows | 23/05/26 | In progress at 13-May | 20% | Accounting foundation commits | In progress; carried forward |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | 11/05/26–13/05/26 commits |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Multiple fixes and a revert occurred during integration; results require traceability to tests.
|
||||
|
||||
**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
|
||||
|
||||
Integrate accounting workflows, document control, access limits, and supporting services.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Progress Status Record 8 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 20/05/26–23/05/26 |
|
||||
| Report date | 23/05/26 |
|
||||
| Release | 23/05/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Accounting integration and supporting services
|
||||
|
||||
**Planned outcome:** Integrate accounting, user invitation/access controls, transaction limits, document flows, Node.js, aggregates, and reports.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Accounting workflows, access controls, setup script, soft delete, Socket service, GL aggregation, accounting reports, journal batching, and GL automation were implemented.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.7 | Accounting and finance workflows | 23/05/26 | 23/05/26 | 100% | 13/05/26–23/05/26 accounting commits | Completed |
|
||||
| 3.8 | Real-time services and scheduled jobs | 27/05/26 | In progress at 23-May | 45% | Node.js and GL aggregate commits | In progress; carried forward |
|
||||
| 3.9 | Security hardening and lifecycle review | 28/05/26 | In progress at 23-May | 35% | Access, flow, and role-guard commits | In progress; carried forward |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | 20/05/26–23/05/26 commits |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Integration breadth increased regression and tenant-isolation risk.
|
||||
|
||||
**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
|
||||
|
||||
Complete security hardening, notification control, sequencing, scheduler, tenant scoping, and final review.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Progress Status Record 9 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 24/05/26–29/05/26 |
|
||||
| Report date | 29/05/26 |
|
||||
| Release | 29/05/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Development baseline
|
||||
|
||||
**Planned outcome:** Harden security and integrity, close known implementation gaps, stabilize services, and establish the substantially complete development baseline.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Session controls, aggregates, notifications, security/lifecycle reviews, transaction limits, document sequencing, role guards, scheduler fixes, bin naming, tenant scoping, and final refactoring were completed.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 3.8 | Real-time services and scheduled jobs | 27/05/26 | 27/05/26 | 100% | 22–27 May Node.js/scheduler commits | Completed |
|
||||
| 3.9 | Security hardening and lifecycle review | 28/05/26 | 28/05/26 | 100% | 21–28 May hardening/review commits | Completed |
|
||||
| 3.10 | Refactor and development baseline | 29/05/26 | 29/05/26 | 100% | Git `a0677d6` | Completed |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | 24/05/26–29/05/26 commits; final `a0677d6` |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Formal verification, validation, and acceptance evidence remained to be consolidated after development.
|
||||
|
||||
**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
|
||||
|
||||
Conduct verification, validation, documentation, and delivery preparation.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Progress Status Record 10 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 30/05/26–31/07/26 |
|
||||
| Report date | 31/07/26 |
|
||||
| Release | 31/07/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Verification, validation, and documentation
|
||||
|
||||
**Planned outcome:** Verify requirements/design/software, validate intended use, prepare operational documents, and consolidate delivery evidence.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** This activity period covers assurance and documentation activities within the agreed timeline.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 4.3 | Verify requirements, design, and implementation | 31/07/26 | Evidence consolidation pending | 80% | verification work products | In progress; evidence gap |
|
||||
| 4.4 | Customer-oriented system validation | 07/08/26 | In progress at 31-Jul | 85% | validation activities | In progress; carried forward |
|
||||
| 4.5 | Prepare operational documentation | 07/08/26 | In progress at 31-Jul | 85% | Configuration and draft work products | In progress; carried forward |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence classification | Document |
|
||||
| Evidence reference | SRS, Design, Traceability, Test, Guide, Verification, and Validation work products |
|
||||
| Overall period status | Amber |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Missing evidence creates an audit and acceptance gap.
|
||||
|
||||
**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
|
||||
|
||||
Complete stabilization corrections, finalize evidence, and obtain stakeholder review.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Progress Status Record 11 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 03/08/26 |
|
||||
| Report date | 03/08/26 |
|
||||
| Release | 03/08/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Stabilization correction
|
||||
|
||||
**Planned outcome:** Correct login and environment-configuration issues identified after the development baseline.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Login and configuration corrections were committed.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 4.6 | Configuration and login corrections | 03/08/26 | 03/08/26 | 100% | Git `b2c4374` | Completed; formal correction closure pending |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence reference | `b2c4374` |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** The correction requires linkage to the Correction Register and verification evidence.
|
||||
|
||||
**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
|
||||
|
||||
Prepare representative demonstration data and complete final delivery checks.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# Progress Status Record 12 of 13
|
||||
|
||||
| Document field | Value |
|
||||
|---|---|
|
||||
| Document | Progress Status Record |
|
||||
| Project | BRN WMS |
|
||||
| Project code | 200-WMS-26-001-00 |
|
||||
| Reporting period | 04/08/26–14/08/26 |
|
||||
| Report date | 14/08/26 |
|
||||
| Release | 14/08/26 V1.0 Final |
|
||||
| Prepared by | Apirach Supattaratpateep — Project Manager |
|
||||
| Status | Final — ready for review and authorization |
|
||||
|
||||
## 2. Period objective
|
||||
|
||||
**Milestone:** Demonstration and completion boundary
|
||||
|
||||
**Planned outcome:** Prepare controlled demonstration data, complete final checks, and reach the agreed project-completion boundary.
|
||||
|
||||
## 3. Task progress
|
||||
|
||||
**Period summary:** Demonstration data was committed on 14/08/26; project closure activities continue through the formal end date of 24/08/26.
|
||||
|
||||
| Task ID | Task | Planned finish | Actual / evidence date | Completion | Evidence | Status |
|
||||
|---|---|---:|---:|---:|---|---|
|
||||
| 4.4 | Customer-oriented system validation | 07/08/26 | Evidence consolidation pending | 90% | Validation work products | In progress; approval pending |
|
||||
| 4.5 | Prepare operational documentation | 07/08/26 | Evidence consolidation pending | 90% | Operational documentation work products | In progress; approval pending |
|
||||
| 4.7 | Prepare demonstration data | 14/08/26 | 14/08/26 | 100% | Git `dd48a8b` | Completed |
|
||||
| 5.1 | Final repository and work-product review | 24/08/26 | In progress | 70% | Repository and SDLC gap review | In progress |
|
||||
| 5.2 | Acceptance and project closure | 24/08/26 | Pending signature | 75% | Closure activities in progress | Acceptance pending |
|
||||
|
||||
|
||||
| Evidence field | Value |
|
||||
|---|---|
|
||||
| Evidence classification | Project milestone |
|
||||
| Evidence reference | `dd48a8b`; agreed completion date |
|
||||
| Overall period status | Green |
|
||||
|
||||
## 4. Schedule status
|
||||
|
||||
|
||||
## 5. Issues, risks, and corrective action
|
||||
|
||||
**Issue or risk:** Formal signatures and some controlled ISO/IEC 29110 evidence remained open at completion.
|
||||
|
||||
**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
|
||||
|
||||
Consolidate remaining work products and obtain Project Sponsor authorization.
|
||||
|
||||
## 8. Approval
|
||||
|
||||
### Prepared by
|
||||
|
||||
Name: Apirach Supattaratpateep
|
||||
Role: Project Manager
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Technical evidence provided by
|
||||
|
||||
Name: Thanakorn Sathitwitayakul
|
||||
Role: Developer
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
|
||||
### Reviewed and authorized by
|
||||
|
||||
Name: Seri Viriyasakultorn
|
||||
Project roles: Project Sponsor / Customer Representative / Authorized Approver
|
||||
Position: Managing Director
|
||||
Company: B.R.N. Enterprise Co., Ltd.
|
||||
Signature: ______________________________________________
|
||||
Date: ___________________________________________________
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user