SELECT 
    TABLE_NAME,
    COLUMN_NAME,
    COLUMN_TYPE,
    IS_NULLABLE,
    COLUMN_KEY,
    COLUMN_DEFAULT,
    EXTRA
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'sademlis_ostad51'
  AND TABLE_NAME LIKE 'wp_accj%'
ORDER BY TABLE_NAME, ORDINAL_POSITION;




SET FOREIGN_KEY_CHECKS = 0;

TRUNCATE TABLE wp_accj_menu_permissions;
TRUNCATE TABLE wp_accj_menus;

SET FOREIGN_KEY_CHECKS = 1;

SET FOREIGN_KEY_CHECKS = 0;

TRUNCATE TABLE wp_accj_menu_permissions;
TRUNCATE TABLE wp_accj_menus;
TRUNCATE TABLE wp_accj_roles;

SET FOREIGN_KEY_CHECKS = 1;

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
VALUES
(NULL,'داشبورد','dashboard','core','dashboard',1,1,NOW(),'نمای کلی سیستم'),
(NULL,'CRM','crm','crm','crm',2,1,NOW(),'مدیریت مشتریان'),
(NULL,'مشاورین املاک','agents','agents','agents',3,1,NOW(),'مدیریت مشاوران'),
(NULL,'املاک','real_estate','real_estate','real-estate',4,1,NOW(),'مدیریت املاک'),
(NULL,'زمین ها','lands','lands','lands',5,1,NOW(),'مدیریت زمین ها'),
(NULL,'پروژه ها','projects','projects','projects',6,1,NOW(),'مدیریت پروژه های ساختمانی'),
(NULL,'کنترل پروژه','project_control','pc','pc',7,1,NOW(),'کنترل زمان و هزینه'),
(NULL,'قراردادها','contracts','contracts','contracts',8,1,NOW(),'مدیریت قراردادها'),
(NULL,'حسابداری','accounting','accounting','accounting',9,1,NOW(),'سیستم مالی'),
(NULL,'خرید','procurement','procurement','procurement',10,1,NOW(),'تدارکات و خرید'),
(NULL,'انبار','warehouse','warehouse','warehouse',11,1,NOW(),'مدیریت انبار'),
(NULL,'منابع انسانی','hr','hr','hr',12,1,NOW(),'مدیریت کارکنان'),
(NULL,'گزارشات','reports','reports','reports',13,1,NOW(),'گزارشات مدیریتی'),
(NULL,'تنظیمات','settings','settings','settings',14,1,NOW(),'تنظیمات سیستم');



/* CRM */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'سرنخ ها','crm_leads','crm','crm.leads',1,1,NOW(),'مدیریت سرنخ'
FROM wp_accj_menus WHERE code='crm';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'مشتریان','crm_customers','crm','crm.customers',2,1,NOW(),'مدیریت مشتری'
FROM wp_accj_menus WHERE code='crm';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پیگیری ها','crm_followups','crm','crm.followups',3,1,NOW(),'پیگیری مشتری'
FROM wp_accj_menus WHERE code='crm';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'تماس ها','crm_calls','crm','crm.calls',4,1,NOW(),'ثبت تماس'
FROM wp_accj_menus WHERE code='crm';



/* AGENTS */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'لیست مشاوران','agents_list','agents','agents.list',1,1,NOW(),'مدیریت مشاوران'
FROM wp_accj_menus WHERE code='agents';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'قرارداد مشاور','agents_contracts','agents','agents.contracts',2,1,NOW(),'قرارداد مشاوران'
FROM wp_accj_menus WHERE code='agents';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پورسانت','agents_commission','agents','agents.commission',3,1,NOW(),'پورسانت مشاوران'
FROM wp_accj_menus WHERE code='agents';



/* REAL ESTATE */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'املاک برای فروش','re_sales','real_estate','re.sales',1,1,NOW(),'مدیریت املاک فروشی'
FROM wp_accj_menus WHERE code='real_estate';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'املاک خریدنی','re_purchase','real_estate','re.purchase',2,1,NOW(),'مدیریت خرید ملک'
FROM wp_accj_menus WHERE code='real_estate';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'معاملات','re_deals','real_estate','re.deals',3,1,NOW(),'مدیریت معاملات'
FROM wp_accj_menus WHERE code='real_estate';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پیش فروش','re_presale','real_estate','re.presale',4,1,NOW(),'پیش فروش واحد'
FROM wp_accj_menus WHERE code='real_estate';



