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.

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