Compare commits
6 Commits
927317a87e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ff9826056 | |||
| 41e523ff35 | |||
| 12c6380ceb | |||
| cb4d5b9520 | |||
| e1b553ba79 | |||
| 4aa4ac0d57 |
@@ -3,6 +3,8 @@ FROM node:20-bookworm-slim
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TZ=Asia/Ho_Chi_Minh
|
||||
ENV APP_TIME_ZONE=Asia/Ho_Chi_Minh
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev && npm cache clean --force
|
||||
|
||||
1431
backend/server.js
1431
backend/server.js
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ BEGIN
|
||||
FullName NVARCHAR(100),
|
||||
Role NVARCHAR(50) NOT NULL,
|
||||
Status NVARCHAR(20) DEFAULT 'Active',
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
LastLogin DATETIME,
|
||||
IsActive BIT DEFAULT 1
|
||||
);
|
||||
@@ -51,8 +51,8 @@ BEGIN
|
||||
Status NVARCHAR(20) DEFAULT 'online',
|
||||
Icon NVARCHAR(50),
|
||||
Description NVARCHAR(500),
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||
);
|
||||
PRINT 'Table Applications created successfully.';
|
||||
END
|
||||
@@ -72,8 +72,8 @@ BEGIN
|
||||
AccessLevel NVARCHAR(50),
|
||||
Status NVARCHAR(20) DEFAULT 'Active',
|
||||
Notes NVARCHAR(MAX),
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE CASCADE,
|
||||
FOREIGN KEY (AppId) REFERENCES Applications(AppId) ON DELETE CASCADE
|
||||
);
|
||||
@@ -109,8 +109,8 @@ BEGIN
|
||||
Status NVARCHAR(30) NOT NULL DEFAULT 'in_use',
|
||||
Notes NVARCHAR(MAX),
|
||||
CreatedBy INT NULL,
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
||||
);
|
||||
PRINT 'Table AssetInventory created successfully.';
|
||||
@@ -175,8 +175,8 @@ BEGIN
|
||||
CREATE TABLE AssetDepartments (
|
||||
DepartmentId INT PRIMARY KEY IDENTITY(1,1),
|
||||
DepartmentName NVARCHAR(100) NOT NULL,
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||
);
|
||||
PRINT 'Table AssetDepartments created successfully.';
|
||||
END
|
||||
@@ -204,8 +204,8 @@ BEGIN
|
||||
CREATE TABLE AssetProjects (
|
||||
ProjectId INT PRIMARY KEY IDENTITY(1,1),
|
||||
ProjectName NVARCHAR(150) NOT NULL,
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||
);
|
||||
PRINT 'Table AssetProjects created successfully.';
|
||||
END
|
||||
@@ -222,16 +222,17 @@ BEGIN
|
||||
RequestStatus NVARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
BorrowerName NVARCHAR(100) NOT NULL,
|
||||
BorrowQuantity INT NOT NULL DEFAULT 1,
|
||||
ReturnedQuantity INT NOT NULL DEFAULT 0,
|
||||
Unit NVARCHAR(50),
|
||||
BorrowDate DATE NOT NULL DEFAULT CAST(GETDATE() AS DATE),
|
||||
BorrowDate DATE NOT NULL DEFAULT (CAST(DATEADD(HOUR, 7, SYSUTCDATETIME()) AS DATE)),
|
||||
RequestNote NVARCHAR(500) NULL,
|
||||
RejectReason NVARCHAR(1000) NULL,
|
||||
CreatedBy INT NULL,
|
||||
ProcessedBy INT NULL,
|
||||
ProcessedByName NVARCHAR(100) NULL,
|
||||
ProcessedDate DATETIME NULL,
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
||||
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE,
|
||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL,
|
||||
FOREIGN KEY (ProcessedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
||||
@@ -246,12 +247,12 @@ END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'BorrowDate') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetBorrowRequests ADD BorrowDate DATE NOT NULL CONSTRAINT DF_AssetBorrowRequests_BorrowDate DEFAULT(CAST(GETDATE() AS DATE));
|
||||
ALTER TABLE AssetBorrowRequests ADD BorrowDate DATE NOT NULL CONSTRAINT DF_AssetBorrowRequests_BorrowDate DEFAULT(CAST(DATEADD(HOUR, 7, SYSUTCDATETIME()) AS DATE));
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'UpdatedDate') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetBorrowRequests ADD UpdatedDate DATETIME NOT NULL CONSTRAINT DF_AssetBorrowRequests_UpdatedDate DEFAULT(GETDATE());
|
||||
ALTER TABLE AssetBorrowRequests ADD UpdatedDate DATETIME NOT NULL CONSTRAINT DF_AssetBorrowRequests_UpdatedDate DEFAULT(DATEADD(HOUR, 7, SYSUTCDATETIME()));
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'RequestType') IS NULL
|
||||
@@ -264,6 +265,11 @@ BEGIN
|
||||
ALTER TABLE AssetBorrowRequests ADD RequestStatus NVARCHAR(20) NOT NULL CONSTRAINT DF_AssetBorrowRequests_RequestStatus DEFAULT('approved');
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'ReturnedQuantity') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetBorrowRequests ADD ReturnedQuantity INT NOT NULL CONSTRAINT DF_AssetBorrowRequests_ReturnedQuantity DEFAULT(0);
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'RequestNote') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetBorrowRequests ADD RequestNote NVARCHAR(500) NULL;
|
||||
@@ -302,6 +308,83 @@ SET RequestType = ISNULL(NULLIF(LTRIM(RTRIM(RequestType)), ''), 'borrow');
|
||||
UPDATE AssetBorrowRequests
|
||||
SET RequestStatus = ISNULL(NULLIF(LTRIM(RTRIM(RequestStatus)), ''), 'approved');
|
||||
|
||||
UPDATE AssetBorrowRequests
|
||||
SET ReturnedQuantity = 0
|
||||
WHERE ReturnedQuantity IS NULL OR ReturnedQuantity < 0;
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AssetBorrowRequestLinks')
|
||||
BEGIN
|
||||
CREATE TABLE AssetBorrowRequestLinks (
|
||||
LinkId INT PRIMARY KEY IDENTITY(1,1),
|
||||
BorrowId INT NOT NULL,
|
||||
ReturnId INT NOT NULL,
|
||||
Quantity INT NOT NULL CONSTRAINT DF_AssetBorrowRequestLinks_Quantity DEFAULT(1),
|
||||
CreatedDate DATETIME NOT NULL CONSTRAINT DF_AssetBorrowRequestLinks_CreatedDate DEFAULT(DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (BorrowId) REFERENCES AssetBorrowRequests(BorrowId) ON DELETE NO ACTION,
|
||||
FOREIGN KEY (ReturnId) REFERENCES AssetBorrowRequests(BorrowId) ON DELETE NO ACTION
|
||||
);
|
||||
END
|
||||
|
||||
IF OBJECT_ID('dbo.AssetBorrowRequestLinks', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
INSERT INTO AssetBorrowRequestLinks (BorrowId, ReturnId, Quantity)
|
||||
SELECT matchedBorrow.BorrowId,
|
||||
ret.BorrowId,
|
||||
CASE
|
||||
WHEN ISNULL(ret.BorrowQuantity, 0) <= 0 THEN 1
|
||||
WHEN ISNULL(ret.BorrowQuantity, 0) > ISNULL(matchedBorrow.BorrowQuantity, 0) THEN ISNULL(matchedBorrow.BorrowQuantity, 0)
|
||||
ELSE ISNULL(ret.BorrowQuantity, 0)
|
||||
END
|
||||
FROM AssetBorrowRequests ret
|
||||
CROSS APPLY (
|
||||
SELECT TOP 1 b.BorrowId, b.BorrowQuantity
|
||||
FROM AssetBorrowRequests b
|
||||
WHERE LOWER(LTRIM(RTRIM(ISNULL(b.RequestType, '')))) = 'borrow'
|
||||
AND LOWER(LTRIM(RTRIM(ISNULL(b.RequestStatus, '')))) IN ('approved', 'returned')
|
||||
AND b.AssetId = ret.AssetId
|
||||
AND (
|
||||
(ret.CreatedBy IS NOT NULL AND b.CreatedBy = ret.CreatedBy)
|
||||
OR LOWER(LTRIM(RTRIM(ISNULL(b.BorrowerName, '')))) = LOWER(LTRIM(RTRIM(ISNULL(ret.BorrowerName, ''))))
|
||||
)
|
||||
AND b.BorrowDate <= ret.BorrowDate
|
||||
ORDER BY b.BorrowDate DESC, b.CreatedDate DESC, b.BorrowId DESC
|
||||
) matchedBorrow
|
||||
WHERE LOWER(LTRIM(RTRIM(ISNULL(ret.RequestType, '')))) = 'return'
|
||||
AND LOWER(LTRIM(RTRIM(ISNULL(ret.RequestStatus, '')))) IN ('pending', 'approved')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM AssetBorrowRequestLinks existed
|
||||
WHERE existed.ReturnId = ret.BorrowId
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM AssetBorrowRequestLinks duplicateLink
|
||||
WHERE duplicateLink.ReturnId = ret.BorrowId
|
||||
AND duplicateLink.BorrowId = matchedBorrow.BorrowId
|
||||
);
|
||||
|
||||
UPDATE borrowRows
|
||||
SET ReturnedQuantity = CASE
|
||||
WHEN summary.ReturnedQuantity > ISNULL(borrowRows.BorrowQuantity, 0) THEN ISNULL(borrowRows.BorrowQuantity, 0)
|
||||
ELSE summary.ReturnedQuantity
|
||||
END,
|
||||
RequestStatus = CASE
|
||||
WHEN summary.ReturnedQuantity >= ISNULL(borrowRows.BorrowQuantity, 0)
|
||||
THEN 'returned'
|
||||
ELSE borrowRows.RequestStatus
|
||||
END,
|
||||
UpdatedDate = DATEADD(HOUR, 7, SYSUTCDATETIME())
|
||||
FROM AssetBorrowRequests borrowRows
|
||||
INNER JOIN (
|
||||
SELECT links.BorrowId, SUM(ISNULL(links.Quantity, 0)) AS ReturnedQuantity
|
||||
FROM AssetBorrowRequestLinks links
|
||||
INNER JOIN AssetBorrowRequests returns ON returns.BorrowId = links.ReturnId
|
||||
WHERE LOWER(LTRIM(RTRIM(ISNULL(returns.RequestStatus, '')))) = 'approved'
|
||||
GROUP BY links.BorrowId
|
||||
) summary ON summary.BorrowId = borrowRows.BorrowId
|
||||
WHERE LOWER(LTRIM(RTRIM(ISNULL(borrowRows.RequestType, '')))) = 'borrow';
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 8. CREATE ASSET EXPORT HISTORY TABLE
|
||||
-- ===========================================
|
||||
@@ -318,9 +401,9 @@ BEGIN
|
||||
ExportedByName NVARCHAR(100) NOT NULL,
|
||||
ExportNote NVARCHAR(1000) NULL,
|
||||
CreatedBy INT NULL,
|
||||
ExportedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
||||
CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
||||
ExportedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
CreatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE
|
||||
);
|
||||
PRINT 'Table AssetExportHistory created successfully.';
|
||||
@@ -368,17 +451,17 @@ END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetExportHistory', 'ExportedDate') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetExportHistory ADD ExportedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_ExportedDate DEFAULT(GETDATE());
|
||||
ALTER TABLE AssetExportHistory ADD ExportedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_ExportedDate DEFAULT(DATEADD(HOUR, 7, SYSUTCDATETIME()));
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetExportHistory', 'CreatedDate') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetExportHistory ADD CreatedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_CreatedDate DEFAULT(GETDATE());
|
||||
ALTER TABLE AssetExportHistory ADD CreatedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_CreatedDate DEFAULT(DATEADD(HOUR, 7, SYSUTCDATETIME()));
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetExportHistory', 'UpdatedDate') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetExportHistory ADD UpdatedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_UpdatedDate DEFAULT(GETDATE());
|
||||
ALTER TABLE AssetExportHistory ADD UpdatedDate DATETIME NOT NULL CONSTRAINT DF_AssetExportHistory_UpdatedDate DEFAULT(DATEADD(HOUR, 7, SYSUTCDATETIME()));
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_AssetExportHistory_CreatedBy')
|
||||
@@ -401,7 +484,45 @@ BEGIN
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 9. CREATE AUDIT LOG TABLE
|
||||
-- 9. CREATE ASSET DAMAGE/DISPOSAL HISTORY TABLE
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AssetDamageDisposalHistory')
|
||||
BEGIN
|
||||
CREATE TABLE AssetDamageDisposalHistory (
|
||||
DamageHistoryId INT PRIMARY KEY IDENTITY(1,1),
|
||||
AssetId INT NOT NULL,
|
||||
AssetCode NVARCHAR(100) NOT NULL,
|
||||
AssetName NVARCHAR(255) NOT NULL,
|
||||
ActionType NVARCHAR(20) NOT NULL,
|
||||
ActionLabel NVARCHAR(50) NOT NULL,
|
||||
ActionQuantity INT NOT NULL DEFAULT 1,
|
||||
Unit NVARCHAR(50) NULL,
|
||||
PreviousQuantity INT NOT NULL DEFAULT 0,
|
||||
NextQuantity INT NOT NULL DEFAULT 0,
|
||||
PreviousImportInPeriod INT NOT NULL DEFAULT 0,
|
||||
NextImportInPeriod INT NOT NULL DEFAULT 0,
|
||||
PreviousExportInPeriod INT NOT NULL DEFAULT 0,
|
||||
NextExportInPeriod INT NOT NULL DEFAULT 0,
|
||||
PreviousEndingBalance INT NOT NULL DEFAULT 0,
|
||||
NextEndingBalance INT NOT NULL DEFAULT 0,
|
||||
PreviousNewQuantity INT NOT NULL DEFAULT 0,
|
||||
NextNewQuantity INT NOT NULL DEFAULT 0,
|
||||
PreviousUsedQuantity INT NOT NULL DEFAULT 0,
|
||||
NextUsedQuantity INT NOT NULL DEFAULT 0,
|
||||
ActionNote NVARCHAR(1000) NULL,
|
||||
CreatedBy INT NULL,
|
||||
CreatedByName NVARCHAR(100) NULL,
|
||||
ActionDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
CreatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
UpdatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE,
|
||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
||||
);
|
||||
PRINT 'Table AssetDamageDisposalHistory created successfully.';
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 10. CREATE AUDIT LOG TABLE
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AuditLog')
|
||||
BEGIN
|
||||
@@ -413,14 +534,14 @@ BEGIN
|
||||
RecordId INT,
|
||||
OldValue NVARCHAR(MAX),
|
||||
NewValue NVARCHAR(MAX),
|
||||
Timestamp DATETIME DEFAULT GETDATE(),
|
||||
Timestamp DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||
FOREIGN KEY (UserId) REFERENCES Users(UserId)
|
||||
);
|
||||
PRINT 'Table AuditLog created successfully.';
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 10. CREATE INDEXES
|
||||
-- 11. CREATE INDEXES
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Users_Username')
|
||||
BEGIN
|
||||
@@ -482,6 +603,21 @@ BEGIN
|
||||
CREATE INDEX IX_AssetBorrowRequests_RequestType ON AssetBorrowRequests(RequestType);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetBorrowRequestLinks_BorrowId')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetBorrowRequestLinks_BorrowId ON AssetBorrowRequestLinks(BorrowId);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetBorrowRequestLinks_ReturnId')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetBorrowRequestLinks_ReturnId ON AssetBorrowRequestLinks(ReturnId);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'UX_AssetBorrowRequestLinks_BorrowReturn')
|
||||
BEGIN
|
||||
CREATE UNIQUE INDEX UX_AssetBorrowRequestLinks_BorrowReturn ON AssetBorrowRequestLinks(BorrowId, ReturnId);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetExportHistory_AssetId')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetExportHistory_AssetId ON AssetExportHistory(AssetId);
|
||||
@@ -492,10 +628,25 @@ BEGIN
|
||||
CREATE INDEX IX_AssetExportHistory_ExportedDate ON AssetExportHistory(ExportedDate DESC);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetDamageDisposalHistory_AssetId')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetDamageDisposalHistory_AssetId ON AssetDamageDisposalHistory(AssetId);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetDamageDisposalHistory_ActionDate')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetDamageDisposalHistory_ActionDate ON AssetDamageDisposalHistory(ActionDate DESC);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetDamageDisposalHistory_ActionType')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetDamageDisposalHistory_ActionType ON AssetDamageDisposalHistory(ActionType);
|
||||
END
|
||||
|
||||
PRINT 'Indexes created successfully.';
|
||||
|
||||
-- ===========================================
|
||||
-- 11. INSERT INITIAL DATA
|
||||
-- 12. INSERT INITIAL DATA
|
||||
-- ===========================================
|
||||
|
||||
-- Check if admin user exists
|
||||
|
||||
@@ -8,6 +8,8 @@ services:
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
PORT: 3000
|
||||
TZ: ${TZ:-Asia/Ho_Chi_Minh}
|
||||
APP_TIME_ZONE: ${APP_TIME_ZONE:-Asia/Ho_Chi_Minh}
|
||||
DB_SERVER: ${DB_SERVER:-172.20.235.176}
|
||||
DB_USER: ${DB_USER:-sa}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-changeme}
|
||||
|
||||
@@ -10,6 +10,8 @@ services:
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
TZ: ${TZ:-Asia/Ho_Chi_Minh}
|
||||
APP_TIME_ZONE: ${APP_TIME_ZONE:-Asia/Ho_Chi_Minh}
|
||||
DB_SERVER: ${DB_SERVER:-172.20.235.176}
|
||||
DB_USER: ${DB_USER:-sa}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-changeme}
|
||||
|
||||
817
public/js/app.js
817
public/js/app.js
File diff suppressed because it is too large
Load Diff
@@ -357,6 +357,66 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Damage/Disposal Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetDamageModal">
|
||||
<div class="modal-content w-full max-w-lg bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
|
||||
<h3 class="text-base font-extrabold text-slate-900">Hỏng / Thanh lý tài sản</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeAssetDamageModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="assetDamageForm" class="p-6 space-y-4">
|
||||
<input type="hidden" id="assetDamageAssetIdInput">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tài sản</label>
|
||||
<input type="text" id="assetDamageAssetNameInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Lý do</label>
|
||||
<select id="assetDamageTypeInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required>
|
||||
<option value="damaged">Hỏng</option>
|
||||
<option value="disposed">Thanh lý</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Số lượng</label>
|
||||
<input type="number" id="assetDamageQuantityInput" min="1" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" value="1" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Số lượng hiện tại</label>
|
||||
<input type="number" id="assetDamageCurrentQuantityInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tồn cuối kỳ hiện tại</label>
|
||||
<input type="number" id="assetDamageCurrentEndingInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">SL hàng mới</label>
|
||||
<input type="number" id="assetDamageCurrentNewInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">SL đã qua sử dụng</label>
|
||||
<input type="number" id="assetDamageCurrentUsedInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Người thao tác</label>
|
||||
<input type="text" id="assetDamageActorInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Ghi chú</label>
|
||||
<textarea id="assetDamageNoteInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 h-20 resize-none" placeholder="Nhập tình trạng, biên bản hoặc lý do chi tiết"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeAssetDamageModal()">Hủy</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-xs font-bold">Xác nhận</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Export History Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[110] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetExportHistoryModal" style="z-index: 125;">
|
||||
<div class="modal-content w-full max-w-6xl bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4 flex flex-col" style="max-height: calc(100vh - 2rem);">
|
||||
@@ -393,6 +453,45 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Damage/Disposal History Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[110] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetDamageHistoryModal" style="z-index: 126;">
|
||||
<div class="modal-content w-full max-w-6xl bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4 flex flex-col" style="max-height: calc(100vh - 2rem);">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
|
||||
<h3 class="text-base font-extrabold text-slate-900">Tài sản hỏng / thanh lý</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeAssetDamageHistoryModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 pt-4 overflow-auto">
|
||||
<div class="rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse" style="min-width: 1280px;">
|
||||
<thead class="bg-slate-50 border-b border-slate-200">
|
||||
<tr>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ngày giờ</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Lý do</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Tài sản</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Số lượng</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Tồn đầu kỳ</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Tồn cuối kỳ</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">SL hàng mới</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">SL đã sử dụng</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Người thao tác</th>
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ghi chú</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="assetDamageHistoryTableBody" class="divide-y divide-slate-100">
|
||||
<tr>
|
||||
<td colspan="10" class="px-4 py-8 text-sm text-center text-slate-500">Chưa có dữ liệu tài sản hỏng/thanh lý.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Borrow Request Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetBorrowRequestModal">
|
||||
<div class="modal-content w-full max-w-lg bg-white rounded-xl shadow-2xl border border-slate-200 overflow-visible m-4">
|
||||
|
||||
@@ -309,6 +309,6 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="../js/app.js?v=20260424-1"></script>
|
||||
<script src="../js/app.js?v=20260515-5"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user