Você ainda não forneceu informações completas - nenhuma tabela de lotes, mesmo a tabela de receitas não existente. De qualquer forma, suponho que não nos importamos com o que está na tabela de lotes, digamos que seja apenas o nome e o id. Sua tabela de recibos tem várias linhas para o mesmo aluno. Isso deve resultar em várias linhas retornadas para as outras tabelas também, devido a todos os JOINs. Portanto, você SUM() várias vezes os valores que devem ser somados apenas uma vez, ou seja, open_balance. Isso pode ser uma pista de onde está o problema, eu diria que você precisa mover as informações necessárias da 'tabela de recibos para as subconsultas, mas não tenho certeza se você nos mostrou a totalidade do seu banco de dados. Tente remover a tabela de recibos da consulta e verifique os resultados novamente. Se for isso, você deve ver o que fazer a partir daí ou pelo menos nos dar mais informações.
EDITAR: A consulta deve ser:
SELECT
b.name AS batch_name,
b.id AS batch_id,
COUNT(DISTINCT s.id)
AS total_students,
COALESCE( SUM(s.open_bal), 0 )
AS open_balance,
SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
) AS gross_fee,
SUM( COALESCE(i.discount, 0) )
AS discount,
COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
AS net_payable,
SUM((SELECT SUM(COALESCE(receipts.reg_fee, 0)
+ COALESCE(receipts.tut_fee, 0)
+ COALESCE(receipts.other_fee, 0)) FROM receipts WHERE receipts.student_id = s.id))
AS net_recieved,
( COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
)
- SUM((SELECT SUM(COALESCE(receipts.reg_fee, 0)
+ COALESCE(receipts.tut_fee, 0)
+ COALESCE(receipts.other_fee, 0)) FROM receipts WHERE receipts.student_id = s.id))
AS balance_due
FROM batches b
LEFT JOIN students s ON s.batch = b.id
LEFT JOIN invoices i ON i.student_id = s.id
WHERE s.inactive = 0
GROUP BY b.name, b.id;
Isso somará os dados dos alunos na tabela de recibos, mesmo que esteja em mais de uma linha, retornando apenas uma linha. A remoção da junção à tabela de recebimentos remove as linhas duplicadas das outras tabelas, portanto, os cálculos agora devem estar corretos.
Mais uma coisa - você tem
s.inactive = 0
na cláusula WHERE, certifique-se de que não seja relevante para esses cálculos. P.S. Como é que você não sabe o que é uma subconsulta e acaba escrevendo coisas assim?