批量对账
This commit is contained in:
@@ -61,7 +61,7 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出行:标题/空行/列头(品名、汇总、市场、各门店)/明细
|
||||
* 导出行:标题+备注(合并区)/空行(合并区)/列头(品名、汇总、市场、各门店)/明细
|
||||
*/
|
||||
public function collection(): Collection
|
||||
{
|
||||
@@ -74,11 +74,16 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
$rows = [];
|
||||
$rowIndex = 0;
|
||||
|
||||
// 标题行
|
||||
$rows[] = [$this->supplier->name . ' · ' . $this->marketLabel . ' · 采购单 ' . $this->purchase->purchase_no];
|
||||
// 标题行(A1:C2 合并占两行),D1 起放采购单备注(D1:I2 合并,红色 22 号字)
|
||||
$rows[] = [
|
||||
$this->supplier->name . ' · ' . $this->marketLabel . ' · 采购单 ' . $this->purchase->purchase_no,
|
||||
'',
|
||||
'',
|
||||
trim((string) $this->purchase->remark),
|
||||
];
|
||||
$this->specialRows[++$rowIndex] = 'title';
|
||||
|
||||
// 空行
|
||||
// 空行(被标题/备注合并区域覆盖)
|
||||
$rows[] = [''];
|
||||
$rowIndex++;
|
||||
|
||||
@@ -117,7 +122,7 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
}
|
||||
|
||||
/**
|
||||
* 标题/列头加粗;全表居中 + 全边框;
|
||||
* 标题合并 A1:C2、备注合并 D1:I2(红色 22 号字);标题/列头加粗;全表居中 + 全边框 + 列头行自动换行;
|
||||
* 汇总列(浅黄)/门店数量列(浅蓝)按值条件填色(0 不填、列头固定填色),冻结列头
|
||||
*/
|
||||
public function styles(Worksheet $sheet): array
|
||||
@@ -126,14 +131,18 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
|
||||
$sheet->freezePane('A' . ($this->headerRow + 1));
|
||||
|
||||
$widths = [24, 10, 12];
|
||||
// 标题合并前三列占两行(A1:C2),备注合并六列两行(D1:I2)
|
||||
$sheet->mergeCells('A1:C2');
|
||||
$sheet->mergeCells('D1:I2');
|
||||
|
||||
$widths = [24, 10, 6];
|
||||
foreach ($widths as $index => $width) {
|
||||
$sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth($width);
|
||||
}
|
||||
$storeIds = array_map('intval', array_keys($this->stores));
|
||||
$lastColumn = Coordinate::stringFromColumnIndex(3 + max(count($storeIds), 1));
|
||||
foreach ($storeIds as $i => $storeId) {
|
||||
$sheet->getColumnDimension(Coordinate::stringFromColumnIndex(4 + $i))->setWidth(12);
|
||||
$sheet->getColumnDimension(Coordinate::stringFromColumnIndex(4 + $i))->setWidth(6);
|
||||
}
|
||||
|
||||
// 全部单元格水平/垂直居中
|
||||
@@ -142,6 +151,14 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
->setHorizontal(Alignment::HORIZONTAL_CENTER)
|
||||
->setVertical(Alignment::VERTICAL_CENTER);
|
||||
|
||||
// 列头行自动换行(门店列窄,门店名换行显示)
|
||||
$sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->headerRow)
|
||||
->getAlignment()
|
||||
->setWrapText(true);
|
||||
|
||||
// 备注合并单元格自动换行(长备注多行显示)
|
||||
$sheet->getStyle('D1')->getAlignment()->setWrapText(true);
|
||||
|
||||
// 表格区域(列头 → 数据末行)添加所有边框
|
||||
$sheet->getStyle('A' . $this->headerRow . ':' . $lastColumn . $this->lastRow)
|
||||
->getBorders()
|
||||
@@ -179,6 +196,9 @@ class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison,
|
||||
}
|
||||
}
|
||||
|
||||
// 备注单元格(D1):红色 22 号字,须放在行样式之后应用以覆盖标题行字号
|
||||
$styles['D1'] = ['font' => ['bold' => true, 'size' => 22, 'color' => ['argb' => 'FFFF0000']]];
|
||||
|
||||
return $styles;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Http\Requests\Purchase\PurchaseStoreItemRequest;
|
||||
use App\Models\BillModel;
|
||||
use App\Models\CustomerLevelModel;
|
||||
use App\Models\ProductModel;
|
||||
use App\Models\PurchaseItemCheckModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\StoreOrderItemModel;
|
||||
@@ -173,6 +174,12 @@ class PurchaseOrderController extends BaseController
|
||||
return $row;
|
||||
}, $rows),
|
||||
'bills' => $bills,
|
||||
// 单品「已对账」标记(入库持久化,商品ID列表)
|
||||
'checked_product_ids' => PurchaseItemCheckModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->pluck('product_id')
|
||||
->map(static fn ($v) => (int) $v)
|
||||
->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -795,6 +802,103 @@ class PurchaseOrderController extends BaseController
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换单品「已对账」标记(入库持久化:未标记→标记,已标记→取消;与采购单状态无关,对账期间可反复勾选)
|
||||
*/
|
||||
#[PutRoute(route: '/{id}/check/{productId}', authorize: 'query', where: ['id' => '[0-9]+', 'productId' => '[0-9]+'])]
|
||||
public function toggleItemCheck(int $id, int $productId, Request $request): JsonResponse
|
||||
{
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
$hasItem = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->where('product_id', $productId)
|
||||
->exists();
|
||||
if (! $hasItem) {
|
||||
throw new RepositoryException('该采购单下无此商品的订货明细');
|
||||
}
|
||||
|
||||
$marked = PurchaseItemCheckModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->where('product_id', $productId)
|
||||
->first();
|
||||
if ($marked !== null) {
|
||||
$marked->delete();
|
||||
return $this->success(['checked' => false], '已取消对账标记');
|
||||
}
|
||||
|
||||
PurchaseItemCheckModel::create([
|
||||
'purchase_id' => $purchase->id,
|
||||
'product_id' => $productId,
|
||||
'operator_id' => (int) $request->user()->id,
|
||||
]);
|
||||
return $this->success(['checked' => true], '已标记为已对账');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置单品「已对账」标记(全选用:checked=true 批量标记 / false 批量取消;
|
||||
* 仅处理本采购单内有订货明细的商品,重复标记幂等;与采购单状态无关)
|
||||
*/
|
||||
#[PutRoute(route: '/{id}/check', authorize: 'query', where: ['id' => '[0-9]+'])]
|
||||
public function batchItemCheck(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'product_ids' => 'required|array|min:1',
|
||||
'product_ids.*' => 'integer',
|
||||
'checked' => 'required|boolean',
|
||||
], [
|
||||
'product_ids.required' => '请选择商品',
|
||||
'product_ids.min' => '请选择商品',
|
||||
]);
|
||||
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
|
||||
// 仅处理本采购单内有订货明细的商品(无效商品静默忽略)
|
||||
$productIds = StoreOrderItemModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->whereIn('product_id', array_map('intval', $data['product_ids']))
|
||||
->distinct()
|
||||
->pluck('product_id')
|
||||
->map(static fn ($v) => (int) $v);
|
||||
|
||||
if (! (bool) $data['checked']) {
|
||||
PurchaseItemCheckModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->whereIn('product_id', $productIds)
|
||||
->delete();
|
||||
return $this->success(['count' => $productIds->count()], '已取消对账标记');
|
||||
}
|
||||
|
||||
// 批量标记:跳过已标记行,仅补插缺失行(幂等)
|
||||
$marked = PurchaseItemCheckModel::query()
|
||||
->where('purchase_id', $purchase->id)
|
||||
->whereIn('product_id', $productIds)
|
||||
->pluck('product_id')
|
||||
->map(static fn ($v) => (int) $v);
|
||||
$operatorId = (int) $request->user()->id;
|
||||
$now = now();
|
||||
$rows = $productIds->diff($marked)
|
||||
->map(static fn (int $productId) => [
|
||||
'purchase_id' => $purchase->id,
|
||||
'product_id' => $productId,
|
||||
'operator_id' => $operatorId,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
if ($rows !== []) {
|
||||
PurchaseItemCheckModel::insert($rows);
|
||||
}
|
||||
|
||||
return $this->success(['count' => $productIds->count()], '已标记为已对账');
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单编辑闸:仅进行中(待采购)允许修改明细
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Modules\SystemUser\Models\SysUserModel;
|
||||
|
||||
/**
|
||||
* 采购单单品对账标记模型(商品明细「已对账」勾选,入库持久化;存在记录=已标记)
|
||||
*/
|
||||
class PurchaseItemCheckModel extends Model
|
||||
{
|
||||
protected $table = 'purchase_item_check';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'purchase_id',
|
||||
'product_id',
|
||||
'operator_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'purchase_id' => 'integer',
|
||||
'product_id' => 'integer',
|
||||
'operator_id' => 'integer',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联采购单
|
||||
*/
|
||||
public function purchase(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PurchaseOrderModel::class, 'purchase_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记人(后台系统用户)
|
||||
*/
|
||||
public function operator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SysUserModel::class, 'operator_id', 'id');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user