Quality Control - [QC Entry]

Quality Control - [QC Entry]

Doubt

  • In QC1 delete not given. From QC main how can I delete uploaded file since there will be different item grps. Should I delete using foreach? - No we will delete using yearqcno as prefix
  • Grn lines without TrnId we will not take right even for manual? - Yes
  • Need to add document Validation for uploaded certificate using AI. - Next Round
  • GRN is only for Incoming. So other options for QC Stage should I hide for now? - Yes
  • Download Certificate not working due to credential issue
  • Ref Doc we will store GRN, Invoice, SCN like this? - 3 Characters. INV, GRN etc.
  • Checked By and Approved By should we do upper case and then insert? - No
  • Report No it is generating as “R-000001-008” i.e “R-{qcno}-{itemgrpcode}” - Ok
  • Mix & manual we will remove, everything should be done through test plan. Upload/Download Certificate we will keep - Next Round

Objective

  • The QC module manages quality inspections for business documents like:
  1. Goods Receipt (Incoming QC)
  2. Production (In-Process QC)
  3. Invoice (Outgoing QC)
  • It supports:
  1. Manual QC (certificate-based)
  2. System QC (parameter-based validation)
  3. Mix QC (Combination of System + Manual based on item groups)

Data Model Overview

QC_Main (Header)
QC_Line (Item Group Level)
QC_Parameter (Parameter Level – only for system/mix mode)

Quality Control (QC) Process Flow

flowchart TD

    Start([Start])

    Start --> QC["QC (Header)
Stores QC Stage, Reference GRN, Mode & Overall Status"] QC --> Fetch["Fetch Distinct Item Groups
from Reference GRN"] Fetch --> QC1["QC1 (Item Groups)
Stores Item Group, Test Plan,
Report Details & Item Status"] QC1 --> Decision{"Test Plan Available?"} Decision -- No --> Manual["Manual QC
Upload Certificate
Enter Report Details
Select Pass / Fail"] Decision -- Yes --> QC2["QC2 (Inspection Parameters)
Parameter Records Autofetched and Inserted"] QC2 --> Bulk Update["Bulk Update Inspection Values"] Bulk Update --> Evaluate["Evaluate Parameter Results"] Manual --> UpdateQC1["Automatically Update QC1 Item Status"] Evaluate --> UpdateQC1 UpdateQC1 --> UpdateQC["Automatically Update Overall QC Status"] UpdateQC --> End([End])

Tables

CREATE TABLE QualityControl
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    qcno NVARCHAR(6),
    qcDate DATETIME,
    yearqcno NVARCHAR(12),
    qcStage NVARCHAR(3), -- IQC (Incoming), PQC (In process), FQC (Final)
    refDoc NVARCHAR(20), -- GRN (Currently restricted)
    refNo NVARCHAR(6),
    refDate DATETIME,
    mode NVARCHAR(1), -- S (System), M (Manual), X (Mix)
    qcStatus NVARCHAR(1), -- O (Open), C (Closed), P (Partial)
    checkedBy NVARCHAR(25),
    approvedBy NVARCHAR(25)
);

CREATE TABLE QualityControl1
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    qcno NVARCHAR(6),
    yearqcno NVARCHAR(12),
    itemgrpid NVARCHAR(3),
    testplancode NVARCHAR(6), -- (0 for Manual)
    reportNo NVARCHAR(20),
    reportDate DATETIME,
    itemQcStatus NVARCHAR(1),  -- Pending (N), P (Pass), F (Fail)
    remarks NVARCHAR(150)
);

CREATE TABLE QualityControl2
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    qcno NVARCHAR(6),
    yearqcno NVARCHAR(12),
    itemgrpid NVARCHAR(3),
    parameterCode NVARCHAR(6),
    actualValue DECIMAL(7,3),
    result NVARCHAR(1) -- Pending (N), P (Pass), F (Fail)
);

QC Main Level

QC Index

Fields
Column Property
QC No QcNo
QC Date QcDate
QC Stage QcStageName
Ref Doc RefDoc
Mode QcModeName
Status QcStatusName
Buttons
  • Create

QC Details