/* PROJECTS */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'لیست پروژه ها','projects_list','projects','projects.list',1,1,NOW(),'مدیریت پروژه'
FROM wp_accj_menus WHERE code='projects';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'واحدهای پروژه','projects_units','projects','projects.units',2,1,NOW(),'مدیریت واحدها'
FROM wp_accj_menus WHERE code='projects';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پیمانکاران','projects_contractors','projects','projects.contractors',3,1,NOW(),'مدیریت پیمانکار'
FROM wp_accj_menus WHERE code='projects';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'کارگاه','projects_site','projects','projects.site',4,1,NOW(),'مدیریت کارگاه'
FROM wp_accj_menus WHERE code='projects';



/* PROJECT CONTROL */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'WBS','pc_wbs','pc','pc.wbs',1,1,NOW(),'ساختار شکست کار'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'زمانبندی','pc_schedule','pc','pc.schedule',2,1,NOW(),'زمانبندی پروژه'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'BOQ','pc_boq','pc','pc.boq',3,1,NOW(),'متره و برآورد'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پیشرفت فیزیکی','pc_progress','pc','pc.progress',4,1,NOW(),'پیشرفت پروژه'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'کنترل هزینه','pc_cost','pc','pc.cost',5,1,NOW(),'کنترل هزینه'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'منابع','pc_resources','pc','pc.resources',6,1,NOW(),'مدیریت منابع'
FROM wp_accj_menus WHERE code='project_control';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'تاخیرات','pc_delays','pc','pc.delays',7,1,NOW(),'مدیریت تاخیر'
FROM wp_accj_menus WHERE code='project_control';



/* ACCOUNTING */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'سرفصل حسابها','acc_accounts','accounting','acc.accounts',1,1,NOW(),'سرفصل حساب'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'اسناد حسابداری','acc_docs','accounting','acc.docs',2,1,NOW(),'ثبت سند'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'دریافت','acc_receive','accounting','acc.receive',3,1,NOW(),'دریافت'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'پرداخت','acc_pay','accounting','acc.pay',4,1,NOW(),'پرداخت'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'دفتر کل','acc_gl','accounting','acc.gl',5,1,NOW(),'دفتر کل'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'تراز آزمایشی','acc_trial','accounting','acc.trial',6,1,NOW(),'تراز آزمایشی'
FROM wp_accj_menus WHERE code='accounting';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'سود و زیان','acc_pl','accounting','acc.pl',7,1,NOW(),'سود و زیان'
FROM wp_accj_menus WHERE code='accounting';



/* PROCUREMENT */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'درخواست خرید','proc_req','procurement','proc.req',1,1,NOW(),'درخواست خرید'
FROM wp_accj_menus WHERE code='procurement';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'سفارش خرید','proc_po','procurement','proc.po',2,1,NOW(),'سفارش خرید'
FROM wp_accj_menus WHERE code='procurement';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'تامین کنندگان','proc_suppliers','procurement','proc.suppliers',3,1,NOW(),'مدیریت تامین کننده'
FROM wp_accj_menus WHERE code='procurement';



/* WAREHOUSE */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'کالاها','wh_items','warehouse','wh.items',1,1,NOW(),'مدیریت کالا'
FROM wp_accj_menus WHERE code='warehouse';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'ورود کالا','wh_in','warehouse','wh.in',2,1,NOW(),'ورود انبار'
FROM wp_accj_menus WHERE code='warehouse';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'خروج کالا','wh_out','warehouse','wh.out',3,1,NOW(),'خروج انبار'
FROM wp_accj_menus WHERE code='warehouse';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'موجودی انبار','wh_stock','warehouse','wh.stock',4,1,NOW(),'گزارش موجودی'
FROM wp_accj_menus WHERE code='warehouse';



/* HR */
INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'کارکنان','hr_employees','hr','hr.employees',1,1,NOW(),'مدیریت کارکنان'
FROM wp_accj_menus WHERE code='hr';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'حقوق و دستمزد','hr_payroll','hr','hr.payroll',2,1,NOW(),'پرداخت حقوق'
FROM wp_accj_menus WHERE code='hr';

