- 新增共享镜像表 business_internal_flow_records 与整份替换导入命令 internal-flow:import - 按 Excel 表头展示抬头、标签、预计9月销量、开票、所需进项成本、收票、剩余缺口与开票比例 - 复用公司基础信息的字段查询、表头排序、筛选字段与导出 Excel 交互 - 新增导入与页面测试,覆盖合计行跳过、重导入替换、筛选排序与未导入空状态
90 lines
2.6 KiB
PHP
90 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Modules\EcommerceFinance\Services\InternalFlowWorkbookImporter;
|
|
|
|
/**
|
|
* 内部流转报表读取共享业务镜像中的导入结果。
|
|
*
|
|
* 页面只读;数据由 InternalFlowWorkbookImporter 按整份工作簿替换导入。
|
|
*/
|
|
class InternalFlowReportRepository
|
|
{
|
|
public const TABLE = InternalFlowWorkbookImporter::TABLE;
|
|
|
|
/** 允许排序的列,防止把用户输入拼进 SQL。 */
|
|
public const SORTABLE = [
|
|
'company_name',
|
|
'tag',
|
|
'projected_sales',
|
|
'invoiced_to_buyer',
|
|
'internal_invoiced',
|
|
'required_input_cost',
|
|
'receipt_from_supplier',
|
|
'receipt_from_suofeiya',
|
|
'receipt_from_gujia',
|
|
'internal_transfer_1',
|
|
'internal_transfer_2',
|
|
'internal_transfer_3',
|
|
'receipt_total',
|
|
'remaining_gap',
|
|
'invoicing_ratio',
|
|
];
|
|
|
|
private readonly ConnectionInterface $connection;
|
|
|
|
public function __construct(GenericBusinessMirrorRepository $mirror)
|
|
{
|
|
$this->connection = $mirror->connection();
|
|
}
|
|
|
|
public function ready(): bool
|
|
{
|
|
return $this->connection->getSchemaBuilder()->hasTable(self::TABLE);
|
|
}
|
|
|
|
/**
|
|
* @param array{company_name?:string,tag?:string} $filters
|
|
* @return array{data: list<array<string,mixed>>, total: int}
|
|
*/
|
|
public function paginate(int $page, int $perPage, array $filters = [], ?string $sort = null, string $direction = 'asc'): array
|
|
{
|
|
if (! $this->ready()) {
|
|
return ['data' => [], 'total' => 0];
|
|
}
|
|
|
|
$query = $this->connection->table(self::TABLE);
|
|
|
|
$companyName = trim((string) ($filters['company_name'] ?? ''));
|
|
if ($companyName !== '') {
|
|
$query->where('company_name', 'like', '%' . addcslashes($companyName, '%_\\') . '%');
|
|
}
|
|
|
|
$tag = trim((string) ($filters['tag'] ?? ''));
|
|
if ($tag !== '') {
|
|
$query->where('tag', '=', $tag);
|
|
}
|
|
|
|
$total = (clone $query)->count();
|
|
|
|
$column = in_array($sort, self::SORTABLE, true) ? $sort : null;
|
|
$direction = strtolower($direction) === 'desc' ? 'desc' : 'asc';
|
|
|
|
if ($column !== null) {
|
|
$query->orderBy($column, $direction)->orderBy('sort_order');
|
|
} else {
|
|
$query->orderBy('sort_order')->orderBy('id');
|
|
}
|
|
|
|
$rows = $query
|
|
->forPage(max($page, 1), min(max($perPage, 1), 100))
|
|
->get()
|
|
->map(static fn (object $row): array => (array) $row)
|
|
->all();
|
|
|
|
return ['data' => $rows, 'total' => $total];
|
|
}
|
|
}
|