From c51c984fbc382ba66d731d929d2ddd80570f51fe Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 24 Mar 2026 06:27:15 +0000 Subject: [PATCH] updating shipments fee --- admin_company_profile.php | 25 ++- admin_shipment_edit.php | 3 +- admin_shipments.php | 20 +- check_status_enum.php | 11 + db/migrations/add_pending_approval_status.php | 13 ++ db/migrations/add_pricing_model.php | 37 ++++ includes/app.php | 11 +- shipment_detail.php | 53 ++++- shipper_dashboard.php | 50 ++++- truck_owner_dashboard.php | 188 ++++++++++++------ 10 files changed, 331 insertions(+), 80 deletions(-) create mode 100644 check_status_enum.php create mode 100644 db/migrations/add_pending_approval_status.php create mode 100644 db/migrations/add_pricing_model.php diff --git a/admin_company_profile.php b/admin_company_profile.php index 2c0691f..ffd77a6 100644 --- a/admin_company_profile.php +++ b/admin_company_profile.php @@ -14,6 +14,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token(); $companyPhone = trim($_POST['company_phone'] ?? ''); $companyAddress = trim($_POST['company_address'] ?? ''); $platformCharge = trim($_POST['platform_charge_percentage'] ?? '0'); + $pricingModel = trim($_POST['pricing_model'] ?? 'percentage'); $timezone = trim($_POST['timezone'] ?? 'UTC'); $updates = [ @@ -22,6 +23,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token(); 'company_phone' => $companyPhone, 'company_address' => $companyAddress, 'platform_charge_percentage' => $platformCharge, + 'pricing_model' => $pricingModel, 'timezone' => $timezone, 'terms_en' => trim($_POST['terms_en'] ?? ''), 'terms_ar' => trim($_POST['terms_ar'] ?? ''), @@ -82,6 +84,7 @@ $currentEmail = $settings['company_email'] ?? ''; $currentPhone = $settings['company_phone'] ?? ''; $currentAddress = $settings['company_address'] ?? ''; $currentPlatformCharge = $settings['platform_charge_percentage'] ?? '0'; +$currentPricingModel = $settings['pricing_model'] ?? 'percentage'; $currentTimezone = $settings['timezone'] ?? 'UTC'; $currentLogo = $settings['logo_path'] ?? ''; $currentFavicon = $settings['favicon_path'] ?? ''; @@ -156,6 +159,26 @@ render_header('Company Profile', 'admin', true);
Displayed in the footer.
+ +
+ +
+
+ > + +
+
+ > + +
+
+
@@ -163,7 +186,7 @@ render_header('Company Profile', 'admin', true); %
-
Percentage applied as a platform fee.
+
Percentage applied as a platform fee (only for Percentage Model).
diff --git a/admin_shipment_edit.php b/admin_shipment_edit.php index 25c3462..0887ff8 100644 --- a/admin_shipment_edit.php +++ b/admin_shipment_edit.php @@ -145,7 +145,7 @@ if (!$isAjax):
- +
@@ -289,6 +289,7 @@ if (!$isAjax):
+ @@ -149,6 +153,9 @@ render_header(t('manage_shipments'), 'admin', true);
+ +
: $
+
@@ -162,11 +169,20 @@ render_header(t('manage_shipments'), 'admin', true); elseif ($shipment['status'] === 'confirmed') $statusClass = 'bg-success-subtle text-success'; elseif ($shipment['status'] === 'in_transit') $statusClass = 'bg-warning-subtle text-warning'; elseif ($shipment['status'] === 'delivered') $statusClass = 'bg-dark-subtle text-dark'; + elseif ($shipment['status'] === 'pending_approval') $statusClass = 'bg-warning text-dark'; ?>
+ +
+ + +
+ diff --git a/check_status_enum.php b/check_status_enum.php new file mode 100644 index 0000000..47ce023 --- /dev/null +++ b/check_status_enum.php @@ -0,0 +1,11 @@ +query("SHOW COLUMNS FROM shipments LIKE 'status'"); + $row = $stmt->fetch(); + echo "Type: " . $row['Type'] . "\n"; +} catch (Throwable $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + diff --git a/db/migrations/add_pending_approval_status.php b/db/migrations/add_pending_approval_status.php new file mode 100644 index 0000000..7435d93 --- /dev/null +++ b/db/migrations/add_pending_approval_status.php @@ -0,0 +1,13 @@ +exec($sql); + echo "Migration successful.\n"; +} catch (PDOException $e) { + echo "Migration failed: " . $e->getMessage() . "\n"; +} + diff --git a/db/migrations/add_pricing_model.php b/db/migrations/add_pricing_model.php new file mode 100644 index 0000000..fa6c17f --- /dev/null +++ b/db/migrations/add_pricing_model.php @@ -0,0 +1,37 @@ +exec(" + SET @dbname = DATABASE(); + SET @tablename = 'shipments'; + SET @columnname = 'target_price'; + SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE + (table_name = @tablename) + AND (table_schema = @dbname) + AND (column_name = @columnname) + ) > 0, + 'SELECT 1', + 'ALTER TABLE shipments ADD COLUMN target_price DECIMAL(10,2) DEFAULT NULL AFTER weight_tons' + )); + PREPARE alterIfNotExists FROM @preparedStatement; + EXECUTE alterIfNotExists; + DEALLOCATE PREPARE alterIfNotExists; + "); + echo "Added target_price column to shipments.\n"; + + // Insert default pricing_model setting + $pdo->exec(" + INSERT IGNORE INTO settings (setting_key, setting_value) VALUES ('pricing_model', 'percentage'); + "); + echo "Added pricing_model setting.\n"; + +} catch (PDOException $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + diff --git a/includes/app.php b/includes/app.php index 3afb3e8..3d72277 100644 --- a/includes/app.php +++ b/includes/app.php @@ -282,7 +282,7 @@ $translations = [ 'address' => 'Address', 'shipper_details' => 'Shipper Details', 'company_name' => 'Company Name', - 'truck_details' => 'Truck Details', + 'truck_details' => 'تفاصيل الشاحنة', 'truck_type' => 'Truck Type', 'load_capacity' => 'Load Capacity (Tons)', 'plate_no' => 'Plate Number', @@ -333,6 +333,9 @@ $translations = [ 'type_frozen' => 'Frozen', 'type_cold' => 'Cold', 'type_dry' => 'Dry', + 'target_price' => 'Target Price', + 'status_pending_approval' => 'Pending Approval', + 'pending_approval_msg' => 'Your shipment is pending admin approval.', ), "ar" => array ( 'app_name' => 'CargoLink', @@ -654,6 +657,9 @@ $translations = [ 'type_frozen' => 'مجمد', 'type_cold' => 'مبرد', 'type_dry' => 'جاف', + 'target_price' => 'السعر المستهدف', + 'status_pending_approval' => 'بانتظار الموافقة', + 'pending_approval_msg' => 'شحنتك بانتظار موافقة الإدارة.', ) ]; @@ -806,6 +812,7 @@ function status_label(string $status): string 'confirmed' => t('status_confirmed'), 'in_transit' => t('status_in_transit'), 'delivered' => t('status_delivered'), + 'pending_approval' => t('status_pending_approval'), ]; return $map[$status] ?? $status; } @@ -916,4 +923,4 @@ try { if ($tz && in_array($tz, DateTimeZone::listIdentifiers())) { date_default_timezone_set($tz); } -} catch (Throwable $e) {} +} catch (Throwable $e) {} \ No newline at end of file diff --git a/shipment_detail.php b/shipment_detail.php index 0f6ad7c..b1cbe2b 100644 --- a/shipment_detail.php +++ b/shipment_detail.php @@ -27,6 +27,7 @@ $isAssignedTruckOwner = $shipment && $shipment['truck_owner_id'] == $currentUser // Platform Fee Configuration $settings = get_settings(); $platformFeePercentage = (float)($settings['platform_charge_percentage'] ?? 0) / 100; +$pricingModel = $settings['pricing_model'] ?? 'percentage'; // Handle POST actions if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token(); @@ -35,6 +36,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token(); if ($action === 'submit_offer') { if (!$isTruckOwner && !$isAdmin) { $errors[] = 'Only truck owners can submit offers.'; } $offerOwner = trim($_POST['offer_owner'] ?? ''); $offerPrice = trim($_POST['offer_price'] ?? ''); + + // Enforce fixed price logic if applicable + if ($pricingModel === 'fixed_price' && !empty($shipment['target_price'])) { + $offerPrice = $shipment['target_price']; + } + if ($offerOwner === '' || $offerPrice === '') { $errors[] = t('error_required'); } elseif (!is_numeric($offerPrice)) { @@ -196,7 +203,10 @@ render_header(t('shipment_detail'));

