diff --git a/app/Http/Controllers/BranchController.php b/app/Http/Controllers/BranchController.php
index c3ee064..12912ac 100644
--- a/app/Http/Controllers/BranchController.php
+++ b/app/Http/Controllers/BranchController.php
@@ -17,7 +17,7 @@ public function index(Request $request)
}
$branches = $query->get();
- // Attach is_deletable flag to each branch
+ // Attach is_deletable flag and total revenue to each branch
$branches->each(function ($branch) {
$inUse = \App\Models\Staff::where('branch_id', $branch->id)->exists()
|| \App\Models\Product::where('branch_id', $branch->id)->exists()
@@ -27,6 +27,9 @@ public function index(Request $request)
|| \App\Models\ProductSale::where('branch_id', $branch->id)->exists()
|| \App\Models\Receptionist::where('branch_id', $branch->id)->exists();
$branch->is_deletable = !$inUse;
+
+ // Calculate total revenue (total credit in accounts)
+ $branch->total_revenue = \App\Models\Account::where('branch_id', $branch->id)->sum('credit');
});
return response()->json($branches);
@@ -123,6 +126,21 @@ public function update(Request $request, $id)
}
}
+ // Process new documents if provided
+ if (!empty($validated['new_docs'])) {
+ foreach ($validated['new_docs'] as $doc) {
+ $path = $doc['file']->store('branch_documents', 'public');
+ BranchDocument::create([
+ 'branch_id' => $branch->id,
+ 'name' => $doc['name'],
+ 'document_number' => $doc['document_number'] ?? null,
+ 'path' => $path,
+ 'expiry_date' => $doc['expiry_date'],
+ 'reminder_days' => $doc['reminder_days'] ?? 30
+ ]);
+ }
+ }
+
return response()->json(['message' => 'Branch updated successfully', 'branch' => $branch->load('documents')]);
}
diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php
index f0a0233..f4d290a 100644
--- a/app/Http/Controllers/ExpenseController.php
+++ b/app/Http/Controllers/ExpenseController.php
@@ -104,8 +104,18 @@ public function getSalaryHistory(Request $request)
$query = Expense::with(['branch'])
->where('expense_category_id', $salaryCategory->id);
- if ($user->isReceptionist()) {
- $query->where('branch_id', $user->branch_id);
+ $branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id');
+ $startDate = $request->query('start_date');
+ $endDate = $request->query('end_date');
+
+ if ($branchId) {
+ $query->where('branch_id', $branchId);
+ }
+ if ($startDate) {
+ $query->where('date', '>=', $startDate);
+ }
+ if ($endDate) {
+ $query->where('date', '<=', $endDate);
}
$expenses = $query->orderBy('date', 'desc')->get();
diff --git a/app/Http/Controllers/InventoryController.php b/app/Http/Controllers/InventoryController.php
index 0b263b9..2061cc9 100644
--- a/app/Http/Controllers/InventoryController.php
+++ b/app/Http/Controllers/InventoryController.php
@@ -145,7 +145,14 @@ public function getSales(Request $request)
$query->where('date', '<=', $endDate);
}
- return response()->json($query->orderBy('date', 'desc')->get());
+ $sales = $query->orderBy('date', 'desc')->get()->map(function($s) {
+ $originalTotal = ($s->subtotal_amount ?? 0) + ($s->vat_amount ?? 0);
+ $s->is_adjusted = abs($s->total_amount - $originalTotal) > 0.01;
+ $s->original_amount = $originalTotal;
+ return $s;
+ });
+
+ return response()->json($sales);
}
public function storeSale(Request $request)
diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php
index 6fc50c8..b7a7a81 100644
--- a/app/Http/Controllers/ReportController.php
+++ b/app/Http/Controllers/ReportController.php
@@ -22,8 +22,12 @@ public function getProfitReport(Request $request)
$branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id');
$startDate = $request->query('start_date');
$endDate = $request->query('end_date');
+ $page = $request->query('page', 1);
+ $perPage = $request->query('per_page', 10);
- $query = Account::with('accountable');
+ // Base Query from Account table (Ledger)
+ $query = Account::query()->with('accountable');
+
if ($branchId) {
$query->where('branch_id', $branchId);
}
@@ -34,24 +38,18 @@ public function getProfitReport(Request $request)
$query->where('date', '<=', $endDate);
}
+ // Stats from the same filtered query
$totalCredits = (clone $query)->sum('credit');
$totalDebits = (clone $query)->sum('debit');
- // Fetch All Ledger Transactions for the breakdown
- $accounts = Account::where(function($q) {
+ // Fetch Paginated Transactions from the same filtered query
+ $allTransactions = $query->where(function($q) {
$q->where('credit', '>', 0)->orWhere('debit', '>', 0);
- });
-
- if ($branchId) {
- $accounts->where('branch_id', $branchId);
- }
- if ($startDate) {
- $accounts->where('date', '>=', $startDate);
- }
- if ($endDate) {
- $accounts->where('date', '<=', $endDate);
- }
- $accounts = $accounts->get()
+ })
+ ->orderBy('date', 'desc')
+ ->orderBy('time', 'desc')
+ ->orderBy('id', 'desc')
+ ->get()
->map(function($a) {
$isAdjusted = false;
$originalAmount = $a->credit > 0 ? $a->credit : $a->debit;
@@ -68,41 +66,22 @@ public function getProfitReport(Request $request)
}
return [
+ 'id' => $a->id,
'date' => $a->date,
+ 'time' => $a->time || '00:00:00',
'type' => $a->credit > 0 ? 'Income' : 'Expense',
'category' => $a->type,
'description' => $a->description,
'amount' => $a->credit > 0 ? $a->credit : $a->debit,
- 'branch' => 'N/A',
+ 'branch' => 'N/A', // Branch name if needed can be added via relation
'is_adjusted' => $isAdjusted,
'original_amount' => $originalAmount,
'remarks' => $remarks
];
});
- $expensesQuery = Expense::with('category', 'branch');
- if ($branchId) {
- $expensesQuery->where('branch_id', $branchId);
- }
- if ($startDate) {
- $expensesQuery->where('date', '>=', $startDate);
- }
- if ($endDate) {
- $expensesQuery->where('date', '<=', $endDate);
- }
-
- $expenses = $expensesQuery->get()->map(function($e) {
- return [
- 'date' => $e->date,
- 'type' => 'Expense',
- 'category' => $e->category->name ?? 'Other',
- 'description' => $e->remarks,
- 'amount' => $e->amount,
- 'branch' => $e->branch->name ?? 'Global'
- ];
- });
-
- $transactions = $accounts->concat($expenses)->sortByDesc('date')->values();
+ $totalTransactions = $allTransactions->count();
+ $paginatedTransactions = $allTransactions->forPage($page, $perPage)->values();
$lowStockCount = \App\Models\Product::query();
if ($branchId) {
@@ -121,9 +100,11 @@ public function getProfitReport(Request $request)
->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()])
->sum('credit');
- $monthExpense = Expense::when($branchId, fn($q) => $q->where('branch_id', $branchId))
+ // For trend, we can also use Account table for expenses
+ $monthExpense = Account::where('branch_id', $branchId ?: '!=', 0)
+ ->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()])
- ->sum('amount');
+ ->sum('debit');
$trend[] = [
'month' => $monthStart->format('M'),
@@ -139,7 +120,13 @@ public function getProfitReport(Request $request)
'total_expense' => $totalDebits,
'net_profit' => $totalCredits - $totalDebits,
'low_stock_count' => $lowStockCount,
- 'transactions' => $transactions,
+ 'transactions' => $paginatedTransactions,
+ 'pagination' => [
+ 'total' => $totalTransactions,
+ 'per_page' => (int)$perPage,
+ 'current_page' => (int)$page,
+ 'last_page' => ceil($totalTransactions / $perPage)
+ ],
'trend' => $trend
]);
}
@@ -151,6 +138,8 @@ public function getExpiryReminders(Request $request)
if (!$user) return response()->json(['message' => 'Unauthenticated'], 401);
$branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id');
+ $startDate = $request->query('start_date');
+ $endDate = $request->query('end_date');
$staffDocsQuery = \App\Models\StaffDocument::with('staff.branch')
->whereNotNull('expiry_date');
@@ -161,6 +150,13 @@ public function getExpiryReminders(Request $request)
});
}
+ if ($startDate) {
+ $staffDocsQuery->where('expiry_date', '>=', $startDate);
+ }
+ if ($endDate) {
+ $staffDocsQuery->where('expiry_date', '<=', $endDate);
+ }
+
$staffDocs = $staffDocsQuery->get()
->filter(function($doc) use ($today) {
$expiryDate = Carbon::parse($doc->expiry_date)->startOfDay();
@@ -192,6 +188,13 @@ public function getExpiryReminders(Request $request)
$branchDocsQuery->where('branch_id', $branchId);
}
+ if ($startDate) {
+ $branchDocsQuery->where('expiry_date', '>=', $startDate);
+ }
+ if ($endDate) {
+ $branchDocsQuery->where('expiry_date', '<=', $endDate);
+ }
+
$branchReminders = $branchDocsQuery->get()
->filter(function($doc) use ($today) {
$expiryDate = Carbon::parse($doc->expiry_date);
@@ -216,7 +219,7 @@ public function getExpiryReminders(Request $request)
});
return response()->json([
- 'reminders' => $staffDocs->concat($branchReminders)->values()
+ 'reminders' => $staffDocs->concat($branchReminders)->sortBy('expiry_date')->values()
]);
}
@@ -311,7 +314,7 @@ public function getInvestmentReport(Request $request)
return response()->json([
'total_invested' => $totalInvested,
'total_roi_returned' => $totalROIReturned,
- 'investors' => $reportData
+ 'investors' => collect($reportData)->sortByDesc('investment_date')->values()
]);
}
}
diff --git a/resources/js/Pages/Owner/Branches/List.jsx b/resources/js/Pages/Owner/Branches/List.jsx
index 4c153a3..34a934f 100644
--- a/resources/js/Pages/Owner/Branches/List.jsx
+++ b/resources/js/Pages/Owner/Branches/List.jsx
@@ -63,7 +63,7 @@ export default function List() {
{ header: 'Manager', key: 'manager_name' },
{
header: 'Revenue (AED)',
- render: () => 0.00
+ render: (row) => {(row.total_revenue || 0).toLocaleString()} AED
},
{
header: 'Status',
diff --git a/resources/js/Pages/Owner/Collections/AddCollectionModal.jsx b/resources/js/Pages/Owner/Collections/AddCollectionModal.jsx
index fca64b9..35f15b2 100644
--- a/resources/js/Pages/Owner/Collections/AddCollectionModal.jsx
+++ b/resources/js/Pages/Owner/Collections/AddCollectionModal.jsx
@@ -215,12 +215,16 @@ export default function AddCollectionModal({ isOpen, onClose, onSave, branches,
) : (
@@ -236,7 +240,7 @@ export default function AddCollectionModal({ isOpen, onClose, onSave, branches,
@@ -258,7 +266,7 @@ export default function AddCollectionModal({ isOpen, onClose, onSave, branches,
)}
- {activeTab !== 'Expiry Reminders' && (
- <>
-
-
- setStartDate(e.target.value)}
- />
-
-
-
- setEndDate(e.target.value)}
- />
-
- >
- )}
+
+
+ setStartDate(e.target.value)}
+ />
+
+
+
+ setEndDate(e.target.value)}
+ />
+
+
@@ -954,7 +966,24 @@ export default function ReportIndex() {
{s.payment_method} |
{parseFloat(s.subtotal_amount || 0).toFixed(2)} AED |
{parseFloat(s.vat_amount || 0).toFixed(2)} AED |
- {parseFloat(s.total_amount).toLocaleString()} AED |
+
+
+ {parseFloat(s.total_amount).toLocaleString()} AED
+ {s.is_adjusted && (
+
+ )}
+
+ |
))
) : (
diff --git a/resources/js/Pages/Owner/Staff/Edit.jsx b/resources/js/Pages/Owner/Staff/Edit.jsx
index 8cda8e7..e6e5901 100644
--- a/resources/js/Pages/Owner/Staff/Edit.jsx
+++ b/resources/js/Pages/Owner/Staff/Edit.jsx
@@ -382,9 +382,20 @@ export default function StaffEdit({ id }) {
))}
-
-
-
+
+ {!isReceptionist && (
+
+
+
+
+ {branches.map(branch => (
+
+ ))}
+
+
+ )}
+
+
diff --git a/resources/js/Pages/Receptionist/Dashboard.jsx b/resources/js/Pages/Receptionist/Dashboard.jsx
index d164a04..5c1cd0d 100644
--- a/resources/js/Pages/Receptionist/Dashboard.jsx
+++ b/resources/js/Pages/Receptionist/Dashboard.jsx
@@ -1,11 +1,10 @@
import React, { useState, useEffect } from 'react';
// Header and SubHeader are now part of the global Layout
import {
- Wallet,
- TrendingUp,
- ArrowUpRight,
- ArrowDownRight,
DollarSign,
+ TrendingUp,
+ Calendar,
+ ChevronDown,
Activity,
ShoppingCart,
Package,
@@ -23,11 +22,29 @@ export default function ReceptionistDashboard() {
});
const [transactions, setTransactions] = useState([]);
const [loading, setLoading] = useState(true);
+ const [filterBranch, setFilterBranch] = useState(window.__APP_DATA__?.branch?.id || '');
+ const [startDate, setStartDate] = useState(new Date().toISOString().split('T')[0]);
+ const [endDate, setEndDate] = useState(new Date().toISOString().split('T')[0]);
+ const [branches, setBranches] = useState([]);
+
+ useEffect(() => {
+ // Fetch branches for the selector (even if disabled for receptionist)
+ fetch('/api/branches?status=Active')
+ .then(res => res.json())
+ .then(data => setBranches(data))
+ .catch(err => console.error("Error fetching branches:", err));
+ }, []);
useEffect(() => {
const fetchDashboardData = async () => {
+ setLoading(true);
try {
- const response = await fetch('/api/reports/profit');
+ const query = new URLSearchParams({
+ branch_id: filterBranch,
+ start_date: startDate,
+ end_date: endDate
+ });
+ const response = await fetch(`/api/reports/profit?${query}`);
const data = await response.json();
setStats({
@@ -45,7 +62,7 @@ export default function ReceptionistDashboard() {
};
fetchDashboardData();
- }, []);
+ }, [filterBranch, startDate, endDate]);
const StatCard = ({ title, amount, icon: Icon, color, trend, iconColor, bgColor, textColor, label }) => (
@@ -103,11 +120,56 @@ export default function ReceptionistDashboard() {
- {/* Welcome Section */}
-
-
- Receptionist Dashboard
-
+ {/* Welcome Section & Filters */}
+
+
+
+ Receptionist Dashboard
+
+
Branch Operations Overview
+
+
+
+ {/* Branch Selector (Hardcoded to their branch for receptionists) */}
+
+
+ {branches.map(b => (
+
+ ))}
+
+
+
+
+ {/* Date Range */}
+
+
{/* Stats Grid */}
@@ -188,7 +250,7 @@ export default function ReceptionistDashboard() {
{transactions.length > 0 ? (
- transactions.map((tx, idx) => (
+ transactions.slice(0, 10).map((tx, idx) => (
|
@@ -201,15 +263,15 @@ export default function ReceptionistDashboard() {
|
0 ? 'bg-red-50 text-red-600' : 'bg-emerald-50 text-emerald-600'
+ tx.type === 'Expense' ? 'bg-red-50 text-red-600' : 'bg-emerald-50 text-emerald-600'
}`}>
- {tx.debit > 0 ? 'DEBITED' : 'RECEIVED'}
+ {tx.type === 'Expense' ? 'DEBITED' : 'RECEIVED'}
|
- 0 ? 'text-red-500' : 'text-emerald-500'}`}>
- {tx.debit > 0 ? '-' : '+'}
- {(tx.debit || tx.credit || 0).toLocaleString('en-AE', { minimumFractionDigits: 2 })}
+
+ {tx.type === 'Expense' ? '-' : '+'}
+ {(tx.amount || 0).toLocaleString('en-AE', { minimumFractionDigits: 2 })}
AED
|
diff --git a/resources/js/Pages/Receptionist/POS.jsx b/resources/js/Pages/Receptionist/POS.jsx
index c35ec08..7f182f4 100644
--- a/resources/js/Pages/Receptionist/POS.jsx
+++ b/resources/js/Pages/Receptionist/POS.jsx
@@ -28,6 +28,7 @@ export default function POS() {
const [branches, setBranches] = useState([]);
const [selectedBranch, setSelectedBranch] = useState(window.__APP_DATA__?.user?.branch_id || '');
const [toast, setToast] = useState(null);
+ const [showSuccessModal, setShowSuccessModal] = useState(false);
const showToast = (message, type = 'success') => {
setToast({ message, type });
@@ -168,6 +169,7 @@ export default function POS() {
if (response.ok) {
setSuccess(true);
+ setShowSuccessModal(true);
setCart([]);
setAdjustmentRemarks('');
setTimeout(() => setSuccess(false), 3000);
@@ -416,6 +418,25 @@ export default function POS() {
+
+ {/* Success Modal */}
+ {showSuccessModal && (
+
+
+
+
+
+
Sale Successful!
+
The transaction has been processed and recorded successfully.
+
+
+
+ )}
>
);
}
diff --git a/resources/js/Pages/Receptionist/Reports/Index.jsx b/resources/js/Pages/Receptionist/Reports/Index.jsx
index 10fe517..1aeb5cf 100644
--- a/resources/js/Pages/Receptionist/Reports/Index.jsx
+++ b/resources/js/Pages/Receptionist/Reports/Index.jsx
@@ -13,8 +13,8 @@ export default function ReceptionistReportIndex() {
const [activeTab, setActiveTab] = useState('Collections');
const [loading, setLoading] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
- const [fromDate, setFromDate] = useState('2026-02-06'); // Defaulting based on screenshot
- const [toDate, setToDate] = useState('2026-03-08');
+ const [fromDate, setFromDate] = useState(new Date().toISOString().split('T')[0]);
+ const [toDate, setToDate] = useState(new Date().toISOString().split('T')[0]);
const [method, setMethod] = useState('All Methods');
const [type, setType] = useState('All Types');
const [expenseType, setExpenseType] = useState('All Types');
diff --git a/resources/js/app.jsx b/resources/js/app.jsx
index 0dd90df..81cba9e 100644
--- a/resources/js/app.jsx
+++ b/resources/js/app.jsx
@@ -169,7 +169,7 @@ function MainApp() {
const id = path.split('/').pop();
component = ;
} else if (path === '/receptionist/reports') {
- component = ;
+ component = ;
}
if (component) {
diff --git a/routes/web.php b/routes/web.php
index 203f8f3..426199c 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -118,5 +118,8 @@
Route::get('/receptionist/expenses', [OwnerController::class, 'index']);
Route::get('/receptionist/inventory', [OwnerController::class, 'index']);
Route::get('/receptionist/staff', [OwnerController::class, 'index']);
+ Route::get('/receptionist/staff/add', [OwnerController::class, 'index']);
+ Route::get('/receptionist/staff/edit/{id}', [OwnerController::class, 'index']);
+ Route::get('/receptionist/staff/view/{id}', [OwnerController::class, 'index']);
Route::get('/receptionist/reports', [OwnerController::class, 'index']);
});