api changes #16
@ -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)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -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 ?: '',
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user