JP, the HAVING clause has no effect in this query, because the estimateddate is part of the GROUP BY clause (and the MIN function is evaluated for each group and, of course, each group has only one date).
I think the query should be written like this:
SELECT r.roofid, c1.estimatedcost as CapCost, c1.estimateddate FROM tblcapitalexpenditures c1 INNER JOIN tblroofs r ON c1.RoofID = r.RoofID WHERE r.CompanyID = @CompanyID AND YEAR(cap.estimateddate)> YEAR(GETDATE())+1 AND c1.estimateddate = ( SELECT MIN(c2.estimateddate) FROM tblcapitalexpenditures c2 WHERE c1.RoofID = c2.RoofID )
I have replaced the outer join with an inner join because an outer join seems unnecessary to me. If it is really needed (if it is possible that a "roof" has no "capital expenditures" and you want that "roof" selected), the query would be:
SELECT r.roofid, c1.estimatedcost as CapCost, c1.estimateddate FROM tblcapitalexpenditures c1 RIGHT JOIN tblroofs r ON c1.RoofID = r.RoofID WHERE r.CompanyID = @CompanyID AND YEAR(cap.estimateddate)> YEAR(GETDATE())+1 AND (c1.estimateddate IS NULL OR c1.estimateddate = ( SELECT MIN(c2.estimateddate) FROM tblcapitalexpenditures c2 WHERE c1.RoofID = c2.RoofID ))
Razvan