Update: 2026-05-03 23:08:56
This commit is contained in:
40
app/modules_app/tenants/create.php
Normal file
40
app/modules_app/tenants/create.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Create Tenant Endpoint (Super Admin Only)
|
||||
*/
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Core\Validator;
|
||||
use App\Middleware\AuthMiddleware;
|
||||
|
||||
$decoded = AuthMiddleware::check();
|
||||
|
||||
if ($decoded['role'] !== 'super_admin') {
|
||||
json_error('Unauthorized', 403);
|
||||
}
|
||||
|
||||
$data = input();
|
||||
|
||||
$errors = Validator::validate($data, [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email'
|
||||
]);
|
||||
|
||||
if ($errors) {
|
||||
json_error('Validation Failed', 422, $errors);
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO tenants (name, email, phone, status, created_at) VALUES (?, ?, ?, 'active', NOW())");
|
||||
$stmt->execute([
|
||||
$data['name'],
|
||||
$data['email'],
|
||||
$data['phone'] ?? null
|
||||
]);
|
||||
|
||||
json_success(null, 'تم إنشاء المكتب بنجاح');
|
||||
} catch (\Exception $e) {
|
||||
json_error('حدث خطأ أثناء حفظ البيانات', 500);
|
||||
}
|
||||
20
app/modules_app/tenants/index.php
Normal file
20
app/modules_app/tenants/index.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* Tenants List Endpoint (Super Admin Only)
|
||||
*/
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Middleware\AuthMiddleware;
|
||||
|
||||
$decoded = AuthMiddleware::check();
|
||||
|
||||
if ($decoded['role'] !== 'super_admin') {
|
||||
json_error('Unauthorized', 403);
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$stmt = $db->query("SELECT id, name, email, phone, status, created_at FROM tenants ORDER BY created_at DESC");
|
||||
$tenants = $stmt->fetchAll();
|
||||
|
||||
json_success($tenants);
|
||||
@@ -25,6 +25,8 @@ $routes = [
|
||||
'v1/companies/create' => ['POST', 'companies/create.php'],
|
||||
'v1/invoices/upload' => ['POST', 'invoices/upload.php'],
|
||||
'v1/dashboard/stats' => ['GET', 'dashboard/stats.php'],
|
||||
'v1/tenants' => ['GET', 'tenants/index.php'],
|
||||
'v1/tenants/create' => ['POST', 'tenants/create.php'],
|
||||
];
|
||||
|
||||
if (isset($routes[$route])) {
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
</div>
|
||||
<nav class="flex-1 px-4 space-y-2">
|
||||
<a href="#" @click="page='dashboard'" class="block p-3 rounded hover:bg-gray-800" :class="page==='dashboard'?'bg-emerald-900/20 text-emerald-500':''">📊 لوحة التحكم</a>
|
||||
<a href="#" @click="page='companies'" class="block p-3 rounded hover:bg-gray-800" :class="page==='companies'?'bg-emerald-900/20 text-emerald-500':''">🏢 الشركات</a>
|
||||
<a x-show="user?.role === 'super_admin'" href="#" @click="page='tenants'" class="block p-3 rounded hover:bg-gray-800" :class="page==='tenants'?'bg-emerald-900/20 text-emerald-500':''">🏢 المكاتب</a>
|
||||
<a x-show="user?.role !== 'accountant' && user?.role !== 'employee'" href="#" @click="page='companies'" class="block p-3 rounded hover:bg-gray-800" :class="page==='companies'?'bg-emerald-900/20 text-emerald-500':''">🏢 الشركات</a>
|
||||
<a x-show="user?.role === 'super_admin' || user?.role === 'admin'" href="#" @click="page='users'" class="block p-3 rounded hover:bg-gray-800" :class="page==='users'?'bg-emerald-900/20 text-emerald-500':''">👥 المستخدمون</a>
|
||||
</nav>
|
||||
<div class="p-6 border-t border-gray-800">
|
||||
@@ -43,6 +44,7 @@
|
||||
<header class="mb-10 flex justify-between items-center">
|
||||
<h2 class="text-2xl font-bold" x-text="title()"></h2>
|
||||
<div class="flex items-center gap-4">
|
||||
<button x-show="page==='tenants' && user?.role === 'super_admin'" @click="showAddTenantModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-4 py-2 rounded text-sm font-bold transition">➕ إضافة مكتب</button>
|
||||
<button x-show="page==='users' && (user?.role === 'super_admin' || user?.role === 'admin')" @click="showAddModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-4 py-2 rounded text-sm font-bold transition">➕ إضافة مستخدم</button>
|
||||
<button x-show="page==='companies' && (user?.role === 'super_admin' || user?.role === 'admin')" @click="showAddCompanyModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-4 py-2 rounded text-sm font-bold transition">➕ إضافة شركة</button>
|
||||
<div class="text-sm text-gray-500" x-text="user?.name"></div>
|
||||
@@ -159,6 +161,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Tenant Modal -->
|
||||
<div x-show="showAddTenantModal" class="fixed inset-0 bg-black/80 flex items-center justify-center p-4 z-50" style="display: none;">
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-lg p-6 w-full max-w-md shadow-2xl" @click.outside="showAddTenantModal = false">
|
||||
<h3 class="text-xl font-bold mb-6 text-emerald-500">مكتب جديد</h3>
|
||||
<form @submit.prevent="createTenant" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 uppercase mb-1">اسم المكتب</label>
|
||||
<input type="text" x-model="newTenant.name" required class="w-full bg-gray-950 border border-gray-800 p-3 rounded outline-none focus:border-emerald-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 uppercase mb-1">البريد الإلكتروني</label>
|
||||
<input type="email" x-model="newTenant.email" required class="w-full bg-gray-950 border border-gray-800 p-3 rounded outline-none focus:border-emerald-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 uppercase mb-1">الهاتف</label>
|
||||
<input type="text" x-model="newTenant.phone" class="w-full bg-gray-950 border border-gray-800 p-3 rounded outline-none focus:border-emerald-500">
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 p-3 rounded font-bold transition">حفظ</button>
|
||||
<button type="button" @click="showAddTenantModal = false" class="flex-1 bg-gray-800 hover:bg-gray-700 p-3 rounded transition">إلغاء</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Company Modal -->
|
||||
<div x-show="showAddCompanyModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50" x-cloak>
|
||||
<div class="bg-surface border border-gray-800 w-full max-w-md p-8 rounded-lg shadow-2xl" @click.away="showAddCompanyModal = false">
|
||||
|
||||
45
scripts/fix_data.php
Normal file
45
scripts/fix_data.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Data Reset & Fix Script
|
||||
* Clears corrupted company/assignment data but keeps Users and Tenants.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../app/bootstrap/init.php';
|
||||
|
||||
use App\Core\Database;
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "--- Starting Data Reset ---\n";
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// 1. Clear corrupted data tables
|
||||
$db->exec("SET FOREIGN_KEY_CHECKS = 0");
|
||||
$db->exec("TRUNCATE TABLE user_company_assignments");
|
||||
$db->exec("TRUNCATE TABLE invoices");
|
||||
$db->exec("TRUNCATE TABLE companies");
|
||||
$db->exec("SET FOREIGN_KEY_CHECKS = 1");
|
||||
echo "[OK] Cleared companies, invoices, and assignments.\n";
|
||||
|
||||
// 2. Ensure Super Admin does not have a tenant_id (if your schema allows NULL, else set to empty string)
|
||||
// Actually, schema.sql says tenant_id CHAR(36) NOT NULL.
|
||||
// This is a flaw in schema.sql for Super Admins. We will leave users alone for now.
|
||||
|
||||
// 3. Fix the admin's tenant_id to match the first available tenant
|
||||
$stmt = $db->query("SELECT id FROM tenants LIMIT 1");
|
||||
$tenantId = $stmt->fetchColumn();
|
||||
|
||||
if ($tenantId) {
|
||||
$db->exec("UPDATE users SET tenant_id = '$tenantId' WHERE role != 'super_admin'");
|
||||
echo "[OK] Linked all non-super-admin users to Tenant ID: $tenantId\n";
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
echo "--- Reset Complete ---\n";
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$db->rollBack();
|
||||
echo "[ERROR] Reset failed: " . $e->getMessage() . "\n";
|
||||
}
|
||||
@@ -59,10 +59,10 @@ echo "User ID {$user['id']} migrated successfully.\n";
|
||||
// 4. Create user_company_assignments table
|
||||
try {
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS user_company_assignments (
|
||||
id CHAR(36) NOT NULL DEFAULT (UUID()),
|
||||
user_id CHAR(36) NOT NULL,
|
||||
company_id CHAR(36) NOT NULL,
|
||||
assigned_by CHAR(36) NOT NULL,
|
||||
id INT AUTO_INCREMENT,
|
||||
user_id VARCHAR(100) NOT NULL,
|
||||
company_id VARCHAR(100) NOT NULL,
|
||||
assigned_by VARCHAR(100) NOT NULL,
|
||||
assigned_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (id),
|
||||
@@ -78,7 +78,7 @@ try {
|
||||
|
||||
// 5. Update invoices table to include uploaded_by
|
||||
try {
|
||||
$db->exec("ALTER TABLE invoices ADD COLUMN uploaded_by CHAR(36) NULL AFTER status");
|
||||
$db->exec("ALTER TABLE invoices ADD COLUMN uploaded_by VARCHAR(100) NULL AFTER status");
|
||||
$db->exec("ALTER TABLE invoices ADD CONSTRAINT fk_inv_uploader FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL");
|
||||
echo "[OK] Updated invoices table with uploaded_by tracker.\n";
|
||||
} catch (\Exception $e) {
|
||||
|
||||
Reference in New Issue
Block a user