ExportFileService.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. <?php
  2. namespace App\Service;
  3. use App\Exports\ExportOrder;
  4. use App\Exports\ItemSalaryFTMultipleSheetExport;
  5. use App\Exports\ItemSalarySheetExport;
  6. use App\Exports\ManMonthlyWorkHourMultipleSheetExport;
  7. use App\Exports\MultiSheetExport;
  8. use App\Exports\ProjectDepreciationMultipleSheetExport;
  9. use App\Exports\ResearchExpenseMultipleSheetExport;
  10. use App\Model\Depart;
  11. use App\Model\PLeaveOverOrder;
  12. use Illuminate\Support\Facades\Log;
  13. use Maatwebsite\Excel\Facades\Excel;
  14. class ExportFileService extends Service
  15. {
  16. public static $filename = "";
  17. //导出的方式 0 选择导出的数据 1 查询后 导出指定的数据(最多每次一千条)
  18. public static $export_type = [
  19. 0,
  20. 1
  21. ];
  22. public function exportAll($data,$user){
  23. if(empty($data['menu_id'])) return [false, 'menu_id不能为空'];
  24. if(empty($data['type'])) return [false, 'type不能为空'];
  25. $function = $data['type'];
  26. $name = $this->fillE($data['type'], $user);
  27. if (! method_exists(self::class, $function)) return [false, "导出方法不存在,请联系开发"];
  28. self::$filename = $name;
  29. $export_type = $data['export_type'] ?? 0;
  30. if(! isset(self::$export_type[$export_type])) return [false,'导出文件方式错误或者不存在'];
  31. if(empty($export_type)){
  32. if(empty($data['id'])) return [false,'请选择导出数据'];
  33. $search = $data;
  34. }else{
  35. if(empty($data['order_search'])) return [false,'搜索条件不能为空'];
  36. $search = $data['order_search'];
  37. if(empty($search['page_index'])) return [false,'请选择导出数据的开始页码'];
  38. if(empty($search['page_size'])) return [false,'请选择导出数据的条数'];
  39. if($search['page_size'] > 5000) return [false,'请选择导出数据的条数每次最多5000条'];
  40. $data['order_search']['menu_id'] = $data['menu_id'];
  41. $search = $data['order_search'];
  42. }
  43. list($status, $return) = $this->$function($search,$user);
  44. if(! $status) return [false, $return];
  45. return [true, $return];
  46. }
  47. public function fillE($type, &$user){
  48. $header = config("excel." . $type) ?? [];
  49. $funcName = $header['name'] ?? "";
  50. // $header_f = "extra_" . $menu_id;
  51. // $service = new TableHeadService();
  52. // if(method_exists($service,$header_f)) $service->$header_f($header_default);
  53. $user['e_header_default'] = $header['array'] ?? [];
  54. return $funcName;
  55. }
  56. private function fillData($data, $column, &$return)
  57. {
  58. // 预先创建包含默认值的键数组
  59. $default = array_fill_keys($column, '');
  60. foreach ($data as $value) {
  61. // 提取交集,并用默认值补充缺失的键
  62. $return[] = array_merge($default, array_intersect_key($value, $default));
  63. }
  64. }
  65. private function fillTotalData($data, $column, &$return){
  66. $tmp = [];
  67. foreach ($column as $value){
  68. $key = $value['key'];
  69. if(! empty($value['sum']) && isset($data[$value['key']])){
  70. $decimals = $col['decimals'] ?? 2;
  71. // $tmp[$value['key']] = $data[$value['key']];
  72. // 用 number_format 格式化输出(保持字符串形式,避免科学计数)
  73. $tmp[$key] = number_format((float)$data[$key], $decimals, '.', '');
  74. }else{
  75. $tmp[$value['key']] = "";
  76. }
  77. }
  78. $return[] = $tmp;
  79. }
  80. public function employee($ergs,$user){
  81. // 导出数据
  82. $return = [];
  83. $header_default = $user['e_header_default'];
  84. $column = array_column($header_default,'export');
  85. $header = array_column($header_default,'value');
  86. $service = new EmployeeService();
  87. $model = $service->employeeCommon($ergs, $user);
  88. $model->chunk(500,function ($data) use(&$return, $service, $column, $user){
  89. $service->fillDataForExport($data, $column, $user, $return);
  90. });
  91. return [true, $this->saveExportData($return,$header)];
  92. }
  93. public function depart($ergs,$user){
  94. // 导出数据
  95. $return = [];
  96. $header_default = $user['e_header_default'];
  97. $column = array_column($header_default,'export');
  98. $header = array_column($header_default,'value');
  99. $service = new EmployeeService();
  100. $model = $service->departCommon($ergs, $user);
  101. $model->chunk(500,function ($data) use(&$return, $service, $column, $user){
  102. $data = $data->toArray();
  103. $list['data'] = $data;
  104. //订单数据
  105. $list = $service->fillDepartList($list, $user, true);
  106. //返回数据
  107. $this->fillData($list['data'], $column, $return);
  108. });
  109. return [true, $this->saveExportData($return,$header)];
  110. }
  111. public function device($ergs,$user){
  112. // 导出数据
  113. $return = [];
  114. $header_default = $user['e_header_default'];
  115. $column = array_column($header_default,'export');
  116. $header = array_column($header_default,'value');
  117. $service = new DeviceService();
  118. $model = $service->deviceCommon($ergs, $user);
  119. $model->chunk(500,function ($data) use(&$return, $service, $column,$ergs, $user){
  120. $data = $data->toArray();
  121. $list['data'] = $data;
  122. //订单数据
  123. $list = $service->fillData($list, $ergs, $user);
  124. //返回数据
  125. $this->fillData($list['data'], $column, $return);
  126. });
  127. return [true, $this->saveExportData($return,$header)];
  128. }
  129. public function item($ergs,$user){
  130. // 导出数据
  131. $return = [];
  132. $header_default = $user['e_header_default'];
  133. $column = array_column($header_default,'export');
  134. $header = array_column($header_default,'value');
  135. $service = new ItemService();
  136. $model = $service->itemCommon($ergs, $user);
  137. $model->chunk(500,function ($data) use(&$return,$column,$service, $user){
  138. // 调用 Service 层的填充方法
  139. $service->fillDataForExport($data, $column, $user, $return);
  140. });
  141. return [true, $this->saveExportData($return,$header)];
  142. }
  143. public function fee($ergs,$user){
  144. // 导出数据
  145. $return = [];
  146. $header_default = $user['e_header_default'];
  147. $column = array_column($header_default,'export');
  148. $header = array_column($header_default,'value');
  149. $service = new FeeService();
  150. $model = $service->feeCommon($ergs, $user);
  151. $model->chunk(500,function ($data) use(&$return, $service, $column, $user){
  152. $data = $data->toArray();
  153. $list['data'] = $data;
  154. //订单数据
  155. $list = $service->fillFeeList($list, $user, true);
  156. //返回数据
  157. $this->fillData($list['data'], $column, $return);
  158. });
  159. return [true, $this->saveExportData($return,$header)];
  160. }
  161. public function monthPwOrder($ergs,$user){
  162. // 导出数据
  163. $return = [];
  164. $header_default = $user['e_header_default'];
  165. $column = array_column($header_default,'export');
  166. $header = array_column($header_default,'value');
  167. $service = new PersonWorkService();
  168. $model = $service->monthlyPwOrderCommon($ergs, $user);
  169. $model->chunk(500,function ($data) use(&$return, $service, $column){
  170. // 直接处理这一批主表数据,将其与详情合并平铺
  171. $service->fillDataForExport($data->toArray(), $column, $return);
  172. });
  173. return [true, $this->saveExportData($return,$header)];
  174. }
  175. public function monthDwOrder($ergs,$user){
  176. // 导出数据
  177. $return = [];
  178. $header_default = $user['e_header_default'];
  179. $column = array_column($header_default,'export');
  180. $header = array_column($header_default,'value');
  181. $service = new DeviceWorkService();
  182. $model = $service->monthlyDwOrderCommon($ergs, $user);
  183. $model->chunk(500,function ($data) use(&$return, $service, $column){
  184. // 直接处理这一批主表数据,将其与详情合并平铺
  185. $service->fillDataForExport($data->toArray(), $column, $return);
  186. });
  187. return [true, $this->saveExportData($return,$header)];
  188. }
  189. public function monthPsOrder($ergs,$user){
  190. // 导出数据
  191. $return = [];
  192. $header_default = $user['e_header_default'];
  193. $column = array_column($header_default,'export');
  194. $header = array_column($header_default,'value');
  195. $service = new PersonSalaryService();
  196. $model = $service->monthlyPsOrderCommon($ergs, $user);
  197. $model->chunk(500,function ($data) use(&$return, $service, $column){
  198. // 直接处理这一批主表数据,将其与详情合并平铺
  199. $service->fillDataForExport($data->toArray(), $column, $return);
  200. });
  201. return [true, $this->saveExportData($return,$header)];
  202. }
  203. public function monthDdOrder($ergs,$user){
  204. // 导出数据
  205. $return = [];
  206. $header_default = $user['e_header_default'];
  207. $column = array_column($header_default,'export');
  208. $header = array_column($header_default,'value');
  209. $service = new DeviceDepreciationService();
  210. $model = $service->monthlyDdOrderCommon($ergs, $user);
  211. $model->chunk(500,function ($data) use(&$return, $service, $column){
  212. // 直接处理这一批主表数据,将其与详情合并平铺
  213. $service->fillDataForExport($data->toArray(), $column, $return);
  214. });
  215. return [true, $this->saveExportData($return,$header)];
  216. }
  217. public function ruleSet($ergs,$user){
  218. // 导出数据
  219. $return = [];
  220. $header_default = $user['e_header_default'];
  221. $column = array_column($header_default,'export');
  222. $header = array_column($header_default,'value');
  223. $service = new RuleSetService();
  224. $model = $service->ruleSetCommon($ergs, $user);
  225. $model->chunk(500,function ($data) use(&$return, $service, $column){
  226. // 直接处理这一批主表数据,将其与详情合并平铺
  227. $service->fillDataForExport($data->toArray(), $column, $return);
  228. });
  229. return [true, $this->saveExportData($return,$header)];
  230. }
  231. public function dailyPwOrder($ergs,$user){
  232. // 导出数据
  233. $return = [];
  234. $header_default = $user['e_header_default'];
  235. $column = array_column($header_default,'export');
  236. $header = array_column($header_default,'value');
  237. $service = new PersonWorkService();
  238. $model = $service->dailyPwOrderCommon($ergs, $user);
  239. $model->chunk(500,function ($data) use(&$return, $service, $column){
  240. // 直接处理这一批主表数据,将其与详情合并平铺
  241. $service->fillDataForExportDaily($data->toArray(), $column, $return);
  242. });
  243. return [true, $this->saveExportData($return,$header)];
  244. }
  245. public function dailyDwOrder($ergs,$user){
  246. // 导出数据
  247. $return = [];
  248. $header_default = $user['e_header_default'];
  249. $column = array_column($header_default,'export');
  250. $header = array_column($header_default,'value');
  251. $service = new DeviceWorkService();
  252. $model = $service->dailyDwOrderCommon($ergs, $user);
  253. $model->chunk(500,function ($data) use(&$return, $service, $column){
  254. // 直接处理这一批主表数据,将其与详情合并平铺
  255. $service->fillDataForExportDaily($data->toArray(), $column, $return);
  256. });
  257. return [true, $this->saveExportData($return,$header)];
  258. }
  259. public function leaveOrder($ergs,$user){
  260. // 导出数据
  261. $return = [];
  262. $header_default = $user['e_header_default'];
  263. $column = array_column($header_default,'export');
  264. $header = array_column($header_default,'value');
  265. $service = new PLeaveOverService();
  266. $ergs['type'] = PLeaveOverOrder::TYPE_ONE;
  267. $model = $service->pLeaveOverCommon($ergs, $user);
  268. $model->chunk(500,function ($data) use(&$return, $service, $column){
  269. // 直接处理这一批主表数据,将其与详情合并平铺
  270. $service->fillDataForExportDaily($data->toArray(), $column, $return);
  271. });
  272. return [true, $this->saveExportData($return,$header)];
  273. }
  274. public function overtimeOrder($ergs,$user){
  275. // 导出数据
  276. $return = [];
  277. $header_default = $user['e_header_default'];
  278. $column = array_column($header_default,'export');
  279. $header = array_column($header_default,'value');
  280. $service = new PLeaveOverService();
  281. $ergs['type'] = PLeaveOverOrder::TYPE_TWO;
  282. $model = $service->pLeaveOverCommon($ergs, $user);
  283. $model->chunk(500,function ($data) use(&$return, $service, $column){
  284. // 直接处理这一批主表数据,将其与详情合并平铺
  285. $service->fillDataForExportDaily($data->toArray(), $column, $return);
  286. });
  287. return [true, $this->saveExportData($return,$header)];
  288. }
  289. // 项目工资统计表
  290. public function exportEmployeeSalary($data, $user)
  291. {
  292. $service = new StatisticService();
  293. // 1. 调用你现有的查询方法获取基础数据
  294. list($status, $itemMonthList) = $service->employeeMonthSalaryStatistic($data, $user);
  295. if (!$status) return $itemMonthList; // 返回错误信息
  296. // 2. 提取所有涉及到的唯一项目 (按 Code 排序,保证列顺序固定)
  297. $projects = collect($itemMonthList)->pluck('item_code')->unique()->sort()->values()->toArray();
  298. // 3. 按月份对数据进行分组
  299. $groupedByMonth = collect($itemMonthList)->groupBy('month')->sortKeys();
  300. $exportData = [];
  301. $columnTotals = array_fill(0, count($projects) * 2, 0); // 用于存储每列的合计
  302. $grandTotalSalary = 0; // 总计金额
  303. // 4. 循环每个月,构造行数据
  304. foreach ($groupedByMonth as $month => $items) {
  305. $row = [$month]; // A列:月份
  306. $monthTotalSalary = 0;
  307. // 创建该月项目的映射,方便快速查找
  308. $monthItemsMap = $items->keyBy('item_code');
  309. foreach ($projects as $index => $code) {
  310. $itemDetail = $monthItemsMap->get($code);
  311. $days = $itemDetail['days'] ?? 0;
  312. $salary = $itemDetail['allocated_salary'] ?? 0;
  313. $row[] = $days > 0 ? $days : ''; // 天数列
  314. $row[] = $salary > 0 ? $salary : ''; // 工资列
  315. // 累加合计行(列合计)
  316. $columnTotals[$index * 2] += $days;
  317. $columnTotals[$index * 2 + 1] += $salary;
  318. $monthTotalSalary += $salary;
  319. }
  320. $row[] = $monthTotalSalary; // 最后一列:该月合计工资
  321. $grandTotalSalary += $monthTotalSalary;
  322. $exportData[] = $row;
  323. }
  324. // 5. 构造最后的“合计”行
  325. $totalRow = ['合计'];
  326. foreach ($columnTotals as $val) {
  327. $totalRow[] = $val > 0 ? $val : 0;
  328. }
  329. $totalRow[] = $grandTotalSalary;
  330. $exportData[] = $totalRow;
  331. $file_name = "项目工资统计表_" . date("Y-m-d") . "_". rand(1000,9999);
  332. $filename = $file_name . '.' . 'xlsx';
  333. $bool = Excel::store(new ItemSalarySheetExport($projects, $exportData, Depart::where('id', $user['top_depart_id'])->value('title')),"/public/export/{$filename}", null, 'Xlsx', []);
  334. return [true, $filename];
  335. }
  336. // 人员月工时统计表
  337. public function exportManMonthlyWorkHour($data, $user)
  338. {
  339. // 1. 获取报表基础数据
  340. $service = new StatisticService();
  341. list($status, $result) = $service->employeeDayHourStatistic($data, $user);
  342. if (!$status) return $result;
  343. $sourceData = collect($result['data']);
  344. // 2. 按月份分组,准备多 Sheet 数据
  345. // 结构:[ '2024-04' => [ 'days' => 30, 'data' => [...] ], ... ]
  346. $monthsData = [];
  347. // 按月分组数据
  348. $groupedByMonth = $sourceData->groupBy(function ($item) {
  349. return date('Y年m月', strtotime($item['order_date']));
  350. });
  351. foreach ($groupedByMonth as $monthName => $monthItems) {
  352. // A. 计算该月总天数
  353. // 转换 '2024年04月' 为 '2024-04' 获取天数
  354. $formatMonth = str_replace(['年', '月'], ['-', ''], $monthName);
  355. $daysInMonth = date('t', strtotime($formatMonth . '-01'));
  356. // B. 进一步按 项目+人员 分组,因为一行显示一个项目一个人的全月工时
  357. $groupedByUserItem = $monthItems->groupBy(function ($item) {
  358. return $item['item_code'] . '_' . $item['employee_id'];
  359. });
  360. $sheetRows = [];
  361. foreach ($groupedByUserItem as $key => $records) {
  362. $first = $records->first();
  363. // 初始化行:前两列是 项目编号 和 姓名
  364. $row = [
  365. $first['item_code'],
  366. $first['employee_name']
  367. ];
  368. // C. 循环 1 号到该月最后一天,填充工时
  369. // 将该员工该项目在这个月的记录转为 日期 => 工时 的映射
  370. $dayMap = $records->keyBy(function($r){
  371. return (int)date('d', strtotime($r['order_date']));
  372. });
  373. for ($d = 1; $d <= $daysInMonth; $d++) {
  374. $workHour = $dayMap->get($d)['total_work_hours'] ?? '';
  375. // 如果工时为 0 或空,按你要求的格式传空字符串
  376. $row[] = ($workHour > 0) ?(float) $workHour : '';
  377. }
  378. $sheetRows[] = $row;
  379. }
  380. $monthsData[$monthName] = [
  381. 'days' => (int)$daysInMonth,
  382. 'data' => $sheetRows
  383. ];
  384. }
  385. $file_name = "人员月工时统计表_" . date("Y-m-d") . "_". rand(1000,9999);
  386. $filename = $file_name . '.' . 'xlsx';
  387. $bool = Excel::store(new ManMonthlyWorkHourMultipleSheetExport($monthsData), "/public/export/{$filename}", null, 'Xlsx', []);
  388. return [true, $filename];
  389. }
  390. // 项目设备折旧
  391. public function exportDeviceZj(array $data, $user)
  392. {
  393. $service = new StatisticService();
  394. list($status, $result) = $service->itemDeviceMonthStatistic($data, $user);
  395. if (!$status) return $result;
  396. // 2. 将数据按 [项目编号][月份] 进行分组
  397. // 预期结构:$monthsData['RD01']['months']['2025-01'] = [设备1, 设备2...]
  398. $groupedData = collect($result)->groupBy('item_code');
  399. $finalExportData = [];
  400. foreach ($groupedData as $itemCode => $itemRecords) {
  401. $projectName = $itemRecords->first()['item_title'] ?? '';
  402. // 2. 获取年份(从 order_month 字段提取,如 "2026-01" 取前4位)
  403. $firstMonth = $firstRecord['month'] ?? date('Y-m');
  404. $year = substr($firstMonth, 0, 4);
  405. // 3. 构造新的 Key:年-项目 (例如: 2026-53code)
  406. $newSheetKey = $year . '年度项目' . $itemCode;
  407. // 按月份进一步分组
  408. $monthGroups = $itemRecords->groupBy('month');
  409. $monthsPayload = [];
  410. foreach ($monthGroups as $month => $devices) {
  411. $monthData = [];
  412. foreach ($devices as $dev) {
  413. $monthData[] = [
  414. 'device_name' => $dev['device_title'],
  415. 'original_value' => $dev['device_original'], // 设备原值
  416. 'total_depreciation' => $dev['total_depreciatio'], // 当月总折旧
  417. 'project_hours' => $dev['hours'], // 本项目工时
  418. 'total_hours' => $dev['total_hours'], // 当月总工时
  419. 'ratio' => bcmul($dev['ratio'], 100,2), // 研发工时占比
  420. 'allocated_depreciation' => $dev['allocated_depreciatio'], // 本项目分摊折旧
  421. ];
  422. }
  423. $monthsPayload[$month] = $monthData;
  424. }
  425. // 构造 Sheet 所需结构
  426. $finalExportData[$newSheetKey] = [
  427. 'project_name' => $projectName,
  428. 'months' => $monthsPayload
  429. ];
  430. }
  431. $file_name = "项目设备折旧费用统计表_" . date("Y-m-d") . "_". rand(1000,9999);
  432. $filename = $file_name . '.' . 'xlsx';
  433. $bool = Excel::store(new ProjectDepreciationMultipleSheetExport($finalExportData), "/public/export/{$filename}", null, 'Xlsx', []);
  434. return [true, $filename];
  435. }
  436. // 项目工资分摊
  437. public function exportItemSalaryFT(array $data, $user)
  438. {
  439. $service = new StatisticService();
  440. list($status, $result) = $service->itemDaySalaryStatistic($data, $user);
  441. if (!$status) return $result;
  442. $sourceData = collect($result);
  443. $groupedByMonth = $sourceData->groupBy('month');
  444. $monthsData = [];
  445. foreach ($groupedByMonth as $month => $monthRecords) {
  446. // A. 获取本月参与的所有唯一项目编码
  447. $monthProjects = $monthRecords->pluck('item_code')->unique()->sort()->values()->all();
  448. // B. 按人员分组组织数据
  449. $groupedByEmployee = $monthRecords->groupBy('employee_id');
  450. $sheetRows = [];
  451. $index = 1;
  452. // 初始化列合计
  453. $colTotals = [
  454. 'total_salary' => 0,
  455. 'total_min_hours' => 0, // 月总工时合计
  456. 'project_days' => array_fill_keys($monthProjects, 0),
  457. 'project_salary' => array_fill_keys($monthProjects, 0),
  458. 'total_attendance_days' => 0, // 合计工时列的合计
  459. ];
  460. foreach ($groupedByEmployee as $employeeId => $records) {
  461. $first = $records->first();
  462. $empMonthSalary = (float)$first['total_salary'];
  463. $empTotalHours = round((float)$first['total_min'] / 60, 2); // 月总工时(小时)
  464. // 1-4列:序号、姓名、工资、月总工时
  465. $row = [$index++, $first['employee_title'], $empMonthSalary, $empTotalHours];
  466. // 5. 动态项目工时列
  467. $empMap = $records->keyBy('item_code');
  468. $rowProjectDaysSum = 0;
  469. foreach ($monthProjects as $code) {
  470. $days = $empMap->has($code) ? (float)$empMap->get($code)['days'] : 0;
  471. $row[] = $days > 0 ? $days : '';
  472. $rowProjectDaysSum += $days;
  473. $colTotals['project_days'][$code] += $days;
  474. }
  475. // 6. 合计工时列
  476. $row[] = $rowProjectDaysSum;
  477. $colTotals['total_attendance_days'] += $rowProjectDaysSum;
  478. // 7. 动态项目金额列
  479. foreach ($monthProjects as $code) {
  480. $salary = $empMap->has($code) ? (float)$empMap->get($code)['allocated_salary'] : 0;
  481. $row[] = $salary > 0 ? $salary : '';
  482. $colTotals['project_salary'][$code] += $salary;
  483. }
  484. // 8. 总计工资列
  485. $row[] = $empMonthSalary;
  486. // 累加合计
  487. $colTotals['total_salary'] += $empMonthSalary;
  488. $colTotals['total_min_hours'] += $empTotalHours;
  489. $sheetRows[] = $row;
  490. }
  491. // C. 构造合计行
  492. $totalRow = ['合计', '', $colTotals['total_salary'], $colTotals['total_min_hours']];
  493. foreach ($monthProjects as $code) { $totalRow[] = $colTotals['project_days'][$code]; }
  494. $totalRow[] = $colTotals['total_attendance_days'];
  495. foreach ($monthProjects as $code) { $totalRow[] = $colTotals['project_salary'][$code]; }
  496. $totalRow[] = $colTotals['total_salary'];
  497. $sheetRows[] = $totalRow;
  498. $monthsData[$month] = [
  499. 'projects' => $monthProjects,
  500. 'data' => $sheetRows
  501. ];
  502. }
  503. $file_name = "项目工资分摊统计表_" . date("Y-m-d") . "_". rand(1000,9999);
  504. $filename = $file_name . '.xlsx';
  505. Excel::store(
  506. new ItemSalaryFTMultipleSheetExport($monthsData, Depart::where('id', $user['top_depart_id'])->value('title')),
  507. "/public/export/{$filename}"
  508. );
  509. return [true, $filename];
  510. }
  511. // 年度研发支出辅助账汇总表
  512. public function exportResearchExpense(array $data, $user)
  513. {
  514. $service = new StatisticService();
  515. list($status, $result) = $service->auxiliaryStatistic($data, $user);
  516. if (!$status) return [false, $result];
  517. $fee_type_list = $result['fee_type_list'];
  518. $raw_list = $result['list'];
  519. if(empty($raw_list)) return [false, '暂无导出数据'];
  520. // 预读年份和动态表头长度
  521. $year = date("Y", strtotime($raw_list[0]['voucher_date']));
  522. $company = Depart::where('id', $user['top_depart_id'])->value('title');
  523. $dynamicHeaderTitles = array_column($fee_type_list, 'title');
  524. $dynamicCount = count($dynamicHeaderTitles);
  525. $totalColCount = 8 + $dynamicCount + 2; // 基础6 + 固定2 + 动态N + 委托2
  526. $groupedData = [];
  527. foreach ($raw_list as $row) {
  528. $sheetKey = $year . $row['code'];
  529. if (!isset($groupedData[$sheetKey])) {
  530. $groupedData[$sheetKey] = [
  531. 'project' => [
  532. 'code' => $row['code'],
  533. 'name' => $row['title'],
  534. ],
  535. 'dynamic_headers' => $dynamicHeaderTitles,
  536. 'data' => [],
  537. 'year' => $company . $year,
  538. 'totals' => array_fill(0, $totalColCount, 0)
  539. ];
  540. $groupedData[$sheetKey]['totals'][0] = '合计'; // 第一列标识
  541. }
  542. // 组织明细行数据 (逻辑不变)
  543. $excelRow = [
  544. $row['voucher_date'], $row['voucher_type'], $row['voucher_no'], $row['voucher_remark'],
  545. (float)$row['voucher_amount'],
  546. (float)$row['aggregation_amount'],
  547. ($row['type'] == 1 ? (float)$row['total_amount'] : 0), // 人员
  548. ($row['type'] == 2 ? (float)$row['total_amount'] : 0) // 折旧
  549. ];
  550. // 动态列填充
  551. foreach ($fee_type_list as $feeId => $feeItem) {
  552. $excelRow[] = ($row['type'] == 3 && $row['fee_id'] == $feeId) ? (float)$row['total_amount'] : 0;
  553. }
  554. // 委托列
  555. $excelRow[] = (float)($row['entrust1_amount'] ?? 0);
  556. $excelRow[] = (float)($row['entrust2_amount'] ?? 0);
  557. // 【性能优化】:同步累加金额 (从索引 4 开始是金额列)
  558. for ($i = 4; $i < $totalColCount; $i++) {
  559. $groupedData[$sheetKey]['totals'][$i] += $excelRow[$i];
  560. }
  561. $groupedData[$sheetKey]['data'][] = $excelRow;
  562. }
  563. foreach ($groupedData as &$group) {
  564. $group['data'][] = $group['totals'];
  565. unset($group['totals']); // 释放内存
  566. }
  567. // 8. 导出逻辑保持不变
  568. $file_name = "年度研发支出辅助账汇总统计表_" . date("Y-m-d") . "_". rand(1000,9999);
  569. $filename = $file_name . '.xlsx';
  570. Excel::store(
  571. new ResearchExpenseMultipleSheetExport($groupedData),
  572. "public/export/{$filename}"
  573. );
  574. return [true, $filename];
  575. }
  576. // 研发支出辅助帐汇总表
  577. public function exportFormalSummary(array $data, $user)
  578. {
  579. $service = new StatisticService();
  580. // 1. 调用业务逻辑获取原始数据
  581. list($status, $result) = $service->employeeAttendanceMonthStatistic($data, $user);
  582. if (!$status) return [false, $result];
  583. $rawList = $result['list'] ?? [];
  584. $feeTypes = $result['fee_type_list'] ?? [];
  585. // 2. 构造动态表头名称
  586. $dynamicHeaderTitles = array_column($feeTypes, 'title');
  587. $dynamicHeaders = array_merge(['人员人工费用', '折旧费用'], $dynamicHeaderTitles);
  588. $items = [];
  589. foreach ($rawList as $v) {
  590. $rowValues = [];
  591. // A. 填充基础固定两项:人工和折旧
  592. $rowValues[] = (float)($v['employee_salary'] ?? 0);
  593. $rowValues[] = (float)($v['device_depreciation'] ?? 0);
  594. // B. 初始化累加变量
  595. $val7_1 = 0; // 其他相关费用合计 (所有 total_amount 之和)
  596. $val8_1 = 0; // 委托境内合计 (所有 entrust1_amount 之和)
  597. $val8_3 = 0; // 委托境外合计 (所有 entrust2_amount 之和)
  598. // 将当前项目的 fee_list 转为集合以便按 ID 快速查找
  599. $currentProjectFees = collect($v['fee_list'] ?? [])->keyBy('id');
  600. // 遍历所有可能的费用类型,确保 values 数组长度与表头一致
  601. foreach ($feeTypes as $type) {
  602. $feeData = $currentProjectFees->get($type['id']);
  603. $amount = (float)($feeData['total_amount'] ?? 0);
  604. // 填充到动态科目列
  605. $rowValues[] = $amount;
  606. // --- 费用归集逻辑修正 ---
  607. // 1. 累加其他相关费用合计 (7.1)
  608. $val7_1 += $amount;
  609. // 2. 累加委托费用 (8.1 和 8.3)
  610. $val8_1 += (float)($feeData['entrust1_amount'] ?? 0);
  611. $val8_3 += (float)($feeData['entrust2_amount'] ?? 0);
  612. }
  613. $items[] = [
  614. 'no' => $v['code'] ?? '',
  615. 'name' => $v['title'] ?? '',
  616. 'status' => (($v['state'] ?? '') == "完结" ? 3 : 2),
  617. 'type' => $v['expense_type'] ?? '费用化支出',
  618. 'values' => $rowValues,
  619. 'val7_1' => $val7_1, // 修正:此处为 total_amount 累加之和
  620. 'val8_1' => $val8_1, // 修正:此处为 entrust1_amount 累加之和
  621. 'val8_3' => $val8_3, // 修正:此处为 entrust2_amount 累加之和
  622. ];
  623. }
  624. // 年份获取逻辑
  625. $timeSource = $data['month_start'] ?? ($data['month_end'] ?? 'now');
  626. $year = is_numeric($timeSource) ? date('Y', $timeSource) : date('Y', strtotime($timeSource));
  627. // 3. 组织多 Sheet 格式数据
  628. $monthsData = [
  629. $year => [
  630. 'tax_id' => $user->tax_id ?? '***********',
  631. 'company_name' => Depart::where('id', $user['top_depart_id'])->value('title'),
  632. 'items' => $items,
  633. 'dynamic_headers' => $dynamicHeaders
  634. ]
  635. ];
  636. // 4. 执行 Excel 存储
  637. $file_name = "研发支出辅助账汇总表_" . date("YmdHis") . "_" . rand(1000, 9999);
  638. $filename = $file_name . '.xlsx';
  639. Log::channel('request')->info('request', ['param'=>$monthsData]);
  640. Excel::store(
  641. new \App\Exports\ResearchExpenseSummaryMultipleSheetExport($monthsData),
  642. "public/export/{$filename}"
  643. );
  644. return [true, $filename];
  645. }
  646. public function saveExportData($data, $headers, $type = 'default',$file_name = ''){
  647. if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
  648. $filename = $file_name . '.' . 'xlsx';
  649. $bool = Excel::store(new ExportOrder($data,$type,$headers),"/public/export/{$filename}", null, 'Xlsx', []);
  650. return $filename;
  651. }
  652. public function saveExportData2($data,$type = 1,$column,$timeRow, $file_name = ''){
  653. if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
  654. $filename = $file_name . '.' . 'xlsx';
  655. \Maatwebsite\Excel\Facades\Excel::store(new MultiSheetExport($data, $type,$column,$timeRow),"/public/export/{$filename}", null, 'Xlsx', []);
  656. return $filename;
  657. }
  658. }