You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
15 KiB

  1. <?php
  2. namespace App\Models;
  3. use App\Models\File;
  4. use App\Models\Model;
  5. use App\Models\SoftDeletes;
  6. use App\Notifications\SocketNotification;
  7. use Illuminate\Support\Facades\Notification;
  8. use Illuminate\Validation\Rule;
  9. use Illuminate\Http\UploadedFile;
  10. use Spatie\MediaLibrary\HasMedia;
  11. use App\Models\ReportableRelation;
  12. use Illuminate\Support\Facades\Cache;
  13. use Spatie\MediaLibrary\InteractsWithMedia;
  14. use Spatie\MediaLibrary\MediaCollections\Models\Media;
  15. class Business extends Model implements HasMedia
  16. {
  17. use SoftDeletes,
  18. InteractsWithMedia;
  19. const CONVERSION_NAME = 'avatar';
  20. const COLLECTION_NAME = 'avatars';
  21. public static $permissions = ['level'];
  22. protected $table = 'businesses';
  23. protected $fillable = ['name', 'slug', 'wallet','files_volume','cache', 'color', 'description', 'has_avatar', 'calculated_at', 'suspended_at', 'users'];
  24. protected $fillable_relations = [
  25. 'users'
  26. ];
  27. protected $reportable = [
  28. 'name', 'slug', 'wallet', 'files_volume', 'cache', 'calculated_at', // fields
  29. ['users' => 'business_user'] // relations [ name => pivot ]
  30. ];
  31. public $detach_relation = false;
  32. protected $casts = [
  33. 'has_avatar' => 'boolean',
  34. 'calculated_at' => 'datetime',
  35. 'suspended_at' => 'datetime',
  36. 'wallet' => 'integer',
  37. ];
  38. public function getValueOf(?string $key)
  39. {
  40. $values = [
  41. 'business_id' => $this->id,
  42. 'workflow_id' => null,
  43. 'project_id' => null,
  44. 'sprint_id' => null,
  45. 'status_id' => null,
  46. 'system_id' => null,
  47. 'user_id' => null,
  48. 'task_id' => null,
  49. 'subject_id' => $this->id,
  50. ];
  51. if ($key && isset($values, $key)) {
  52. return $values[$key];
  53. }
  54. return $values;
  55. }
  56. public function rules()
  57. {
  58. return [
  59. 'name' => 'bail|required|string|min:2|max:255',
  60. 'color' => 'nullable|string|min:2|max:255',
  61. 'slug' => ['bail', 'required', 'string', 'min:2', 'max:255', 'regex:/^[a-zA-Z]+[a-zA-Z\d-]*(.*[a-zA-Z\d])+$/',
  62. Rule::unique($this->table, 'slug')->ignore($this->id)],
  63. 'description' => 'nullable|string|min:2|max:1000',
  64. ];
  65. }
  66. public function cost()
  67. {
  68. return $this->hasMany(Cost::class, 'business_id','id');
  69. }
  70. public function owners()
  71. {
  72. return $this->users()->wherePivot('level', '=', enum('levels.owner.id'));
  73. }
  74. public function members()
  75. {
  76. return $this->users()->wherePivot('owner', '!=', enum('levels.owner.id'));
  77. }
  78. public function users()
  79. {
  80. return $this->belongsToMany(
  81. User::class, 'business_user', 'business_id', 'user_id',
  82. 'id', 'id', __FUNCTION__
  83. )
  84. ->using(ReportableRelation::class)
  85. ->withPivot(self::$permissions);
  86. }
  87. public function tags()
  88. {
  89. return $this->hasMany(Tag::class, 'business_id', 'id');
  90. }
  91. public function projects()
  92. {
  93. return $this->hasMany(Project::class, 'business_id', 'id');
  94. }
  95. public function systems()
  96. {
  97. return $this->hasMany(System::class, 'business_id', 'id');
  98. }
  99. public function workflows()
  100. {
  101. return $this->hasMany(Workflow::class, 'business_id', 'id');
  102. }
  103. public function sprints()
  104. {
  105. return $this->hasMany(Sprint::class, 'business_id', 'id');
  106. }
  107. public function statuses()
  108. {
  109. return $this->hasMany(Status::class, 'business_id', 'id');
  110. }
  111. public function files()
  112. {
  113. return $this->hasMany(File::class, 'user_id', 'id');
  114. }
  115. public function transactions()
  116. {
  117. return $this->hasMany(Transaction::class, 'business_id', 'id');
  118. }
  119. public function updateRelations()
  120. {
  121. // users relations
  122. if (!empty($this->filled_relations['users']) || $this->detach_relation) {
  123. $this->dirties['users'] = $this->users()->sync($this->filled_relations['users'], $this->detach_relation);
  124. }
  125. }
  126. public static function info($businessId, $fresh = false)
  127. {
  128. $info = [];
  129. $fresh = true;
  130. if ($fresh){
  131. Cache::forget('business_info'.$businessId);
  132. }
  133. if (Cache::has('business_info'.$businessId)) {
  134. return Cache::get('business_info'.$businessId);
  135. } else {
  136. $business = self::findOrFail($businessId);
  137. $tags = $business->tags()->select('id', 'label', 'color')->get()->keyBy('id');
  138. $info['tags'] = $tags->pluck('id');
  139. $workflows = $business->workflows()->select ('id', 'business_id', 'name','desc')->get()->keyBy('id')
  140. ->load(['statuses'=> fn($q) => $q->select('id', 'business_id', 'workflow_id', 'name', 'state', 'order')])
  141. ->map(fn($q) => [
  142. 'id' => $q->id,
  143. 'business_id' => $q->business_id,
  144. 'name' => $q->name,
  145. 'desc' => $q->desc,
  146. 'statuses' => $q['statuses']->keyBy('id'),
  147. ]);
  148. $info['workflows'] = $workflows->map(fn($q) => ['statuses' => $q['statuses']->pluck('id')]);
  149. $users = $business->users()->select('id', 'name', 'email', 'mobile', 'username')->with('media')->get()
  150. ->keyBy('id')
  151. ->map(fn($u) => [
  152. 'id' => $u->id,
  153. 'name' => $u->name,
  154. 'email' => $u->email,
  155. 'mobile' => $u->mobile,
  156. 'username' => $u->get,
  157. 'avatar' => $u->has_avatar,
  158. 'level' => $u->pivot['level'],
  159. ]);
  160. $info['users'] = $users->map(fn($u) => [
  161. 'level' => $u['level']
  162. ]);
  163. $projects = $business->projects()->get()->keyBy('id')->load([
  164. 'members' => fn($q) => $q->select('id', 'level'),
  165. 'systems' => fn($q) => $q->select('id', 'project_id', 'name'),
  166. 'sprints' => fn($q) => $q->select('id', 'project_id', 'name', 'description', 'started_at', 'ended_at', 'active'),
  167. 'media',
  168. ]);
  169. $info['projects'] = $projects->map(function($q) use($users){
  170. return [
  171. 'avatar' => $q->has_avatar,
  172. 'systems' => $q->systems->pluck('id'),
  173. 'sprints' => $q->sprints->pluck('id'),
  174. 'members' => $users->keyBy('id')->map(function($u, $uid) use ($q){
  175. $project_user = $q->members->firstWhere('id', $uid);
  176. return $project_user? ['level' => $project_user->pivot->level, 'is_direct' => true]:
  177. ['level' => $q->private && $u['level'] != enum('levels.owner.id') ? enum('levels.inactive.id') : $u['level'],
  178. 'is_direct'=> $project_user ? true : false];
  179. })
  180. ];
  181. });
  182. $business_info = array_merge(
  183. $business->only('id', 'name', 'slug', 'color','wallet', 'files_volume'),
  184. ['avatar' => $business->has_avatar],
  185. compact(
  186. 'info',
  187. 'tags',
  188. 'workflows',
  189. 'users',
  190. 'projects'
  191. )
  192. );
  193. Cache::put('business_info'.$businessId , $business_info, config('app.cache_ttl'));
  194. Notification::send(auth()->user(), new SocketNotification(['message' => 'business info update','payload'=>$business_info]));
  195. return $business_info;
  196. }
  197. }
  198. public static function stats()
  199. {
  200. return [
  201. 'users' => [
  202. 10 => [
  203. 'statuses' => [
  204. 10 => 20,
  205. 30 => 40
  206. ],
  207. 'workflows' => [
  208. 10 => 20,
  209. 30 => 40
  210. ],
  211. 'tags' => [
  212. 10 => 20,
  213. 30 => 40
  214. ],
  215. 'project' => [
  216. 10 => 20,
  217. 30 => 40
  218. ],
  219. 'sprints' => [
  220. 10 => 20,
  221. 30 => 40
  222. ],
  223. 'works' => [
  224. ],
  225. '__subsystems' => [
  226. 10 => 20,
  227. 30 => 40
  228. ],
  229. ]
  230. ],
  231. 'workflows' => [
  232. 50 => [
  233. 'statuses' => [
  234. 20 => 50
  235. ],
  236. ]
  237. ],
  238. 'statuses' => [
  239. 10 => [
  240. 'users' => [
  241. ],
  242. 'projects' => [
  243. ]
  244. ]
  245. ],
  246. 'sprints' => [
  247. 10 => [
  248. 'statuses' => [
  249. 10 => [
  250. 10 => 1
  251. ]
  252. ]
  253. ]
  254. ]
  255. ];
  256. }
  257. public function reportActivity()
  258. {
  259. }
  260. public static function nuxtInfo($businessId)
  261. {
  262. if (empty($businessId)){
  263. return null;
  264. }
  265. Cache::forget('business_nuxt_info' . $businessId);
  266. return Cache::rememberForever('business_nuxt_info' . $businessId, function () use ($businessId) {
  267. $business = self::with([
  268. 'projects.members' => fn($q) => $q->select('id', 'name'),
  269. 'projects',
  270. 'tags',
  271. 'workflows.statuses',
  272. 'workflows',
  273. 'statuses',
  274. 'users',
  275. ])->findOrFail($businessId);
  276. $globals = [];
  277. $business->users->each(function ($u) use (&$globals) {
  278. $globals[$u->id] = $u->pivot->owner == true ? ['owner' => true] : $u->pivot->only(self::$permissions);
  279. });
  280. $projects = [];
  281. $business->projects->each(function ($p) use (&$projects, &$globals) {
  282. });
  283. $business->setRelation('projects', collect($projects));
  284. $business->setRelation('users', collect($globals));
  285. return $business;
  286. });
  287. }
  288. public static function infoOld($businessId)
  289. {
  290. if (empty($businessId))
  291. return null;
  292. Cache::forget('business_info' . $businessId);
  293. return Cache::rememberForever('business_info' . $businessId, function () use ($businessId) {
  294. $ob = [];
  295. $business = self::with([
  296. 'projects.members' => fn($q) => $q->select('id', 'name'),
  297. 'projects' => fn($q) => $q->select('id', 'business_id', 'private'),
  298. 'tags' => fn($q) => $q->select('id', 'business_id', 'label'),
  299. 'sprints' => fn($q) => $q->select('id','business_id','name', 'active'),
  300. 'workflows.statuses' => fn($q) => $q->select('id', 'name'),
  301. 'workflows' => fn($q) => $q->select('id', 'business_id', 'name'),
  302. 'statuses' => fn($q) => $q->select('id', 'business_id', 'name', 'state'),
  303. 'users' => fn($q) => $q->select('id', 'name'),
  304. ])->findOrFail($businessId);
  305. // permissions in business
  306. $permissions = [];
  307. $business->users->each(function ($user) use (&$permissions) {
  308. $permissions[$user->id] = $user->pivot->only(['owner']);
  309. $permissions[$user->id]['global_PA'] = $permissions[$user->id]['owner'] == true ?
  310. enum('roles.manager.id') : $user->pivot->PA;
  311. $permissions[$user->id]['global_FA'] = $permissions[$user->id]['owner'] == true ?
  312. enum('roles.manager.id') : $user->pivot->FA;
  313. $permissions[$user->id]['PA'] = [];
  314. $permissions[$user->id]['FA'] = [];
  315. });
  316. //projects
  317. $projects = [];
  318. $business->projects->each(function ($p) use (&$permissions, $business, &$projects) {
  319. $business->users->each(function ($user) use (&$permissions, $p) {
  320. $PA = null;
  321. $FA = null;
  322. if ($permissions[$user->id]['owner'] == true) {
  323. $PA = enum('roles.manager.id');
  324. $FA = enum('roles.manager.id');
  325. } else if (!empty($project_user = $p->getPermissions($user->id))) {
  326. $PA = $project_user->pivot->PA;
  327. $FA = $project_user->pivot->FA;
  328. } else if (empty($p->getPermissions($user->id))) {
  329. $PA = $p->private == false ? $permissions[$user->id]['global_PA'] : enum('roles.hidden.id') ;
  330. $FA = $p->private == false ? $permissions[$user->id]['global_FA'] : enum('roles.hidden.id');
  331. }
  332. if ($PA !== null && $FA !== null) {
  333. $permissions[$user->id]['PA'][$p->id] = $PA;
  334. $permissions[$user->id]['FA'][$p->id] = $FA;
  335. }
  336. });
  337. $projects[$p->id] ='';
  338. });
  339. //workflow
  340. $workflows = [];
  341. $business->workflows->each(function ($w) use (&$workflows) {
  342. $workflows[$w->id] = $w->statuses->pluck('id');
  343. });
  344. $ob['tags'] = $business->tags->pluck('id');
  345. $ob['statuses'] = $business->statuses;
  346. $ob['sprints'] = $business->sprints;
  347. $ob['workflows'] = $workflows;
  348. $ob['users'] = $permissions;
  349. $ob['projects'] = $projects;
  350. return collect($ob);
  351. });
  352. }
  353. public function registerMediaCollections(): void
  354. {
  355. $this->addMediaCollection(static::COLLECTION_NAME)
  356. ->acceptsMimeTypes([
  357. 'image/jpeg',
  358. 'image/png',
  359. 'image/tiff',
  360. 'image/gif',
  361. ])
  362. ->useDisk('public')
  363. ->singleFile();
  364. }
  365. public function registerMediaConversions(Media $media = null): void
  366. {
  367. $this->addMediaConversion(static::CONVERSION_NAME)
  368. ->width(200)
  369. ->height(200)
  370. ->queued()
  371. ->nonOptimized()
  372. ->performOnCollections(static::COLLECTION_NAME);
  373. }
  374. public function saveAsAvatar(UploadedFile $avatar): void
  375. {
  376. $this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME);
  377. $this->update([
  378. 'has_avatar' => true,
  379. ]);
  380. @unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath());
  381. }
  382. public function deleteAvatar(): void
  383. {
  384. $path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath();
  385. $this->getFirstMedia(static::COLLECTION_NAME)->delete();
  386. $this->update([
  387. 'has_avatar' => false,
  388. ]);
  389. @unlink($path);
  390. }
  391. public function getAvatarUrl(): ?string
  392. {
  393. if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) {
  394. return $url;
  395. }
  396. return null;
  397. }
  398. }