feat: create PostCategoryFactory for generating post category data feat: create PostFactory for generating post data feat: add retry_requested_at column to services table feat: add vcenter provisioning fields to products table feat: add module_host_id to os table feat: create email_notification_templates table for email templates feat: add scenario and format fields to template table feat: add admin fields to transactions table fix: update ticket table columns and types feat: add uuid to ticket table and backfill existing records fix: make ticket_reply user_id nullable feat: create notifications table for user notifications fix: make ticket uuid nullable feat: create post_categories table for blog categories feat: create posts table for blog posts feat: add invoice email template for PDF generation
600 lines
19 KiB
PHP
600 lines
19 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Admin\Customer;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\CreateCustomerRequest;
|
|
use App\Http\Resources\Api\Admin\CustomerResource;
|
|
use App\Jobs\Mail\PasswordRecoverJob;
|
|
use App\Models\Customer;
|
|
use App\Models\Invoice;
|
|
use App\Models\Order;
|
|
use App\Models\Transaction;
|
|
use App\Models\Log;
|
|
use App\Repositories\Customer\CustomerRepositoryInterface;
|
|
use App\Repositories\Invoice\InvoiceRepositoryInterface;
|
|
use App\Repositories\InvoiceItem\InvoiceItemRepositoryInterface;
|
|
use App\Repositories\Module\Domain\DomainRepositoryInterface;
|
|
use App\Repositories\Module\Hosting\HostingRepositoryInterface as ModuleHostingRepositoryInterface;
|
|
use App\Repositories\Module\Vmware\VmwareRepositoryInterface;
|
|
use App\Repositories\Order\OrderRepositoryInterface;
|
|
use App\Repositories\Service\ServiceRepositoryInterface;
|
|
use App\Repositories\Transaction\TransactionRepositoryInterface;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Str;
|
|
|
|
class CustomerController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly CustomerRepositoryInterface $customerRepository,
|
|
private readonly InvoiceRepositoryInterface $invoiceRepository,
|
|
private readonly InvoiceItemRepositoryInterface $invoiceItemRepository,
|
|
private readonly ModuleHostingRepositoryInterface $moduleHostingRepository,
|
|
private readonly VmwareRepositoryInterface $vmwareRepository,
|
|
private readonly OrderRepositoryInterface $orderRepository,
|
|
private readonly TransactionRepositoryInterface $transactionRepository,
|
|
private readonly DomainRepositoryInterface $moduleDomainRepository,
|
|
private readonly ServiceRepositoryInterface $serviceRepository,
|
|
) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$perPage = min((int) $request->get('per_page', 15), 500);
|
|
$query = $this->customerRepository->filter(
|
|
$request->all(),
|
|
[],
|
|
['hosting', 'vps', 'domain'],
|
|
['*'],
|
|
true
|
|
);
|
|
$paginator = $query->paginate($perPage);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => [
|
|
'items' => CustomerResource::collection($paginator),
|
|
'total' => $paginator->total(),
|
|
'per_page' => $paginator->perPage(),
|
|
'current_page' => $paginator->currentPage(),
|
|
'last_page' => $paginator->lastPage(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function store(CreateCustomerRequest $request): JsonResponse
|
|
{
|
|
$request->validated();
|
|
|
|
$existingCustomer = $this->customerRepository->findBy('email', $request->get('email'));
|
|
if ($existingCustomer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Email already exists on the system!'
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
$data = $request->all();
|
|
$data['password'] = Hash::make($request->password);
|
|
$data['email_marketing'] = $request->email_marketing === "on" ? 1 : 0;
|
|
|
|
$customer = $this->customerRepository->create($data);
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Customer created successfully',
|
|
'data' => new CustomerResource($customer)
|
|
], 201);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function show(string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$customer = $this->customerRepository->findWithRelationships(
|
|
$customer->id,
|
|
['mail', 'file'],
|
|
[
|
|
'invoice' => [
|
|
'invoices_status_count_1' => ['key' => 'status', 'condition' => '=', 'value' => 1],
|
|
'invoices_status_count_0' => ['key' => 'status', 'condition' => '=', 'value' => 0]
|
|
],
|
|
'hosting' => 'hosting_count',
|
|
'vps' => 'vps_count',
|
|
'domain' => 'domain_count',
|
|
'ticket' => 'ticket_count'
|
|
],
|
|
[
|
|
'invoice' => [
|
|
'invoices_status_1' => ['key' => 'status', 'condition' => '=', 'value' => 1, 'sum' => 'total'],
|
|
'invoices_status_0' => ['key' => 'status', 'condition' => '=', 'value' => 0, 'sum' => 'total']
|
|
]
|
|
]
|
|
);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => new CustomerResource($customer)
|
|
]);
|
|
}
|
|
|
|
public function update(string $uuid, CreateCustomerRequest $request): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$request->validated();
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
$data = $request->except(['_token', 'password']);
|
|
|
|
if (!empty($request->password)) {
|
|
$data['password'] = Hash::make($request->password);
|
|
}
|
|
|
|
$this->customerRepository->update($customer->id, $data);
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Customer updated successfully',
|
|
'data' => new CustomerResource($this->customerRepository->find($customer->id))
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function destroy(string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
Invoice::where('customer_id', $customer->id)->delete();
|
|
Order::where('customer_id', $customer->id)->delete();
|
|
Transaction::where('customer_id', $customer->id)->delete();
|
|
$customer->delete();
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Customer deleted successfully'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function trash(Request $request): JsonResponse
|
|
{
|
|
$query = $this->customerRepository->onlyTrashed([], ['hosting', 'vps', 'domain']);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => CustomerResource::collection($query->get())
|
|
]);
|
|
}
|
|
|
|
public function restore(string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
if (!$customer) {
|
|
$customer = $this->customerRepository->onlyTrashed()->where('uuid', $uuid)->first();
|
|
}
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
$customer->restore();
|
|
Invoice::withTrashed()->where('customer_id', $customer->id)->restore();
|
|
Order::withTrashed()->where('customer_id', $customer->id)->restore();
|
|
Transaction::withTrashed()->where('customer_id', $customer->id)->restore();
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Customer restored successfully'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function forceDelete(string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->onlyTrashed()->where('uuid', $uuid)->first();
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
Invoice::withTrashed()->where('customer_id', $customer->id)->forceDelete();
|
|
Order::withTrashed()->where('customer_id', $customer->id)->forceDelete();
|
|
Transaction::withTrashed()->where('customer_id', $customer->id)->forceDelete();
|
|
$customer->forceDelete();
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Customer permanently deleted'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function resetPassword(Request $request, string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$password = Str::random(10);
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
$this->customerRepository->update($customer->id, ['password' => Hash::make($password)]);
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Password reset successfully',
|
|
'data' => ['new_password' => $password]
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function addCredit(Request $request, string $uuid): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'amount' => 'required|numeric|min:1',
|
|
'action' => 'required|in:+,-'
|
|
]);
|
|
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$amount = (int) $request->amount;
|
|
|
|
if ($request->action === "+") {
|
|
$this->customerRepository->update($customer->id, [
|
|
'credit' => (int) $customer->credit + $amount,
|
|
'total' => (int) $customer->total + $amount,
|
|
]);
|
|
} else {
|
|
$this->customerRepository->update($customer->id, [
|
|
'credit' => (int) $customer->credit - $amount,
|
|
'total' => (int) $customer->total - $amount,
|
|
]);
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Credit updated successfully'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function addTotal(Request $request, string $uuid): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'amount' => 'required|numeric|min:1',
|
|
'note' => 'required|string|max:1000',
|
|
]);
|
|
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid,
|
|
], 404);
|
|
}
|
|
|
|
$admin = Auth::guard('admin')->user();
|
|
if (!$admin) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Unauthorized',
|
|
], 401);
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$amount = (int) $request->amount;
|
|
|
|
$transaction = Transaction::create([
|
|
'uuid' => (string) Str::uuid(),
|
|
'trans' => 'ADMIN' . now()->format('YmdHis'),
|
|
'amount' => $amount,
|
|
'customer_id' => $customer->id,
|
|
'user_id' => $admin->id,
|
|
'status' => 1,
|
|
'type' => 'admin_add_total',
|
|
'note' => $request->string('note')->trim()->toString(),
|
|
]);
|
|
|
|
$customer->update([
|
|
'total' => (int) $customer->total + $amount,
|
|
]);
|
|
|
|
Log::create([
|
|
'agent' => 'admin',
|
|
'client_ip' => $request->ip(),
|
|
'description' => sprintf(
|
|
'%s cộng tiền trực tiếp cho customer %s: %s VNĐ - %s',
|
|
$admin->username ?? $admin->email,
|
|
$customer->email,
|
|
number_format($amount),
|
|
$transaction->note,
|
|
),
|
|
'customer_id' => $customer->id,
|
|
]);
|
|
|
|
DB::commit();
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Total updated successfully',
|
|
'data' => [
|
|
'transaction' => [
|
|
'uuid' => $transaction->uuid,
|
|
'amount' => $transaction->amount,
|
|
'note' => $transaction->note,
|
|
'admin' => $admin->username ?? $admin->email,
|
|
],
|
|
],
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function getProducts(string $uuid): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$hostingServices = $this->moduleHostingRepository->filter(['customer_id' => $customer->id], ['server']);
|
|
$vpsServices = $this->vmwareRepository->filter(
|
|
['customer_id' => $customer->id],
|
|
['ipaddress.host.datacenter.vCenter', 'os.osGroup']
|
|
);
|
|
|
|
$allService = collect();
|
|
|
|
foreach ($hostingServices as $hosting) {
|
|
$allService->push([
|
|
'product_id' => $hosting->id,
|
|
'product_type' => 3,
|
|
'name' => $hosting->name,
|
|
'description' => $hosting->name . ' (Hosting)'
|
|
]);
|
|
}
|
|
|
|
foreach ($vpsServices as $vps) {
|
|
$allService->push([
|
|
'product_id' => $vps->id,
|
|
'product_type' => 4,
|
|
'name' => $vps->name,
|
|
'description' => $vps->name . ' (VPS)'
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $allService->values()
|
|
]);
|
|
}
|
|
|
|
public function getInvoices(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$invoices = $this->invoiceRepository->filter(
|
|
['customer_id' => $customer->id],
|
|
['payment']
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $invoices
|
|
]);
|
|
}
|
|
|
|
public function getTransactions(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$transactions = $this->transactionRepository->filter(
|
|
['customer_id' => $customer->id],
|
|
['user']
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $transactions
|
|
]);
|
|
}
|
|
|
|
public function getDomains(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$domains = $this->moduleDomainRepository->filter(
|
|
['customer_id' => $customer->id],
|
|
['customer']
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $domains
|
|
]);
|
|
}
|
|
|
|
public function getServices(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$services = $this->serviceRepository->filter(
|
|
['customer_uuid' => $uuid],
|
|
['product', 'orderItem', 'invoice']
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $services
|
|
]);
|
|
}
|
|
|
|
public function getOrders(string $uuid, Request $request): JsonResponse
|
|
{
|
|
$customer = $this->customerRepository->findBy('uuid', $uuid);
|
|
|
|
if (!$customer) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Can't find customer by UUID #" . $uuid
|
|
], 404);
|
|
}
|
|
|
|
$orders = $this->orderRepository->filter(
|
|
['client' => $customer->id],
|
|
['customer', 'payment', 'invoice']
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => $orders
|
|
]);
|
|
}
|
|
}
|