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
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ENV TZ=Asia/Ho_Chi_Minh
|
||||||
|
ENV APP_TIME_ZONE=Asia/Ho_Chi_Minh
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci --omit=dev && npm cache clean --force
|
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),
|
FullName NVARCHAR(100),
|
||||||
Role NVARCHAR(50) NOT NULL,
|
Role NVARCHAR(50) NOT NULL,
|
||||||
Status NVARCHAR(20) DEFAULT 'Active',
|
Status NVARCHAR(20) DEFAULT 'Active',
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
LastLogin DATETIME,
|
LastLogin DATETIME,
|
||||||
IsActive BIT DEFAULT 1
|
IsActive BIT DEFAULT 1
|
||||||
);
|
);
|
||||||
@@ -51,8 +51,8 @@ BEGIN
|
|||||||
Status NVARCHAR(20) DEFAULT 'online',
|
Status NVARCHAR(20) DEFAULT 'online',
|
||||||
Icon NVARCHAR(50),
|
Icon NVARCHAR(50),
|
||||||
Description NVARCHAR(500),
|
Description NVARCHAR(500),
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||||
);
|
);
|
||||||
PRINT 'Table Applications created successfully.';
|
PRINT 'Table Applications created successfully.';
|
||||||
END
|
END
|
||||||
@@ -72,8 +72,8 @@ BEGIN
|
|||||||
AccessLevel NVARCHAR(50),
|
AccessLevel NVARCHAR(50),
|
||||||
Status NVARCHAR(20) DEFAULT 'Active',
|
Status NVARCHAR(20) DEFAULT 'Active',
|
||||||
Notes NVARCHAR(MAX),
|
Notes NVARCHAR(MAX),
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE CASCADE,
|
FOREIGN KEY (UserId) REFERENCES Users(UserId) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (AppId) REFERENCES Applications(AppId) 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',
|
Status NVARCHAR(30) NOT NULL DEFAULT 'in_use',
|
||||||
Notes NVARCHAR(MAX),
|
Notes NVARCHAR(MAX),
|
||||||
CreatedBy INT NULL,
|
CreatedBy INT NULL,
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
PRINT 'Table AssetInventory created successfully.';
|
PRINT 'Table AssetInventory created successfully.';
|
||||||
@@ -175,8 +175,8 @@ BEGIN
|
|||||||
CREATE TABLE AssetDepartments (
|
CREATE TABLE AssetDepartments (
|
||||||
DepartmentId INT PRIMARY KEY IDENTITY(1,1),
|
DepartmentId INT PRIMARY KEY IDENTITY(1,1),
|
||||||
DepartmentName NVARCHAR(100) NOT NULL,
|
DepartmentName NVARCHAR(100) NOT NULL,
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||||
);
|
);
|
||||||
PRINT 'Table AssetDepartments created successfully.';
|
PRINT 'Table AssetDepartments created successfully.';
|
||||||
END
|
END
|
||||||
@@ -204,8 +204,8 @@ BEGIN
|
|||||||
CREATE TABLE AssetProjects (
|
CREATE TABLE AssetProjects (
|
||||||
ProjectId INT PRIMARY KEY IDENTITY(1,1),
|
ProjectId INT PRIMARY KEY IDENTITY(1,1),
|
||||||
ProjectName NVARCHAR(150) NOT NULL,
|
ProjectName NVARCHAR(150) NOT NULL,
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE()
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME()))
|
||||||
);
|
);
|
||||||
PRINT 'Table AssetProjects created successfully.';
|
PRINT 'Table AssetProjects created successfully.';
|
||||||
END
|
END
|
||||||
@@ -222,16 +222,17 @@ BEGIN
|
|||||||
RequestStatus NVARCHAR(20) NOT NULL DEFAULT 'pending',
|
RequestStatus NVARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||||
BorrowerName NVARCHAR(100) NOT NULL,
|
BorrowerName NVARCHAR(100) NOT NULL,
|
||||||
BorrowQuantity INT NOT NULL DEFAULT 1,
|
BorrowQuantity INT NOT NULL DEFAULT 1,
|
||||||
|
ReturnedQuantity INT NOT NULL DEFAULT 0,
|
||||||
Unit NVARCHAR(50),
|
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,
|
RequestNote NVARCHAR(500) NULL,
|
||||||
RejectReason NVARCHAR(1000) NULL,
|
RejectReason NVARCHAR(1000) NULL,
|
||||||
CreatedBy INT NULL,
|
CreatedBy INT NULL,
|
||||||
ProcessedBy INT NULL,
|
ProcessedBy INT NULL,
|
||||||
ProcessedByName NVARCHAR(100) NULL,
|
ProcessedByName NVARCHAR(100) NULL,
|
||||||
ProcessedDate DATETIME NULL,
|
ProcessedDate DATETIME NULL,
|
||||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
CreatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
UpdatedDate DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE,
|
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL,
|
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL,
|
||||||
FOREIGN KEY (ProcessedBy) 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
|
IF COL_LENGTH('dbo.AssetBorrowRequests', 'BorrowDate') IS NULL
|
||||||
BEGIN
|
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
|
END
|
||||||
|
|
||||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'UpdatedDate') IS NULL
|
IF COL_LENGTH('dbo.AssetBorrowRequests', 'UpdatedDate') IS NULL
|
||||||
BEGIN
|
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
|
END
|
||||||
|
|
||||||
IF COL_LENGTH('dbo.AssetBorrowRequests', 'RequestType') IS NULL
|
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');
|
ALTER TABLE AssetBorrowRequests ADD RequestStatus NVARCHAR(20) NOT NULL CONSTRAINT DF_AssetBorrowRequests_RequestStatus DEFAULT('approved');
|
||||||
END
|
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
|
IF COL_LENGTH('dbo.AssetBorrowRequests', 'RequestNote') IS NULL
|
||||||
BEGIN
|
BEGIN
|
||||||
ALTER TABLE AssetBorrowRequests ADD RequestNote NVARCHAR(500) NULL;
|
ALTER TABLE AssetBorrowRequests ADD RequestNote NVARCHAR(500) NULL;
|
||||||
@@ -302,6 +308,83 @@ SET RequestType = ISNULL(NULLIF(LTRIM(RTRIM(RequestType)), ''), 'borrow');
|
|||||||
UPDATE AssetBorrowRequests
|
UPDATE AssetBorrowRequests
|
||||||
SET RequestStatus = ISNULL(NULLIF(LTRIM(RTRIM(RequestStatus)), ''), 'approved');
|
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
|
-- 8. CREATE ASSET EXPORT HISTORY TABLE
|
||||||
-- ===========================================
|
-- ===========================================
|
||||||
@@ -318,9 +401,9 @@ BEGIN
|
|||||||
ExportedByName NVARCHAR(100) NOT NULL,
|
ExportedByName NVARCHAR(100) NOT NULL,
|
||||||
ExportNote NVARCHAR(1000) NULL,
|
ExportNote NVARCHAR(1000) NULL,
|
||||||
CreatedBy INT NULL,
|
CreatedBy INT NULL,
|
||||||
ExportedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
ExportedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
CreatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
UpdatedDate DATETIME NOT NULL DEFAULT GETDATE(),
|
UpdatedDate DATETIME NOT NULL DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE
|
FOREIGN KEY (AssetId) REFERENCES AssetInventory(AssetId) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
PRINT 'Table AssetExportHistory created successfully.';
|
PRINT 'Table AssetExportHistory created successfully.';
|
||||||
@@ -368,17 +451,17 @@ END
|
|||||||
|
|
||||||
IF COL_LENGTH('dbo.AssetExportHistory', 'ExportedDate') IS NULL
|
IF COL_LENGTH('dbo.AssetExportHistory', 'ExportedDate') IS NULL
|
||||||
BEGIN
|
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
|
END
|
||||||
|
|
||||||
IF COL_LENGTH('dbo.AssetExportHistory', 'CreatedDate') IS NULL
|
IF COL_LENGTH('dbo.AssetExportHistory', 'CreatedDate') IS NULL
|
||||||
BEGIN
|
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
|
END
|
||||||
|
|
||||||
IF COL_LENGTH('dbo.AssetExportHistory', 'UpdatedDate') IS NULL
|
IF COL_LENGTH('dbo.AssetExportHistory', 'UpdatedDate') IS NULL
|
||||||
BEGIN
|
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
|
END
|
||||||
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_AssetExportHistory_CreatedBy')
|
IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_AssetExportHistory_CreatedBy')
|
||||||
@@ -401,7 +484,45 @@ BEGIN
|
|||||||
END
|
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')
|
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AuditLog')
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -413,14 +534,14 @@ BEGIN
|
|||||||
RecordId INT,
|
RecordId INT,
|
||||||
OldValue NVARCHAR(MAX),
|
OldValue NVARCHAR(MAX),
|
||||||
NewValue NVARCHAR(MAX),
|
NewValue NVARCHAR(MAX),
|
||||||
Timestamp DATETIME DEFAULT GETDATE(),
|
Timestamp DATETIME DEFAULT (DATEADD(HOUR, 7, SYSUTCDATETIME())),
|
||||||
FOREIGN KEY (UserId) REFERENCES Users(UserId)
|
FOREIGN KEY (UserId) REFERENCES Users(UserId)
|
||||||
);
|
);
|
||||||
PRINT 'Table AuditLog created successfully.';
|
PRINT 'Table AuditLog created successfully.';
|
||||||
END
|
END
|
||||||
|
|
||||||
-- ===========================================
|
-- ===========================================
|
||||||
-- 10. CREATE INDEXES
|
-- 11. CREATE INDEXES
|
||||||
-- ===========================================
|
-- ===========================================
|
||||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Users_Username')
|
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Users_Username')
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -482,6 +603,21 @@ BEGIN
|
|||||||
CREATE INDEX IX_AssetBorrowRequests_RequestType ON AssetBorrowRequests(RequestType);
|
CREATE INDEX IX_AssetBorrowRequests_RequestType ON AssetBorrowRequests(RequestType);
|
||||||
END
|
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')
|
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetExportHistory_AssetId')
|
||||||
BEGIN
|
BEGIN
|
||||||
CREATE INDEX IX_AssetExportHistory_AssetId ON AssetExportHistory(AssetId);
|
CREATE INDEX IX_AssetExportHistory_AssetId ON AssetExportHistory(AssetId);
|
||||||
@@ -492,10 +628,25 @@ BEGIN
|
|||||||
CREATE INDEX IX_AssetExportHistory_ExportedDate ON AssetExportHistory(ExportedDate DESC);
|
CREATE INDEX IX_AssetExportHistory_ExportedDate ON AssetExportHistory(ExportedDate DESC);
|
||||||
END
|
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.';
|
PRINT 'Indexes created successfully.';
|
||||||
|
|
||||||
-- ===========================================
|
-- ===========================================
|
||||||
-- 11. INSERT INITIAL DATA
|
-- 12. INSERT INITIAL DATA
|
||||||
-- ===========================================
|
-- ===========================================
|
||||||
|
|
||||||
-- Check if admin user exists
|
-- Check if admin user exists
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
NODE_ENV: ${NODE_ENV:-production}
|
NODE_ENV: ${NODE_ENV:-production}
|
||||||
PORT: 3000
|
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_SERVER: ${DB_SERVER:-172.20.235.176}
|
||||||
DB_USER: ${DB_USER:-sa}
|
DB_USER: ${DB_USER:-sa}
|
||||||
DB_PASSWORD: ${DB_PASSWORD:-changeme}
|
DB_PASSWORD: ${DB_PASSWORD:-changeme}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
PORT: 3000
|
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_SERVER: ${DB_SERVER:-172.20.235.176}
|
||||||
DB_USER: ${DB_USER:-sa}
|
DB_USER: ${DB_USER:-sa}
|
||||||
DB_PASSWORD: ${DB_PASSWORD:-changeme}
|
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>
|
||||||
</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 -->
|
<!-- 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-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);">
|
<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>
|
||||||
</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 -->
|
<!-- 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-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">
|
<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>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="../js/app.js?v=20260424-1"></script>
|
<script src="../js/app.js?v=20260515-5"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user