api changes

This commit is contained in:
mohanmd 2026-06-24 15:37:46 +05:30
parent ccd999643a
commit 3db4ced068
4 changed files with 230 additions and 45 deletions

View File

@ -497,9 +497,10 @@ public function register(Request $request)
// Create visa document if provided
if ($visaDataInput) {
$visaNum = $visaDataInput['entry_permit_no'] ?? $visaDataInput['file_number'] ?? $visaDataInput['id_number'] ?? ('V' . rand(1000000, 9999999));
$expiryDate = $visaDataInput['valid_until'] ?? $visaDataInput['expiry_date'] ?? null;
$issueDate = $visaDataInput['issue_date'] ?? null;
$cleanedVisaOcr = $this->cleanVisaData($visaDataInput);
$visaNum = $cleanedVisaOcr['entry_permit_no'] ?: ('V' . rand(1000000, 9999999));
$expiryDate = $cleanedVisaOcr['valid_until'] ?? null;
$issueDate = $cleanedVisaOcr['issue_date'] ?? null;
$expiryDateFormatted = $expiryDate ? $this->normaliseDateForController($expiryDate) : now()->addYears(2)->toDateString();
$issueDateFormatted = $issueDate ? $this->normaliseDateForController($issueDate) : now()->subYears(2)->toDateString();
@ -511,7 +512,7 @@ public function register(Request $request)
'expiry_date' => $expiryDateFormatted,
'ocr_accuracy' => isset($visaDataInput['ocr_accuracy']) ? (float)$visaDataInput['ocr_accuracy'] : 99.0,
'file_path' => null,
'ocr_data' => $visaDataInput,
'ocr_data' => $cleanedVisaOcr,
]);
}
@ -1236,6 +1237,62 @@ private function normaliseDateForController(?string $raw): ?string
}
}
/**
* Standardize and clean Visa OCR data structure to exactly the 15 required fields.
*/
private function cleanVisaData(array $visaDataInput): array
{
$rawVisaType = $visaDataInput['visa_type'] ?? null;
$rawEntryPermitNo = $visaDataInput['entry_permit_no'] ?? $visaDataInput['file_number'] ?? $visaDataInput['id_number'] ?? $visaDataInput['number'] ?? null;
$rawIssueDate = $visaDataInput['issue_date'] ?? null;
$rawValidUntil = $visaDataInput['valid_until'] ?? $visaDataInput['expiry_date'] ?? null;
$rawUidNo = $visaDataInput['uid_no'] ?? null;
$rawFullName = $visaDataInput['full_name'] ?? $visaDataInput['name'] ?? null;
$rawNationality = $visaDataInput['nationality'] ?? null;
$rawPlaceOfBirth = $visaDataInput['place_of_birth'] ?? null;
$rawDateOfBirth = $visaDataInput['date_of_birth'] ?? $visaDataInput['dob'] ?? null;
$rawPassportNo = $visaDataInput['passport_no'] ?? $visaDataInput['passport_number'] ?? null;
$rawProfession = $visaDataInput['profession'] ?? null;
$rawSponsorName = $visaDataInput['sponsor_name'] ?? null;
// Clean sponsor
$sponsorData = $visaDataInput['sponsor'] ?? [];
if (is_string($sponsorData)) {
$sponsorNameFromObj = $sponsorData;
$sponsorAddress = '';
$sponsorPhone = '';
} else {
$sponsorNameFromObj = $sponsorData['name'] ?? $sponsorData['sponsor_name'] ?? $sponsorData['full_name'] ?? '';
$sponsorAddress = $sponsorData['address'] ?? '';
$sponsorPhone = $sponsorData['phone'] ?? $sponsorData['mobile'] ?? $sponsorData['mobile_number'] ?? $sponsorData['phone_number'] ?? '';
}
if (empty($rawSponsorName)) {
$rawSponsorName = $sponsorNameFromObj;
}
return [
'document_type' => 'uae_visa',
'country' => 'United Arab Emirates',
'visa_type' => $rawVisaType ?: 'ENTRY PERMIT',
'entry_permit_no' => $rawEntryPermitNo ?: '',
'issue_date' => $rawIssueDate ?: '',
'valid_until' => $rawValidUntil ?: '',
'uid_no' => $rawUidNo ?: '',
'full_name' => $rawFullName ?: '',
'nationality' => $rawNationality ?: '',
'place_of_birth' => $rawPlaceOfBirth ?: '',
'date_of_birth' => $rawDateOfBirth ?: '',
'passport_no' => $rawPassportNo ?: '',
'profession' => $rawProfession ?: '',
'sponsor_name' => $rawSponsorName ?: '',
'sponsor' => [
'name' => $sponsorNameFromObj ?: '',
'address' => $sponsorAddress ?: '',
'phone' => $sponsorPhone ?: '',
]
];
}
// -------------------------------------------------------------------------
// Forgot / Reset Password (via Mobile Number)
// -------------------------------------------------------------------------

