AI Insights [Job Work Register, Ledger, Salary]

AI Insights [Job Work Register, Ledger, Salary]

Implementation Guidelines

  • Follow the SalesByProduct report pattern.
  • Use the GRN Report approach as a fallback if issues occur with the invalid object name ##charttemp.
  • If both summary and details sheets are present, modifications should be made to the details query.
  • The Dash table script columns should match the database tables.
  • Introduce a new parameter ind set to C for all four report methods; if it is C, perform all chart processing.
  • Do not alter existing system prompts; instead, add a new string variable named reportName and make changes accordingly, such as in pendingOrderReportPrompt: When asked.
  • FromDate Calculation: The FromDate is now dynamically calculated as the start of the current fiscal year minus 3 years using GetPreviousFyStartDate(dbname, "3") instead of hardcoded dates.
  • Error Handling: The scheduler now includes try-catch blocks with email notifications sent to support@erpcrystal.in for any exceptions during report generation.

script

CREATE TABLE dash.DashboardJobWork
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    jobno NVARCHAR(6) COLLATE DATABASE_DEFAULT,
    ChallanTypeName NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    unitname NVARCHAR(20) COLLATE DATABASE_DEFAULT,
    unitcode NVARCHAR(2) COLLATE DATABASE_DEFAULT,
    AnnexureNo NVARCHAR(20) COLLATE DATABASE_DEFAULT,
    AnnexureDate DATETIME,
    InOutTypeName NVARCHAR(20) COLLATE DATABASE_DEFAULT,
    partyshortname NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    partyname NVARCHAR(100) COLLATE DATABASE_DEFAULT,
    itemgroupname NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    itemid NVARCHAR(6) COLLATE DATABASE_DEFAULT,
    itemshortname NVARCHAR(100) COLLATE DATABASE_DEFAULT,
    ItemName NVARCHAR(150) COLLATE DATABASE_DEFAULT,
    brandname NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    itemstatename NVARCHAR(10) COLLATE DATABASE_DEFAULT,
    ActualWeight NUMERIC(14,3),
    ActualQty NUMERIC(14,3),
    Amount NUMERIC(14,2),
    aging INT,
    RemarkName NVARCHAR(100) COLLATE DATABASE_DEFAULT,
    EWayBillNo NVARCHAR(20) COLLATE DATABASE_DEFAULT,
    JobItemNotes NVARCHAR(500) COLLATE DATABASE_DEFAULT,
    JobItemSpecialNotes NVARCHAR(500) COLLATE DATABASE_DEFAULT,
    JobNotes NVARCHAR(500) COLLATE DATABASE_DEFAULT,
    Packets INT,
    ind NVARCHAR(1) COLLATE DATABASE_DEFAULT,
    partygroupname NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    refno NVARCHAR(25) COLLATE DATABASE_DEFAULT,
    refdate DATETIME
);

CREATE TABLE dash.DashboardLedger
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    doctype NVARCHAR(1) COLLATE DATABASE_DEFAULT,
    docno NVARCHAR(6) COLLATE DATABASE_DEFAULT,
    docdate DATETIME,
    unitcode NVARCHAR(2) COLLATE DATABASE_DEFAULT,
    refdoc NVARCHAR(2) COLLATE DATABASE_DEFAULT,
    refno NVARCHAR(20) COLLATE DATABASE_DEFAULT,
    refdate DATETIME,
    notes NVARCHAR(1000) COLLATE DATABASE_DEFAULT,
    cheqdetails NVARCHAR(500) COLLATE DATABASE_DEFAULT,
    debitamount NUMERIC(15,2),
    creditamount NUMERIC(15,2),
    mainsubac NVARCHAR(13) COLLATE DATABASE_DEFAULT,
    mainsubacname NVARCHAR(200) COLLATE DATABASE_DEFAULT
);

CREATE TABLE dash.DashboardSalary
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    EmployeeCode NVARCHAR(6) COLLATE DATABASE_DEFAULT,
    EmployeeName NVARCHAR(120) COLLATE DATABASE_DEFAULT,
    EmployeeCategoryCode NVARCHAR(3) COLLATE DATABASE_DEFAULT,
    EmployeeCategoryName NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    EmpTypeCode NVARCHAR(3) COLLATE DATABASE_DEFAULT,
    EmpTypeName NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    PayDate DATETIME,
    PayHeadCode NVARCHAR(3) COLLATE DATABASE_DEFAULT,
    PayHeadName NVARCHAR(50) COLLATE DATABASE_DEFAULT,
    Amount NUMERIC(10,2),
    PayType NVARCHAR(1) COLLATE DATABASE_DEFAULT
);

Prompts in AiInsightService

var jobWorkRegisterSystemPrompt = @"Table 9: DashboardJobWork
Use for queries about:
- Job work register and processing activities
- Inward and outward job work transactions
- Job work party-wise and item-wise analysis
- Job work aging and pending analysis
- Job work challan tracking and status
- Job work quantity and value summaries
- Job work performance and trends

Key columns include:
jobno, AnnexureDate, InOutTypeName, partyname, itemname, ActualWeight, ActualQty, Amount, aging

Important Notes:
- This table contains individual job work transaction records
- ActualWeight, ActualQty, Amount can be positive (inward) or negative (outward)
- aging represents days since annexure date for outward transactions
- AnnexureDate is the key date field for filtering

Filtering Rules:
- InOutTypeName = 'OUT' for sent out job work
- InOutTypeName = 'IN GRN' for received job work with GRN
- InOutTypeName = 'IN No GRN' for received job work without GRN
- itemstatename = 'Good' for good items
- itemstatename = 'Rejected' for rejected items
- Use AnnexureDate BETWEEN for date range filtering
- Filter by partyname, itemname, unitname for specific analysis

Aggregation Rules (create totals on the fly when requested):
- For job-wise totals: GROUP BY jobno with SUM(ActualWeight), SUM(ActualQty), SUM(Amount), MAX(aging)
- For party-wise summaries: GROUP BY partyname with SUM(ActualWeight), SUM(ActualQty), SUM(Amount)
- For item-wise summaries: GROUP BY itemname with SUM(ActualWeight), SUM(ActualQty), SUM(Amount)
- For monthly trends: GROUP BY YEAR(AnnexureDate), MONTH(AnnexureDate) with SUM(ActualWeight), SUM(ActualQty), SUM(Amount)
- For overall totals: no GROUP BY, just SUM(ActualWeight), SUM(ActualQty), SUM(Amount)
- Always format monetary results as CAST(SUM(Amount) AS DECIMAL(18,2))
- For aging analysis: AVG(aging) or categorize by aging ranges";


var ledgerSystemPrompt = @"Table 10: DashboardLedger
Use for queries about:
- General ledger transactions and entries
- Account-wise debit and credit analysis
- Financial transaction tracking
- Journal voucher and ledger analysis
- Account balance and movement analysis
- Trial balance and account statements
- Cash flow analysis
- Financial period comparisons

Key columns include:
doctype, docno, docdate, unitcode, refdoc, refno, refdate, notes, cheqdetails, debitamount, creditamount, mainsubac, mainsubacname

Important Notes:
- Each row represents a single transaction entry for an account
- debitamount and creditamount are mutually exclusive (one will be 0, the other will have the amount)
- mainsubac is the account code in format 'mainac-subac'
- mainsubacname includes the account code and account name
- Transactions are ordered by account, then by date, then by document type and number

Document Type (doctype) Values:
- 'A' = Opening Balance entries
- 'I' = Sales Invoice transactions
- 'B' = Purchase Bill transactions  
- 'D' = Debit/Credit Note transactions
- 'V' = Payment/Receipt Voucher transactions
- 'J' = Journal Voucher transactions

Reference Document (refdoc) Values:
- 'SI' = Sales Invoice
- 'PB' = Purchase Bill
- 'JV' = Journal Voucher
- 'DN' = Debit Note
- 'CN' = Credit Note
- 'RV' = Receipt Voucher
- 'PV' = Payment Voucher

Filtering Rules:
- Use doctype IN ('I','B','D','V','J') for transaction entries (exclude 'A' for opening balances)
- Use doctype = 'A' for opening balance entries only
- Filter by mainsubac or mainsubacname for specific accounts
- Use docdate BETWEEN for date range filtering
- Use unitcode for business unit filtering

Aggregation Rules:
- For account balance: SUM(debitamount) - SUM(creditamount) GROUP BY mainsubac
- For period totals: SUM(debitamount), SUM(creditamount) with date filtering
- For transaction counts: COUNT(*) with appropriate filters
- For account statements: ORDER BY mainsubac, docdate, doctype, docno
- Always format monetary results as CAST(SUM(amount) AS DECIMAL(18,2))

Common Query Patterns:
- Account-wise balance: SELECT mainsubac, mainsubacname, SUM(debitamount) - SUM(creditamount) AS balance FROM dash.DashboardLedger WHERE doctype != 'A' GROUP BY mainsubac, mainsubacname
- Transaction listing: SELECT * FROM dash.DashboardLedger WHERE mainsubac = 'account_code' AND docdate BETWEEN 'start' AND 'end' ORDER BY docdate, doctype, docno
- Period summary: SELECT SUM(debitamount) AS total_debit, SUM(creditamount) AS total_credit FROM dash.DashboardLedger WHERE docdate BETWEEN 'start' AND 'end' AND doctype != 'A'";


var salarySystemPrompt = @"Table 11: DashboardSalary
Use for queries about:
- Employee salary and payroll analysis
- Pay head-wise salary distribution
- Employee category and type-wise analysis
- Salary trends and comparisons
- Payroll cost analysis
- Monthly/period-wise salary summaries
- Employee-wise total earnings and deductions
- Gross salary and deduction breakdowns

Key columns include:
EmployeeCode, EmployeeName, EmployeeCategoryCode, EmployeeCategoryName, EmpTypeCode, EmpTypeName, PayDate, PayHeadCode, PayHeadName, Amount,PayType

Important Notes:
- PayDate represents the salary period monthly and PayDate is Always month end date
- Each row represents one pay component for one employee in one pay period

Filtering and Aggregation Rules:
- Group by EmployeeCategoryName for category-wise analysis
- Group by EmpTypeName for employee type analysis  
- Filter by PayHeadName for specific pay component analysis
- Use PayDate BETWEEN for date range filtering (financial year or calendar periods)
- For net salary per employee per period: SUM(Amount) GROUP BY EmployeeCode, PayDate (gross minus deductions)
- For monthly trends: GROUP BY FORMAT(PayDate, 'yyyy-MM') or YEAR(PayDate), MONTH(PayDate)
- For payhead analysis: GROUP BY PayHeadName with SUM(Amount)
- For addition payheads only: WHERE PayType = 'A'
- For deduction payheads only: WHERE PayType = 'D'
- Always format monetary results as CAST(SUM(...) AS DECIMAL(18,2))";



{jobWorkRegisterSystemPrompt}
{salarySystemPrompt}
{ledgerSystemPrompt}

Parameters

Job Work Register Report
  • FromDate: fyStartDate (dynamically calculated as current fiscal year start minus 3 years)
  • ToDate: Current date
  • ReportType: A and B
  • OmitAnnexures: N
  • IsPendingJob: false
  • Sheets:
    • Job - In and Job - Out both
Salary Table Report
  • FromDate: fyStartDate (dynamically calculated as current fiscal year start minus 3 years)
  • ToDate: Current date

We will fetch the salary table from MainDB and insert the data into ChatDB. The prompt must include all PayHeadCode / PayHeadName values so the LLM can generate the correct SQL query. Required fields: EmployeeCode,EmployeeName,EmployeeCategoryCode,EmployeeCategoryName,EmpTypeCode,EmpTypeName,PayDate,PayHeadCode,PayHeadName,Amount.

Ledger

Parameters

  • FromDate: fyStartDate

  • ToDate: Current date

  • ReportType: A

  • UnitCode: All

  • DivisionCode: All

  • ReportLedgerType: GL,CL,VL

  • ReportFilter: All

  • ExcludeMatchOffJv: Y

  • ExcludeMisJv: N

  • No need of Closing Balance and Total rows.

  • We have to run for each year separately for 3 years data ,else it will show one line for each account head.

Scheduler Implementation:

// -------salary table report
var truncateSalaryTable = "TRUNCATE TABLE dash.DashboardSalary";

using var connection = _DapperContext.SetClientConnection(dbname);
connection.Execute(truncateSalaryTable);

var salaryQuery = @"WITH A (EmployeeCode, EmployeeName, EmployeeCategoryCode, EmployeeCategoryName,
    EmpTypeCode, EmpTypeName, PayDate, PayHeadCode, PayHeadName, Amount) AS
(
    SELECT
        E.employeecode AS EmployeeCode,
        CONCAT_WS(' ', E.firstname, E.lastname) AS EmployeeName,
        E.categorycode AS EmployeeCategoryCode,
        EC.categoryname AS EmployeeCategoryName,
        E.emptypecode AS EmpTypeCode,
        ET.emptypename AS EmpTypeName,
        S.paydate AS PayDate,
        S.payheadcode AS PayHeadCode,
        PH.headname AS PayHeadName,
        S.amount AS Amount
    FROM Salary S
    LEFT JOIN Employeemst E ON S.employeecode = E.employeecode
    LEFT JOIN Empcategorymst EC ON E.categorycode = EC.categorycode
    LEFT JOIN Emptypemst ET ON E.emptypecode = ET.emptypecode
    LEFT JOIN Payheadmst PH ON S.payheadcode = PH.headcode
    WHERE CONVERT(DATE, S.paydate) BETWEEN @fromdate AND @todate
)
INSERT INTO dash.DashboardSalary (EmployeeCode, EmployeeName, EmployeeCategoryCode, EmployeeCategoryName,
    EmpTypeCode, EmpTypeName, PayDate, PayHeadCode, PayHeadName, Amount)
SELECT EmployeeCode, EmployeeName, EmployeeCategoryCode, EmployeeCategoryName,
       EmpTypeCode, EmpTypeName, PayDate, PayHeadCode, PayHeadName, Amount FROM A";

connection.Execute(salaryQuery, new { fromdate = fyStartDate, todate = DateTime.Now.AddDays(-1).Date });

Changes in Ai Inights Scheduler

using Coravel.Invocable;
using ErpCrystal_MFG.Api.Controllers;
using ErpCrystal_MFG.Api.Repositories;
using ErpCrystal_MFG.Models;


namespace ErpCrystal_MFG.Api.TaskScheduler
{
    public class AIInsightsScheduler
    (
        IEnumerable<string> dbnamelist,
        ISalesAnalysisReportRepository isalesanalysisreportrepository,
        IGrnAnalysisReportRepository igrnanalysisreportrepository,
        IArApReportRepository iarapreportrepository,
        IUtilityMethodsRepository iutilitymethodsrepository,
        IAIInsightsRepository iaiinsightsrepository,
        IEmailRepository iemailrepository,
        IPurchaseAnalysisReportRepository ipurchaseanalysisreportrepository,
        ISalesOrderAnalysisReportRepository isalesorderanalysisreportrepository,
        IIndentAnalysisReportRepository iindentanalysisreportrepository,
        IJobWorkAnalysisReportRepository ijobworkanalysisreportrepository,
        IFinancialReportRepository ifinancialreportrepository,
        IPayrollRepository ipayrollrepository

    ) : IInvocable
    {
        private readonly ISalesAnalysisReportRepository _ISalesAnalysisReportRepository = isalesanalysisreportrepository;
        private readonly IGrnAnalysisReportRepository _IGrnAnalysisReportRepository = igrnanalysisreportrepository;
        private readonly IArApReportRepository _IArApReportRepository = iarapreportrepository;
        private readonly IUtilityMethodsRepository _IUtilityMethodsRepository = iutilitymethodsrepository;
        private readonly IAIInsightsRepository _IAIInsightsRepository = iaiinsightsrepository;
        private readonly IEmailRepository _IEmailRepository = iemailrepository;
        private readonly IPurchaseAnalysisReportRepository _IPurchaseAnalysisReportRepository = ipurchaseanalysisreportrepository;
        private readonly ISalesOrderAnalysisReportRepository _ISalesOrderAnalysisReportRepository = isalesorderanalysisreportrepository;
        private readonly IIndentAnalysisReportRepository _IIndentAnalysisReportRepository = iindentanalysisreportrepository;
        private readonly IJobWorkAnalysisReportRepository _IJobWorkAnalysisReportRepository = ijobworkanalysisreportrepository;
        private readonly IFinancialReportRepository _IFinancialReportRepository = ifinancialreportrepository;
        private readonly IPayrollRepository _IPayrollRepository = ipayrollrepository;


        public async Task Invoke()
        {
            foreach (var dbname in dbnamelist)
            {
                try
                {
                    var fyStartDate = await _IAIInsightsRepository.GetPreviousFyStartDate(dbname,"3");
                    var yearLabel = await _IUtilityMethodsRepository.GetActiveYearlabel();
                    var fyStart = fyStartDate; // Use the same fyStartDate for consistency

                    // Dashboard Sales
                    SalesAnalysisReport _SalesAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportFilter = "0"
                    };

                    await _ISalesAnalysisReportRepository.SalesByProductReport(_SalesAnalysisReport, dbname, "C");


                    // Dashboard GRN
                    GrnAnalysisReport _GrnAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ConsiderBillDateUpto = DateTime.Now.AddDays(-1).Date,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportFilter = "0",
                        ReportType = "B"
                    };
                    await _IGrnAnalysisReportRepository.GrnReport(_GrnAnalysisReport, dbname, "C");

                    var arAp = new string[] { "R", "P" };
                    _IArApReportRepository.TruncateDashboardArAp(dbname);
                    // Dashboard ARAP
                    foreach (var arApType in arAp)
                    {
                        var controlAcList = await _IArApReportRepository.ControlAcCombo(dbname, arApType);
                        foreach (var controlAc in controlAcList)
                        {

                            var _ArApReport = new ArApReport
                            {
                                AgeDocType = "R",
                                ArApType = arApType,
                                ControlAcCode = controlAc.ControlAcCode,
                                FromDate = Convert.ToDateTime("01-Jan-01"),
                                ToDate = DateTime.Now.Date,
                                ReportType = "A", // Summary report
                                ShowOnlyDue = "N",
                                ScanDocType = "R",
                                ShowOnlyMatched = "N",
                                ReportFilter = "0"

                            };
                            await _IArApReportRepository.ArApAnalysis(_ArApReport, dbname, yearLabel, "C");
                        }
                    }
                    // Dashboard Puchase By Product
                    PurchaseAnalysisReport _PurchaseAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportFilter = "0"
                    };

                    await _IPurchaseAnalysisReportRepository.PurchaseByProductReport(_PurchaseAnalysisReport, dbname, "C");

                    // Dashboard Order Register
                    SalesOrderAnalysisReport _SalesOrderAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportFilter = "0"
                    };

