Skip to content

Instantly share code, notes, and snippets.

@code-boxx
Last active May 26, 2023 05:24
Show Gist options
  • Save code-boxx/96c7f63bbd7612cc8431f66702eae105 to your computer and use it in GitHub Desktop.
Save code-boxx/96c7f63bbd7612cc8431f66702eae105 to your computer and use it in GitHub Desktop.
PHP MYSQL Courier Management System

PHP MYSQL COURIER MANAGEMENT SYSTEM

https://code-boxx.com/courier-management-system-php-mysql/

NOTES

  1. Create a test database and import 1-database.sql.
  2. Change the database settings in 2-lib-ship.php to your own.
  3. Access 4a-admin.php in the browser.

LICENSE

Copyright by Code Boxx

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-- (A) SHIPMENT
CREATE TABLE `shipment` (
`ship_id` bigint(20) NOT NULL,
`send_name` varchar(255) NOT NULL,
`send_address` text NOT NULL,
`recv_name` varchar(255) NOT NULL,
`recv_address` text NOT NULL,
`ship_status` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `shipment`
ADD PRIMARY KEY (`ship_id`),
ADD KEY `send_name` (`send_name`),
ADD KEY `recv_name` (`recv_name`),
ADD KEY `ship_status` (`ship_status`);
ALTER TABLE `shipment`
MODIFY `ship_id` bigint(20) NOT NULL AUTO_INCREMENT;
-- (B) SHIPMENT HISTORY
CREATE TABLE `shipment_history` (
`ship_id` bigint(20) NOT NULL,
`ship_date` datetime NOT NULL DEFAULT current_timestamp(),
`ship_status` tinyint(1) NOT NULL,
`ship_notes` text NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `shipment_history`
ADD PRIMARY KEY (`ship_id`,`ship_date`),
ADD KEY `ship_status` (`ship_status`);
<?php
class Ship {
// (A) CONSTRUCTOR - CONNECT TO DATABASE
private $pdo = null;
private $stmt = null;
public $error = "";
function __construct () {
$this->pdo = new PDO(
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
}
// (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
function __destruct () {
if ($this->stmt!==null) { $this->stmt = null; }
if ($this->pdo!==null) { $this->pdo = null; }
}
// (C) HELPER FUNCTION - EXECUTE SQL QUERY
function query ($sql, $data=null) : void {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($data);
}
// (D) ADD SHIPMENT
function add ($sname, $saddr, $rname, $raddr) {
// (D1) ADD SHIPMENT ENTRY
$this->query(
"INSERT INTO `shipment` (`send_name`, `send_address`, `recv_name`, `recv_address`, `ship_status`) VALUES (?,?,?,?,?)",
[$sname, $saddr, $rname, $raddr, 1]
);
// (D2) ADD HISTORY
$this->history($this->pdo->lastInsertId(), 1);
// (D3) DONE
return true;
}
// (E) UPDATE SHIPMENT STATUS
function stat ($id, $stat, $notes=null) {
// (E1) UPDATE SHIPMENT ENTRY
$this->query(
"UPDATE `shipment` SET `ship_status`=? WHERE `ship_id`=?",
[$stat, $id]
);
// (E2) ADD HISTORY
$this->history($id, $stat, $notes);
// (E3) DONE
return true;
}
// (F) ADD SHIPMENT HISTORY
function history ($id, $stat, $notes=null) {
$this->query(
"INSERT INTO `shipment_history` (`ship_id`, `ship_status`, `ship_notes`) VALUES (?,?,?)",
[$id, $stat, $notes]
);
return true;
}
// (G) GET ALL SHIPMENTS
function getAll () {
$this->query("SELECT * FROM `shipment`");
return $this->stmt->fetchAll();
}
// (H) GET SHIPMENT
function get ($id) {
$this->query("SELECT * FROM `shipment` WHERE `ship_id`=?", [$id]);
return $this->stmt->fetch();
}
// (I) GET SHIPMENT HISTORY
function getHistory ($id) {
$this->query("SELECT * FROM `shipment_history` WHERE `ship_id`=? ORDER BY `ship_date` DESC", [$id]);
return $this->stmt->fetchAll();
}
}
// (J) SHIPMENT STATUS CODES - CHANGE TO YOUR OWN!
define("SHIP_STATUS", [
1 => "Accepted",
2 => "Transit",
3 => "Delivered",
0 => "Canceled"
]);
// (K) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");
// (L) NEW SHIPMENT OBJECT
$_SHIP = new Ship();
<?php
if (isset($_POST["req"])) {
require "2-lib-ship.php";
switch ($_POST["req"]) {
// (A) ADD SHIPMENT
case "add":
echo $_SHIP->add($_POST["sname"], $_POST["saddr"], $_POST["rname"], $_POST["raddr"])
? "OK" : "ERROR";
break;
// (B) UPDATE SHIPMENT STATUS
case "stat":
echo $_SHIP->stat($_POST["id"], $_POST["stat"], isset($_POST["notes"])?$_POST["notes"]:null)
? "OK" : "ERROR";
break;
// (C) GET ALL SHIPMENTS
case "getAll":
echo json_encode($_SHIP->getAll());
break;
// (D) GET SHIPMENT
case "get":
echo json_encode($_SHIP->get($_POST["id"]));
break;
// (E) GET SHIPMENT HISTORY
case "getHistory":
echo json_encode($_SHIP->getHistory($_POST["id"]));
break;
}}
<!DOCTYPE html>
<html>
<head>
<title>Dummy Admin</title>
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="4c-styles.css">
<script>
// (A) OUTPUT SHIPMENT STATUS CODES IN JS
var stat = JSON.parse('<?php
require "2-lib-ship.php";
echo json_encode(SHIP_STATUS);
?>');
</script>
<script src="4b-admin.js"></script>
</head>
<body>
<!-- (B) SHIPMENT LIST -->
<div id="listWrap">
<div id="listAdd" class="flex" onclick="ship.toggle(1)">
<i class="mi">add_circle_outline</i>
<span>NEW SHIPMENT</span>
</div>
<div id="listShip"></div>
</div>
<!-- (C) ADD SHIPMENT -->
<form id="formAdd" class="form hide" onsubmit="return ship.add()">
<div class="fClose" onclick="ship.toggle(0)">
<i class="mi">close</i>
</div>
<h2>ADD SHIPMENT</h2>
<label>Sender Name</label>
<input type="text" required name="sname">
<label>Sender Address</label>
<input type="text" required name="saddr">
<label>Receiver Name</label>
<input type="text" required name="rname">
<label>Receiver Address</label>
<input type="text" required name="raddr">
<input type="submit" value="Save">
</form>
<!-- (D) UPDATE SHIPMENT STATUS -->
<form id="formStat" class="form hide" onsubmit="return ship.stat()">
<div class="fClose" onclick="ship.toggle(0)">
<i class="mi">close</i>
</div>
<h2>SHIPMENT HISTORY</h2>
<input type="hidden" id="statID" name="id" required>
<label>Status</label>
<select id="statCode" name="stat" required><?php
foreach (SHIP_STATUS as $i=>$s) {
echo "<option value='$i'>$s</option>";
}
?></select>
<label>Notes (if any)</label>
<input type="text" name="notes">
<input type="submit" value="Save">
</form>
</body>
</html>
var ship = {
// (A) HELPER - AJAX FETCH
fetch : (req, data, after) => {
// (A1) FORM DATA
let form = new FormData(data instanceof HTMLElement ? data : undefined);
form.append("req", req);
if (data!=null && !(data instanceof HTMLElement)) {
for (let [k,v] of Object.entries(data)) { form.append(k, v); }
}
// (A2) DO FETCH
fetch("3-ajax-ship.php", { method : "POST", body : form })
.then(res => res.text())
.then(txt => after(txt))
.catch(err => console.error(err));
},
// (B) INITIALIZE
hListW : null, // html shipment list wrapper
hList : null, // html shipment list
hAdd : null, // html add shipment form
hStat : null, // html shipment status form
hsID : null, // html update status id
hsCode : null, // html update status code
init : () => {
// (B1) GET HTML ELEMENTS
ship.hListW = document.getElementById("listWrap");
ship.hList = document.getElementById("listShip");
ship.hAdd = document.getElementById("formAdd");
ship.hStat = document.getElementById("formStat");
ship.hsID = document.getElementById("statID");
ship.hsCode = document.getElementById("statCode");
// (B2) DRAW SHIPMENT LIST
ship.draw();
},
// (C) DRAW SHIPMENT LIST
draw : () => {
ship.hList.innerHTML = "";
ship.fetch("getAll", null, data => {
JSON.parse(data).forEach(s => {
let row = document.createElement("div");
row.className = "row flex";
row.innerHTML =
`<div class="rowInfo flexGrow">
<div class="rFrom">
<strong>Sender</strong><br>
<div>${s.send_name} ${s.send_address}</div>
</div>
<div class="rTo">
<strong>Recipient</strong><br>
<div>${s.recv_name} ${s.recv_address}</div>
</div>
<span class="rStat${s.ship_status==0?" red":""}">
${stat[s.ship_status]}
</span>
</div>
<a class="mi" target="_blank" href="5a-print.php?id=${s.ship_id}">print</a>
<button class="mi" onclick="ship.toggle(2, ${s.ship_id})">update</button>`;
ship.hList.appendChild(row);
});
});
},
// (D) TOGGLE SHIPMENT SCREENS
toggle : (show, id) => {
// (D1) SHOW SHIPMENT LIST
if (show==0) {
ship.hListW.classList.remove("hide");
ship.hAdd.classList.add("hide");
ship.hStat.classList.add("hide");
}
// (D2) SHOW ADD SHIPMENT
else if (show==1) {
ship.hAdd.reset();
ship.hListW.classList.add("hide");
ship.hAdd.classList.remove("hide");
ship.hStat.classList.add("hide");
}
// (D3) SHOW UPDATE SHIPMENT STATUS
else {
ship.fetch("get", { id : id }, data => {
data = JSON.parse(data);
ship.hStat.reset();
ship.hsID.value = id;
ship.hsCode.value = data["ship_status"];
ship.hListW.classList.add("hide");
ship.hAdd.classList.add("hide");
ship.hStat.classList.remove("hide");
});
}
},
// (E) SAVE NEW SHIPMENT
add : () => {
ship.fetch("add", ship.hAdd, res => {
if (res == "OK") {
ship.draw();
ship.toggle(0);
} else { alert(res); }
});
return false;
},
// (F) UPDATE SHIPMENT STATUS
stat : () => {
ship.fetch("stat", ship.hStat, res => {
if (res == "OK") {
ship.draw();
ship.toggle(0);
} else { alert(res); }
});
return false;
}
};
window.onload = ship.init;
/* (A) GOOGLE MATERIAL ICONS - https://fonts.google.com/icons */
.mi{font-family:"Material Icons";font-weight:400;font-style:normal;text-decoration:none;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased}
/* (B) WHOLE PAGE */
* {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
}
body {
max-width: 600px;
padding: 20px;
margin: 0 auto;
background : #f3f5f9;
}
.flex {
display: flex;
align-items: center;
}
.flexGrow { flex-grow: 1; }
.hide { display: none !important; }
/* (C) SHIPMENT LIST */
/* (C1) ADD SHIPMENT */
#listAdd {
justify-content: center;
padding: 15px 0;
color: #3926d5;
border: 2px dashed #3926d5;
cursor: pointer;
}
#listAdd i {
font-size: 24px;
padding-right: 5px;
}
#listAdd span { font-size: 18px; }
/* (C2) SHIPMENT ROWS */
.row {
padding: 20px;
margin: 10px 0;
border: 1px solid #e1e1e1;
background : #fff;
}
.rInfo { color: #333; }
.rStat {
font-size: 0.8em;
background: #8effb1;
padding: 3px 10px;
}
.rStat.red{ background: #ffcece; }
.rTo { margin: 15px 0; }
.rFrom strong, .rTo strong { color: #ff5a5a; }
.row button, .row a {
font-size: 30px;
padding: 0 10px;
border: 0;
color: #273da9;
background: 0;
cursor: pointer;
}
/* (D) FORM SHARED */
.fClose {
position: absolute;
top: 0; right: 0;
padding: 5px;
font-size: 24px;
color: #fff;
background: #910909;
cursor: pointer;
}
.form {
padding: 20px;
border: 1px solid #e1e1e1;
background: #fff;
position: relative;
}
.form label, .form input, .form select {
display: block;
width: 100%;
}
.form label {
color: #707070;
margin: 15px 0 5px 0;
}
.form input, .form select { padding: 10px; }
.form input[type=submit] {
font-weight: 700;
margin-top: 20px;
padding: 15px 0;
border: 0;
color: #fff;
background: #910909;
}
<!DOCTYPE html>
<html>
<head>
<title>Dummy Shipping Label</title>
<meta charset="utf-8">
<style>
* {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
}
#shipWrap {
width: 500px;
border: 1px solid #000;
}
.section {
display: grid;
grid-template-columns: 150px 1fr;
padding: 15px;
border-bottom: 5px solid #000;
font-size: 20px;
}
#company {
padding: 20px;
text-align: center;
}
</style>
</head>
<body>
<?php
// (A) GET SHIPMENT
require "2-lib-ship.php";
$shipment = $_SHIP->get($_GET["id"]);
?>
<div id="shipWrap">
<!-- (B) SHIP TO -->
<div class="section">
<strong>Ship To</strong>
<div>
<div><?=$shipment["recv_name"]?></div>
<div><?=$shipment["recv_address"]?></div>
</div>
</div>
<!-- (C) SHIP FROM -->
<div class="section">
<strong>Ship From</strong>
<div>
<div><?=$shipment["send_name"]?></div>
<div><?=$shipment["send_address"]?></div>
</div>
</div>
<!-- (D) QR CODE -->
<!-- https://davidshimjs.github.io/qrcodejs/ -->
<div class="section">
<strong>Tracking</strong>
<div id="qrcode"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script>
new QRCode(document.getElementById("qrcode"), {
text: "http://localhost/5b-track.php?id=<?=$_GET["id"]?>",
width: 128, height: 128
});
</script>
</div>
<!-- (E) COMANY INFO -->
<div id="company">
<div>blah blah company</div>
<div>blah blah slogan</div>
<div>blah blah address</div>
<div>whatever else contact security codes</div>
<a href="http://localhost/5b-track.php?id=<?=$_GET["id"]?>">http://localhost/5b-track.php?id=<?=$_GET["id"]?></a>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Track Shipment</title>
<meta charset="utf-8">
</head>
<body><?php
// (A) GET SHIPMENT HISTORY
require "2-lib-ship.php";
$history = $_SHIP->getHistory($_GET["id"]);
// (B) SHOW HISTORY
foreach ($history as $h) {
printf("<div>[%s] %s</div>",
$h["ship_date"], SHIP_STATUS[$h["ship_status"]]
);
}
?></body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment