CaiWuXiTong/modules/EcommerceFinance/Services/SupplierPayableOverrideStore.php
renshen 813e956df5 应付款终版目录接入独立按表数据
两个「终版」目录(供应商应付(终版)、快递应付(终版))改为独立数据:
保留原有导入、导出、查询、重置、编辑按钮和导入模板,数据初始为空、按表独立存放,
不再借用基础目录的页面与数据。

- 终版目录拥有自己的同名子页面与横向页签
- 新增终版按表数据的独立导入/导出/模板/编辑接口(数据集键 final:<目录>:<页面>)
- 供应商终版 13 个页面、快递终版 4 个页面已接入
- 终版导入模板统一增加“月份”列,记录标识列改为选填
2026-09-29 11:06:41 +08:00

129 lines
4.6 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use Closure;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\DB;
class SupplierPayableOverrideStore
{
public function key(string $dataset, array $row): string
{
if (isset($row['sourceId']) && $row['sourceId'] !== '') {
return 'id:' . (string) ($row['recordType'] ?? '') . ':' . (string) $row['sourceId'];
}
if (isset($row['id']) && $row['id'] !== '') {
return 'id:' . (string) $row['id'];
}
$immutable = $row;
unset($immutable['_action_key'], $immutable['_overridden']);
return 'hash:' . hash('sha256', json_encode($immutable, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
/** @param array<int,array<string,mixed>> $rows */
public function decorate(string $dataset, array $rows, int $companyId): array
{
if ($rows === []) {
return [];
}
$keys = [];
foreach ($rows as $row) {
$keys[] = $this->key($dataset, $row);
}
$overrides = $this->connection()->table('supplier_payable_overrides')
->where('company_id', $companyId)
->where('dataset', $dataset)
->whereIn('record_key', $keys)
->get()
->keyBy('record_key');
foreach ($rows as $index => &$row) {
$key = $keys[$index];
$row['_action_key'] = $key;
$override = $overrides->get($key);
if ($override) {
$values = json_decode((string) $override->values, true) ?: [];
$row = array_merge($row, $values);
$row['_overridden'] = true;
} else {
$row['_overridden'] = false;
}
}
unset($row);
return $rows;
}
/** @return array{created:int,updated:int} */
public function save(string $dataset, string $recordKey, array $values, int $companyId, ?int $userId, string $source = 'manual'): array
{
$connection = $this->connection();
$existing = $connection->table('supplier_payable_overrides')
->where(['company_id' => $companyId, 'dataset' => $dataset, 'record_key' => $recordKey])
->first();
$before = $existing ? (json_decode((string) $existing->values, true) ?: []) : null;
$now = now();
$connection->transaction(function () use ($connection, $existing, $dataset, $recordKey, $values, $companyId, $userId, $now, $source, $before): void {
$connection->table('supplier_payable_overrides')->updateOrInsert(
['company_id' => $companyId, 'dataset' => $dataset, 'record_key' => $recordKey],
[
'values' => json_encode($values, JSON_UNESCAPED_UNICODE),
'updated_by' => $userId,
'created_by' => $existing->created_by ?? $userId,
'created_at' => $existing->created_at ?? $now,
'updated_at' => $now,
],
);
$connection->table('supplier_payable_override_events')->insert([
'company_id' => $companyId,
'dataset' => $dataset,
'record_key' => $recordKey,
'source' => $source,
'before_values' => $before === null ? null : json_encode($before, JSON_UNESCAPED_UNICODE),
'after_values' => json_encode($values, JSON_UNESCAPED_UNICODE),
'user_id' => $userId,
'created_at' => $now,
]);
});
return ['created' => $existing ? 0 : 1, 'updated' => $existing ? 1 : 0];
}
public function transaction(Closure $callback): mixed
{
return $this->connection()->transaction($callback);
}
/**
* 列出某个数据集已经保存的全部记录(「终版」目录的独立数据就是这些行)。
*
* @return array<int,array<string,mixed>>
*/
public function listRows(string $dataset, int $companyId): array
{
return $this->connection()->table('supplier_payable_overrides')
->where('company_id', $companyId)
->where('dataset', $dataset)
->orderBy('id')
->get()
->map(static function (object $record): array {
$values = json_decode((string) $record->values, true) ?: [];
$values['_action_key'] = (string) $record->record_key;
return $values;
})
->all();
}
private function connection(): ConnectionInterface
{
return DB::connection(config('database.supplier_payable_maintenance_connection'));
}
}