接入公司非店铺支付宝账单、受限批处理导入,并为账户总表及每日资金流水增加管理员手动编辑与审计。 Co-authored-by: 92451 <924518135@qq.com> Co-committed-by: 92451 <924518135@qq.com>
206 lines
9.0 KiB
PHP
206 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Services;
|
|
|
|
use App\Utilities\FinanceLedgerAccountStatus;
|
|
use App\Utilities\FinanceLedgerAccountType;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\DatabaseManager;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class FinanceLedgerAccountEditor
|
|
{
|
|
private readonly ConnectionInterface $connection;
|
|
|
|
public function __construct(
|
|
DatabaseManager $databases,
|
|
private readonly FinanceLedgerCalculator $calculator,
|
|
) {
|
|
$this->connection = $databases->connection(
|
|
(string) config('upstream_mirrors.connection', 'business_mirror'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $data
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function update(int $id, array $data): array
|
|
{
|
|
return $this->connection->transaction(function () use ($id, $data): array {
|
|
$account = $this->connection->table('fin_accounts')->where('id', $id)->lockForUpdate()->first();
|
|
if ($account === null) {
|
|
abort(404);
|
|
}
|
|
|
|
$oldShort = trim((string) $account->account_short);
|
|
$accountShort = $this->text($data['account_short'] ?? '');
|
|
$companyName = $this->text($data['company_name'] ?? '');
|
|
if ($accountShort === '' || $companyName === '') {
|
|
throw ValidationException::withMessages([
|
|
'account_short' => '账户简称和公司名称不能为空。',
|
|
]);
|
|
}
|
|
|
|
$duplicate = $this->connection->table('fin_accounts')
|
|
->where('account_short', $accountShort)
|
|
->where('id', '<>', $id)
|
|
->exists();
|
|
if ($duplicate) {
|
|
throw ValidationException::withMessages([
|
|
'account_short' => '账户简称已存在,请改用其他简称。',
|
|
]);
|
|
}
|
|
|
|
$accountType = $this->nullableText($data['account_type'] ?? null);
|
|
$accountNo = $this->nullableText($data['account_no'] ?? null);
|
|
$status = FinanceLedgerAccountStatus::normalize((string) ($data['status'] ?? ''));
|
|
$accountLast4 = FinanceLedgerAccountType::last4(
|
|
(string) $accountType,
|
|
(string) $accountNo,
|
|
(string) ($account->account_last4 ?? ''),
|
|
);
|
|
$values = [
|
|
'company_name' => $companyName,
|
|
'account_short' => $accountShort,
|
|
'region' => $this->nullableText($data['region'] ?? null),
|
|
'account_no' => $accountNo,
|
|
'bank_name' => $this->nullableText($data['bank_name'] ?? null),
|
|
'bank_city_branch' => $this->nullableText($data['bank_city_branch'] ?? null),
|
|
'account_type' => $accountType,
|
|
'company_plan' => $this->nullableText($data['company_plan'] ?? null),
|
|
'account_should_keep' => $this->nullableText($data['account_should_keep'] ?? null),
|
|
'account_last4' => $accountLast4,
|
|
'single_limit' => $this->nullableText($data['single_limit'] ?? null),
|
|
'daily_limit' => $this->nullableText($data['daily_limit'] ?? null),
|
|
'monthly_limit' => $this->nullableText($data['monthly_limit'] ?? null),
|
|
'yearly_limit' => $this->nullableText($data['yearly_limit'] ?? null),
|
|
'clearing_days' => $this->nullableNumber($data['clearing_days'] ?? null),
|
|
'raise_target' => $this->nullableText($data['raise_target'] ?? null),
|
|
'raise_status' => $this->nullableText($data['raise_status'] ?? null),
|
|
'raise_actions' => $this->nullableText($data['raise_actions'] ?? null),
|
|
'done_actions' => $this->nullableText($data['done_actions'] ?? null),
|
|
'next_action' => $this->nullableText($data['next_action'] ?? null),
|
|
'remark' => $this->nullableText($data['remark'] ?? null),
|
|
'owner' => $this->nullableText($data['owner'] ?? null),
|
|
'updated_at' => now(),
|
|
];
|
|
|
|
$before = (array) $account;
|
|
if ($oldShort !== $accountShort) {
|
|
$this->renameReferences($oldShort, $accountShort);
|
|
}
|
|
|
|
$this->connection->table('fin_accounts')->where('id', $id)->update($values);
|
|
$this->upsertStatus($accountShort, $status);
|
|
$this->connection->table('fin_account_type_overrides')
|
|
->where('account_short', $accountShort)
|
|
->delete();
|
|
$this->connection->table('fin_daily_flows')
|
|
->where('account_short', $accountShort)
|
|
->update([
|
|
'company_name' => $companyName,
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$this->calculator->refreshAccountAmounts($accountShort);
|
|
|
|
$after = (array) $this->connection->table('fin_accounts')->where('id', $id)->first();
|
|
$this->connection->table('fin_ledger_edit_events')->insert([
|
|
'entity_type' => 'account',
|
|
'entity_key' => (string) $id,
|
|
'changes' => json_encode([
|
|
'before' => $before,
|
|
'after' => $after,
|
|
], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
|
|
'user_id' => user()?->id,
|
|
'user_name' => (string) (user()?->name ?? ''),
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
return $after;
|
|
});
|
|
}
|
|
|
|
private function upsertStatus(string $accountShort, string $status): void
|
|
{
|
|
$values = [
|
|
'status' => $status,
|
|
'updated_by' => (string) (user()?->name ?? ''),
|
|
'updated_at' => now(),
|
|
];
|
|
if ($this->connection->table('fin_account_statuses')->where('account_short', $accountShort)->exists()) {
|
|
$this->connection->table('fin_account_statuses')->where('account_short', $accountShort)->update($values);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->connection->table('fin_account_statuses')->insert($values + [
|
|
'account_short' => $accountShort,
|
|
'created_at' => now(),
|
|
]);
|
|
}
|
|
|
|
private function renameReferences(string $oldShort, string $newShort): void
|
|
{
|
|
$now = now();
|
|
foreach ([
|
|
'fin_account_opening' => ['account_short' => [], 'updated_at' => $now],
|
|
'fin_daily_flows' => ['account_short' => [], 'updated_at' => $now],
|
|
'fin_transactions' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_account_map' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_account_statuses' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_account_type_overrides' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_account_todos' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_account_keep_todos' => ['account_short' => [], 'updated_by' => (string) (user()?->name ?? ''), 'updated_at' => $now],
|
|
'fin_shops' => ['account_short' => [], 'updated_at' => $now],
|
|
] as $table => $extra) {
|
|
if (! Schema::connection($this->connection->getName())->hasTable($table)
|
|
|| ! Schema::connection($this->connection->getName())->hasColumn($table, 'account_short')) {
|
|
continue;
|
|
}
|
|
$this->connection->table($table)->where('account_short', $oldShort)->update([
|
|
'account_short' => $newShort,
|
|
...$extra,
|
|
]);
|
|
}
|
|
|
|
if (Schema::connection($this->connection->getName())->hasTable('fin_daily_flow_overrides')) {
|
|
$this->connection->table('fin_daily_flow_overrides')
|
|
->where('account_short', $oldShort)
|
|
->update([
|
|
'account_short' => $newShort,
|
|
'updated_at' => $now,
|
|
]);
|
|
}
|
|
$this->connection->table('fin_account_map_events')
|
|
->where('old_account_short', $oldShort)
|
|
->update(['old_account_short' => $newShort]);
|
|
$this->connection->table('fin_account_map_events')
|
|
->where('new_account_short', $oldShort)
|
|
->update(['new_account_short' => $newShort]);
|
|
}
|
|
|
|
private function text(mixed $value): string
|
|
{
|
|
return preg_replace('/\s+/u', ' ', trim((string) $value)) ?? trim((string) $value);
|
|
}
|
|
|
|
private function nullableText(mixed $value): ?string
|
|
{
|
|
$text = $this->text($value);
|
|
|
|
return $text !== '' ? $text : null;
|
|
}
|
|
|
|
private function nullableNumber(mixed $value): ?float
|
|
{
|
|
if ($value === null || trim((string) $value) === '') {
|
|
return null;
|
|
}
|
|
|
|
return round((float) $value, 2);
|
|
}
|
|
}
|