Fields
Column Property
QC No QcNo
QC Date QcDate
QC Stage QcStageName
Ref Doc RefDoc
Ref No RefNo
Ref Date RefDate
QC Mode QcModeName
Status QcStatusName
Approved By ApprovedBy
Approve QC Link Show if QCState is Closed and approvedby is empty
Buttons
  • Create
  • Print
  • Delete
  • Back
Delete Validation
Message Condition
QC is Approved. You may not delete it. If approved by is not empty

QC Create

Fields
Column Property Model Class Validation
QC Date QcDate
QC Stage HardCoded Combo QcStageCode QC Stage needs to be selected
Ref Doc HardCoded Combo RefDoc Ref Doc needs to be selected
Ref No RefNo 1. Ref No needs to be filled
2. Ref No can be upto 6 digits only
Ref Date RefDate
QC Mode HardCoded Combo QcMode QC Mode needs to be selected
Hard Coded combo values
QC Stage
  1. IQC (Incoming)
  2. PQC (In-Process)
  3. FQC (Final)
Ref Doc (Currently restricted)
  1. GRN
Mode
  1. S (System)
  2. M (Manual)
  3. X (Mix)
Validations
Message Condition
Invalid Ref No / Ref Date If not found in GRN
No lines found with QR generated. If GRN has no items with qr generated
Duplicate QC Entry If QC exists for same RefNo
Test plan available for all item groups. Mode should be system. If manual or mixed is selected when test plan is available for all item groups.
Test plan missing for all item groups. Mode should be manual. If system or mixed is selected when test plan not found for any item group.
Test plan is available for some item groups and missing for some. Mode should be mixed. If manual or system is selected when partial test plans are available.
One or more item groups are linked to Test Plans that do not contain any test parameters. Please update. If not manual and there exists test plan for correspomding item groups where test parameters are not added.
Validation Queries
public int GetRefDocExistCnt(string refNo, DateTime? refDate, string dbname)
{
    var query = @"SELECT COUNT(*) FROM Grn 
    WHERE grnNo = @refNo AND CONVERT(DATE, dated) = @refDate";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var count = connection.QuerySingleOrDefault<int>(query, new { refNo, refDate = refDate?.Date });
    return count;
}

public int GetDuplicateQcCount(string refNo, DateTime? refDate, string refDoc, int id, string dbname)
{
    var query = @"SELECT COUNT(*) FROM QualityControl
    WHERE refNo = @refNo AND CONVERT(DATE, refdate) = @refDate
    AND refDoc = @refDoc AND Id != @id";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var count = connection.QuerySingleOrDefault<int>(query, new { refNo, refDate = refDate?.Date, refDoc, id });
    return count;
}

public int GetRefDocLineCount(string refNo, DateTime? refDate, string dbname)
{
    var query = @"SELECT COUNT(*) FROM Grn1 G1
    LEFT JOIN Grn G ON G1.yeargrnno = G.yeargrnno
    WHERE G.grnno = @refNo AND CONVERT(DATE, dated) = @refDate
    AND COALESCE(trnid, '') != ''";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var count = connection.QuerySingleOrDefault<int>(query, new { refNo, refDate = refDate?.Date });
    return count;
}

public string GetItemGroupsPlanStatus(string refNo, DateTime? refDate, string qcStageCode, string dbname)
{
    var query = @"WITH A (itemgroupcode, hastestplancnt, recordcnt) AS
    (
        SELECT DISTINCT I.itemgroupid AS ItemGroupCode, IIF(TP.testPlanCode IS NULL, 0, 1) AS hastestplancnt, 1 AS recordcnt
        FROM Grn1 G1
        LEFT JOIN Grn G ON G1.yeargrnno = G.yeargrnno
        LEFT JOIN inventorymst I ON G1.itemid = I.itemid
        LEFT JOIN TestPlanMst TP ON I.itemgroupid = TP.itemgroupid AND TP.qcStage = @qcStageCode AND TP.isactive = 'Y'
        WHERE G.grnno = @refNo AND CONVERT(DATE, dated) = @refDate
        AND COALESCE(trnid, '') != ''
    ),
    B (hastestplancnt, recordcnt) AS
    (
        SELECT SUM(hastestplancnt) AS hastestplancnt,
        SUM(recordcnt) AS recordcnt FROM A
    )
    SELECT IIF(hastestplancnt = 0, 'M',
    IIF(hastestplancnt = recordcnt, 'S', 'X')) AS ind FROM B";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var data = connection.QuerySingleOrDefault<string>(query, new { refNo, refDate = refDate?.Date, qcStageCode });
    return data;
}

