diff --git a/application/controllers/Users.php b/application/controllers/Users.php index 4ff1a56..e130d52 100644 --- a/application/controllers/Users.php +++ b/application/controllers/Users.php @@ -154,9 +154,41 @@ class Users extends MY_Admin_Controller { 'employee_id' => $this->validatedEmployeeId($this->input->post('employee_id')) ]; - $this->dm->setTable('users')->insert($data); - - log_activity( + $companyId = (int) $this->companycontext->id(); + $this->db->trans_begin(); + + try { + $userId = (int) $this->dm->setTable('users')->insert($data); + if ($userId < 1) { + throw new RuntimeException('Data pengguna gagal disimpan.'); + } + + $role = $this->db->select('is_super_admin')->get_where('roles', array( + 'id' => (int) $data['role_id'], + ))->row(); + + $this->db->insert('user_companies', array( + 'user_id' => $userId, + 'company_id' => $companyId, + 'is_default' => 1, + 'can_consolidate' => $role && (int) $role->is_super_admin === 1 ? 1 : 0, + )); + + if (!$this->db->trans_status()) { + throw new RuntimeException('Pengguna gagal dihubungkan ke perusahaan aktif.'); + } + + $this->db->trans_commit(); + } catch (Throwable $exception) { + $this->db->trans_rollback(); + echo json_encode(array( + 'status' => false, + 'message' => $exception->getMessage(), + )); + return; + } + + log_activity( 'users', 'create', 'Menambahkan user: ' . $data['username'], diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index ab2d257..efdcae3 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -13,7 +13,17 @@ class MY_Controller extends CI_Controller } if ($this->db->table_exists('companies')) { $this->load->library('CompanyContext'); - $this->companycontext->id(); + try { + $this->companycontext->id(); + } catch (BusinessException $exception) { + $this->session->unset_userdata(array( + 'user_id', 'nama', 'username', 'role', 'role_id', + 'is_super_admin', 'permissions', 'company_id', 'logged_in' + )); + $this->session->set_flashdata('error', $exception->getMessage()); + redirect('auth'); + exit; + } } $this->enforceRoutePermission(); } diff --git a/application/libraries/CompanyContext.php b/application/libraries/CompanyContext.php index 973d99e..c7841f0 100644 --- a/application/libraries/CompanyContext.php +++ b/application/libraries/CompanyContext.php @@ -1,9 +1,102 @@ CI=&get_instance();} - public function id(){if($this->id)return$this->id;$uid=(int)$this->CI->session->userdata('user_id');$selected=(int)$this->CI->session->userdata('company_id');if($selected&&$this->CI->db->get_where('user_companies',array('user_id'=>$uid,'company_id'=>$selected))->row())return$this->id=$selected;$row=$this->CI->db->where('user_id',$uid)->order_by('is_default','DESC')->get('user_companies')->row();if(!$row)throw new BusinessException('User belum diberi akses perusahaan.');$this->CI->session->set_userdata('company_id',$row->company_id);return$this->id=(int)$row->company_id;} - public function assertAccess($companyId){if(!$this->CI->db->get_where('user_companies',array('user_id'=>(int)$this->CI->session->userdata('user_id'),'company_id'=>(int)$companyId))->row())throw new BusinessException('Akses perusahaan ditolak.');return true;} - public function scope($builder,$column='company_id'){$builder->where($column,$this->id());return$builder;} + private $CI; + private $id; + + public function __construct() + { + $this->CI =& get_instance(); + } + + public function id() + { + if ($this->id) { + return $this->id; + } + + $userId = (int) $this->CI->session->userdata('user_id'); + $selected = (int) $this->CI->session->userdata('company_id'); + + if ($selected && $this->hasAccess($userId, $selected)) { + return $this->id = $selected; + } + + $access = $this->CI->db + ->where('user_id', $userId) + ->order_by('is_default', 'DESC') + ->order_by('id', 'ASC') + ->get('user_companies') + ->row(); + + // Memulihkan user lama yang dibuat sebelum relasi perusahaan diterapkan. + // Auto-assignment hanya aman jika memang hanya ada satu perusahaan aktif. + if (!$access) { + $access = $this->assignSoleActiveCompany($userId); + } + + if (!$access) { + throw new BusinessException('User belum diberi akses perusahaan. Hubungi administrator.'); + } + + $this->CI->session->set_userdata('company_id', (int) $access->company_id); + return $this->id = (int) $access->company_id; + } + + public function assertAccess($companyId) + { + if (!$this->hasAccess((int) $this->CI->session->userdata('user_id'), (int) $companyId)) { + throw new BusinessException('Akses perusahaan ditolak.'); + } + return true; + } + + public function scope($builder, $column = 'company_id') + { + $builder->where($column, $this->id()); + return $builder; + } + + private function hasAccess($userId, $companyId) + { + return (bool) $this->CI->db->get_where('user_companies', array( + 'user_id' => (int) $userId, + 'company_id' => (int) $companyId, + ))->row(); + } + + private function assignSoleActiveCompany($userId) + { + if ($userId < 1 || !$this->CI->db->table_exists('companies')) { + return null; + } + + $companies = $this->CI->db + ->select('id') + ->where('is_active', 1) + ->order_by('id', 'ASC') + ->limit(2) + ->get('companies') + ->result(); + + if (count($companies) !== 1) { + return null; + } + + $companyId = (int) $companies[0]->id; + $this->CI->db->query( + 'INSERT IGNORE INTO user_companies (user_id, company_id, is_default, can_consolidate) VALUES (?, ?, 1, 0)', + array($userId, $companyId) + ); + + if (!$this->hasAccess($userId, $companyId)) { + throw new RuntimeException('Akses perusahaan pengguna gagal disiapkan.'); + } + + return (object) array('company_id' => $companyId); + } }