CompanyModel.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. <?php
  2. namespace Models;
  3. use Libs\Database;
  4. use Libs\Hmac;
  5. use Libs\Logger;
  6. /**
  7. * Provisionamento de empresas (tenants).
  8. *
  9. * Cria a empresa e o seu primeiro usuário (admin) de forma ATÔMICA: ou os dois
  10. * são criados, ou nenhum. Já gera o segredo HMAC da empresa (usado nos webhooks
  11. * de CRM) no momento da criação.
  12. */
  13. class CompanyModel
  14. {
  15. private \PDO $pdo;
  16. public function __construct()
  17. {
  18. $this->pdo = Database::pdo();
  19. }
  20. /**
  21. * Cria a empresa + usuário admin numa única transação.
  22. *
  23. * @param array $company ['name', 'cnpj', 'logo']
  24. * @param array $admin ['name', 'email', 'phone', 'password']
  25. * @return array{status:string, company?:array, user?:array}
  26. * status: 'created' | 'cnpj_exists' | 'email_exists' | 'error'
  27. */
  28. public function createCompanyWithAdmin(array $company, array $admin): array
  29. {
  30. $companyName = trim((string) ($company['name'] ?? ''));
  31. $cnpj = preg_replace('/\D/', '', (string) ($company['cnpj'] ?? ''));
  32. $logo = trim((string) ($company['logo'] ?? ''));
  33. $userName = trim((string) ($admin['name'] ?? ''));
  34. $email = mb_strtolower(trim((string) ($admin['email'] ?? '')));
  35. $phone = trim((string) ($admin['phone'] ?? ''));
  36. $password = (string) ($admin['password'] ?? '');
  37. if ($this->cnpjExists($cnpj)) {
  38. return ['status' => 'cnpj_exists'];
  39. }
  40. if ($this->emailExists($email)) {
  41. return ['status' => 'email_exists'];
  42. }
  43. $secret = Hmac::generateSecret();
  44. $passwordHash = password_hash($password, PASSWORD_DEFAULT);
  45. $this->pdo->beginTransaction();
  46. try {
  47. $companyStmt = $this->pdo->prepare(
  48. "INSERT INTO company (company_name, company_cnpj, company_logo, company_hmac_secret)
  49. VALUES (:name, :cnpj, :logo, :secret)
  50. RETURNING company_id, company_name, company_cnpj, company_logo,
  51. company_hmac_secret, company_created_at"
  52. );
  53. $companyStmt->execute([
  54. 'name' => $companyName,
  55. 'cnpj' => $cnpj,
  56. 'logo' => $logo,
  57. 'secret' => $secret,
  58. ]);
  59. $createdCompany = $companyStmt->fetch(\PDO::FETCH_ASSOC);
  60. $companyId = (int) $createdCompany['company_id'];
  61. $userStmt = $this->pdo->prepare(
  62. "INSERT INTO \"user\" (company_id, user_name, user_phone, user_email, user_role, user_password)
  63. VALUES (:company_id, :user_name, :user_phone, :user_email, 'admin', :user_password)
  64. RETURNING user_id, company_id, user_name, user_phone, user_email, user_role, user_created_at"
  65. );
  66. $userStmt->execute([
  67. 'company_id' => $companyId,
  68. 'user_name' => $userName,
  69. 'user_phone' => $phone,
  70. 'user_email' => $email,
  71. 'user_password' => $passwordHash,
  72. ]);
  73. $createdUser = $userStmt->fetch(\PDO::FETCH_ASSOC);
  74. $this->pdo->commit();
  75. } catch (\Throwable $e) {
  76. $this->pdo->rollBack();
  77. Logger::error('Failed to create company with admin', [
  78. 'cnpj' => $cnpj,
  79. 'email' => $email,
  80. 'error' => $e->getMessage(),
  81. ]);
  82. return ['status' => 'error'];
  83. }
  84. return [
  85. 'status' => 'created',
  86. 'company' => [
  87. 'company_id' => $companyId,
  88. 'company_name' => $createdCompany['company_name'],
  89. 'company_cnpj' => $createdCompany['company_cnpj'],
  90. 'company_logo' => $createdCompany['company_logo'],
  91. 'company_hmac_secret' => $createdCompany['company_hmac_secret'],
  92. 'company_created_at' => $createdCompany['company_created_at'],
  93. ],
  94. 'user' => [
  95. 'user_id' => (int) $createdUser['user_id'],
  96. 'company_id' => (int) $createdUser['company_id'],
  97. 'user_name' => $createdUser['user_name'],
  98. 'user_phone' => $createdUser['user_phone'],
  99. 'user_email' => $createdUser['user_email'],
  100. 'user_role' => $createdUser['user_role'],
  101. 'user_created_at' => $createdUser['user_created_at'],
  102. ],
  103. ];
  104. }
  105. private function cnpjExists(string $cnpj): bool
  106. {
  107. $stmt = $this->pdo->prepare(
  108. "SELECT 1 FROM company
  109. WHERE company_cnpj = :cnpj AND company_deleted_at = 'infinity'
  110. LIMIT 1"
  111. );
  112. $stmt->execute(['cnpj' => $cnpj]);
  113. return $stmt->fetchColumn() !== false;
  114. }
  115. private function emailExists(string $email): bool
  116. {
  117. // user_email é UNIQUE global (não filtra por empresa).
  118. $stmt = $this->pdo->prepare('SELECT 1 FROM "user" WHERE user_email = :email LIMIT 1');
  119. $stmt->execute(['email' => $email]);
  120. return $stmt->fetchColumn() !== false;
  121. }
  122. }