public int GetTestPlanLineCount(string refNo, DateTime? refDate, string qcStageCode, string dbname)
{
    var query = @"WITH A (itemgroupid, lineCnt, recordcnt) AS
    (
        SELECT DISTINCT
        I.itemgroupid,
        IIF
        (
            EXISTS
            (
            SELECT 1
            FROM TestPlanMst1 TP1
            WHERE TP1.testPlanCode = TP.testPlanCode
            ),
            1,
            0
        ) AS lineCnt,
        1 AS recordcnt
        FROM Grn1 G1
        LEFT JOIN Grn G ON G1.yeargrnno = G.yeargrnno
        LEFT JOIN inventorymst I ON G1.itemid = I.itemid
        LEFT JOIN TestPlanMst TP ON I.itemgroupid = TP.itemgroupid AND TP.qcStage = @qcStageCode AND TP.isactive = 'Y'
        WHERE G.grnno = @refNo AND CONVERT(DATE, dated) = @refDate
        AND COALESCE(trnid, '') != ''
        AND TP.testplancode IS NOT NULL
    ),
    B (lineCnt, recordcnt) AS
    (
        SELECT SUM(lineCnt) AS lineCnt,
        SUM(recordcnt) AS recordcnt FROM A
    )
    SELECT IIF(lineCnt < recordCnt, 0, 1) AS cnt FROM B";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var data = connection.QuerySingleOrDefault<int>(query, new { refNo, refDate = refDate?.Date, qcStageCode });
    return data;
}

public List<QualityControl1> GetRefDocItemGroups(string refNo, DateTime? refDate, string qcStageCode, string dbname)
{
    var query = @"SELECT DISTINCT I.itemgroupid AS ItemGroupCode,
    COALESCE(TP.testPlanCode, '0') AS testPlanCode
    FROM Grn1 G1
    LEFT JOIN Grn G ON G1.yeargrnno = G.yeargrnno
    LEFT JOIN inventorymst I ON G1.itemid = I.itemid
    LEFT JOIN TestPlanMst TP ON I.itemgroupid = TP.itemgroupid AND TP.qcStage = @qcStageCode AND TP.isactive = 'Y'
    WHERE  G.grnno = @refNo AND CONVERT(DATE, dated) = @refDate
    AND COALESCE(trnid, '') != ''";

    using var connection = _DapperContext.SetClientConnection(dbname);
    var data = connection.Query<QualityControl1>(query, new { refNo, refDate = refDate?.Date, qcStageCode });
    return data.ToList();
}
Points
  • QC No & YearQCNo → System generated
  • Default Status → O (OPEN)
Mode Determination Logic
Condition Mode
All item groups have test plan S
None have test plan M
Partial availability X
Processing Logic

Manual Mode

  • QC1 populated with item groups
  • testplancode = 0
  • User must:
    1. Upload certificate (per item group)
    2. Enter Report No & Date
    3. Select the QC Status for that item group
  • QC2 → NOT used
System Mode
  • QC1 populated with item groups
  • testplancode assigned
  • QC2 populated with parameters
  • Report No → Auto generated
  • Report Date → DateTime.Now
Mix Mode
  • Item groups with test plan → System
  • Item groups without → Manual

Print Report PDF

Header
  • QC No LEFT ALIGN
  • QC Date LEFT ALIGN
  • QC Mode LEFT ALIGN
  • Overall Result RIGHT ALIGN
  • QC Status RIGHT ALIGN
  • Approval Status RIGHT ALIGN
Reference Information
  • Reference Document LEFT ALIGN
  • Reference Number LEFT ALIGN
  • Reference Date LEFT ALIGN
  • Checked By RIGHT ALIGN
  • Approved By RIGHT ALIGN
Inspection Summary
  • Sr No
  • Item Group
  • Report No
  • Report Date
  • Test Plan
  • Result
  • QA Remark
Detailed Inspection Results
Header
  • Item Group
  • Report No
  • Report Date
Details
  • Sr No
  • Parameter Name
  • Parameter Type
  • Value
  • Range
  • Result