                    await _ISalesOrderAnalysisReportRepository.OrderRegisterReport(_SalesOrderAnalysisReport, dbname, "C");

                    // Indent Register Report
                    IndentAnalysisReport _IndentAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportFilter = "0"
                    };

                    await _IIndentAnalysisReportRepository.IndentRegisterReport(_IndentAnalysisReport, dbname, "1", "C");

                    // -------job work register report
                    JobWorkAnalysisReport _JobWorkAnalysisReport = new()
                    {
                        FromDate = fyStartDate,
                        ToDate = DateTime.Now.AddDays(-1).Date,
                        UnitCode = "00",
                        ReportType = "AB", // A for In, B for Out
                        ReportFilter = "0",
                        OmitAnnexures = "N",
                        IsPendingJob = false
                    };

                    await _IJobWorkAnalysisReportRepository.JobInOutReport(_JobWorkAnalysisReport, dbname, "C");

                    // -------ledger report
                    string[] ledgerTypes = ["GL", "CL", "VL"];
                    _IFinancialReportRepository.TruncateDashboardLedger(dbname); // Truncate once outside the loop
                    foreach (var ledgerType in ledgerTypes)
                    {
                        FinancialReport _FinancialReport = new()
                        {
                            FromDate = fyStartDate,
                            ToDate = DateTime.Now.AddDays(-1).Date,
                            UnitCode = "00",
                            DivisionCode = "99",
                            ReportType = "A",
                            ReportLedgerType = ledgerType,
                            ReportFilter = "0",
                            GroupType = "B"
                        };

                        await _IFinancialReportRepository.FinancialReportData(_FinancialReport, dbname, yearLabel, fyStart, "C");
                    }

                    // Salary Table for AI Insights
                    await _IPayrollRepository.InsertSalaryDashboard(dbname, fyStartDate.Value.Date,DateTime.Now.AddDays(-1).Date);

                }
                catch (Exception ex)
                {
                    var emailController = new EmailController(_IEmailRepository, _IUtilityMethodsRepository);
                    var _ReceiverEmail = new ReceiverEmail
                    {
                        email1 = "udit@erpcrystal.in", // System Admin Email
                        emailsubject = $"AI Insights Scheduler Error for {dbname}",
                        emailmessage = $"An error occurred in the AI Insights Scheduler for database '{dbname}'.<br><br><b>Exception:</b><br>{ex.Message}<br><br><b>Stack Trace:</b><br>{ex.StackTrace}"
                    };
                    await emailController.SendEmail(dbname, _ReceiverEmail, "S");
                }
            }
        }
    }
}

Changes in JobWorkAnalysisReportRepository

