ReportController.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Helpers\Price;
  4. use App\Models\Dictionary\District;
  5. use App\Models\ObjectType;
  6. use App\Models\Order;
  7. use App\Models\OrderStatus;
  8. use App\Models\Product;
  9. use App\Models\ProductSKU;
  10. use App\Models\Reclamation;
  11. use App\Models\ReclamationStatus;
  12. use App\Models\User;
  13. use Illuminate\Database\Eloquent\Collection;
  14. use Illuminate\Http\Request;
  15. use Illuminate\Support\Str;
  16. class ReportController extends Controller
  17. {
  18. protected array $data = [
  19. 'active' => 'reports',
  20. 'title' => 'Отчёты',
  21. 'id' => 'reports',
  22. ];
  23. public function index()
  24. {
  25. $installedStatuses = [
  26. Order::STATUS_READY_TO_HAND_OVER,
  27. Order::STATUS_NOT_HANDED_OVER_WITH_NOTES,
  28. Order::STATUS_HANDED_OVER_WITH_NOTES,
  29. Order::STATUS_HANDED_OVER,
  30. Order::STATUS_PAID,
  31. ];
  32. $doneStatuses = [
  33. Order::STATUS_HANDED_OVER_WITH_NOTES,
  34. Order::STATUS_HANDED_OVER,
  35. Order::STATUS_PAID,
  36. ];
  37. $paidStatus = Order::STATUS_PAID;
  38. $objectTypes = ObjectType::query()->get()->pluck('name', 'id')->toArray();
  39. $this->data['objectTypes'] = $objectTypes;
  40. $user_ids = Order::query()->distinct()->get('user_id')->pluck('user_id')->toArray();
  41. $managers = User::query()->whereIn('id', $user_ids)->get()->pluck('name', 'id')->toArray();
  42. $this->data['managers'] = $managers ?? [];
  43. $this->data['doneMafsManager'] = $this->data['notDoneMafsManager'] = [];
  44. // всего заказов, выполнено заказов
  45. $this->data['totalOrders'] = Order::all()->count();
  46. $this->data['doneOrders'] = Order::query()->whereIn('order_status_id', $doneStatuses)->count();
  47. $this->data['mountOrders'] = Order::query()->whereIn('order_status_id', $installedStatuses)->count();
  48. $this->data['installedOrders'] = Order::query()->whereIn('order_status_id', $installedStatuses)->count();
  49. // всего маф / завершено маф / установлено маф
  50. $this->data['totalMafs'] = ProductSKU::all()->count();
  51. $this->data['doneMafs'] = ProductSKU::query()->
  52. whereHas('order', function ($query) use ($doneStatuses) {
  53. $query->whereIn('order_status_id', $doneStatuses);
  54. })->count();
  55. $this->data['mountMafs'] = ProductSKU::query()->
  56. whereHas('order', function ($query) use ($installedStatuses){
  57. $query->whereIn('order_status_id', $installedStatuses);
  58. })->count();
  59. $this->data['installedMafs'] = ProductSKU::query()
  60. ->whereHas('order', function ($query) use ($installedStatuses) {
  61. $query->whereIn('order_status_id', $installedStatuses);
  62. })
  63. ->count();
  64. // сумма сданных МАФ по площадкам со сданными и оплаченными статусами
  65. $this->data['totalHandedOverSum'] = Price::format(
  66. $this->getDoneSumByStatus($doneStatuses)
  67. );
  68. // done by managers
  69. foreach ($managers as $userId => $userName) {
  70. $this->data['totalOrderManager'] = Order::where('user_id', $userId)->count();
  71. $this->data['totalMafsManager'][$userId] = ProductSKU::query()->
  72. whereHas('order', function ($query) use ($userId) {
  73. $query->where('user_id', '=', $userId);
  74. })->count();
  75. $this->data['doneOrdersManager'][$userId] = Order::query()
  76. ->where('user_id', '=', $userId)
  77. ->whereIn('order_status_id', $doneStatuses)
  78. ->count();
  79. $this->data['doneMafsManager'][$userId] = ProductSKU::query()->
  80. whereHas('order', function ($query) use ($userId, $doneStatuses) {
  81. $query->where('user_id', '=', $userId)
  82. ->whereIn('order_status_id', $doneStatuses);
  83. })->count();
  84. $this->data['notDoneOrdersManager'][$userId] = Order::query()
  85. ->where('user_id', '=', $userId)
  86. ->whereNotIn('order_status_id', $doneStatuses)
  87. ->count();
  88. $this->data['notDoneMafsManager'][$userId] = ProductSKU::query()->
  89. whereHas('order', function ($query) use ($userId, $doneStatuses) {
  90. $query->where('user_id', '=', $userId)
  91. ->whereNotIn('order_status_id', $doneStatuses);
  92. })->count();
  93. }
  94. // total mount by types
  95. foreach ($objectTypes as $objectTypeId => $objectType) {
  96. // total by types
  97. $this->data['totalOrdersType'][$objectTypeId] = Order::where('object_type_id', '=', $objectTypeId)->count();
  98. $this->data['totalMafsType'][$objectTypeId] = ProductSKU::query()->
  99. whereHas('order', function ($query) use ($objectTypeId) {
  100. $query->where('object_type_id', '=', $objectTypeId);
  101. })->count();
  102. // Установленные: статусы после выхода из "В монтаже".
  103. $this->data['mountOrdersType'][$objectTypeId] = Order::query()
  104. ->where('object_type_id', '=', $objectTypeId)
  105. ->whereIn('order_status_id', $installedStatuses)
  106. ->count();
  107. $this->data['mountMafsType'][$objectTypeId] = ProductSKU::query()->
  108. whereHas('order', function ($query) use ($objectTypeId, $installedStatuses) {
  109. $query->where('object_type_id', '=', $objectTypeId)
  110. ->whereIn('order_status_id', $installedStatuses);
  111. })->count();
  112. // остальные - не готовы
  113. $this->data['notMountOrdersType'][$objectTypeId] = Order::query()
  114. ->where('object_type_id', '=', $objectTypeId)
  115. ->whereNotIn('order_status_id', $installedStatuses)
  116. ->count();
  117. $this->data['notMountMafsType'][$objectTypeId] = ProductSKU::query()->
  118. whereHas('order', function ($query) use ($objectTypeId, $installedStatuses) {
  119. $query->where('object_type_id', '=', $objectTypeId)
  120. ->whereNotIn('order_status_id', $installedStatuses);
  121. })->count();
  122. }
  123. // рекламации
  124. $this->data['totalReclamations'] = Reclamation::query()->count();
  125. $this->data['reclamationStatuses'] = ReclamationStatus::query()->get()->pluck('name', 'id')->toArray();
  126. foreach ($this->data['reclamationStatuses'] as $reclamationStatusId => $reclamationStatus) {
  127. $this->data['reclamations'][$reclamationStatus] = Reclamation::query()->where('status_id', '=', $reclamationStatusId)->count();
  128. }
  129. $this->data['reclamationMafs'] = $this->data['reclamationDetails'] = [];
  130. $reclamations = Reclamation::query()
  131. ->with([
  132. 'skus.product',
  133. 'details',
  134. 'spareParts',
  135. ])
  136. ->get();
  137. foreach ($reclamations as $reclamation) {
  138. foreach ($reclamation->skus as $sku) {
  139. $a = $sku->product->article;
  140. if(isset($this->data['reclamationMafs'][$a])){
  141. $this->data['reclamationMafs'][$a]++;
  142. } else {
  143. $this->data['reclamationMafs'][$a] = 1;
  144. }
  145. }
  146. foreach ($reclamation->details as $detail) {
  147. $a = $detail->name;
  148. if(isset($this->data['reclamationDetails'][$a])){
  149. $this->data['reclamationDetails'][$a] += $detail->quantity;
  150. } else {
  151. $this->data['reclamationDetails'][$a] = $detail->quantity;
  152. }
  153. }
  154. }
  155. $this->appendReclamationSparePartsToReport($reclamations);
  156. // svod
  157. $orderStatuses = OrderStatus::query()->orderBy('id')->get()->pluck('name', 'id')->toArray();
  158. foreach ($orderStatuses as $orderStatusId => $orderStatus) {
  159. $this->data['orderStatuses'][$orderStatus] = Order::query()->where('order_status_id', '=', $orderStatusId)->count();
  160. $this->data['orderMafStatuses'][$orderStatus] = ProductSKU::query()->
  161. whereHas('order', function ($query) use ($orderStatusId) {
  162. $query->where('order_status_id', $orderStatusId);
  163. })->count();
  164. }
  165. // общая сумма
  166. $this->data['totalSum'] = Price::format(
  167. ProductSKU::query()
  168. ->withSum('product', 'total_price')
  169. ->get()
  170. ->sum('product_sum_total_price') / 100
  171. );
  172. // общая сумма done
  173. $this->data['totalDoneSum'] = Price::format(
  174. $this->getDoneSumByStatus($doneStatuses)
  175. );
  176. $this->data['totalPaidSum'] = Price::format(
  177. $this->getDoneSumByStatus($paidStatus)
  178. );
  179. $districts = District::query()->get(['id', 'shortname']);
  180. foreach ($districts as $districtModel) {
  181. $districtId = $districtModel->id;
  182. $district = $districtModel->shortname;
  183. $totalOrders = $this->getOrderCount($districtId);
  184. $totalMafs = $this->getMafCount($districtId);
  185. $readyOrders = $this->getOrderCount($districtId, 4);
  186. $readyMafs = $this->getMafCount($districtId, 4);
  187. $mountOrders = $this->getOrderCount($districtId, 5);
  188. $mountMafs = $this->getMafCount($districtId, 5);
  189. $readyDoneOrders = $this->getOrderCount($districtId, 7);
  190. $readyDoneMafs = $this->getMafCount($districtId, 7);
  191. $doneOrders = $this->getOrderCount($districtId, $doneStatuses);
  192. $doneMafs = $this->getMafCount($districtId, $doneStatuses);
  193. $statusOstOrders = $totalOrders - $mountOrders - $readyDoneOrders - $doneOrders;
  194. $statusOstMafs = $totalMafs - $mountMafs - $readyDoneMafs - $doneMafs;
  195. $totalYardOrders = $this->getOrderCount($districtId, null, 1);
  196. $totalYardMafs = $this->getMafCount($districtId, null, 1);
  197. $mountYardOrders = $this->getOrderCount($districtId, $installedStatuses, 1);
  198. $mountYardMafs = $this->getMafCount($districtId, $installedStatuses, 1);
  199. $ostYardOrders = $totalYardOrders - $mountYardOrders;
  200. $ostYardMafs = $totalYardMafs - $mountYardMafs;
  201. $totalEduOrders = $this->getOrderCount($districtId, null, 2);
  202. $totalEduMafs = $this->getMafCount($districtId, null, 2);
  203. $mountEduOrders = $this->getOrderCount($districtId, $installedStatuses, 2);
  204. $mountEduMafs = $this->getMafCount($districtId, $installedStatuses, 2);
  205. $ostEduOrders = $totalEduOrders - $mountEduOrders;
  206. $ostEduMafs = $totalEduMafs - $mountEduMafs;
  207. $totalZnakOrders = $this->getOrderCount($districtId, null, 3);
  208. $totalZnakMafs = $this->getMafCount($districtId, null, 3);
  209. $mountZnakOrders = $this->getOrderCount($districtId, $installedStatuses, 3);
  210. $mountZnakMafs = $this->getMafCount($districtId, $installedStatuses, 3);
  211. $ostZnakOrders = $totalZnakOrders - $mountZnakOrders;
  212. $ostZnakMafs = $totalZnakMafs - $mountZnakMafs;
  213. $this->data['byDistrict'][$districtId] = [
  214. 'name' => $district,
  215. 'totalSum' => $this->getDistrictSum($districtId),
  216. 'doneSum' => $this->getDistrictSum($districtId, $doneStatuses),
  217. 'paidSum' => $this->getDistrictSum($districtId, $paidStatus),
  218. 'totalOrders' => $totalOrders,
  219. 'totalMafs' => $totalMafs,
  220. 'readyOrders' => $readyOrders,
  221. 'readyMafs' => $readyMafs,
  222. 'mountOrders' => $mountOrders,
  223. 'mountMafs' => $mountMafs,
  224. 'readyDoneOrders' => $readyDoneOrders,
  225. 'readyDoneMafs' => $readyDoneMafs,
  226. 'doneOrders' => $doneOrders,
  227. 'doneMafs' => $doneMafs,
  228. 'statusOstOrders' => $statusOstOrders,
  229. 'statusOstMafs' => $statusOstMafs,
  230. 'totalYardOrders' => $totalYardOrders,
  231. 'totalYardMafs' => $totalYardMafs,
  232. 'mountYardOrders' => $mountYardOrders,
  233. 'mountYardMafs' => $mountYardMafs,
  234. 'ostYardOrders' => $ostYardOrders,
  235. 'ostYardMafs' => $ostYardMafs,
  236. 'totalEduOrders' => $totalEduOrders,
  237. 'totalEduMafs' => $totalEduMafs,
  238. 'mountEduOrders' => $mountEduOrders,
  239. 'mountEduMafs' => $mountEduMafs,
  240. 'ostEduOrders' => $ostEduOrders,
  241. 'ostEduMafs' => $ostEduMafs,
  242. 'totalZnakOrders' => $totalZnakOrders,
  243. 'totalZnakMafs' => $totalZnakMafs,
  244. 'mountZnakOrders' => $mountZnakOrders,
  245. 'mountZnakMafs' => $mountZnakMafs,
  246. 'ostZnakOrders' => $ostZnakOrders,
  247. 'ostZnakMafs' => $ostZnakMafs,
  248. ];
  249. }
  250. return view('reports.index', $this->data);
  251. }
  252. private function appendReclamationSparePartsToReport(Collection $reclamations): void
  253. {
  254. foreach ($reclamations as $reclamation) {
  255. foreach ($reclamation->spareParts as $sparePart) {
  256. $article = trim((string) $sparePart->article);
  257. $quantity = (int) ($sparePart->pivot?->quantity ?? 0);
  258. if ($article === '' || $quantity < 1) {
  259. continue;
  260. }
  261. if (isset($this->data['reclamationDetails'][$article])) {
  262. $this->data['reclamationDetails'][$article] += $quantity;
  263. } else {
  264. $this->data['reclamationDetails'][$article] = $quantity;
  265. }
  266. }
  267. }
  268. }
  269. private function getOrderCount($districtId, $status = null, $type = null)
  270. {
  271. $q = Order::query()->where('district_id', '=', $districtId);
  272. if($status) {
  273. if(!is_array($status)) $status = [$status];
  274. $q->whereIn('order_status_id', $status);
  275. }
  276. if($type) {
  277. if(!is_array($type)) $type = [$type];
  278. $q->whereIn('object_type_id', $type);
  279. }
  280. return $q->count();
  281. }
  282. private function getMafCount($districtId, $status = null, $type = null)
  283. {
  284. return ProductSKU::query()->
  285. whereHas('order', function ($query) use ($districtId, $status, $type) {
  286. $query->where('district_id', '=', $districtId);
  287. if($status) {
  288. if(!is_array($status)) $status = [$status];
  289. $query->whereIn('order_status_id', $status);
  290. }
  291. if($type) {
  292. if(!is_array($type)) $type = [$type];
  293. $query->whereIn('object_type_id', $type);
  294. }
  295. })->count();
  296. }
  297. private function getDistrictSum($districtId, $done = false)
  298. {
  299. return Price::format(
  300. $this->getDoneSumByStatus($done ?: null, $districtId)
  301. );
  302. }
  303. private function getDoneSumByStatus(array|int|null $statuses = null, ?int $districtId = null): float
  304. {
  305. $query = ProductSKU::query()
  306. ->join('orders', 'orders.id', '=', 'products_sku.order_id')
  307. ->join('products', 'products.id', '=', 'products_sku.product_id')
  308. ->where('orders.year', year())
  309. ->where('products.year', year());
  310. if ($districtId !== null) {
  311. $query->where('orders.district_id', $districtId);
  312. }
  313. if ($statuses !== null && $statuses !== false) {
  314. $statuses = is_array($statuses) ? $statuses : [$statuses];
  315. $query->whereIn('orders.order_status_id', $statuses);
  316. }
  317. return ((float) $query->sum('products.total_price')) / 100;
  318. }
  319. }