Compare commits

70 Commits
Author SHA1 Message Date
Thanakorn 501c70050f Merge fix/untrack-node-logs 2026-09-14 17:06:16 +07:00
Thanakorn f6909b08eb Stop tracking PM2 log files 2026-09-14 17:05:57 +07:00
Thanakorn 5846c8b282 Merge fix/app-registry-default 2026-09-14 16:58:00 +07:00
Thanakorn 6a271c1f7d Default the app registry when config.php does not define it 2026-09-14 16:57:36 +07:00
Thanakorn 2ef2f32107 Merge fix/retrieve-bin-endpoint 2026-09-14 16:29:56 +07:00
Thanakorn 1f72633cb6 Install libpng, libjpeg and freetype for the gd extension 2026-09-14 16:29:38 +07:00
Thanakorn 4efab9f7c9 Rename retrieve_rack.php to retrieve_bin.php 2026-09-14 16:27:53 +07:00
Thanakorn 44c66c49a5 Merge feature/otp-off-by-default 2026-09-14 15:38:53 +07:00
Thanakorn 6b3a590aa9 Make email OTP login off by default 2026-09-14 15:38:36 +07:00
Thanakorn 21148bf50c Merge feature/onboarding-optional-smtp 2026-09-14 15:27:46 +07:00
Thanakorn cf8106771b Make onboarding SMTP optional when OTP is off 2026-09-14 15:27:01 +07:00
Thanakorn d7203583b7 Merge feature/otp-login-toggle 2026-09-14 15:11:45 +07:00
Thanakorn 9afcf072b0 Add OTP_REQUIRED switch for email OTP login 2026-09-14 15:03:16 +07:00
Thanakorn 2f290ddb26 Merge fix/api-json-notices 2026-09-14 13:33:59 +07:00
Thanakorn 5d021d7683 Accept uppercase channel names in onboarding and company settings 2026-09-14 13:31:26 +07:00
Thanakorn 5ee0c8d41b Keep PHP notices out of login API JSON responses 2026-09-14 12:46:13 +07:00
Thanakorn c3113bc70d Fix login redirect and hide PHP errors on pages 2026-09-14 12:46:13 +07:00
Thanakorn 6765054950 SDLC delivery package 2026-08-18 13:22:26 +07:00
Thanakorn 075d80ad40 SDLC docs package delivery script 2026-08-18 12:38:15 +07:00
Thanakorn a859cfda3e SDLC docs alignment 2026-08-18 12:37:28 +07:00
Thanakorn 37d2f8e725 Hand off 2026-08-17 17:12:34 +07:00
Thanakorn 39201df36e SDLC docs 2026-08-17 17:09:27 +07:00
Thanakorn 3045c4a8ef Add interactive script to generate root .env for docker-compose 2026-08-17 13:50:03 +07:00
Thanakorn 4c4522169c Add Docker Compose production stack (php-apache, mariadb, node/pm2) 2026-08-17 13:44:14 +07:00
Thanakorn 234814a23c Seed Demo Data - Rebranding 2026-08-17 13:12:07 +07:00
Thanakorn 0eab2a3b96 Demo Data Population 2026-08-14 14:10:31 +07:00
nok 618045540a fix login and configurations 2026-08-03 11:17:41 +07:00
Thanakorn S 7b294f70da Classe methods: remove reducdancy 2026-05-29 08:56:58 +07:00
Thanakorn S 356d308907 Fix horizontal scrollbar caused by sidebar margin overflowing viewport 2026-05-28 16:58:15 +07:00
Thanakorn S 9c7a4a139d Block concurrent login: reject new session if account already active 2026-05-28 16:21:55 +07:00
Thanakorn S b634372b22 Scope stock table access by company warehouses 2026-05-28 15:36:49 +07:00
Thanakorn S 2a6441c6a9 Complete Rack→Bin rename and fix ReportManager property bug 2026-05-28 09:53:19 +07:00
Thanakorn S 987cf7ded9 Change 'Rack' to 'Bin' 2026-05-27 17:14:53 +07:00
Thanakorn S ea94b6a6ae Wire targeted toast notifications to document status transitions 2026-05-27 13:44:55 +07:00
Thanakorn S dbbc89f8f2 notify node: userIDguard 2026-05-27 13:12:18 +07:00
Thanakorn S 82f66b0c41 NODEJS cron fix 2026-05-27 12:05:29 +07:00
Thanakorn S 8905b5bf35 fix NodeJS cron 2026-05-27 11:56:40 +07:00
Thanakorn S 8916d9180d cron low stock - overdue invoice 2026-05-27 11:45:48 +07:00
Thanakorn S ebccca4989 CORS whitelist for NodeJS 2026-05-27 11:26:40 +07:00
Thanakorn S aee797b998 nodejs status check 2026-05-27 11:18:09 +07:00
Thanakorn S 5ff1a60c7d autostart node process 2026-05-27 11:07:30 +07:00
Thanakorn S 5bfaf6f8c3 all .md reviewed - fix remaining gaps 2026-05-27 10:42:44 +07:00
Thanakorn S 17a62e50e4 add missing roleGuards 2026-05-27 09:19:52 +07:00
Thanakorn S 1b34482216 Stop tracking docs/ — already in .gitignore 2026-05-27 08:15:29 +07:00
Thanakorn S 1c5236d8e0 Master data review: spec updates and C1/C2/M3-M6 fixes 2026-05-26 17:33:58 +07:00
Thanakorn S 6ca4c91865 Document lifecycle review: spec updates and C2/M9 code fixes 2026-05-26 16:23:46 +07:00
Thanakorn S 632c039790 system notification 2026-05-26 13:26:57 +07:00
Thanakorn S 1a952b42dd Seal transaction limit coverage gaps 2026-05-26 13:10:54 +07:00
Thanakorn S 9e200d31fe Security hardening: invited user onboarding flow (C1–N7) 2026-05-26 10:18:40 +07:00
Thanakorn S 0815ae3292 document number sequence 2026-05-26 08:19:40 +07:00
Thanakorn S 5c0166ae73 Seal live dashboard event gaps 2026-05-25 17:02:52 +07:00
Thanakorn S 3c9475f3fa Close login gap 2026-05-25 15:34:12 +07:00
Thanakorn S 8027ab569e PHP event trigger by NodeJS [stock dashboard] 2026-05-25 14:33:36 +07:00
Thanakorn S 1237888ab9 stock aggregate table 2026-05-25 13:30:10 +07:00
Thanakorn S 45a78a3fed login: block concurrent login, single-factor auth for staff/viewer 2026-05-25 09:43:30 +07:00
Thanakorn S 5ca6b49fd0 code audit fixes: require_once, issue flow, role guards 2026-05-23 17:06:08 +07:00
Thanakorn S 54f3f11fd2 automate and view gl entries 2026-05-23 16:46:35 +07:00
Thanakorn S 5affae1fc5 batch journal entries 2026-05-23 15:55:22 +07:00
Thanakorn S 4bb378a905 accounting Reports 2026-05-23 15:07:11 +07:00
Thanakorn S 15618f4955 fix file path 2026-05-23 13:11:22 +07:00
Thanakorn S c0de84a575 fix file path 2026-05-23 13:09:18 +07:00
Thanakorn S 5780183b82 gl aggregate table (ETL), cronjob by NODEJS 2026-05-23 10:52:27 +07:00
Thanakorn S 5a3bfa3435 NODE JS introduction: socket polling 2026-05-22 17:01:59 +07:00
Thanakorn S 648efee991 softDelete features 2026-05-22 14:07:22 +07:00
Thanakorn S 738f600fe8 users app access badge 2026-05-22 13:21:46 +07:00
Thanakorn S 7379ac9e4a user badge 2026-05-22 10:42:01 +07:00
Thanakorn S cf25732da0 use roles guards 2026-05-22 08:45:35 +07:00
Thanakorn S f2b87cfd0f fix onboarding bugs 2026-05-21 16:51:19 +07:00
Thanakorn S 86b1aa9d62 add setup.php — one-shot production database setup script 2026-05-21 15:21:36 +07:00
Thanakorn S e1135d2bce fix StockManager lot/serial coercion + add document flow test suite 2026-05-21 14:20:41 +07:00
125 changed files with 5022 additions and 419 deletions
+6
View File
@@ -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
View File
@@ -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/
+22
View File
@@ -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
+36
View File
@@ -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
));
}
}
+18
View File
@@ -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.
+5
View File
@@ -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.
+19
View File
@@ -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');
+18
View File
@@ -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>
+9 -2
View File
@@ -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));
}
+9 -4
View File
@@ -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"
);
+79 -64
View File
@@ -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,59 +117,67 @@ 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 ────────────────────────────
// Clamp to known-good values to prevent storing unsupported configuration.
$smtp_port = trim($data['smtp_port'] ?? '587');
$smtp_encryption = trim($data['smtp_encryption'] ?? 'tls');
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');
if (!in_array($smtp_port, ['25', '465', '587'], true)) $smtp_port = '587';
if (!in_array($smtp_encryption, ['tls', 'ssl', 'none'], true)) $smtp_encryption = 'tls';
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 ────────────────────────────────────────
// 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);
// ── 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);
// Assemble a temporary SMTP config for the test send (step 8)
$smtp_config = [
'server' => $smtp_host,
'port' => $smtp_port,
'username' => $smtp_username,
'password' => $encrypted_pass,
'from_name' => $company_name ?: $smtp_username,
'from_email' => $email ?: $smtp_username,
'encryption' => $smtp_encryption,
];
// Assemble a temporary SMTP config for the test send (step 8)
$smtp_config = [
'server' => $smtp_host,
'port' => $smtp_port,
'username' => $smtp_username,
'password' => $encrypted_pass,
'from_name' => $company_name ?: $smtp_username,
'from_email' => $email ?: $smtp_username,
'encryption' => $smtp_encryption,
];
// ── 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.
require_once '../../../assets/utils/module/mailer.php';
// ── 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.
require_once '../../../assets/utils/module/mailer.php';
$mailer = new mailer(['pdo1' => $pdo1]);
$mailer->send_email([
'company_id' => 0,
'smtp' => $smtp_config,
'to' => $_SESSION['onboarding_email'] ?? $smtp_username,
'subject' => 'WMS — SMTP Verification',
'message' => "Your SMTP is working correctly.\n\nSetup is now complete.",
'channel_name' => $company_name ?: 'WMS',
'key' => $pinkey,
]);
// If mailer fails, it calls exit() internally — nothing below this line runs.
$mailer = new mailer(['pdo1' => $pdo1]);
$mailer->send_email([
'company_id' => 0,
'smtp' => $smtp_config,
'to' => $_SESSION['onboarding_email'] ?? $smtp_username,
'subject' => 'WMS — SMTP Verification',
'message' => "Your SMTP is working correctly.\n\nSetup is now complete.",
'channel_name' => $company_name ?: 'WMS',
'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,25 +237,29 @@ 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.).
$sth = $pdo1->prepare("
INSERT INTO company_smtp
(company_id, server, port, username, password,
from_name, from_email, encryption, updated_at)
VALUES
(:company_id, :server, :port, :username, :password,
:from_name, :from_email, :encryption, NOW())
");
$sth->execute([
':company_id' => $company_id,
':server' => $smtp_host,
':port' => $smtp_port,
':username' => $smtp_username,
':password' => $encrypted_pass,
':from_name' => $company_name,
':from_email' => $email ?: $smtp_username,
':encryption' => $smtp_encryption,
]);
db_check($sth, $answer);
// 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,
from_name, from_email, encryption, updated_at)
VALUES
(:company_id, :server, :port, :username, :password,
:from_name, :from_email, :encryption, NOW())
");
$sth->execute([
':company_id' => $company_id,
':server' => $smtp_host,
':port' => $smtp_port,
':username' => $smtp_username,
':password' => $encrypted_pass,
':from_name' => $company_name,
':from_email' => $email ?: $smtp_username,
':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
View File
@@ -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>
+29 -8
View File
@@ -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();
+6
View File
@@ -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
View File
@@ -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.'])); }
+2 -1
View File
@@ -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
View File
@@ -1,6 +1,7 @@
<?php
session_start();
require '../config.php';
require_once '../assets/utils/app_registry.php';
require '../include_header.php';
?>
+1
View File
@@ -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:
+1
View File
@@ -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
View 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/*
+16
View File
@@ -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' => [
+25 -1
View File
@@ -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"
-21
View File
@@ -1,21 +0,0 @@
2026-07-22T14:37:48: [2026-07-22T07:37:48.626Z] [PID: 505] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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.
-58
View File
@@ -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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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] [NODE-CRON] [WARN] 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.
-255
View File
@@ -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
+32
View File
@@ -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>
+4
View File
@@ -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>
+8
View File
@@ -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 &nbsp; Fax. (+66) 2687 0255</p>
</div>
<div class="document-header__title">{{documentTitle}}</div>
</header>
+37
View File
@@ -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 &nbsp; 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;
}
+54
View File
@@ -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"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "brn-wms-sdlc-delivery",
"private": true,
"type": "module",
"dependencies": {
"playwright": "1.50.0"
}
}
+175
View File
@@ -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('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;');
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); });
+91
View File
@@ -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); });
+28
View File
@@ -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."
BIN
View File
Binary file not shown.
@@ -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:** ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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: ___________________________________________________
@@ -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