BIService.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. namespace App\Service;
  3. use App\Model\DailyPwOrder;
  4. use App\Model\DailyPwOrderDetails;
  5. use App\Model\ExpenseClaims;
  6. use App\Model\ExpenseClaimsDetails;
  7. use App\Model\Fee;
  8. use App\Model\Item;
  9. use App\Model\MonthlyDdOrder;
  10. use App\Model\MonthlyDdOrderDetails;
  11. use App\Model\MonthlyPsOrder;
  12. use App\Model\MonthlyPsOrderDetails;
  13. use App\Model\PLeaveOverOrder;
  14. use App\Model\PLeaveOverOrderDetails;
  15. use Illuminate\Support\Facades\DB;
  16. class BIService extends Service
  17. {
  18. private function commonRule($data)
  19. {
  20. if (isset($data['time']) && !empty($data['time'])) {
  21. $start = $this->changeDateToDate($data['time'][0]);
  22. $end = $this->changeDateToDate($data['time'][1], true);
  23. $data['month_start'] = date("Y-m-d", $start);
  24. $data['month_end'] = date("Y-m-d", $end);
  25. }
  26. if (!isset($data['month_start'])) $month_start = date('Y-01-01');
  27. else $month_start = date('Y-m-01', strtotime($data['month_start']));
  28. if (!isset($data['month_end'])) $month_end = date('Y-01-01', strtotime('+1 year', strtotime($month_start)));
  29. else {
  30. $start_year = date('Y', strtotime($month_start));
  31. $end_year = date('Y', strtotime($data['month_end']));
  32. if ($start_year != $end_year) return [false, "查询不得跨年!", ""];
  33. $month_end = date('Y-m-01', strtotime($data['month_end'] . ' +1 month'));
  34. }
  35. return [true, strtotime($month_start), strtotime($month_end)];
  36. }
  37. public function homePageData($data, $user)
  38. {
  39. list($status, $month_start, $month_end) = $this->commonRule($data);
  40. if (!$status) return [false, $month_start];
  41. //人员费用
  42. list($total_man, $salary_map) = $this->getEmployeeSalary($month_start, $month_end, $data, $user);
  43. //折旧费用
  44. list($total_zj, $zj_map) = $this->getDeviceSalary($month_start, $month_end, $data, $user);
  45. //费用报销
  46. list($total_other, $other_map, $return_fee) = $this->getFeeItemSalary($month_start, $month_end, $data, $user);
  47. //研发费用总额
  48. $total = bcadd(bcadd($total_man, $total_zj,3), $total_other,3);
  49. //研发费用结构
  50. $rd = [
  51. 0 => [
  52. 'title' => '人工费用',
  53. 'rate' => bcmul(bcdiv($total_man,$total,4),100,2),
  54. 'rate_unit' => '%',
  55. 'total' => $total_man,
  56. 'total_unit' => '¥',
  57. ],
  58. 1 => [
  59. 'title' => '折旧费用',
  60. 'rate' => bcmul(bcdiv($total_zj,$total,4),100,2),
  61. 'rate_unit' => '%',
  62. 'total' => $total_zj,
  63. 'total_unit' => '¥',
  64. ],
  65. 2 => [
  66. 'title' => '费用报销',
  67. 'rate' => bcmul(bcdiv($total_other,$total,4),100,2),
  68. 'rate_unit' => '%',
  69. 'total' => $total_other,
  70. 'total_unit' => '¥',
  71. ],
  72. ];
  73. //研发费用趋势
  74. $rd_trend = $this->makeTrend($month_start, $salary_map, $zj_map, $other_map);
  75. //研发费用报销占比
  76. $rd_rate = $return_fee;
  77. //加班 请假 总时长
  78. list($over_time, $leave_time) = $this->overLeave($month_start, $month_end, $data, $user);
  79. // 项目人员工时汇总
  80. $man_work = $this->manWork($month_start, $month_end, $data, $user);
  81. //加计扣除金额
  82. $discount = 0; //todo
  83. return [true, [
  84. 'rd_total' => $total,
  85. 'rd_unit' => '¥',
  86. 'rd_title' => '研发费用总额', //-----
  87. 'discount_total' => $discount,
  88. 'discount_unit' => '¥',
  89. 'discount_title' => '加计扣除金额', //------
  90. 'over_time' => $over_time, // 加班
  91. 'leave_time' => $leave_time, // 请假
  92. 'rd' => $rd, //研发费用结构
  93. 'rd_trend' => $rd_trend, //研发费用趋势
  94. 'rd_rate' => $rd_rate, // 研发费用报销占比
  95. 'man_work' => $man_work, //项目人员工时汇总
  96. ]];
  97. }
  98. private function getEmployeeSalary($month_start, $month_end, $data, $user)
  99. {
  100. $monthly_ps_order = MonthlyPsOrder::Clear($user, $data)
  101. ->where('del_time', 0)
  102. ->where("month", ">=", $month_start)
  103. ->where("month", "<", $month_end)
  104. ->pluck('month','id')
  105. ->toArray();
  106. $monthly_ps_order_ids = array_keys($monthly_ps_order);
  107. $month_employee_salary = MonthlyPsOrderDetails::whereIn('main_id', $monthly_ps_order_ids)
  108. ->select("salary", "main_id")
  109. ->get()
  110. ->toArray();
  111. $total = 0;
  112. $salary_map = [];
  113. foreach ($month_employee_salary as $value) {
  114. $currentAmount = (string)$value['salary'];
  115. if(isset($monthly_ps_order[$value['main_id']])){
  116. $month = $monthly_ps_order[$value['main_id']];
  117. if(isset($salary_map[$month])){
  118. $salary_map[$month] = bcadd($salary_map[$month], $currentAmount,3);
  119. }else{
  120. $salary_map[$month] = $currentAmount;
  121. }
  122. }
  123. $total = bcadd($total, $currentAmount,3);
  124. }
  125. return [$total, $salary_map];
  126. }
  127. private function getDeviceSalary($month_start, $month_end, $data, $user)
  128. {
  129. $monthly_dd_order= MonthlyDdOrder::Clear($user, $data)
  130. ->where('del_time', 0)
  131. ->where("month", ">=", $month_start)
  132. ->where("month", "<", $month_end)
  133. ->pluck('month','id')
  134. ->toArray();
  135. $monthly_dd_order_ids = array_keys($monthly_dd_order);
  136. $month_device_salary = MonthlyDdOrderDetails::whereIn('main_id', $monthly_dd_order_ids)
  137. ->select("depreciation_amount as amount", "main_id")
  138. ->get()
  139. ->toArray();
  140. $total = 0;
  141. $amount_map = [];
  142. foreach ($month_device_salary as $value) {
  143. $currentAmount = (string)$value['amount'];
  144. if(isset($monthly_dd_order[$value['main_id']])){
  145. $month = $monthly_dd_order[$value['main_id']];
  146. if(isset($amount_map[$month])){
  147. $amount_map[$month] = bcadd($amount_map[$month], $currentAmount,3);
  148. }else{
  149. $amount_map[$month] = $currentAmount;
  150. }
  151. }
  152. $total = bcadd($total, $currentAmount,3);
  153. }
  154. return [$total, $amount_map];
  155. }
  156. private function getFeeItemSalary($month_start, $month_end, $data, $user)
  157. {
  158. $e_order = ExpenseClaims::Clear($user, $data)
  159. ->where('del_time', 0)
  160. ->where("month", ">=", $month_start)
  161. ->where("month", "<", $month_end)
  162. ->pluck('month','id')
  163. ->toArray();
  164. $e_order_ids = array_keys($e_order);
  165. $e_order_detail = ExpenseClaimsDetails::whereIn('expense_claims_id', $e_order_ids)
  166. ->select("amount", "expense_claims_id as main_id", "fee_id")
  167. ->get()
  168. ->toArray();
  169. $fee = Fee::TopClear($user, $data);
  170. $fee_map = $fee->whereIn('id', array_unique(array_column($e_order_detail,'fee_id')))->pluck('title','id')->toArray();
  171. $total = 0;
  172. $amount_map = [];
  173. $fee_amount_map = [];
  174. foreach ($e_order_detail as $value) {
  175. $currentAmount = (string)$value['amount'];
  176. if(isset($e_order[$value['main_id']])){
  177. $month = $e_order[$value['main_id']];
  178. if(isset($amount_map[$month])){
  179. $amount_map[$month] = bcadd($amount_map[$month], $currentAmount,3);
  180. }else{
  181. $amount_map[$month] = $currentAmount;
  182. }
  183. }
  184. $fee_tmp = $fee_map[$value['fee_id']] ?? "";
  185. if(! empty($fee_tmp)){
  186. if(isset($fee_amount_map[$fee_tmp])){
  187. $fee_amount_map[$fee_tmp] = bcadd($fee_amount_map[$fee_tmp], $currentAmount,3);
  188. }else{
  189. $fee_amount_map[$fee_tmp] = $currentAmount;
  190. }
  191. }
  192. $total = bcadd($total, $currentAmount,3);
  193. }
  194. $return_fee = [];
  195. foreach ($fee_amount_map as $key => $value){
  196. $amount = (string)$value;
  197. $return_fee[] = [
  198. "title" => $key,
  199. "total" => $amount,
  200. "total_unit" => "元",
  201. "rate" => bcmul(bcdiv($amount,$total,4),100,2),
  202. "rate_unit" => "%"
  203. ];
  204. }unset($fee_amount_map);
  205. return [$total, $amount_map, $return_fee];
  206. }
  207. private function makeTrend($month_start, $salary_map, $zj_map, $other_map)
  208. {
  209. $trend = [];
  210. for ($i = 0; $i < 12; $i++) {
  211. // 计算每个月第一天的时间戳作为 Key
  212. $timestamp = strtotime("+$i month", $month_start);
  213. $monthName = ($i + 1) . '月';
  214. $trend[$timestamp] = [
  215. 'month' => $monthName,
  216. // 'salary' => "0.000",
  217. // 'zj' => "0.000",
  218. // 'other' => "0.000",
  219. 'total' => "0.000",
  220. ];
  221. }
  222. // 2. 将传入的 Map 数据填充进去
  223. foreach ($trend as $timestamp => &$item) {
  224. // 使用 ?? "0" 容错,并强制转为 string 保证 bcadd 精度
  225. $s = $salary_map[$timestamp] ?? "0";
  226. $z = $zj_map[$timestamp] ?? "0";
  227. $o = $other_map[$timestamp] ?? "0";
  228. // $item['salary'] = bcadd($s, "0", 3);
  229. // $item['zj'] = bcadd($z, "0", 3);
  230. // $item['other'] = bcadd($o, "0", 3);
  231. // 计算该月总计
  232. $item['total'] = bcadd(bcadd($s, $z, 3), $o, 3);
  233. }
  234. return array_values($trend);
  235. }
  236. private function overLeave($month_start, $month_end, $data, $user)
  237. {
  238. $id = PLeaveOverOrder::Clear($user, $data)
  239. ->where('del_time', 0)
  240. ->where("order_time", ">=", $month_start)
  241. ->where("order_time", "<", $month_end)
  242. ->pluck('id')
  243. ->toArray();
  244. $p = PLeaveOverOrderDetails::whereIn('main_id', $id)
  245. ->select("type", "total_min")
  246. ->get()
  247. ->toArray();
  248. $total = $total_1 = 0;
  249. foreach ($p as $value) {
  250. $total_min = (string)$value['total_min'];
  251. if($value['type'] == PLeaveOverOrderDetails::TYPE_ONE){
  252. $total = bcadd($total, $total_min,2);
  253. }else{
  254. $total_1 = bcadd($total_1, $total_min,2);
  255. }
  256. }
  257. // 最终返回或输出前转换
  258. $total_hours = bcdiv($total, 60, 2);
  259. $total_1_hours = bcdiv($total_1, 60, 2);
  260. return [$total_1_hours, $total_hours];
  261. }
  262. private function manWork($month_start, $month_end, $data, $user)
  263. {
  264. $daily_pw = DailyPwOrder::Clear($user, $data)
  265. ->where('del_time', 0)
  266. ->where("order_time", ">=", $month_start)
  267. ->where("order_time", "<", $month_end)
  268. ->select('id','item_id')
  269. ->get()->toArray();
  270. $item_map = Item::whereIn('id',array_unique(array_column($daily_pw,'item_id')))
  271. ->pluck('title','id')
  272. ->toArray();
  273. $p = DailyPwOrderDetails::whereIn('main_id', array_column($daily_pw,'id'))
  274. ->select("item_id", "total_work_min")
  275. ->get()
  276. ->toArray();
  277. $total = 0;
  278. $map = [];
  279. foreach ($p as $value) {
  280. $total_min = (string)$value['total_min'];
  281. if(isset($map[$value['item_id']])){
  282. $map[$value['item_id']] = bcadd($map[$value['item_id']], $total_min,2);
  283. }else{
  284. $map[$value['item_id']] = $total_min;
  285. }
  286. $total = bcadd($total, $total_min,2);
  287. }
  288. foreach ($map as $key => $value){
  289. $rate = bcmul(bcdiv($value, $total,4),100,2);
  290. $total_hours = bcdiv($value, 60, 2);
  291. $return[] = [
  292. 'title' => $item_map[$key] ?? "",
  293. 'total_work_hour' => $total_hours,
  294. 'total_work_hour_unit' => "小时",
  295. "rate" => $rate,
  296. "rate_unit" => "%",
  297. ];
  298. }
  299. return $return;
  300. }
  301. }