采购单优化
This commit is contained in:
@@ -5,7 +5,10 @@ namespace App\Exports;
|
||||
use App\Models\ProductCategoryModel;
|
||||
use App\Models\ProductModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\StoreOrderItemModel;
|
||||
use App\Models\SupplierModel;
|
||||
use App\Services\PurchaseEditService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
@@ -15,6 +18,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
/**
|
||||
* 采购单导出(C2 全品类按分类 sort 排序 / C3 仅蔬果分类)
|
||||
* 数据源为订货明细按商品聚合(无独立采购明细表),每门店一列显示数量
|
||||
*/
|
||||
class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping, WithStyles
|
||||
{
|
||||
@@ -24,6 +28,9 @@ class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping,
|
||||
/** @var array<int, string> 供应商ID => 名称 */
|
||||
private array $supplierNames = [];
|
||||
|
||||
/** @var array<int, string> 门店ID => 名称(导出列) */
|
||||
private array $storeNames = [];
|
||||
|
||||
private ?Collection $items = null;
|
||||
|
||||
/**
|
||||
@@ -37,7 +44,7 @@ class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping,
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出行(按 sort 排序;蔬果分类时按顶级分类名过滤)
|
||||
* 导出行(商品聚合行,按 分类sort → 商品sort 排序;蔬果分类时按顶级分类名过滤)
|
||||
*/
|
||||
public function collection(): Collection
|
||||
{
|
||||
@@ -45,38 +52,112 @@ class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping,
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
$items = $this->purchase->items()->orderBy('sort')->get();
|
||||
$orderItems = StoreOrderItemModel::query()
|
||||
->join('store_order', 'store_order.id', '=', 'store_order_item.order_id')
|
||||
->where('store_order_item.purchase_id', $this->purchase->id)
|
||||
->whereNull('store_order.deleted_at')
|
||||
->select('store_order_item.*')
|
||||
->get()
|
||||
->makeVisible('cost_price');
|
||||
|
||||
$this->storeNames = StoreModel::withTrashed()
|
||||
->whereIn('id', $orderItems->pluck('store_id')->unique())
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
|
||||
$products = ProductModel::withTrashed()
|
||||
->with('category:id,sort')
|
||||
->whereIn('id', $orderItems->pluck('product_id')->unique())
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$rows = [];
|
||||
foreach ($orderItems->groupBy('product_id') as $productId => $group) {
|
||||
$first = $group->first();
|
||||
$product = $products->get((int) $productId);
|
||||
$unitCost = PurchaseEditService::unitCost((string) ($first->cost_price ?? '0'), (string) $first->product_spec);
|
||||
|
||||
$quantity = '0';
|
||||
$weight = '0';
|
||||
$amount = '0';
|
||||
$storeQuantities = array_fill_keys(array_keys($this->storeNames), '0');
|
||||
foreach ($group as $item) {
|
||||
$quantity = bcadd($quantity, (string) $item->quantity, 2);
|
||||
$weight = bcadd($weight, (string) $item->weight, 3);
|
||||
$amount = bcadd($amount, PurchaseEditService::costAmount(
|
||||
(string) $item->quantity,
|
||||
(string) $item->weight,
|
||||
$unitCost
|
||||
), 2);
|
||||
$storeQuantities[(int) $item->store_id] = bcadd(
|
||||
$storeQuantities[(int) $item->store_id] ?? '0',
|
||||
(string) $item->quantity,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'product_id' => (int) $productId,
|
||||
'supplier_id' => (int) $first->supplier_id,
|
||||
'product_name' => $first->product_name,
|
||||
'product_spec' => $first->product_spec,
|
||||
'unit' => $first->unit,
|
||||
'cost_price' => (float) ($first->cost_price ?? 0),
|
||||
'unit_cost' => (float) $unitCost,
|
||||
'quantity' => (float) $quantity,
|
||||
'weight' => (float) $weight,
|
||||
'amount' => (float) $amount,
|
||||
'store_quantities' => array_map('floatval', $storeQuantities),
|
||||
'category_sort' => (int) ($product->category->sort ?? 9999),
|
||||
'product_sort' => (int) ($product->sort ?? 9999),
|
||||
];
|
||||
}
|
||||
usort($rows, static fn (array $a, array $b): int =>
|
||||
[$a['category_sort'], $a['product_sort'], $a['product_id']]
|
||||
<=> [$b['category_sort'], $b['product_sort'], $b['product_id']]);
|
||||
|
||||
$items = collect($rows);
|
||||
$this->loadLookups($items);
|
||||
|
||||
if ($this->type === 'category') {
|
||||
$items = $items->filter(function ($item): bool {
|
||||
$rootName = $this->categoryNames[(int) $item->product_id] ?? '';
|
||||
$items = $items->filter(function (array $row): bool {
|
||||
$rootName = $this->categoryNames[$row['product_id']] ?? '';
|
||||
return str_contains($rootName, '蔬菜') || str_contains($rootName, '水果');
|
||||
})->values();
|
||||
}
|
||||
|
||||
return $this->items = $items;
|
||||
$sort = 1;
|
||||
return $this->items = $items->map(static function (array $row) use (&$sort): array {
|
||||
$row['sort'] = $sort++;
|
||||
return $row;
|
||||
})->values();
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return ['序号', '分类', '品名', '规格/包规', '单价', '数量', '实际称重', '金额', '供应商', '备注'];
|
||||
$this->collection();
|
||||
|
||||
return array_merge(
|
||||
['序号', '分类', '品名', '供应商', '包规', '单位', '成本', '单价', '数量', '实际称重', '金额'],
|
||||
array_map(static fn (string $name): string => $name . '(数量)', array_values($this->storeNames)),
|
||||
);
|
||||
}
|
||||
|
||||
public function map($item): array
|
||||
public function map($row): array
|
||||
{
|
||||
return [
|
||||
$item->sort,
|
||||
$this->categoryNames[(int) $item->product_id] ?? '',
|
||||
$item->product_name,
|
||||
$item->product_spec,
|
||||
(float) $item->price,
|
||||
(float) $item->quantity,
|
||||
(float) $item->weight,
|
||||
(float) $item->amount,
|
||||
$this->supplierNames[(int) $item->supplier_id] ?? '',
|
||||
$item->remark,
|
||||
];
|
||||
return array_merge([
|
||||
$row['sort'],
|
||||
$this->categoryNames[$row['product_id']] ?? '',
|
||||
$row['product_name'],
|
||||
$this->supplierNames[$row['supplier_id']] ?? '',
|
||||
$row['product_spec'],
|
||||
$row['unit'],
|
||||
$row['cost_price'],
|
||||
$row['unit_cost'],
|
||||
$row['quantity'],
|
||||
$row['weight'],
|
||||
$row['amount'],
|
||||
], array_values($row['store_quantities']));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,13 +175,14 @@ class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping,
|
||||
/**
|
||||
* PDF 模板视图数据
|
||||
*
|
||||
* @return array{purchase: PurchaseOrderModel, items: Collection, categoryNames: array<int, string>, supplierNames: array<int, string>}
|
||||
* @return array{purchase: PurchaseOrderModel, items: Collection, storeNames: array<int, string>, categoryNames: array<int, string>, supplierNames: array<int, string>}
|
||||
*/
|
||||
public function viewData(): array
|
||||
{
|
||||
return [
|
||||
'purchase' => $this->purchase,
|
||||
'items' => $this->collection(),
|
||||
'storeNames' => $this->storeNames,
|
||||
'categoryNames' => $this->categoryNames,
|
||||
'supplierNames' => $this->supplierNames,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user