View File

@ -201,14 +201,15 @@ public function updateProfile(Request $request)
$visaDataInput = $request->input('visa');
if ($visaDataInput) {
$existingVisa = $worker->documents()->where('type', 'visa')->first();
$expiryDate = $visaDataInput['expiry_date'] ?? null;
$issueDate = $visaDataInput['issue_date'] ?? null;
$cleanedVisaOcr = $this->cleanVisaData($visaDataInput);
$expiryDate = $cleanedVisaOcr['valid_until'] ?? null;
$issueDate = $cleanedVisaOcr['issue_date'] ?? null;
$expiryDateFormatted = $expiryDate ? $this->normaliseDateForController($expiryDate) : null;
$issueDateFormatted = $issueDate ? $this->normaliseDateForController($issueDate) : null;
if ($existingVisa) {
$updateFields = [
'ocr_data' => $visaDataInput,
'ocr_data' => $cleanedVisaOcr,
];
if ($expiryDateFormatted) {
$updateFields['expiry_date'] = $expiryDateFormatted;
@ -216,9 +217,12 @@ public function updateProfile(Request $request)
if ($issueDateFormatted) {
$updateFields['issue_date'] = $issueDateFormatted;
}
if (!empty($cleanedVisaOcr['entry_permit_no'])) {
$updateFields['number'] = $cleanedVisaOcr['entry_permit_no'];
}
$existingVisa->update($updateFields);
} else {
$visaNum = $visaDataInput['file_number'] ?? $visaDataInput['id_number'] ?? ('V' . rand(1000000, 9999999));
$visaNum = $cleanedVisaOcr['entry_permit_no'] ?: ('V' . rand(1000000, 9999999));
$expiryDateFormatted = $expiryDateFormatted ?: now()->addYears(2)->toDateString();
$issueDateFormatted = $issueDateFormatted ?: now()->subYears(2)->toDateString();
@ -227,9 +231,9 @@ public function updateProfile(Request $request)
'number' => $visaNum,
'issue_date' => $issueDateFormatted,
'expiry_date' => $expiryDateFormatted,
'ocr_accuracy' => 99.0,
'ocr_accuracy' => isset($visaDataInput['ocr_accuracy']) ? (float)$visaDataInput['ocr_accuracy'] : 99.0,
'file_path' => null,
'ocr_data' => $visaDataInput,
'ocr_data' => $cleanedVisaOcr,
]);
}
}
@ -754,4 +758,60 @@ private function normaliseDateForController(?string $raw): ?string
return $raw;
}
}
/**
* Standardize and clean Visa OCR data structure to exactly the 15 required fields.
*/
private function cleanVisaData(array $visaDataInput): array
{
$rawVisaType = $visaDataInput['visa_type'] ?? null;
$rawEntryPermitNo = $visaDataInput['entry_permit_no'] ?? $visaDataInput['file_number'] ?? $visaDataInput['id_number'] ?? $visaDataInput['number'] ?? null;
$rawIssueDate = $visaDataInput['issue_date'] ?? null;
$rawValidUntil = $visaDataInput['valid_until'] ?? $visaDataInput['expiry_date'] ?? null;
$rawUidNo = $visaDataInput['uid_no'] ?? null;
$rawFullName = $visaDataInput['full_name'] ?? $visaDataInput['name'] ?? null;
$rawNationality = $visaDataInput['nationality'] ?? null;
$rawPlaceOfBirth = $visaDataInput['place_of_birth'] ?? null;
$rawDateOfBirth = $visaDataInput['date_of_birth'] ?? $visaDataInput['dob'] ?? null;
$rawPassportNo = $visaDataInput['passport_no'] ?? $visaDataInput['passport_number'] ?? null;
$rawProfession = $visaDataInput['profession'] ?? null;
$rawSponsorName = $visaDataInput['sponsor_name'] ?? null;
// Clean sponsor
$sponsorData = $visaDataInput['sponsor'] ?? [];
if (is_string($sponsorData)) {
$sponsorNameFromObj = $sponsorData;
$sponsorAddress = '';
$sponsorPhone = '';
} else {
$sponsorNameFromObj = $sponsorData['name'] ?? $sponsorData['sponsor_name'] ?? $sponsorData['full_name'] ?? '';
$sponsorAddress = $sponsorData['address'] ?? '';
$sponsorPhone = $sponsorData['phone'] ?? $sponsorData['mobile'] ?? $sponsorData['mobile_number'] ?? $sponsorData['phone_number'] ?? '';
}
if (empty($rawSponsorName)) {
$rawSponsorName = $sponsorNameFromObj;
}
return [
'document_type' => 'uae_visa',
'country' => 'United Arab Emirates',
'visa_type' => $rawVisaType ?: 'ENTRY PERMIT',
'entry_permit_no' => $rawEntryPermitNo ?: '',
'issue_date' => $rawIssueDate ?: '',
'valid_until' => $rawValidUntil ?: '',
'uid_no' => $rawUidNo ?: '',
'full_name' => $rawFullName ?: '',
'nationality' => $rawNationality ?: '',
'place_of_birth' => $rawPlaceOfBirth ?: '',
'date_of_birth' => $rawDateOfBirth ?: '',
'passport_no' => $rawPassportNo ?: '',
'profession' => $rawProfession ?: '',
'sponsor_name' => $rawSponsorName ?: '',
'sponsor' => [
'name' => $sponsorNameFromObj ?: '',
'address' => $sponsorAddress ?: '',
'phone' => $sponsorPhone ?: '',
]
];
}
}