public async Task<List<JobWorkMovements>> JobInOutReport(JobWorkAnalysisReport _JobWorkAnalysisReport, string dbname, string ind)
{
    // ... existing query code ...

    // Add conditional job type logic
    var jobTypeCondition = ind == "C" ? "J.Jobtype IN ('1','2')" : "J.Jobtype = @jobtype";
    // Use this in the WHERE clause: WHERE CONVERT(DATE,J.Dated) BETWEEN @fromdate AND @todate AND {jobTypeCondition}

    var truncateJobWorkTable = "TRUNCATE TABLE dash.DashboardJobWork";

    var dashboardJobWorkInsert = @"INSERT INTO dash.DashboardJobWork (jobno, ChallanTypeName, unitnamecode, AnnexureNo, AnnexureDate, InOutTypeName, partyshortname, partyname, itemgroupname, itemid, itemshortname, itemname, brandname, itemstatename, ActualWeight, ActualQty, Amount, aging, RemarkName, EWayBillNo, JobItemNotes, JobItemSpecialNotes, JobNotes, Packets, ind, partygroupname, refno, refdate)
    SELECT jobno, ChallanTypeName, unitnamecode, AnnexureNo, AnnexureDate, InOutTypeName, partyshortname, partyname, itemgroupname, itemid, itemshortname, itemname, brandname, itemstatename, ActualWeight, ActualQty, Amount, aging, RemarkName, EWayBillNo, JobItemNotes, JobItemSpecialNotes, JobNotes, Packets, ind, partygroupname, refno, refdate FROM #jobworkTemp";

    using var connection = _DapperContext.SetClientConnection(dbname);

    if (ind == "C")
    {
        connection.Execute(truncateJobWorkTable);

        var fullQuery = $"{query.Replace("-- INTO_temp", "INTO #jobworkTemp")}
        {dashboardJobWorkInsert}";

        connection.Execute(fullQuery, new
        {
            FromDate = _JobWorkAnalysisReport.FromDate?.Date,
            ToDate = _JobWorkAnalysisReport.ToDate?.Date,
            jobType,
            _JobWorkAnalysisReport.UnitCode,
            _JobWorkAnalysisReport.PartyGroupCode,
            _JobWorkAnalysisReport.PartyId,
            _JobWorkAnalysisReport.ItemGroupCode,
            _JobWorkAnalysisReport.ItemId
        });
        return new List<JobWorkMovements>();
    }
    else
    {
        var data = await connection.QueryAsync<JobWorkMovements>(query, new
        {
            FromDate = _JobWorkAnalysisReport.FromDate?.Date,
            ToDate = _JobWorkAnalysisReport.ToDate?.Date,
            jobType,
            _JobWorkAnalysisReport.UnitCode,
            _JobWorkAnalysisReport.PartyGroupCode,
            _JobWorkAnalysisReport.PartyId,
            _JobWorkAnalysisReport.ItemGroupCode,
            _JobWorkAnalysisReport.ItemId
        });
        return data.ToList();
    }
}

Changes in IFinancialReportRepository

void TruncateDashboardLedger(string dbname);

Changes in FinancialReportRepository

public void TruncateDashboardLedger(string dbname)
{
    var query = @"TRUNCATE TABLE dash.DashboardLedger";
    using var connection = _DapperContext.SetClientConnection(dbname);
    connection.Execute(query);
}

public async Task<Tuple<List<LedgerReport>, List<TrialBalanceReport>, List<TrialBalanceReport>, List<TrialBalanceReport>,
List<TrialBalanceReport>, ConfirmationOfBalance, List<ConfirmationOfBalance>>>
FinancialReportData(FinancialReport _FinancialReport, string dbname, string yearLabel, DateTime? fyStart, string ind = "")
{
    // ... existing code ...

    if (ind == "C")
    {
        var dashboardLedgerInsert = @"INSERT INTO dash.DashboardLedger (doctype, docno, docdate, unitcode, refdoc, refno, refdate, notes, cheqdetails, debitamount, creditamount, mainsubac, mainsubacname)
        SELECT doctype, docno, docdate, unitcode, refdoc, refno, refdate, notes, cheqdetails, debitamount, creditamount,
        CONCAT_WS('-', mainac, subac) AS mainsubac,
        CONCAT_WS('-', mainac, subac, ' ', COALESCE(IIF(A.achead = A1.acname, A.achead, CONCAT_WS(',', A.achead, A1.acname)), 'INVALID ACCOUNT NAME')) AS mainsubacname
        FROM #ledger L
        LEFT JOIN accounts A ON L.mainac = A.mainac
        LEFT JOIN accounts1 A1 ON L.mainac + L.subac = A1.mainac + A1.subac
        WHERE doctype NOT IN ('Y', 'Z')
        ORDER BY mainac, subac, docdate, doctype, docno, id";

        connection.Execute(dashboardLedgerInsert);
    }

    // ... return existing tuple ...
}