PaymentStates fix

This commit is contained in:
Michał Zieliński
2025-08-26 11:16:43 +02:00
parent 4f8812117c
commit f63dd0c7e2

View File

@@ -18,22 +18,20 @@ $db->query('FLUSH QUERY CACHE');
// Initialize variables with defaults // Initialize variables with defaults
$account_type = ''; $account_type = '';
$saldo_type = ''; $saldo_type = '';
$and = '';
$type = '';
$data = array(); $data = array();
$sum = array(); $sum = array();
$idToPdf = ''; $idToPdf = '';
if (!empty($_REQUEST['submit'])) { if (!empty($_REQUEST['submit'])) {
// Sanitize and validate inputs // Sanitize and validate inputs
$account_type = isset($_REQUEST['account_type']) ? $db->quote($_REQUEST['account_type']) : ''; $account_type = isset($_REQUEST['account_type']) ? trim($_REQUEST['account_type']) : '';
if ($account_type == 'a') { if ($account_type == 'a') {
$account_type = ''; $account_type = '';
} }
$saldo_type = isset($_REQUEST['saldo_type']) ? $db->quote($_REQUEST['saldo_type']) : ''; $saldo_type = isset($_REQUEST['saldo_type']) ? trim($_REQUEST['saldo_type']) : '';
$type2 = isset($_REQUEST['type2']) ? $db->quote($_REQUEST['type2']) : ''; $type2 = isset($_REQUEST['type2']) ? $db->quote(trim($_REQUEST['type2'])) : '';
$user_id = isset($_REQUEST['user_id']) ? $db->quote($_REQUEST['user_id']) : ''; $user_id = isset($_REQUEST['user_id']) ? $db->quote(trim($_REQUEST['user_id'])) : '';
// Build WHERE conditions safely // Build WHERE conditions safely
$conditions = array("t.deleted='0'"); $conditions = array("t.deleted='0'");
@@ -43,7 +41,7 @@ if (!empty($_REQUEST['submit'])) {
} }
if (!empty($type2)) { if (!empty($type2)) {
$conditions[] = "account_type2='" . $type2 . "'"; $conditions[] = "a.account_type2='" . $type2 . "'";
} }
if (!empty($user_id)) { if (!empty($user_id)) {
@@ -53,7 +51,7 @@ if (!empty($_REQUEST['submit'])) {
// Build account type condition // Build account type condition
$account_condition = ''; $account_condition = '';
if ($account_type != '') { if ($account_type != '') {
$account_condition = " AND (a.account_type='rs' OR a.account_type = '" . $account_type . "')"; $account_condition = " AND (a.account_type='rs' OR a.account_type = '" . $db->quote($account_type) . "')";
} }
// Optimized query - get all needed data in one go // Optimized query - get all needed data in one go
@@ -69,16 +67,33 @@ if (!empty($_REQUEST['submit'])) {
ORDER BY a.index_dbf"; ORDER BY a.index_dbf";
$clients = $db->query($query); $clients = $db->query($query);
$client_ids = array();
$clients_data = array();
// First pass - collect all client IDs and data
while ($c = $db->fetchByAssoc($clients)) {
if (!empty($c['parent_id'])) {
$client_ids[] = $c['parent_id'];
$clients_data[] = $c;
}
}
// Get all financial data in one batch
$financial_batch = getAllFinancialData($client_ids);
// Cache currency objects to avoid repeated database calls // Cache currency objects to avoid repeated database calls
$currency_cache = array(); $currency_cache = array();
while ($c = $db->fetchByAssoc($clients)) { foreach ($clients_data as $c) {
// Skip invalid accounts $parent_id = $c['parent_id'];
if (empty($c['parent_id'])) continue;
// Skip if no financial data
if (!isset($financial_batch[$parent_id])) {
continue;
}
$row = array(); $row = array();
$row['id'] = $c['parent_id']; $row['id'] = $parent_id;
$row['name'] = $c['account_name']; $row['name'] = $c['account_name'];
$row['index'] = $c['index_dbf']; $row['index'] = $c['index_dbf'];
@@ -91,16 +106,17 @@ if (!empty($_REQUEST['submit'])) {
} }
$row['currency_id'] = $currency_cache[$currency_id]; $row['currency_id'] = $currency_cache[$currency_id];
// Get financial data (these functions need optimization too) // Use pre-calculated financial data
$row['unsettled'] = getData($c['parent_id'], 0); $fin_data = $financial_batch[$parent_id];
$row['not_overdue'] = getData($c['parent_id'], 7); $row['unsettled'] = $fin_data['unsettled'];
$row['overdue'] = getData($c['parent_id'], 1); $row['not_overdue'] = $fin_data['not_overdue'];
$row['2'] = getData2($c['parent_id'], 2); $row['overdue'] = $fin_data['overdue'];
$row['3'] = getData2($c['parent_id'], 3); $row['2'] = $fin_data['2'];
$row['4'] = getData2($c['parent_id'], 4); $row['3'] = $fin_data['3'];
$row['5'] = getData2($c['parent_id'], 5); $row['4'] = $fin_data['4'];
$row['6'] = getData2($c['parent_id'], 6); $row['5'] = $fin_data['5'];
$row['saldo'] = getData($c['parent_id'], 8); $row['6'] = $fin_data['6'];
$row['saldo'] = $fin_data['saldo'];
$row['today_saldo'] = 0; $row['today_saldo'] = 0;
// Apply saldo filters // Apply saldo filters
@@ -143,12 +159,15 @@ if (!empty($_REQUEST['submit'])) {
} }
} }
// Get user list with single query // Get user list with single optimized query
$user_list = array(); $user_list = array();
$users_query = "SELECT id, first_name, last_name FROM users WHERE deleted=0"; $users_query = "SELECT id, CONCAT(COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) as full_name
FROM users
WHERE deleted=0
ORDER BY first_name, last_name";
$users_result = $db->query($users_query); $users_result = $db->query($users_query);
while ($user_data = $db->fetchByAssoc($users_result)) { while ($user_data = $db->fetchByAssoc($users_result)) {
$user_list[$user_data['id']] = trim($user_data['first_name'] . ' ' . $user_data['last_name']); $user_list[$user_data['id']] = trim($user_data['full_name']);
} }
// Initialize Smarty template // Initialize Smarty template
@@ -158,7 +177,7 @@ global $mod_strings, $app_list_strings;
$ss->assign("MOD", $mod_strings); $ss->assign("MOD", $mod_strings);
$ss->assign("DATA", $data); $ss->assign("DATA", $data);
$ss->assign("SUM", $sum); $ss->assign("SUM", $sum);
$ss->assign("SORT", array()); // Initialize empty sort array $ss->assign("SORT", array());
$ss->assign("account_type", $account_type); $ss->assign("account_type", $account_type);
$app_list_strings['account_type_dom']['a'] = 'Wszystkie'; $app_list_strings['account_type_dom']['a'] = 'Wszystkie';
@@ -173,245 +192,177 @@ $ss->assign("users", $user_list);
echo $ss->display('modules/EcmPaymentStates/tpls/summary1.tpl'); echo $ss->display('modules/EcmPaymentStates/tpls/summary1.tpl');
// OPTIMIZED HELPER FUNCTIONS // OPTIMIZED CORE FUNCTION - Gets all financial data in one query
function getData($id, $expired) { function getAllFinancialData($client_ids) {
static $cache = array(); static $global_cache = null;
$cache_key = $id . '_' . $expired;
if (isset($cache[$cache_key])) { if ($global_cache !== null && !empty($global_cache)) {
return $cache[$cache_key]; return $global_cache;
}
if (empty($client_ids)) {
return array();
} }
$db = $GLOBALS['db']; $db = $GLOBALS['db'];
$today = date('Y-m-d'); $today = date('Y-m-d');
$ids_string = "'" . implode("','", array_unique($client_ids)) . "'";
$settled_condition = "t.settled!='1' AND"; // Single comprehensive query for all financial calculations
$date_condition = "1=1"; $comprehensive_query = "
$is_saldo = false; SELECT
t.parent_id,
switch ($expired) { t.type,
case 0: t.settled,
$date_condition = "1=1"; t.value,
break; t.payment_date,
case 1: COALESCE(settled_data.settled_amount, 0) as settled_amount
$date_condition = "t.payment_date <= '$today'"; FROM ecmtransactions t
$is_saldo = true; LEFT JOIN (
break; SELECT
case 7: parent_trans.id as transaction_id,
$date_condition = "t.payment_date >= '$today'"; SUM(
break; CASE
case 8: WHEN parent_trans.type = '0' AND rel_trans.type = '0' AND parent_trans.value < 0 AND r.value < 0
case 9: THEN ABS(r.value)
$date_condition = "t.payment_date <= '$today'"; WHEN parent_trans.type = '0'
$settled_condition = "1=1 AND"; THEN r.value
$is_saldo = true; ELSE ABS(r.value)
break; END
default: ) as settled_amount
return getData2($id, $expired); FROM ecmtransactions parent_trans
} LEFT JOIN ecmtransactions_rel r ON (r.ecmtransaction_a_id = parent_trans.id OR r.ecmtransaction_b_id = parent_trans.id)
LEFT JOIN ecmtransactions rel_trans ON (
// Get type 1 transactions (MA) rel_trans.id = CASE
$query_ma = " WHEN r.ecmtransaction_a_id = parent_trans.id THEN r.ecmtransaction_b_id
SELECT SUM(t.value) AS sum ELSE r.ecmtransaction_a_id
FROM ecmtransactions AS t END
WHERE t.parent_id = '$id' AND rel_trans.deleted = '0'
AND t.deleted='0' )
AND $settled_condition WHERE parent_trans.parent_id IN ($ids_string)
t.type='1' AND parent_trans.deleted = '0'
AND $date_condition GROUP BY parent_trans.id
) settled_data ON settled_data.transaction_id = t.id
WHERE t.parent_id IN ($ids_string)
AND t.deleted = '0'
ORDER BY t.parent_id, t.payment_date
"; ";
$result_ma = $db->query($query_ma); $result = $db->query($comprehensive_query);
$ma_sum = 0; $financial_data = array();
if ($row = $db->fetchByAssoc($result_ma)) {
$ma_sum = floatval($row['sum']); while ($row = $db->fetchByAssoc($result)) {
$parent_id = $row['parent_id'];
$type = $row['type'];
$settled = $row['settled'];
$value = floatval($row['value']);
$settled_amount = floatval($row['settled_amount']);
$payment_date = $row['payment_date'];
if (!isset($financial_data[$parent_id])) {
$financial_data[$parent_id] = array(
'unsettled' => 0,
'not_overdue' => 0,
'overdue' => 0,
'2' => 0, '3' => 0, '4' => 0, '5' => 0, '6' => 0,
'saldo' => 0
);
}
// Calculate effective value based on settlement
$effective_value = ($settled == '1') ? $value : ($value - $settled_amount);
// Determine date category
$days_diff = (strtotime($today) - strtotime($payment_date)) / (60 * 60 * 24);
// Apply business logic based on type and date
if ($type == '1') { // MA (receivable)
$financial_data[$parent_id]['saldo'] += $effective_value;
if ($days_diff < 0) { // Future date
$financial_data[$parent_id]['not_overdue'] += $effective_value;
}
if ($settled != '1') {
$financial_data[$parent_id]['unsettled'] += $effective_value;
}
} else { // WINIEN (payable)
$financial_data[$parent_id]['saldo'] -= $effective_value;
if ($days_diff >= 0 && $settled != '1') { // Past due and not settled
$financial_data[$parent_id]['overdue'] += $effective_value;
}
if ($settled != '1') {
$financial_data[$parent_id]['unsettled'] += $effective_value;
}
}
// Categorize by aging periods (only for non-settled)
if ($settled != '1') {
if ($days_diff >= 1 && $days_diff <= 30) {
$financial_data[$parent_id]['2'] += $effective_value;
} elseif ($days_diff >= 31 && $days_diff <= 60) {
$financial_data[$parent_id]['3'] += $effective_value;
} elseif ($days_diff >= 61 && $days_diff <= 90) {
$financial_data[$parent_id]['4'] += $effective_value;
} elseif ($days_diff >= 91 && $days_diff <= 180) {
$financial_data[$parent_id]['5'] += $effective_value;
} elseif ($days_diff > 180) {
$financial_data[$parent_id]['6'] += $effective_value;
}
}
} }
if (!$is_saldo) { $global_cache = $financial_data;
$cache[$cache_key] = $ma_sum; return $financial_data;
return $ma_sum;
}
// Get type 0 transactions (WINIEN) for saldo calculation
$query_winien = "
SELECT SUM(t.value) AS sum
FROM ecmtransactions AS t
WHERE t.parent_id = '$id'
AND t.deleted='0'
AND $settled_condition
t.type='0'
AND $date_condition
";
$result_winien = $db->query($query_winien);
$winien_sum = 0;
if ($row = $db->fetchByAssoc($result_winien)) {
$winien_sum = floatval($row['sum']);
}
$result = $ma_sum - $winien_sum;
// Special case for expired = 1
if ($expired == 1 && $result > 0) {
$result = 0;
}
$cache[$cache_key] = $result;
return $result;
} }
function getData2($id, $expired, $dates = null) { // Legacy function wrappers for backward compatibility (now use cached data)
static $cache = array(); function getData($id, $expired) {
$cache_key = $id . '_' . $expired . '_' . $dates; static $batch_data = null;
if (isset($cache[$cache_key])) { if ($batch_data === null) {
return $cache[$cache_key]; $batch_data = getAllFinancialData(array($id));
} }
$db = $GLOBALS['db']; if (!isset($batch_data[$id])) {
// Prepare date conditions based on expired type
$date_conditions = getDateConditions($expired, $dates);
if ($date_conditions === false) {
return 0; return 0;
} }
$settled_condition = ($expired == 9) ? "" : "t.settled!='1' AND"; $data = $batch_data[$id];
// Get type 0 transactions (WINIEN)
$query_winien = "
SELECT SUM(t.value) AS sum
FROM ecmtransactions AS t
WHERE t.parent_id = '$id'
AND t.deleted='0'
AND $settled_condition
t.type='0'
$date_conditions
";
$result_winien = $db->query($query_winien);
$winien_sum = 0;
if ($row = $db->fetchByAssoc($result_winien)) {
$winien_sum = floatval($row['sum']);
}
// Calculate settled amounts for type 0
$settled_winien = 0;
$rel_query_winien = "
SELECT r.value
FROM ecmtransactions AS t
LEFT JOIN ecmtransactions_rel AS r ON (r.ecmtransaction_a_id=t.id OR r.ecmtransaction_b_id=t.id)
LEFT JOIN ecmtransactions AS t2 ON (
t2.id = CASE
WHEN r.ecmtransaction_a_id = t.id THEN r.ecmtransaction_b_id
ELSE r.ecmtransaction_a_id
END
)
WHERE t.parent_id = '$id'
AND t.deleted='0'
AND $settled_condition
t.type='0'
$date_conditions
AND t2.deleted='0'
";
if ($expired == 9 && $dates) {
$target_date = date('Y-m-d', strtotime($dates));
$rel_query_winien .= " AND t2.payment_date <= '$target_date'";
}
$rel_result_winien = $db->query($rel_query_winien);
while ($rel_row = $db->fetchByAssoc($rel_result_winien)) {
$settled_winien += floatval($rel_row['value']);
}
// Get type 1 transactions (MA)
$query_ma = "
SELECT SUM(t.value) AS sum
FROM ecmtransactions AS t
WHERE t.parent_id = '$id'
AND t.deleted='0'
AND $settled_condition
t.type='1'
$date_conditions
";
$result_ma = $db->query($query_ma);
$ma_sum = 0;
if ($row = $db->fetchByAssoc($result_ma)) {
$ma_sum = floatval($row['sum']);
}
// Calculate settled amounts for type 1
$settled_ma = 0;
$rel_query_ma = "
SELECT r.value
FROM ecmtransactions AS t
LEFT JOIN ecmtransactions_rel AS r ON (r.ecmtransaction_a_id=t.id OR r.ecmtransaction_b_id=t.id)
LEFT JOIN ecmtransactions AS t2 ON (
t2.id = CASE
WHEN r.ecmtransaction_a_id = t.id THEN r.ecmtransaction_b_id
ELSE r.ecmtransaction_a_id
END
)
WHERE t.parent_id = '$id'
AND t.deleted='0'
AND $settled_condition
t.type='1'
$date_conditions
AND t2.deleted='0'
";
if ($expired == 9 && $dates) {
$target_date = date('Y-m-d', strtotime($dates));
$rel_query_ma .= " AND t2.payment_date <= '$target_date'";
}
$rel_result_ma = $db->query($rel_query_ma);
while ($rel_row = $db->fetchByAssoc($rel_result_ma)) {
$settled_ma += abs(floatval($rel_row['value']));
}
$result_value = $settled_ma - $winien_sum + $ma_sum - $settled_winien;
$cache[$cache_key] = $result_value;
return $result_value;
}
function getDateConditions($expired, $dates = null) {
$today = date('Y-m-d');
switch ($expired) { switch ($expired) {
case 1: case 0: return $data['unsettled']; // All unsettled
return "AND t.payment_date <= '$today'"; case 1: return $data['overdue']; // Overdue
case 2: case 7: return $data['not_overdue']; // Not overdue
$date_from = date('Y-m-d', strtotime('-30 days')); case 8:
return "AND t.payment_date >= '$date_from' AND t.payment_date < '$today'"; case 9: return $data['saldo']; // Saldo
case 3: default: return getData2($id, $expired);
$date_to = date('Y-m-d', strtotime('-31 days'));
$date_from = date('Y-m-d', strtotime('-60 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 4:
$date_to = date('Y-m-d', strtotime('-61 days'));
$date_from = date('Y-m-d', strtotime('-90 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 5:
$date_to = date('Y-m-d', strtotime('-91 days'));
$date_from = date('Y-m-d', strtotime('-180 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 6:
$date = date('Y-m-d', strtotime('-181 days'));
return "AND t.payment_date <= '$date'";
case 9:
$target_date = $dates ? date('Y-m-d', strtotime($dates)) : $today;
return "AND t.payment_date <= '$target_date'";
default:
return "AND 1=1";
} }
} }
function getData2($id, $expired, $dates = null) {
static $batch_data = null;
if ($batch_data === null) {
$batch_data = getAllFinancialData(array($id));
}
if (!isset($batch_data[$id])) {
return 0;
}
$data = $batch_data[$id];
switch ($expired) {
case 2: return $data['2']; // 1-30 days
case 3: return $data['3']; // 31-60 days
case 4: return $data['4']; // 61-90 days
case 5: return $data['5']; // 91-180 days
case 6: return $data['6']; // 180+ days
case 9: return $data['saldo']; // Saldo with date
default: return 0;
}
}
// Comparison functions (compatible with PHP 5.6) // Comparison functions (PHP 5.6 compatible)
function cmpUnsettled($a, $b) { function cmpUnsettled($a, $b) {
if ($a['unsettled'] == $b['unsettled']) return 0; if ($a['unsettled'] == $b['unsettled']) return 0;
return ($a['unsettled'] < $b['unsettled']) ? -1 : 1; return ($a['unsettled'] < $b['unsettled']) ? -1 : 1;
@@ -470,7 +421,34 @@ function cmpTodaySaldoDesc($a, $b) {
return ($a['today_saldo'] < $b['today_saldo']) ? 1 : -1; return ($a['today_saldo'] < $b['today_saldo']) ? 1 : -1;
} }
// Legacy functions (marked for removal - currently unused) // Helper function for date conditions (now unused but kept for compatibility)
function getSaldo($id) { /* Implementation removed - function appears unused */ } function getDateConditions($expired, $dates = null) {
function unsettledFormatValue($settled, $val) { /* Implementation removed - function appears unused */ } $today = date('Y-m-d');
function unsettledValue($settled, $val) { /* Implementation removed - function appears unused */ }
switch ($expired) {
case 1: return "AND t.payment_date <= '$today'";
case 2:
$date_from = date('Y-m-d', strtotime('-30 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date < '$today'";
case 3:
$date_to = date('Y-m-d', strtotime('-31 days'));
$date_from = date('Y-m-d', strtotime('-60 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 4:
$date_to = date('Y-m-d', strtotime('-61 days'));
$date_from = date('Y-m-d', strtotime('-90 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 5:
$date_to = date('Y-m-d', strtotime('-91 days'));
$date_from = date('Y-m-d', strtotime('-180 days'));
return "AND t.payment_date >= '$date_from' AND t.payment_date <= '$date_to'";
case 6:
$date = date('Y-m-d', strtotime('-181 days'));
return "AND t.payment_date <= '$date'";
case 9:
$target_date = $dates ? date('Y-m-d', strtotime($dates)) : $today;
return "AND t.payment_date <= '$target_date'";
default:
return "AND 1=1";
}
}