Add personal application fields to members
This commit is contained in:
@@ -9,6 +9,7 @@ use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
|
||||
|
||||
class ImportMembersCommand extends Command
|
||||
{
|
||||
@@ -78,11 +79,17 @@ class ImportMembersCommand extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
$phone = $this->normalizePhone($row[6] ?? '');
|
||||
$email = $this->resolveEmail($name, $row[10] ?? '');
|
||||
$nationalId = trim($row[7] ?? '');
|
||||
$memberNumber = $this->normalizeMemberNumber($row[0] ?? '');
|
||||
$birthDate = $this->normalizeRocDate($row[2] ?? '');
|
||||
$gender = $this->normalizeGender($row[3] ?? '');
|
||||
$occupation = trim($row[4] ?? '');
|
||||
$address = trim($row[5] ?? '');
|
||||
$memberType = $this->mapMemberType($row[8] ?? '');
|
||||
$phone = $this->normalizePhone($row[6] ?? '');
|
||||
$nationalId = trim($row[7] ?? '');
|
||||
[$identityType, $identityOtherText] = $this->parseIdentityType($row[8] ?? '');
|
||||
$applyDate = $this->normalizeRocDate($row[9] ?? '');
|
||||
$email = $this->resolveEmail($name, $row[10] ?? '');
|
||||
$memberType = Member::TYPE_INDIVIDUAL;
|
||||
|
||||
// Validate phone for password generation
|
||||
if (strlen($phone) < 4) {
|
||||
@@ -102,6 +109,15 @@ class ImportMembersCommand extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($memberNumber) {
|
||||
$existingMemberNumber = Member::where('member_number', $memberNumber)->first();
|
||||
if ($existingMemberNumber) {
|
||||
$this->warn("Row {$rowNum}: {$name} - Member number already exists: {$memberNumber}");
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing member by national ID
|
||||
if ($nationalId) {
|
||||
$existingMember = Member::where('national_id_hash', hash('sha256', $nationalId))->first();
|
||||
@@ -128,13 +144,24 @@ class ImportMembersCommand extends Command
|
||||
// Create Member
|
||||
$member = Member::create([
|
||||
'user_id' => $user->id,
|
||||
'member_number' => $memberNumber ?: null,
|
||||
'full_name' => $name,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'phone_home' => null,
|
||||
'phone_fax' => null,
|
||||
'address_line_1' => $address,
|
||||
'national_id' => $nationalId ?: null,
|
||||
'membership_status' => Member::STATUS_ACTIVE,
|
||||
'membership_type' => $memberType,
|
||||
'identity_type' => $identityType,
|
||||
'identity_other_text' => $identityOtherText ?: null,
|
||||
'birth_date' => $birthDate,
|
||||
'gender' => $gender,
|
||||
'occupation' => $occupation ?: null,
|
||||
'employer' => null,
|
||||
'job_title' => null,
|
||||
'applied_at' => $applyDate,
|
||||
'membership_started_at' => now(),
|
||||
'membership_expires_at' => now()->endOfYear(), // 2026-12-31
|
||||
]);
|
||||
@@ -277,18 +304,96 @@ class ImportMembersCommand extends Command
|
||||
return $phone;
|
||||
}
|
||||
|
||||
protected function mapMemberType(string $type): string
|
||||
protected function normalizeMemberNumber(mixed $value): ?string
|
||||
{
|
||||
// Map Chinese member types to constants
|
||||
$type = trim($type);
|
||||
return match ($type) {
|
||||
'榮譽會員' => Member::TYPE_HONORARY,
|
||||
'終身會員' => Member::TYPE_LIFETIME,
|
||||
'學生會員' => Member::TYPE_STUDENT,
|
||||
default => Member::TYPE_REGULAR, // 患者, 家屬, etc.
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$raw = is_numeric($value) ? (string) (int) $value : trim((string) $value);
|
||||
return $raw !== '' ? $raw : null;
|
||||
}
|
||||
|
||||
protected function normalizeGender(mixed $value): ?string
|
||||
{
|
||||
$value = trim((string) $value);
|
||||
return match ($value) {
|
||||
'男' => 'male',
|
||||
'女' => 'female',
|
||||
'其他' => 'other',
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
protected function normalizeRocDate(mixed $value): ?string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return $value->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
$numeric = (float) $value;
|
||||
if ($numeric > 10000) {
|
||||
return ExcelDate::excelToDateTimeObject($numeric)->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
$raw = trim((string) $value);
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw)) {
|
||||
return $raw;
|
||||
}
|
||||
|
||||
if (preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $raw)) {
|
||||
return str_replace('/', '-', $raw);
|
||||
}
|
||||
|
||||
$digits = preg_replace('/\D/', '', $raw);
|
||||
if (strlen($digits) === 6 || strlen($digits) === 7) {
|
||||
$yearLength = strlen($digits) - 4;
|
||||
$rocYear = (int) substr($digits, 0, $yearLength);
|
||||
$month = (int) substr($digits, $yearLength, 2);
|
||||
$day = (int) substr($digits, $yearLength + 2, 2);
|
||||
$year = $rocYear + 1911;
|
||||
|
||||
if (checkdate($month, $day, $year)) {
|
||||
return sprintf('%04d-%02d-%02d', $year, $month, $day);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function parseIdentityType(mixed $value): array
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
|
||||
if ($raw === '') {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (str_contains($raw, '病友')) {
|
||||
return [Member::IDENTITY_PATIENT, null];
|
||||
}
|
||||
|
||||
if (str_contains($raw, '父母') || str_contains($raw, '家長')) {
|
||||
return [Member::IDENTITY_PARENT, null];
|
||||
}
|
||||
|
||||
if (str_contains($raw, '社會') || str_contains($raw, '學者') || str_contains($raw, '醫師')) {
|
||||
return [Member::IDENTITY_SOCIAL, null];
|
||||
}
|
||||
|
||||
if (str_contains($raw, '其他')) {
|
||||
return [Member::IDENTITY_OTHER, $raw];
|
||||
}
|
||||
|
||||
return [Member::IDENTITY_OTHER, $raw];
|
||||
}
|
||||
|
||||
protected function toPinyin(string $name): string
|
||||
{
|
||||
// Simple romanization for email generation
|
||||
|
||||
Reference in New Issue
Block a user