账单导出
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use App\Models\BillModel;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
|
||||
/**
|
||||
* 门店账单批量导出:同一门店的账单合并在一张工作表,不同门店拆分为多个工作表
|
||||
*/
|
||||
class BillBatchExport implements WithMultipleSheets
|
||||
{
|
||||
/**
|
||||
* @param Collection<int, BillModel> $bills 导出账单(with store)
|
||||
*/
|
||||
public function __construct(private readonly Collection $bills)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, BillExport>
|
||||
*/
|
||||
public function sheets(): array
|
||||
{
|
||||
$usedNames = [];
|
||||
$sheets = [];
|
||||
foreach ($this->bills->groupBy('store_id') as $storeBills) {
|
||||
$store = $storeBills->first()->store;
|
||||
$storeId = (int) $storeBills->first()->store_id;
|
||||
$sheetTitle = SheetName::make($store->name ?? ('门店#' . $storeId), $storeId, $usedNames);
|
||||
$sheets[] = new BillExport($storeBills->values(), 0, $sheetTitle);
|
||||
}
|
||||
|
||||
return $sheets;
|
||||
}
|
||||
}
|
||||
+65
-26
@@ -11,16 +11,17 @@ use App\Services\WeightEstimator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use Maatwebsite\Excel\Concerns\WithTitle;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
/**
|
||||
* 门店账单单独导出:账单内明细按商品合并(可按一级分类过滤),明细行展示商品一级分类,
|
||||
* 表尾汇总商品金额/配送费/附加金额/售后金额/总金额
|
||||
* 门店账单导出:单账单可直接下载;多账单在同一工作表依次展示(批量导出按门店分组),
|
||||
* 账单内明细按商品合并(可按一级分类过滤),表尾汇总商品金额/配送费/附加金额/售后金额/总金额
|
||||
*/
|
||||
class BillExport implements FromCollection, WithStyles
|
||||
class BillExport implements FromCollection, WithStyles, WithTitle
|
||||
{
|
||||
/** 数量列填充色(浅黄) */
|
||||
private const string QUANTITY_FILL = 'FFFFF2CC';
|
||||
@@ -45,21 +46,38 @@ class BillExport implements FromCollection, WithStyles
|
||||
/** 列头所在行索引 */
|
||||
private int $headerRow = 1;
|
||||
|
||||
/** @var array<int, int> 各账单列头所在行索引 */
|
||||
private array $headerRows = [];
|
||||
|
||||
/** @var array<int, array{header: int, last: int}> 各账单表格区域 */
|
||||
private array $tableRanges = [];
|
||||
|
||||
/** 数据末行索引 */
|
||||
private int $lastRow = 1;
|
||||
|
||||
/** @var Collection<int, BillModel> */
|
||||
private Collection $bills;
|
||||
|
||||
/**
|
||||
* @param BillModel $bill 导出账单(with store)
|
||||
* @param BillModel|Collection<int, BillModel> $bills 导出账单(with store);多账单须属于同一门店
|
||||
* @param int $categoryId 一级分类ID(0=全部)
|
||||
* @param string $sheetTitle 工作表名称
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly BillModel $bill,
|
||||
BillModel|Collection $bills,
|
||||
private readonly int $categoryId = 0,
|
||||
private readonly string $sheetTitle = '门店账单',
|
||||
) {
|
||||
$this->bills = $bills instanceof BillModel ? collect([$bills]) : $bills->values();
|
||||
}
|
||||
|
||||
public function title(): string
|
||||
{
|
||||
return $this->sheetTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出行:标题/范围/列头/明细/合计全部手工构建(行位置不固定,不用 WithHeadings)
|
||||
* 导出行:每张账单保留独立的标题/范围/列头/明细/合计,多账单之间空一行
|
||||
*/
|
||||
public function collection(): Collection
|
||||
{
|
||||
@@ -67,7 +85,27 @@ class BillExport implements FromCollection, WithStyles
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
$items = app(BillDetailService::class)->mergedItems($this->bill);
|
||||
$rows = [];
|
||||
$rowIndex = 0;
|
||||
foreach ($this->bills as $index => $bill) {
|
||||
if ($index > 0) {
|
||||
$rows[] = [''];
|
||||
$rowIndex++;
|
||||
}
|
||||
$this->appendBillRows($bill, $rows, $rowIndex);
|
||||
}
|
||||
|
||||
return $this->rows = collect($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加一张账单的完整表格
|
||||
*
|
||||
* @param array<int, array<int, mixed>> $rows
|
||||
*/
|
||||
private function appendBillRows(BillModel $bill, array &$rows, int &$rowIndex): void
|
||||
{
|
||||
$items = app(BillDetailService::class)->mergedItems($bill);
|
||||
|
||||
// 商品 => 一级分类解析(明细行「一级分类」列与分类过滤共用同一份结果)
|
||||
[$categories, $rootIds] = $this->resolveRootCategories($items);
|
||||
@@ -77,10 +115,6 @@ class BillExport implements FromCollection, WithStyles
|
||||
[$items, $categoryName] = $this->filterByRootCategory($items, $categories, $rootIds);
|
||||
}
|
||||
|
||||
$bill = $this->bill;
|
||||
$rows = [];
|
||||
$rowIndex = 0;
|
||||
|
||||
// 标题行
|
||||
$rows[] = ['门店账单 ' . $bill->bill_no];
|
||||
$this->specialRows[++$rowIndex] = 'title';
|
||||
@@ -102,7 +136,11 @@ class BillExport implements FromCollection, WithStyles
|
||||
// 列头(包规=包规+单位合并;单价=每件零售价+单价单位;一级分类=商品根分类名)
|
||||
$rows[] = ['序号', '品名', '一级分类', '包规', '单价', '数量', '重量(斤)', '金额'];
|
||||
$this->specialRows[++$rowIndex] = 'header';
|
||||
$this->headerRow = $rowIndex;
|
||||
$currentHeaderRow = $rowIndex;
|
||||
$this->headerRows[] = $currentHeaderRow;
|
||||
if (count($this->headerRows) === 1) {
|
||||
$this->headerRow = $rowIndex;
|
||||
}
|
||||
|
||||
// 明细行(账单内同商品已合并,单价为每件价折算的零售价)
|
||||
$totalQuantity = 0;
|
||||
@@ -159,8 +197,7 @@ class BillExport implements FromCollection, WithStyles
|
||||
$this->mergeRows[] = $rowIndex;
|
||||
$this->fillCells[] = ['row' => $rowIndex, 'column' => 'H', 'value' => (float) $grandTotal];
|
||||
$this->lastRow = $rowIndex;
|
||||
|
||||
return $this->rows = collect($rows);
|
||||
$this->tableRanges[] = ['header' => $currentHeaderRow, 'last' => $rowIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,20 +231,22 @@ class BillExport implements FromCollection, WithStyles
|
||||
$sheet->getStyle('A' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
|
||||
}
|
||||
|
||||
// 表格区域(列头 → 末行)添加所有边框
|
||||
$sheet->getStyle('A' . $this->headerRow . ':H' . $this->lastRow)
|
||||
->getBorders()
|
||||
->getAllBorders()
|
||||
->setBorderStyle(Border::BORDER_THIN);
|
||||
|
||||
// 金额列货币格式:¥ + 两位小数(单价列为文本,不设格式)
|
||||
$sheet->getStyle('H' . ($this->headerRow + 1) . ':H' . $this->lastRow)
|
||||
->getNumberFormat()
|
||||
->setFormatCode(self::CURRENCY_FORMAT);
|
||||
// 各账单表格区域(列头 → 末行)添加所有边框,金额列设置货币格式
|
||||
foreach ($this->tableRanges as $range) {
|
||||
$sheet->getStyle('A' . $range['header'] . ':H' . $range['last'])
|
||||
->getBorders()
|
||||
->getAllBorders()
|
||||
->setBorderStyle(Border::BORDER_THIN);
|
||||
$sheet->getStyle('H' . ($range['header'] + 1) . ':H' . $range['last'])
|
||||
->getNumberFormat()
|
||||
->setFormatCode(self::CURRENCY_FORMAT);
|
||||
}
|
||||
|
||||
// 列头固定填色:数量列(F)浅黄、金额列(H)浅红
|
||||
$this->fillCell($sheet, 'F' . $this->headerRow, self::QUANTITY_FILL);
|
||||
$this->fillCell($sheet, 'H' . $this->headerRow, self::AMOUNT_FILL);
|
||||
foreach ($this->headerRows as $headerRow) {
|
||||
$this->fillCell($sheet, 'F' . $headerRow, self::QUANTITY_FILL);
|
||||
$this->fillCell($sheet, 'H' . $headerRow, self::AMOUNT_FILL);
|
||||
}
|
||||
|
||||
// 数据区条件填色:非 0 填色,0 无背景(金额含负值)
|
||||
foreach ($this->fillCells as $cell) {
|
||||
|
||||
Reference in New Issue
Block a user