INSERT INTO wp_accj_menus
(parent_id, title, code, module, route, menu_order, is_active, created_at, description)
SELECT id,'حضور و غیاب','hr_attendance','hr','hr.attendance',3,1,NOW(),'حضور و غیاب'
FROM wp_accj_menus WHERE code='hr';





/** ✅ افزودن مسیرهای کوکی تا در تمام صفحات chat.local فعال باشند **/
define('ADMIN_COOKIE_PATH', '/');
define('PLUGINS_COOKIE_PATH', '/');
define('COOKIEPATH', '/');
define('SITECOOKIEPATH', '/');
define('COOKIE_DOMAIN', 'chat.local'); // یا false اگر SSL نداری



/* =========================================
RESET: DROP OLD TABLES (IF EXIST)
========================================= */

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS wp_erp_access_audit;
DROP TABLE IF EXISTS wp_erp_ui_permissions;
DROP TABLE IF EXISTS wp_erp_object_permissions;
DROP TABLE IF EXISTS wp_erp_user_roles;
DROP TABLE IF EXISTS wp_erp_role_permissions;
DROP TABLE IF EXISTS wp_erp_roles;
DROP TABLE IF EXISTS wp_erp_permissions_catalog;
DROP TABLE IF EXISTS wp_erp_ui_catalog;

SET FOREIGN_KEY_CHECKS = 1;

/* =========================================
ERP ACCESS CONTROL TABLES - CORE
========================================= */

CREATE TABLE IF NOT EXISTS wp_erp_roles (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    role_key VARCHAR(100) UNIQUE NOT NULL,
    label VARCHAR(255) NOT NULL,
    description TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS wp_erp_role_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    role_id BIGINT UNSIGNED NOT NULL,
    permission_key VARCHAR(150) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(role_id)
    -- در صورت نیاز می‌توانی بعداً FOREIGN KEY روی role_id به wp_erp_roles(id) اضافه کنی
);

CREATE TABLE IF NOT EXISTS wp_erp_user_roles (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    role_id BIGINT UNSIGNED NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(role_id)
    -- در صورت نیاز می‌توانی بعداً FOREIGN KEY روی role_id به wp_erp_roles(id) اضافه کنی
);

CREATE TABLE IF NOT EXISTS wp_erp_object_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    object_type VARCHAR(50) NOT NULL,
    object_id BIGINT UNSIGNED NOT NULL,
    actions JSON,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(object_type),
    INDEX(object_id)
);

CREATE TABLE IF NOT EXISTS wp_erp_ui_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    ui_type VARCHAR(50) NOT NULL,
    ui_key VARCHAR(150) NOT NULL,
    visible TINYINT(1) NOT NULL DEFAULT 1,
    enabled TINYINT(1) NOT NULL DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(ui_type),
    INDEX(ui_key)
);

/* =========================================
OPTIONAL CATALOG TABLES (FOR BETTER ADMIN UI)
========================================= */

