employer profile api issue fixed

This commit is contained in:
mohanmd 2026-06-23 18:27:14 +05:30
parent ac4cc64cb6
commit 72da3c5b8d
3 changed files with 348 additions and 85 deletions

View File

@ -43,6 +43,17 @@ public function getProfile(Request $request)
'verification_status' => $profile->verification_status ?? 'approved', 'verification_status' => $profile->verification_status ?? 'approved',
'emirates_id_number' => $profile->emirates_id_number, 'emirates_id_number' => $profile->emirates_id_number,
'emirates_id_expiry' => $profile->emirates_id_expiry, 'emirates_id_expiry' => $profile->emirates_id_expiry,
'id_number' => $profile->emirates_id_number,
'card_number' => $profile->emirates_id_card_number,
'full_name' => $profile->emirates_id_name,
'date_of_birth' => $profile->emirates_id_dob,
'nationality' => $profile->nationality,
'gender' => $profile->emirates_id_gender,
'issue_date' => $profile->emirates_id_issue_date,
'expiry_date' => $profile->emirates_id_expiry,
'occupation' => $profile->emirates_id_occupation,
'employer' => $profile->emirates_id_employer,
'issuing_place' => $profile->emirates_id_issue_place,
]; ];
return response()->json([ return response()->json([
@ -94,10 +105,21 @@ public function updateProfile(Request $request)
$sponsor ? 'unique:sponsors,mobile,' . $sponsor->id : 'unique:sponsors,mobile', $sponsor ? 'unique:sponsors,mobile,' . $sponsor->id : 'unique:sponsors,mobile',
], ],
'address' => 'required|string|max:255', 'address' => 'required|string|max:255',
'notifications' => 'required|boolean', 'notifications' => 'nullable|boolean',
'current_password' => 'nullable|required_with:new_password|string',
'new_password' => 'nullable|string|min:8|confirmed',
'fcm_token' => 'nullable|string|max:255', 'fcm_token' => 'nullable|string|max:255',
// Emirates ID optional fields
'id_number' => 'nullable|string|max:255',
'card_number' => 'nullable|string|max:255',
'full_name' => 'nullable|string|max:255',
'date_of_birth' => 'nullable|string|max:255',
'nationality' => 'nullable|string|max:255',
'gender' => 'nullable|string|max:255',
'issue_date' => 'nullable|string|max:255',
'expiry_date' => 'nullable|string|max:255',
'occupation' => 'nullable|string|max:255',
'employer' => 'nullable|string|max:255',
'issuing_place' => 'nullable|string|max:255',
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
@ -109,14 +131,15 @@ public function updateProfile(Request $request)
} }
try { try {
// Check current password if new password is provided // Validate Emirates ID immutability — once set, it cannot be changed
if ($request->filled('new_password')) { if ($request->filled('id_number')) {
if (!Hash::check($request->current_password, $employer->password)) { $existingProfile = $employer->employerProfile;
if ($existingProfile && $existingProfile->emirates_id_number && $existingProfile->emirates_id_number !== $request->id_number) {
return response()->json([ return response()->json([
'success' => false, 'success' => false,
'message' => 'The provided current password does not match.', 'message' => 'Emirates ID mismatch. You cannot change your registered Emirates ID number.',
'errors' => [ 'errors' => [
'current_password' => ['The provided password does not match your current password.'] 'id_number' => ['Emirates ID mismatch.']
] ]
], 422); ], 422);
} }
@ -138,12 +161,48 @@ public function updateProfile(Request $request)
// Sync with corresponding sponsor record if found // Sync with corresponding sponsor record if found
$matchingSponsor = \App\Models\Sponsor::where('email', $oldEmail)->first(); $matchingSponsor = \App\Models\Sponsor::where('email', $oldEmail)->first();
if ($matchingSponsor) { if ($matchingSponsor) {
$matchingSponsor->update([ $sponsorData = [
'full_name' => $request->name, 'full_name' => $request->name,
'email' => $request->email, 'email' => $request->email,
'mobile' => $request->phone, 'mobile' => $request->phone,
'address' => $request->address, 'address' => $request->address,
]); ];
if ($request->has('id_number')) {
$sponsorData['emirates_id'] = $request->id_number;
}
if ($request->has('card_number')) {
$sponsorData['emirates_id_card_number'] = $request->card_number;
}
if ($request->has('full_name')) {
$sponsorData['emirates_id_name'] = $request->full_name;
}
if ($request->has('date_of_birth')) {
$sponsorData['emirates_id_dob'] = $request->date_of_birth;
}
if ($request->has('nationality')) {
$sponsorData['nationality'] = $request->nationality;
}
if ($request->has('gender')) {
$sponsorData['emirates_id_gender'] = $request->gender;
}
if ($request->has('issue_date')) {
$sponsorData['emirates_id_issue_date'] = $request->issue_date;
}
if ($request->has('expiry_date')) {
$sponsorData['emirates_id_expiry'] = $request->expiry_date;
}
if ($request->has('occupation')) {
$sponsorData['emirates_id_occupation'] = $request->occupation;
}
if ($request->has('employer')) {
$sponsorData['emirates_id_employer'] = $request->employer;
}
if ($request->has('issuing_place')) {
$sponsorData['emirates_id_issue_place'] = $request->issuing_place;
}
$matchingSponsor->update($sponsorData);
} }
// Update employer_profiles table // Update employer_profiles table
@ -153,15 +212,43 @@ public function updateProfile(Request $request)
} }
$profile->address = $request->address; $profile->address = $request->address;
$profile->phone = $request->phone; $profile->phone = $request->phone;
$profile->notifications = $request->notifications; $profile->notifications = $request->has('notifications') ? $request->notifications : ($profile->notifications ?? true);
$profile->save();
// Update password if present if ($request->has('id_number')) {
if ($request->filled('new_password')) { $profile->emirates_id_number = $request->id_number;
$employer->update([
'password' => Hash::make($request->new_password),
]);
} }
if ($request->has('card_number')) {
$profile->emirates_id_card_number = $request->card_number;
}
if ($request->has('full_name')) {
$profile->emirates_id_name = $request->full_name;
}
if ($request->has('date_of_birth')) {
$profile->emirates_id_dob = $request->date_of_birth;
}
if ($request->has('nationality')) {
$profile->nationality = $request->nationality;
}
if ($request->has('gender')) {
$profile->emirates_id_gender = $request->gender;
}
if ($request->has('issue_date')) {
$profile->emirates_id_issue_date = $request->issue_date;
}
if ($request->has('expiry_date')) {
$profile->emirates_id_expiry = $request->expiry_date;
}
if ($request->has('occupation')) {
$profile->emirates_id_occupation = $request->occupation;
}
if ($request->has('employer')) {
$profile->emirates_id_employer = $request->employer;
}
if ($request->has('issuing_place')) {
$profile->emirates_id_issue_place = $request->issuing_place;
}
$profile->save();
$employerProfile = [ $employerProfile = [
'name' => $employer->name, 'name' => $employer->name,
@ -172,6 +259,17 @@ public function updateProfile(Request $request)
'verification_status' => $profile->verification_status ?? 'approved', 'verification_status' => $profile->verification_status ?? 'approved',
'emirates_id_number' => $profile->emirates_id_number, 'emirates_id_number' => $profile->emirates_id_number,
'emirates_id_expiry' => $profile->emirates_id_expiry, 'emirates_id_expiry' => $profile->emirates_id_expiry,
'id_number' => $profile->emirates_id_number,
'card_number' => $profile->emirates_id_card_number,
'full_name' => $profile->emirates_id_name,
'date_of_birth' => $profile->emirates_id_dob,
'nationality' => $profile->nationality,
'gender' => $profile->emirates_id_gender,
'issue_date' => $profile->emirates_id_issue_date,
'expiry_date' => $profile->emirates_id_expiry,
'occupation' => $profile->emirates_id_occupation,
'employer' => $profile->emirates_id_employer,
'issuing_place' => $profile->emirates_id_issue_place,
]; ];
return response()->json([ return response()->json([

View File

@ -23,7 +23,7 @@
"Sponsor/Auth" "Sponsor/Auth"
], ],
"summary": "Register Sponsor Account", "summary": "Register Sponsor Account",
"description": "Registers a new sponsor with basic information and uploads their organization/trade license. Sponsors have restricted access dashboard and charity events only. No payment required.", "description": "Registers a new sponsor with basic information and uploads their organization/trade license. Sponsors have restricted access \u2014 dashboard and charity events only. No payment required.",
"security": [], "security": [],
"requestBody": { "requestBody": {
"required": true, "required": true,
@ -51,7 +51,7 @@
"mobile": { "mobile": {
"type": "string", "type": "string",
"example": "+971501112233", "example": "+971501112233",
"description": "Mobile phone number — must be unique. (REQUIRED)" "description": "Mobile phone number \u00e2\u20ac\u201d must be unique. (REQUIRED)"
}, },
"password": { "password": {
"type": "string", "type": "string",
@ -600,7 +600,7 @@
"Worker/Auth" "Worker/Auth"
], ],
"summary": "Register Worker Account (Step 1)", "summary": "Register Worker Account (Step 1)",
"description": "Creates the worker account and returns a secure Bearer token.\n\nDocument upload is a **separate step** after registration:\n- `POST /workers/register/passport` — upload passport (OCR-processed)\n- `POST /workers/register/visa` — upload visa (OCR-processed)\n\nBoth document endpoints require the Bearer token returned here.", "description": "Creates the worker account and returns a secure Bearer token.\n\nDocument upload is a **separate step** after registration:\n- `POST /workers/register/passport` \u2014 upload passport (OCR-processed)\n- `POST /workers/register/visa` \u2014 upload visa (OCR-processed)\n\nBoth document endpoints require the Bearer token returned here.",
"security": [], "security": [],
"requestBody": { "requestBody": {
"required": true, "required": true,
@ -746,7 +746,7 @@
"ocr_accuracy": { "ocr_accuracy": {
"type": "number", "type": "number",
"format": "float", "format": "float",
"example": 99.0 "example": 99
}, },
"passport_number": { "passport_number": {
"type": "string" "type": "string"
@ -816,7 +816,7 @@
"ocr_accuracy": { "ocr_accuracy": {
"type": "number", "type": "number",
"format": "float", "format": "float",
"example": 99.0 "example": 99
}, },
"passport_no": { "passport_no": {
"type": "string", "type": "string",
@ -895,7 +895,7 @@
} }
}, },
"422": { "422": {
"description": "Validation error field validation failed or Passport OCR extraction failed.", "description": "Validation error \u2014 field validation failed or Passport OCR extraction failed.",
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
@ -1182,7 +1182,7 @@
}, },
"name": { "name": {
"type": "string", "type": "string",
"example": "Hindi (हिन्दी)" "example": "Hindi (\u0939\u093f\u0928\u094d\u0926\u0940)"
} }
} }
} }
@ -3689,6 +3689,50 @@
"emirates_id_expiry": { "emirates_id_expiry": {
"type": "string", "type": "string",
"example": "2028-04-11" "example": "2028-04-11"
},
"id_number": {
"type": "string",
"example": "784-1987-5493842-5"
},
"card_number": {
"type": "string",
"example": "151023946"
},
"full_name": {
"type": "string",
"example": "Mohammad Jobaier Mohammad Abul Kalam"
},
"date_of_birth": {
"type": "string",
"example": "14/04/1987"
},
"nationality": {
"type": "string",
"example": "Bangladesh"
},
"gender": {
"type": "string",
"example": "M"
},
"issue_date": {
"type": "string",
"example": "12/12/2025"
},
"expiry_date": {
"type": "string",
"example": "11/12/2027"
},
"occupation": {
"type": "string",
"example": "Electrician"
},
"employer": {
"type": "string",
"example": "Msj International Technical Services L.L.C UAE"
},
"issuing_place": {
"type": "string",
"example": "Dubai"
} }
} }
} }
@ -3708,7 +3752,7 @@
"Employer/Profile" "Employer/Profile"
], ],
"summary": "Update Employer Profile Details", "summary": "Update Employer Profile Details",
"description": "Updates the authenticated employer's profile details.", "description": "Updates the authenticated employer's profile details including optional Emirates ID metadata. Password changes are not supported via this endpoint.",
"requestBody": { "requestBody": {
"required": true, "required": true,
"content": { "content": {
@ -3719,8 +3763,7 @@
"name", "name",
"email", "email",
"phone", "phone",
"address", "address"
"notifications"
], ],
"properties": { "properties": {
"name": { "name": {
@ -3741,24 +3784,69 @@
}, },
"notifications": { "notifications": {
"type": "boolean", "type": "boolean",
"example": true "example": true,
}, "description": "Optional. Enable/disable push notifications. Defaults to true if not provided.",
"current_password": { "default": true
"type": "string",
"example": "Password@123"
},
"new_password": {
"type": "string",
"example": "NewSecurePassword@123"
},
"new_password_confirmation": {
"type": "string",
"example": "NewSecurePassword@123"
}, },
"fcm_token": { "fcm_token": {
"type": "string", "type": "string",
"example": "fcm_token_example", "example": "fcm_token_example",
"description": "Firebase Cloud Messaging token for push notifications." "description": "Firebase Cloud Messaging token for push notifications."
},
"id_number": {
"type": "string",
"example": "784-1987-5493842-5",
"description": "Emirates ID card number/ID number (optional)"
},
"card_number": {
"type": "string",
"example": "151023946",
"description": "Emirates ID card physical serial number (optional)"
},
"full_name": {
"type": "string",
"example": "Mohammad Jobaier Mohammad Abul Kalam",
"description": "Emirates ID full name (optional)"
},
"date_of_birth": {
"type": "string",
"example": "14/04/1987",
"description": "Emirates ID date of birth (optional)"
},
"nationality": {
"type": "string",
"example": "Bangladesh",
"description": "Emirates ID nationality (optional)"
},
"gender": {
"type": "string",
"example": "M",
"description": "Emirates ID gender (optional)"
},
"issue_date": {
"type": "string",
"example": "12/12/2025",
"description": "Emirates ID card issue date (optional)"
},
"expiry_date": {
"type": "string",
"example": "11/12/2027",
"description": "Emirates ID card expiry date (optional)"
},
"occupation": {
"type": "string",
"example": "Electrician",
"description": "Emirates ID card holder occupation (optional)"
},
"employer": {
"type": "string",
"example": "Msj International Technical Services L.L.C UAE",
"description": "Emirates ID card holder sponsor/employer name (optional)"
},
"issuing_place": {
"type": "string",
"example": "Dubai",
"description": "Emirates ID card place of issue (optional)"
} }
} }
} }
@ -3818,6 +3906,50 @@
"emirates_id_expiry": { "emirates_id_expiry": {
"type": "string", "type": "string",
"example": "2028-04-11" "example": "2028-04-11"
},
"id_number": {
"type": "string",
"example": "784-1987-5493842-5"
},
"card_number": {
"type": "string",
"example": "151023946"
},
"full_name": {
"type": "string",
"example": "Mohammad Jobaier Mohammad Abul Kalam"
},
"date_of_birth": {
"type": "string",
"example": "14/04/1987"
},
"nationality": {
"type": "string",
"example": "Bangladesh"
},
"gender": {
"type": "string",
"example": "M"
},
"issue_date": {
"type": "string",
"example": "12/12/2025"
},
"expiry_date": {
"type": "string",
"example": "11/12/2027"
},
"occupation": {
"type": "string",
"example": "Electrician"
},
"employer": {
"type": "string",
"example": "Msj International Technical Services L.L.C UAE"
},
"issuing_place": {
"type": "string",
"example": "Dubai"
} }
} }
} }
@ -5124,7 +5256,7 @@
"Workers - Auth" "Workers - Auth"
], ],
"summary": "Forgot Password (Send OTP)", "summary": "Forgot Password (Send OTP)",
"description": "Sends a 6-digit OTP to verify the worker's identity using their registered mobile number. Workers do not use email mobile/phone is the primary identifier. OTP is valid for 10 minutes.", "description": "Sends a 6-digit OTP to verify the worker's identity using their registered mobile number. Workers do not use email \u2014 mobile/phone is the primary identifier. OTP is valid for 10 minutes.",
"operationId": "workerForgotPassword", "operationId": "workerForgotPassword",
"requestBody": { "requestBody": {
"required": true, "required": true,
@ -5188,7 +5320,7 @@
} }
}, },
"422": { "422": {
"description": "Validation error phone is required" "description": "Validation error \u2014 phone is required"
} }
} }
} }
@ -5416,7 +5548,7 @@
} }
}, },
"422": { "422": {
"description": "Validation error email or mobile required" "description": "Validation error \u2014 email or mobile required"
} }
} }
} }
@ -5577,7 +5709,10 @@
"items": { "items": {
"type": "string" "type": "string"
}, },
"example": ["English", "Arabic"] "example": [
"English",
"Arabic"
]
}, },
"fcm_token": { "fcm_token": {
"type": "string", "type": "string",

View File

@ -109,58 +109,88 @@ public function test_employer_can_update_profile()
} }
/** /**
* Test employer can update password when providing correct current password. * Test employer can update profile with optional Emirates ID fields.
*/ */
public function test_employer_can_update_password() public function test_employer_can_update_profile_with_emirates_id_fields()
{ {
$response = $this->withHeaders([ $response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->token, 'Authorization' => 'Bearer ' . $this->token,
])->postJson('/api/employers/profile/update', [ ])->postJson('/api/employers/profile/update', [
'name' => 'Original Name', 'name' => 'Ahmad',
'email' => 'employer@example.com', 'email' => 'ahmad@example.com',
'phone' => '+971501112222', 'phone' => '+971509990001',
'address' => 'Original Address', 'address' => 'Villa 12, Jumeirah 2',
'notifications' => true, 'notifications' => true,
'current_password' => 'Password@123', 'id_number' => '784-1987-5493842-5',
'new_password' => 'NewSecurePassword@123', 'card_number' => '151023946',
'new_password_confirmation' => 'NewSecurePassword@123', 'full_name' => 'Mohammad Jobaier Mohammad Abul Kalam',
'date_of_birth' => '14/04/1987',
'nationality' => 'Bangladesh',
'gender' => 'M',
'issue_date' => '12/12/2025',
'expiry_date' => '11/12/2027',
'occupation' => 'Electrician',
'employer' => 'Msj International Technical Services L.L.C UAE',
'issuing_place' => 'Dubai',
]); ]);
$response->assertStatus(200); $response->assertStatus(200)
->assertJson([
// Verify password hash updated 'success' => true,
$this->assertTrue(Hash::check('NewSecurePassword@123', $this->employer->fresh()->password)); 'data' => [
} 'profile' => [
'name' => 'Ahmad',
/** 'email' => 'ahmad@example.com',
* Test employer password update fails with incorrect current password. 'phone' => '+971509990001',
*/ 'address' => 'Villa 12, Jumeirah 2',
public function test_employer_password_update_fails_with_incorrect_current_password() 'id_number' => '784-1987-5493842-5',
{ 'card_number' => '151023946',
$response = $this->withHeaders([ 'full_name' => 'Mohammad Jobaier Mohammad Abul Kalam',
'Authorization' => 'Bearer ' . $this->token, 'gender' => 'M',
])->postJson('/api/employers/profile/update', [ ]
'name' => 'Original Name',
'email' => 'employer@example.com',
'phone' => '+971501112222',
'address' => 'Original Address',
'notifications' => true,
'current_password' => 'WrongCurrentPassword',
'new_password' => 'NewSecurePassword@123',
'new_password_confirmation' => 'NewSecurePassword@123',
]);
$response->assertStatus(422)
->assertJsonStructure([
'success',
'message',
'errors' => [
'current_password'
] ]
]); ]);
// Verify password was NOT changed $this->assertDatabaseHas('employer_profiles', [
$this->assertTrue(Hash::check('Password@123', $this->employer->fresh()->password)); 'user_id' => $this->employer->id,
'emirates_id_number' => '784-1987-5493842-5',
'emirates_id_card_number' => '151023946',
'emirates_id_name' => 'Mohammad Jobaier Mohammad Abul Kalam',
'emirates_id_gender' => 'M',
]);
}
/**
* Test employer cannot change an existing Emirates ID number.
*/
public function test_employer_cannot_change_existing_emirates_id_number()
{
// First, set the Emirates ID
$profile = $this->employer->employerProfile;
$profile->emirates_id_number = '784-1987-5493842-5';
$profile->save();
// Attempt to change to a different ID
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->token,
])->postJson('/api/employers/profile/update', [
'name' => 'Original Name',
'email' => 'employer@example.com',
'phone' => '+971501112222',
'address' => 'Original Address',
'id_number' => '784-9999-0000000-0',
]);
$response->assertStatus(422)
->assertJson([
'success' => false,
'errors' => [
'id_number' => ['Emirates ID mismatch.']
]
]);
// Verify ID was NOT changed
$this->assertEquals('784-1987-5493842-5', $profile->fresh()->emirates_id_number);
} }
/** /**