fix mượn trả
This commit is contained in:
@@ -4196,6 +4196,167 @@ app.post('/api/asset-borrows', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/asset-borrows/:id/return', async (req, res) => {
|
||||
const transaction = new sql.Transaction(pool);
|
||||
|
||||
try {
|
||||
const requesterRole = normalizeRole(req.headers['x-user-role'] || req.query.userRole);
|
||||
const requesterId = getUserIdFromRequest(req);
|
||||
const canManageRequests = requesterRole === 'admin' || requesterRole === 'asset';
|
||||
const borrowId = Number(req.params.id);
|
||||
|
||||
if (!Number.isInteger(borrowId) || borrowId <= 0) {
|
||||
return res.status(400).json({ success: false, message: 'Ma don muon khong hop le' });
|
||||
}
|
||||
|
||||
if (!canManageRequests && (!Number.isInteger(requesterId) || requesterId <= 0)) {
|
||||
return res.status(401).json({ success: false, message: 'Yeu cau xac thuc nguoi dung' });
|
||||
}
|
||||
|
||||
await transaction.begin();
|
||||
|
||||
const targetResult = await new sql.Request(transaction)
|
||||
.input('borrowId', sql.Int, borrowId)
|
||||
.input('requesterId', sql.Int, requesterId || -1)
|
||||
.query(`
|
||||
SELECT TOP 1
|
||||
br.BorrowId,
|
||||
br.AssetId,
|
||||
br.RequestType,
|
||||
br.RequestStatus,
|
||||
br.BorrowerName,
|
||||
br.BorrowQuantity,
|
||||
ISNULL(br.ReturnedQuantity, 0) AS ReturnedQuantity,
|
||||
ISNULL(activeReturns.ActiveReturnQuantity, 0) AS ActiveReturnQuantity,
|
||||
COALESCE(NULLIF(LTRIM(RTRIM(br.Unit)), ''), ai.Unit) AS Unit,
|
||||
br.CreatedBy,
|
||||
ai.Borrower
|
||||
FROM AssetBorrowRequests br WITH (UPDLOCK, HOLDLOCK)
|
||||
INNER JOIN AssetInventory ai WITH (UPDLOCK, HOLDLOCK) ON ai.AssetId = br.AssetId
|
||||
OUTER APPLY (
|
||||
SELECT SUM(ISNULL(links.Quantity, 0)) AS ActiveReturnQuantity
|
||||
FROM AssetBorrowRequestLinks links
|
||||
INNER JOIN AssetBorrowRequests returnRows ON returnRows.BorrowId = links.ReturnId
|
||||
WHERE links.BorrowId = br.BorrowId
|
||||
AND LOWER(LTRIM(RTRIM(ISNULL(returnRows.RequestStatus, '')))) IN ('pending', 'approved')
|
||||
) activeReturns
|
||||
WHERE br.BorrowId = @borrowId
|
||||
${canManageRequests ? '' : 'AND br.CreatedBy = @requesterId'}
|
||||
`);
|
||||
|
||||
const targetRequest = targetResult.recordset?.[0];
|
||||
if (!targetRequest) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json({ success: false, message: 'Khong tim thay don muon can tra' });
|
||||
}
|
||||
|
||||
if (normalizeAssetRequestType(targetRequest.RequestType) !== 'borrow') {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ success: false, message: 'Chi co the tao don tra tu don muon' });
|
||||
}
|
||||
|
||||
if (normalizeAssetRequestStatus(targetRequest.RequestStatus) !== 'approved') {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ success: false, message: 'Chi co the tra tai san khi don dang o trang thai dang muon' });
|
||||
}
|
||||
|
||||
const originalQuantity = parseNonNegativeInteger(targetRequest.BorrowQuantity, 0);
|
||||
const returnedQuantity = parseNonNegativeInteger(targetRequest.ReturnedQuantity, 0);
|
||||
const activeReturnQuantity = parseNonNegativeInteger(targetRequest.ActiveReturnQuantity, 0);
|
||||
const availableReturnQuantity = Math.max(originalQuantity - Math.max(returnedQuantity, activeReturnQuantity), 0);
|
||||
|
||||
if (availableReturnQuantity <= 0) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ success: false, message: 'Don muon nay da co don tra hoac da tra het' });
|
||||
}
|
||||
|
||||
const borrowerName = String(targetRequest.BorrowerName || '').trim();
|
||||
const borrowedEntry = parseBorrowerEntries(targetRequest.Borrower)
|
||||
.find(entry => entry.name.toLowerCase() === borrowerName.toLowerCase());
|
||||
const currentBorrowedQuantity = parseNonNegativeInteger(borrowedEntry?.quantity, 0);
|
||||
const returnQuantity = availableReturnQuantity;
|
||||
|
||||
if (currentBorrowedQuantity < returnQuantity) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ success: false, message: 'Khong con so luong dang muon de tao don tra' });
|
||||
}
|
||||
|
||||
const originalCreatedBy = Number(targetRequest.CreatedBy);
|
||||
const createdBy = Number.isInteger(originalCreatedBy) && originalCreatedBy > 0
|
||||
? originalCreatedBy
|
||||
: requesterId;
|
||||
|
||||
const insertResult = await new sql.Request(transaction)
|
||||
.input('assetId', sql.Int, targetRequest.AssetId)
|
||||
.input('requestType', sql.NVarChar, 'return')
|
||||
.input('requestStatus', sql.NVarChar, 'pending')
|
||||
.input('borrowerName', sql.NVarChar, borrowerName)
|
||||
.input('borrowQuantity', sql.Int, returnQuantity)
|
||||
.input('unit', sql.NVarChar, String(targetRequest.Unit || '').trim() || null)
|
||||
.input('borrowDate', sql.Date, new Date())
|
||||
.input('requestNote', sql.NVarChar, `Tu dong tao tu don muon #${borrowId}`)
|
||||
.input('createdBy', sql.Int, createdBy || null)
|
||||
.query(`
|
||||
INSERT INTO AssetBorrowRequests (
|
||||
AssetId,
|
||||
RequestType,
|
||||
RequestStatus,
|
||||
BorrowerName,
|
||||
BorrowQuantity,
|
||||
Unit,
|
||||
BorrowDate,
|
||||
RequestNote,
|
||||
CreatedBy
|
||||
) VALUES (
|
||||
@assetId,
|
||||
@requestType,
|
||||
@requestStatus,
|
||||
@borrowerName,
|
||||
@borrowQuantity,
|
||||
@unit,
|
||||
@borrowDate,
|
||||
@requestNote,
|
||||
@createdBy
|
||||
);
|
||||
SELECT SCOPE_IDENTITY() AS BorrowId;
|
||||
`);
|
||||
|
||||
const returnRequestId = Number(insertResult.recordset?.[0]?.BorrowId) || null;
|
||||
if (!returnRequestId) {
|
||||
await transaction.rollback();
|
||||
return res.status(500).json({ success: false, message: 'Khong tao duoc don tra tai san' });
|
||||
}
|
||||
|
||||
await new sql.Request(transaction)
|
||||
.input('borrowId', sql.Int, borrowId)
|
||||
.input('returnId', sql.Int, returnRequestId)
|
||||
.input('quantity', sql.Int, returnQuantity)
|
||||
.query(`
|
||||
INSERT INTO AssetBorrowRequestLinks (BorrowId, ReturnId, Quantity)
|
||||
VALUES (@borrowId, @returnId, @quantity);
|
||||
`);
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Da tao don tra tai san. Don dang cho xu ly.',
|
||||
data: {
|
||||
borrowId: returnRequestId,
|
||||
sourceBorrowId: borrowId,
|
||||
quantity: returnQuantity
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
try {
|
||||
await transaction.rollback();
|
||||
} catch (rollbackErr) {
|
||||
// Ignore rollback errors when transaction already finished.
|
||||
}
|
||||
return res.status(500).json({ success: false, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/asset-borrows/:id/process', requireAssetOrAdmin, async (req, res) => {
|
||||
const transaction = new sql.Transaction(pool);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user