CREATE TABLE IF NOT EXISTS wp_erp_permissions_catalog (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    permission_key VARCHAR(150) NOT NULL UNIQUE,
    group_key VARCHAR(100) NULL,
    label VARCHAR(255) NOT NULL,
    description TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS wp_erp_ui_catalog (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    ui_type VARCHAR(50) NOT NULL,
    ui_key VARCHAR(150) NOT NULL,
    label VARCHAR(255) NOT NULL,
    description TEXT,
    group_key VARCHAR(100) NULL,
    is_default_visible TINYINT(1) NOT NULL DEFAULT 1,
    is_default_enabled TINYINT(1) NOT NULL DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY (ui_type, ui_key)
);

/* =========================================
AUDIT LOG TABLE
========================================= */

CREATE TABLE IF NOT EXISTS wp_erp_access_audit (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    admin_user BIGINT UNSIGNED,
    target_user BIGINT UNSIGNED,
    action VARCHAR(100),
    old_value JSON,
    new_value JSON,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

/* =========================================
TEST DATA
========================================= */

/* Roles */
INSERT INTO wp_erp_roles (role_key,label,description) VALUES
('administrator','مدیر سیستم','full access'),
('project_manager','مدیر پروژه','manage projects'),
('accountant','حسابدار','manage accounting');

/* Role → Permissions (RBAC) */
INSERT INTO wp_erp_role_permissions (role_id,permission_key) VALUES
(1,'*'),
(2,'cpm_project:view'),
(2,'cpm_project:edit'),
(3,'acc_document:view'),
(3,'acc_document:create');

/* Users → Roles */
INSERT INTO wp_erp_user_roles (user_id,role_id) VALUES
(1,1),   -- user 1: administrator
(2,2),   -- user 2: project_manager
(3,3);   -- user 3: accountant

/* Object-level permissions (object_permissions) */
INSERT INTO wp_erp_object_permissions (user_id,object_type,object_id,actions) VALUES
(2,'project',12,'["view","edit"]'),
(2,'project',33,'["view"]'),
(3,'document',201,'["view"]');

/* UI-level overrides (ui_permissions)
   توجه: با قرارداد ما، presence یعنی یک override نسبت به default.
   این داده تست را طوری ست می‌کنیم که:
   - user 2: منوی cpm را می‌بیند ولی tab_cpm برایش disabled است
   - user 3: منو و تب accounting را می‌بیند و enabled هستند (برای تست positive override)
*/
INSERT INTO wp_erp_ui_permissions (user_id,ui_type,ui_key,visible,enabled) VALUES
(2,'menu','menu_cpm',1,1),
(2,'tab','tab_cpm',1,0),
(3,'menu','menu_accounting',1,1),
(3,'tab','tab_accounting',1,1);

/* Optional: seed catalog tables for nicer admin UI */

INSERT INTO wp_erp_permissions_catalog (permission_key,group_key,label,description) VALUES
('*','system','Full Access','دسترسی کامل به تمام ماژول‌ها'),
('cpm_project:view','project','View Projects','مشاهده لیست و جزئیات پروژه‌ها'),
('cpm_project:edit','project','Edit Projects','ایجاد و ویرایش پروژه‌ها'),
('acc_document:view','accounting','View Documents','مشاهده اسناد حسابداری'),
('acc_document:create','accounting','Create Documents','ایجاد اسناد حسابداری');

INSERT INTO wp_erp_ui_catalog (ui_type,ui_key,label,description,group_key,is_default_visible,is_default_enabled) VALUES
('menu','menu_cpm','منوی پروژه‌ها','دسترسی به ماژول مدیریت پروژه','project',1,1),
('tab','tab_cpm','تب پروژه‌ها','تب اصلی در صفحه داشبورد پروژه','project',1,1),
('menu','menu_accounting','منوی حسابداری','دسترسی به ماژول حسابداری','accounting',1,1),
('tab','tab_accounting','تب حسابداری','تب اصلی در صفحه داشبورد حسابداری','accounting',1,1);



erp-access-control
│
├── erp-access-control.php
│
├── includes
│   ├── class-access-api.php
│   ├── class-access-db.php
│   ├── class-access-service.php
│
├── assets
│   ├── access-control.js
│   ├── access-control.css
│
└── templates
    └── access-control-view.php

    
/* =========================================
ERP ACCESS CONTROL TABLES
========================================= */

CREATE TABLE IF NOT EXISTS wp_erp_roles (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    role_key VARCHAR(100) UNIQUE NOT NULL,
    label VARCHAR(255) NOT NULL,
    description TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS wp_erp_role_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    role_id BIGINT UNSIGNED NOT NULL,
    permission_key VARCHAR(150) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(role_id)
);

CREATE TABLE IF NOT EXISTS wp_erp_user_roles (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    role_id BIGINT UNSIGNED NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(role_id)
);

CREATE TABLE IF NOT EXISTS wp_erp_object_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    object_type VARCHAR(50) NOT NULL,
    object_id BIGINT UNSIGNED NOT NULL,
    actions JSON,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(object_type),
    INDEX(object_id)
);

CREATE TABLE IF NOT EXISTS wp_erp_ui_permissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    ui_type VARCHAR(50) NOT NULL,
    ui_key VARCHAR(150) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX(user_id),
    INDEX(ui_type)
);

