<?php

namespace ProjectManagement\prjProjectManagement;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Container\ContainerInterface;
use Slim\Routing\RouteCollectorProxy;
use Slim\App;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Closure;

/**
 * Table class for projectpaymentprocess
 */
class Projectpaymentprocess extends DbTable
{
    protected $SqlFrom = "";
    protected $SqlSelect = null;
    protected $SqlSelectList = null;
    protected $SqlWhere = "";
    protected $SqlGroupBy = "";
    protected $SqlHaving = "";
    protected $SqlOrderBy = "";
    public $DbErrorMessage = "";
    public $UseSessionForListSql = true;

    // Column CSS classes
    public $LeftColumnClass = "col-sm-2 col-form-label ew-label";
    public $RightColumnClass = "col-sm-10";
    public $OffsetColumnClass = "col-sm-10 offset-sm-2";
    public $TableLeftColumnClass = "w-col-2";

    // Ajax / Modal
    public $UseAjaxActions = false;
    public $ModalSearch = false;
    public $ModalView = false;
    public $ModalAdd = false;
    public $ModalEdit = false;
    public $ModalUpdate = false;
    public $InlineDelete = false;
    public $ModalGridAdd = false;
    public $ModalGridEdit = false;
    public $ModalMultiEdit = false;

    // Fields
    public $PaymentProcessID;
    public $DocumentName;
    public $FileName;
    public $Completed;
    public $DateDelivered;
    public $PICClient;
    public $Remark;
    public $templateid;
    public $_ProjectID;
    public $Created_Date;
    public $Created_By;
    public $Last_Updated_Date;
    public $Last_Updated_By;

    // Page ID
    public $PageID = ""; // To be overridden by subclass

