Counting pending bookings using a subselect Can this query be improved? Is there a way to eliminate the duplicate function call?

-- a Special Group has many Items which have many Bookings
create function f_BookingsForSpecialGroup(@specialGroupId varchar(99))
returns table as
    return select * from v_Booking where ITEM_CODE in
        (select ITEM_CODE from v_Item i where i.SPECIAL_GROUP_ID = @specialGroupId);
go


create view v_SpecialGroup as
select (NON_BAD_BOOKINGS - PAID_BOOKINGS) as PENDING_BOOKINGS, * from
(
select
    (select count(*) from f_BookingsForSpecialGroup(g.SPECIAL_GROUP_ID) where IS_BAD=0) as NON_BAD_BOOKINGS,
    (select count(*) from f_BookingsForSpecialGroup(g.SPECIAL_GROUP_ID) where IS_PAID=1) as PAID_BOOKINGS,
    *
from SPECIAL_GROUP g
) g
go


The view v_SpecialGroup is never queried directly by the application; it is used to build other views which select individual columns as needed. (You can think of v_SpecialGroup as a "base view" which exists solely to augment the SPECIAL_GROUP table. I have profiled this strategy and it seems that if you don't select the more expensive columns you don't pay for them, but I could be wrong of course...)
Yes, there is a way to eliminate the repeated function call, but as to whether or not it would improve performance you need to benchmark it. This method writes the resulting data into a temporary table, which means it will call the function only once when populating it, but in turn will mean that it will have to write the table to tempdb for that session. Here is how it could be written that way:
if object_id('tempdb..#SpecialBookings') is not null
    drop table tempdb..#SpecialBookings;

select *
into #SpecialBookings
from SPECIAL_GROUP as grp
    cross apply f_BookingsForSpecialGroup(grp.SPECIAL_GROUP_ID) as bookings
where (bookings.IS_BAD = 0 or bookings.IS_PAID = 1);

select (grpCounted.NON_BAD_BOOKINGS - grpCounted.PAID_BOOKINGS) as PENDING_BOOKINGS, * 
from 
(
    select
        (select count(*) from #SpecialBookings where IS_BAD=0) as NON_BAD_BOOKINGS,
        (select count(*) from #SpecialBookings where IS_PAID=1) as PAID_BOOKINGS,
        *
    from #SpecialBookings
) as grpCounted;

if object_id('tempdb..#SpecialBookings') is not null
    drop table tempdb..#SpecialBookings;


select *
Do you really need all the fields from both the SPECIAL_GROUP table and the f_BookingsForSpecialGroup? If you do really need them, then fair enough, but otherwise, you should not use select *.

Aliases
As you can see I renamed your table aliases a bit to make your code easier to read. Whenever possible, avoid single-letter or otherwise meaningless aliases as they make the code less clear.