- PUT v1/client/update: gender theo enum ClientGender (0 Nam/1 Nữ), thêm
birthday (cột mới), province_code/ward_code validate theo bảng provinces/wards
(phường phải thuộc tỉnh). Migration nạp dữ liệu hành chính 2025 từ
province_ward_import.sql (chỉ bảng trống), API GET v1/provinces[/{code}/wards].
- CustomerObserver/OrderObserver log giá trị cũ bằng getRawOriginal: getOriginal
trả enum object, nối chuỗi gây 500 khi khách đổi giới tính.
- ClientResource: các cờ trả boolean, CCCD chỉ trả cccd_updated (không lộ ảnh/số).
- GET v1/setting/menu trả cả items + quick_links, bỏ v1/setting/quick-links.
- Thông báo admin/khách tra theo uuid thay vì id tự tăng.
- Chặn gửi template marketing cho khách đã tắt nhận email marketing.
- Xoá ProvinceWardSeeder và Customer::$updatable không dùng.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
177 lines
5.4 KiB
PHP
177 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Customer;
|
|
|
|
use App\Models\Customer;
|
|
use App\Models\Invoice;
|
|
use App\Models\Order;
|
|
use App\Models\Transaction;
|
|
use App\Repositories\Customer\CustomerRepositoryInterface;
|
|
use App\Services\Customer\Concerns\ResolvesCustomer;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class CustomerAccountService
|
|
{
|
|
use ResolvesCustomer;
|
|
|
|
public function __construct(
|
|
private readonly CustomerRepositoryInterface $customerRepository,
|
|
) {}
|
|
|
|
public function create(array $data): Customer
|
|
{
|
|
if ($this->customerRepository->findBy('email', $data['email'] ?? null)) {
|
|
abort(422, 'Email already exists on the system!');
|
|
}
|
|
|
|
return DB::transaction(function () use ($data) {
|
|
$note = $data['note'] ?? null;
|
|
unset($data['note']);
|
|
|
|
$data['password'] = Hash::make($data['password']);
|
|
$data['email_marketing'] = (bool) ($data['email_marketing'] ?? true);
|
|
|
|
$customer = $this->customerRepository->create($data);
|
|
|
|
if ($note !== null) {
|
|
$customer->update(['note' => $note]);
|
|
}
|
|
|
|
return $customer->fresh();
|
|
});
|
|
}
|
|
|
|
public function update(string $uuid, array $data): Customer
|
|
{
|
|
$customer = $this->resolveCustomer($uuid);
|
|
|
|
return DB::transaction(function () use ($customer, $data) {
|
|
$note = $data['note'] ?? null;
|
|
unset($data['note'], $data['password']);
|
|
|
|
if (!empty($data['password_raw'])) {
|
|
$data['password'] = Hash::make($data['password_raw']);
|
|
unset($data['password_raw']);
|
|
}
|
|
|
|
$this->customerRepository->update($customer->id, $data);
|
|
|
|
if ($note !== null) {
|
|
$customer->update(['note' => $note]);
|
|
}
|
|
|
|
return $customer->fresh();
|
|
});
|
|
}
|
|
|
|
public function destroy(string $uuid): void
|
|
{
|
|
$customer = $this->resolveCustomer($uuid);
|
|
|
|
DB::transaction(function () use ($customer) {
|
|
$this->deleteRelatedRecords($customer->id);
|
|
$customer->delete();
|
|
});
|
|
}
|
|
|
|
public function restore(string $uuid): void
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid)
|
|
?? $this->customerRepository->onlyTrashed()->where('uuid', $uuid)->first();
|
|
|
|
abort_if(!$customer, 404, "Can't find customer by UUID #{$uuid}");
|
|
|
|
DB::transaction(function () use ($customer) {
|
|
$customer->restore();
|
|
$this->restoreRelatedRecords($customer->id);
|
|
});
|
|
}
|
|
|
|
public function forceDelete(string $uuid): void
|
|
{
|
|
$customer = $this->customerRepository->onlyTrashed()->where('uuid', $uuid)->first();
|
|
|
|
abort_if(!$customer, 404, "Can't find customer by UUID #{$uuid}");
|
|
|
|
DB::transaction(function () use ($customer) {
|
|
$this->forceDeleteRelatedRecords($customer->id);
|
|
$customer->forceDelete();
|
|
});
|
|
}
|
|
|
|
public function updateStatus(string $uuid, int $status): void
|
|
{
|
|
$customer = $this->resolveCustomer($uuid);
|
|
$customer->update(['status' => $status]);
|
|
}
|
|
|
|
public function bulkDelete(array $uuids): int
|
|
{
|
|
$customers = Customer::whereIn('uuid', $uuids)->get();
|
|
|
|
DB::transaction(function () use ($customers) {
|
|
foreach ($customers as $customer) {
|
|
$this->deleteRelatedRecords($customer->id);
|
|
$customer->delete();
|
|
}
|
|
});
|
|
|
|
return $customers->count();
|
|
}
|
|
|
|
public function bulkRestore(array $uuids): int
|
|
{
|
|
$customers = Customer::onlyTrashed()->whereIn('uuid', $uuids)->get();
|
|
|
|
DB::transaction(function () use ($customers) {
|
|
foreach ($customers as $customer) {
|
|
$customer->restore();
|
|
$this->restoreRelatedRecords($customer->id);
|
|
}
|
|
});
|
|
|
|
return $customers->count();
|
|
}
|
|
|
|
public function bulkForceDelete(array $uuids): int
|
|
{
|
|
$customers = Customer::onlyTrashed()->whereIn('uuid', $uuids)->get();
|
|
|
|
DB::transaction(function () use ($customers) {
|
|
foreach ($customers as $customer) {
|
|
$this->forceDeleteRelatedRecords($customer->id);
|
|
$customer->forceDelete();
|
|
}
|
|
});
|
|
|
|
return $customers->count();
|
|
}
|
|
|
|
public function bulkStatus(array $uuids, int $status): int
|
|
{
|
|
return Customer::whereIn('uuid', $uuids)->update(['status' => $status]);
|
|
}
|
|
|
|
private function deleteRelatedRecords(int $customerId): void
|
|
{
|
|
Invoice::where('customer_id', $customerId)->delete();
|
|
Order::where('customer_id', $customerId)->delete();
|
|
Transaction::where('customer_id', $customerId)->delete();
|
|
}
|
|
|
|
private function restoreRelatedRecords(int $customerId): void
|
|
{
|
|
Invoice::withTrashed()->where('customer_id', $customerId)->restore();
|
|
Order::withTrashed()->where('customer_id', $customerId)->restore();
|
|
Transaction::withTrashed()->where('customer_id', $customerId)->restore();
|
|
}
|
|
|
|
private function forceDeleteRelatedRecords(int $customerId): void
|
|
{
|
|
Invoice::withTrashed()->where('customer_id', $customerId)->forceDelete();
|
|
Order::withTrashed()->where('customer_id', $customerId)->forceDelete();
|
|
Transaction::withTrashed()->where('customer_id', $customerId)->forceDelete();
|
|
}
|
|
}
|