Add group bulkstore backend

This commit is contained in:
philipp lang 2023-12-30 02:02:07 +01:00
parent 6972091ad0
commit eb14189bf0
12 changed files with 241 additions and 21 deletions

View File

@ -2,6 +2,7 @@
namespace App;
use App\Group\Enums\Level;
use App\Nami\HasNamiField;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -12,9 +13,13 @@ class Group extends Model
use HasFactory;
use HasNamiField;
public $fillable = ['nami_id', 'name', 'parent_id'];
public $fillable = ['nami_id', 'name', 'inner_name', 'level', 'parent_id'];
public $timestamps = false;
public $casts = [
'level' => Level::class
];
/**
* @return BelongsTo<static, self>
*/

View File

@ -0,0 +1,39 @@
<?php
namespace App\Group\Actions;
use App\Group;
use App\Group\Enums\Level;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\Rule;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;
class GroupBulkstoreAction
{
use AsAction;
public function rules(): array
{
return [
'*.id' => 'required|integer|exists:groups,id',
'*.inner_name' => 'required|string|max:255',
'*.level' => ['required', 'string', Rule::in(Level::values())],
];
}
public function handle($groups): void
{
foreach ($groups as $payload) {
Group::find($payload['id'])->update(['level' => $payload['level'], 'inner_name' => $payload['inner_name']]);
}
}
public function asController(ActionRequest $request): JsonResponse
{
$this->handle($request->validated());
return response()->json([]);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Group\Actions;
use App\Group;
use App\Group\Resources\GroupResource;
use Illuminate\Database\Eloquent\Collection;
use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class GroupIndexAction
{
use AsAction;
/**
* @return Collection<int, Group>
*/
public function handle(): Collection
{
return Group::get();
}
public function asController(): Response
{
return Inertia::render('group/Index', [
'data' => GroupResource::collection($this->handle()),
]);
}
}

29
app/Group/Enums/Level.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace App\Group\Enums;
use Illuminate\Support\Collection;
enum Level: string
{
case FEDERATION = 'Diözese';
case REGION = 'Bezirk';
case GROUP = 'Stamm';
/**
* @return Collection<int, string>
*/
public static function values(): Collection
{
return collect(static::cases())->map(fn ($case) => $case->value);
}
/**
* @return array<int, array{id: string, name: string}>
*/
public static function forSelect(): array
{
return array_map(fn ($case) => ['id' => $case->value, 'name' => $case->value], static::cases());
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Group\Resources;
use App\Lib\HasMeta;
use Illuminate\Http\Resources\Json\JsonResource;
class GroupResource extends JsonResource
{
use HasMeta;
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'name' => $this->name,
'inner_name' => $this->inner_name,
'parent_id' => $this->parent_id,
'id' => $this->id,
'level' => $this->level?->value,
];
}
public static function meta(): array
{
return [
'links' => [
'bulkstore' => route('group.bulkstore'),
]
];
}
}

View File

@ -3,6 +3,7 @@
namespace Database\Factories;
use App\Group;
use App\Group\Enums\Level;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@ -22,6 +23,8 @@ class GroupFactory extends Factory
return [
'name' => $this->faker->words(5, true),
'nami_id' => $this->faker->randomNumber(),
'inner_name' => $this->faker->words(5, true),
'level' => $this->faker->randomElement(Level::cases()),
];
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('groups', function (Blueprint $table) {
$table->string('inner_name');
$table->string('level')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('groups', function (Blueprint $table) {
$table->dropColumn('inner_name');
$table->dropColumn('level');
});
}
};

View File

@ -16,6 +16,7 @@
<v-link v-show="hasModule('bill')" href="/invoice" menu="invoice" icon="moneypaper">Rechnungen</v-link>
<v-link href="/contribution" menu="contribution" icon="contribution">Zuschüsse</v-link>
<v-link href="/activity" menu="activity" icon="activity">Tätigkeiten</v-link>
<v-link href="/group" menu="group" icon="group">Gruppierungen</v-link>
<v-link href="/maildispatcher" menu="maildispatcher" icon="at">Mail-Verteiler</v-link>
</div>
<div class="grid gap-2">

View File

@ -19,7 +19,8 @@ use App\Invoice\Actions\InvoiceStoreAction;
use App\Course\Actions\CourseUpdateAction;
use App\Dashboard\Actions\IndexAction as DashboardIndexAction;
use App\Efz\ShowEfzDocumentAction;
use App\Group\Actions\ListAction;
use App\Group\Actions\GroupBulkstoreAction;
use App\Group\Actions\GroupIndexAction;
use App\Initialize\Actions\InitializeAction;
use App\Initialize\Actions\InitializeFormAction;
use App\Initialize\Actions\NamiGetSearchLayerAction;
@ -31,7 +32,7 @@ use App\Invoice\Actions\InvoiceDestroyAction;
use App\Invoice\Actions\InvoiceIndexAction;
use App\Invoice\Actions\InvoiceUpdateAction;
use App\Invoice\Actions\MassPostPdfAction;
use App\Invoice\Actions\MassStoreAction;
use App\Invoice\Actions\MassStoreAction as InvoiceMassStoreAction;
use App\Invoice\Actions\PaymentPositionIndexAction;
use App\Maildispatcher\Actions\CreateAction;
use App\Maildispatcher\Actions\DestroyAction;
@ -49,10 +50,11 @@ use App\Member\Actions\SearchAction;
use App\Member\MemberController;
use App\Membership\Actions\IndexAction as MembershipIndexAction;
use App\Membership\Actions\ListForGroupAction;
use App\Membership\Actions\MassListAction;
use App\Membership\Actions\MassStoreAction;
use App\Membership\Actions\MembershipDestroyAction;
use App\Membership\Actions\MembershipStoreAction;
use App\Membership\Actions\MembershipUpdateAction;
use App\Membership\Actions\StoreForGroupAction;
use App\Payment\SubscriptionController;
Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void {
@ -99,11 +101,9 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::post('/maildispatcher', MaildispatcherStoreAction::class)->name('maildispatcher.store');
Route::delete('/maildispatcher/{maildispatcher}', DestroyAction::class)->name('maildispatcher.destroy');
// ----------------------------------- group -----------------------------------
Route::get('/group', ListAction::class)->name('group.index');
// -------------------------------- allpayment ---------------------------------
Route::post('/invoice/mass-store', MassStoreAction::class)->name('invoice.mass-store');
Route::post('/invoice/mass-store', InvoiceMassStoreAction::class)->name('invoice.mass-store');
// ---------------------------------- invoice ----------------------------------
Route::get('/invoice', InvoiceIndexAction::class)->name('invoice.index');
@ -124,7 +124,12 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::patch('/membership/{membership}', MembershipUpdateAction::class)->name('membership.update');
Route::delete('/membership/{membership}', MembershipDestroyAction::class)->name('membership.destroy');
Route::post('/api/membership/member-list', ListForGroupAction::class)->name('membership.member-list');
Route::post('/api/membership/sync', StoreForGroupAction::class)->name('membership.sync');
Route::post('/api/membership/masslist', MassStoreAction::class)->name('membership.masslist.store');
Route::get('/membership/masslist', MassListAction::class)->name('membership.masslist.index');
// ----------------------------------- group ----------------------------------
Route::get('/group', GroupIndexAction::class)->name('group.index');
Route::post('/group/bulkstore', GroupBulkstoreAction::class)->name('group.bulkstore');
// ----------------------------------- course ----------------------------------
Route::get('/member/{member}/course', CourseIndexAction::class)->name('member.course.index');

View File

@ -0,0 +1,30 @@
<?php
namespace Tests\Feature\Group;
use App\Group;
use App\Group\Enums\Level;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class BulkstoreTest extends TestCase
{
use DatabaseTransactions;
public function testItSavesGroupsLevelAndParent(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
$group = Group::factory()->for(Group::first(), 'parent')->create(['inner_name' => 'Gruppe', 'level' => Level::REGION]);
$this->postJson(route('group.bulkstore'), [
['id' => $group->id, 'inner_name' => 'Abc', 'level' => Level::FEDERATION->value]
])->assertOk();
$this->assertDatabaseHas('groups', [
'id' => $group->id,
'inner_name' => 'Abc',
'level' => 'Diözese',
]);
}
}

View File

@ -2,9 +2,8 @@
namespace Tests\Feature\Group;
use App\Activity;
use App\Group;
use App\Subactivity;
use App\Group\Enums\Level;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
@ -14,18 +13,25 @@ class IndexTest extends TestCase
public function testItDisplaysAllActivitiesAndSubactivities(): void
{
$this->login()->loginNami();
$this->login()->loginNami()->withoutExceptionHandling();
$leiter = Activity::factory()->name('Leiter*in')->hasAttached(Subactivity::factory()->name('Rover'))->create();
$intern = Activity::factory()->name('Intern')->hasAttached(Subactivity::factory()->name('Lager'))->create();
$group = Group::factory()->create();
$group = Group::factory()->for(Group::first(), 'parent')->create(['name' => 'Afff', 'inner_name' => 'Gruppe', 'level' => Level::REGION]);
$response = $this->get('/group');
$this->get('/group')
->assertInertiaPath('data.data.2.name', 'Afff')
->assertInertiaPath('data.data.2.inner_name', 'Gruppe')
->assertInertiaPath('data.data.2.id', $group->id)
->assertInertiaPath('data.data.2.level', 'Bezirk')
->assertInertiaPath('data.data.2.parent_id', Group::first()->id)
->assertInertiaPath('data.meta.links.bulkstore', route('group.bulkstore'));
}
$this->assertInertiaHas('Leiter*in', $response, "activities.{$leiter->id}");
$this->assertInertiaHas('Intern', $response, "activities.{$intern->id}");
$this->assertInertiaHas('Rover', $response, "subactivities.{$leiter->id}.{$leiter->subactivities->first()->id}");
$this->assertInertiaHas('Lager', $response, "subactivities.{$intern->id}.{$intern->subactivities->first()->id}");
$this->assertInertiaHas($group->name, $response, "groups.{$group->id}");
public function testLevelCanBeNull(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
Group::factory()->create(['level' => null]);
$this->get('/group')->assertInertiaPath('data.data.2.level', null);
}
}

View File

@ -40,7 +40,7 @@ class InitializeGroupsTest extends TestCase
(new InitializeGroups(app(NamiSettings::class)->login()))->handle();
$this->assertDatabaseHas('groups', ['nami_id' => 1000, 'name' => 'testgroup']);
$this->assertDatabaseHas('groups', ['nami_id' => 1000, 'name' => 'testgroup', 'inner_name' => 'testgroup', 'level' => null]);
$this->assertDatabaseHas('groups', ['nami_id' => 1001, 'name' => 'subgroup1']);
$this->assertDatabaseHas('groups', ['nami_id' => 1002, 'name' => 'subgroup2']);
}