feat: Reimplement order and invoice management with new service and repository layers, introduce compression middleware, and update database schema.

This commit is contained in:
pbao9
2026-03-12 09:30:29 +07:00
parent 7f6264c87f
commit 1e09ccf17b
87 changed files with 2348 additions and 2612 deletions
@@ -2,78 +2,83 @@
namespace App\Repositories\Api\Invoice;
use App\Enums\Invoice\InvoiceStatus;
use App\Helpers\OrderHelper;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Order;
use App\Repositories\Api\BaseApiRepository;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
class InvoiceRepository extends BaseApiRepository implements InvoiceRepositoryInterface
{
public function getModel()
public function getModel(): string
{
return Invoice::class;
}
/**
* Lấy danh sách sản phẩm đang hoạt động
*/
public function getInvoice(int $limit = 10, $customer = null)
// -------------------------------------------------------------------------
// getInvoice
// -------------------------------------------------------------------------
public function getInvoice(int $limit = 10, int $customerId = 0): LengthAwarePaginator
{
return $this->model->where('customer_id', $customer)->paginate($limit);
return $this->model
->where('customer_id', $customerId)
->orderByDesc('created_at')
->paginate($limit);
}
public function createFromOrder($order)
// -------------------------------------------------------------------------
// createFromOrder
// -------------------------------------------------------------------------
public function createFromOrder(Order $order): Invoice
{
$invoice = $this->model->create([
'customer_id' => $order->customer_id,
'order_id' => $order->id,
'order_num' => \App\Helpers\OrderHelper::generateOrderNumber('INV'),
'date' => now(),
'due_date' => now()->addDays(3),
'total' => $order->amount,
'sub_total' => $order->amount,
'tax' => 0,
'status' => 1, // Unpaid
'invoice_number' => OrderHelper::generateOrderNumber('INV'),
'customer_id' => $order->customer_id,
'order_id' => $order->id,
'invoice_type' => 1, // new service
'sub_total' => $order->total,
'discount_amount' => $order->discount_amount ?? 0,
'tax' => 0,
'total' => $order->total,
'status' => InvoiceStatus::UNPAID->value,
'due_date' => now()->addDays(3)->toDateString(),
]);
$order->update(['invoice_id' => $invoice->id]);
$orderItems = $order->orderItems;
if ($orderItems->isEmpty()) {
$orderItems = $order->orderItems()->get();
}
foreach ($orderItems as $item) {
$invoiceItem = \App\Models\InvoiceItem::create([
'invoice_id' => $invoice->id,
// 'customer_id' removed as not in migration
'product_id' => $item->product_id,
// 'description' removed as not in migration
'price' => $item->price ?? $order->amount,
// 'type_id' removed, use 'product_type'
'product_type' => $item->product->product_type instanceof \App\Enums\Product\ProductTypeEnums
? $item->product->product_type->value
: (\App\Enums\Product\ProductTypeEnums::tryFrom($item->product->product_type)?->value ?? 1),
// 'billing' removed, map to 'months' if possible? default 0 in migration
'months' => match ($item->billing_cycle) {
'quarterly' => 3,
'semi_annually' => 6,
'annually' => 12,
default => 1,
},
'server_name' => $item->server_name,
'os_id' => $item->os_id,
// Create one invoice_item per order_item line
foreach ($order->orderItems as $item) {
InvoiceItem::create([
'invoice_id' => $invoice->id,
'product_id' => $item->product_id,
'type' => 'service',
'description' => $item->product?->name ?? 'Dịch vụ',
'months' => $this->cycleToMonths($item->billing_cycle),
'quantity' => $item->quantity ?? 1,
'unit_price' => $item->price,
'total_price' => $item->price * ($item->quantity ?? 1),
]);
// Copy addons from OrderItem to InvoiceItem
foreach ($item->addons as $addon) {
\App\Models\InvoiceAddons::create([
'invoice_item_id' => $invoiceItem->id,
'addon_id' => $addon->addon_id,
'qty' => $addon->qty,
'price' => $addon->price // Unit price preserved from order
]);
}
}
return $invoice;
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
private function cycleToMonths(?string $cycle): int
{
return match ($cycle) {
'monthly' => 1,
'quarterly' => 3,
'semi_annually' => 6,
'annually' => 12,
'biennially' => 24,
'triennially' => 36,
default => 1,
};
}
}