CREATE TABLE IF NOT EXISTS wp_erp_access_audit (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    admin_user BIGINT UNSIGNED,
    target_user BIGINT UNSIGNED,
    action VARCHAR(100),
    old_value JSON,
    new_value JSON,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

/* =========================================
TEST DATA
========================================= */

INSERT INTO wp_erp_roles (role_key,label,description) VALUES
('administrator','مدیر سیستم','full access'),
('project_manager','مدیر پروژه','manage projects'),
('accountant','حسابدار','manage accounting');

INSERT INTO wp_erp_role_permissions (role_id,permission_key) VALUES
(1,'*'),
(2,'cpm_project:view'),
(2,'cpm_project:edit'),
(3,'acc_document:view'),
(3,'acc_document:create');

INSERT INTO wp_erp_user_roles (user_id,role_id) VALUES
(1,1),
(2,2),
(3,3);

INSERT INTO wp_erp_object_permissions (user_id,object_type,object_id,actions) VALUES
(2,'project',12,'["view","edit"]'),
(2,'project',33,'["view"]'),
(3,'document',201,'["view"]');

INSERT INTO wp_erp_ui_permissions (user_id,ui_type,ui_key) VALUES
(2,'menu','menu_cpm'),
(2,'tab','tab_cpm'),
(3,'menu','menu_accounting'),
(3,'tab','tab_accounting');


با js  و ورد پرس مثل کمپوننت کنترل پروژه فقط یک نکته بگوییم وقتی محدودیتی 

Object-level permissions (روی پروژه/سند/انبار خاص)
UI-level permissions (نمایش منوها/تب‌ها/دکمه‌ها)
در این تعریف نکنیم یعنی کاربر همه دسترسی ها را دارد
و نکته مهم دیگر باید بتوانیم همه خواص یک نفر را کپی کرد به شخص دیگری و نخواهیم دوباره برای شخص جدی همین کارهار ار دوباره خودمان انجام دهیم

ام ۲ — طراحی ساختار داده کامپوننت دسترسی (Access Control Component)
تا کامپوننت را نسازیم، باید ساختار داده‌اش مشخص باشد.

کامپوننت ما باید سه نوع دسترسی را مدیریت کند:

RBAC (نقش‌ها و مجوزهای کلی)
Object-level permissions (روی پروژه/سند/انبار خاص)
UI-level permissions (نمایش منوها/تب‌ها/دکمه‌ها)
برای این‌که کامپوننت قابل‌استفاده و قابل‌توسعه باشد، باید مجموع این‌ها را با یک ساختار استاندارد کنار هم قرار دهیم.

کاملاً مشخصه؛ این نکته مهمه و باید جزو «قرارداد رسمی» ثبت بشه.

از این لحظه، قرارداد ما اینه:

> برای هر user/role:
> - اگر `object_permissions` خالی باشد (یا اصلاً نیاید)، یعنی **هیچ محدودیت object-level نداریم**  
>   → تفسیر: «به همه‌ی آبجکت‌ها دسترسی دارد» (بر اساس RBAC کلی خودش).
> - اگر `ui_permissions` خالی باشد (یا اصلاً نیاید)، یعنی **هیچ محدودیت UI-level نداریم**  
>   → تفسیر: «همه‌ی منوها/تب‌ها/دکمه‌ها طبق رفتار پیش‌فرض/نقش نمایش داده می‌شوند».

یعنی:

- `object_permissions = []` → همه پروژه‌ها/سندها/انبارها مجاز هستند، تا جایی که RBAC اجازه می‌دهد.
- `ui_permissions = []` → همه‌ی elementهای UI، طبق نقش و منطق پیش‌فرض، نمایش/فعال می‌شوند.

این باید هم در بک‌اند و هم در فرانت‌اند همین‌طور تفسیر شود.  
جایی هم اگر بعداً «غیر خالی» شد، یعنی به‌جای «اجازه به همه»، داری **explicit rule** می‌گذاری و باید به صورت محافظه‌کارانه‌تری تفسیر شود (مثلاً only همین‌ها مجازند).

اگر بخواهی، قدم بعدی را می‌توانیم روی این تنظیم کنیم که:

- در `GET /access/users/{user_id}/package` و بقیه‌ی APIها،  
  دقیقاً در چه شرایطی این دو آرایه را **اصلاً نفرستیم** و در چه شرایطی `[]` بفرستیم،  
  تا فرانت‌اند بتواند فرق «هیچ رولی تعریف نشده» و «محدودیت خاصی وجود ندارد» را بفهمد.