Files
wms-app/setup.php
T
2026-05-27 17:14:53 +07:00

1186 lines
52 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Production Setup Script
* Run once on a fresh server via CLI: php setup.php
*
* What it does:
* 1. Reads DB credentials from app/config.php
* 2. Creates wms + wms2 databases if they don't exist
* 3. Creates all tables (CREATE TABLE IF NOT EXISTS — safe to re-run)
* 4. Prints a status report
*
* Dynamic td_stock_<warehouse_id> tables are created automatically by the app
* the first time a warehouse is used; they are NOT listed here.
*/
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit('Run via CLI only: php setup.php');
}
// ── Load config ───────────────────────────────────────────────────────────────
$config = __DIR__ . '/app/config.php';
if (!file_exists($config)) {
exit("ERROR: app/config.php not found. Configure it before running setup.\n");
}
require $config;
$host = $db_server ?? 'localhost';
$user = $db_user ?? 'root';
$pass = $db_pass ?? '';
$db1 = $db_database ?? 'wms';
$db2 = $db_database2 ?? 'wms2';
// ── Helpers ───────────────────────────────────────────────────────────────────
$ok_count = 0;
$skip_count = 0;
$err_count = 0;
function info(string $msg): void { echo "\033[36m •\033[0m {$msg}\n"; }
function ok(string $msg): void { global $ok_count; $ok_count++; echo "\033[32m ✓\033[0m {$msg}\n"; }
function skip(string $msg): void { global $skip_count; $skip_count++; echo "\033[33m –\033[0m {$msg}\n"; }
function fail(string $msg): void { global $err_count; $err_count++; echo "\033[31m ✗\033[0m {$msg}\n"; }
function run(PDO $pdo, string $sql, string $label): void {
try {
$pdo->exec($sql);
ok($label);
} catch (PDOException $e) {
if (str_contains($e->getMessage(), 'already exists') ||
str_contains($e->getMessage(), 'Duplicate column name')) {
skip($label . ' (already exists)');
} else {
fail($label . ': ' . $e->getMessage());
}
}
}
// ── Connect (no DB selected) ──────────────────────────────────────────────────
echo "\n\033[1m=== mn3wms Production Setup ===\033[0m\n\n";
echo "Host : {$host}\n";
echo "DB1 : {$db1} (auth / identity)\n";
echo "DB2 : {$db2} (business data)\n\n";
try {
$pdo = new PDO("mysql:host={$host};charset=utf8", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
ok("Connected to MySQL as {$user}@{$host}");
} catch (PDOException $e) {
fail("Cannot connect: " . $e->getMessage());
exit(1);
}
// ── Create databases ──────────────────────────────────────────────────────────
echo "\n--- Databases ---\n";
run($pdo, "CREATE DATABASE IF NOT EXISTS `{$db1}` CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci", "Database `{$db1}`");
run($pdo, "CREATE DATABASE IF NOT EXISTS `{$db2}` CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci", "Database `{$db2}`");
// ─────────────────────────────────────────────────────────────────────────────
// WMS (auth / identity) tables
// ─────────────────────────────────────────────────────────────────────────────
echo "\n--- {$db1} tables ---\n";
$pdo->exec("USE `{$db1}`");
run($pdo, "
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`status` varchar(40) NOT NULL DEFAULT 'not activated',
`name` tinytext NOT NULL,
`surname` tinytext NOT NULL,
`address` tinytext DEFAULT NULL,
`country` varchar(50) NOT NULL DEFAULT '',
`profile_picture` tinytext NOT NULL,
`default_company` int(11) DEFAULT 0,
`language` varchar(3) NOT NULL DEFAULT 'th',
`regis_date` timestamp NOT NULL DEFAULT current_timestamp(),
`license` enum('owner','user') NOT NULL DEFAULT 'owner',
`app_access` enum('all','wms','accounting') NOT NULL DEFAULT 'all',
`active` varchar(255) NOT NULL DEFAULT 'yes',
`phone` varchar(50) NOT NULL DEFAULT '',
`verify_token` varchar(64) DEFAULT NULL,
`verify_expires_at` datetime DEFAULT NULL,
`login_attempts` int(11) NOT NULL DEFAULT 0,
`locked_until` datetime DEFAULT NULL,
`session_token` varchar(64) DEFAULT NULL,
`session_token_at` datetime DEFAULT NULL,
`session_last_seen` datetime DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
KEY `idx_user_verify_token` (`verify_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'user');
run($pdo, "
CREATE TABLE IF NOT EXISTS `company_list` (
`company_id` int(11) NOT NULL AUTO_INCREMENT,
`channel_name` varchar(100) NOT NULL DEFAULT '' COMMENT 'Unique slug used for login routing',
`company_name` varchar(255) NOT NULL DEFAULT '',
`company_name2` varchar(255) NOT NULL DEFAULT '' COMMENT 'Secondary / English name',
`company_logo` varchar(255) NOT NULL DEFAULT '',
`company_seal` varchar(255) NOT NULL DEFAULT '',
`branch` varchar(255) NOT NULL DEFAULT 'สำนักงานใหญ่',
`branch_no` varchar(50) NOT NULL DEFAULT '',
`fiscal_year` varchar(10) NOT NULL DEFAULT '',
`fx` varchar(10) NOT NULL DEFAULT 'thb',
`address` text NOT NULL DEFAULT '',
`address2` text NOT NULL DEFAULT '',
`tax_id` varchar(50) NOT NULL DEFAULT '',
`prompt_pay` varchar(50) NOT NULL DEFAULT '',
`entrepreneur` varchar(100) NOT NULL DEFAULT '',
`email` varchar(255) NOT NULL DEFAULT '',
`phone` varchar(50) NOT NULL DEFAULT '',
`fax` varchar(50) NOT NULL DEFAULT '',
`website` varchar(255) NOT NULL DEFAULT '',
`facebook_page` varchar(255) NOT NULL DEFAULT '',
`package` varchar(50) NOT NULL DEFAULT 'starter',
PRIMARY KEY (`company_id`),
UNIQUE KEY `channel_name` (`channel_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'company_list');
run($pdo, "
CREATE TABLE IF NOT EXISTS `company_map_user` (
`map_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`role` varchar(15) NOT NULL DEFAULT 'user',
`app_access` varchar(15) NOT NULL DEFAULT 'wms',
`invite_token` varchar(64) DEFAULT NULL,
`invite_expires_at` datetime DEFAULT NULL,
`invite_resent_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`map_id`),
KEY `user_id` (`user_id`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'company_map_user');
// Column added for explicit-consent invite flow (existing users)
run($pdo, "ALTER TABLE `company_map_user` ADD COLUMN `invite_expires_at` DATETIME DEFAULT NULL AFTER `invite_token`", 'company_map_user.invite_expires_at');
run($pdo, "ALTER TABLE `company_map_user` ADD COLUMN `invite_resent_at` DATETIME DEFAULT NULL AFTER `invite_expires_at`", 'company_map_user.invite_resent_at');
run($pdo, "
CREATE TABLE IF NOT EXISTS `company_setting` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`setting_key` varchar(64) NOT NULL,
`value` varchar(255) NOT NULL DEFAULT '',
`updated_at` datetime DEFAULT NULL,
`log` mediumtext DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `company_key` (`company_id`,`setting_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'company_setting');
run($pdo, "
CREATE TABLE IF NOT EXISTS `company_smtp` (
`smtp_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(10) unsigned NOT NULL,
`server` varchar(255) NOT NULL,
`port` varchar(10) NOT NULL DEFAULT '587',
`username` varchar(255) NOT NULL,
`password` text NOT NULL,
`from_name` varchar(255) NOT NULL DEFAULT '',
`from_email` varchar(255) NOT NULL DEFAULT '',
`encryption` enum('tls','ssl','none') NOT NULL DEFAULT 'tls',
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`smtp_id`),
UNIQUE KEY `uq_company_smtp` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'company_smtp');
run($pdo, "
CREATE TABLE IF NOT EXISTS `company_usage` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`day_date` date NOT NULL,
`daily_count` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_company_day` (`company_id`,`day_date`),
KEY `idx_company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'company_usage');
run($pdo, "
CREATE TABLE IF NOT EXISTS `schema_migrations` (
`version` varchar(64) NOT NULL,
`applied_at` datetime NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'schema_migrations');
run($pdo, "
CREATE TABLE IF NOT EXISTS `whitelist` (
`list_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cookie` varchar(32) NOT NULL,
`status` int(11) NOT NULL DEFAULT 1 COMMENT '0=block 1=wait 2=allow',
`ip` varchar(20) NOT NULL,
`dt` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`list_id`),
UNIQUE KEY `cookie` (`cookie`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'whitelist');
// ─────────────────────────────────────────────────────────────────────────────
// WMS2 (business data) tables
// ─────────────────────────────────────────────────────────────────────────────
echo "\n--- {$db2} tables ---\n";
$pdo->exec("USE `{$db2}`");
// ── Master data ───────────────────────────────────────────────────────────────
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_warehouse` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`warehouse_name` varchar(255) NOT NULL DEFAULT '',
`location` text NOT NULL DEFAULT '',
`manager` int(11) NOT NULL DEFAULT 0,
`description` text NOT NULL DEFAULT '',
`status` int(1) NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_warehouse_name` (`company_id`,`warehouse_name`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_warehouse');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_storage` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`warehouse` varchar(255) NOT NULL DEFAULT '',
`zone` text NOT NULL DEFAULT '',
`aisle_from` varchar(255) NOT NULL DEFAULT '',
`aisle_to` varchar(255) NOT NULL DEFAULT '',
`bin_from` varchar(255) NOT NULL DEFAULT '',
`bin_to` varchar(255) NOT NULL DEFAULT '',
`description` text NOT NULL DEFAULT '',
`status` int(1) NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_storage');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_bin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`td_stock_id` int(11) NOT NULL DEFAULT 0,
`warehouse` int(11) NOT NULL,
`storage_id` int(11) NOT NULL DEFAULT 0,
`zone` varchar(10) NOT NULL,
`aisle` varchar(10) NOT NULL,
`bin` varchar(10) NOT NULL,
`product_sku` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_bin_location` (`company_id`,`warehouse`,`zone`,`aisle`,`bin`),
KEY `idx_storage` (`storage_id`),
KEY `idx_sku` (`company_id`,`product_sku`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'md_bin');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_bin_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(10) NOT NULL,
`md_bin_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`dt` datetime NOT NULL DEFAULT current_timestamp(),
`login` datetime DEFAULT NULL,
`action` enum('occupy','release','transfer_in','transfer_out') NOT NULL,
`product_sku` varchar(100) DEFAULT NULL,
`td_stock_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_bin` (`company_id`,`md_bin_id`),
KEY `idx_user` (`company_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_bin_log');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_product_category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`category` varchar(255) NOT NULL DEFAULT '',
`slug` varchar(255) NOT NULL DEFAULT '',
`description` text NOT NULL DEFAULT '',
`status` int(1) NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_product_id` (`company_id`,`category`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_product_category');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_product` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`product_name` varchar(255) NOT NULL DEFAULT '',
`sku` varchar(255) NOT NULL DEFAULT '',
`barcode` varchar(64) DEFAULT NULL,
`price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`cost_price` decimal(15,2) NOT NULL DEFAULT 0.00,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`sales_account_code` varchar(20) DEFAULT NULL,
`purchase_account_code` varchar(20) DEFAULT NULL,
`uom` varchar(20) NOT NULL DEFAULT 'pcs',
`min_stock` decimal(18,4) NOT NULL DEFAULT 0.0000,
`reorder_point` decimal(18,4) NOT NULL DEFAULT 0.0000,
`category` int(11) NOT NULL DEFAULT 0,
`product_image` text NOT NULL DEFAULT '',
`description` text NOT NULL DEFAULT '',
`status` int(1) NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_product_id` (`company_id`,`product_name`),
KEY `company_id` (`company_id`),
KEY `idx_product_barcode` (`company_id`,`barcode`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_product');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_lot` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(10) unsigned NOT NULL,
`product_sku` varchar(100) NOT NULL,
`lot_number` varchar(100) NOT NULL,
`expiry_date` date NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_product_sku_lot_number` (`company_id`,`product_sku`,`lot_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_lot');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_contact_type` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`contact_type` varchar(255) NOT NULL DEFAULT '',
`description` text NOT NULL DEFAULT '',
`status` int(1) NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_product_id` (`company_id`,`contact_type`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_contact_type');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_contact` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`contact_name` varchar(255) NOT NULL,
`tax_id` varchar(255) NOT NULL,
`organization` varchar(255) NOT NULL,
`branch` varchar(255) NOT NULL,
`contact_type` int(11) unsigned NOT NULL,
`billing_address` text NOT NULL,
`shipping_location` varchar(255) NOT NULL,
`shipping_address` text NOT NULL,
`remark` text NOT NULL,
`contact_image` mediumtext NOT NULL,
`status` int(1) unsigned NOT NULL DEFAULT 1,
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_contact');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_barcode` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(10) unsigned NOT NULL,
`barcode_type` varchar(20) NOT NULL,
`barcode` varchar(320) NOT NULL,
`product_sku` varchar(100) DEFAULT NULL,
`lot_number` varchar(100) DEFAULT NULL,
`serial_number` varchar(100) DEFAULT NULL,
`warehouse_id` int(10) unsigned DEFAULT NULL,
`zone` varchar(100) DEFAULT NULL,
`aisle` varchar(100) DEFAULT NULL,
`bin` varchar(100) DEFAULT NULL,
`print_count` int(10) unsigned NOT NULL DEFAULT 0,
`last_printed_dt` datetime DEFAULT NULL,
`created_dt` datetime NOT NULL DEFAULT current_timestamp(),
`status` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_barcode` (`company_id`,`barcode`),
KEY `company_id_type` (`company_id`,`barcode_type`),
KEY `company_id_product_lot` (`company_id`,`product_sku`,`lot_number`),
KEY `company_id_location` (`company_id`,`warehouse_id`,`zone`,`aisle`,`bin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_barcode');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_sku_barcode_label` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(10) unsigned NOT NULL,
`product_sku` varchar(100) NOT NULL,
`lot_number` varchar(100) NOT NULL,
`serial_number` varchar(100) NOT NULL,
`barcode` varchar(320) NOT NULL,
`created_dt` datetime NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_barcode` (`company_id`,`barcode`),
KEY `company_id_product_lot` (`company_id`,`product_sku`,`lot_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_sku_barcode_label');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_lock_operation` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`operation_type` varchar(50) NOT NULL DEFAULT '',
`user_id` int(11) NOT NULL DEFAULT 0,
`locked_at` datetime NOT NULL,
`expires_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `op` (`company_id`,`operation_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_lock_operation');
// ── Accounting master ─────────────────────────────────────────────────────────
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`dept_code` varchar(20) NOT NULL,
`dept_name` varchar(255) NOT NULL,
`description` text DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_company_dept` (`company_id`,`dept_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_department');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`account_code` varchar(20) NOT NULL,
`account_name` varchar(255) NOT NULL,
`account_type` enum('asset','liability','equity','revenue','expense') NOT NULL,
`account_category` varchar(50) NOT NULL DEFAULT '',
`parent_code` varchar(20) DEFAULT NULL,
`is_posting` tinyint(1) NOT NULL DEFAULT 1,
`status` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_company_account` (`company_id`,`account_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_account');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_account_formula` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`formula_name` varchar(100) NOT NULL,
`document_type` varchar(50) NOT NULL,
`description` text DEFAULT NULL,
`is_default` tinyint(1) NOT NULL DEFAULT 0,
`status` tinyint(1) NOT NULL DEFAULT 1,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_company_doc` (`company_id`,`document_type`),
KEY `idx_status` (`company_id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_account_formula');
run($pdo, "
CREATE TABLE IF NOT EXISTS `md_account_formula_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`formula_id` int(11) NOT NULL,
`drcr` enum('D','C') NOT NULL,
`account_code` varchar(20) NOT NULL,
`amount_key` enum('grand_total','total','tax','amount') NOT NULL,
`description` varchar(255) DEFAULT NULL,
`sort_order` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_formula` (`company_id`,`formula_id`),
KEY `idx_account` (`company_id`,`account_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'md_account_formula_item');
// ── Stock (template + base) ───────────────────────────────────────────────────
// td_stock_<warehouse_id> tables are created automatically by the app on first use.
// This base table matches the template used by WarehouseManager::createStockTableForWarehouse().
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_stock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ref_id` int(11) unsigned NOT NULL DEFAULT 0,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(20) NOT NULL,
`type` varchar(20) NOT NULL,
`ref_warehouse` varchar(20) NOT NULL DEFAULT '',
`date` datetime DEFAULT NULL,
`product_sku` varchar(255) NOT NULL DEFAULT '0',
`lot_number` varchar(255) NOT NULL DEFAULT '',
`serial_number` varchar(255) NOT NULL DEFAULT '',
`source` varchar(255) NOT NULL DEFAULT '',
`source_id` int(11) NOT NULL DEFAULT 0,
`in` decimal(18,4) NOT NULL DEFAULT 0.0000,
`out` decimal(18,4) NOT NULL DEFAULT 0.0000,
`price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`zone` varchar(255) NOT NULL DEFAULT '',
`aisle` varchar(255) NOT NULL DEFAULT '',
`bin` varchar(255) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`description` text NOT NULL DEFAULT '',
`status` int(3) NOT NULL DEFAULT 0,
`log` mediumtext NOT NULL DEFAULT '{}',
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `product_id` (`product_sku`),
KEY `updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_stock (base template)');
run($pdo, "
CREATE TABLE IF NOT EXISTS `etl_stock_summary` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) unsigned NOT NULL DEFAULT 0,
`warehouse_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(255) NOT NULL DEFAULT '',
`month` varchar(10) NOT NULL,
`total_in` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_out` decimal(18,4) NOT NULL DEFAULT 0.0000,
`source_updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `company_id_warehouse_id_product_sku_month` (`company_id`,`warehouse_id`,`product_sku`,`month`),
KEY `company_id` (`company_id`),
KEY `product_id` (`warehouse_id`),
KEY `source_updated_at` (`source_updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'etl_stock_summary');
// ── Transactions ──────────────────────────────────────────────────────────────
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_gl` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`source_type` varchar(50) NOT NULL DEFAULT '',
`source_id` int(11) NOT NULL DEFAULT 0,
`reference` varchar(100) NOT NULL DEFAULT '',
`description` varchar(255) NOT NULL DEFAULT '',
`journal_date` date DEFAULT NULL,
`formula_id` int(11) NOT NULL DEFAULT 0,
`period` varchar(7) NOT NULL DEFAULT '',
`current_version` tinyint(4) NOT NULL DEFAULT 1,
`history` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '[]' CHECK (json_valid(`history`)),
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `period` (`period`),
KEY `formula_id` (`formula_id`),
KEY `source` (`company_id`,`source_type`,`source_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_gl');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_gl_item` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`gl_id` int(10) unsigned NOT NULL,
`account_code` varchar(20) NOT NULL DEFAULT '',
`department_id` int(11) NOT NULL DEFAULT 0,
`debit` decimal(18,4) NOT NULL DEFAULT 0.0000,
`credit` decimal(18,4) NOT NULL DEFAULT 0.0000,
`description` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `gl_id` (`gl_id`),
KEY `company_id` (`company_id`),
KEY `account_code` (`account_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_gl_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_batch_action` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`type` varchar(50) NOT NULL DEFAULT '',
`doc_type` varchar(50) NOT NULL DEFAULT '',
`total` int(11) NOT NULL DEFAULT 0,
`succeeded` int(11) NOT NULL DEFAULT 0,
`failed` int(11) NOT NULL DEFAULT 0,
`failed_ids` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '[]' CHECK (json_valid(`failed_ids`)),
`user_id` int(11) NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `type` (`type`),
KEY `doc_type` (`doc_type`),
KEY `user_id` (`user_id`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_batch_action');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_quotation` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`quotation_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`quotation_date` date DEFAULT NULL,
`valid_until` date DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`subtotal` decimal(18,4) NOT NULL DEFAULT 0.0000,
`discount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`grand_total` decimal(18,4) NOT NULL DEFAULT 0.0000,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `quotation_number` (`quotation_number`),
KEY `status` (`status`),
KEY `contact_id` (`contact_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_quotation');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_quotation_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`quotation_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`converted_qty` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_qt_item` (`quotation_id`,`item_id`),
KEY `idx_quotation` (`company_id`,`quotation_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_quotation_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_order` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`source_id` int(11) NOT NULL DEFAULT 0,
`source` varchar(20) NOT NULL DEFAULT '',
`order_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`order_date` date DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`subtotal` decimal(18,4) NOT NULL DEFAULT 0.0000,
`discount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_adjustment` decimal(8,2) NOT NULL DEFAULT 0.00,
`shipping_fee` decimal(18,4) NOT NULL DEFAULT 0.0000,
`shipping_tracking_number` varchar(100) DEFAULT NULL,
`grand_total` decimal(18,4) NOT NULL DEFAULT 0.0000,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `contact_id` (`contact_id`),
KEY `order_number` (`order_number`),
KEY `status` (`status`),
KEY `order_date` (`order_date`),
KEY `quotation_id` (`source_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_order');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_order_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`order_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`warehouse_id` int(11) NOT NULL DEFAULT 0,
`stock_out_id` int(11) NOT NULL DEFAULT 0,
`lot_number` varchar(100) NOT NULL DEFAULT '',
`invoiced_qty` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_order_item` (`order_id`,`item_id`),
KEY `idx_order` (`company_id`,`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_order_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_invoice` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`source_id` int(11) NOT NULL DEFAULT 0,
`source` varchar(20) NOT NULL DEFAULT '',
`doc_type` varchar(20) NOT NULL DEFAULT 'invoice',
`invoice_number` varchar(50) NOT NULL DEFAULT '',
`ref_invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`order_id` int(11) unsigned NOT NULL DEFAULT 0,
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`issued_date` date DEFAULT NULL,
`due_date` date DEFAULT NULL,
`subtotal` decimal(18,4) NOT NULL DEFAULT 0.0000,
`discount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_adjustment` decimal(8,2) NOT NULL DEFAULT 0.00,
`shipping_fee` decimal(18,4) NOT NULL DEFAULT 0.0000,
`grand_total` decimal(18,4) NOT NULL DEFAULT 0.0000,
`formula_id` int(11) DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `order_id` (`order_id`),
KEY `contact_id` (`contact_id`),
KEY `invoice_number` (`invoice_number`),
KEY `ref_invoice_id` (`ref_invoice_id`),
KEY `doc_type` (`doc_type`),
KEY `status` (`status`),
KEY `due_date` (`due_date`),
KEY `source_id` (`source_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_invoice');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_invoice_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_invoice_item` (`invoice_id`,`item_id`),
KEY `idx_invoice` (`company_id`,`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_invoice_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_return` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`return_number` varchar(50) NOT NULL DEFAULT '',
`order_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`return_date` date DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`reason` text NOT NULL DEFAULT '',
`refund_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_adjustment` decimal(8,2) NOT NULL DEFAULT 0.00,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `order_id` (`order_id`),
KEY `invoice_id` (`invoice_id`),
KEY `contact_id` (`contact_id`),
KEY `return_number` (`return_number`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_return');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_return_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`return_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`warehouse_id` int(11) NOT NULL DEFAULT 0,
`stock_out_id` int(11) NOT NULL DEFAULT 0,
`stock_out_warehouse_id` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_return_item` (`return_id`,`item_id`),
KEY `idx_return` (`company_id`,`return_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_return_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_purchase_request` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`request_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`request_date` date DEFAULT NULL,
`required_date` date DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`subtotal` decimal(18,4) NOT NULL DEFAULT 0.0000,
`discount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`shipping_fee` decimal(18,4) NOT NULL DEFAULT 0.0000,
`grand_total` decimal(18,4) NOT NULL DEFAULT 0.0000,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `request_number` (`request_number`),
KEY `status` (`status`),
KEY `contact_id` (`contact_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_purchase_request');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_purchase_request_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`request_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`converted_qty` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_pr_item` (`request_id`,`item_id`),
KEY `idx_pr_item` (`company_id`,`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_purchase_request_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_purchase_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`uuid` varchar(64) NOT NULL,
`source_id` int(11) NOT NULL DEFAULT 0,
`source` varchar(32) NOT NULL DEFAULT '',
`po_number` varchar(32) NOT NULL,
`contact_id` int(11) DEFAULT NULL,
`department_id` int(11) NOT NULL DEFAULT 0,
`po_date` date NOT NULL,
`expected_date` date DEFAULT NULL,
`closed_at` datetime DEFAULT NULL,
`warehouse_id` int(11) NOT NULL DEFAULT 0,
`status` tinyint(4) NOT NULL DEFAULT 0,
`subtotal` decimal(15,2) NOT NULL DEFAULT 0.00,
`discount` decimal(15,2) NOT NULL DEFAULT 0.00,
`tax` decimal(15,2) NOT NULL DEFAULT 0.00,
`tax_adjustment` decimal(8,2) NOT NULL DEFAULT 0.00,
`shipping_fee` decimal(15,2) NOT NULL DEFAULT 0.00,
`grand_total` decimal(15,2) NOT NULL DEFAULT 0.00,
`notes` text DEFAULT NULL,
`log` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`log`)),
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_po_company` (`company_id`),
KEY `idx_po_status` (`company_id`,`status`),
KEY `idx_po_contact` (`company_id`,`contact_id`),
KEY `idx_po_date` (`company_id`,`po_date`),
KEY `idx_po_source` (`company_id`,`source`,`source_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_purchase_order');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_purchase_order_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`order_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`received_qty` decimal(18,4) NOT NULL DEFAULT 0.0000,
`stock_in_id` int(11) NOT NULL DEFAULT 0,
`invoiced_qty` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_po_item` (`order_id`,`item_id`),
KEY `idx_order` (`company_id`,`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_purchase_order_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_supplier_return` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`return_number` varchar(32) NOT NULL DEFAULT '',
`po_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`return_date` date NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`reason` text NOT NULL DEFAULT '',
`refund_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_adjustment` decimal(8,2) NOT NULL DEFAULT 0.00,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`shipping_tracking_number` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `idx_company_id` (`company_id`),
KEY `idx_po_id` (`po_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_supplier_return');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_supplier_return_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`return_id` int(11) unsigned NOT NULL DEFAULT 0,
`item_id` int(11) unsigned NOT NULL DEFAULT 0,
`product_sku` varchar(100) NOT NULL DEFAULT '',
`product_name` varchar(255) NOT NULL DEFAULT '',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000,
`unit_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`total_price` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`tax_rate` decimal(5,2) NOT NULL DEFAULT 0.00,
`warehouse_id` int(11) NOT NULL DEFAULT 0,
`stock_in_id` int(11) NOT NULL DEFAULT 0,
`zone` varchar(50) NOT NULL DEFAULT '',
`aisle` varchar(50) NOT NULL DEFAULT '',
`bin` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_sr_item` (`return_id`,`item_id`),
KEY `idx_return` (`company_id`,`return_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
", 'td_supplier_return_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_receipt_billing` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`billing_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`billing_date` date DEFAULT NULL,
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`status` tinyint(1) NOT NULL DEFAULT 1,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `contact_id` (`contact_id`),
KEY `billing_number` (`billing_number`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_receipt_billing');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_receipt_billing_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`billing_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`doc_type` varchar(20) NOT NULL DEFAULT '',
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `billing_id` (`billing_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_receipt_billing_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_receipt` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`receipt_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`receipt_billing_id` int(11) unsigned NOT NULL DEFAULT 0,
`receipt_date` date DEFAULT NULL,
`payment_method` varchar(50) NOT NULL DEFAULT '',
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`formula_id` int(11) DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 1,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `contact_id` (`contact_id`),
KEY `receipt_number` (`receipt_number`),
KEY `status` (`status`),
KEY `receipt_billing_id` (`receipt_billing_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_receipt');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_receipt_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`receipt_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`doc_type` varchar(20) NOT NULL DEFAULT '',
`department_id` int(11) NOT NULL DEFAULT 0,
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `receipt_id` (`receipt_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_receipt_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_payment_billing` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`billing_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`billing_date` date DEFAULT NULL,
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`status` tinyint(1) NOT NULL DEFAULT 1,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `contact_id` (`contact_id`),
KEY `billing_number` (`billing_number`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_payment_billing');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_payment_billing_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`billing_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`doc_type` varchar(20) NOT NULL DEFAULT '',
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `billing_id` (`billing_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_payment_billing_item');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_payment` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`uuid` varchar(32) NOT NULL DEFAULT '',
`payment_number` varchar(50) NOT NULL DEFAULT '',
`contact_id` int(11) NOT NULL DEFAULT 0,
`department_id` int(11) NOT NULL DEFAULT 0,
`payment_billing_id` int(11) unsigned NOT NULL DEFAULT 0,
`payment_date` date DEFAULT NULL,
`payment_method` varchar(50) NOT NULL DEFAULT '',
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
`formula_id` int(11) DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 1,
`notes` text NOT NULL DEFAULT '',
`log` mediumtext NOT NULL DEFAULT '{}',
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `contact_id` (`contact_id`),
KEY `payment_billing_id` (`payment_billing_id`),
KEY `payment_number` (`payment_number`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_payment');
run($pdo, "
CREATE TABLE IF NOT EXISTS `td_payment_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`payment_id` int(11) unsigned NOT NULL DEFAULT 0,
`invoice_id` int(11) unsigned NOT NULL DEFAULT 0,
`doc_type` varchar(20) NOT NULL DEFAULT '',
`department_id` int(11) NOT NULL DEFAULT 0,
`amount` decimal(18,4) NOT NULL DEFAULT 0.0000,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`),
KEY `payment_id` (`payment_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'td_payment_item');
run_ddl("
CREATE TABLE IF NOT EXISTS `document_types` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`doc_type_key` varchar(50) NOT NULL DEFAULT '',
`name` varchar(100) NOT NULL DEFAULT '',
`prefix` varchar(20) NOT NULL DEFAULT '',
`format_type` enum('YY','YYMM','YYMMDD','none') NOT NULL DEFAULT 'YYMM',
`sequence_digits` tinyint(2) unsigned NOT NULL DEFAULT 5,
`allow_manual` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_company_doctype_prefix` (`company_id`, `doc_type_key`, `prefix`),
KEY `company_id` (`company_id`),
KEY `doc_type_key` (`doc_type_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'document_types');
run_ddl("
CREATE TABLE IF NOT EXISTS `document_number_sequences` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL DEFAULT 0,
`prefix` varchar(20) NOT NULL DEFAULT '',
`format_type` enum('YY','YYMM','YYMMDD','none') NOT NULL DEFAULT 'YYMM',
`year` smallint(4) unsigned NOT NULL DEFAULT 0,
`month` tinyint(2) unsigned NOT NULL DEFAULT 0,
`day` tinyint(2) unsigned NOT NULL DEFAULT 0,
`last_sequence` int(11) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_scope` (`company_id`, `prefix`, `format_type`, `year`, `month`, `day`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
", 'document_number_sequences');
// ── Summary ───────────────────────────────────────────────────────────────────
$total = $ok_count + $skip_count + $err_count;
echo "\n\033[1m=== Done ===\033[0m\n";
printf(" Created : %d\n", $ok_count);
printf(" Skipped : %d\n", $skip_count);
printf(" Errors : %d\n", $err_count);
echo PHP_EOL;
if ($err_count > 0) {
echo "\033[31mSetup completed with errors. Review the output above.\033[0m\n\n";
exit(1);
}
echo "\033[32mSetup complete. Both databases are ready.\033[0m\n";
echo "Next step: visit /login/index.php and register the first owner account.\n\n";
exit(0);