fix mươn tài sản đã xuất
This commit is contained in:
@@ -4000,6 +4000,8 @@ app.post('/api/asset-borrows', async (req, res) => {
|
|||||||
AssetName,
|
AssetName,
|
||||||
Quantity,
|
Quantity,
|
||||||
ImportInPeriod,
|
ImportInPeriod,
|
||||||
|
ExportInPeriod,
|
||||||
|
EndingBalance,
|
||||||
Borrower,
|
Borrower,
|
||||||
Unit
|
Unit
|
||||||
FROM AssetInventory
|
FROM AssetInventory
|
||||||
@@ -4015,10 +4017,12 @@ app.post('/api/asset-borrows', async (req, res) => {
|
|||||||
const currentBorrowed = currentBorrowedEntries.reduce((sum, entry) => (
|
const currentBorrowed = currentBorrowedEntries.reduce((sum, entry) => (
|
||||||
sum + parseNonNegativeInteger(entry?.quantity, 0)
|
sum + parseNonNegativeInteger(entry?.quantity, 0)
|
||||||
), 0);
|
), 0);
|
||||||
const endingBalance = Math.max(
|
const derivedEndingBalance = Math.max(
|
||||||
parseNonNegativeInteger(asset.Quantity, 0) + parseNonNegativeInteger(asset.ImportInPeriod, 0) - currentBorrowed,
|
parseNonNegativeInteger(asset.Quantity, 0) + parseNonNegativeInteger(asset.ImportInPeriod, 0) - currentBorrowed,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
const storedEndingBalance = parseOptionalNonNegativeInteger(asset.EndingBalance);
|
||||||
|
const endingBalance = storedEndingBalance !== null ? storedEndingBalance : derivedEndingBalance;
|
||||||
|
|
||||||
const unit = String(req.body?.unit || '').trim() || String(asset.Unit || '').trim() || null;
|
const unit = String(req.body?.unit || '').trim() || String(asset.Unit || '').trim() || null;
|
||||||
|
|
||||||
@@ -4836,12 +4840,14 @@ app.get('/api/assets/search', async (req, res) => {
|
|||||||
const keywordLike = `%${rawKeyword}%`;
|
const keywordLike = `%${rawKeyword}%`;
|
||||||
const limit = Math.min(parsePositiveInteger(req.query.limit, 80), 200);
|
const limit = Math.min(parsePositiveInteger(req.query.limit, 80), 200);
|
||||||
const offset = parseNonNegativeInteger(req.query.offset, 0);
|
const offset = parseNonNegativeInteger(req.query.offset, 0);
|
||||||
|
const borrowableOnly = ['1', 'true', 'yes'].includes(String(req.query.borrowableOnly || '').trim().toLowerCase());
|
||||||
|
|
||||||
const result = await pool.request()
|
const result = await pool.request()
|
||||||
.input('limit', sql.Int, limit)
|
.input('limit', sql.Int, limit)
|
||||||
.input('offset', sql.Int, offset)
|
.input('offset', sql.Int, offset)
|
||||||
.input('keyword', sql.NVarChar, rawKeyword)
|
.input('keyword', sql.NVarChar, rawKeyword)
|
||||||
.input('keywordLike', sql.NVarChar, keywordLike)
|
.input('keywordLike', sql.NVarChar, keywordLike)
|
||||||
|
.input('borrowableOnly', sql.Bit, borrowableOnly ? 1 : 0)
|
||||||
.query(`
|
.query(`
|
||||||
;WITH FilteredAssets AS (
|
;WITH FilteredAssets AS (
|
||||||
SELECT
|
SELECT
|
||||||
@@ -4849,14 +4855,23 @@ app.get('/api/assets/search', async (req, res) => {
|
|||||||
AssetCode,
|
AssetCode,
|
||||||
AssetName,
|
AssetName,
|
||||||
Unit,
|
Unit,
|
||||||
|
EndingBalance,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(EndingBalance, 0) <= 0 THEN 'exported'
|
||||||
|
WHEN ISNULL(ExportInPeriod, 0) > 0 THEN 'in_use'
|
||||||
|
ELSE 'in_stock'
|
||||||
|
END AS Status,
|
||||||
UpdatedDate,
|
UpdatedDate,
|
||||||
CASE WHEN @keyword <> '' AND AssetCode LIKE @keywordLike THEN 0 ELSE 1 END AS CodeRank,
|
CASE WHEN @keyword <> '' AND AssetCode LIKE @keywordLike THEN 0 ELSE 1 END AS CodeRank,
|
||||||
CASE WHEN @keyword <> '' AND AssetName LIKE @keywordLike THEN 0 ELSE 1 END AS NameRank
|
CASE WHEN @keyword <> '' AND AssetName LIKE @keywordLike THEN 0 ELSE 1 END AS NameRank
|
||||||
FROM AssetInventory
|
FROM AssetInventory
|
||||||
WHERE @keyword = ''
|
WHERE (@borrowableOnly = 0 OR ISNULL(EndingBalance, 0) > 0)
|
||||||
|
AND (
|
||||||
|
@keyword = ''
|
||||||
OR AssetCode LIKE @keywordLike
|
OR AssetCode LIKE @keywordLike
|
||||||
OR AssetName LIKE @keywordLike
|
OR AssetName LIKE @keywordLike
|
||||||
OR Model LIKE @keywordLike
|
OR Model LIKE @keywordLike
|
||||||
|
)
|
||||||
),
|
),
|
||||||
OrderedAssets AS (
|
OrderedAssets AS (
|
||||||
SELECT
|
SELECT
|
||||||
@@ -4864,6 +4879,8 @@ app.get('/api/assets/search', async (req, res) => {
|
|||||||
AssetCode,
|
AssetCode,
|
||||||
AssetName,
|
AssetName,
|
||||||
Unit,
|
Unit,
|
||||||
|
EndingBalance,
|
||||||
|
Status,
|
||||||
ROW_NUMBER() OVER (
|
ROW_NUMBER() OVER (
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CodeRank ASC,
|
CodeRank ASC,
|
||||||
@@ -4873,17 +4890,20 @@ app.get('/api/assets/search', async (req, res) => {
|
|||||||
) AS RowNum
|
) AS RowNum
|
||||||
FROM FilteredAssets
|
FROM FilteredAssets
|
||||||
)
|
)
|
||||||
SELECT AssetId, AssetCode, AssetName, Unit
|
SELECT AssetId, AssetCode, AssetName, Unit, EndingBalance, Status
|
||||||
FROM OrderedAssets
|
FROM OrderedAssets
|
||||||
WHERE RowNum > @offset AND RowNum <= (@offset + @limit)
|
WHERE RowNum > @offset AND RowNum <= (@offset + @limit)
|
||||||
ORDER BY RowNum;
|
ORDER BY RowNum;
|
||||||
|
|
||||||
SELECT COUNT(*) AS TotalCount
|
SELECT COUNT(*) AS TotalCount
|
||||||
FROM AssetInventory
|
FROM AssetInventory
|
||||||
WHERE @keyword = ''
|
WHERE (@borrowableOnly = 0 OR ISNULL(EndingBalance, 0) > 0)
|
||||||
|
AND (
|
||||||
|
@keyword = ''
|
||||||
OR AssetCode LIKE @keywordLike
|
OR AssetCode LIKE @keywordLike
|
||||||
OR AssetName LIKE @keywordLike
|
OR AssetName LIKE @keywordLike
|
||||||
OR Model LIKE @keywordLike;
|
OR Model LIKE @keywordLike
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const rows = Array.isArray(result.recordsets?.[0]) ? result.recordsets[0] : [];
|
const rows = Array.isArray(result.recordsets?.[0]) ? result.recordsets[0] : [];
|
||||||
|
|||||||
@@ -525,15 +525,40 @@ class AccountManager {
|
|||||||
return `${code} - ${name}`.replace(/^\s*-\s*|\s*-\s*$/g, '').trim() || name || '-- Chọn tài sản --';
|
return `${code} - ${name}`.replace(/^\s*-\s*|\s*-\s*$/g, '').trim() || name || '-- Chọn tài sản --';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isBorrowAssetRequestMode() {
|
||||||
|
const typeInput = document.getElementById('assetBorrowRequestTypeInput');
|
||||||
|
return this.normalizeAssetRequestType(typeInput?.value || this.assetBorrowRequestType) === 'borrow';
|
||||||
|
}
|
||||||
|
|
||||||
|
isAssetAvailableForBorrow(asset) {
|
||||||
|
if (!asset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endingBalance = this.parseOptionalNonNegativeInteger(asset?.EndingBalance ?? asset?.endingBalance);
|
||||||
|
if (endingBalance !== null) {
|
||||||
|
return endingBalance > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = String(asset?.Status || asset?.status || '').trim().toLowerCase();
|
||||||
|
return status !== 'exported';
|
||||||
|
}
|
||||||
|
|
||||||
getAssetBorrowProductById(assetIdValue) {
|
getAssetBorrowProductById(assetIdValue) {
|
||||||
const assetId = Number(assetIdValue);
|
const assetId = Number(assetIdValue);
|
||||||
if (!Number.isFinite(assetId) || assetId <= 0) {
|
if (!Number.isFinite(assetId) || assetId <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.assetBorrowProductItems.find(item => Number(item?.AssetId) === assetId)
|
const asset = this.assetBorrowProductItems.find(item => Number(item?.AssetId) === assetId)
|
||||||
|| this.assets.find(item => Number(item?.AssetId) === assetId)
|
|| this.assets.find(item => Number(item?.AssetId) === assetId)
|
||||||
|| null;
|
|| null;
|
||||||
|
|
||||||
|
if (this.isBorrowAssetRequestMode() && !this.isAssetAvailableForBorrow(asset)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAssetBorrowProductDisplay(assetIdValue) {
|
updateAssetBorrowProductDisplay(assetIdValue) {
|
||||||
@@ -654,9 +679,12 @@ class AccountManager {
|
|||||||
const encodedKeyword = encodeURIComponent(this.assetBorrowProductQuery);
|
const encodedKeyword = encodeURIComponent(this.assetBorrowProductQuery);
|
||||||
const offset = this.assetBorrowProductOffset;
|
const offset = this.assetBorrowProductOffset;
|
||||||
const limit = this.assetBorrowProductLimit;
|
const limit = this.assetBorrowProductLimit;
|
||||||
|
const borrowableOnly = this.isBorrowAssetRequestMode();
|
||||||
|
|
||||||
const appendRows = (rows = [], hasMore = false) => {
|
const appendRows = (rows = [], hasMore = false) => {
|
||||||
const source = Array.isArray(rows) ? rows : [];
|
const rowsArray = Array.isArray(rows) ? rows : [];
|
||||||
|
const source = rowsArray
|
||||||
|
.filter(asset => !borrowableOnly || this.isAssetAvailableForBorrow(asset));
|
||||||
if (source.length) {
|
if (source.length) {
|
||||||
const merged = new Map(
|
const merged = new Map(
|
||||||
this.assetBorrowProductItems.map(item => [String(item.AssetId), item])
|
this.assetBorrowProductItems.map(item => [String(item.AssetId), item])
|
||||||
@@ -665,8 +693,8 @@ class AccountManager {
|
|||||||
merged.set(String(item.AssetId), item);
|
merged.set(String(item.AssetId), item);
|
||||||
});
|
});
|
||||||
this.assetBorrowProductItems = Array.from(merged.values());
|
this.assetBorrowProductItems = Array.from(merged.values());
|
||||||
this.assetBorrowProductOffset += source.length;
|
|
||||||
}
|
}
|
||||||
|
this.assetBorrowProductOffset += rowsArray.length;
|
||||||
|
|
||||||
this.assetBorrowProductHasMore = Boolean(hasMore);
|
this.assetBorrowProductHasMore = Boolean(hasMore);
|
||||||
this.assetBorrowProductLoading = false;
|
this.assetBorrowProductLoading = false;
|
||||||
@@ -686,6 +714,10 @@ class AccountManager {
|
|||||||
const source = Array.isArray(this.assets) ? this.assets : [];
|
const source = Array.isArray(this.assets) ? this.assets : [];
|
||||||
const normalized = this.assetBorrowProductQuery.toLowerCase();
|
const normalized = this.assetBorrowProductQuery.toLowerCase();
|
||||||
const filtered = source.filter(asset => {
|
const filtered = source.filter(asset => {
|
||||||
|
if (borrowableOnly && !this.isAssetAvailableForBorrow(asset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -705,7 +737,7 @@ class AccountManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBase}/assets/search?q=${encodedKeyword}&limit=${limit}&offset=${offset}`, {
|
const response = await fetch(`${this.apiBase}/assets/search?q=${encodedKeyword}&limit=${limit}&offset=${offset}&borrowableOnly=${borrowableOnly ? '1' : '0'}`, {
|
||||||
headers: this.getAuthHeaders(false)
|
headers: this.getAuthHeaders(false)
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -3773,7 +3805,7 @@ class AccountManager {
|
|||||||
|
|
||||||
await this.searchAssetBorrowProducts('', '', { reset: true });
|
await this.searchAssetBorrowProducts('', '', { reset: true });
|
||||||
if (!this.assetBorrowProductItems.length) {
|
if (!this.assetBorrowProductItems.length) {
|
||||||
this.notifyWarning('Hiện chưa có tài sản để tạo đơn.');
|
this.notifyWarning(isReturnRequest ? 'Hiện chưa có tài sản để tạo đơn trả.' : 'Hiện chưa có tài sản còn tồn cuối kỳ để tạo đơn mượn.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3805,6 +3837,19 @@ class AccountManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedAsset = this.assetBorrowProductItems.find(item => Number(item?.AssetId) === assetId)
|
||||||
|
|| this.assets.find(item => Number(item?.AssetId) === assetId)
|
||||||
|
|| null;
|
||||||
|
if (requestType === 'borrow' && selectedAsset && !this.isAssetAvailableForBorrow(selectedAsset)) {
|
||||||
|
this.notifyWarning('Tài sản đã xuất hoặc hết tồn cuối kỳ, không thể tạo đơn mượn.');
|
||||||
|
await this.searchAssetBorrowProducts(
|
||||||
|
document.getElementById('assetBorrowProductSearchInput')?.value || '',
|
||||||
|
'',
|
||||||
|
{ reset: true }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const borrowDate = String(dateInput?.value || '').trim() || this.toDateInputValue(new Date());
|
const borrowDate = String(dateInput?.value || '').trim() || this.toDateInputValue(new Date());
|
||||||
const unit = String(unitInput?.value || '').trim();
|
const unit = String(unitInput?.value || '').trim();
|
||||||
const borrowerName = String(requesterInput?.value || this.getCurrentUserDisplayName() || '').trim();
|
const borrowerName = String(requesterInput?.value || this.getCurrentUserDisplayName() || '').trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user