feat(admin): IP pool allocation, billing exports, service attachments
- IP pool: sheet import (xlsx/csv) matched by address, bulk allocate/revoke with notification email (ip_allocated/ip_revoked), ip_pool_logs history. - Orders/invoices: Excel export on branded template (BrandedReportWriter), shared billing filters, order item service info in list responses. - Web design quote Excel export; activity log export. - Service attachments with copy-to-invoice; deployment invoice attachments. - Customer email attachments stored on disk and attached by path. - Admin password change endpoint; user.fullname backfill migration; orders.user_id backfill from logs. - Disable deploy workflow until CI/CD is set up. - Ignore stored customer email and service attachment uploads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ea5697cb81
commit
46aa138eec
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Api\Admin\WebDesign;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\WebDesignQuote;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Tests\TestCase;
|
||||
|
||||
class WebDesignQuoteXlsxTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_downloads_quote_into_pricing_template_with_live_totals(): void
|
||||
{
|
||||
Role::firstOrCreate(['name' => 'Full Administrator', 'guard_name' => 'admin']);
|
||||
$admin = User::factory()->create();
|
||||
$admin->assignRole('Full Administrator');
|
||||
|
||||
$quote = WebDesignQuote::create([
|
||||
'quote_number' => 'BG-WEB-202610-0001',
|
||||
'status' => 'draft',
|
||||
'customer_name' => 'Nguyễn Văn A',
|
||||
'website_name' => 'shop.vn',
|
||||
'website_category' => 'Thương mại điện tử',
|
||||
'platform' => 'wordpress_elementor',
|
||||
'items' => [
|
||||
['name' => 'Thiết kế UI', 'description' => '<ul><li>Trang chủ</li><li>Sản phẩm</li></ul>', 'quantity' => 1, 'unit_price' => 8000000],
|
||||
['name' => '=HYPERLINK("x")', 'description' => null, 'quantity' => 2, 'unit_price' => 1000000],
|
||||
],
|
||||
'subtotal' => 10000000,
|
||||
'discount' => 500000,
|
||||
'vat_percent' => 8,
|
||||
'total' => 10260000,
|
||||
'currency' => 'VND',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($admin, 'admin')
|
||||
->get("/api/v1/administrator/web-design-quotes/{$quote->id}/xlsx")
|
||||
->assertOk();
|
||||
|
||||
$book = IOFactory::load($response->baseResponse->getFile()->getPathname());
|
||||
$sheet = $book->getActiveSheet();
|
||||
$cells = collect($sheet->toArray(null, true, false, true));
|
||||
|
||||
$this->assertSame(1, $book->getSheetCount());
|
||||
$this->assertCount(1, $sheet->getDrawingCollection());
|
||||
$this->assertSame('BÁO GIÁ THIẾT KẾ WEBSITE SHOP.VN', $sheet->getCell('A6')->getValue());
|
||||
|
||||
$itemRow = $cells->search(fn (array $row) => $row['B'] === 'Thiết kế UI');
|
||||
$this->assertSame("- Trang chủ\n- Sản phẩm", $sheet->getCell("C{$itemRow}")->getValue());
|
||||
$this->assertSame('=HYPERLINK("x")', $sheet->getCell('B' . ($itemRow + 1))->getCalculatedValue());
|
||||
|
||||
$totalRow = $cells->search(fn (array $row) => $row['A'] === 'Tổng giá trị báo giá');
|
||||
$this->assertEquals($quote->total, $sheet->getCell("F{$totalRow}")->getCalculatedValue());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user