diff --git a/backend/server.js b/backend/server.js index 87dda81..518b3cb 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4426,6 +4426,10 @@ app.post('/api/assets', requireAssetOrAdmin, async (req, res) => { return res.status(400).json({ success: false, message: 'Asset name is required' }); } + if (!payload.model) { + return res.status(400).json({ success: false, message: 'Model is required' }); + } + if (!payload.assetCode) { payload.assetCode = await generateUniqueManualAssetCode(payload); } @@ -4490,6 +4494,10 @@ app.put('/api/assets/:id', requireAssetOrAdmin, async (req, res) => { return res.status(400).json({ success: false, message: 'Asset code and asset name are required' }); } + if (!payload.model) { + return res.status(400).json({ success: false, message: 'Model is required' }); + } + await ensureDepartmentExists(payload.department); await pool.request() @@ -4608,6 +4616,8 @@ app.post('/api/assets/import', requireAssetOrAdmin, upload.single('file'), async const normalizedRows = incomingRows .map((row, rowIndex) => { const normalized = normalizeAssetPayload(row); + const rowSourceStt = parseAssetImportSttNumber(row?.sourceStt); + normalized.__importRowLabel = rowSourceStt !== null ? `STT ${rowSourceStt}` : `dong ${rowIndex + 1}`; const hasOriginalAssetCode = String(normalized.assetCode || '').trim() !== ''; if (!hasOriginalAssetCode && normalized.assetName) { normalized.assetCode = generateImportAssetCodeFromRow(normalized, rowIndex + 1); @@ -4619,7 +4629,20 @@ app.post('/api/assets/import', requireAssetOrAdmin, upload.single('file'), async .filter(row => isMeaningfulImportedAssetRow(row)); if (!normalizedRows.length) { - return res.status(400).json({ success: false, message: 'No valid rows found. Asset name or model is required.' }); + return res.status(400).json({ success: false, message: 'No valid rows found. MODEL is required.' }); + } + + const missingModelRows = normalizedRows + .filter(row => !String(row.model || '').trim()) + .map(row => row.__importRowLabel) + .filter(Boolean); + if (missingModelRows.length) { + const limitedRows = missingModelRows.slice(0, 20); + const suffix = missingModelRows.length > limitedRows.length ? ', ...' : ''; + return res.status(400).json({ + success: false, + message: `Cot MODEL la bat buoc. Thieu du lieu tai: ${limitedRows.join(', ')}${suffix}` + }); } const transaction = new sql.Transaction(pool); diff --git a/public/js/app.js b/public/js/app.js index 45b581c..80ec111 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -4418,6 +4418,7 @@ class AccountManager { bindInput('assetCodeInput', 'assetCodeError'); bindInput('assetNameInput', 'assetNameError'); + bindInput('assetModelInput', 'assetModelError'); } clearAssetFieldValidation(inputId, errorId) { @@ -4438,6 +4439,7 @@ class AccountManager { clearAssetFormValidation() { this.clearAssetFieldValidation('assetCodeInput', 'assetCodeError'); this.clearAssetFieldValidation('assetNameInput', 'assetNameError'); + this.clearAssetFieldValidation('assetModelInput', 'assetModelError'); } setAssetFieldValidationError(inputId, errorId, message) { @@ -4828,6 +4830,13 @@ class AccountManager { return; } + if (!payload.model) { + this.setAssetFieldValidationError('assetModelInput', 'assetModelError', 'Vui lòng nhập model.'); + this.notifyWarning('Vui lòng nhập đầy đủ các trường bắt buộc.'); + document.getElementById('assetModelInput')?.focus(); + return; + } + if (isEdit && !payload.assetCode) { this.setAssetFieldValidationError('assetCodeInput', 'assetCodeError', 'Mã tài sản là bắt buộc khi cập nhật.'); this.notifyWarning('Vui lòng nhập đầy đủ các trường bắt buộc.'); diff --git a/public/modals.html b/public/modals.html index e7109e9..45e233b 100644 --- a/public/modals.html +++ b/public/modals.html @@ -225,8 +225,9 @@