Certificate
  • The inspected material(s) have been verified against the defined quality control requirements and found to conform to all applicable acceptance criteria.
  • Material Status
Footer
  • Checked By
  • Approved By
  • Generated By

QC Line Level 1

  • Records will be autofetched and inserted from Reference Document.
  • For System model Report Number and date will be generated by System.
  • For Manaual mode Report Number and date will be given by user.
  • User can also write remarks.
  • Initially Item QC Status will be inserted as Pending

QC 1 Index

Fields
Column Property
QC No QcNo
Item Group ItemGroupName
Test Plan Code TestPlanCode
Item QC Status ItemQcStatusName

QC 1 Details

Main Fields
Column Property
QC No QcNo
Item Group ItemGroupName
Test Plan Code TestPlanCode
Test Plan Name TestPlanName Show if TestPlanCode != "0"
Report No ReportNo
Report Date ReportDate
Item QC Status ItemQcStatusName
QA Remark Remarks
Sub Fields Show only if testplancode != "0"
Column Property
Parameter Code ParameterCode
Parameter Name ParameterName
Parameter Type ParameterTypeName
Value If Type User then ResultName else ActualValue
Result ResultName
Buttons
  • Modify QA Remark Dialog Box
  • Upload / Download Certificate Show only if testplancode = "0"
  • Bulk Update Show only if testplancode != "0"

QC 1 Upload

Fields
Column Property
QC No Read only QcNo
Item Group ItemGroupName
Report No ReportNo
Report Date ReportDate
Item QC Status Hardcoded Combo (Pass/Fail) ItemQcStatusCode By default will be Pass
Upload File
Validations
  • QC is Approved. You may not modify it.
  • File needs to be selected.
  • File size must be up to 1 MB
  • Report Date needs to be selected. If it is 01-Jan-01 only

QC Line Level 2

  • Will be available only for System or Mix mode and test plan exists
  • Initially result will be Pending

QC 2 Bulk Update

  • Bulk Update will not be allowed if QC is approved.
Download Model
  • Values will be prefilled and only actual Value column will be open

  • Fields

    Column Property
    Id Hidden Id
    Parameter Code ParameterCode
    Parameter Name ParameterCode
    Parameter Type Code Hidden ParameterTypeCode
    Parameter Type Name ParameterTypeName
    Value Value
  • Instruction Sheet

    Column Validation
    Value For parameter type System fill numeric value and for Manual fill P(Pass)/F(Fail)
Reading XL Error code
Column Validation
Z1 Value must be in proper number format If parameter type System
Z2 Value must be between 0 to 9999.999 If parameter type System
Z3 Value must be P - Pass, F - Fail If parameter type User
Repository XL Sheet
Column Property
Company Name companydata.CompanyName
Report Name Quality Control Parameter Bulk Update
Report Date DateTime.Now.Date.ToString(“dd-MMM-yy”)
QC No, Item Group data.QcNo, data.ItemGroupName
Error Code ErrorCode
Id Id
Parameter Code ParameterCode
Parameter Name ParameterCode
Parameter Type Code ParameterTypeCode
Parameter Type Name ParameterTypeName
Value Value
Actual Value ActualValue
Result ResultName
Repository Error Code Message
Validation
Model has been altered. Records are missing. Please re-download the model. Bulk update failed due to validation errors.
Model has been altered. Records are not matching. Please re-download the model. Bulk update failed due to validation errors.
Bulk uspdate Succeeded.

Repository Query

