Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions apps/api/app/Http/Controllers/ReportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public function store(Request $request): JsonResponse
'layout.fields.*.order' => 'sometimes|integer',
'layout.group_order' => 'nullable|array',
'layout.group_order.*' => 'string',
// Declared explicitly: validate() only returns keys that carry a rule, so
// anything omitted here is dropped before the report is persisted.
'layout.view_id' => 'nullable|uuid|exists:views,id',
'layout.show_headers_only' => 'nullable|boolean',
'layout.per_page' => 'nullable|integer|min:1|max:100',
]);

$report = Report::create($validated);
Expand Down Expand Up @@ -84,6 +89,11 @@ public function update(Request $request, Report $report): JsonResponse
'layout.fields.*.order' => 'sometimes|integer',
'layout.group_order' => 'nullable|array',
'layout.group_order.*' => 'string',
// Declared explicitly: validate() only returns keys that carry a rule, so
// anything omitted here is dropped before the report is persisted.
'layout.view_id' => 'nullable|uuid|exists:views,id',
'layout.show_headers_only' => 'nullable|boolean',
'layout.per_page' => 'nullable|integer|min:1|max:100',
]);

$report->update($validated);
Expand Down Expand Up @@ -243,11 +253,13 @@ private function generateCsvResponse(array $columns, array $groups, ?string $gro

$csvHeaders = [];
$showHeadersOnly = ($layout['show_headers_only'] ?? false) === true;
// The group field is commonly also a selected column; emit it once.
$groupIsSelectedColumn = $groupBy !== null && in_array($groupBy, $columns, true);
if ($showHeadersOnly) {
$csvHeaders[] = $groupBy ?? 'Groupe';
$csvHeaders[] = 'Nombre de fiches';
} else {
if ($groupBy) {
if ($groupBy && ! $groupIsSelectedColumn) {
$csvHeaders[] = $groupBy;
}
foreach ($columns as $col) {
Expand All @@ -264,7 +276,7 @@ private function generateCsvResponse(array $columns, array $groups, ?string $gro
} else {
foreach ($group['records'] as $rec) {
$row = [];
if ($groupBy) {
if ($groupBy && ! $groupIsSelectedColumn) {
$row[] = $groupKey;
}
foreach ($columns as $col) {
Expand Down
73 changes: 73 additions & 0 deletions apps/api/tests/Feature/ReportFeatureTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Models\Report;
use App\Models\Table;
use App\Models\User;
use App\Models\View;
use App\Models\Workspace;
use App\Models\WorkspaceMember;
use Illuminate\Foundation\Testing\RefreshDatabase;
Expand Down Expand Up @@ -359,3 +360,75 @@ function createAuthenticatedUser()
expect($previewCsvResponse->headers->get('Content-Type'))->toContain('text/csv');
expect(str_contains($previewCsvResponse->streamedContent(), 'title'))->toBeTrue();
});

test('layout view_id, show_headers_only and per_page survive save and reload', function () {
$setup = createAuthenticatedUser();
$user = $setup['user'];
$table = $setup['table'];
$view = View::factory()->create(['table_id' => $table->id]);

$payload = [
'table_id' => $table->id,
'name' => 'Rapport avec vue',
'query' => ['select' => ['Nom'], 'group_by' => 'Ville'],
'layout' => [
'fields' => [['name' => 'Nom', 'visible' => true, 'order' => 1]],
'view_id' => $view->id,
'show_headers_only' => true,
'per_page' => 25,
],
];

$created = $this->actingAs($user)->postJson('/api/v1/reports', $payload);
$created->assertStatus(201);

$report = Report::find($created->json('id'));
expect($report->layout['view_id'])->toBe($view->id);
expect($report->layout['show_headers_only'])->toBeTrue();
expect($report->layout['per_page'])->toBe(25);

// The same keys must also survive an update.
$otherView = View::factory()->create(['table_id' => $table->id]);
$payload['layout']['view_id'] = $otherView->id;
$payload['layout']['per_page'] = 50;

$this->actingAs($user)
->putJson("/api/v1/reports/{$report->id}", $payload)
->assertStatus(200);

$report->refresh();
expect($report->layout['view_id'])->toBe($otherView->id);
expect($report->layout['per_page'])->toBe(50);
});

test('csv export emits the group field once when it is also a selected column', function () {
$setup = createAuthenticatedUser();
$user = $setup['user'];
$table = $setup['table'];

Field::factory()->create(['table_id' => $table->id, 'name' => 'Ville', 'type' => 'text']);
Field::factory()->create(['table_id' => $table->id, 'name' => 'Nom', 'type' => 'text']);

Record::create([
'table_id' => $table->id,
'data' => ['Ville' => 'Québec', 'Nom' => 'Tremblay'],
'version' => 1,
]);

$response = $this->actingAs($user)->postJson('/api/v1/reports/preview/csv', [
'table_id' => $table->id,
'query' => ['select' => ['Ville', 'Nom'], 'group_by' => 'Ville'],
'layout' => ['fields' => [
['name' => 'Ville', 'visible' => true, 'order' => 1],
['name' => 'Nom', 'visible' => true, 'order' => 2],
]],
]);

$response->assertStatus(200);
$csv = $response->streamedContent();
$headerLine = str_getcsv(explode("\n", trim($csv))[0]);
$headerLine[0] = preg_replace('/^\x{FEFF}/u', '', $headerLine[0]);

expect($headerLine)->toBe(['Ville', 'Nom']);
expect(array_count_values($headerLine)['Ville'])->toBe(1);
});
Loading