Audit Trail - JV
Why we need Audit Trail
An audit trail is essential to comply with government regulations and industry standards, ensuring transparency and accountability. It tracks changes to critical data like what was changed, who made the change, and when,to meet legal requirements, prevent fraud, and support audits effectively.
Applicability of Audit Trail
Audit trail functionality will be implemented for the following financial documents:
- Invoice
- Bill
- DNCN (Debit/Credit Note)
- Voucher
- Journal Voucher
- Charts of Accounts
- Party Master
- Opening Balances
- Audit trail applies only for Delete All,Import & CarryForwards (logs also need to be maintained in ERP and MFG).
- Delete & Import we will allow for the First Year Only
- Opening Outstanding (Opg Os)
- Audit trail applies only for Delete All,Import & CarryForwards (logs also need to be maintained in ERP and MFG).
- Delete & Import we will allow for the First Year Only
Audit Trail table Structure & Script
CREATE TABLE AuditTrail
(
id INT PRIMARY KEY IDENTITY(1,1),
AuditTrailNo NVARCHAR(8),---generated by the system
DocTypeCode NVARCHAR(5),---for eg. V, , DNCN1
yearDocNo NVARCHAR(12),----for eg.YearVoucherNo
Operationcode NVARCHAR(2),-----for Eg. Modify, Delete, recreated
operationDate DATETIME,-----On what date it was Modified or delete
username NVARCHAR(50),----who deleted/Modify
Notes NVARCHAR(250)---user will mention reason for delete
);
CREATE TABLE AuditTrail1 (
id INT PRIMARY KEY IDENTITY(1,1),
AuditTrailNo NVARCHAR(8),--for joining purpose
displayname NVARCHAR(100),----what will be visible to the user
oldvalue NVARCHAR(500),
newvalue NVARCHAR(500),
updatedBy NVARCHAR(1),---- ind will be U and S (user and System)
);
-----used only for modify
CREATE TABLE AuditTrailMetaData
(
id INT PRIMARY KEY IDENTITY(1,1),
tableName NVARCHAR(50),--- will be the table name
propertyname NVARCHAR(50),---name which has been used in the Property
displayname NVARCHAR(100),-- will be the name which is visible to the user for eg Voucher Amount
ind NVARCHAR(3),---->pending to add description
coulmnname NVARCHAR(50),---->pending to add description
internalcolumn NVARCHAR(50)---> for deleting purpose
);
CREATE TABLE AuditTrailDocType
(
id INT PRIMARY KEY IDENTITY(1,1),
docType NVARCHAR(4),--- for eg B (for Bill), V(for Voucher)
docTypeName NVARCHAR(50),---for eg Bill, Voucer
);
---FOR PARTYMST
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('I', 'Invoice ')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('B', 'Bill')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('D', 'Debit/Credit Note')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('V', 'Voucher')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('J', 'Journal Voucher ')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('A', 'Charts of Account')
INSERT INTO AuditTrailDocType (docType, docTypeName)
VALUES ('P', 'PartyMst')
---JV
INSERT INTO AuditTrailMetaData (tablename, propertyname, displayname, ind, internalcolumn)
VALUES
('journalvoucher', 'UnitCode', 'Unit Code', 'MJV', 'unitcode'),
('journalvoucher', 'dated', 'JV Date', 'MJV', 'dated'),
('journalvoucher', 'debitamount', 'Debit Amount', 'MJV', 'debit'),
('journalvoucher', 'creditamount', 'Credit Amount', 'MJV', 'credit'),
('journalvoucher', 'jvtypename', 'JV Type', 'MJV', 'jvtype'),
('journalvoucher', 'divisioncode', 'Division Code', 'MJV', 'divisionid'),
('journalvoucher1', 'drcrname', 'Debit/Credit Type', 'LJV', 'drcr'),
('journalvoucher1', 'refno', 'Ref No', 'LJV', 'refno'),
('journalvoucher1', 'refdate', 'Ref Date', 'LJV', 'refdate'),
('journalvoucher1', 'amount', 'Amount', 'LJV', 'amount'),
('journalvoucher1', 'mainacnamecode', 'Account Head', 'LJV', 'mainac'),
('journalvoucher1', 'subacnamecode', 'Sub Account Head', 'LJV', 'subac'),
('journalvoucher1', 'refdocname', 'Ref Doc', 'LJV', 'refdoc');
INSERT INTO sysparameters (shortname, name, syscondition, dated, isactive, docno, status) VALUES ('AuditTrail', 'Enable Audit Trail', 'Y', null, 'Y', null, null)
ALTER TABLE Audittrail1 ADD docId INTImplementation Mechanism
The following mechanisms will be applied to track changes across documents for delete, delete1, modify, modify1, modify other info or any other features where necessary(eg. UpdateTaxableAmount)
Audit Trail Main Index
| Field | Property Name |
|---|---|
| Audit Trail No | AuditTrailNo |
| Doc Type | DocType |
| Year/Doc No | YearDocNo |
| Operation | OperationName |
| Performed By | UserName |
| Performed At | ModifiedAt |
Audit Trail Details
| Field | Property Name |
|---|---|
| Audit Trail No | AuditTrailNo |
| Doc Type | DocType |
| Year/Doc No | YearDocNo |
| Operation | OperationName |
| Performed By | UserName |
| Performed At | ModifiedAt |
| Notes | Notes |
Audit Trail Line Index
| Fields | Property Name |
|---|---|
| Audit Trail No | AuditTrailNo |
| Particulars | DisplayName |
| OldValue | OldValue |
| NewValue | NewValue |
| Updated By | UpdatedBy |
Audit Trail Queries
--------Audit Trail Index------------------------------
SELECT AuditTrailNo, docTypeName,
CONCAT(SUBSTRING(yeardocno, 1, 4), '-', SUBSTRING(yeardocno, 5, 2), '/', RIGHT(yeardocno, 6)) AS yeardocNo,
IIF(Operationcode = 'RC', 'Re-Created',
IIF(Operationcode = 'MM', 'Modify Main',
IIF(Operationcode = 'ML', 'Modify Line',
IIF(Operationcode = 'DM', 'Delete Main', 'Delete Line')
)
)
) AS operationname, operationDate, username, A.id
FROM AuditTrail A
LEFT JOIN AuditTrailDocType AD ON A.DocTypeCode = AD.docType
ORDER BY A.id DESC
--------------Audit Trail Details-----------------------
SELECT AuditTrailNo, docTypeName,
CONCAT(SUBSTRING(yeardocno, 1, 4), '-', SUBSTRING(yeardocno, 5, 2), '/', RIGHT(yeardocno, 6)) AS yeardocNo,
IIF(Operationcode = 'RC', 'Re-Created',
IIF(Operationcode = 'MM', 'Modify Main',
IIF(Operationcode = 'ML', 'Modify Line',
IIF(Operationcode = 'DM', 'Delete Main', 'Delete Line')
)
)
) AS operationname, operationDate, username, A.id, A.Notes
FROM AuditTrail A
LEFT JOIN AuditTrailDocType AD ON A.DocTypeCode = AD.docType
WHERE A.id = @id
------------------Audit Trail1 Index----------------------------------
SELECT AuditTrailNo, oldvalue, newvalue, displayname,
IIF(updatedby = 'U', 'user', 'System') AS updatedby, id
FROM
AuditTrail1
WHERE audittrailNo = @audittrailno
ORDER BY id DESCCreate Operations
- Once the create Operation is completed we will check if the YearDocNo exists for the same doctypeName for any Delete Operation in Audit Trail
- If it exist then we will Insert in Audit Trail main Level with Operation Re-Created(RC)
- If the doc no doesnot exist in Audit Trail then Logs will be captured from the ERP log system.
Already added in system - This feature will be only for Main level Create
Code for Audit Trail Re-Create Method
//------------------------------------HOW TO ADD IN CREATE FEATURE----------------------------------------
[HttpPost("{yearLabel},{dbname},{userName}")]
public ActionResult JournalVoucherCreate(JournalVoucher _JournalVoucher, string yearLabel, string dbname, string userName)
{
if (ModelState.IsValid)
{
var maxJvNo = _IJournalVoucherRepository.GetMaxJvNo(dbname, yearLabel);
int jvNoLength = string.IsNullOrEmpty(maxJvNo) ? 6 : maxJvNo.Length;
var nextJvNo = _IUtilityMethodsRepository.GetNextDocno(maxJvNo, jvNoLength);
_JournalVoucher.JvNo = nextJvNo;
_JournalVoucher.YearJvNo = $"{yearLabel.Replace("-", "")}{_JournalVoucher.JvNo}";
_IJournalVoucherRepository.JournalVoucherCreate(_JournalVoucher, dbname);
var auditTrailSysparameter = _IUtilityMethodsRepository.GetSysParameterValue(dbname, "AuditTrail");
if(auditTrailSysparameter.IsActive == "Y")
{
//--------------------AUDIT TRAIL BLOCK-------------------------------------------
var yearDocNoCnt = _IAuditTrailRepository.GetYearDocNoCnt(_JournalVoucher.YearJvNo, dbname, "J");
if(yearDocNoCnt > 0)
{
var nextAuditTrailNo = _AuditTrailController.GetNextAuditTrailNo(dbname);
// assigning values for main level audit trail create
AuditTrail _AuditTrail = new()
{
AuditTrailNo = nextAuditTrailNo,
DocTypeCode = "J",
YearDocNo = _JournalVoucher.YearJvNo,
UserName = userName,
OperationCode = "RC",
Notes = $"Re-created by {userName}",
};
_IAuditTrailRepository.AuditTrailCreate(dbname, _AuditTrail);// for creating main level
_IAuditTrailRepository.AuditTrail1DynamicCreate(dbname, _AuditTrail.YearDocNo, "journalvoucher", _AuditTrail.AuditTrailNo, "YearJvNo", data.Id);// for creating line level
}
}
_IUtilityMethodsRepository.InsertMFGLog(dbname, userName, _JournalVoucher.JvNo, "JV", "Add", yearLabel, "");
}
return Ok(_JournalVoucher);
}YearDocNoCntQuery
--- to check record exist or not for delete feature
--Paramteres : Yeardocno - eg. PartyId, YearVoucherNo etc..., docTypeCode = P for PartyMst, V for Voucher, OperationCode = D(for Delete)
SELECT COUNT(*) FROM audittrail
WHERE yeardocno = @yeardocno AND
doctypecode = @doctypecode AND operationCode = 'DM'AuditTrailCreate
-- to insert at main level
-- Paramteres :
-- auditrailNo - getting from getnextaudittrailno,
-- docTypeCode = P for PartyMst, V for Voucher,
-- OperationCode = RC(Re-Created),
-- OperationDate = DateTime.Now
-- UserName - coming from postlogin
-- Notes = $"Recreated by {_AuditChange.UserName}"
INSERT INTO AuditTrail(AuditTrailNo,DocTypeCode,yearDocNo, OperationCode, operationDate, username, Notes)
VALUES (@AuditTrailNo,@DocTypeCode, @yearDocNo, @Operationcode, @operationDate, @username, @Notes)AuditTrailDynamicCreate
"DECLARE
@columns NVARCHAR(MAX),
@castedColumns NVARCHAR(MAX),
@query NVARCHAR(MAX);
-- Step 1: Build column list dynamically based on tablename
SELECT
@columns = STRING_AGG(internalcolumn, ', '),
@castedColumns = STRING_AGG('CAST(' + internalcolumn + ' AS NVARCHAR(MAX)) AS ' + internalcolumn, ', ')
FROM AuditTrailMetaData
WHERE tablename = @tableName;
-- Step 2: Build the dynamic SQL
SET @query = 'WITH A (dynamicdocno, ' + @columns + ') AS
(
SELECT ' + QUOTENAME(@tableYearDocNameId) + ' AS dynamicdocno, ' + @castedColumns + '
FROM {tableName}
),
B(internalcolumn, value) AS
(
SELECT internalcolumn, value
FROM A
UNPIVOT (value FOR internalcolumn IN (' + @columns + ')) AS U
WHERE dynamicdocno = @yearDocNoId
)
INSERT INTO audittrail1 (audittrailno, displayname, oldvalue, updatedby, docid)
SELECT @audittrailno, M.displayname, B.value, @updatedby, @docid
FROM B
JOIN AuditTrailMetaData M ON M.internalcolumn = B.internalcolumn
AND M.tablename = @tableName;';
-- Execute the dynamic SQL
EXEC sp_executesql @query,
N'@yearDocNoId NVARCHAR(MAX), @audittrailno NVARCHAR(MAX), @tableName NVARCHAR(MAX), @updatedby NVARCHAR(10)',
@yearDocNoId, @audittrailno, @tableName, 'U', @docid";Modify Main/ Modify Line Operations
- Operation: Modify Main/ Modify Line
1. Capture Old Values Before Modification
- Add a field in Modify page for Audit Trail Notes
- When the Save button is clicked in the controller:
- Main-Level Details:
Fetch the current Main-level details (existing records) from the database into a model class, referred to asOldValues.
- Main-Level Details:
2. Detect Line-Level Changes
- Pass the following to a Detect Changes method:
OldValues(fetched old values)- New values coming from the Modify Razor Page (user-modified data).
- The
Detect Changesmethod compares the old and new values for each field which needs to be tracked and identifies only those fields where the data has been changed.
3. Update Audit Trail for Main-Level Changes
- Pass the list of changes (from the Detect Changes method) to an
UpdateAuditTrailmethod. - Insert these changes into a temporary table in the database.
- Each record will contain:
PropertyName(Field Name)OldValue(Previous Value)NewValue(Updated Value)
- Each record will contain:
Example Output in Audit Trail Index(Main Level):
| AuditTrailNo | DocType | Year/DocNo | Performed By | Performed At | Operation | |
|---|---|---|---|---|---|---|
| 00000001 | Journal Voucher | 2024-25/000006 | Bhumika | 2024-11-28 09:00 | Modify Main | Changed the Discount Amountshown in details |
Example Output in Audit Trail Index1(Line Level):
| AuditTrailNo | Particular | OldValue | newValue | Updatedby |
|---|---|---|---|---|
| 00000001 | Unit Name | VAPI | BENGLURU | User |
| 00000001 | JV Date | 08-Apr-24 | 07-Apr-24 | User |
In Case computation is also there in Modify Line
- Execute the Compute method to process any modifications or
- Fetch the updated main-level details into a new model class called
NewValuesMain. - Pass both
OldValuesMainandNewValuesMainto the Detect Changes method. - Pass the changes (from the Detect Changes method) along with the Audit Trail No to the
AuditTrailCreatemethod.
Example Output in Audit Trail Index(Line Level):
Audit Trail Item Details
| Audit Trail No. | Particulars | Old Value | New Value | Updated By |
|---|---|---|---|---|
| 00000002 | Ac Particulars | ADVANCE TAX - 2016-2017 - [078002] | ADANI ANDHERI OFFICE LEASE DEPOSIT - [076050] | user |
| 00000002 | Ref No | 012345 | user | |
| 00000002 | Ref Date | 17-Mar-25 | 16-Mar-25 | user |
| 00000002 | Amount | 520.00 | 200.00 | user |
| 00000002 | Debit/Credit Type | Credit | Debit | user |
| 00000002 | Debit Amount | 0.00 | 200.00 | System |
| 00000002 | Credit Amount | 520.00 | 0.00 | System |
Example of Code in JV modify1
[HttpPut("{dbname},{yearlabel},{username}")]
public ActionResult JournalVoucher1Modify(JournalVoucher1 _JournalVoucher1, string dbname, string yearlabel, string username)
{
if (ModelState.IsValid)
{
// picking old values for audit trail
JournalVoucher1 _LineOldValues = _IJournalVoucherRepository.JournalVoucher1Details(_JournalVoucher1.Id, dbname).Result;
_JournalVoucher1.RefNo = !string.IsNullOrEmpty(_JournalVoucher1.RefNo) ? _JournalVoucher1.RefNo.PadLeft(6, '0') : "";
_IJournalVoucherRepository.JournalVoucher1Modify(_JournalVoucher1, dbname);
JournalVoucher _MainOldValues = new();
AuditTrail _AuditTrail = new();
var auditTrailSysparameter = _IUtilityMethodsRepository.GetSysParameterValue(dbname, "AuditTrail");
if(auditTrailSysparameter.IsActive == "Y")
{
//------------------------AUDIT TRAIL BLOCK------------------------------------------------------------------------
// need to call to fetch new values else it will not consider combo values
JournalVoucher1 _LineNewValues = _IJournalVoucherRepository.JournalVoucher1Details(_JournalVoucher1.Id, dbname).Result;
//calling detect changes for audit trail to detect changes made
var detectedChanges = _AuditTrailController.DetectChanges(_LineOldValues, _LineNewValues, dbname, "Journalvoucher1");
if (detectedChanges.Count != 0)//only going forward if change is done
{
//assigning the values
_AuditTrail.DocTypeCode = "J";
_AuditTrail.YearDocNo = _JournalVoucher1.YearJvNo;
_AuditTrail.UserName = username;
_AuditTrail.OperationCode = "ML";
_AuditTrail.Notes = _JournalVoucher1.AuditTrailNotes;
_AuditTrail.AuditTrailNo = _AuditTrailController.GetNextAuditTrailNo(dbname);
_IAuditTrailRepository.AuditTrailCreate(dbname, _AuditTrail);
_IAuditTrailRepository.AuditTrail1Create(detectedChanges1, dbname, _AuditTrail.AuditTrailNo, "S", _JournalVoucher1.Id);
}
// calling main level details to get values before updating
_MainOldValues = _IJournalVoucherRepository.JournalVoucherDetails(_JournalVoucher1.MainId, dbname).Result;
}
_IJournalVoucherRepository.UpdateAmount(_JournalVoucher1.YearJvNo, dbname);
if(auditTrailSysparameter.IsActive == "Y")
{
// calling main level details to get values after updating
JournalVoucher _MainNewValues = _IJournalVoucherRepository.JournalVoucherDetails(_JournalVoucher1.MainId, dbname).Result;
var detectedChanges1 = _AuditTrailController.DetectChanges(_MainOldValues, _MainNewValues, dbname, "Journalvoucher");
if (detectedChanges1.Count != 0)
{
_IAuditTrailRepository.AuditTrail1Create(detectedChanges1, dbname, _AuditTrail.AuditTrailNo, "S", _JournalVoucher1.Id);
}
}
_IUtilityMethodsRepository.InsertMFGLog(dbname, username, _JournalVoucher1.JvNo, "JV", "Edit Line", yearlabel, "");
}
return Ok();
}Line Delete
- Operation: DeleteLine
1. Capture Old Values Before Deletion
- When the Delete button is clicked in the controller:
- Assign all the values for main level Audit Trail and call AuditTrailCreate and AuditTrailDynamicCreate mentioned above
- Fetch the current line-level details (existing records) from the database into a model class, referred to as
OldValuesbefore deleting it - Delete the records - Call the UpdateAmount method - Call the details method after Update to fetch the new values - Call the Detect Changes method after that call AuditTrail1Create Method
Example Output in Audit Trail Index(Main Level):
| AuditTrailNo | DocType | Year/DocNo | Performed By | Performed At | Operation | Notes |
|---|---|---|---|---|---|---|
| 00000001 | Journal Voucher | 2024-25/000006 | Bhumika | 2024-11-28 09:00 | Delete Line | Created by Mistakeshown in details |
Example Output in Audit Trail Index(Line Level):
| Audit Trail No. | Particulars | Old Value | New Value | Updated By |
|---|---|---|---|---|
| 00000003 | Ac Particulars | ADVANCE TAX - [780050] | user | |
| 00000003 | Ref Date | Oct 31 2024 12:00AM | Apr 8 2025 6:31PM | user |
| 00000003 | Amount | 1435000.00 | 0 | user |
| 00000003 | Debit/Credit Type | Credit | user | |
| 00000003 | Credit Amount | 1902350.00 | 467350.00 | System |
Example of Audit Trail in JV Line Delete
[HttpDelete("{id},{mainId},{dbname},{userName},{yearLabel},{deleteReason}")]
public void JournalVoucher1Delete(int id, int mainId, string dbname, string userName, string yearLabel, string deleteReason)
{
var data = _IJournalVoucherRepository.JournalVoucherDetails(mainId, dbname).Result;
var auditTrailSysparameter = _IUtilityMethodsRepository.GetSysParameterValue(dbname, "AuditTrail");
AuditTrail _AuditTrail = new();
if(auditTrailSysparameter.IsActive == "Y")
{
_AuditTrail.AuditTrailNo = _AuditTrailController.GetNextAuditTrailNo(dbname);
//assigning the values
_AuditTrail.DocTypeCode = "J";
_AuditTrail.YearDocNo = data.YearJvNo;
_AuditTrail.UserName = userName;
_AuditTrail.OperationCode = "DL";
_AuditTrail.Notes = deleteReason;
// for creating main level audit trail
_IAuditTrailRepository.AuditTrailCreate(dbname, _AuditTrail);
// for inserting records from line level jv
_IAuditTrailRepository.AuditTrail1DynamicCreate(dbname, $"{id}", "journalvoucher1", _AuditTrail.AuditTrailNo, "id", id);
}
_IJournalVoucherRepository.JournalVoucher1Delete(id, dbname);
_IJournalVoucherRepository.UpdateAmount(data.YearJvNo, dbname);
if(auditTrailSysparameter.IsActive == "Y")
{
// calling main level details to get values after updating
JournalVoucher _jvDataAfterUpdate = _IJournalVoucherRepository.JournalVoucherDetails(mainId, dbname).Result;
var detectedChanges1 = _AuditTrailController.DetectChanges(data, _jvDataAfterUpdate, dbname, "Journalvoucher");
if (detectedChanges1.Count != 0)
{
_IAuditTrailRepository.AuditTrail1Create(detectedChanges1, dbname, _AuditTrail.AuditTrailNo, "S", id);
}
}
_IUtilityMethodsRepository.InsertMFGLog(dbname, userName, data.JvNo, "JV", "Delete Line", yearLabel, deleteReason);
}Detect Changes
public List<AuditTrail1> DetectChanges<T>(T original, T updated, string dbName, string tableName)
{
var changes = new List<AuditTrail1>();
// Fetch the property names that should be tracked from AuditTrailInfo table
var auditTrailProperties = _IAuditTrailRepository.AuditTrailMetaData(dbName, tableName); // Ensure it's a List<string>
foreach (var propertyName in auditTrailProperties) // Loop through properties from DB
{
var property = typeof(T).GetProperty(propertyName.PropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var oldValue = property.GetValue(original);
var newValue = property.GetValue(updated);
if (!Equals(oldValue, newValue)) // Log only if values are different
{
changes.Add(new AuditTrail1
{
DisplayName = propertyName.DisplayName,
OldValue = oldValue,
NewValue = newValue
});
}
}
return changes;
}AuditTrail1Create
INSERT INTO AuditTrail1(audittrailno, displayname, oldvalue, newvalue, updatedby)
VALUES (@audittrailno, @displayname, @oldvalue, @newvalue, @updatedby)
-- foreach (var dataLine in detectedChangesList)
-- {
-- connection.Query<AuditTrail1>(insertAuditTrail1,
-- new
-- {
-- auditTrailNo,
-- dataLine.DisplayName,
-- dataLine.OldValue,
-- dataLine.NewValue,
-- updatedBy
-- });
-- }Main Delete
- Operation: DeleteMain
1. Capture Old Values Before Deletion
- When the Delete button is clicked in the controller:
- Assign all the values for main level Audit Trail and call AuditTrailCreate and AuditTrailDynamicCreate(For main and line level) mentioned above
Code Example
[HttpDelete("{id},{dbname},{userName},{yearLabel},{deleteReason}")]
public void JournalVoucherDelete(int id, string dbname, string userName, string yearLabel, string deleteReason)
{
var data = _IJournalVoucherRepository.JournalVoucherDetails(id, dbname).Result;
var auditTrailSysparameter = _IUtilityMethodsRepository.GetSysParameterValue(dbname, "AuditTrail");
if(auditTrailSysparameter.IsActive == "Y")
{
//-------------------AUDIT TRAIL BLOCK--------------------------------------------------------
AuditTrail _AuditTrail = new();
_AuditTrail.AuditTrailNo = _AuditTrailController.GetNextAuditTrailNo(dbname);
//assigning the values
_AuditTrail.DocTypeCode = "J";
_AuditTrail.YearDocNo = data.YearJvNo;
_AuditTrail.UserName = userName;
_AuditTrail.OperationCode = "DM";
_AuditTrail.Notes = deleteReason;
// for creating main level audit trail
_IAuditTrailRepository.AuditTrailCreate(dbname, _AuditTrail);
// for inserting records from main level jv
_IAuditTrailRepository.AuditTrail1DynamicCreate(dbname, _AuditTrail.YearDocNo, "journalvoucher", _AuditTrail.AuditTrailNo, "YearJvNo", id);
// checking line level records are there or not
var journalVoucher1Cnt = _IUtilityMethodsRepository.GetRecordCount(dbname, data.YearJvNo, "journalvoucher1", "yearjvno");
if(journalVoucher1Cnt > 0)
{
// for inserting records from line level jv
_IAuditTrailRepository.AuditTrail1DynamicCreate(dbname, _AuditTrail.YearDocNo, "journalvoucher1", _AuditTrail.AuditTrailNo, "YearJvNo", id);
}
}
_IJournalVoucherRepository.JournalVoucherDelete(data.YearJvNo, dbname);
_IUtilityMethodsRepository.InsertMFGLog(dbname, userName, data.JvNo, "JV", "Delete", yearLabel, deleteReason);
}Example of how it is stored at line level
| Audit Trail No. | Particulars | Old Value | New Value | Updated By |
|---|---|---|---|---|
| 00000005 | Unit Name | NA | user | |
| 00000005 | JV Date | 31-Oct-25 | 30-Oct-25 | user |
| 00000005 | Debit Amount | 1902350.00 | 0 | user |
| 00000005 | Credit Amount | 27000.00 | 0 | user |
| 00000005 | Debit/Credit Type | D | user | |
| 00000005 | Ref No | user | ||
| 00000005 | Ref Date | 31-Oct-25 | user | |
| 00000005 | Amount | 1900000.00 | user | |
| 00000005 | Account Particulars | 125080 | user | |
| 00000005 | Debit/Credit Type | D | user | |
| 00000005 | Ref No | user | ||
| 00000005 | Ref Date | 31-Oct-25 | user | |
| 00000005 | Amount | 1900000.00 | user | |
| 00000005 | Account Particulars | 125080 | user |
File Names
| File Name |
|---|
| Controller - AuditTrailController |
| Repository - IAuditTrailRepository, AuditTrailRepository |
| Service - IAuditTrailService, AuditTrailService |
| Method Names - AuditTrailIndex - AuditTrailDetails - AuditTrail1Index - GetYearDocNoCnt - AuditTrailCreate - GetMaxAuditTrailNo - GetAuditTrailProperties - AuditTrailDelete - DetectChanges - GetNextAuditTrailNo |