public async Task<Tuple<List<QualityControl2>, string>> QualityControl2BulkUpdate(int qc1Id, List<QualityControl2> listData, string dbname, string yearLabel, string userName, string itemGroupCode)
{
    var tempCreate = @"CREATE TABLE #temp
    (
        Id INT,
        parametercode NVARCHAR(6) COLLATE DATABASE_DEFAULT,
        parametername NVARCHAR(100) COLLATE DATABASE_DEFAULT,
        parametertypecode NVARCHAR(1) COLLATE DATABASE_DEFAULT,
        parametertypename NVARCHAR(10) COLLATE DATABASE_DEFAULT,
        value NVARCHAR(10) COLLATE DATABASE_DEFAULT,
        actualvalue NUMERIC(7,3),
        result NVARCHAR(1) COLLATE DATABASE_DEFAULT
    )";

    var tempinsertion = @"INSERT INTO #temp (Id,parametercode,parametername,parametertypecode,parametertypename,value,actualvalue) 
    VALUES (@Id,@parametercode,@parametername,@parametertypecode,@parametertypename,@value,0)";

    var errorCodeA = @"WITH A (LineCnt) AS 
    (
        SELECT COUNT(*) AS LineCnt
        FROM #temp
    ),
    B (LineCnt) AS 
    (
        SELECT COUNT(*) AS LineCnt
        FROM QualityControl2 Q2
        LEFT JOIN QualityControl1 Q1 ON Q2.yearqcno = Q1.yearqcno AND Q2.itemgrpid = Q1.itemgrpid
        WHERE Q1.id = @qc1Id
    ),
    C (LineCnt) AS 
    (
        SELECT (LineCnt * -1) AS LineCnt FROM A
        UNION ALL
        SELECT LineCnt FROM B

    )
    SELECT SUM(LineCnt) FROM C";

    var errorCodeB = @"WITH A (id) AS 
    (
        SELECT Q2.id
        FROM QualityControl2 Q2
        LEFT JOIN QualityControl1 Q1 ON Q2.yearqcno = Q1.yearqcno AND Q2.itemgrpid = Q1.itemgrpid
        WHERE Q1.id = @qc1Id
    ),
    B (id) AS 
    (
        SELECT id FROM A WHERE NOT EXISTS (SELECT id FROM #temp WHERE A.id = #temp.id)
    )
    SELECT COUNT(*) FROM B";

    var updateQc2TempSystemStatus = @"WITH A (result, actualvalue, id) AS
    (
        SELECT IIF(Z.value BETWEEN Q.minvalue AND Q.maxvalue, 'P', 'F') AS Result,
        Z.value AS actualvalue, Z.id
        FROM #temp Z
        LEFT JOIN QualityParameter Q ON Z.parametercode = Q.parametercode
        WHERE Z.parametertypecode = 'S'
    )
    UPDATE #temp SET actualvalue = A.actualvalue, result = A.result
    FROM A
    WHERE #temp.id = A.id";

    var updateQc2TempUserStatus = @"WITH A (result, id) AS
    (
        SELECT Z.value AS result, Z.id
        FROM #temp Z
        WHERE Z.parametertypecode = 'U'
    )
    UPDATE #temp SET result = A.result
    FROM A
    WHERE #temp.id = A.id";

    var updateQc2Status = @"UPDATE QualityControl2 SET result = Z.result,
    actualvalue = Z.actualvalue
    FROM #temp Z
    WHERE QualityControl2.id = Z.id";

    var updateQc1Status = @"WITH A (itemqcstatus) AS
    (
        SELECT IIF(SUM(IIF(Q2.result != 'P', 1, 0)) = 0, 'P', 'F') AS itemqcstatus
        FROM QualityControl2 Q2
        LEFT JOIN QualityControl1 Q1 ON Q2.yearqcno = Q1.yearqcno AND Q2.itemgrpid = Q1.itemgrpid
        WHERE Q1.id = @qc1Id
    )
    UPDATE QualityControl1 SET itemqcstatus = A.itemqcstatus
    FROM A
    WHERE QualityControl1.id = @qc1Id";

    var updateQcStatus = @"WITH A (qcStatus) AS
    (
        SELECT IIF(SUM(IIF(Q1.itemqcstatus = 'N', 1, 0)) = 0, 'C', 'P') AS qcStatus
        FROM QualityControl1 Q1
        WHERE Q1.YearQcNo = @YearQcNo
    )
    UPDATE QualityControl SET qcStatus = A.qcStatus
    FROM A
    WHERE QualityControl.yearqcno = @YearQcNo";

    var finalSelect = @"SELECT id, parametercode, parametername, parametertypecode, parametertypename, value,
    actualvalue, IIF(result = 'P', 'Pass', 'Fail') AS resultname
    FROM #temp
    ORDER BY Id ASC";

    using var connection = _DapperContext.SetClientConnection(dbname);
    connection.Open();

    connection.Execute(tempCreate);

    connection.Execute(tempinsertion, listData.Select(item => new
    {
        item.Id,
        item.ParameterCode,
        item.ParameterName,
        item.ParameterTypeCode,
        item.ParameterTypeName,
        item.Value
    }));

    var errorcodeACnt = connection.QuerySingleOrDefault<int>(errorCodeA, new { qc1Id });
    var errorcodeBCnt = connection.QuerySingleOrDefault<int>(errorCodeB, new { qc1Id });

    string msg;
    string errorCodeMsg = string.Empty;
    if (errorcodeACnt != 0)
    {
        errorCodeMsg = "Model has been altered. Records are missing. Please re-download the model.";
    }
    else if (errorcodeBCnt != 0)
    {
        errorCodeMsg = "Model has been altered. Records are not matching. Please re-download the model.";
    }
    if (errorcodeACnt == 0 && errorcodeBCnt == 0)
    {
        connection.Execute(updateQc2TempSystemStatus);
        connection.Execute(updateQc2TempUserStatus);
        connection.Execute(updateQc2Status);
        connection.Execute(updateQc1Status, new { qc1Id });
        var data = QualityControl1Details(qc1Id, dbname).Result;
        connection.Execute(updateQcStatus, new { data.YearQcNo });
        _IUtilityMethodsRepository.InsertMFGLog(dbname, userName, "", "QualityControl", "Bulk Update", yearLabel, $"Updated Parameters for Item Group : {itemGroupCode}");
        msg = "Bulk update Succeeded.";
    }
    else
    {
        msg = $"{errorCodeMsg} Bulk update failed due to validation errors.";
    }

    var resultData = await connection.QueryAsync<QualityControl2>(finalSelect);
    var resultList = resultData.ToList();

    return new Tuple<List<QualityControl2>, string>(resultList, msg);
}

Parameter Execution Logic

System Parameter
  • Inititally status will be → Pending
  • User fills actual values
  • IF actualValue BETWEEN minValue AND maxValue → PASS ELSE → FAIL
Manual Parameter
  • User selects: Pass / Fail

Status Logic

QC2 (Parameter)

  • N → Pending
  • P → Pass
  • F → Fail

QC1 (Item Group)

IF Manual → user input
IF System:
    IF any FAIL → F
    ELSE IF all PASS → P
    ELSE → N

QC_Main

IF all QC1 = PASS → C
IF all QC1 = Pending → O
ELSE → P

QC Locking

  • If QC exists → Modify/Delete not allowed for linked refdoc

Edge Case Handling

Multiple Items in Item Group (Critical)

Problem:

  • 3 items → 2 good, 1 bad

Solution

👉 Use Worst Case Logic

IF ANY sample fails → Item Group FAIL

Input Approach

  • User must enter: Minimum observed value

Model Class

using System.ComponentModel.DataAnnotations;

namespace ErpCrystal_MFG.Models;

public class QualityControl
{
    public int Id { get; set; }
    public string QcNo { get; set; } = string.Empty;
    public DateTime? QcDate { get; set; } = DateTime.Now;
    public string YearQcNo { get; set; } = string.Empty;
    [Required(ErrorMessage = "QC Stage needs to be selected")]
    public string QcStageCode { get; set; } = string.Empty;
    public string QcStageName { get; set; } = string.Empty;
    [Required(ErrorMessage = "Ref Doc needs to be selected")]
    public string RefDoc { get; set; } = string.Empty;
    [Required(ErrorMessage = "Ref No needs to be filled")]
    [StringLength(6, ErrorMessage = "Ref No can be upto 6 digits only")]
    public string RefNo { get; set; } = string.Empty;
    public DateTime? RefDate { get; set; } = DateTime.Now;
    [Required(ErrorMessage = "QC Mode needs to be selected")]
    public string QcModeCode { get; set; } = string.Empty;
    public string QcModeName { get; set; } = string.Empty;
    public string QcStatusCode { get; set; } = string.Empty;
    public string QcStatusName { get; set; } = string.Empty;
    public string CheckedBy { get; set; } = string.Empty;
    public string ApprovedBy { get; set; } = string.Empty;
    public string DeleteReason { get; set; } = string.Empty;
}

public class QualityControl1
{
    public int Id { get; set; }
    public string QcNo { get; set; } = string.Empty;
    public string YearQcNo { get; set; } = string.Empty;
    public string ItemGroupCode { get; set; } = string.Empty;
    public string ItemGroupName { get; set; } = string.Empty;
    public string TestPlanCode { get; set; } = string.Empty;
    public string TestPlanName { get; set; } = string.Empty;
    public int MainId { get; set; }
    [Required(ErrorMessage = "Report No needs to be filled")]
    [StringLength(20, ErrorMessage = "Report No can be upto 20 characters only")]
    public string ReportNo { get; set; } = string.Empty;
    public DateTime? ReportDate { get; set; } = DateTime.Now;
    public string ItemQcStatusCode { get; set; } = string.Empty;
    public string ItemQcStatusName { get; set; } = string.Empty;
    [StringLength(150, ErrorMessage = "QA Remark can be upto 150 characters only")]
    public string Remarks { get; set; } = string.Empty;
}

public class QualityControl2
{
    public int Id { get; set; }
    public string QcNo { get; set; } = string.Empty;
    public string YearQcNo { get; set; } = string.Empty;
    public string ItemGroupCode { get; set; } = string.Empty;
    public string ItemGroupName { get; set; } = string.Empty;
    public string ParameterCode { get; set; } = string.Empty;
    public string ParameterName { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
    public decimal ActualValue { get; set; }
    public string ResultCode { get; set; } = string.Empty;
    public string ResultName { get; set; } = string.Empty;
    public string ParameterTypeCode { get; set; } = string.Empty;
    public string ParameterTypeName { get; set; } = string.Empty;
    public string ErrorCode { get; set; } = string.Empty;
}

public class QualityControlPrint
{
    public string YearQcNo { get; set; } = string.Empty;
    public string QcNo { get; set; } = string.Empty;
    public DateTime? QcDate { get; set; } = DateTime.Now;
    public string QcStageName { get; set; } = string.Empty;
    public string RefDoc { get; set; } = string.Empty;
    public string RefNo { get; set; } = string.Empty;
    public DateTime? RefDate { get; set; } = DateTime.Now;
    public string QcModeName { get; set; } = string.Empty;
    public string QcStatusName { get; set; } = string.Empty;
    public string PdfFileName { get; set; } = string.Empty;
    // QC 1
    public string ItemGroupName { get; set; } = string.Empty;
    public string TestPlanName { get; set; } = string.Empty;
    public string ReportNo { get; set; } = string.Empty;
    public DateTime? ReportDate { get; set; } = DateTime.Now;
    public string ItemQcStatusName { get; set; } = string.Empty;
    public string Remarks { get; set; } = string.Empty;
    // QC 2
    public string ParameterName { get; set; } = string.Empty;
    public string ParameterTypeCode { get; set; } = string.Empty;
    public string ParameterTypeName { get; set; } = string.Empty;
    public string ActualValue { get; set; } = string.Empty;
    public string ResultName { get; set; } = string.Empty;
    public string CheckedBy { get; set; } = string.Empty;
    public string ApprovedBy { get; set; } = string.Empty;
}

File & Method Names

File Name
Model Class - QualityControl
Service - IQualityControlService, QualityControlService
Controller - QualityControlController, QuestPDF_QualityControl
Repository - IQualityControlRepository, QualityControlRepository
Razor Pages - QualityControlIndex
- QualityControlDetails
- QualityControlCreate
- QualityControl1Index
- QualityControl1Details
- QualityControl1Upload
- QualityControl2BulkUpdate
Method Name - QualityControlIndex
- QualityControlDetails
- QualityControlValidateCreate
- GetRefDocExistCnt
- GetDuplicateQcCount
- GetRefDocLineCount
- GetItemGroupsPlanStatus
- GetTestPlanLineCount
- GetRefDocItemGroups
- QualityControlCreate
- GetMaxQcNo
- QualityControlDelete
- QualityControl1Index
- QualityControl1Details
- QualityControl1CreateLines
- QualityControl1ModifyRemarks
- QualityControl1ModifyUpload
- QualityControl2Index
- QualityControl2DownloadModel
- QualityControl2BulkUpdate
- QualityControlPrint
- QualityControlPrintData
- ApproveQC

Role Name

Role
QualityControlDetails
QualityControlDelete
ApproveQC
QualityControlCreate
QualityControlModify