<?php
defined('ABSPATH') || die();

require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/staff/WLSM_M_Staff_Class.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/staff/WLSM_M_Staff_General.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/staff/WLSM_M_Staff_Accountant.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_M_Invoice.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_Notify.php';

class WLSM_Staff_Accountant
{
	public static function get_invoices()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];
		$session_label = $current_user['session']['label'];

		if (!wp_verify_nonce($_POST['get-invoices'], 'get-invoices')) {
			die();
		}

		$from_table = isset($_POST['from_table']) ? (bool) ($_POST['from_table']) : 0;

		$output = array(
			'draw' => 1,
			'recordsTotal' => 0,
			'recordsFiltered' => 0,
			'data' => [],
		);

		$search_students_by = isset($_POST['search_students_by']) ? sanitize_text_field($_POST['search_students_by']) : '';

		$search_field = isset($_POST['search_field']) ? sanitize_text_field($_POST['search_field']) : '';
		$search_keyword = isset($_POST['search_keyword']) ? sanitize_text_field($_POST['search_keyword']) : '';

		$class_id = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;
		$section_id = isset($_POST['section_id']) ? absint($_POST['section_id']) : 0;

		try {
			ob_start();
			global $wpdb;

			// Start validation.
			$errors = array();

			if (!in_array($search_students_by, array('search_by_keyword', 'search_by_class'))) {
				throw new Exception(esc_html__('Please specify search criteria.', 'school-management-system'));
			}

			if ('search_by_keyword' === $search_students_by) {
				if (!empty($search_field) && empty($search_keyword)) {
					$errors['search_keyword'] = esc_html__('Please enter search keyword.', 'school-management-system');
				} else if (!empty($search_keyword) && empty($search_field)) {
					$errors['search_field'] = esc_html__('Please specify search field.', 'school-management-system');
				}

				$filter = array(
					'search_field' => $search_field,
					'search_keyword' => $search_keyword,
				);

			} else {
				if (empty($class_id)) {
					$errors['class_id'] = esc_html__('Please select a class.', 'school-management-system');
				}

				$filter = array(
					'class_id' => $class_id,
					'section_id' => $section_id,
				);
			}

		} catch (Exception $exception) {
			if ($from_table) {
				echo json_encode($output);
				die();
			}
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			if (!$from_table) {
				wp_send_json_success();
			}
			try {
				$filter['search_by'] = $search_students_by;

				$page_url = WLSM_M_Staff_Accountant::get_invoices_page_url();

				$query = WLSM_M_Staff_Accountant::fetch_invoices_query($school_id, $session_id, $filter);

				$query_filter = $query;

				// Grouping.
				$group_by = ' ' . WLSM_M_Staff_Accountant::fetch_invoices_query_group_by();

				$query .= $group_by;
				$query_filter .= $group_by;

				// Searching.
				$condition = '';
				if (esc_sql($_POST['search']['value'])) {
					$search_value = sanitize_text_field(esc_sql($_POST['search']['value']));
					if ('' !== $search_value) {
						$search_value_like = '%' . $wpdb->esc_like($search_value) . '%';
				
						$condition .= $wpdb->prepare(
							'(i.invoice_number LIKE %s) OR ' .
							'(i.label LIKE %s) OR ' .
							'(sr.name LIKE %s) OR ' .
							'(sr.admission_number LIKE %s) OR ' .
							'(sr.enrollment_number LIKE %s) OR ' .
							'(sr.phone LIKE %s) OR ' .
							'(c.label LIKE %s) OR ' .
							'(se.label LIKE %s)',
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like
						);

						$search_value_lowercase = strtolower($search_value);
						if (preg_match('/^paid$/', $search_value_lowercase)) {
							$status = WLSM_M_Invoice::get_paid_key();
						} else if (preg_match('/^unpa(|i|id)$/', $search_value_lowercase)) {
							$status = WLSM_M_Invoice::get_unpaid_key();
						} else if (preg_match('/^partially(| p| pa| pai| paid)$/', $search_value_lowercase)) {
							$status = WLSM_M_Invoice::get_partially_paid_key();
						}

						if (isset($status)) {
							$condition .= $wpdb->prepare(' OR (i.status = %s)', $status);
						}

						$date_issued = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);
						if ($date_issued) {
							$format_date_issued = 'Y-m-d';
						} else {
							if ('d-m-Y' === WLSM_Config::date_format()) {
								$date_issued = DateTime::createFromFormat('m-Y', $search_value);
								$format_date_issued = 'Y-m';
							} elseif ('d/m/Y' === WLSM_Config::date_format()) {
								$date_issued = DateTime::createFromFormat('m/Y', $search_value);
								$format_date_issued = 'Y-m';
							} elseif ('Y-m-d' === WLSM_Config::date_format()) {
								$date_issued = DateTime::createFromFormat('Y-m', $search_value);
								$format_date_issued = 'Y-m';
							} elseif ('Y/m/d' === WLSM_Config::date_format()) {
								$date_issued = DateTime::createFromFormat('Y/m', $search_value);
								$format_date_issued = 'Y-m';
							}

							if (!$date_issued) {
								$date_issued = DateTime::createFromFormat('Y', $search_value);
								$format_date_issued = 'Y';
							}
						}

						if ($date_issued && isset($format_date_issued)) {
							$date_issued = $date_issued->format($format_date_issued);
							$condition .= $wpdb->prepare(' OR (i.date_issued LIKE %s)', '%' . $wpdb->esc_like($date_issued) . '%');
						}

						$due_date = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);
						if ($due_date) {
							$format_due_date = 'Y-m-d';
						} else {
							if ('d-m-Y' === WLSM_Config::date_format()) {
								$due_date = DateTime::createFromFormat('m-Y', $search_value);
								$format_due_date = 'Y-m';
							} elseif ('d/m/Y' === WLSM_Config::date_format()) {
								$due_date = DateTime::createFromFormat('m/Y', $search_value);
								$format_due_date = 'Y-m';
							} elseif ('Y-m-d' === WLSM_Config::date_format()) {
								$due_date = DateTime::createFromFormat('Y-m', $search_value);
								$format_due_date = 'Y-m';
							} elseif ('Y/m/d' === WLSM_Config::date_format()) {
								$due_date = DateTime::createFromFormat('Y/m', $search_value);
								$format_due_date = 'Y-m';
							}

							if (!$due_date) {
								$due_date = DateTime::createFromFormat('Y', $search_value);
								$format_due_date = 'Y';
							}
						}

						if ($due_date && isset($format_due_date)) {
							$due_date = $due_date->format($format_due_date);
							$condition .= $wpdb->prepare(' OR (i.due_date LIKE %s)', '%' . $wpdb->esc_like($due_date) . '%');
						}

						// Finally, construct the query condition
						$query_filter .= ' HAVING ' . $condition;
					}
				}

				// Ordering.
				$columns = array('sr.name', 'sr.admission_number', 'i.invoice_number', 'i.label', 'payable', 'paid', 'due', 'i.status', 'i.date_issued', 'i.due_date', 'sr.phone', 'c.label', 'se.label', 'sr.enrollment_number');
				if (isset($_POST['order']) && esc_sql($columns[$_POST['order']['0']['column']])) {
					$order_by = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
					$order_dir = sanitize_text_field(esc_sql($_POST['order']['0']['dir']));

					$query_filter .= ' ORDER BY ' . $order_by . ' ' . $order_dir;
				} else {
					$query_filter .= ' ORDER BY i.ID DESC';
				}

				// Limiting.
				$limit = '';
				if (-1 != $_POST['length']) {
					$start = absint(esc_sql($_POST['start']));
					$length = absint(esc_sql($_POST['length']));

					$limit = ' LIMIT ' . $start . ', ' . $length;
				}

				// Total query.
				$rows_query = WLSM_M_Staff_Accountant::fetch_invoices_query_count($school_id, $session_id, $filter);

				// Total rows count.
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
				$total_rows_count = $wpdb->get_var($rows_query);

				// Filtered rows count.
				if ($condition) {
					// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic components are built from safely prepared fragments.
					$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
				} else {
					$filter_rows_count = $total_rows_count;
				}

				// Filtered limit rows.
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
				$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

				$data = array();
				if (count($filter_rows_limit)) {
					foreach ($filter_rows_limit as $row) {
						$due = $row->payable - $row->paid;

						if (WLSM_M_Invoice::get_paid_key() !== $row->status) {
							$collect_payment = '<br><a href="' . esc_url($page_url . '&action=collect_payment&id=' . $row->ID . '#wlsm-fee-invoice-status') . '" class="btn wlsm-btn-xs btn-success">' . esc_html__('Collect Payment', 'school-management-system') . '</a>';
						} else {
							$collect_payment = '';
						}

						// Table columns.
						$data[] = array(
							esc_html(WLSM_M_Staff_Class::get_name_text($row->student_name)),
							esc_html(WLSM_M_Staff_Class::get_admission_no_text($row->admission_number)),
							esc_html($row->invoice_number),
							esc_html(WLSM_M_Staff_Accountant::get_invoice_title_text($row->invoice_title)),
							esc_html(WLSM_Config::get_money_text($row->payable)),
							esc_html(WLSM_Config::get_money_text($row->paid)),
							'<span class="wlsm-font-bold">' . esc_html(WLSM_Config::get_money_text($due)) . '</span>',
							wp_kses(
								WLSM_M_Invoice::get_status_text($row->status),
								array('span' => array('class' => array()))
							) . $collect_payment,
							esc_html(WLSM_Config::get_date_text($row->date_issued)),
							esc_html(WLSM_Config::get_date_text($row->due_date)),
							esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
							esc_html(WLSM_M_Class::get_label_text($row->class_label)),
							esc_html(WLSM_M_Staff_Class::get_section_label_text($row->section_label)),
							esc_html($row->enrollment_number),
							'<a class="text-success wlsm-print-invoice" data-nonce="' . esc_attr(wp_create_nonce('print-invoice-' . $row->ID)) . '" data-invoice="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Print Invoice', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><i class="fas fa-print"></i></a>&nbsp;&nbsp;
							<a class="text-primary" href="' . esc_url($page_url . "&action=save&id=" . $row->ID) . '"><span class="dashicons dashicons-edit"></span></a>&nbsp;&nbsp;
							<a class="text-danger wlsm-delete-invoice" data-nonce="' . esc_attr(wp_create_nonce('delete-invoice-' . $row->ID)) . '" data-invoice="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . sprintf(esc_attr__('This will delete the invoice.', 'school-management-system'), esc_html(WLSM_M_Session::get_label_text($session_label))) . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>'
						);
					}
				}

				$output = array(
					'draw' => intval(esc_sql($_POST['draw'])),
					'recordsTotal' => $total_rows_count,
					'recordsFiltered' => $filter_rows_count,
					'data' => $data,
					'export' => array(
						'nonce' => wp_create_nonce('export-staff-invoices-table'),
						'action' => 'wlsm-export-staff-invoices-table'
					)
				);

				echo json_encode($output);
				die();
			} catch (Exception $exception) {
				if ($from_table) {
					echo json_encode($output);
					die();
				}
				wp_send_json_error($exception->getMessage());
			}
		}

		if ($from_table) {
			echo json_encode($output);
			die();
		}
		wp_send_json_error($errors);
	}

	public static function save_invoice()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$invoice_id = isset($_POST['invoice_id']) ? absint($_POST['invoice_id']) : 0;

			if ($invoice_id) {
				if (!wp_verify_nonce($_POST['edit-invoice-' . $invoice_id], 'edit-invoice-' . $invoice_id)) {
					die();
				}
			} else {
				if (!wp_verify_nonce($_POST['add-invoice'], 'add-invoice')) {
					die();
				}
			}

			// Checks if invoice exists.
			if ($invoice_id) {
				$invoice = WLSM_M_Staff_Accountant::fetch_invoice($school_id, $session_id, $invoice_id);

				if (!$invoice) {
					throw new Exception(esc_html__('Invoice not found.', 'school-management-system'));
				}
			}

			$invoice_title = isset($_POST['invoice_label']) ? sanitize_text_field($_POST['invoice_label']) : '';
			$invoice_description = isset($_POST['invoice_description']) ? sanitize_text_field($_POST['invoice_description']) : '';
			$invoice_amount = isset($_POST['invoice_amount']) ? WLSM_Config::sanitize_money($_POST['invoice_amount']) : 0;
			$invoice_discount = isset($_POST['invoice_discount']) ? WLSM_Config::sanitize_money($_POST['invoice_discount']) : 0;
			$invoice_date_issued = isset($_POST['invoice_date_issued']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['invoice_date_issued'])) : NULL;
			$invoice_due_date = isset($_POST['invoice_due_date']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['invoice_due_date'])) : NULL;
			$partial_payment = isset($_POST['partial_payment']) ? (bool) $_POST['partial_payment'] : 0;

			if (!$invoice_id) {
				$invoice_type = isset($_POST['invoice_type']) ? sanitize_text_field($_POST['invoice_type']) : '';
			}

			// Start validation.
			$errors = array();

			if (empty($invoice_title)) {
				$errors['invoice_label'] = esc_html__('Please provide invoice title.', 'school-management-system');
			} else {
				if (strlen($invoice_title) > 50) {
					$errors['invoice_label'] = esc_html__('Maximum length cannot exceed 100 characters.', 'school-management-system');
				}
			}

			if ($invoice_amount <= 0) {
				$errors['invoice_amount'] = esc_html__('Please specify a valid invoice amount.', 'school-management-system');
			}

			if ($invoice_discount > $invoice_amount) {
				$errors['invoice_discount'] = esc_html__('Discount must be lower or equal to invoice amount.', 'school-management-system');
			}

			if ($invoice_date_issued > $invoice_due_date) {
				$errors['invoice_due_date'] = esc_html__('Invoice due date must be greater than issued date.', 'school-management-system');
			}

			if (empty($invoice_date_issued)) {
				$errors['invoice_date_issued'] = esc_html__('Please provide date issued.', 'school-management-system');
			} else {
				$invoice_date_issued = $invoice_date_issued->format('Y-m-d');
			}

			if (empty($invoice_due_date)) {
				$invoice_due_date = NULL;
			} else {
				$invoice_due_date = $invoice_due_date->format('Y-m-d');
			}

			if (!$invoice_id) {
				if (!in_array($invoice_type, array('single_invoice', 'bulk_invoice'))) {
					throw new Exception(esc_html__('Please select either single invoice or bulk invoice option.', 'school-management-system'));
				}

				if ('single_invoice' === $invoice_type) {
					$student_id = isset($_POST['student']) ? absint($_POST['student']) : 0;

					$collect_invoice_payment = isset($_POST['collect_invoice_payment']) ? (bool) $_POST['collect_invoice_payment'] : 0;

					if (empty($student_id)) {
						$errors['student'] = esc_html__('Please select a student.', 'school-management-system');
						wp_send_json_error($errors);
					}

					// Checks if student exists.
					$student = WLSM_M_Staff_General::get_student($school_id, $session_id, $student_id, true);

					if (!$student) {
						throw new Exception(esc_html__('Student not found.', 'school-management-system'));
					}

					if ($collect_invoice_payment) {
						$payment_amount = isset($_POST['payment_amount']) ? WLSM_Config::sanitize_money($_POST['payment_amount']) : 0;
						$payment_method = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';
						$transaction_id = isset($_POST['transaction_id']) ? sanitize_text_field($_POST['transaction_id']) : '';
						$payment_note = isset($_POST['payment_note']) ? sanitize_text_field($_POST['payment_note']) : '';

						$due = WLSM_M_Invoice::get_due_amount(
							array(
								'total' => $invoice_amount,
								'discount' => $invoice_discount,
							)
						);


						$errors = self::validate_invoice_payment($errors, $partial_payment, $due, $payment_amount, $payment_method);
					}

				} else {
					$student_ids = (isset($_POST['student']) && is_array($_POST['student'])) ? $_POST['student'] : array();

					if (!count($student_ids)) {
						$errors['student[]'] = esc_html__('Please select students.', 'school-management-system');
					}

					// Checks if students exists.
					$students_count = WLSM_M_Staff_General::get_students_count($school_id, $session_id, $student_ids, true);

					if ($students_count != count($student_ids)) {
						throw new Exception(esc_html__('Student(s) not found.', 'school-management-system'));
					}
				}
			}

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				// Invoice data.
				$invoice_data = array(
					'label' => $invoice_title,
					'description' => $invoice_description,
					'amount' => $invoice_amount,
					'discount' => $invoice_discount,
					'date_issued' => $invoice_date_issued,
					'due_date' => $invoice_due_date,
					'partial_payment' => $partial_payment,
				);

				// Checks if update or insert.
				if ($invoice_id) {
					$message = esc_html__('Invoice updated successfully.', 'school-management-system');
					$reset = false;

					$invoice_data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_INVOICES, $invoice_data, array('ID' => $invoice_id));

					$buffer = ob_get_clean();
					if (!empty($buffer)) {
						throw new Exception($buffer);
					}

					WLSM_M_Staff_Accountant::refresh_invoice_status($invoice_id);
				} else {
					$message = esc_html__('Invoice added successfully.', 'school-management-system');
					$reset = true;

					if ('bulk_invoice' === $invoice_type) {
						$bulk_invoice_ids = array();
						foreach ($student_ids as $student_id) {
							$invoice_number = WLSM_M_Invoice::get_invoice_number($school_id);

							$invoice_data['invoice_number'] = $invoice_number;
							$invoice_data['student_record_id'] = $student_id;

							$success = $wpdb->insert(WLSM_INVOICES, $invoice_data);

							$bulk_invoice_id = $wpdb->insert_id;
							array_push($bulk_invoice_ids, $bulk_invoice_id);

							$buffer = ob_get_clean();
							if (!empty($buffer)) {
								throw new Exception($buffer);
							}
						}
					} else if (('single_invoice' === $invoice_type)) {
						$invoice_number = WLSM_M_Invoice::get_invoice_number($school_id);

						$invoice_data['invoice_number'] = $invoice_number;
						$invoice_data['student_record_id'] = $student_id;

						$success = $wpdb->insert(WLSM_INVOICES, $invoice_data);

						$single_invoice_id = $wpdb->insert_id;

						if ($collect_invoice_payment) {
							$invoice_id = $wpdb->insert_id;

							$receipt_number = WLSM_M_Invoice::get_receipt_number($school_id);

							// Payment data.
							$payment_data = array(
								'receipt_number' => $receipt_number,
								'amount' => $payment_amount,
								'payment_method' => $payment_method,
								'transaction_id' => $transaction_id,
								'note' => $payment_note,
								'invoice_label' => $invoice_title,
								'invoice_payable' => $due,
								'student_record_id' => $student_id,
								'invoice_id' => $invoice_id,
								'school_id' => $school_id,
							);

							$success = $wpdb->insert(WLSM_PAYMENTS, $payment_data);

							$new_payment_id = $wpdb->insert_id;

							$buffer = ob_get_clean();
							if (!empty($buffer)) {
								throw new Exception($buffer);
							}

							WLSM_M_Staff_Accountant::refresh_invoice_status($invoice_id);
						}
					}
				}

				$buffer = ob_get_clean();
				if (!empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				$wpdb->query('COMMIT;');

				if (isset($bulk_invoice_ids) && count($bulk_invoice_ids) > 0) {
					foreach ($bulk_invoice_ids as $bulk_invoice_id) {
						// Notify for invoice generated.
						$data = array(
							'school_id' => $school_id,
							'session_id' => $session_id,
							'invoice_id' => $bulk_invoice_id,
						);

						wp_schedule_single_event(time() + 30, 'wlsm_notify_for_invoice_generated', $data);
					}
				} else if (isset($single_invoice_id)) {
					// Notify for invoice generated.
					$data = array(
						'school_id' => $school_id,
						'session_id' => $session_id,
						'invoice_id' => $single_invoice_id,
					);

					wp_schedule_single_event(time() + 30, 'wlsm_notify_for_invoice_generated', $data);
				}

				if (isset($new_payment_id)) {
					// Notify for offline fee submission.
					$data = array(
						'school_id' => $school_id,
						'session_id' => $session_id,
						'payment_id' => $new_payment_id,
					);

					wp_schedule_single_event(time() + 30, 'wlsm_notify_for_offline_fee_submission', $data);
				}

				wp_send_json_success(array('message' => $message, 'reset' => $reset));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function delete_invoice()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$invoice_id = isset($_POST['invoice_id']) ? absint($_POST['invoice_id']) : 0;

			if (!wp_verify_nonce($_POST['delete-invoice-' . $invoice_id], 'delete-invoice-' . $invoice_id)) {
				die();
			}

			// Checks if invoice exists.
			$invoice = WLSM_M_Staff_Accountant::get_invoice($school_id, $session_id, $invoice_id);

			if (!$invoice) {
				throw new Exception(esc_html__('Invoice not found.', 'school-management-system'));
			}

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_INVOICES, array('ID' => $invoice_id));
			$message = esc_html__('Invoice deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (!empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function print_invoice()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$invoice_id = isset($_POST['invoice_id']) ? absint($_POST['invoice_id']) : 0;

			if (!wp_verify_nonce($_POST['print-invoice-' . $invoice_id], 'print-invoice-' . $invoice_id)) {
				die();
			}

			// Checks if invoice exists.
			$invoice = WLSM_M_Staff_Accountant::fetch_invoice($school_id, $session_id, $invoice_id);

			if (!$invoice) {
				throw new Exception(esc_html__('Invoice not found.', 'school-management-system'));
			}

			$payments = WLSM_M_Staff_Accountant::get_invoice_payments($invoice_id);

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		ob_start();
		require_once WLSM_PLUGIN_DIR_PATH . 'admin/inc/school/print/invoice.php';
		$html = ob_get_clean();

		wp_send_json_success(array('html' => $html));
	}

	public static function print_invoice_fee_structure()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$student_id = isset($_POST['student_id']) ? absint($_POST['student_id']) : 0;

			if (!wp_verify_nonce($_POST['print-invoice-fee-structure'], 'print-invoice-fee-structure')) {
				die();
			}

			// Checks if student exists.
			$student = WLSM_M_Staff_General::fetch_student($school_id, $session_id, $student_id);

			if (!$student) {
				throw new Exception(esc_html__('Student not found.', 'school-management-system'));
			}

			$invoices = WLSM_M_Staff_Accountant::get_student_invoices($student_id);
			$payments = WLSM_M_Staff_Accountant::get_student_payments($student_id);

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		ob_start();
		require_once WLSM_PLUGIN_DIR_PATH . 'includes/partials/student_invoices.php';
		require_once WLSM_PLUGIN_DIR_PATH . 'includes/partials/student_payments.php';
		$html = ob_get_clean();

		wp_send_json_success(array('html' => $html));
	}

	public static function fetch_invoice_payments()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$current_school = $current_user['school'];

		$can_delete_payments = WLSM_M_Role::check_permission(array('delete_payments'), $current_school['permissions']);

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		global $wpdb;

		$invoice_id = isset($_POST['invoice']) ? absint($_POST['invoice']) : 0;

		if (!wp_verify_nonce($_POST['invoice-payments-' . $invoice_id], 'invoice-payments-' . $invoice_id)) {
			die();
		}

		$query = WLSM_M_Staff_Accountant::fetch_invoice_payments_query($school_id, $session_id, $invoice_id);

		$query_filter = $query;

		// Grouping.
		$group_by = ' ' . WLSM_M_Staff_Accountant::fetch_payments_query_group_by();

		$query .= $group_by;
		$query_filter .= $group_by;

		$condition = '';
		if (isset($_POST['search']['value'])) {
			$search_value = sanitize_text_field($_POST['search']['value']);
			if ('' !== $search_value) {
				$search_value = '%' . $search_value . '%'; // Add wildcard characters for partial matching

				$condition .= $wpdb->prepare(
					'(p.receipt_number LIKE %s) OR ' .
					'(p.amount LIKE %s) OR ' .
					'(p.transaction_id LIKE %s) OR ' .
					'(p.note LIKE %s)',
					$search_value,
					$search_value,
					$search_value,
					$search_value
				);

				$payment_method = strtolower(preg_replace('/[^A-Za-z0-9-]+/', '-', $search_value));
				if (isset($payment_method)) {
					$condition .= $wpdb->prepare(' OR (p.payment_method LIKE %s)', '%' . $payment_method . '%');
				}

				$created_at = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);
				if ($created_at) {
					$format_created_at = 'Y-m-d';
				} else {
					if ('d-m-Y' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('m-Y', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('d/m/Y' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('m/Y', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('Y-m-d' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('Y-m', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('Y/m/d' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('Y/m', $search_value);
						$format_created_at = 'Y-m';
					}

					if (!$created_at) {
						$created_at = DateTime::createFromFormat('Y', $search_value);
						$format_created_at = 'Y';
					}
				}

				if ($created_at && isset($format_created_at)) {
					$created_at = $created_at->format($format_created_at);
					$condition .= $wpdb->prepare(' OR (p.created_at LIKE %s)', '%' . $wpdb->esc_like($created_at) . '%');
				}

				// Finally, construct the query condition
				$query_filter .= ' HAVING ' . $condition;
			}
		}

		// Ordering.
		$columns = array('p.receipt_number', 'p.amount', 'p.payment_method', 'p.transaction_id', 'p.created_at', 'p.note');
		if (isset($_POST['order']) && esc_sql($_POST['order']) && isset($columns[$_POST['order']['0']['column']]) && esc_sql($columns[$_POST['order']['0']['column']])) {
			$order_by = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = sanitize_text_field(esc_sql($_POST['order']['0']['dir']));

			// Validate order direction
			$order_dir = in_array(strtoupper($order_dir), array('ASC', 'DESC')) ? strtoupper($order_dir) : 'DESC';

			// Construct the order by clause using $wpdb->prepare()
			$order_by_clause = $wpdb->prepare(' ORDER BY %s %s', $order_by, $order_dir);

			$query_filter .= $order_by_clause;
		} else {
			$query_filter .= ' ORDER BY p.ID DESC';
		}

		// Limiting.
		$limit = '';
		if (isset($_POST['length']) && -1 != $_POST['length']) {
			$start = absint($_POST['start']);
			$length = absint($_POST['length']);

			// Construct the LIMIT clause
			$limit = $wpdb->prepare(' LIMIT %d, %d', $start, $length);
		}

		// Total query.
		$rows_query = WLSM_M_Staff_Accountant::fetch_invoice_payments_query_count($school_id, $session_id, $invoice_id);

		// Total rows count.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
		$total_rows_count = $wpdb->get_var($rows_query);

		// Filtered rows count.
		if ($condition) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic segments are built from safely prepared fragments.
			$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
		} else {
			$filter_rows_count = $total_rows_count;
		}

		// Filtered limit rows.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
		$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

		$data = array();
		if (count($filter_rows_limit)) {
			foreach ($filter_rows_limit as $row) {
				if ($row->note) {
					$view_note = '<a class="text-primary wlsm-view-payment-note" data-nonce="' . esc_attr(wp_create_nonce('view-payment-note-' . $row->ID)) . '" data-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Payment Note', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><span class="dashicons dashicons-search"></span></a>';
				} else {
					$view_note = '-';
				}

				// Table columns.
				$columns = array(
					esc_html(WLSM_M_Invoice::get_receipt_number_text($row->receipt_number)),
					esc_html(WLSM_Config::get_money_text($row->amount)),
					esc_html(WLSM_M_Invoice::get_payment_method_text($row->payment_method)),
					esc_html(WLSM_M_Invoice::get_transaction_id_text($row->transaction_id)),
					esc_html(WLSM_Config::get_date_text($row->created_at)),
					$view_note,
				);

				$columns[] = '<a class="text-success wlsm-print-invoice-payment" data-nonce="' . esc_attr(wp_create_nonce('print-invoice-payment-' . $row->ID)) . '" data-invoice-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Print Payment Receipt', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><i class="fas fa-print"></i></a>';

				if ($can_delete_payments) {
					$columns[] = '<a class="text-danger wlsm-delete-invoice-payment" data-nonce="' . esc_attr(wp_create_nonce('delete-payment-' . $row->ID)) . '" data-invoice="' . esc_attr($invoice_id) . '" data-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . esc_attr__('This will delete the payment from invoice.', 'school-management-system') . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>';
				}

				$data[] = $columns;
			}
		}

		$output = array(
			'draw' => intval(esc_sql($_POST['draw'])),
			'recordsTotal' => $total_rows_count,
			'recordsFiltered' => $filter_rows_count,
			'data' => $data,
		);

		echo json_encode($output);
		die;
	}

	public static function collect_invoice_payment()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$invoice_id = isset($_POST['invoice_id']) ? absint($_POST['invoice_id']) : 0;

		if (!wp_verify_nonce($_POST['collect-invoice-payment-' . $invoice_id], 'collect-invoice-payment-' . $invoice_id)) {
			die();
		}

		try {
			ob_start();
			global $wpdb;

			// Checks if invoice exists.
			$invoice = WLSM_M_Staff_Accountant::fetch_invoice($school_id, $session_id, $invoice_id);

			if (!$invoice) {
				throw new Exception(esc_html__('Invoice not found.', 'school-management-system'));
			}

			$invoice_id = $invoice->ID;

			$partial_payment = $invoice->partial_payment;

			$payment_amount = isset($_POST['payment_amount']) ? WLSM_Config::sanitize_money($_POST['payment_amount']) : 0;
			$payment_method = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';
			$transaction_id = isset($_POST['transaction_id']) ? sanitize_text_field($_POST['transaction_id']) : '';
			$payment_note = isset($_POST['payment_note']) ? sanitize_text_field($_POST['payment_note']) : '';

			// Start validation.
			$errors = array();

			if (strlen($payment_method) > 50) {
				$errors['payment_method'] = esc_html__('Maximum length cannot exceed 50 characters.', 'school-management-system');
			}

			$due = $invoice->payable - $invoice->paid;

			$errors = self::validate_invoice_payment($errors, $partial_payment, $due, $payment_amount, $payment_method);

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				$message = esc_html__('Payment added successfully.', 'school-management-system');
				$reset = true;

				$receipt_number = WLSM_M_Invoice::get_receipt_number($school_id);

				// Payment data.
				$payment_data = array(
					'receipt_number' => $receipt_number,
					'amount' => $payment_amount,
					'transaction_id' => $transaction_id,
					'payment_method' => $payment_method,
					'note' => $payment_note,
					'invoice_label' => $invoice->invoice_title,
					'invoice_payable' => $invoice->payable,
					'student_record_id' => $invoice->student_id,
					'invoice_id' => $invoice_id,
					'school_id' => $school_id,
				);

				$success = $wpdb->insert(WLSM_PAYMENTS, $payment_data);

				$new_payment_id = $wpdb->insert_id;

				$buffer = ob_get_clean();
				if (!empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				$invoice_status = WLSM_M_Staff_Accountant::refresh_invoice_status($invoice_id);

				if (WLSM_M_Invoice::get_paid_key() === $invoice_status && ($invoice_status !== $invoice->status)) {
					$reload = true;
				} else {
					$reload = false;
				}

				$wpdb->query('COMMIT;');


				if (isset($new_payment_id)) {
					// Notify for offline fee submission.
					$data = array(
						'school_id' => $school_id,
						'session_id' => $session_id,
						'payment_id' => $new_payment_id,
					);

					wp_schedule_single_event(time() + 30, 'wlsm_notify_for_offline_fee_submission', $data);
				}

				wp_send_json_success(array('message' => $message, 'reset' => $reset, 'reload' => $reload));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function delete_invoice_payment()
	{
		$current_user = WLSM_M_Role::can('delete_payments');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$payment_id = isset($_POST['payment_id']) ? absint($_POST['payment_id']) : 0;

			if (!wp_verify_nonce($_POST['delete-payment-' . $payment_id], 'delete-payment-' . $payment_id)) {
				die();
			}

			$invoice_id = isset($_POST['invoice_id']) ? absint($_POST['invoice_id']) : 0;

			// Checks if invoice exists.
			$invoice = WLSM_M_Staff_Accountant::get_invoice($school_id, $session_id, $invoice_id);

			if (!$invoice) {
				throw new Exception(esc_html__('Invoice not found.', 'school-management-system'));
			}

			$invoice_id = $invoice->ID;

			// Checks if payment exists.
			$payment = WLSM_M_Staff_Accountant::get_invoice_payment($invoice_id, $payment_id);

			if (!$payment) {
				throw new Exception(esc_html__('Payment not found.', 'school-management-system'));
			}

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_PAYMENTS, array('ID' => $payment_id));
			$message = esc_html__('Payment deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (!empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$invoice_status = WLSM_M_Staff_Accountant::refresh_invoice_status($invoice_id);

			if (WLSM_M_Invoice::get_paid_key() === $invoice->status && ($invoice_status !== $invoice->status)) {
				$reload = true;
			} else {
				$reload = false;
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message, 'reload' => $reload));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function validate_invoice_payment($errors, $partial_payment, $due, $payment_amount, $payment_method)
	{
		if ($payment_amount <= 0) {
			$errors['payment_amount'] = esc_html__('Please provide a valid amount.', 'school-management-system');
		} else {
			if ($payment_amount > $due) {
				$errors['payment_amount'] = sprintf(
					/* translators: %s: payable amount */
					__('Amount cannot exceed payable amount: %s', 'school-management-system'),
					WLSM_Config::get_money_text($due)
				);
			}

			if (!$partial_payment && ($payment_amount != $due)) {
				$errors['payment_amount'] = sprintf(
					/* translators: %s: payable amount */
					__('Partial payment is not allowed. Amount must be equal to payable amount: %s', 'school-management-system'),
					WLSM_Config::get_money_text($due)
				);
			}
		}

		if (strlen($payment_method) > 50) {
			$errors['payment_method'] = esc_html__('Maximum length cannot exceed 50 characters.', 'school-management-system');
		}

		if (!in_array($payment_method, array_keys(WLSM_M_Invoice::collect_payment_methods()))) {
			$errors['payment_method'] = esc_html__('Please select a valid payment method.', 'school-management-system');
		}

		return $errors;
	}

	public static function fetch_payments()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$current_school = $current_user['school'];

		$can_delete_payments = WLSM_M_Role::check_permission(array('delete_payments'), $current_school['permissions']);

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		global $wpdb;

		$page_url = WLSM_M_Staff_Accountant::get_invoices_page_url();

		$query = WLSM_M_Staff_Accountant::fetch_payments_query($school_id, $session_id);

		$query_filter = $query;

		// Grouping.
		$group_by = ' ' . WLSM_M_Staff_Accountant::fetch_payments_query_group_by();

		$query .= $group_by;
		$query_filter .= $group_by;

		$condition = '';
		if (isset($_POST['search']['value'])) {
			$search_value = sanitize_text_field($_POST['search']['value']);
			if ('' !== $search_value) {
				$search_value_like = '%' . $wpdb->esc_like($search_value) . '%';

				$condition .= $wpdb->prepare(
					'(p.receipt_number LIKE %s) OR ' .
					'(p.amount LIKE %s) OR ' .
					'(p.transaction_id LIKE %s) OR ' .
					'(p.note LIKE %s) OR ' .
					'(sr.name LIKE %s) OR ' .
					'(sr.admission_number LIKE %s) OR ' .
					'(sr.father_name LIKE %s) OR ' .
					'(sr.father_phone LIKE %s) OR ' .
					'(sr.enrollment_number LIKE %s) OR ' .
					'(i.label LIKE %s) OR ' .
					'(c.label LIKE %s) OR ' .
					'(se.label LIKE %s)',
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like
				);

				$payment_method = strtolower(preg_replace('/[^A-Za-z0-9-]+/', '-', $search_value));
				if (isset($payment_method)) {
					$condition .= $wpdb->prepare(' OR (p.payment_method LIKE %s)', '%' . $payment_method . '%');
				}

				$created_at = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);
				if ($created_at) {
					$format_created_at = 'Y-m-d';
				} else {
					if ('d-m-Y' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('m-Y', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('d/m/Y' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('m/Y', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('Y-m-d' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('Y-m', $search_value);
						$format_created_at = 'Y-m';
					} elseif ('Y/m/d' === WLSM_Config::date_format()) {
						$created_at = DateTime::createFromFormat('Y/m', $search_value);
						$format_created_at = 'Y-m';
					}

					if (!$created_at) {
						$created_at = DateTime::createFromFormat('Y', $search_value);
						$format_created_at = 'Y';
					}
				}

				if ($created_at && isset($format_created_at)) {
					$created_at = $created_at->format($format_created_at);
					$condition .= $wpdb->prepare(' OR (p.created_at LIKE %s)', '%' . $wpdb->esc_like($created_at) . '%');
				}

				// Finally, construct the query condition
				$query_filter .= ' HAVING ' . $condition;
			}
		}

		// Ordering.
		$columns = array('p.receipt_number', 'p.amount', 'p.payment_method', 'p.transaction_id', 'p.created_at', 'p.note', 'i.label', 'sr.name', 'sr.admission_number', 'c.label', 'se.label', 'sr.enrollment_number', 'sr.phone', 'sr.father_name', 'sr.father_phone');
		if (isset($_POST['order']) && esc_sql($_POST['order']) && isset($columns[$_POST['order']['0']['column']]) && esc_sql($columns[$_POST['order']['0']['column']])) {
			$order_by = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = sanitize_text_field(esc_sql($_POST['order']['0']['dir']));

			$query_filter .= ' ORDER BY ' . $order_by . ' ' . $order_dir;
		} else {
			$query_filter .= ' ORDER BY p.ID DESC';
		}

		// Limiting.
		$limit = '';
		if (-1 != $_POST['length']) {
			$start = absint(esc_sql($_POST['start']));
			$length = absint(esc_sql($_POST['length']));

			$limit = ' LIMIT ' . $start . ', ' . $length;
		}

		// Total query.
		$rows_query = WLSM_M_Staff_Accountant::fetch_payments_query_count($school_id, $session_id);

		// Total rows count.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
		$total_rows_count = $wpdb->get_var($rows_query);

		// Filtered rows count.
		// Filtered rows count.
		if ($condition) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic components are built from safely prepared fragments.
			$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
		} else {
			$filter_rows_count = $total_rows_count;
		}

		// Filtered limit rows.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
		$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

		$data = array();
		if (count($filter_rows_limit)) {
			foreach ($filter_rows_limit as $row) {
				if ($row->invoice_id) {
					$invoice_title = '<a target="_blank" href="' . esc_url($page_url . '&action=save&id=' . $row->invoice_id) . '">' . esc_html(WLSM_M_Staff_Accountant::get_invoice_title_text($row->invoice_title)) . '</a>';
				} else {
					$invoice_title = '<span class="text-danger">' . esc_html__('Deleted', 'school-management-system') . '<br><span class="text-secondary">' . esc_html(WLSM_M_Staff_Accountant::get_invoice_title_text($row->invoice_label)) . '<br><small>' . esc_html(WLSM_Config::get_money_text($row->invoice_payable)) . ' ' . esc_html__('Payable', 'school-management-system') . '</small></span></span>';
				}

				if ($row->note) {
					$view_note = '<a class="text-primary wlsm-view-payment-note" data-nonce="' . esc_attr(wp_create_nonce('view-payment-note-' . $row->ID)) . '" data-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Payment Note', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><span class="dashicons dashicons-search"></span></a>';
				} else {
					$view_note = '-';
				}

				// Table columns.
				$columns = array(
					esc_html(WLSM_M_Invoice::get_receipt_number_text($row->receipt_number)),
					esc_html(WLSM_Config::get_money_text($row->amount)),
					esc_html(WLSM_M_Invoice::get_payment_method_text($row->payment_method)),
					esc_html(WLSM_M_Invoice::get_transaction_id_text($row->transaction_id)),
					esc_html(WLSM_Config::get_date_text($row->created_at)),
					$view_note,
					$invoice_title,
					esc_html(WLSM_M_Staff_Class::get_name_text($row->student_name)),
					esc_html(WLSM_M_Staff_Class::get_admission_no_text($row->admission_number)),
					esc_html(WLSM_M_Class::get_label_text($row->class_label)),
					esc_html(WLSM_M_Staff_Class::get_section_label_text($row->section_label)),
					esc_html($row->enrollment_number),
					esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->father_name)),
					esc_html(WLSM_M_Staff_Class::get_phone_text($row->father_phone)),
					'<a class="text-success wlsm-print-invoice-payment" data-nonce="' . esc_attr(wp_create_nonce('print-invoice-payment-' . $row->ID)) . '" data-invoice-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Print Payment Receipt', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><i class="fas fa-print"></i></a>'
				);

				if ($can_delete_payments) {
					$columns[] = '<a class="text-danger wlsm-delete-payment" data-nonce="' . esc_attr(wp_create_nonce('delete-payment-' . $row->ID)) . '" data-payment="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . esc_attr__('This will delete the payment.', 'school-management-system') . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>';
				}

				$data[] = $columns;
			}
		}

		$output = array(
			'draw' => intval(esc_sql($_POST['draw'])),
			'recordsTotal' => $total_rows_count,
			'recordsFiltered' => $filter_rows_count,
			'data' => $data,
		);

		echo json_encode($output);
		die;
	}

	public static function delete_payment()
	{
		$current_user = WLSM_M_Role::can('delete_payments');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$payment_id = isset($_POST['payment_id']) ? absint($_POST['payment_id']) : 0;

			if (!wp_verify_nonce($_POST['delete-payment-' . $payment_id], 'delete-payment-' . $payment_id)) {
				die();
			}

			// Checks if payment exists.
			$payment = WLSM_M_Staff_Accountant::get_payment($school_id, $session_id, $payment_id);

			if (!$payment) {
				throw new Exception(esc_html__('Payment not found.', 'school-management-system'));
			}

			$invoice_id = $payment->invoice_id;

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_PAYMENTS, array('ID' => $payment_id));
			$message = esc_html__('Payment deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (!empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			if ($invoice_id) {
				$invoice_status = WLSM_M_Staff_Accountant::refresh_invoice_status($invoice_id);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function view_payment_note()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$payment_id = isset($_POST['payment_id']) ? absint($_POST['payment_id']) : 0;

			if (!wp_verify_nonce($_POST['view-payment-note-' . $payment_id], 'view-payment-note-' . $payment_id)) {
				die();
			}

			// Checks if payment exists.
			$payment = WLSM_M_Staff_Accountant::get_payment_note($school_id, $session_id, $payment_id);

			if (!$payment) {
				throw new Exception(esc_html__('Payment not found.', 'school-management-system'));
			}

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		wp_send_json_success(esc_html(WLSM_Config::get_note_text($payment->note)));
	}

	public static function print_payment()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$payment_id = isset($_POST['payment_id']) ? absint($_POST['payment_id']) : 0;

			if (!wp_verify_nonce($_POST['print-invoice-payment-' . $payment_id], 'print-invoice-payment-' . $payment_id)) {
				die();
			}

			// Checks if payment exists.
			$payment = WLSM_M_Staff_Accountant::fetch_payment($school_id, $session_id, $payment_id);

			if (!$payment) {
				throw new Exception(esc_html__('Payment not found.', 'school-management-system'));
			}

		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		ob_start();
		require_once WLSM_PLUGIN_DIR_PATH . 'admin/inc/school/print/payment.php';
		$html = ob_get_clean();

		wp_send_json_success(array('html' => $html));
	}
}