View File

@ -2564,14 +2564,80 @@
},
"visa": {
"type": "object",
"description": "Standardized UAE Visa metadata.",
"properties": {
"file_number": {
"document_type": {
"type": "string",
"example": "202/2022/2840234"
"example": "uae_visa"
},
"expiry_date": {
"country": {
"type": "string",
"example": "2028-12-31"
"example": "United Arab Emirates"
},
"visa_type": {
"type": "string",
"example": "ENTRY PERMIT"
},
"entry_permit_no": {
"type": "string",
"example": "87654091 / 2016 / 210"
},
"issue_date": {
"type": "string",
"example": "2016-11-18"
},
"valid_until": {
"type": "string",
"example": "2017-01-18"
},
"uid_no": {
"type": "string",
"example": "181825009"
},
"full_name": {
"type": "string",
"example": "GILBERT DSOUZA W/O TIMOTHY"
},
"nationality": {
"type": "string",
"example": "INDIAN"
},
"place_of_birth": {
"type": "string",
"example": "MANGALORE"
},
"date_of_birth": {
"type": "string",
"example": "1980-02-02"
},
"passport_no": {
"type": "string",
"example": "U4690639"
},
"profession": {
"type": "string",
"example": "BUSINESS PERSON"
},
"sponsor_name": {
"type": "string",
"example": "Sponsor LLC"
},
"sponsor": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "Sponsor LLC"
},
"address": {
"type": "string",
"example": "Dubai Marina, Dubai"
},
"phone": {
"type": "string",
"example": "2977725"
}
}
}
}
}

View File

