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.

161 lines
5.4 KiB

  1. <?php
  2. namespace App\Console\Commands;
  3. use Throwable;
  4. use Carbon\Carbon;
  5. use App\Models\Cost;
  6. use App\Models\Business;
  7. use Illuminate\Console\Command;
  8. use Morilog\Jalali\CalendarUtils;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Cache;
  11. use Illuminate\Support\Facades\Storage;
  12. class CostCommand extends Command
  13. {
  14. protected $signature = 'cost:work';
  15. protected $description = 'Run the cost worker';
  16. public function handle()
  17. {
  18. while (true) {
  19. $lock = Cache::get('lock', false);
  20. if ($lock) {
  21. $this->info('There is no business for auditing.');
  22. continue;
  23. }
  24. $business = Business::orderBy('calculated_at')->first();
  25. if ($business === null) {
  26. continue;
  27. }
  28. if ($business->calculated_at->isFuture()) {
  29. continue;
  30. }
  31. $next_month = jdate($business->calculated_at)->addMonths();
  32. [$year, $month, $day] = CalendarUtils::toGregorian(
  33. $next_month->getYear(), $next_month->getMonth(), 1
  34. );
  35. $now = Carbon::createFromDate($year, $month, $day);
  36. $now->setTime(0, 0, 0);
  37. if ($now->isFuture()) {
  38. $now = Carbon::now();
  39. }
  40. // if calculated_at less than an hour stop
  41. if ($now->diffInMinutes($business->calculated_at) <= 59) {
  42. $this->info('Must be one hour after the last audit.');
  43. Cache::put('lock', true, $now->diffInSeconds($business->calculated_at));
  44. continue;
  45. }
  46. try {
  47. DB::beginTransaction();
  48. // Fixed amounts of expenses
  49. $business->load('users', 'files');
  50. $costs = 0;
  51. $costs += $this->calculateCostOfBusinessUsers($business, $now);
  52. $costs += $this->calculateCostOfBusinessFiles($business, $now);
  53. // increment and decrement of wallet in php
  54. // deduct costs from your business wallet
  55. // make sure save the calculated_at
  56. $business->update([
  57. 'wallet' => $business->wallet - $costs,
  58. 'calculated_at' => $now,
  59. ]);
  60. DB::commit();
  61. $this->info("The business #{$business->id} was audited.");
  62. } catch (Throwable $throwable) {
  63. throw $throwable;
  64. DB::rollback();
  65. report($throwable);
  66. continue;
  67. }
  68. }
  69. }
  70. public function calculateCostOfBusinessUsers($business, $now)
  71. {
  72. $user_fee = enum('business.fee.user');
  73. $recorded_month = jdate($business->calculated_at)->format("Y-m-01");
  74. if ($business->users->isEmpty()) {
  75. return 0;
  76. }
  77. // get business employee
  78. $users_cost = $business->cost
  79. ->where('type', '=', Cost::USER_TYPE)
  80. ->where('fee', '=', $user_fee)
  81. ->where('month', '=', $recorded_month)
  82. ->where('amount', '=', $business->users->count())
  83. ->first();
  84. if ($users_cost === null) {
  85. $business->cost()->create([
  86. 'type' => Cost::USER_TYPE,
  87. 'month' => $recorded_month,
  88. 'amount' => $business->users->count(),
  89. 'fee' => $user_fee,
  90. 'duration' => $duration = $now->diffInMinutes($business->calculated_at), // from the created_at time of the newset fifth user
  91. 'additional' => $business->users->pluck('id')->toArray(),
  92. ]);
  93. } else {
  94. $users_cost->update([
  95. 'duration' => $duration = $now->diffInMinutes($business->calculated_at) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month),
  96. 'additional' => $business->users->pluck('id')->toArray(),
  97. ]);
  98. }
  99. return $user_fee * $duration;
  100. }
  101. public function calculateCostOfBusinessFiles($business, $now)
  102. {
  103. $file_fee = enum('business.fee.file');
  104. $calculated_at = $business->calculated_at;
  105. $recorded_month = jdate($business->calculated_at)->format("Y-m-01");
  106. // do the math in php
  107. $packs = intdiv($business->files_volume, 200);
  108. if ($packs === 0) {
  109. return 0;
  110. } else {
  111. $packs--;
  112. }
  113. $files = $business->cost
  114. ->where('type', '=', Cost::FILE_TYPE)
  115. ->where('fee', '=', $file_fee)
  116. ->where('month', '=', $recorded_month)
  117. ->where('amount', '=', $packs)
  118. ->first();
  119. if ($files === null) {
  120. $business->cost()->create([
  121. 'type' => Cost::FILE_TYPE,
  122. 'month' => $recorded_month,
  123. 'amount' => $packs,
  124. 'fee' => $file_fee,
  125. 'duration' => $duration = $now->diffInMinutes($calculated_at), // how to determine the file?,
  126. 'additional' => ['volume' => $business->files_volume],
  127. ]);
  128. } else {
  129. $files->update([
  130. 'duration' => $duration = $now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),,
  131. 'additional' => ['volume' => $business->files_volume],
  132. ]);
  133. }
  134. return $file_fee * $duration;
  135. }
  136. }