    // Constructor
    public function __construct()
    {
        parent::__construct();
        global $Language, $CurrentLanguage, $CurrentLocale;

        // Language object
        $Language = Container("app.language");
        $this->TableVar = "projectpaymentprocess";
        $this->TableName = 'projectpaymentprocess';
        $this->TableType = "TABLE";
        $this->ImportUseTransaction = $this->supportsTransaction() && Config("IMPORT_USE_TRANSACTION");
        $this->UseTransaction = $this->supportsTransaction() && Config("USE_TRANSACTION");

        // Update Table
        $this->UpdateTable = "projectpaymentprocess";
        $this->Dbid = 'DB';
        $this->ExportAll = true;
        $this->ExportPageBreakCount = 0; // Page break per every n record (PDF only)

        // PDF
        $this->ExportPageOrientation = "portrait"; // Page orientation (PDF only)
        $this->ExportPageSize = "a4"; // Page size (PDF only)

        // PhpSpreadsheet
        $this->ExportExcelPageOrientation = \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_DEFAULT; // Page orientation (PhpSpreadsheet only)
        $this->ExportExcelPageSize = \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_A4; // Page size (PhpSpreadsheet only)

        // PHPWord
        $this->ExportWordPageOrientation = "portrait"; // Page orientation (PHPWord only)
        $this->ExportWordPageSize = "A4"; // Page orientation (PHPWord only)
        $this->ExportWordColumnWidth = null; // Cell width (PHPWord only)
        $this->DetailAdd = false; // Allow detail add
        $this->DetailEdit = false; // Allow detail edit
        $this->DetailView = true; // Allow detail view
        $this->ShowMultipleDetails = false; // Show multiple details
        $this->GridAddRowCount = 5;
        $this->AllowAddDeleteRow = true; // Allow add/delete row
        $this->UseAjaxActions = $this->UseAjaxActions || Config("USE_AJAX_ACTIONS");
        $this->UseColumnVisibility = true;
        $this->UserIDAllowSecurity = Config("DEFAULT_USER_ID_ALLOW_SECURITY"); // Default User ID allowed permissions
        $this->BasicSearch = new BasicSearch($this);

        // PaymentProcessID
        $this->PaymentProcessID = new DbField(
            $this, // Table
            'x_PaymentProcessID', // Variable name
            'PaymentProcessID', // Name
            '`PaymentProcessID`', // Expression
            '`PaymentProcessID`', // Basic search expression
            3, // Type
            11, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`PaymentProcessID`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'NO' // Edit Tag
        );
        $this->PaymentProcessID->InputTextType = "text";
        $this->PaymentProcessID->Raw = true;
        $this->PaymentProcessID->IsAutoIncrement = true; // Autoincrement field
        $this->PaymentProcessID->IsPrimaryKey = true; // Primary key field
        $this->PaymentProcessID->Nullable = false; // NOT NULL field
        $this->PaymentProcessID->DefaultErrorMessage = $Language->phrase("IncorrectInteger");
        $this->PaymentProcessID->SearchOperators = ["=", "<>", "IN", "NOT IN", "<", "<=", ">", ">=", "BETWEEN", "NOT BETWEEN"];
        $this->Fields['PaymentProcessID'] = &$this->PaymentProcessID;

        // DocumentName
        $this->DocumentName = new DbField(
            $this, // Table
            'x_DocumentName', // Variable name
            'DocumentName', // Name
            '`DocumentName`', // Expression
            '`DocumentName`', // Basic search expression
            200, // Type
            100, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`DocumentName`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'TEXT' // Edit Tag
        );
        $this->DocumentName->InputTextType = "text";
        $this->DocumentName->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->DocumentName->Lookup = new Lookup($this->DocumentName, 'projectpaymentprocess', true, 'DocumentName', ["DocumentName","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->DocumentName->Lookup = new Lookup($this->DocumentName, 'projectpaymentprocess', true, 'DocumentName', ["DocumentName","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->DocumentName->SearchOperators = ["=", "<>", "IN", "NOT IN", "STARTS WITH", "NOT STARTS WITH", "LIKE", "NOT LIKE", "ENDS WITH", "NOT ENDS WITH", "IS EMPTY", "IS NOT EMPTY", "IS NULL", "IS NOT NULL"];
        $this->Fields['DocumentName'] = &$this->DocumentName;

        // FileName
        $this->FileName = new DbField(
            $this, // Table
            'x_FileName', // Variable name
            'FileName', // Name
            '`FileName`', // Expression
            '`FileName`', // Basic search expression
            200, // Type
            500, // Size
            -1, // Date/Time format
            true, // Is upload field
            '`FileName`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'FILE' // Edit Tag
        );
        $this->FileName->addMethod("getUploadPath", fn() => "files/projects/".$this->_ProjectID->DbValue . "/payments");
        $this->FileName->InputTextType = "text";
        $this->FileName->SearchOperators = ["=", "<>", "STARTS WITH", "NOT STARTS WITH", "LIKE", "NOT LIKE", "ENDS WITH", "NOT ENDS WITH", "IS EMPTY", "IS NOT EMPTY", "IS NULL", "IS NOT NULL"];
        $this->Fields['FileName'] = &$this->FileName;

        // Completed
        $this->Completed = new DbField(
            $this, // Table
            'x_Completed', // Variable name
            'Completed', // Name
            '`Completed`', // Expression
            '`Completed`', // Basic search expression
            200, // Type
            1, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`Completed`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'CHECKBOX' // Edit Tag
        );
        $this->Completed->InputTextType = "text";
        $this->Completed->Raw = true;
        $this->Completed->setDataType(DataType::BIT);
        $this->Completed->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->Completed->Lookup = new Lookup($this->Completed, 'projectpaymentprocess', true, 'Completed', ["Completed","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->Completed->Lookup = new Lookup($this->Completed, 'projectpaymentprocess', true, 'Completed', ["Completed","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->Completed->OptionCount = 2;
        $this->Completed->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['Completed'] = &$this->Completed;

        // DateDelivered
        $this->DateDelivered = new DbField(
            $this, // Table
            'x_DateDelivered', // Variable name
            'DateDelivered', // Name
            '`DateDelivered`', // Expression
            CastDateFieldForLike("`DateDelivered`", 5, "DB"), // Basic search expression
            133, // Type
            10, // Size
            5, // Date/Time format
            false, // Is upload field
            '`DateDelivered`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'TEXT' // Edit Tag
        );
        $this->DateDelivered->InputTextType = "text";
        $this->DateDelivered->Raw = true;
        $this->DateDelivered->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->DateDelivered->Lookup = new Lookup($this->DateDelivered, 'projectpaymentprocess', true, 'DateDelivered', ["DateDelivered","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->DateDelivered->Lookup = new Lookup($this->DateDelivered, 'projectpaymentprocess', true, 'DateDelivered', ["DateDelivered","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->DateDelivered->DefaultErrorMessage = str_replace("%s", DateFormat(5), $Language->phrase("IncorrectDate"));
        $this->DateDelivered->SearchOperators = ["=", "<>", "IN", "NOT IN", "<", "<=", ">", ">=", "BETWEEN", "NOT BETWEEN", "IS NULL", "IS NOT NULL"];
        $this->Fields['DateDelivered'] = &$this->DateDelivered;

        // PICClient
        $this->PICClient = new DbField(
            $this, // Table
            'x_PICClient', // Variable name
            'PICClient', // Name
            '`PICClient`', // Expression
            '`PICClient`', // Basic search expression
            3, // Type
            255, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`PICClient`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'SELECT' // Edit Tag
        );
        $this->PICClient->InputTextType = "text";
        $this->PICClient->Raw = true;
        $this->PICClient->setSelectMultiple(false); // Select one
        $this->PICClient->UsePleaseSelect = true; // Use PleaseSelect by default
        $this->PICClient->PleaseSelectText = $Language->phrase("PleaseSelect"); // "PleaseSelect" text
        $this->PICClient->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->PICClient->Lookup = new Lookup($this->PICClient, 'masteruser', true, 'EmployeeID', ["FullName","","",""], '', '', [], [], [], [], [], [], false, '', '', "CONCAT(FirstName, ' ', LastName)");
                break;
            default:
                $this->PICClient->Lookup = new Lookup($this->PICClient, 'masteruser', true, 'EmployeeID', ["FullName","","",""], '', '', [], [], [], [], [], [], false, '', '', "CONCAT(FirstName, ' ', LastName)");
                break;
        }
        $this->PICClient->DefaultErrorMessage = $Language->phrase("IncorrectInteger");
        $this->PICClient->SearchOperators = ["=", "<>", "<", "<=", ">", ">=", "BETWEEN", "NOT BETWEEN", "IS NULL", "IS NOT NULL"];
        $this->Fields['PICClient'] = &$this->PICClient;

        // Remark
        $this->Remark = new DbField(
            $this, // Table
            'x_Remark', // Variable name
            'Remark', // Name
            '`Remark`', // Expression
            '`Remark`', // Basic search expression
            200, // Type
            1200, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`Remark`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'TEXTAREA' // Edit Tag
        );
        $this->Remark->InputTextType = "text";
        $this->Remark->SearchOperators = ["=", "<>", "IN", "NOT IN", "STARTS WITH", "NOT STARTS WITH", "LIKE", "NOT LIKE", "ENDS WITH", "NOT ENDS WITH", "IS EMPTY", "IS NOT EMPTY", "IS NULL", "IS NOT NULL"];
        $this->Fields['Remark'] = &$this->Remark;

        // templateid
        $this->templateid = new DbField(
            $this, // Table
            'x_templateid', // Variable name
            'templateid', // Name
            '`templateid`', // Expression
            '`templateid`', // Basic search expression
            3, // Type
            11, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`templateid`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'HIDDEN' // Edit Tag
        );
        $this->templateid->InputTextType = "text";
        $this->templateid->Raw = true;
        $this->templateid->Sortable = false; // Allow sort
        $this->templateid->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->templateid->Lookup = new Lookup($this->templateid, 'projectpaymentprocess', true, 'templateid', ["templateid","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->templateid->Lookup = new Lookup($this->templateid, 'projectpaymentprocess', true, 'templateid', ["templateid","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->templateid->DefaultErrorMessage = $Language->phrase("IncorrectInteger");
        $this->templateid->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['templateid'] = &$this->templateid;

        // ProjectID
        $this->_ProjectID = new DbField(
            $this, // Table
            'x__ProjectID', // Variable name
            'ProjectID', // Name
            '`ProjectID`', // Expression
            '`ProjectID`', // Basic search expression
            3, // Type
            11, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`ProjectID`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'SELECT' // Edit Tag
        );
        $this->_ProjectID->InputTextType = "text";
        $this->_ProjectID->Raw = true;
        $this->_ProjectID->IsForeignKey = true; // Foreign key field
        $this->_ProjectID->Required = true; // Required field
        $this->_ProjectID->setSelectMultiple(false); // Select one
        $this->_ProjectID->UsePleaseSelect = true; // Use PleaseSelect by default
        $this->_ProjectID->PleaseSelectText = $Language->phrase("PleaseSelect"); // "PleaseSelect" text
        $this->_ProjectID->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->_ProjectID->Lookup = new Lookup($this->_ProjectID, 'masterproject', true, 'ProjectID', ["ProjectName","","",""], '', '', [], [], [], [], [], [], false, '', '', "`ProjectName`");
                break;
            default:
                $this->_ProjectID->Lookup = new Lookup($this->_ProjectID, 'masterproject', true, 'ProjectID', ["ProjectName","","",""], '', '', [], [], [], [], [], [], false, '', '', "`ProjectName`");
                break;
        }
        $this->_ProjectID->DefaultErrorMessage = $Language->phrase("IncorrectInteger");
        $this->_ProjectID->SearchOperators = ["=", "<>", "<", "<=", ">", ">=", "BETWEEN", "NOT BETWEEN", "IS NULL", "IS NOT NULL"];
        $this->Fields['ProjectID'] = &$this->_ProjectID;

        // Created_Date
        $this->Created_Date = new DbField(
            $this, // Table
            'x_Created_Date', // Variable name
            'Created_Date', // Name
            '`Created_Date`', // Expression
            CastDateFieldForLike("`Created_Date`", 109, "DB"), // Basic search expression
            135, // Type
            19, // Size
            109, // Date/Time format
            false, // Is upload field
            '`Created_Date`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'HIDDEN' // Edit Tag
        );
        $this->Created_Date->addMethod("getDefault", fn() => CurrentDateTime());
        $this->Created_Date->InputTextType = "text";
        $this->Created_Date->Raw = true;
        $this->Created_Date->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->Created_Date->Lookup = new Lookup($this->Created_Date, 'projectpaymentprocess', true, 'Created_Date', ["Created_Date","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->Created_Date->Lookup = new Lookup($this->Created_Date, 'projectpaymentprocess', true, 'Created_Date', ["Created_Date","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->Created_Date->DefaultErrorMessage = str_replace("%s", DateFormat(109), $Language->phrase("IncorrectDate"));
        $this->Created_Date->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['Created_Date'] = &$this->Created_Date;

        // Created_By
        $this->Created_By = new DbField(
            $this, // Table
            'x_Created_By', // Variable name
            'Created_By', // Name
            '`Created_By`', // Expression
            '`Created_By`', // Basic search expression
            200, // Type
            255, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`Created_By`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'HIDDEN' // Edit Tag
        );
        $this->Created_By->addMethod("getDefault", fn() => CurrentUserName());
        $this->Created_By->InputTextType = "text";
        $this->Created_By->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->Created_By->Lookup = new Lookup($this->Created_By, 'projectpaymentprocess', true, 'Created_By', ["Created_By","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->Created_By->Lookup = new Lookup($this->Created_By, 'projectpaymentprocess', true, 'Created_By', ["Created_By","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->Created_By->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['Created_By'] = &$this->Created_By;

        // Last_Updated_Date
        $this->Last_Updated_Date = new DbField(
            $this, // Table
            'x_Last_Updated_Date', // Variable name
            'Last_Updated_Date', // Name
            '`Last_Updated_Date`', // Expression
            CastDateFieldForLike("`Last_Updated_Date`", 109, "DB"), // Basic search expression
            135, // Type
            19, // Size
            109, // Date/Time format
            false, // Is upload field
            '`Last_Updated_Date`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'HIDDEN' // Edit Tag
        );
        $this->Last_Updated_Date->addMethod("getAutoUpdateValue", fn() => CurrentDateTime());
        $this->Last_Updated_Date->addMethod("getDefault", fn() => CurrentDateTime());
        $this->Last_Updated_Date->InputTextType = "text";
        $this->Last_Updated_Date->Raw = true;
        $this->Last_Updated_Date->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->Last_Updated_Date->Lookup = new Lookup($this->Last_Updated_Date, 'projectpaymentprocess', true, 'Last_Updated_Date', ["Last_Updated_Date","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->Last_Updated_Date->Lookup = new Lookup($this->Last_Updated_Date, 'projectpaymentprocess', true, 'Last_Updated_Date', ["Last_Updated_Date","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->Last_Updated_Date->DefaultErrorMessage = str_replace("%s", DateFormat(109), $Language->phrase("IncorrectDate"));
        $this->Last_Updated_Date->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['Last_Updated_Date'] = &$this->Last_Updated_Date;

        // Last_Updated_By
        $this->Last_Updated_By = new DbField(
            $this, // Table
            'x_Last_Updated_By', // Variable name
            'Last_Updated_By', // Name
            '`Last_Updated_By`', // Expression
            '`Last_Updated_By`', // Basic search expression
            200, // Type
            255, // Size
            -1, // Date/Time format
            false, // Is upload field
            '`Last_Updated_By`', // Virtual expression
            false, // Is virtual
            false, // Force selection
            false, // Is Virtual search
            'FORMATTED TEXT', // View Tag
            'HIDDEN' // Edit Tag
        );
        $this->Last_Updated_By->addMethod("getAutoUpdateValue", fn() => CurrentUserName());
        $this->Last_Updated_By->addMethod("getDefault", fn() => CurrentUserName());
        $this->Last_Updated_By->InputTextType = "text";
        $this->Last_Updated_By->UseFilter = true; // Table header filter
        global $CurrentLanguage;
        switch ($CurrentLanguage) {
            case "en-US":
                $this->Last_Updated_By->Lookup = new Lookup($this->Last_Updated_By, 'projectpaymentprocess', true, 'Last_Updated_By', ["Last_Updated_By","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
            default:
                $this->Last_Updated_By->Lookup = new Lookup($this->Last_Updated_By, 'projectpaymentprocess', true, 'Last_Updated_By', ["Last_Updated_By","","",""], '', '', [], [], [], [], [], [], false, '', '', "");
                break;
        }
        $this->Last_Updated_By->SearchOperators = ["=", "<>", "IS NULL", "IS NOT NULL"];
        $this->Fields['Last_Updated_By'] = &$this->Last_Updated_By;

        // Add Doctrine Cache
        $this->Cache = new \Symfony\Component\Cache\Adapter\ArrayAdapter();
        $this->CacheProfile = new \Doctrine\DBAL\Cache\QueryCacheProfile(0, $this->TableVar);

        // Call Table Load event
        $this->tableLoad();
    }

    // Field Visibility
    public function getFieldVisibility($fldParm)
    {
        global $Security;
        return $this->$fldParm->Visible; // Returns original value
    }

    // Set left column class (must be predefined col-*-* classes of Bootstrap grid system)
    public function setLeftColumnClass($class)
    {
        if (preg_match('/^col\-(\w+)\-(\d+)$/', $class, $match)) {
            $this->LeftColumnClass = $class . " col-form-label ew-label";
            $this->RightColumnClass = "col-" . $match[1] . "-" . strval(12 - (int)$match[2]);
            $this->OffsetColumnClass = $this->RightColumnClass . " " . str_replace("col-", "offset-", $class);
            $this->TableLeftColumnClass = preg_replace('/^col-\w+-(\d+)$/', "w-col-$1", $class); // Change to w-col-*
        }
    }

    // Single column sort
    public function updateSort(&$fld)
    {
        if ($this->CurrentOrder == $fld->Name) {
            $sortField = $fld->Expression;
            $lastSort = $fld->getSort();
            if (in_array($this->CurrentOrderType, ["ASC", "DESC", "NO"])) {
                $curSort = $this->CurrentOrderType;
            } else {
                $curSort = $lastSort;
            }
            $orderBy = in_array($curSort, ["ASC", "DESC"]) ? $sortField . " " . $curSort : "";
            $this->setSessionOrderBy($orderBy); // Save to Session
        }
    }

    // Update field sort
    public function updateFieldSort()
    {
        $orderBy = $this->getSessionOrderBy(); // Get ORDER BY from Session
        $flds = GetSortFields($orderBy);
        foreach ($this->Fields as $field) {
            $fldSort = "";
            foreach ($flds as $fld) {
                if ($fld[0] == $field->Expression || $fld[0] == $field->VirtualExpression) {
                    $fldSort = $fld[1];
                }
            }
            $field->setSort($fldSort);
        }
    }

    // Current master table name
    public function getCurrentMasterTable()
    {
        return Session(PROJECT_NAME . "_" . $this->TableVar . "_" . Config("TABLE_MASTER_TABLE"));
    }

    public function setCurrentMasterTable($v)
    {
        $_SESSION[PROJECT_NAME . "_" . $this->TableVar . "_" . Config("TABLE_MASTER_TABLE")] = $v;
    }

    // Get master WHERE clause from session values
    public function getMasterFilterFromSession()
    {
        // Master filter
        $masterFilter = "";
        if ($this->getCurrentMasterTable() == "masterproject") {
            $masterTable = Container("masterproject");
            if ($this->_ProjectID->getSessionValue() != "") {
                $masterFilter .= "" . GetKeyFilter($masterTable->_ProjectID, $this->_ProjectID->getSessionValue(), $masterTable->_ProjectID->DataType, $masterTable->Dbid);
            } else {
                return "";
            }
        }
        return $masterFilter;
    }

    // Get detail WHERE clause from session values
    public function getDetailFilterFromSession()
    {
        // Detail filter
        $detailFilter = "";
        if ($this->getCurrentMasterTable() == "masterproject") {
            $masterTable = Container("masterproject");
            if ($this->_ProjectID->getSessionValue() != "") {
                $detailFilter .= "" . GetKeyFilter($this->_ProjectID, $this->_ProjectID->getSessionValue(), $masterTable->_ProjectID->DataType, $this->Dbid);
            } else {
                return "";
            }
        }
        return $detailFilter;
    }

    /**
     * Get master filter
     *
     * @param object $masterTable Master Table
     * @param array $keys Detail Keys
     * @return mixed NULL is returned if all keys are empty, Empty string is returned if some keys are empty and is required
     */
    public function getMasterFilter($masterTable, $keys)
    {
        $validKeys = true;
        switch ($masterTable->TableVar) {
            case "masterproject":
                $key = $keys["ProjectID"] ?? "";
                if (EmptyValue($key)) {
                    if ($masterTable->_ProjectID->Required) { // Required field and empty value
                        return ""; // Return empty filter
                    }
                    $validKeys = false;
                } elseif (!$validKeys) { // Already has empty key
                    return ""; // Return empty filter
                }
                if ($validKeys) {
                    return GetKeyFilter($masterTable->_ProjectID, $keys["ProjectID"], $this->_ProjectID->DataType, $this->Dbid);
                }
                break;
        }
        return null; // All null values and no required fields
    }

    // Get detail filter
    public function getDetailFilter($masterTable)
    {
        switch ($masterTable->TableVar) {
            case "masterproject":
                return GetKeyFilter($this->_ProjectID, $masterTable->_ProjectID->DbValue, $masterTable->_ProjectID->DataType, $masterTable->Dbid);
        }
        return "";
    }

    // Render X Axis for chart
    public function renderChartXAxis($chartVar, $chartRow)
    {
        return $chartRow;
    }

    // Get FROM clause
    public function getSqlFrom()
    {
        return ($this->SqlFrom != "") ? $this->SqlFrom : "projectpaymentprocess";
    }

    // Get FROM clause (for backward compatibility)
    public function sqlFrom()
    {
        return $this->getSqlFrom();
    }

    // Set FROM clause
    public function setSqlFrom($v)
    {
        $this->SqlFrom = $v;
    }

    // Get SELECT clause
    public function getSqlSelect() // Select
    {
        return $this->SqlSelect ?? $this->getQueryBuilder()->select($this->sqlSelectFields());
    }

    // Get list of fields
    private function sqlSelectFields()
    {
        $useFieldNames = false;
        $fieldNames = [];
        $platform = $this->getConnection()->getDatabasePlatform();
        foreach ($this->Fields as $field) {
            $expr = $field->Expression;
            $customExpr = $field->CustomDataType?->convertToPHPValueSQL($expr, $platform) ?? $expr;
            if ($customExpr != $expr) {
                $fieldNames[] = $customExpr . " AS " . QuotedName($field->Name, $this->Dbid);
                $useFieldNames = true;
            } else {
                $fieldNames[] = $expr;
            }
        }
        return $useFieldNames ? implode(", ", $fieldNames) : "*";
    }

    // Get SELECT clause (for backward compatibility)
    public function sqlSelect()
    {
        return $this->getSqlSelect();
    }

    // Set SELECT clause
    public function setSqlSelect($v)
    {
        $this->SqlSelect = $v;
    }

    // Get WHERE clause
    public function getSqlWhere()
    {
        $where = ($this->SqlWhere != "") ? $this->SqlWhere : "";
        $this->DefaultFilter = "";
        AddFilter($where, $this->DefaultFilter);
        return $where;
    }

    // Get WHERE clause (for backward compatibility)
    public function sqlWhere()
    {
        return $this->getSqlWhere();
    }

    // Set WHERE clause
    public function setSqlWhere($v)
    {
        $this->SqlWhere = $v;
    }

    // Get GROUP BY clause
    public function getSqlGroupBy()
    {
        return $this->SqlGroupBy != "" ? $this->SqlGroupBy : "";
    }

    // Get GROUP BY clause (for backward compatibility)
    public function sqlGroupBy()
    {
        return $this->getSqlGroupBy();
    }

    // set GROUP BY clause
    public function setSqlGroupBy($v)
    {
        $this->SqlGroupBy = $v;
    }

    // Get HAVING clause
    public function getSqlHaving() // Having
    {
        return ($this->SqlHaving != "") ? $this->SqlHaving : "";
    }

    // Get HAVING clause (for backward compatibility)
    public function sqlHaving()
    {
        return $this->getSqlHaving();
    }

    // Set HAVING clause
    public function setSqlHaving($v)
    {
        $this->SqlHaving = $v;
    }

    // Get ORDER BY clause
    public function getSqlOrderBy()
    {
        return ($this->SqlOrderBy != "") ? $this->SqlOrderBy : "";
    }

    // Get ORDER BY clause (for backward compatibility)
    public function sqlOrderBy()
    {
        return $this->getSqlOrderBy();
    }

    // set ORDER BY clause
    public function setSqlOrderBy($v)
    {
        $this->SqlOrderBy = $v;
    }

    // Apply User ID filters
    public function applyUserIDFilters($filter, $id = "")
    {
        return $filter;
    }

    // Check if User ID security allows view all
    public function userIDAllow($id = "")
    {
        $allow = $this->UserIDAllowSecurity;
        switch ($id) {
            case "add":
            case "copy":
            case "gridadd":
            case "register":
            case "addopt":
                return ($allow & Allow::ADD->value) == Allow::ADD->value;
            case "edit":
            case "gridedit":
            case "update":
            case "changepassword":
            case "resetpassword":
                return ($allow & Allow::EDIT->value) == Allow::EDIT->value;
            case "delete":
                return ($allow & Allow::DELETE->value) == Allow::DELETE->value;
            case "view":
                return ($allow & Allow::VIEW->value) == Allow::VIEW->value;
            case "search":
                return ($allow & Allow::SEARCH->value) == Allow::SEARCH->value;
            case "lookup":
                return ($allow & Allow::LOOKUP->value) == Allow::LOOKUP->value;
            default:
                return ($allow & Allow::LIST->value) == Allow::LIST->value;
        }
    }

    /**
     * Get record count
     *
     * @param string|QueryBuilder $sql SQL or QueryBuilder
     * @param mixed $c Connection
     * @return int
     */
    public function getRecordCount($sql, $c = null)
    {
        $cnt = -1;
        $sqlwrk = $sql instanceof QueryBuilder // Query builder
            ? (clone $sql)->resetQueryPart("orderBy")->getSQL()
            : $sql;
        $pattern = '/^SELECT\s([\s\S]+)\sFROM\s/i';
        // Skip Custom View / SubQuery / SELECT DISTINCT / ORDER BY
        if (
            in_array($this->TableType, ["TABLE", "VIEW", "LINKTABLE"]) &&
            preg_match($pattern, $sqlwrk) &&
            !preg_match('/\(\s*(SELECT[^)]+)\)/i', $sqlwrk) &&
            !preg_match('/^\s*SELECT\s+DISTINCT\s+/i', $sqlwrk) &&
            !preg_match('/\s+ORDER\s+BY\s+/i', $sqlwrk)
        ) {
            $sqlcnt = "SELECT COUNT(*) FROM " . preg_replace($pattern, "", $sqlwrk);
        } else {
            $sqlcnt = "SELECT COUNT(*) FROM (" . $sqlwrk . ") COUNT_TABLE";
        }
        $conn = $c ?? $this->getConnection();
        $cnt = $conn->fetchOne($sqlcnt);
        if ($cnt !== false) {
            return (int)$cnt;
        }
        // Unable to get count by SELECT COUNT(*), execute the SQL to get record count directly
        $result = $conn->executeQuery($sqlwrk);
        $cnt = $result->rowCount();
        if ($cnt == 0) { // Unable to get record count, count directly
            while ($result->fetch()) {
                $cnt++;
            }
        }
        return $cnt;
    }

    // Get SQL
    public function getSql($where, $orderBy = "")
    {
        return $this->getSqlAsQueryBuilder($where, $orderBy)->getSQL();
    }

    // Get QueryBuilder
    public function getSqlAsQueryBuilder($where, $orderBy = "")
    {
        return $this->buildSelectSql(
            $this->getSqlSelect(),
            $this->getSqlFrom(),
            $this->getSqlWhere(),
            $this->getSqlGroupBy(),
            $this->getSqlHaving(),
            $this->getSqlOrderBy(),
            $where,
            $orderBy
        );
    }

    // Table SQL
    public function getCurrentSql()
    {
        $filter = $this->CurrentFilter;
        $filter = $this->applyUserIDFilters($filter);
        $sort = $this->getSessionOrderBy();
        return $this->getSql($filter, $sort);
    }

    /**
     * Table SQL with List page filter
     *
     * @return QueryBuilder
     */
    public function getListSql()
    {
        $filter = $this->UseSessionForListSql ? $this->getSessionWhere() : "";
        AddFilter($filter, $this->CurrentFilter);
        $filter = $this->applyUserIDFilters($filter);
        $this->recordsetSelecting($filter);
        $select = $this->getSqlSelect();
        $from = $this->getSqlFrom();
        $sort = $this->UseSessionForListSql ? $this->getSessionOrderBy() : "";
        $this->Sort = $sort;
        return $this->buildSelectSql(
            $select,
            $from,
            $this->getSqlWhere(),
            $this->getSqlGroupBy(),
            $this->getSqlHaving(),
            $this->getSqlOrderBy(),
            $filter,
            $sort
        );
    }

    // Get ORDER BY clause
    public function getOrderBy()
    {
        $orderBy = $this->getSqlOrderBy();
        $sort = $this->getSessionOrderBy();
        if ($orderBy != "" && $sort != "") {
            $orderBy .= ", " . $sort;
        } elseif ($sort != "") {
            $orderBy = $sort;
        }
        return $orderBy;
    }

    // Get record count based on filter (for detail record count in master table pages)
    public function loadRecordCount($filter)
    {
        $origFilter = $this->CurrentFilter;
        $this->CurrentFilter = $filter;
        $this->recordsetSelecting($this->CurrentFilter);
        $isCustomView = $this->TableType == "CUSTOMVIEW";
        $select = $isCustomView ? $this->getSqlSelect() : $this->getQueryBuilder()->select("*");
        $groupBy = $isCustomView ? $this->getSqlGroupBy() : "";
        $having = $isCustomView ? $this->getSqlHaving() : "";
        $sql = $this->buildSelectSql($select, $this->getSqlFrom(), $this->getSqlWhere(), $groupBy, $having, "", $this->CurrentFilter, "");
        $cnt = $this->getRecordCount($sql);
        $this->CurrentFilter = $origFilter;
        return $cnt;
    }

    // Get record count (for current List page)
    public function listRecordCount()
    {
        $filter = $this->getSessionWhere();
        AddFilter($filter, $this->CurrentFilter);
        $filter = $this->applyUserIDFilters($filter);
        $this->recordsetSelecting($filter);
        $isCustomView = $this->TableType == "CUSTOMVIEW";
        $select = $isCustomView ? $this->getSqlSelect() : $this->getQueryBuilder()->select("*");
        $groupBy = $isCustomView ? $this->getSqlGroupBy() : "";
        $having = $isCustomView ? $this->getSqlHaving() : "";
        $sql = $this->buildSelectSql($select, $this->getSqlFrom(), $this->getSqlWhere(), $groupBy, $having, "", $filter, "");
        $cnt = $this->getRecordCount($sql);
        return $cnt;
    }

    /**
     * INSERT statement
     *
     * @param mixed $rs
     * @return QueryBuilder
     */
    public function insertSql(&$rs)
    {
        $queryBuilder = $this->getQueryBuilder();
        $queryBuilder->insert($this->UpdateTable);
        $platform = $this->getConnection()->getDatabasePlatform();
        foreach ($rs as $name => $value) {
            if (!isset($this->Fields[$name]) || $this->Fields[$name]->IsCustom) {
                continue;
            }
            $field = $this->Fields[$name];
            $parm = $queryBuilder->createPositionalParameter($value, $field->getParameterType());
            $parm = $field->CustomDataType?->convertToDatabaseValueSQL($parm, $platform) ?? $parm; // Convert database SQL
            $queryBuilder->setValue($field->Expression, $parm);
        }
        return $queryBuilder;
    }

    // Insert
    public function insert(&$rs)
    {
        $conn = $this->getConnection();
        try {
            $queryBuilder = $this->insertSql($rs);
            $result = $queryBuilder->executeStatement();
            $this->DbErrorMessage = "";
        } catch (\Exception $e) {
            $result = false;
            $this->DbErrorMessage = $e->getMessage();
        }
        if ($result) {
            $this->PaymentProcessID->setDbValue($conn->lastInsertId());
            $rs['PaymentProcessID'] = $this->PaymentProcessID->DbValue;
        }
        return $result;
    }

    /**
     * UPDATE statement
     *
     * @param array $rs Data to be updated
     * @param string|array $where WHERE clause
     * @param string $curfilter Filter
     * @return QueryBuilder
     */
    public function updateSql(&$rs, $where = "", $curfilter = true)
    {
        $queryBuilder = $this->getQueryBuilder();
        $queryBuilder->update($this->UpdateTable);
        $platform = $this->getConnection()->getDatabasePlatform();
        foreach ($rs as $name => $value) {
            if (!isset($this->Fields[$name]) || $this->Fields[$name]->IsCustom || $this->Fields[$name]->IsAutoIncrement) {
                continue;
            }
            $field = $this->Fields[$name];
            $parm = $queryBuilder->createPositionalParameter($value, $field->getParameterType());
            $parm = $field->CustomDataType?->convertToDatabaseValueSQL($parm, $platform) ?? $parm; // Convert database SQL
            $queryBuilder->set($field->Expression, $parm);
        }
        $filter = $curfilter ? $this->CurrentFilter : "";
        if (is_array($where)) {
            $where = $this->arrayToFilter($where);
        }
        AddFilter($filter, $where);
        if ($filter != "") {
            $queryBuilder->where($filter);
        }
        return $queryBuilder;
    }

    // Update
    public function update(&$rs, $where = "", $rsold = null, $curfilter = true)
    {
        // If no field is updated, execute may return 0. Treat as success
        try {
            $success = $this->updateSql($rs, $where, $curfilter)->executeStatement();
            $success = $success > 0 ? $success : true;
            $this->DbErrorMessage = "";
        } catch (\Exception $e) {
            $success = false;
            $this->DbErrorMessage = $e->getMessage();
        }

        // Return auto increment field
        if ($success) {
            if (!isset($rs['PaymentProcessID']) && !EmptyValue($this->PaymentProcessID->CurrentValue)) {
                $rs['PaymentProcessID'] = $this->PaymentProcessID->CurrentValue;
            }
        }
        return $success;
    }

    /**
     * DELETE statement
     *
     * @param array $rs Key values
     * @param string|array $where WHERE clause
     * @param string $curfilter Filter
     * @return QueryBuilder
     */
    public function deleteSql(&$rs, $where = "", $curfilter = true)
    {
        $queryBuilder = $this->getQueryBuilder();
        $queryBuilder->delete($this->UpdateTable);
        if (is_array($where)) {
            $where = $this->arrayToFilter($where);
        }
        if ($rs) {
            if (array_key_exists('PaymentProcessID', $rs)) {
                AddFilter($where, QuotedName('PaymentProcessID', $this->Dbid) . '=' . QuotedValue($rs['PaymentProcessID'], $this->PaymentProcessID->DataType, $this->Dbid));
            }
        }
        $filter = $curfilter ? $this->CurrentFilter : "";
        AddFilter($filter, $where);
        return $queryBuilder->where($filter != "" ? $filter : "0=1");
    }

    // Delete
    public function delete(&$rs, $where = "", $curfilter = false)
    {
        $success = true;
        if ($success) {
            try {
                $success = $this->deleteSql($rs, $where, $curfilter)->executeStatement();
                $this->DbErrorMessage = "";
            } catch (\Exception $e) {
                $success = false;
                $this->DbErrorMessage = $e->getMessage();
            }
        }
        return $success;
    }

    // Load DbValue from result set or array
    protected function loadDbValues($row)
    {
        if (!is_array($row)) {
            return;
        }
        $this->PaymentProcessID->DbValue = $row['PaymentProcessID'];
        $this->DocumentName->DbValue = $row['DocumentName'];
        $this->FileName->Upload->DbValue = $row['FileName'];
        $this->Completed->DbValue = $row['Completed'];
        $this->DateDelivered->DbValue = $row['DateDelivered'];
        $this->PICClient->DbValue = $row['PICClient'];
        $this->Remark->DbValue = $row['Remark'];
        $this->templateid->DbValue = $row['templateid'];
        $this->_ProjectID->DbValue = $row['ProjectID'];
        $this->Created_Date->DbValue = $row['Created_Date'];
        $this->Created_By->DbValue = $row['Created_By'];
        $this->Last_Updated_Date->DbValue = $row['Last_Updated_Date'];
        $this->Last_Updated_By->DbValue = $row['Last_Updated_By'];
    }

    // Delete uploaded files
    public function deleteUploadedFiles($row)
    {
        $this->loadDbValues($row);
        $this->FileName->OldUploadPath = $this->FileName->getUploadPath(); // PHP
        $oldFiles = EmptyValue($row['FileName']) ? [] : [$row['FileName']];
        foreach ($oldFiles as $oldFile) {
            if (file_exists($this->FileName->oldPhysicalUploadPath() . $oldFile)) {
                @unlink($this->FileName->oldPhysicalUploadPath() . $oldFile);
            }
        }
    }

    // Record filter WHERE clause
    protected function sqlKeyFilter()
    {
        return "`PaymentProcessID` = @PaymentProcessID@";
    }

    // Get Key
    public function getKey($current = false, $keySeparator = null)
    {
        $keys = [];
        $val = $current ? $this->PaymentProcessID->CurrentValue : $this->PaymentProcessID->OldValue;
        if (EmptyValue($val)) {
            return "";
        } else {
            $keys[] = $val;
        }
        $keySeparator ??= Config("COMPOSITE_KEY_SEPARATOR");
        return implode($keySeparator, $keys);
    }

    // Set Key
    public function setKey($key, $current = false, $keySeparator = null)
    {
        $keySeparator ??= Config("COMPOSITE_KEY_SEPARATOR");
        $this->OldKey = strval($key);
        $keys = explode($keySeparator, $this->OldKey);
        if (count($keys) == 1) {
            if ($current) {
                $this->PaymentProcessID->CurrentValue = $keys[0];
            } else {
                $this->PaymentProcessID->OldValue = $keys[0];
            }
        }
    }

    // Get record filter
    public function getRecordFilter($row = null, $current = false)
    {
        $keyFilter = $this->sqlKeyFilter();
        if (is_array($row)) {
            $val = array_key_exists('PaymentProcessID', $row) ? $row['PaymentProcessID'] : null;
        } else {
            $val = !EmptyValue($this->PaymentProcessID->OldValue) && !$current ? $this->PaymentProcessID->OldValue : $this->PaymentProcessID->CurrentValue;
        }
        if (!is_numeric($val)) {
            return "0=1"; // Invalid key
        }
        if ($val === null) {
            return "0=1"; // Invalid key
        } else {
            $keyFilter = str_replace("@PaymentProcessID@", AdjustSql($val, $this->Dbid), $keyFilter); // Replace key value
        }
        return $keyFilter;
    }

    // Return page URL
    public function getReturnUrl()
    {
        $referUrl = ReferUrl();
        $referPageName = ReferPageName();
        $name = PROJECT_NAME . "_" . $this->TableVar . "_" . Config("TABLE_RETURN_URL");
        // Get referer URL automatically
        if ($referUrl != "" && $referPageName != CurrentPageName() && $referPageName != "login") { // Referer not same page or login page
            $_SESSION[$name] = $referUrl; // Save to Session
        }
        return $_SESSION[$name] ?? GetUrl("projectpaymentprocesslist");
    }

    // Set return page URL
    public function setReturnUrl($v)
    {
        $_SESSION[PROJECT_NAME . "_" . $this->TableVar . "_" . Config("TABLE_RETURN_URL")] = $v;
    }

    // Get modal caption
    public function getModalCaption($pageName)
    {
        global $Language;
        return match ($pageName) {
            "projectpaymentprocessview" => $Language->phrase("View"),
            "projectpaymentprocessedit" => $Language->phrase("Edit"),
            "projectpaymentprocessadd" => $Language->phrase("Add"),
            default => ""
        };
    }

    // Default route URL
    public function getDefaultRouteUrl()
    {
        return "projectpaymentprocesslist";
    }

    // API page name
    public function getApiPageName($action)
    {
        return match (strtolower($action)) {
            Config("API_VIEW_ACTION") => "ProjectpaymentprocessView",
            Config("API_ADD_ACTION") => "ProjectpaymentprocessAdd",
            Config("API_EDIT_ACTION") => "ProjectpaymentprocessEdit",
            Config("API_DELETE_ACTION") => "ProjectpaymentprocessDelete",
            Config("API_LIST_ACTION") => "ProjectpaymentprocessList",
            default => ""
        };
    }

    // Current URL
    public function getCurrentUrl($parm = "")
    {
        $url = CurrentPageUrl(false);
        if ($parm != "") {
            $url = $this->keyUrl($url, $parm);
        } else {
            $url = $this->keyUrl($url, Config("TABLE_SHOW_DETAIL") . "=");
        }
        return $this->addMasterUrl($url);
    }

    // List URL
    public function getListUrl()
    {
        return "projectpaymentprocesslist";
    }

    // View URL
    public function getViewUrl($parm = "")
    {
        if ($parm != "") {
            $url = $this->keyUrl("projectpaymentprocessview", $parm);
        } else {
            $url = $this->keyUrl("projectpaymentprocessview", Config("TABLE_SHOW_DETAIL") . "=");
        }
        return $this->addMasterUrl($url);
    }

    // Add URL
    public function getAddUrl($parm = "")
    {
        if ($parm != "") {
            $url = "projectpaymentprocessadd?" . $parm;
        } else {
            $url = "projectpaymentprocessadd";
        }
        return $this->addMasterUrl($url);
    }

    // Edit URL
    public function getEditUrl($parm = "")
    {
        $url = $this->keyUrl("projectpaymentprocessedit", $parm);
        return $this->addMasterUrl($url);
    }

    // Inline edit URL
    public function getInlineEditUrl()
    {
        $url = $this->keyUrl("projectpaymentprocesslist", "action=edit");
        return $this->addMasterUrl($url);
    }

    // Copy URL
    public function getCopyUrl($parm = "")
    {
        $url = $this->keyUrl("projectpaymentprocessadd", $parm);
        return $this->addMasterUrl($url);
    }

    // Inline copy URL
    public function getInlineCopyUrl()
    {
        $url = $this->keyUrl("projectpaymentprocesslist", "action=copy");
        return $this->addMasterUrl($url);
    }

    // Delete URL
    public function getDeleteUrl($parm = "")
    {
        if ($this->UseAjaxActions && ConvertToBool(Param("infinitescroll")) && CurrentPageID() == "list") {
            return $this->keyUrl(GetApiUrl(Config("API_DELETE_ACTION") . "/" . $this->TableVar));
        } else {
            return $this->keyUrl("projectpaymentprocessdelete", $parm);
        }
    }

    // Add master url
    public function addMasterUrl($url)
    {
        if ($this->getCurrentMasterTable() == "masterproject" && !ContainsString($url, Config("TABLE_SHOW_MASTER") . "=")) {
            $url .= (ContainsString($url, "?") ? "&" : "?") . Config("TABLE_SHOW_MASTER") . "=" . $this->getCurrentMasterTable();
            $url .= "&" . GetForeignKeyUrl("fk__ProjectID", $this->_ProjectID->getSessionValue()); // Use Session Value
        }
        return $url;
    }

    public function keyToJson($htmlEncode = false)
    {
        $json = "";
        $json .= "\"PaymentProcessID\":" . VarToJson($this->PaymentProcessID->CurrentValue, "number");
        $json = "{" . $json . "}";
        if ($htmlEncode) {
            $json = HtmlEncode($json);
        }
        return $json;
    }

    // Add key value to URL
    public function keyUrl($url, $parm = "")
    {
        if ($this->PaymentProcessID->CurrentValue !== null) {
            $url .= "/" . $this->encodeKeyValue($this->PaymentProcessID->CurrentValue);
        } else {
            return "javascript:ew.alert(ew.language.phrase('InvalidRecord'));";
        }
        if ($parm != "") {
            $url .= "?" . $parm;
        }
        return $url;
    }

    // Render sort
    public function renderFieldHeader($fld)
    {
        global $Security, $Language;
        $sortUrl = "";
        $attrs = "";
        if ($this->PageID != "grid" && $fld->Sortable) {
            $sortUrl = $this->sortUrl($fld);
            $attrs = ' role="button" data-ew-action="sort" data-ajax="' . ($this->UseAjaxActions ? "true" : "false") . '" data-sort-url="' . $sortUrl . '" data-sort-type="1"';
            if ($this->ContextClass) { // Add context
                $attrs .= ' data-context="' . HtmlEncode($this->ContextClass) . '"';
            }
        }
        $html = '<div class="ew-table-header-caption"' . $attrs . '>' . $fld->caption() . '</div>';
        if ($sortUrl) {
            $html .= '<div class="ew-table-header-sort">' . $fld->getSortIcon() . '</div>';
        }
        if ($this->PageID != "grid" && !$this->isExport() && $fld->UseFilter && $Security->canSearch()) {
            $html .= '<div class="ew-filter-dropdown-btn" data-ew-action="filter" data-table="' . $fld->TableVar . '" data-field="' . $fld->FieldVar .
                '"><div class="ew-table-header-filter" role="button" aria-haspopup="true">' . $Language->phrase("Filter") .
                (is_array($fld->EditValue) ? str_replace("%c", count($fld->EditValue), $Language->phrase("FilterCount")) : '') .
                '</div></div>';
        }
        $html = '<div class="ew-table-header-btn">' . $html . '</div>';
        if ($this->UseCustomTemplate) {
            $scriptId = str_replace("{id}", $fld->TableVar . "_" . $fld->Param, "tpc_{id}");
            $html = '<template id="' . $scriptId . '">' . $html . '</template>';
        }
        return $html;
    }

    // Sort URL
    public function sortUrl($fld)
    {
        global $DashboardReport;
        if (
            $this->CurrentAction || $this->isExport() ||
            in_array($fld->Type, [128, 204, 205])
        ) { // Unsortable data type
                return "";
        } elseif ($fld->Sortable) {
            $urlParm = "order=" . urlencode($fld->Name) . "&amp;ordertype=" . $fld->getNextSort();
            if ($DashboardReport) {
                $urlParm .= "&amp;" . Config("PAGE_DASHBOARD") . "=" . $DashboardReport;
            }
            return $this->addMasterUrl($this->CurrentPageName . "?" . $urlParm);
        } else {
            return "";
        }
    }

    // Get record keys from Post/Get/Session
    public function getRecordKeys()
    {
        $arKeys = [];
        $arKey = [];
        if (Param("key_m") !== null) {
            $arKeys = Param("key_m");
            $cnt = count($arKeys);
        } else {
            $isApi = IsApi();
            $keyValues = $isApi
                ? (Route(0) == "export"
                    ? array_map(fn ($i) => Route($i + 3), range(0, 0))  // Export API
                    : array_map(fn ($i) => Route($i + 2), range(0, 0))) // Other API
                : []; // Non-API
            if (($keyValue = Param("PaymentProcessID") ?? Route("PaymentProcessID")) !== null) {
                $arKeys[] = $keyValue;
            } elseif ($isApi && (($keyValue = Key(0) ?? $keyValues[0] ?? null) !== null)) {
                $arKeys[] = $keyValue;
            } else {
                $arKeys = null; // Do not setup
            }
        }
        // Check keys
        $ar = [];
        if (is_array($arKeys)) {
            foreach ($arKeys as $key) {
                if (!is_numeric($key)) {
                    continue;
                }
                $ar[] = $key;
            }
        }
        return $ar;
    }

    // Get filter from records
    public function getFilterFromRecords($rows)
    {
        $keyFilter = "";
        foreach ($rows as $row) {
            if ($keyFilter != "") {
                $keyFilter .= " OR ";
            }
            $keyFilter .= "(" . $this->getRecordFilter($row) . ")";
        }
        return $keyFilter;
    }

    // Get filter from record keys
    public function getFilterFromRecordKeys($setCurrent = true)
    {
        $arKeys = $this->getRecordKeys();
        $keyFilter = "";
        foreach ($arKeys as $key) {
            if ($keyFilter != "") {
                $keyFilter .= " OR ";
            }
            if ($setCurrent) {
                $this->PaymentProcessID->CurrentValue = $key;
            } else {
                $this->PaymentProcessID->OldValue = $key;
            }
            $keyFilter .= "(" . $this->getRecordFilter() . ")";
        }
        return $keyFilter;
    }

    // Load result set based on filter/sort
    public function loadRs($filter, $sort = "")
    {
        $sql = $this->getSql($filter, $sort); // Set up filter (WHERE Clause) / sort (ORDER BY Clause)
        $conn = $this->getConnection();
        return $conn->executeQuery($sql);
    }

    // Load row values from record
    public function loadListRowValues(&$rs)
    {
        if (is_array($rs)) {
            $row = $rs;
        } elseif ($rs && property_exists($rs, "fields")) { // Recordset
            $row = $rs->fields;
        } else {
            return;
        }
        $this->PaymentProcessID->setDbValue($row['PaymentProcessID']);
        $this->DocumentName->setDbValue($row['DocumentName']);
        $this->FileName->Upload->DbValue = $row['FileName'];
        $this->Completed->setDbValue($row['Completed']);
        $this->DateDelivered->setDbValue($row['DateDelivered']);
        $this->PICClient->setDbValue($row['PICClient']);
        $this->Remark->setDbValue($row['Remark']);
        $this->templateid->setDbValue($row['templateid']);
        $this->_ProjectID->setDbValue($row['ProjectID']);
        $this->Created_Date->setDbValue($row['Created_Date']);
        $this->Created_By->setDbValue($row['Created_By']);
        $this->Last_Updated_Date->setDbValue($row['Last_Updated_Date']);
        $this->Last_Updated_By->setDbValue($row['Last_Updated_By']);
    }

    // Render list content
    public function renderListContent($filter)
    {
        global $Response;
        $listPage = "ProjectpaymentprocessList";
        $listClass = PROJECT_NAMESPACE . $listPage;
        $page = new $listClass();
        $page->loadRecordsetFromFilter($filter);
        $view = Container("app.view");
        $template = $listPage . ".php"; // View
        $GLOBALS["Title"] ??= $page->Title; // Title
        try {
            $Response = $view->render($Response, $template, $GLOBALS);
        } finally {
            $page->terminate(); // Terminate page and clean up
        }
    }

    // Render list row values
    public function renderListRow()
    {
        global $Security, $CurrentLanguage, $Language;

        // Call Row Rendering event
        $this->rowRendering();

        // Common render codes

        // PaymentProcessID

        // DocumentName
        $this->DocumentName->CellCssStyle = "white-space: nowrap;";

        // FileName
        $this->FileName->CellCssStyle = "white-space: nowrap;";

        // Completed
        $this->Completed->CellCssStyle = "white-space: nowrap;";

        // DateDelivered
        $this->DateDelivered->CellCssStyle = "white-space: nowrap;";

        // PICClient
        $this->PICClient->CellCssStyle = "white-space: nowrap;";

        // Remark

        // templateid
        $this->templateid->CellCssStyle = "white-space: nowrap;";

        // ProjectID
        $this->_ProjectID->CellCssStyle = "white-space: nowrap;";

        // Created_Date
        $this->Created_Date->CellCssStyle = "white-space: nowrap;";

        // Created_By
        $this->Created_By->CellCssStyle = "white-space: nowrap;";

        // Last_Updated_Date
        $this->Last_Updated_Date->CellCssStyle = "white-space: nowrap;";

        // Last_Updated_By
        $this->Last_Updated_By->CellCssStyle = "white-space: nowrap;";

        // PaymentProcessID
        $this->PaymentProcessID->ViewValue = $this->PaymentProcessID->CurrentValue;

        // DocumentName
        $this->DocumentName->ViewValue = $this->DocumentName->CurrentValue;

        // FileName
        $this->FileName->UploadPath = $this->FileName->getUploadPath(); // PHP
        if (!EmptyValue($this->FileName->Upload->DbValue)) {
            $this->FileName->ViewValue = $this->FileName->Upload->DbValue;
        } else {
            $this->FileName->ViewValue = "";
        }

        // Completed
        if (ConvertToBool($this->Completed->CurrentValue)) {
            $this->Completed->ViewValue = $this->Completed->tagCaption(1) != "" ? $this->Completed->tagCaption(1) : "Yes";
        } else {
            $this->Completed->ViewValue = $this->Completed->tagCaption(2) != "" ? $this->Completed->tagCaption(2) : "No";
        }
        $this->Completed->CellCssStyle .= "text-align: center;";

        // DateDelivered
        $this->DateDelivered->ViewValue = $this->DateDelivered->CurrentValue;
        $this->DateDelivered->ViewValue = FormatDateTime($this->DateDelivered->ViewValue, $this->DateDelivered->formatPattern());

        // PICClient
        $curVal = strval($this->PICClient->CurrentValue);
        if ($curVal != "") {
            $this->PICClient->ViewValue = $this->PICClient->lookupCacheOption($curVal);
            if ($this->PICClient->ViewValue === null) { // Lookup from database
                $filterWrk = SearchFilter($this->PICClient->Lookup->getTable()->Fields["EmployeeID"]->searchExpression(), "=", $curVal, $this->PICClient->Lookup->getTable()->Fields["EmployeeID"]->searchDataType(), "");
                $sqlWrk = $this->PICClient->Lookup->getSql(false, $filterWrk, '', $this, true, true);
                $conn = Conn();
                $config = $conn->getConfiguration();
                $config->setResultCache($this->Cache);
                $rswrk = $conn->executeCacheQuery($sqlWrk, [], [], $this->CacheProfile)->fetchAll();
                $ari = count($rswrk);
                if ($ari > 0) { // Lookup values found
                    $arwrk = $this->PICClient->Lookup->renderViewRow($rswrk[0]);
                    $this->PICClient->ViewValue = $this->PICClient->displayValue($arwrk);
                } else {
                    $this->PICClient->ViewValue = FormatNumber($this->PICClient->CurrentValue, $this->PICClient->formatPattern());
                }
            }
        } else {
            $this->PICClient->ViewValue = null;
        }

        // Remark
        $this->Remark->ViewValue = $this->Remark->CurrentValue;

        // templateid
        $this->templateid->ViewValue = $this->templateid->CurrentValue;
        $this->templateid->ViewValue = FormatNumber($this->templateid->ViewValue, $this->templateid->formatPattern());

        // ProjectID
        $curVal = strval($this->_ProjectID->CurrentValue);
        if ($curVal != "") {
            $this->_ProjectID->ViewValue = $this->_ProjectID->lookupCacheOption($curVal);
            if ($this->_ProjectID->ViewValue === null) { // Lookup from database
                $filterWrk = SearchFilter($this->_ProjectID->Lookup->getTable()->Fields["ProjectID"]->searchExpression(), "=", $curVal, $this->_ProjectID->Lookup->getTable()->Fields["ProjectID"]->searchDataType(), "");
                $sqlWrk = $this->_ProjectID->Lookup->getSql(false, $filterWrk, '', $this, true, true);
                $conn = Conn();
                $config = $conn->getConfiguration();
                $config->setResultCache($this->Cache);
                $rswrk = $conn->executeCacheQuery($sqlWrk, [], [], $this->CacheProfile)->fetchAll();
                $ari = count($rswrk);
                if ($ari > 0) { // Lookup values found
                    $arwrk = $this->_ProjectID->Lookup->renderViewRow($rswrk[0]);
                    $this->_ProjectID->ViewValue = $this->_ProjectID->displayValue($arwrk);
                } else {
                    $this->_ProjectID->ViewValue = FormatNumber($this->_ProjectID->CurrentValue, $this->_ProjectID->formatPattern());
                }
            }
        } else {
            $this->_ProjectID->ViewValue = null;
        }

        // Created_Date
        $this->Created_Date->ViewValue = $this->Created_Date->CurrentValue;
        $this->Created_Date->ViewValue = FormatDateTime($this->Created_Date->ViewValue, $this->Created_Date->formatPattern());

        // Created_By
        $this->Created_By->ViewValue = $this->Created_By->CurrentValue;

        // Last_Updated_Date
        $this->Last_Updated_Date->ViewValue = $this->Last_Updated_Date->CurrentValue;
        $this->Last_Updated_Date->ViewValue = FormatDateTime($this->Last_Updated_Date->ViewValue, $this->Last_Updated_Date->formatPattern());

        // Last_Updated_By
        $this->Last_Updated_By->ViewValue = $this->Last_Updated_By->CurrentValue;

        // PaymentProcessID
        $this->PaymentProcessID->HrefValue = "";
        $this->PaymentProcessID->TooltipValue = "";

        // DocumentName
        $this->DocumentName->HrefValue = "";
        $this->DocumentName->TooltipValue = "";

        // FileName
        $this->FileName->UploadPath = $this->FileName->getUploadPath(); // PHP
        if (!EmptyValue($this->FileName->Upload->DbValue)) {
            $this->FileName->HrefValue = GetFileUploadUrl($this->FileName, $this->FileName->htmlDecode($this->FileName->Upload->DbValue)); // Add prefix/suffix
            $this->FileName->LinkAttrs["target"] = "_blank"; // Add target
            if ($this->isExport()) {
                $this->FileName->HrefValue = FullUrl($this->FileName->HrefValue, "href");
            }
        } else {
            $this->FileName->HrefValue = "";
        }
        $this->FileName->ExportHrefValue = $this->FileName->UploadPath . $this->FileName->Upload->DbValue;
        $this->FileName->TooltipValue = "";

        // Completed
        $this->Completed->HrefValue = "";
        $this->Completed->TooltipValue = "";

        // DateDelivered
        $this->DateDelivered->HrefValue = "";
        $this->DateDelivered->TooltipValue = "";

        // PICClient
        $this->PICClient->HrefValue = "";
        $this->PICClient->TooltipValue = "";

        // Remark
        $this->Remark->HrefValue = "";
        $this->Remark->TooltipValue = "";

        // templateid
        $this->templateid->HrefValue = "";
        $this->templateid->TooltipValue = "";

        // ProjectID
        $this->_ProjectID->HrefValue = "";
        $this->_ProjectID->TooltipValue = "";

        // Created_Date
        $this->Created_Date->HrefValue = "";
        $this->Created_Date->TooltipValue = "";

        // Created_By
        $this->Created_By->HrefValue = "";
        $this->Created_By->TooltipValue = "";

        // Last_Updated_Date
        $this->Last_Updated_Date->HrefValue = "";
        $this->Last_Updated_Date->TooltipValue = "";

        // Last_Updated_By
        $this->Last_Updated_By->HrefValue = "";
        $this->Last_Updated_By->TooltipValue = "";

        // Call Row Rendered event
        $this->rowRendered();

        // Save data for Custom Template
        $this->Rows[] = $this->customTemplateFieldValues();
    }

    // Render edit row values
    public function renderEditRow()
    {
        global $Security, $CurrentLanguage, $Language;

        // Call Row Rendering event
        $this->rowRendering();

        // PaymentProcessID
        $this->PaymentProcessID->setupEditAttributes();
        $this->PaymentProcessID->EditValue = $this->PaymentProcessID->CurrentValue;

        // DocumentName
        $this->DocumentName->setupEditAttributes();
        if (!$this->DocumentName->Raw) {
            $this->DocumentName->CurrentValue = HtmlDecode($this->DocumentName->CurrentValue);
        }
        $this->DocumentName->EditValue = $this->DocumentName->CurrentValue;
        $this->DocumentName->PlaceHolder = RemoveHtml($this->DocumentName->caption());

        // FileName
        $this->FileName->setupEditAttributes();
        $this->FileName->UploadPath = $this->FileName->getUploadPath(); // PHP
        if (!EmptyValue($this->FileName->Upload->DbValue)) {
            $this->FileName->EditValue = $this->FileName->Upload->DbValue;
        } else {
            $this->FileName->EditValue = "";
        }
        if (!EmptyValue($this->FileName->CurrentValue)) {
            $this->FileName->Upload->FileName = $this->FileName->CurrentValue;
        }

        // Completed
        $this->Completed->EditValue = $this->Completed->options(false);
        $this->Completed->PlaceHolder = RemoveHtml($this->Completed->caption());

        // DateDelivered
        $this->DateDelivered->setupEditAttributes();
        $this->DateDelivered->EditValue = FormatDateTime($this->DateDelivered->CurrentValue, $this->DateDelivered->formatPattern());
        $this->DateDelivered->PlaceHolder = RemoveHtml($this->DateDelivered->caption());

        // PICClient
        $this->PICClient->setupEditAttributes();
        $this->PICClient->PlaceHolder = RemoveHtml($this->PICClient->caption());

        // Remark
        $this->Remark->setupEditAttributes();
        $this->Remark->EditValue = $this->Remark->CurrentValue;
        $this->Remark->PlaceHolder = RemoveHtml($this->Remark->caption());

        // templateid
        $this->templateid->setupEditAttributes();
        $this->templateid->CurrentValue = FormatNumber($this->templateid->CurrentValue, $this->templateid->formatPattern());
        if (strval($this->templateid->EditValue) != "" && is_numeric($this->templateid->EditValue)) {
            $this->templateid->EditValue = FormatNumber($this->templateid->EditValue, null);
        }

        // ProjectID
        $this->_ProjectID->setupEditAttributes();
        if ($this->_ProjectID->getSessionValue() != "") {
            $this->_ProjectID->CurrentValue = GetForeignKeyValue($this->_ProjectID->getSessionValue());
            $curVal = strval($this->_ProjectID->CurrentValue);
            if ($curVal != "") {
                $this->_ProjectID->ViewValue = $this->_ProjectID->lookupCacheOption($curVal);
                if ($this->_ProjectID->ViewValue === null) { // Lookup from database
                    $filterWrk = SearchFilter($this->_ProjectID->Lookup->getTable()->Fields["ProjectID"]->searchExpression(), "=", $curVal, $this->_ProjectID->Lookup->getTable()->Fields["ProjectID"]->searchDataType(), "");
                    $sqlWrk = $this->_ProjectID->Lookup->getSql(false, $filterWrk, '', $this, true, true);
                    $conn = Conn();
                    $config = $conn->getConfiguration();
                    $config->setResultCache($this->Cache);
                    $rswrk = $conn->executeCacheQuery($sqlWrk, [], [], $this->CacheProfile)->fetchAll();
                    $ari = count($rswrk);
                    if ($ari > 0) { // Lookup values found
                        $arwrk = $this->_ProjectID->Lookup->renderViewRow($rswrk[0]);
                        $this->_ProjectID->ViewValue = $this->_ProjectID->displayValue($arwrk);
                    } else {
                        $this->_ProjectID->ViewValue = FormatNumber($this->_ProjectID->CurrentValue, $this->_ProjectID->formatPattern());
                    }
                }
            } else {
                $this->_ProjectID->ViewValue = null;
            }
        } else {
            $this->_ProjectID->PlaceHolder = RemoveHtml($this->_ProjectID->caption());
        }

        // Created_Date
        $this->Created_Date->setupEditAttributes();
        $this->Created_Date->CurrentValue = FormatDateTime($this->Created_Date->CurrentValue, $this->Created_Date->formatPattern());

        // Created_By
        $this->Created_By->setupEditAttributes();

        // Last_Updated_Date

        // Last_Updated_By

        // Call Row Rendered event
        $this->rowRendered();
    }

    // Aggregate list row values
    public function aggregateListRowValues()
    {
    }

    // Aggregate list row (for rendering)
    public function aggregateListRow()
    {
        // Call Row Rendered event
        $this->rowRendered();
    }

    // Export data in HTML/CSV/Word/Excel/Email/PDF format
    public function exportDocument($doc, $result, $startRec = 1, $stopRec = 1, $exportPageType = "")
    {
        if (!$result || !$doc) {
            return;
        }
        if (!$doc->ExportCustom) {
            // Write header
            $doc->exportTableHeader();
            if ($doc->Horizontal) { // Horizontal format, write header
                $doc->beginExportRow();
                if ($exportPageType == "view") {
                    $doc->exportCaption($this->PaymentProcessID);
                    $doc->exportCaption($this->DocumentName);
                    $doc->exportCaption($this->FileName);
                    $doc->exportCaption($this->Completed);
                    $doc->exportCaption($this->DateDelivered);
                    $doc->exportCaption($this->PICClient);
                    $doc->exportCaption($this->Remark);
                    $doc->exportCaption($this->_ProjectID);
                    $doc->exportCaption($this->Created_Date);
                    $doc->exportCaption($this->Created_By);
                    $doc->exportCaption($this->Last_Updated_Date);
                    $doc->exportCaption($this->Last_Updated_By);
                } else {
                    $doc->exportCaption($this->PaymentProcessID);
                    $doc->exportCaption($this->DocumentName);
                    $doc->exportCaption($this->FileName);
                    $doc->exportCaption($this->Completed);
                    $doc->exportCaption($this->DateDelivered);
                    $doc->exportCaption($this->PICClient);
                    $doc->exportCaption($this->Remark);
                    $doc->exportCaption($this->_ProjectID);
                    $doc->exportCaption($this->Created_Date);
                    $doc->exportCaption($this->Created_By);
                    $doc->exportCaption($this->Last_Updated_Date);
                    $doc->exportCaption($this->Last_Updated_By);
                }
                $doc->endExportRow();
            }
        }
        $recCnt = $startRec - 1;
        $stopRec = $stopRec > 0 ? $stopRec : PHP_INT_MAX;
        while (($row = $result->fetch()) && $recCnt < $stopRec) {
            $recCnt++;
            if ($recCnt >= $startRec) {
                $rowCnt = $recCnt - $startRec + 1;

                // Page break
                if ($this->ExportPageBreakCount > 0) {
                    if ($rowCnt > 1 && ($rowCnt - 1) % $this->ExportPageBreakCount == 0) {
                        $doc->exportPageBreak();
                    }
                }
                $this->loadListRowValues($row);

                // Render row
                $this->RowType = RowType::VIEW; // Render view
                $this->resetAttributes();
                $this->renderListRow();
                if (!$doc->ExportCustom) {
                    $doc->beginExportRow($rowCnt); // Allow CSS styles if enabled
                    if ($exportPageType == "view") {
                        $doc->exportField($this->PaymentProcessID);
                        $doc->exportField($this->DocumentName);
                        $doc->exportField($this->FileName);
                        $doc->exportField($this->Completed);
                        $doc->exportField($this->DateDelivered);
                        $doc->exportField($this->PICClient);
                        $doc->exportField($this->Remark);
                        $doc->exportField($this->_ProjectID);
                        $doc->exportField($this->Created_Date);
                        $doc->exportField($this->Created_By);
                        $doc->exportField($this->Last_Updated_Date);
                        $doc->exportField($this->Last_Updated_By);
                    } else {
                        $doc->exportField($this->PaymentProcessID);
                        $doc->exportField($this->DocumentName);
                        $doc->exportField($this->FileName);
                        $doc->exportField($this->Completed);
                        $doc->exportField($this->DateDelivered);
                        $doc->exportField($this->PICClient);
                        $doc->exportField($this->Remark);
                        $doc->exportField($this->_ProjectID);
                        $doc->exportField($this->Created_Date);
                        $doc->exportField($this->Created_By);
                        $doc->exportField($this->Last_Updated_Date);
                        $doc->exportField($this->Last_Updated_By);
                    }
                    $doc->endExportRow($rowCnt);
                }
            }

            // Call Row Export server event
            if ($doc->ExportCustom) {
                $this->rowExport($doc, $row);
            }
        }
        if (!$doc->ExportCustom) {
            $doc->exportTableFooter();
        }
    }

    // Get file data
    public function getFileData($fldparm, $key, $resize, $width = 0, $height = 0, $plugins = [])
    {
        global $DownloadFileName;
        $width = ($width > 0) ? $width : Config("THUMBNAIL_DEFAULT_WIDTH");
        $height = ($height > 0) ? $height : Config("THUMBNAIL_DEFAULT_HEIGHT");

        // Set up field name / file name field / file type field
        $fldName = "";
        $fileNameFld = "";
        $fileTypeFld = "";
        if ($fldparm == 'FileName') {
            $fldName = "FileName";
            $fileNameFld = "FileName";
        } else {
            return false; // Incorrect field
        }

        // Set up key values
        $ar = explode(Config("COMPOSITE_KEY_SEPARATOR"), $key);
        if (count($ar) == 1) {
            $this->PaymentProcessID->CurrentValue = $ar[0];
        } else {
            return false; // Incorrect key
        }

        // Set up filter (WHERE Clause)
        $filter = $this->getRecordFilter();
        $this->CurrentFilter = $filter;
        $sql = $this->getCurrentSql();
        $conn = $this->getConnection();
        $dbtype = GetConnectionType($this->Dbid);
        if ($row = $conn->fetchAssociative($sql)) {
            $val = $row[$fldName];
            if (!EmptyValue($val)) {
                $fld = $this->Fields[$fldName];

                // Binary data
                if ($fld->DataType == DataType::BLOB) {
                    if ($dbtype != "MYSQL") {
                        if (is_resource($val) && get_resource_type($val) == "stream") { // Byte array
                            $val = stream_get_contents($val);
                        }
                    }
                    if ($resize) {
                        ResizeBinary($val, $width, $height, $plugins);
                    }

                    // Write file type
                    if ($fileTypeFld != "" && !EmptyValue($row[$fileTypeFld])) {
                        AddHeader("Content-type", $row[$fileTypeFld]);
                    } else {
                        AddHeader("Content-type", ContentType($val));
                    }

                    // Write file name
                    $downloadPdf = !Config("EMBED_PDF") && Config("DOWNLOAD_PDF_FILE");
                    if ($fileNameFld != "" && !EmptyValue($row[$fileNameFld])) {
                        $fileName = $row[$fileNameFld];
                        $pathinfo = pathinfo($fileName);
                        $ext = strtolower($pathinfo["extension"] ?? "");
                        $isPdf = SameText($ext, "pdf");
                        if ($downloadPdf || !$isPdf) { // Skip header if not download PDF
                            AddHeader("Content-Disposition", "attachment; filename=\"" . $fileName . "\"");
                        }
                    } else {
                        $ext = ContentExtension($val);
                        $isPdf = SameText($ext, ".pdf");
                        if ($isPdf && $downloadPdf) { // Add header if download PDF
                            AddHeader("Content-Disposition", "attachment" . ($DownloadFileName ? "; filename=\"" . $DownloadFileName . "\"" : ""));
                        }
                    }

                    // Write file data
                    if (
                        StartsString("PK", $val) &&
                        ContainsString($val, "[Content_Types].xml") &&
                        ContainsString($val, "_rels") &&
                        ContainsString($val, "docProps")
                    ) { // Fix Office 2007 documents
                        if (!EndsString("\0\0\0", $val)) { // Not ends with 3 or 4 \0
                            $val .= "\0\0\0\0";
                        }
                    }

                    // Clear any debug message
                    if (ob_get_length()) {
                        ob_end_clean();
                    }

                    // Write binary data
                    Write($val);

                // Upload to folder
                } else {
                    if ($fld->UploadMultiple) {
                        $files = explode(Config("MULTIPLE_UPLOAD_SEPARATOR"), $val);
                    } else {
                        $files = [$val];
                    }
                    $data = [];
                    $ar = [];
                    if ($fld->hasMethod("getUploadPath")) { // Check field level upload path
                        $fld->UploadPath = $fld->getUploadPath();
                    }
                    foreach ($files as $file) {
                        if (!EmptyValue($file)) {
                            if (Config("ENCRYPT_FILE_PATH")) {
                                $ar[$file] = FullUrl(GetApiUrl(Config("API_FILE_ACTION") .
                                    "/" . $this->TableVar . "/" . Encrypt($fld->physicalUploadPath() . $file)));
                            } else {
                                $ar[$file] = FullUrl($fld->hrefPath() . $file);
                            }
                        }
                    }
                    $data[$fld->Param] = $ar;
                    WriteJson($data);
                }
            }
            return true;
        }
        return false;
    }

    // Table level events

    // Table Load event
    public function tableLoad()
    {
        // Enter your code here
    }

    // Recordset Selecting event
    public function recordsetSelecting(&$filter)
    {
                if (in_array(CurrentUserLevel(), array("4", "5", "6","7","8","9","10"))) {
            $currentUser = CurrentUserName(); // Ambil nama pengguna saat ini
            // Pastikan untuk meng-escape string nama pengguna untuk keamanan
            $currentUser = addslashes($currentUser);

            // Perbarui filter dengan query yang benar
            $existsClause = "EXISTS (
                    SELECT 1 FROM vprojectid 
                    WHERE vprojectid.projectid = projectpaymentprocess.projectid 
                    AND vprojectid.UserName = '" . $currentUser . "'
                )";
            if (strlen($filter) == 0) {
                $filter = $existsClause;
            } else {
                $filter .= ' AND ' . $existsClause;
            }
        }
    }

    // Recordset Selected event
    public function recordsetSelected($rs)
    {
        //Log("Recordset Selected");
    }

    // Recordset Search Validated event
    public function recordsetSearchValidated()
    {
        // Example:
        //$this->MyField1->AdvancedSearch->SearchValue = "your search criteria"; // Search value
    }

    // Recordset Searching event
    public function recordsetSearching(&$filter)
    {
        // Enter your code here
    }

    // Row_Selecting event
    public function rowSelecting(&$filter)
    {
        // Enter your code here
    }

    // Row Selected event
    public function rowSelected(&$rs)
    {
        //Log("Row Selected");
    }

    // Row Inserting event
    public function rowInserting($rsold, &$rsnew)
    {
        // Enter your code here
        // To cancel, set return value to false
        return true;
    }

    public function rowInserted($rsold, $rsnew)
    {
        $projectIdToUpdate = isset($rsnew["ProjectID"]) ? $rsnew["ProjectID"] : $rsold["ProjectID"];

        // Query pertama untuk mengupdate Progress di tabel MasterProject
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET mp.PaymentProgress = COALESCE((
            SELECT SUM(pr.Completed) / CAST(COUNT(*) AS FLOAT)
            FROM projectpaymentprocess pr
            WHERE pr.ProjectID = mp.ProjectID
        ),0)
        WHERE mp.ProjectID = " . $projectIdToUpdate;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
     $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = CURDATE()
        WHERE PaymentProgress>=1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = null
        WHERE PaymentProgress<1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQue
        }

    // Row Updating event
    public function rowUpdating($rsold, &$rsnew)
    {
        // Enter your code here
        // To cancel, set return value to false
        //$this->DocumentFile->UploadPath = "files/documents";
        if(isset($rsold["Completed"]) && isset($rsnew["Completed"]))
        {
            if($rsnew["Completed"]!=$rsold["Completed"] && $rsnew["Completed"]==1)
            {
                $rsnew["DateDelivered"] = CurrentDate();
            }
        }
        return true;
    }
    // Row Updated event
    public function rowUpdated($rsold, $rsnew)
    {
        $projectIdToUpdate = isset($rsnew["ProjectID"]) ? $rsnew["ProjectID"] : $rsold["ProjectID"];

        // Query pertama untuk mengupdate Progress di tabel MasterProject
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET mp.PaymentProgress = COALESCE((
            SELECT SUM(pr.Completed) / CAST(COUNT(*) AS FLOAT)
            FROM projectpaymentprocess pr
            WHERE pr.ProjectID = mp.ProjectID
        ),0)
        WHERE mp.ProjectID = " . $projectIdToUpdate;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
     $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = CURDATE()
        WHERE PaymentProgress>=1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = null
        WHERE PaymentProgress<1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQue
    }

    // Row Update Conflict event
    public function rowUpdateConflict($rsold, &$rsnew)
    {
        // Enter your code here
        // To ignore conflict, set return value to false
        return true;
    }

    // Grid Inserting event
    public function gridInserting()
    {
        // Enter your code here
        // To reject grid insert, set return value to false
        return true;
    }

    // Grid Inserted event
    public function gridInserted($rsnew)
    {
        //Log("Grid Inserted");
    }

    // Grid Updating event
    public function gridUpdating($rsold)
    {
        // Enter your code here
        // To reject grid update, set return value to false
        return true;
    }

    // Grid Updated event
    public function gridUpdated($rsold, $rsnew)
    {
        //Log("Grid Updated");
    }

    // Row Deleting event
    public function rowDeleting(&$rs)
    {
        // Enter your code here
        // To cancel, set return value to False
        return true;
    }

    // Row Deleted event
    public function rowDeleted($rs)
    {
         $projectIdToUpdate = $rs["ProjectID"];

         // Query pertama untuk mengupdate Progress di tabel MasterProject
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET mp.PaymentProgress = COALESCE((
            SELECT SUM(pr.Completed) / CAST(COUNT(*) AS FLOAT)
            FROM projectpaymentprocess pr
            WHERE pr.ProjectID = mp.ProjectID
        ),0)
        WHERE mp.ProjectID = " . $projectIdToUpdate;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
     $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = CURDATE()
        WHERE PaymentProgress>=1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQuery dengan fungsi yang sesuai
        $queryProgressUpdate = "UPDATE MasterProject mp
        SET PaymentDate = null
        WHERE PaymentProgress<1 AND mp.ProjectID = " . $projectIdToUpdate  ;

        // Eksekusi query untuk mengupdate Progress di tabel MasterProject
        $resultProgressUpdate = ExecuteQuery($queryProgressUpdate); // Anda perlu mengganti ExecuteQue
    }

    // Email Sending event
    public function emailSending($email, $args)
    {
        //var_dump($email, $args); exit();
        return true;
    }

    // Lookup Selecting event
    public function lookupSelecting($fld, &$filter)
    {
        //var_dump($fld->Name, $fld->Lookup, $filter); // Uncomment to view the filter
        // Enter your code here
    }

    // Row Rendering event
    public function rowRendering()
    {
        // Enter your code here
    }

    // Row Rendered event
    public function rowRendered()
    {
      if($this->templateid->DbValue>0)
        {
            $pathBase = ""; // Awalnya $this->getContainer()->PathBase; // Mendapatkan ew.PATH_BASE
            if ($this->PageID == "view") {
                $pathBase = "../";
            } else {
                $pathBase = "./";
            }
            if ($this->PageID == "view" || $this->PageID == "list"|| $this->PageID == "preview") {
                $yourAdditionalPath = "projecttemplatepaymentprocessview/" . $this->templateid->DbValue . "?showdetail="; // Sesuaikan ini sesuai kebutuhan
                $fullUrl = $pathBase . $yourAdditionalPath;
                $this->DocumentName->ViewValue = '<a href="' . htmlspecialchars($fullUrl) . '" target="_blank">' . $this->DocumentName->ViewValue . '</a>';
            }
        }
    }

    // User ID Filtering event
    public function userIdFiltering(&$filter)
    {
        // Enter your code here
    }
}
