Files
portal-api/app/Services/Customer/CustomerBillingService.php

170 lines
6.8 KiB
PHP

<?php
namespace App\Services\Customer;
use App\Models\Invoice;
use App\Http\Resources\Api\Admin\OrderItemServiceResource;
use App\Models\Order;
use App\Enums\TransactionStatus;
use App\Models\Transaction;
use App\Repositories\Customer\CustomerRepositoryInterface;
use App\Repositories\Invoice\InvoiceRepositoryInterface;
use App\Repositories\Order\OrderRepositoryInterface;
use App\Repositories\Transaction\TransactionRepositoryInterface;
use App\Services\Customer\Concerns\ResolvesCustomer;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
class CustomerBillingService
{
use ResolvesCustomer;
public function __construct(
private readonly CustomerRepositoryInterface $customerRepository,
private readonly InvoiceRepositoryInterface $invoiceRepository,
private readonly TransactionRepositoryInterface $transactionRepository,
private readonly OrderRepositoryInterface $orderRepository,
) {}
public function getInvoices(string $uuid): Collection
{
$customer = $this->resolveCustomer($uuid);
$invoices = $this->invoiceRepository->filter(['customer_id' => $customer->id], ['payment']);
// Frontend đọc ngày thanh toán qua `date_paid` — alias từ cột paid_at.
$invoices->each(fn($invoice) => $invoice->date_paid = $invoice->paid_at);
return $invoices;
}
/**
* Sửa hóa đơn của khách hàng, kể cả số tiền (sub_total/tax/total). Không
* tự đồng bộ lại invoice_items hay customer.total.
*/
public function updateInvoice(string $uuid, int $invoiceId, array $data): Invoice
{
$customer = $this->resolveCustomer($uuid);
$invoice = Invoice::where('customer_id', $customer->id)->where('id', $invoiceId)->first();
abort_if(!$invoice, 404, 'Không tìm thấy hóa đơn');
// `date_paid` là tên field phía frontend — cột thật trong DB là paid_at.
if (array_key_exists('date_paid', $data)) {
$data['paid_at'] = $data['date_paid'];
unset($data['date_paid']);
}
if (array_key_exists('amount_paid', $data) && $data['amount_paid'] === null) {
unset($data['amount_paid']);
}
if (isset($data['amount_paid']) && $data['amount_paid'] > (int) ($data['total'] ?? $invoice->total)) {
abort(422, 'Số tiền đã thu không được lớn hơn tổng hóa đơn');
}
// Đi qua repository (không gọi $invoice->update() trực tiếp) để cache
// của invoiceRepository->filter() (dùng ở getInvoices) được xoá theo,
// nếu không danh sách hóa đơn sẽ hiển thị dữ liệu cũ cho tới khi cache hết hạn.
$this->invoiceRepository->update($invoiceId, $data);
$invoice = $invoice->refresh();
$invoice->date_paid = $invoice->paid_at;
return $invoice->load('payment');
}
public function getTransactions(string $uuid): Collection
{
$customer = $this->resolveCustomer($uuid);
return $this->transactionRepository->filter(['customer_id' => $customer->id], ['user']);
}
/**
* Sửa thông tin giao dịch, kể cả amount (để sửa số tiền nhập/hiển thị sai).
* Không tự động cộng/trừ lại customer.total khi sửa amount — chiều +/- của
* amount phụ thuộc vào type (balance trừ tiền, transfer/admin_add_total
* cộng tiền,...) và không có field nào lưu lại chiều đó, nên tự suy luận
* dễ làm sai số dư hơn là sửa đúng. Nếu cần đồng bộ lại số dư, dùng
* "Cộng tiền" ở tab Tổng quan sau khi sửa xong.
*/
public function updateTransaction(string $uuid, int $transactionId, array $data): Transaction
{
$customer = $this->resolveCustomer($uuid);
$transaction = Transaction::where('customer_id', $customer->id)->where('id', $transactionId)->first();
abort_if(!$transaction, 404, 'Không tìm thấy giao dịch');
// Đi qua repository để cache của transactionRepository->filter()
// (dùng ở getTransactions) được xoá theo — xem ghi chú ở updateInvoice.
$this->transactionRepository->update($transactionId, $data);
return $transaction->refresh()->load('user');
}
public function createTransaction(string $uuid, array $data, ?int $adminId): Transaction
{
$customer = $this->resolveCustomer($uuid);
$transaction = $this->transactionRepository->create([
'uuid' => (string) Str::uuid(),
'public_code' => Transaction::newPublicCode('ADM'),
'trans' => $data['trans'] ?? null,
'amount' => $data['amount'],
'received_amount' => $data['amount'],
'customer_id' => $customer->id,
'user_id' => $adminId,
'status' => $data['status'] ?? TransactionStatus::COMPLETED->value,
'type' => 'admin_record',
'note' => $data['note'] ?? null,
]);
return $transaction->load('user');
}
public function deleteTransaction(string $uuid, int $transactionId): void
{
$customer = $this->resolveCustomer($uuid);
$exists = Transaction::where('customer_id', $customer->id)->where('id', $transactionId)->exists();
abort_if(!$exists, 404, 'Không tìm thấy giao dịch');
$this->transactionRepository->delete($transactionId);
}
public function getOrders(string $uuid): Collection
{
$customer = $this->resolveCustomer($uuid);
$orders = $this->orderRepository->filter(['client' => $customer->id], [
'customer',
'user:id,username',
'payment',
'invoices',
...OrderItemServiceResource::relationsFor('orderItems'),
]);
$orders->each(fn ($order) => $order->invoices->each(fn ($invoice) => $invoice->date_paid = $invoice->paid_at));
return $orders;
}
/**
* Sửa đơn hàng của khách hàng, kể cả số tiền (sub_total/discount_amount/
* total). Không tự đồng bộ lại invoice/invoice_item hay customer.total —
* nếu số tiền hóa đơn liên kết cũng cần sửa, sửa riêng ở updateInvoice.
*/
public function updateOrder(string $uuid, int $orderId, array $data): Order
{
$customer = $this->resolveCustomer($uuid);
$order = Order::where('customer_id', $customer->id)->where('id', $orderId)->first();
abort_if(!$order, 404, 'Không tìm thấy đơn hàng');
// Đi qua repository để cache của orderRepository->filter()
// (dùng ở getOrders) được xoá theo — xem ghi chú ở updateInvoice.
$this->orderRepository->update($orderId, $data);
return $order->refresh()->load(['payment', 'invoices']);
}
}