From f796f2c658074a3daa110523e82a58f6071ddb67 Mon Sep 17 00:00:00 2001 From: Nebulae Date: Sat, 15 Nov 2025 17:09:56 +0100 Subject: [PATCH] feat(Import members from Dolibarr) --- app/Console/Commands/SyncDolibarrMembers.php | 150 +++++++++++++----- app/Models/Member.php | 16 +- app/Models/Membership.php | 20 ++- ...10_20_111412_create_member_types_table.php | 31 ++++ ...2025_10_20_111419_create_members_table.php | 9 ++ ..._10_20_111428_create_memberships_table.php | 6 + routes/dev-routes.php | 12 +- 7 files changed, 195 insertions(+), 49 deletions(-) create mode 100644 database/migrations/2025_10_20_111412_create_member_types_table.php diff --git a/app/Console/Commands/SyncDolibarrMembers.php b/app/Console/Commands/SyncDolibarrMembers.php index 4627253..751828f 100644 --- a/app/Console/Commands/SyncDolibarrMembers.php +++ b/app/Console/Commands/SyncDolibarrMembers.php @@ -3,26 +3,25 @@ namespace App\Console\Commands; use App\Models\Member; +use App\Models\Membership; use App\Services\DolibarrService; use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Http\Client\ConnectionException; -use function Deployer\timestamp; +use function Laravel\Prompts\progress; class SyncDolibarrMembers extends Command { /** * The name and signature of the console command. * - * @var string - */ - protected $signature = 'app:sync-dolibarr-members'; + * @var string */ + protected $signature = 'sync:dolibarr-members'; /** - * The console command description. + * The console command description. * * - * @var string - */ + * @var string */ protected $description = 'Retrieve members data from Dolibarr'; /** @@ -32,55 +31,120 @@ class SyncDolibarrMembers extends Command public function handle(): void { $this->info('Starting Dolibarr members import...'); - // Dolibarr API call - $client = new DolibarrService; + + $client = new DolibarrService; + + // $doliMembers = collect($client->getAllMembers())->take(10); // For test $doliMembers = collect($client->getAllMembers()); - $progressBar = $this->output->createProgressBar(count($doliMembers)); + $progressBar = progress(label: 'Dolibarr Members import', steps: $doliMembers->count()); $progressBar->start(); - $i = 0; + + // Stats trackers + $createdMembers = 0; + $updatedMembers = 0; + $createdMemberships = 0; + $updatedMemberships = 0; + + // Status mapping from Dolibarr + $memberStatuses = [ + '-2' => 'excluded', + '0' => 'cancelled', + '1' => 'valid' + ]; foreach ($doliMembers as $member) { - dd($member); - $newMember = Member::updateOrCreate([ - 'dolibarr_id' => $member->id, - ], [ - 'status' => $member['status'], // @todo: faire concorder les statuts - 'nature' => 'physical', - 'member_type' => $member['type'], - 'group_id' => null, - 'lastname' => $member['firstname'], - 'firstname' => $member['lastname'], - 'email' => $member['email'], - 'personal_email' => '', - 'company' => '', - 'website_url' => $member['url'], - 'date_of_birth' => '', - 'address' => '', - 'zipcode' => '', - 'city' => '', - 'country' => '', - 'phone1' => '', - 'phone2' => '', - 'public_membership' => '', - 'created_at' => Carbon::create($member['date_creation'])->format(timestamp()), - 'updated_at' => '', - ]); + // CREATE or UPDATE MEMBER + $newMember = Member::updateOrCreate( + ['dolibarr_id' => $member['id']], + [ + 'status' => $memberStatuses[$member['status']] ?? 'draft', + 'nature' => 'physical', + 'member_type' => $member['type'], + 'group_id' => null, + 'lastname' => $member['firstname'], + 'firstname' => $member['lastname'], + 'email' => $member['email'] ?: null, + 'retzien_email' => '', + 'company' => $member['societe'], + 'website_url' => $member['url'], + 'address' => $member['address'], + 'zipcode' => $member['zip'], + 'city' => $member['town'], + 'country' => '', + 'phone1' => $member['phone'], + 'phone2' => $member['phone_mobile'], + 'public_membership' => 0, + 'created_at' => $this->toDate($member['date_creation']), + ] + ); - // On crée l'adhérent en remplissant les données en bdd avec ses coordonnées, son statut etc ... + // Count member creation/update + if ($newMember->wasRecentlyCreated) { + $createdMembers++; + } else { + $updatedMembers++; + } - // On récupère toutes les adhésions/cotisations pour chaque adhérent - $memberships = $client->getMemberSubscriptions($member->id); + // Get subscriptions for memeber + $memberships = collect($client->getMemberSubscriptions($member['id'])); - // on traite les notes (privée, publique, lien ect) contenues dans dolibarr + foreach ($memberships as $membership) { - $i++; + $membershipStatus = $membership['datef'] < now()->timestamp ? 'expired' : 'active'; + + $newMembership = Membership::updateOrCreate( + ['dolibarr_id' => $membership['id']], + [ + 'member_id' => $newMember->id, + 'admin_id' => 1, + 'package_id' => 2, // annual subscription + 'start_date' => $this->toDate($membership['dateh']), + 'end_date' => $this->toDate($membership['datef']), + 'status' => $membershipStatus, + 'validation_date' => $this->toDate($membership['datem']), + 'payment_method' => null, + 'amount' => number_format($membership['amount'], 2), + 'payment_status' => 'paid', + 'note_public' => $membership['note_public'], + 'note_private' => $membership['note_private'], + 'dolibarr_user_id' => $member['id'] + ] + ); + + // Count membership creation/update + if ($newMembership->wasRecentlyCreated) { + $createdMemberships++; + } else { + $updatedMemberships++; + } + } + + $progressBar->advance(); } $progressBar->finish(); - // Logs - $this->info('Import finished. ' .$i.' members have been imported.'); + // Report + $this->info(''); + $this->info('===== IMPORT SUMMARY ====='); + $this->info("Members created : $createdMembers"); + $this->info("Members updated : $updatedMembers"); + $this->info("Memberships created : $createdMemberships"); + $this->info("Memberships updated : $updatedMemberships"); + $this->info('==========================='); + $this->info('Import completed successfully.'); + } + + /** + * Convert timestamp to date format safely + * @todo: export this in a service or repo + */ + private function toDate($timestamp): ?string + { + return $timestamp + ? Carbon::createFromTimestamp($timestamp)->format('Y-m-d H:i:s') + : null; } } diff --git a/app/Models/Member.php b/app/Models/Member.php index 6a10569..3815b2c 100644 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -11,13 +11,16 @@ use Illuminate\Notifications\Notifiable; /** * @property int $id * @property int|null $user_id + * @property string|null $dolibarr_id * @property string|null $keycloak_id * @property string $status * @property string $nature + * @property int|null $type_id * @property int|null $group_id * @property string|null $lastname * @property string|null $firstname * @property string $email + * @property string|null $retzien_email * @property string|null $company * @property string|null $date_of_birth * @property string|null $address @@ -27,6 +30,7 @@ use Illuminate\Notifications\Notifiable; * @property string|null $phone1 * @property string|null $phone2 * @property int $public_membership + * @property string|null $website_url * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $deleted_at @@ -34,6 +38,8 @@ use Illuminate\Notifications\Notifiable; * @property-read \App\Models\MemberGroup|null $group * @property-read \Illuminate\Database\Eloquent\Collection $memberships * @property-read int|null $memberships_count + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read int|null $notifications_count * @property-read \App\Models\User|null $user * @method static \Database\Factories\MemberFactory factory($count = null, $state = []) * @method static \Illuminate\Database\Eloquent\Builder|Member newModelQuery() @@ -46,6 +52,7 @@ use Illuminate\Notifications\Notifiable; * @method static \Illuminate\Database\Eloquent\Builder|Member whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereDateOfBirth($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Member whereDolibarrId($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereFirstname($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereGroupId($value) @@ -56,9 +63,12 @@ use Illuminate\Notifications\Notifiable; * @method static \Illuminate\Database\Eloquent\Builder|Member wherePhone1($value) * @method static \Illuminate\Database\Eloquent\Builder|Member wherePhone2($value) * @method static \Illuminate\Database\Eloquent\Builder|Member wherePublicMembership($value) + * @method static \Illuminate\Database\Eloquent\Builder|Member whereRetzienEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|Member whereTypeId($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Member whereWebsiteUrl($value) * @method static \Illuminate\Database\Eloquent\Builder|Member whereZipcode($value) * @mixin \Eloquent */ @@ -67,9 +77,11 @@ class Member extends Model use HasFactory, Notifiable; protected $fillable = [ 'user_id', + 'dolibarr_id', 'keycloak_id', 'status', 'nature', + 'type_id', 'group_id', 'lastname', 'firstname', @@ -83,9 +95,7 @@ class Member extends Model 'phone1', 'phone2', 'public_membership', - 'package_id', - 'amount', - 'payment_status' + 'website_url' ]; public static function getAttributeLabel(string $attribute): string diff --git a/app/Models/Membership.php b/app/Models/Membership.php index 017501c..739fcc1 100644 --- a/app/Models/Membership.php +++ b/app/Models/Membership.php @@ -13,11 +13,17 @@ use Illuminate\Database\Eloquent\Relations\HasOne; * @property int $member_id * @property int|null $admin_id * @property int $package_id - * @property string $start_date + * @property string|null $start_date * @property string|null $end_date * @property string $status + * @property string|null $validation_date + * @property string|null $payment_method * @property string $amount * @property string $payment_status + * @property string|null $note_public + * @property string|null $note_private + * @property string|null $dolibarr_id + * @property string|null $dolibarr_user_id * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $deleted_at @@ -33,14 +39,20 @@ use Illuminate\Database\Eloquent\Relations\HasOne; * @method static \Illuminate\Database\Eloquent\Builder|Membership whereAmount($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership whereDolibarrId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership whereDolibarrUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereEndDate($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereMemberId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership whereNotePrivate($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership whereNotePublic($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership wherePackageId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership wherePaymentMethod($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership wherePaymentStatus($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereStartDate($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereStatus($value) * @method static \Illuminate\Database\Eloquent\Builder|Membership whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Membership whereValidationDate($value) * @mixin \Eloquent */ class Membership extends Model @@ -52,8 +64,14 @@ class Membership extends Model 'start_date', 'end_date', 'status', + 'validation_date', + 'payment_method', 'amount', 'payment_status', + 'note_public', + 'note_private', + 'dolibarr_id', + 'dolibarr_user_id' ]; diff --git a/database/migrations/2025_10_20_111412_create_member_types_table.php b/database/migrations/2025_10_20_111412_create_member_types_table.php new file mode 100644 index 0000000..c032f78 --- /dev/null +++ b/database/migrations/2025_10_20_111412_create_member_types_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('identifier')->unique(); + $table->string('name'); + $table->string('description')->nullable(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('member_types'); + } +}; diff --git a/database/migrations/2025_10_20_111419_create_members_table.php b/database/migrations/2025_10_20_111419_create_members_table.php index 645d268..34ef802 100644 --- a/database/migrations/2025_10_20_111419_create_members_table.php +++ b/database/migrations/2025_10_20_111419_create_members_table.php @@ -18,6 +18,8 @@ return new class extends Migration // User $table->foreignId('user_id')->nullable()->constrained('users')->onDelete('set null'); + $table->string('dolibarr_id')->nullable(); + // Keycloak $table->string('keycloak_id')->nullable(); @@ -33,6 +35,9 @@ return new class extends Migration // Nature $table->enum('nature', ['physical', 'legal'])->default('physical'); + // Type + $table->unsignedBigInteger('type_id')->nullable(); + // Group $table->unsignedBigInteger('group_id')->nullable(); @@ -40,6 +45,7 @@ return new class extends Migration $table->string('lastname')->nullable(); $table->string('firstname')->nullable(); $table->string('email'); + $table->string('retzien_email')->nullable(); $table->string('company')->nullable(); $table->date('date_of_birth')->nullable(); @@ -54,6 +60,9 @@ return new class extends Migration // Membership type $table->boolean('public_membership')->default(false); + // Others + $table->string('website_url')->nullable(); + $table->timestamps(); $table->softDeletes(); }); diff --git a/database/migrations/2025_10_20_111428_create_memberships_table.php b/database/migrations/2025_10_20_111428_create_memberships_table.php index 0b719f1..67759da 100644 --- a/database/migrations/2025_10_20_111428_create_memberships_table.php +++ b/database/migrations/2025_10_20_111428_create_memberships_table.php @@ -19,8 +19,14 @@ return new class extends Migration $table->date('start_date')->nullable(); $table->date('end_date')->nullable(); $table->enum('status', ['active', 'expired', 'pending'])->default('pending'); + $table->date('validation_date')->nullable(); + $table->string('payment_method')->nullable(); $table->decimal('amount', 10, 2)->default(0); $table->enum('payment_status', ['paid', 'unpaid', 'partial'])->default('unpaid'); + $table->longText('note_public')->nullable(); + $table->longText('note_private')->nullable(); + $table->string('dolibarr_id')->nullable(); + $table->string('dolibarr_user_id')->nullable(); $table->timestamps(); $table->softDeletes(); }); diff --git a/routes/dev-routes.php b/routes/dev-routes.php index a588223..25f5e00 100644 --- a/routes/dev-routes.php +++ b/routes/dev-routes.php @@ -6,8 +6,16 @@ Route::get('/call-dolibarr', function () { $call = new App\Services\DolibarrService; $members = $call->getAllMembers(); // find specific - $userData = collect($members)->firstWhere('id', 139); + $userData = collect($members)->firstWhere('id', 124); // Isabelle AK - dd($userData); + //dd($userData); + $subscriptions = collect($call->getMemberSubscriptions(124))->firstWhere('id', 324); + dd($subscriptions); + $date1 = \Carbon\Carbon::createFromTimestamp($subscriptions['datec'])->format('d/m/Y'); + $date2 = \Carbon\Carbon::createFromTimestamp($subscriptions['datem'])->format('d/m/Y'); + $date3 = \Carbon\Carbon::createFromTimestamp($subscriptions['dateh'])->format('d/m/Y'); + $date4 = \Carbon\Carbon::createFromTimestamp($subscriptions['datef'])->format('d/m/Y'); + + dd($date1, $date2, $date3, $date4); })->name('call-dolibarr');