update: scanner submit struk
parent
b5056edfa4
commit
86647ef04e
|
@ -7,7 +7,7 @@
|
||||||
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
<div class="max-w-sm mx-auto bg-white min-h-screen">
|
||||||
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||||
|
@ -26,16 +26,26 @@
|
||||||
</div>
|
</div>
|
||||||
<h2 class="text-xl font-bold text-orange-500">Scan Struk Otomatis</h2>
|
<h2 class="text-xl font-bold text-orange-500">Scan Struk Otomatis</h2>
|
||||||
<p class="text-sm text-gray-500 text-center">Arahkan kamera ke struk atau upload foto struk untuk membaca data secara otomatis.</p>
|
<p class="text-sm text-gray-500 text-center">Arahkan kamera ke struk atau upload foto struk untuk membaca data secara otomatis.</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="ocr-processing" class="hidden bg-yellow-50 border border-yellow-200 rounded-lg p-3">
|
<div id="ocr-processing" class="hidden bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="loading-spinner-small mr-2"></div>
|
<div class="loading-spinner-small mr-2"></div>
|
||||||
<span class="text-yellow-800 text-sm">Memproses teks dari struk...</span>
|
<span class="text-yellow-800 text-sm">Memproses teks dari struk...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="scan-success" class="hidden bg-green-50 border border-green-200 rounded-lg p-3 mb-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="w-5 h-5 text-green-600 mr-2" data-lucide="check-circle"></i>
|
||||||
|
<span class="text-green-800 font-medium">Data struk berhasil diisi otomatis!</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-green-700 text-sm mt-1">Scanning dihentikan. Periksa form di bawah dan lengkapi data jika diperlukan.</p>
|
||||||
|
<p class="text-green-700 text-xs mt-1">💡 Untuk scan ulang, "Upload Foto Struk"</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="scanner-container mb-4" style="height: 300px;">
|
<div class="scanner-container mb-4" style="height: 300px;">
|
||||||
<div id="scanner-container" class="w-full h-full relative bg-gray-900 rounded-lg overflow-hidden">
|
<div id="scanner-container" class="w-full h-full relative bg-gray-900 rounded-lg overflow-hidden">
|
||||||
<div id="loading-scanner" class="absolute inset-0 bg-gray-900 flex items-center justify-center z-10">
|
<div id="loading-scanner" class="absolute inset-0 bg-gray-900 flex items-center justify-center z-10">
|
||||||
|
@ -48,10 +58,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-3 mb-4">
|
<div class="space-y-3 mb-4">
|
||||||
<button id="start-scanner" type="button" class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner">
|
@* <button id="start-scanner" type="button" class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner">
|
||||||
<i class="w-5 h-5 inline mr-2" data-lucide="camera"></i>
|
<i class="w-5 h-5 inline mr-2" data-lucide="camera"></i>
|
||||||
Mulai Scan Struk
|
Mulai Scan Struk
|
||||||
</button>
|
</button> *@
|
||||||
|
|
||||||
<button id="stop-scanner" type="button" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner hidden">
|
<button id="stop-scanner" type="button" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner hidden">
|
||||||
<i class="w-5 h-5 inline mr-2" data-lucide="camera-off"></i>
|
<i class="w-5 h-5 inline mr-2" data-lucide="camera-off"></i>
|
||||||
|
@ -59,11 +69,11 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex items-center">
|
@* <div class="flex items-center">
|
||||||
<div class="flex-1 border-t border-gray-200"></div>
|
<div class="flex-1 border-t border-gray-200"></div>
|
||||||
<span class="px-3 text-sm text-gray-500 bg-white">atau</span>
|
<span class="px-3 text-sm text-gray-500 bg-white">atau</span>
|
||||||
<div class="flex-1 border-t border-gray-200"></div>
|
<div class="flex-1 border-t border-gray-200"></div>
|
||||||
</div>
|
</div> *@
|
||||||
<label for="file-upload" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-4 rounded-lg transition-colors cursor-pointer flex items-center justify-center gap-2 upload-label">
|
<label for="file-upload" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-4 rounded-lg transition-colors cursor-pointer flex items-center justify-center gap-2 upload-label">
|
||||||
<i class="w-5 h-5" data-lucide="upload"></i>
|
<i class="w-5 h-5" data-lucide="upload"></i>
|
||||||
Upload Foto Struk
|
Upload Foto Struk
|
||||||
|
@ -110,17 +120,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="scan-success" class="hidden bg-green-50 border border-green-200 rounded-lg p-3">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<i class="w-5 h-5 text-green-600 mr-2" data-lucide="check-circle"></i>
|
|
||||||
<span class="text-green-800 font-medium">Data struk berhasil diisi otomatis!</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-green-700 text-sm mt-1">Scanning dihentikan. Periksa form di bawah dan lengkapi data jika diperlukan.</p>
|
|
||||||
<p class="text-green-700 text-xs mt-1">💡 Untuk scan ulang, klik "Mulai Scan Struk" atau "Upload Foto Struk"</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -253,7 +252,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<register-block dynamic-section="scripts" key="jsSubmitStruk">
|
<register-block dynamic-section="scripts" key="jsSubmitStruk">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5.1.1/dist/tesseract.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.1/dist/tesseract.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||||
|
@ -667,150 +666,239 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!truckNumber) {
|
if (!truckNumber) {
|
||||||
for (const line of lines) {
|
console.log('No truck number found in context lines, trying all lines...');
|
||||||
if (line.toLowerCase().includes('no truk')) {
|
for (const line of lines) {
|
||||||
const match = line.match(/no\s*truk\s*:?\s*(.+)/i);
|
console.log(`Scanning line for truck pattern: "${line}"`);
|
||||||
if (match && match[1]) {
|
|
||||||
truckNumber = match[1].trim();
|
if (line.match(/[A-Z]\s*\d+\s*[A-Z]{2,3}/i)) {
|
||||||
console.log(`Found truck number: ${truckNumber} (ambil semua karakter setelah label No Truk)`);
|
console.log('Potential truck pattern found:', line);
|
||||||
break;
|
|
||||||
}
|
for (const pattern of truckPatterns) {
|
||||||
}
|
const match = line.match(pattern);
|
||||||
}
|
if (match) {
|
||||||
if (!truckNumber) {
|
let foundTruck = '';
|
||||||
for (const line of lines) {
|
|
||||||
const match = line.match(/\bB\s*\d{3,5}\s*[A-Z]{2,4}\b/i);
|
if (match.length === 4 && match[1] && match[2] && match[3]) {
|
||||||
if (match && match[0]) {
|
foundTruck = `${match[1]} ${match[2]} ${match[3]}`;
|
||||||
truckNumber = match[0].trim();
|
} else if (match[1]) {
|
||||||
console.log(`Fallback truck number: ${truckNumber}`);
|
foundTruck = match[1].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundTruck) {
|
||||||
|
foundTruck = foundTruck.replace(/([A-Z])(\d+)(\s+[A-Z]{2,3})/g, '$1 $2$3');
|
||||||
|
foundTruck = foundTruck.replace(/([A-Z]{1,2})(\d{3,4})([A-Z]{2,3})/g, '$1 $2 $3');
|
||||||
|
foundTruck = foundTruck.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
if (foundTruck.match(/^[A-Z]{1,2}\s+\d{3,4}\s+[A-Z]{2,3}$/)) {
|
||||||
|
truckNumber = foundTruck;
|
||||||
|
console.log(`Found truck number: "${truckNumber}" using general pattern`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (truckNumber) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!truckNumber) {
|
||||||
|
console.log('Still no truck number, trying loose patterns...');
|
||||||
|
for (const line of lines) {
|
||||||
|
const looseMatch = line.match(/([A-Z]{1,2})\s*(\d{3,4})\s*([A-Z]{2,3})/i);
|
||||||
|
if (looseMatch && looseMatch[1] && looseMatch[2] && looseMatch[3]) {
|
||||||
|
const candidate = `${looseMatch[1].toUpperCase()} ${looseMatch[2]} ${looseMatch[3].toUpperCase()}`;
|
||||||
|
console.log(`Found potential truck number with loose pattern: "${candidate}"`);
|
||||||
|
|
||||||
|
if (looseMatch[1].length <= 2 &&
|
||||||
|
looseMatch[2].length >= 3 && looseMatch[2].length <= 4 &&
|
||||||
|
looseMatch[3].length >= 2 && looseMatch[3].length <= 3) {
|
||||||
|
truckNumber = candidate;
|
||||||
|
console.log(`Accepted truck number: "${truckNumber}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Truck detection results:', { truckNumber });
|
||||||
|
|
||||||
console.log('Truck detection result:', truckNumber);
|
console.log('Truck detection result:', truckNumber);
|
||||||
|
|
||||||
|
const assignmentPatterns = [
|
||||||
|
/(?:penugasan|assignment)[\s.:]*([A-Z\s]+?)(?:\n|$)/gi,
|
||||||
|
/(JAKARTA\s+\w+)/gi, // Specific pattern for Jakarta areas
|
||||||
|
/(BANDUNG|SURABAYA|MEDAN|SEMARANG|PALEMBANG|MAKASSAR)[\s\w]*/gi,
|
||||||
|
];
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for assignment: "${line}"`);
|
console.log(`Processing line for assignment: "${line}"`);
|
||||||
|
|
||||||
if (line.toLowerCase().includes('penugasan')) {
|
if (line.toLowerCase().includes('penugasan')) {
|
||||||
console.log('Found penugasan line:', line);
|
console.log('Found penugasan line:', line);
|
||||||
|
|
||||||
const match = line.match(/penugasan\s*:?\s*(.+)/i);
|
const assignmentMatch = line.match(/penugasan\s*:\s*(.+)/i);
|
||||||
if (match && match[1]) {
|
if (assignmentMatch && assignmentMatch[1]) {
|
||||||
assignment = match[1].trim();
|
assignment = assignmentMatch[1].trim();
|
||||||
console.log(`Found assignment: ${assignment} (ambil semua karakter setelah label Penugasan)`);
|
console.log(`Found assignment: ${assignment}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line.toUpperCase().includes('JAKARTA') && line.toUpperCase().includes('BARAT')) {
|
||||||
|
assignment = 'JAKARTA BARAT';
|
||||||
|
console.log(`Found assignment: ${assignment} from Jakarta Barat line`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pattern of assignmentPatterns) {
|
||||||
|
const match = line.match(pattern);
|
||||||
|
if (match && match[1]) {
|
||||||
|
assignment = match[1].trim();
|
||||||
|
console.log(`Found assignment: ${assignment} using pattern: ${pattern}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (assignment) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Assignment detection result:', assignment);
|
console.log('Assignment detection result:', assignment);
|
||||||
|
|
||||||
|
// Enhanced time patterns
|
||||||
const timePatterns = [
|
const timePatterns = [
|
||||||
/(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/gi, // 2025-08-02 12:35:34 (highest priority)
|
|
||||||
/(\d{1,2}\s+\w{3}\s+\d{4},\s*\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025, 08:13:51
|
/(\d{1,2}\s+\w{3}\s+\d{4},\s*\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025, 08:13:51
|
||||||
/(\d{1,2}\s+\w{3}\s+\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025 08:13:51
|
/(\d{1,2}\s+\w{3}\s+\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025 08:13:51
|
||||||
/(\d{1,2}\/\d{1,2}\/\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04/08/2025 08:13:51
|
/(\d{1,2}\/\d{1,2}\/\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04/08/2025 08:13:51
|
||||||
/(\d{1,2}-\d{1,2}-\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04-08-2025 08:13:51
|
/(\d{1,2}-\d{1,2}-\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04-08-2025 08:13:51
|
||||||
|
/(\d{4}-\d{1,2}-\d{1,2},\s*\d{1,2}:\d{2}:\d{2})/gi, // 2025-08-04, 08:13:51
|
||||||
|
/(\d{4}\/\d{1,2}\/\d{1,2}\s+\d{1,2}:\d{2}:\d{2})/gi, // 2025/08/04 08:13:51
|
||||||
];
|
];
|
||||||
|
|
||||||
const convertDateFormat = (dateString) => {
|
const convertToStandardFormat = (dateTimeString) => {
|
||||||
const monthMap = {
|
if (!dateTimeString) return '';
|
||||||
'jan': '01', 'january': '01', 'januari': '01',
|
|
||||||
'feb': '02', 'february': '02', 'februari': '02',
|
|
||||||
'mar': '03', 'march': '03', 'maret': '03',
|
|
||||||
'apr': '04', 'april': '04', 'april': '04',
|
|
||||||
'may': '05', 'may': '05', 'mei': '05',
|
|
||||||
'jun': '06', 'june': '06', 'juni': '06',
|
|
||||||
'jul': '07', 'july': '07', 'juli': '07',
|
|
||||||
'aug': '08', 'august': '08', 'agustus': '08',
|
|
||||||
'sep': '09', 'september': '09', 'september': '09',
|
|
||||||
'oct': '10', 'october': '10', 'oktober': '10',
|
|
||||||
'nov': '11', 'november': '11', 'november': '11',
|
|
||||||
'dec': '12', 'december': '12', 'desember': '12'
|
|
||||||
};
|
|
||||||
|
|
||||||
const match = dateString.match(/(\d{1,2})\s+(\w{3,})\s+(\d{4}),?\s*(\d{1,2}:\d{2}:\d{2})/i);
|
console.log('Converting date string:', dateTimeString);
|
||||||
if (match) {
|
|
||||||
const day = match[1].padStart(2, '0');
|
|
||||||
const monthName = match[2].toLowerCase();
|
|
||||||
const year = match[3];
|
|
||||||
const time = match[4];
|
|
||||||
|
|
||||||
const month = monthMap[monthName];
|
const monthNamePattern = /(\d{1,2})\s+(\w{3})\s+(\d{4})[,\s]+(\d{1,2}:\d{2}:\d{2})/i;
|
||||||
if (month) {
|
const monthNameMatch = dateTimeString.match(monthNamePattern);
|
||||||
return `${year}-${month}-${day}, ${time}`;
|
if (monthNameMatch) {
|
||||||
}
|
const day = monthNameMatch[1].padStart(2, '0');
|
||||||
|
const monthName = monthNameMatch[2].toLowerCase();
|
||||||
|
const year = monthNameMatch[3];
|
||||||
|
const time = monthNameMatch[4];
|
||||||
|
|
||||||
|
const monthMap = {
|
||||||
|
'jan': '01', 'feb': '02', 'mar': '03', 'apr': '04',
|
||||||
|
'may': '05', 'jun': '06', 'jul': '07', 'aug': '08',
|
||||||
|
'sep': '09', 'oct': '10', 'nov': '11', 'dec': '12'
|
||||||
|
};
|
||||||
|
|
||||||
|
const month = monthMap[monthName] || '01';
|
||||||
|
const converted = `${year}-${month}-${day}, ${time}`;
|
||||||
|
console.log('Converted month name format:', converted);
|
||||||
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateString;
|
const ddmmyyyyPattern = /(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}:\d{2}:\d{2})/;
|
||||||
|
const ddmmyyyyMatch = dateTimeString.match(ddmmyyyyPattern);
|
||||||
|
if (ddmmyyyyMatch) {
|
||||||
|
const day = ddmmyyyyMatch[1].padStart(2, '0');
|
||||||
|
const month = ddmmyyyyMatch[2].padStart(2, '0');
|
||||||
|
const year = ddmmyyyyMatch[3];
|
||||||
|
const time = ddmmyyyyMatch[4];
|
||||||
|
const converted = `${year}-${month}-${day}, ${time}`;
|
||||||
|
console.log('Converted DD/MM/YYYY format:', converted);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ddmmyyyyDashPattern = /(\d{1,2})-(\d{1,2})-(\d{4})\s+(\d{1,2}:\d{2}:\d{2})/;
|
||||||
|
const ddmmyyyyDashMatch = dateTimeString.match(ddmmyyyyDashPattern);
|
||||||
|
if (ddmmyyyyDashMatch) {
|
||||||
|
const day = ddmmyyyyDashMatch[1].padStart(2, '0');
|
||||||
|
const month = ddmmyyyyDashMatch[2].padStart(2, '0');
|
||||||
|
const year = ddmmyyyyDashMatch[3];
|
||||||
|
const time = ddmmyyyyDashMatch[4];
|
||||||
|
const converted = `${year}-${month}-${day}, ${time}`;
|
||||||
|
console.log('Converted DD-MM-YYYY format:', converted);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
const yyyymmddSlashPattern = /(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}:\d{2}:\d{2})/;
|
||||||
|
const yyyymmddSlashMatch = dateTimeString.match(yyyymmddSlashPattern);
|
||||||
|
if (yyyymmddSlashMatch) {
|
||||||
|
const year = yyyymmddSlashMatch[1];
|
||||||
|
const month = yyyymmddSlashMatch[2].padStart(2, '0');
|
||||||
|
const day = yyyymmddSlashMatch[3].padStart(2, '0');
|
||||||
|
const time = yyyymmddSlashMatch[4];
|
||||||
|
const converted = `${year}-${month}-${day}, ${time}`;
|
||||||
|
console.log('Converted YYYY/MM/DD format:', converted);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
const standardPattern = /(\d{4}-\d{1,2}-\d{1,2}),?\s*(\d{1,2}:\d{2}:\d{2})/;
|
||||||
|
const standardMatch = dateTimeString.match(standardPattern);
|
||||||
|
if (standardMatch) {
|
||||||
|
const datePart = standardMatch[1];
|
||||||
|
const timePart = standardMatch[2];
|
||||||
|
|
||||||
|
const [year, month, day] = datePart.split('-');
|
||||||
|
const paddedMonth = month.padStart(2, '0');
|
||||||
|
const paddedDay = day.padStart(2, '0');
|
||||||
|
|
||||||
|
const converted = `${year}-${paddedMonth}-${paddedDay}, ${timePart}`;
|
||||||
|
console.log('Standardized YYYY-MM-DD format:', converted);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('No date pattern matched, returning original:', dateTimeString);
|
||||||
|
return dateTimeString;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for time: "${line}"`);
|
console.log(`Processing line for time: "${line}"`);
|
||||||
|
|
||||||
|
// Entry time - look for "Masuk :" pattern
|
||||||
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
||||||
console.log('Found masuk line:', line);
|
console.log('Found masuk line:', line);
|
||||||
|
for (const pattern of timePatterns) {
|
||||||
const masukMatch = line.match(/masuk\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
const match = line.match(pattern);
|
||||||
if (masukMatch && masukMatch[1]) {
|
if (match && match[1]) {
|
||||||
entryTime = masukMatch[1].trim();
|
entryTime = convertToStandardFormat(match[1].trim());
|
||||||
console.log(`Found entry time via masuk pattern: ${entryTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
console.log(`Found entry time: ${entryTime}`);
|
||||||
} else {
|
break;
|
||||||
for (const pattern of timePatterns) {
|
|
||||||
const match = line.match(pattern);
|
|
||||||
if (match && match[1]) {
|
|
||||||
const rawTime = match[1].trim();
|
|
||||||
entryTime = convertDateFormat(rawTime);
|
|
||||||
console.log(`Found entry time: ${rawTime} -> converted to: ${entryTime}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Also try to extract after "Masuk : "
|
||||||
if (!entryTime) {
|
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
||||||
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
if (masukMatch && masukMatch[1] && !entryTime) {
|
||||||
if (masukMatch && masukMatch[1]) {
|
entryTime = convertToStandardFormat(masukMatch[1].trim());
|
||||||
const rawTime = masukMatch[1].trim();
|
console.log(`Found entry time via masuk pattern: ${entryTime}`);
|
||||||
entryTime = convertDateFormat(rawTime);
|
|
||||||
console.log(`Found entry time via masuk pattern: ${rawTime} -> converted to: ${entryTime}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit time - look for "Keluar :" pattern
|
||||||
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
||||||
console.log('Found keluar line:', line);
|
console.log('Found keluar line:', line);
|
||||||
|
for (const pattern of timePatterns) {
|
||||||
const keluarMatch = line.match(/keluar\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
const match = line.match(pattern);
|
||||||
if (keluarMatch && keluarMatch[1]) {
|
if (match && match[1]) {
|
||||||
exitTime = keluarMatch[1].trim();
|
exitTime = convertToStandardFormat(match[1].trim());
|
||||||
console.log(`Found exit time via keluar pattern: ${exitTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
console.log(`Found exit time: ${exitTime}`);
|
||||||
} else {
|
break;
|
||||||
for (const pattern of timePatterns) {
|
|
||||||
const match = line.match(pattern);
|
|
||||||
if (match && match[1]) {
|
|
||||||
const rawTime = match[1].trim();
|
|
||||||
exitTime = convertDateFormat(rawTime);
|
|
||||||
console.log(`Found exit time: ${rawTime} -> converted to: ${exitTime}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Also try to extract after "Keluar : "
|
||||||
if (!exitTime) {
|
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
||||||
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
if (keluarMatch && keluarMatch[1] && !exitTime) {
|
||||||
if (keluarMatch && keluarMatch[1]) {
|
exitTime = convertToStandardFormat(keluarMatch[1].trim());
|
||||||
const rawTime = keluarMatch[1].trim();
|
console.log(`Found exit time via keluar pattern: ${exitTime}`);
|
||||||
exitTime = convertDateFormat(rawTime);
|
|
||||||
console.log(`Found exit time via keluar pattern: ${rawTime} -> converted to: ${exitTime}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Time detection results:', { entryTime, exitTime });
|
console.log('Time detection results:', { entryTime, exitTime });
|
||||||
|
|
||||||
|
// Enhanced weight patterns - more specific for Indonesian receipts
|
||||||
const weightPatterns = [
|
const weightPatterns = [
|
||||||
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
||||||
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
||||||
|
@ -833,38 +921,41 @@
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightIn = numbers[1]; // Hanya angka, tanpa "kg"
|
weightIn = numbers[1];
|
||||||
console.log(`Found weight in via specific pattern: ${weightIn} (removed kg)`);
|
console.log(`Found weight in via specific pattern: ${weightIn} kg`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
// Try general patterns
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||||
weightIn = match[1]; // Hanya angka
|
weightIn = match[1];
|
||||||
console.log(`Found weight in via general pattern: ${weightIn} (removed kg)`);
|
console.log(`Found weight in via general pattern: ${weightIn} kg`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Berat Keluar - look for exact pattern "Berat Keluar : 13540 kg"
|
||||||
if (lowerLine.includes('berat keluar')) {
|
if (lowerLine.includes('berat keluar')) {
|
||||||
console.log('Found berat keluar line:', line);
|
console.log('Found berat keluar line:', line);
|
||||||
|
|
||||||
|
// Try specific pattern first
|
||||||
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightOut = numbers[1]; // Hanya angka, tanpa "kg"
|
weightOut = numbers[1];
|
||||||
console.log(`Found weight out via specific pattern: ${weightOut} (removed kg)`);
|
console.log(`Found weight out via specific pattern: ${weightOut} kg`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Try general patterns
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||||
weightOut = match[1]; // Hanya angka
|
weightOut = match[1];
|
||||||
console.log(`Found weight out via general pattern: ${weightOut} (removed kg)`);
|
console.log(`Found weight out via general pattern: ${weightOut} kg`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -880,16 +971,16 @@
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightNett = numbers[1]; // Hanya angka, tanpa "kg"
|
weightNett = numbers[1];
|
||||||
console.log(`Found weight nett via specific pattern: ${weightNett} (removed kg)`);
|
console.log(`Found weight nett via specific pattern: ${weightNett} kg`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
// Try general patterns
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 100) {
|
if (match && match[1] && parseInt(match[1]) > 100) {
|
||||||
weightNett = match[1]; // Hanya angka
|
weightNett = match[1];
|
||||||
console.log(`Found weight nett via general pattern: ${weightNett} (removed kg)`);
|
console.log(`Found weight nett via general pattern: ${weightNett} kg`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -897,24 +988,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validasi Berat Nett = Berat Masuk - Berat Keluar
|
|
||||||
if (weightIn && weightOut && weightNett) {
|
|
||||||
const calculatedNett = parseInt(weightIn) - parseInt(weightOut);
|
|
||||||
const detectedNett = parseInt(weightNett);
|
|
||||||
|
|
||||||
console.log(`Weight validation: ${weightIn} - ${weightOut} = ${calculatedNett}, detected: ${detectedNett}`);
|
|
||||||
|
|
||||||
if (calculatedNett !== detectedNett) {
|
|
||||||
console.warn(`WARNING: Berat Nett tidak sesuai! Perhitungan: ${calculatedNett}, Terdeteksi: ${detectedNett}`);
|
|
||||||
// Gunakan hasil perhitungan yang benar
|
|
||||||
weightNett = calculatedNett.toString();
|
|
||||||
console.log(`Using calculated weight nett: ${weightNett}`);
|
|
||||||
|
|
||||||
// Alert akan ditampilkan di frontend saat apply data
|
|
||||||
this.weightValidationError = `Berat Nett tidak sesuai! Perhitungan: ${calculatedNett} kg, Terdeteksi: ${detectedNett} kg. Menggunakan hasil perhitungan.`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Weight detection results:', { weightIn, weightOut, weightNett });
|
console.log('Weight detection results:', { weightIn, weightOut, weightNett });
|
||||||
|
|
||||||
console.log('Final Detected Data before assignment:', {
|
console.log('Final Detected Data before assignment:', {
|
||||||
|
@ -967,15 +1040,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDetectedDataDirectly() {
|
applyDetectedDataDirectly() {
|
||||||
console.log(`=== APPLYING DATA DIRECTLY TO FORM ===`);
|
console.log('=== APPLYING DATA DIRECTLY TO FORM ===');
|
||||||
console.log('Data to apply:', this.detectedData);
|
console.log('Data to apply:', this.detectedData);
|
||||||
|
|
||||||
// Tampilkan alert jika ada error validasi berat nett
|
|
||||||
if (this.weightValidationError) {
|
|
||||||
alert(this.weightValidationError);
|
|
||||||
this.weightValidationError = null; // Reset error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get input elements by ID directly
|
// Get input elements by ID directly
|
||||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||||
const nomorPolisiInput = document.getElementById('NomorPolisi');
|
const nomorPolisiInput = document.getElementById('NomorPolisi');
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="max-w-sm mx-auto bg-white min-h-screen">
|
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
||||||
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||||
|
@ -180,7 +180,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Waktu Masuk dan Keluar -->
|
<!-- Waktu Masuk dan Keluar -->
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label for="WaktuMasuk" class="block text-sm font-medium text-gray-700 mb-1">Masuk</label>
|
<label for="WaktuMasuk" class="block text-sm font-medium text-gray-700 mb-1">Masuk</label>
|
||||||
<input
|
<input
|
||||||
|
@ -188,7 +188,7 @@
|
||||||
id="WaktuMasuk"
|
id="WaktuMasuk"
|
||||||
name="WaktuMasuk"
|
name="WaktuMasuk"
|
||||||
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
||||||
placeholder="04 Aug 2025, 08:13:51"
|
placeholder="2025-08-04, 08:13:51"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
id="WaktuKeluar"
|
id="WaktuKeluar"
|
||||||
name="WaktuKeluar"
|
name="WaktuKeluar"
|
||||||
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
||||||
placeholder="04 Aug 2025, 14:35:10"
|
placeholder="2025-08-04, 14:35:10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -253,7 +253,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<register-block dynamic-section="scripts" key="jsSubmitStruk">
|
<register-block dynamic-section="scripts" key="jsSubmitStruk">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.1/dist/tesseract.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5.1.1/dist/tesseract.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||||
|
@ -577,36 +577,35 @@
|
||||||
let weightNett = '';
|
let weightNett = '';
|
||||||
|
|
||||||
const receiptPatterns = [
|
const receiptPatterns = [
|
||||||
/(\d{2})\s+(\d{7,})/gi,
|
/(\d{2})_(\d{6,})/gi,
|
||||||
/(\d{2})_(\d{7,})/gi,
|
/(\d{2})\s+(\d{6,})/gi,
|
||||||
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{7,})/gi,
|
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{6,})/gi,
|
||||||
/(?:^|\s)(\d{7,10})(?:\s|$)/g,
|
/(?:^|\s)(\d{6,10})(?:\s|$)/g,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for receipt: "${line}"`);
|
console.log(`Processing line for receipt: "${line}"`);
|
||||||
|
|
||||||
const monthNumberMatch = line.match(/(\d{2})\s+(\d{7,})/);
|
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{6,})/);
|
||||||
if (monthNumberMatch && monthNumberMatch[2]) {
|
|
||||||
receiptNumber = monthNumberMatch[2]; // Take the number after space/underscore
|
|
||||||
console.log(`Found receipt number: ${receiptNumber} using month-number pattern`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{7,})/);
|
|
||||||
if (monthUnderscoreMatch && monthUnderscoreMatch[2]) {
|
if (monthUnderscoreMatch && monthUnderscoreMatch[2]) {
|
||||||
receiptNumber = monthUnderscoreMatch[2]; // Take the number after underscore
|
receiptNumber = monthUnderscoreMatch[2];
|
||||||
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern`);
|
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern (removed prefix: ${monthUnderscoreMatch[1]}_)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other patterns
|
const monthSpaceMatch = line.match(/(\d{2})\s+(\d{6,})/);
|
||||||
for (const pattern of receiptPatterns.slice(2)) { // Skip first 2 patterns already handled above
|
if (monthSpaceMatch && monthSpaceMatch[2]) {
|
||||||
|
receiptNumber = monthSpaceMatch[2]; // Take ONLY the number after space
|
||||||
|
console.log(`Found receipt number: ${receiptNumber} using month-space pattern (removed prefix: ${monthSpaceMatch[1]} )`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pattern of receiptPatterns.slice(2)) {
|
||||||
const matches = [...line.matchAll(pattern)];
|
const matches = [...line.matchAll(pattern)];
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
if (match[1] && match[1].length >= 7) {
|
if (match[1] && match[1].length >= 6) {
|
||||||
receiptNumber = match[1];
|
receiptNumber = match[1];
|
||||||
console.log(`Found receipt number: ${receiptNumber} using pattern: ${pattern}`);
|
console.log(`Found receipt number: ${receiptNumber} using context pattern: ${pattern}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,157 +667,150 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!truckNumber) {
|
if (!truckNumber) {
|
||||||
console.log('No truck number found in context lines, trying all lines...');
|
for (const line of lines) {
|
||||||
for (const line of lines) {
|
if (line.toLowerCase().includes('no truk')) {
|
||||||
console.log(`Scanning line for truck pattern: "${line}"`);
|
const match = line.match(/no\s*truk\s*:?\s*(.+)/i);
|
||||||
|
if (match && match[1]) {
|
||||||
if (line.match(/[A-Z]\s*\d+\s*[A-Z]{2,3}/i)) {
|
truckNumber = match[1].trim();
|
||||||
console.log('Potential truck pattern found:', line);
|
console.log(`Found truck number: ${truckNumber} (ambil semua karakter setelah label No Truk)`);
|
||||||
|
break;
|
||||||
for (const pattern of truckPatterns) {
|
}
|
||||||
const match = line.match(pattern);
|
}
|
||||||
if (match) {
|
}
|
||||||
let foundTruck = '';
|
if (!truckNumber) {
|
||||||
|
for (const line of lines) {
|
||||||
if (match.length === 4 && match[1] && match[2] && match[3]) {
|
const match = line.match(/\bB\s*\d{3,5}\s*[A-Z]{2,4}\b/i);
|
||||||
foundTruck = `${match[1]} ${match[2]} ${match[3]}`;
|
if (match && match[0]) {
|
||||||
} else if (match[1]) {
|
truckNumber = match[0].trim();
|
||||||
foundTruck = match[1].trim();
|
console.log(`Fallback truck number: ${truckNumber}`);
|
||||||
}
|
|
||||||
|
|
||||||
if (foundTruck) {
|
|
||||||
foundTruck = foundTruck.replace(/([A-Z])(\d+)(\s+[A-Z]{2,3})/g, '$1 $2$3');
|
|
||||||
foundTruck = foundTruck.replace(/([A-Z]{1,2})(\d{3,4})([A-Z]{2,3})/g, '$1 $2 $3');
|
|
||||||
foundTruck = foundTruck.replace(/\s+/g, ' ').trim();
|
|
||||||
|
|
||||||
if (foundTruck.match(/^[A-Z]{1,2}\s+\d{3,4}\s+[A-Z]{2,3}$/)) {
|
|
||||||
truckNumber = foundTruck;
|
|
||||||
console.log(`Found truck number: "${truckNumber}" using general pattern`);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (truckNumber) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!truckNumber) {
|
|
||||||
console.log('Still no truck number, trying loose patterns...');
|
|
||||||
for (const line of lines) {
|
|
||||||
const looseMatch = line.match(/([A-Z]{1,2})\s*(\d{3,4})\s*([A-Z]{2,3})/i);
|
|
||||||
if (looseMatch && looseMatch[1] && looseMatch[2] && looseMatch[3]) {
|
|
||||||
const candidate = `${looseMatch[1].toUpperCase()} ${looseMatch[2]} ${looseMatch[3].toUpperCase()}`;
|
|
||||||
console.log(`Found potential truck number with loose pattern: "${candidate}"`);
|
|
||||||
|
|
||||||
if (looseMatch[1].length <= 2 &&
|
|
||||||
looseMatch[2].length >= 3 && looseMatch[2].length <= 4 &&
|
|
||||||
looseMatch[3].length >= 2 && looseMatch[3].length <= 3) {
|
|
||||||
truckNumber = candidate;
|
|
||||||
console.log(`Accepted truck number: "${truckNumber}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Truck detection results:', { truckNumber });
|
|
||||||
|
|
||||||
console.log('Truck detection result:', truckNumber);
|
console.log('Truck detection result:', truckNumber);
|
||||||
|
|
||||||
const assignmentPatterns = [
|
|
||||||
/(?:penugasan|assignment)[\s.:]*([A-Z\s]+?)(?:\n|$)/gi,
|
|
||||||
/(JAKARTA\s+\w+)/gi, // Specific pattern for Jakarta areas
|
|
||||||
/(BANDUNG|SURABAYA|MEDAN|SEMARANG|PALEMBANG|MAKASSAR)[\s\w]*/gi,
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for assignment: "${line}"`);
|
console.log(`Processing line for assignment: "${line}"`);
|
||||||
|
|
||||||
if (line.toLowerCase().includes('penugasan')) {
|
if (line.toLowerCase().includes('penugasan')) {
|
||||||
console.log('Found penugasan line:', line);
|
console.log('Found penugasan line:', line);
|
||||||
|
|
||||||
const assignmentMatch = line.match(/penugasan\s*:\s*(.+)/i);
|
const match = line.match(/penugasan\s*:?\s*(.+)/i);
|
||||||
if (assignmentMatch && assignmentMatch[1]) {
|
|
||||||
assignment = assignmentMatch[1].trim();
|
|
||||||
console.log(`Found assignment: ${assignment}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.toUpperCase().includes('JAKARTA') && line.toUpperCase().includes('BARAT')) {
|
|
||||||
assignment = 'JAKARTA BARAT';
|
|
||||||
console.log(`Found assignment: ${assignment} from Jakarta Barat line`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const pattern of assignmentPatterns) {
|
|
||||||
const match = line.match(pattern);
|
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
assignment = match[1].trim();
|
assignment = match[1].trim();
|
||||||
console.log(`Found assignment: ${assignment} using pattern: ${pattern}`);
|
console.log(`Found assignment: ${assignment} (ambil semua karakter setelah label Penugasan)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (assignment) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Assignment detection result:', assignment);
|
console.log('Assignment detection result:', assignment);
|
||||||
|
|
||||||
// Enhanced time patterns
|
|
||||||
const timePatterns = [
|
const timePatterns = [
|
||||||
|
/(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/gi, // 2025-08-02 12:35:34 (highest priority)
|
||||||
/(\d{1,2}\s+\w{3}\s+\d{4},\s*\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025, 08:13:51
|
/(\d{1,2}\s+\w{3}\s+\d{4},\s*\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025, 08:13:51
|
||||||
/(\d{1,2}\s+\w{3}\s+\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025 08:13:51
|
/(\d{1,2}\s+\w{3}\s+\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025 08:13:51
|
||||||
/(\d{1,2}\/\d{1,2}\/\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04/08/2025 08:13:51
|
/(\d{1,2}\/\d{1,2}\/\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04/08/2025 08:13:51
|
||||||
/(\d{1,2}-\d{1,2}-\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04-08-2025 08:13:51
|
/(\d{1,2}-\d{1,2}-\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04-08-2025 08:13:51
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const line of lines) {
|
const convertDateFormat = (dateString) => {
|
||||||
console.log(`Processing line for time: "${line}"`);
|
const monthMap = {
|
||||||
|
'jan': '01', 'january': '01', 'januari': '01',
|
||||||
|
'feb': '02', 'february': '02', 'februari': '02',
|
||||||
|
'mar': '03', 'march': '03', 'maret': '03',
|
||||||
|
'apr': '04', 'april': '04', 'april': '04',
|
||||||
|
'may': '05', 'may': '05', 'mei': '05',
|
||||||
|
'jun': '06', 'june': '06', 'juni': '06',
|
||||||
|
'jul': '07', 'july': '07', 'juli': '07',
|
||||||
|
'aug': '08', 'august': '08', 'agustus': '08',
|
||||||
|
'sep': '09', 'september': '09', 'september': '09',
|
||||||
|
'oct': '10', 'october': '10', 'oktober': '10',
|
||||||
|
'nov': '11', 'november': '11', 'november': '11',
|
||||||
|
'dec': '12', 'december': '12', 'desember': '12'
|
||||||
|
};
|
||||||
|
|
||||||
// Entry time - look for "Masuk :" pattern
|
const match = dateString.match(/(\d{1,2})\s+(\w{3,})\s+(\d{4}),?\s*(\d{1,2}:\d{2}:\d{2})/i);
|
||||||
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
if (match) {
|
||||||
console.log('Found masuk line:', line);
|
const day = match[1].padStart(2, '0');
|
||||||
for (const pattern of timePatterns) {
|
const monthName = match[2].toLowerCase();
|
||||||
const match = line.match(pattern);
|
const year = match[3];
|
||||||
if (match && match[1]) {
|
const time = match[4];
|
||||||
entryTime = match[1].trim();
|
|
||||||
console.log(`Found entry time: ${entryTime}`);
|
const month = monthMap[monthName];
|
||||||
break;
|
if (month) {
|
||||||
}
|
return `${year}-${month}-${day}, ${time}`;
|
||||||
}
|
|
||||||
// Also try to extract after "Masuk : "
|
|
||||||
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
|
||||||
if (masukMatch && masukMatch[1] && !entryTime) {
|
|
||||||
entryTime = masukMatch[1].trim();
|
|
||||||
console.log(`Found entry time via masuk pattern: ${entryTime}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit time - look for "Keluar :" pattern
|
return dateString;
|
||||||
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
};
|
||||||
console.log('Found keluar line:', line);
|
|
||||||
for (const pattern of timePatterns) {
|
for (const line of lines) {
|
||||||
const match = line.match(pattern);
|
console.log(`Processing line for time: "${line}"`);
|
||||||
if (match && match[1]) {
|
|
||||||
exitTime = match[1].trim();
|
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
||||||
console.log(`Found exit time: ${exitTime}`);
|
console.log('Found masuk line:', line);
|
||||||
break;
|
|
||||||
|
const masukMatch = line.match(/masuk\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
||||||
|
if (masukMatch && masukMatch[1]) {
|
||||||
|
entryTime = masukMatch[1].trim();
|
||||||
|
console.log(`Found entry time via masuk pattern: ${entryTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||||
|
} else {
|
||||||
|
for (const pattern of timePatterns) {
|
||||||
|
const match = line.match(pattern);
|
||||||
|
if (match && match[1]) {
|
||||||
|
const rawTime = match[1].trim();
|
||||||
|
entryTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found entry time: ${rawTime} -> converted to: ${entryTime}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also try to extract after "Keluar : "
|
|
||||||
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
if (!entryTime) {
|
||||||
if (keluarMatch && keluarMatch[1] && !exitTime) {
|
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
||||||
|
if (masukMatch && masukMatch[1]) {
|
||||||
|
const rawTime = masukMatch[1].trim();
|
||||||
|
entryTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found entry time via masuk pattern: ${rawTime} -> converted to: ${entryTime}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
||||||
|
console.log('Found keluar line:', line);
|
||||||
|
|
||||||
|
const keluarMatch = line.match(/keluar\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
||||||
|
if (keluarMatch && keluarMatch[1]) {
|
||||||
exitTime = keluarMatch[1].trim();
|
exitTime = keluarMatch[1].trim();
|
||||||
console.log(`Found exit time via keluar pattern: ${exitTime}`);
|
console.log(`Found exit time via keluar pattern: ${exitTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||||
|
} else {
|
||||||
|
for (const pattern of timePatterns) {
|
||||||
|
const match = line.match(pattern);
|
||||||
|
if (match && match[1]) {
|
||||||
|
const rawTime = match[1].trim();
|
||||||
|
exitTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found exit time: ${rawTime} -> converted to: ${exitTime}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exitTime) {
|
||||||
|
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
||||||
|
if (keluarMatch && keluarMatch[1]) {
|
||||||
|
const rawTime = keluarMatch[1].trim();
|
||||||
|
exitTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found exit time via keluar pattern: ${rawTime} -> converted to: ${exitTime}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Time detection results:', { entryTime, exitTime });
|
console.log('Time detection results:', { entryTime, exitTime });
|
||||||
|
|
||||||
// Enhanced weight patterns - more specific for Indonesian receipts
|
|
||||||
const weightPatterns = [
|
const weightPatterns = [
|
||||||
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
||||||
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
||||||
|
@ -841,41 +833,38 @@
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightIn = numbers[1];
|
weightIn = numbers[1]; // Hanya angka, tanpa "kg"
|
||||||
console.log(`Found weight in via specific pattern: ${weightIn} kg`);
|
console.log(`Found weight in via specific pattern: ${weightIn} (removed kg)`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
// Try general patterns
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||||
weightIn = match[1];
|
weightIn = match[1]; // Hanya angka
|
||||||
console.log(`Found weight in via general pattern: ${weightIn} kg`);
|
console.log(`Found weight in via general pattern: ${weightIn} (removed kg)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Berat Keluar - look for exact pattern "Berat Keluar : 13540 kg"
|
|
||||||
if (lowerLine.includes('berat keluar')) {
|
if (lowerLine.includes('berat keluar')) {
|
||||||
console.log('Found berat keluar line:', line);
|
console.log('Found berat keluar line:', line);
|
||||||
|
|
||||||
// Try specific pattern first
|
|
||||||
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightOut = numbers[1];
|
weightOut = numbers[1]; // Hanya angka, tanpa "kg"
|
||||||
console.log(`Found weight out via specific pattern: ${weightOut} kg`);
|
console.log(`Found weight out via specific pattern: ${weightOut} (removed kg)`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||||
weightOut = match[1];
|
weightOut = match[1]; // Hanya angka
|
||||||
console.log(`Found weight out via general pattern: ${weightOut} kg`);
|
console.log(`Found weight out via general pattern: ${weightOut} (removed kg)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -891,16 +880,16 @@
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
if (numbers && numbers[1]) {
|
if (numbers && numbers[1]) {
|
||||||
weightNett = numbers[1];
|
weightNett = numbers[1]; // Hanya angka, tanpa "kg"
|
||||||
console.log(`Found weight nett via specific pattern: ${weightNett} kg`);
|
console.log(`Found weight nett via specific pattern: ${weightNett} (removed kg)`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
// Try general patterns
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 100) {
|
if (match && match[1] && parseInt(match[1]) > 100) {
|
||||||
weightNett = match[1];
|
weightNett = match[1]; // Hanya angka
|
||||||
console.log(`Found weight nett via general pattern: ${weightNett} kg`);
|
console.log(`Found weight nett via general pattern: ${weightNett} (removed kg)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -908,6 +897,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validasi Berat Nett = Berat Masuk - Berat Keluar
|
||||||
|
if (weightIn && weightOut && weightNett) {
|
||||||
|
const calculatedNett = parseInt(weightIn) - parseInt(weightOut);
|
||||||
|
const detectedNett = parseInt(weightNett);
|
||||||
|
|
||||||
|
console.log(`Weight validation: ${weightIn} - ${weightOut} = ${calculatedNett}, detected: ${detectedNett}`);
|
||||||
|
|
||||||
|
if (calculatedNett !== detectedNett) {
|
||||||
|
console.warn(`WARNING: Berat Nett tidak sesuai! Perhitungan: ${calculatedNett}, Terdeteksi: ${detectedNett}`);
|
||||||
|
// Gunakan hasil perhitungan yang benar
|
||||||
|
weightNett = calculatedNett.toString();
|
||||||
|
console.log(`Using calculated weight nett: ${weightNett}`);
|
||||||
|
|
||||||
|
// Alert akan ditampilkan di frontend saat apply data
|
||||||
|
this.weightValidationError = `Berat Nett tidak sesuai! Perhitungan: ${calculatedNett} kg, Terdeteksi: ${detectedNett} kg. Menggunakan hasil perhitungan.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Weight detection results:', { weightIn, weightOut, weightNett });
|
console.log('Weight detection results:', { weightIn, weightOut, weightNett });
|
||||||
|
|
||||||
console.log('Final Detected Data before assignment:', {
|
console.log('Final Detected Data before assignment:', {
|
||||||
|
@ -960,9 +967,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDetectedDataDirectly() {
|
applyDetectedDataDirectly() {
|
||||||
console.log('=== APPLYING DATA DIRECTLY TO FORM ===');
|
console.log(`=== APPLYING DATA DIRECTLY TO FORM ===`);
|
||||||
console.log('Data to apply:', this.detectedData);
|
console.log('Data to apply:', this.detectedData);
|
||||||
|
|
||||||
|
// Tampilkan alert jika ada error validasi berat nett
|
||||||
|
if (this.weightValidationError) {
|
||||||
|
alert(this.weightValidationError);
|
||||||
|
this.weightValidationError = null; // Reset error
|
||||||
|
}
|
||||||
|
|
||||||
// Get input elements by ID directly
|
// Get input elements by ID directly
|
||||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||||
const nomorPolisiInput = document.getElementById('NomorPolisi');
|
const nomorPolisiInput = document.getElementById('NomorPolisi');
|
Loading…
Reference in New Issue