0620635abb
Audit pass (33 new tests in tests/Feature/Audit/): - CrmFunnelE2ETest: full Lead→Deal→Appointment→WO→Payment journey, covering 5 previously-untested models. Verifies WO.balanceDue updates correctly after payments, including refunds (negative amount → balance increases). - WorkOrderTotalsTest: works+parts+subcontract+discount sum correctly, cancelled subcontract excluded, deleting lines triggers recalc, status=done consumes part reservations into issues, cancelled releases reservations. - ShopJourneyE2ETest: register→cart→checkout→email confirmation→tracking page reachable→admin fulfills→stock drops→warehouse event recorded. Also guest checkout still works without account. - CsvImportExportTest: round-trip, dedup-by-phone, **caught real bug** — Vehicle export wrote $row->brand (no such property) and import set 'brand' => row['brand'] in Vehicle::create (column is `make`). Fix applied to both paths. - TenantBackupServiceTest: zip contains valid manifest with counts + data/*.json per model + works embedded with WorkOrder. - WorkOrderPdfServiceTest: generated PDF starts with %PDF, includes WO data, non-trivial size, handles empty WO. - PayrollCalculatorTest: base + works_pct + parts_pct + bonus - fine - advance, scoped to user + period. - NotificationFallbackTest: Telegram wins when chat_id present, falls back to email when not, returns false when neither, tenant disable flag stops both. - AiProvidersCrossCheckTest: OpenAI request shape, Gemini URL with model, no-key friendly message, tenant model override propagates into HTTP body. - SettingsPersistenceTest: 25-key settings JSON round-trips, partial update via array_replace_recursive preserves other keys. - CompanyProvisionerTest: suspend / reactivate / archive behavior. Bug fixed: CsvImportExport used `brand` on Vehicle which has column `make`. The export silently emitted empty values, the import silently dropped the brand. Now both paths use `make`. Full suite: 173 passed (468 assertions). 0 failed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
82 lines
3.1 KiB
PHP
82 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Audit;
|
|
|
|
use App\Models\Central\Company;
|
|
use App\Models\Central\Plan;
|
|
use App\Models\Tenant\Client;
|
|
use App\Models\Tenant\Vehicle;
|
|
use App\Models\Tenant\WorkOrder;
|
|
use App\Models\Tenant\WorkOrderPart;
|
|
use App\Models\Tenant\WorkOrderWork;
|
|
use App\Services\WorkOrderPdfService;
|
|
use App\Tenancy\TenantManager;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class WorkOrderPdfServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_pdf_generates_with_works_and_parts(): void
|
|
{
|
|
$ctx = $this->bootTenant();
|
|
$client = Client::create(['name' => 'PdfClient', 'phone' => '+37300100200', 'type' => 'individual', 'status' => 'active']);
|
|
$vehicle = Vehicle::create([
|
|
'client_id' => $client->id, 'make' => 'Audi', 'model' => 'A4', 'plate' => 'PDF-001',
|
|
]);
|
|
$wo = WorkOrder::create([
|
|
'number' => WorkOrder::generateNumber($ctx['company']->id),
|
|
'client_id' => $client->id, 'vehicle_id' => $vehicle->id,
|
|
'opened_at' => today(), 'status' => 'in_work',
|
|
'complaint' => 'Zgomot la frânare',
|
|
]);
|
|
WorkOrderWork::create([
|
|
'work_order_id' => $wo->id, 'name' => 'Schimb plăcuțe',
|
|
'hours' => 1.5, 'price_per_hour' => 400, 'status' => 'done',
|
|
]);
|
|
WorkOrderPart::create([
|
|
'work_order_id' => $wo->id, 'name' => 'Plăcuțe TRW',
|
|
'qty' => 1, 'buy_price' => 200, 'sell_price' => 350,
|
|
'status' => 'installed',
|
|
]);
|
|
|
|
$svc = app(WorkOrderPdfService::class);
|
|
$pdf = $svc->generate($wo);
|
|
$output = $pdf->output();
|
|
|
|
$this->assertStringStartsWith('%PDF', $output, 'binary starts with PDF magic');
|
|
$this->assertGreaterThan(2000, strlen($output), 'non-trivial PDF size');
|
|
|
|
$filename = $svc->filename($wo);
|
|
$this->assertStringContainsString($wo->number, $filename);
|
|
$this->assertStringEndsWith('.pdf', $filename);
|
|
}
|
|
|
|
public function test_pdf_handles_wo_with_no_lines(): void
|
|
{
|
|
$ctx = $this->bootTenant();
|
|
$client = Client::create(['name' => 'X', 'phone' => '+37399000000', 'type' => 'individual', 'status' => 'active']);
|
|
$vehicle = Vehicle::create(['client_id' => $client->id, 'make' => 'X', 'model' => 'Y', 'plate' => 'EMPTY-1']);
|
|
$wo = WorkOrder::create([
|
|
'number' => WorkOrder::generateNumber($ctx['company']->id),
|
|
'client_id' => $client->id, 'vehicle_id' => $vehicle->id,
|
|
'opened_at' => today(), 'status' => 'new',
|
|
]);
|
|
|
|
$pdf = app(WorkOrderPdfService::class)->generate($wo);
|
|
$this->assertStringStartsWith('%PDF', $pdf->output());
|
|
}
|
|
|
|
private function bootTenant(): array
|
|
{
|
|
$plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]);
|
|
$company = Company::create([
|
|
'plan_id' => $plan->id, 'slug' => 'pdf-' . uniqid(),
|
|
'name' => 'PDF Co', 'status' => 'active',
|
|
]);
|
|
app(TenantManager::class)->setCurrent($company);
|
|
return compact('company');
|
|
}
|
|
}
|