回筐导出

This commit is contained in:
liu
2026-08-15 00:14:19 +08:00
parent 7c3f6147ac
commit cd35213ae6
7 changed files with 459 additions and 3 deletions
+145
View File
@@ -0,0 +1,145 @@
<?php
namespace App\Exports;
use App\Models\ContainerReturnModel;
use Carbon\CarbonPeriod;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
/**
* 压回筐记录汇总导出:行=日期(区间内逐日),列=门店,
* 单元格=当日该门店抵扣(附加)金额合计(同日多条记录合并),无记录填 0;
* 末列为行合计,末行为列合计(右下角为总计)
*
* WithStrictNullComparisonPhpSpreadsheet fromArray 默认松散比较(0 == null 为 true),
* 数值 0 的单元格会被跳过写成空白,必须启用严格比较才能把 0 填进表格
*/
class ContainerReturnExport implements FromCollection, WithStyles, WithStrictNullComparison
{
private ?Collection $rows = null;
/** @var array<int, string> 特殊行索引(1 起)=> 类型(title/scope/header/total */
private array $specialRows = [];
/** 列头所在行索引 */
private int $headerRow = 1;
/**
* @param Collection<int, ContainerReturnModel> $records 区间内压回筐记录
* @param array<int, string> $storeNames 门店ID => 名称(导出列,升序)
* @param string $startDate 开始日期 Y-m-d
* @param string $endDate 结束日期 Y-m-d
*/
public function __construct(
private readonly Collection $records,
private readonly array $storeNames,
private readonly string $startDate,
private readonly string $endDate,
) {
}
/**
* 导出行:标题/范围/列头/日期矩阵/合计全部手工构建(行位置不固定,不用 WithHeadings
*/
public function collection(): Collection
{
if ($this->rows !== null) {
return $this->rows;
}
// 同日期同门店多条记录合并:金额代数和(正=压筐附加,负=回筐抵扣)
$amounts = [];
foreach ($this->records as $record) {
$date = $record->created_at->format('Y-m-d');
$amounts[$date][(int) $record->store_id] = bcadd(
$amounts[$date][(int) $record->store_id] ?? '0',
(string) $record->amount,
2
);
}
$rows = [];
$rowIndex = 0;
// 标题行
$rows[] = ['压回筐记录汇总(' . $this->startDate . ' ~ ' . $this->endDate . ''];
$this->specialRows[++$rowIndex] = 'title';
// 范围行:门店 / 导出时间
$rows[] = ['门店:' . implode('、', array_values($this->storeNames)) . ' 导出时间:' . now()->format('Y-m-d H:i:s')];
$this->specialRows[++$rowIndex] = 'scope';
// 空行
$rows[] = [''];
$rowIndex++;
// 列头:日期 + 门店列 + 行合计
$rows[] = array_merge(['日期'], array_values($this->storeNames), ['合计']);
$this->specialRows[++$rowIndex] = 'header';
$this->headerRow = $rowIndex;
// 日期矩阵:区间内逐日一行,当天门店无记录填 0
$storeIds = array_map(intval(...), array_keys($this->storeNames));
$columnTotals = array_fill_keys($storeIds, '0');
$grandTotal = '0';
foreach (CarbonPeriod::create($this->startDate, $this->endDate) as $date) {
$dateKey = $date->format('Y-m-d');
$row = [$dateKey];
$rowTotal = '0';
foreach ($storeIds as $storeId) {
$amount = $amounts[$dateKey][$storeId] ?? '0.00';
$row[] = (float) $amount;
$rowTotal = bcadd($rowTotal, $amount, 2);
$columnTotals[$storeId] = bcadd($columnTotals[$storeId], $amount, 2);
}
$grandTotal = bcadd($grandTotal, $rowTotal, 2);
$row[] = (float) $rowTotal;
$rows[] = $row;
$rowIndex++;
}
// 合计行:各门店列合计 + 总计
$totalRow = ['合计'];
foreach ($storeIds as $storeId) {
$totalRow[] = (float) $columnTotals[$storeId];
}
$totalRow[] = (float) $grandTotal;
$rows[] = $totalRow;
$this->specialRows[++$rowIndex] = 'total';
return $this->rows = collect($rows);
}
/**
* 标题/列头/合计行加粗,冻结列头行
*/
public function styles(Worksheet $sheet): array
{
$this->collection();
$sheet->freezePane('A' . ($this->headerRow + 1));
$sheet->getColumnDimension('A')->setWidth(12);
$columnCount = count($this->storeNames) + 2;
for ($column = 2; $column <= $columnCount; $column++) {
$sheet->getColumnDimensionByColumn($column)->setWidth(12);
}
$styles = [];
foreach ($this->specialRows as $row => $type) {
$style = match ($type) {
'title' => ['font' => ['bold' => true, 'size' => 14]],
'header', 'total' => ['font' => ['bold' => true]],
default => [],
};
if ($style !== []) {
$styles[$row] = $style;
}
}
return $styles;
}
}