#

:

- + +
@@ -243,6 +253,14 @@ render_header(t('shipment_detail'));
+ + +
+
+
$
+
+ +
@@ -329,6 +347,11 @@ render_header(t('shipment_detail'));

Waiting for truck owners to submit offers.

+ +
+ +

+
@@ -373,15 +396,31 @@ render_header(t('shipment_detail'));
-
- - -
- + + +
+ +
+ $ + + +
+
Fixed Price Offer
+
+ + +
+ + +
+ +
- This shipment is already confirmed/processed. + This shipment is already confirmed/processed or pending approval.
diff --git a/shipper_dashboard.php b/shipper_dashboard.php index f9e79c8..6e70e4a 100644 --- a/shipper_dashboard.php +++ b/shipper_dashboard.php @@ -7,6 +7,8 @@ require_once __DIR__ . '/includes/NotificationService.php'; ensure_schema(); $errors = []; +$settings = get_settings(); +$pricingModel = $settings['pricing_model'] ?? 'percentage'; // Try to pre-fill from profile if logged in $prefillName = $_SESSION['shipper_name'] ?? ''; @@ -34,19 +36,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'creat $pickupDate = trim($_POST['pickup_date'] ?? ''); $deliveryDate = trim($_POST['delivery_date'] ?? ''); $payment = $_POST['payment_method'] ?? 'thawani'; + + $targetPrice = ($pricingModel === 'fixed_price') ? trim($_POST['target_price'] ?? '') : null; + $initialStatus = ($pricingModel === 'fixed_price') ? 'pending_approval' : 'posted'; if ($shipperName === '' || $shipperCompany === '' || $origin === '' || $destination === '' || $cargo === '' || $weight === '' || $pickupDate === '' || $deliveryDate === '') { $errors[] = t('error_required'); } elseif (!is_numeric($weight)) { $errors[] = t('error_invalid'); } + + if ($pricingModel === 'fixed_price' && (!is_numeric($targetPrice) || $targetPrice <= 0)) { + $errors[] = t('error_invalid'); + } if (!$errors) { $shipperId = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null; $stmt = db()->prepare( - "INSERT INTO shipments (shipper_id, shipper_name, shipper_company, origin_city, destination_city, shipment_type, cargo_description, weight_tons, pickup_date, delivery_date, payment_method) - VALUES (:shipper_id, :shipper_name, :shipper_company, :origin_city, :destination_city, :shipment_type, :cargo_description, :weight_tons, :pickup_date, :delivery_date, :payment_method)" + "INSERT INTO shipments (shipper_id, shipper_name, shipper_company, origin_city, destination_city, shipment_type, cargo_description, weight_tons, pickup_date, delivery_date, payment_method, status, target_price) + VALUES (:shipper_id, :shipper_name, :shipper_company, :origin_city, :destination_city, :shipment_type, :cargo_description, :weight_tons, :pickup_date, :delivery_date, :payment_method, :status, :target_price)" ); $stmt->execute([ ':shipper_id' => $shipperId, @@ -60,6 +69,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'creat ':pickup_date' => $pickupDate, ':delivery_date' => $deliveryDate, ':payment_method' => $payment, + ':status' => $initialStatus, + ':target_price' => $targetPrice, ]); $newShipmentId = db()->lastInsertId(); @@ -78,7 +89,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'creat } } - set_flash('success', t('success_shipment')); + $msg = ($initialStatus === 'pending_approval') ? t('pending_approval_msg') : t('success_shipment'); + set_flash('success', $msg); header('Location: ' . url_with_lang('shipper_dashboard.php')); exit; } @@ -107,9 +119,17 @@ $stats = [ try { if ($filterCompany) { - $stats['total'] = (int)db()->prepare("SELECT COUNT(*) FROM shipments WHERE shipper_company = ?")->execute([$filterCompany]) ? (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany))->fetchColumn() : 0; - $stats['active'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany) . " AND status != 'delivered'")->fetchColumn(); - $stats['delivered'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany) . " AND status = 'delivered'")->fetchColumn(); + $stmt = db()->prepare("SELECT COUNT(*) FROM shipments WHERE shipper_company = ?"); + $stmt->execute([$filterCompany]); + $stats['total'] = (int)$stmt->fetchColumn(); + + $stmt = db()->prepare("SELECT COUNT(*) FROM shipments WHERE shipper_company = ? AND status != 'delivered'"); + $stmt->execute([$filterCompany]); + $stats['active'] = (int)$stmt->fetchColumn(); + + $stmt = db()->prepare("SELECT COUNT(*) FROM shipments WHERE shipper_company = ? AND status = 'delivered'"); + $stmt->execute([$filterCompany]); + $stats['delivered'] = (int)$stmt->fetchColumn(); } else { $stats['total'] = (int)db()->query("SELECT COUNT(*) FROM shipments")->fetchColumn(); $stats['active'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE status != 'delivered'")->fetchColumn(); @@ -230,6 +250,17 @@ $flash = get_flash();
+ + +
+ +
+ $ + +
+
+
+
@@ -296,8 +327,13 @@ $flash = get_flash();
+ +
+ : $ +
+ - +
$
diff --git a/truck_owner_dashboard.php b/truck_owner_dashboard.php index 8e492c6..c74f4ec 100644 --- a/truck_owner_dashboard.php +++ b/truck_owner_dashboard.php @@ -4,6 +4,8 @@ declare(strict_types=1); require_once __DIR__ . '/includes/layout.php'; require_role('truck_owner'); $userId = $_SESSION['user_id']; +$settings = get_settings(); +$pricingModel = $settings['pricing_model'] ?? 'percentage'; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'add_truck') { $truckType = trim($_POST['truck_type'] ?? ''); @@ -29,81 +31,147 @@ $trucks = db()->prepare("SELECT * FROM trucks WHERE user_id = ?"); $trucks->execute([$userId]); $myTrucks = $trucks->fetchAll(); +// Fetch Available Shipments +$availShipments = []; +try { + $stmt = db()->query("SELECT * FROM shipments WHERE status = 'posted' ORDER BY created_at DESC"); + $availShipments = $stmt->fetchAll(); +} catch (Throwable $e) {} + render_header('Truck Owner Dashboard', 'owner'); $flash = get_flash(); ?>

Truck Owner Dashboard

-

Manage your trucks and view their approval status.

+

Manage your trucks and view available shipments.

-
-
Add New Truck
-
- -
- +
+
+
+
Add New Truck
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
-
- + +
+
My Trucks
+ +

No trucks registered yet.

+ +
+ + +
+
+ + + Expired + + Approved + + Pending + +
+
Tons
+
+ +
+
-
- +
+ +
+
+
Available Shipments
+ +
+ +

No shipments available at the moment.

+
+ +
+ + + + + + + + + + + + + + + + + + + +
RouteDetailsDatesAction
+
+ + + +
+ +
+ +
+ + +
+ Price: $ +
+ +
+
+
Tons •
+
+
Pick:
+
Drop:
+
+ + View + +
+
+
-
- -
-
- -
-
- -
- -
- -
-
My Trucks
- - - - - - - - - - - - - - - - - - - - - - - - -
Truck TypeCapacity (T)Plate NoReg ExpiryIns ExpiryStatus
- - Expired - - Approved - - Pending - -
+
\ No newline at end of file