@ -676,51 +676,57 @@ export default function Show({ worker }) {
)}
</div>
<div className="p-6">
{visaDoc.ocr_data ? (() => {
{visaDoc.ocr_data ? (() => {
const sponsorObj = visaDoc.ocr_data?.sponsor;
const isSponsorObj = sponsorObj && typeof sponsorObj === 'object' && !Array.isArray(sponsorObj);
const sponsorName = isSponsorObj
? (sponsorObj.name || sponsorObj.sponsor_name || sponsorObj.full_name)
: (visaDoc.ocr_data?.sponsor_name || (typeof sponsorObj === 'string' ? sponsorObj : null));
const sponsorMobile = isSponsorObj
? (sponsorObj.mobile || sponsorObj.mobile_number || sponsorObj.phone)
: null;
? (sponsorObj.name || sponsorObj.sponsor_name)
: (visaDoc.ocr_data?.sponsor_name || (typeof sponsorObj === 'string' ? sponsorObj : ''));
const sponsorPhone = isSponsorObj
? (sponsorObj.phone || sponsorObj.mobile || sponsorObj.mobile_number)
: '';
const sponsorAddress = isSponsorObj
? (sponsorObj.address || '')
: '';
return (
<div className="grid grid-cols-2 md:grid-cols-3 gap-y-4 gap-x-6">
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('full_name', 'Full Name')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.full_name || worker.name)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('entry_permit_no', 'Entry Permit No')}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data.entry_permit_no)}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data.entry_permit_no || visaDoc.number)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('uid_no', 'UID No')}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data.uid_no)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('id_number', 'ID Number')}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data.id_number || visaDoc.ocr_data.emirates_id_number)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('visa_type', 'Visa Type')}</div>
<div className="text-xs font-bold text-slate-800 uppercase">{safeRender(visaDoc.ocr_data.visa_type || 'ENTRY PERMIT')}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('passport_no', 'Passport No')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.passport_no || visaDoc.ocr_data.passport_number)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('profession', 'Profession')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.profession || worker.visa_status)}</div>
</div>
{sponsorName && (
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('sponsor_name', 'Sponsor Name')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(sponsorName)}</div>
</div>
)}
{sponsorMobile && (
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('sponsor_mobile', 'Sponsor Mobile')}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(sponsorMobile)}</div>
</div>
)}
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('sponsor_name', 'Sponsor Name')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(sponsorName || visaDoc.ocr_data.sponsor_name)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('sponsor_phone', 'Sponsor Phone')}</div>
<div className="text-xs font-mono font-bold text-slate-800">{safeRender(sponsorPhone)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('sponsor_address', 'Sponsor Address')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(sponsorAddress)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('nationality', 'Nationality')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.nationality || worker.nationality)}</div>
@ -737,17 +743,13 @@ export default function Show({ worker }) {
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('place_of_birth', 'Place of Birth')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.place_of_birth)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('place_of_issue', 'Place of Issue')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.place_of_issue)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('issue_date', 'Issue Date')}</div>
<div className="text-xs font-bold text-slate-800">{safeRender(visaDoc.ocr_data.issue_date || visaDoc.issue_date)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('expiry_date', 'Expiry Date')}</div>
<div className="text-xs font-bold text-emerald-600">{safeRender(visaDoc.ocr_data.expiry_date || visaDoc.expiry_date)}</div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('valid_until', 'Valid Until')}</div>
<div className="text-xs font-bold text-emerald-600">{safeRender(visaDoc.ocr_data.valid_until || visaDoc.ocr_data.expiry_date || visaDoc.expiry_date)}</div>
</div>
<div>
<div className="text-[8px] font-black text-slate-400 uppercase tracking-tighter">{t('document_type', 'Document Type')}</div>
@ -758,7 +760,7 @@ export default function Show({ worker }) {
<div className="text-xs font-bold text-emerald-600">{(visaDoc.ocr_accuracy || visaDoc.ocr_data?.ocr_accuracy || 99)}%</div>
</div>
</div>
);
);
})() : (
<div className="grid grid-cols-2 md:grid-cols-3 gap-y-4 gap-x-6">
<div>