You're in non-deterministic territory using user-defined variables in a query that changes them, and the explanation is straightforward: the answer you get is actually from the previous run of the same query.
UDVs are scoped to your individual database connection. Their values persist between queries, but not across connections. This query is giving you the value of @UsedSales + @NewSales from before the query is run, not after. (Why? Because it just is. There is not a reason... it could go either way. See below.)
SET @UsedSales = 1, @NewSales = 2; and run your query again. Total will be 3 in the next run, clearly the wrong answer (compared to what you expected) but not wrong in the sense that the server is free to resolve these in any order it likes, because they look like constants.
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
...
For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed.
...
The order of evaluation for expressions involving user variables is undefined.
https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
You're trying to solve a problem that isn't really a problem.
Instead, either do this:
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
SUM(CASE WHEN `v`.`new_used` IN ('N','U') THEN 1 ELSE 0 END) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
Or if you insist, make a derived table (here, named x) and do the extra addition of the resulting columns.
SELECT
x.NewSales,
x.UsedSales,
x.NewSales + x.UsedSales AS TotalSales
FROM (
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
) x;
This materializes the inner result into a temporary table that is discarded as soon as the query finishes executing.
Or, if you really want clever and short, go with this:
SELECT
COUNT(`v`.`new_used`='N' OR NULL) AS `NewSales`,
COUNT(`v`.`new_used`='U' OR NULL) AS `UsedSales`,
COUNT(`v`.`new_used` IN ('N','U') OR NULL) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
This works, because COUNT() counts only rows with non-null arguments, and any expression expr OR NULL coerces expr to be evaluated as a boolean expression, and thus is logically equivalent to CASE WHEN expr [IS TRUE] THEN 1 ELSE NULL END and thus can only evaluate to 1 (if expr is truthy)... or NULL (if expr is either false or null)... which an exact match for how COUNT() works.