You can use MERGE along with the OUTPUT clause to capture the mapping of new to old data, e.g.
DECLARE @CustomerMapping TABLE (OldCustomerID INT NOT NULL, NewCustomerID INT NOT NULL);
MERGE Customers AS t
USING
( SELECT CustomerID, Name, Age
FROM Customers
WHERE Age > 18
) AS s
ON 1 = 0 -- THIS WILL NEVER BE TRUE SO WILL ALWAYS BE "NOT MATCHED"
WHEN NOT MATCHED THEN
INSERT (Name, Age)
VALUES (s.Name, s.Age)
OUTPUT s.CustomerID, inserted.CustomerID
INTO @CustomerMapping (OldCustomerID, NewCustomerID);
You can then use this mapping of ID's to repeat the same for your invoices:
DECLARE @InvoiceMapping TABLE (OldInvoiceID INT NOT NULL, NewInvoiceID INT NOT NULL);
MERGE Invoices AS t
USING
( SELECT i.InvoiceID,
CustomerID = m.NewCustomerID,
i.Date
FROM Invoices AS i
INNER JOIN @CustomerMapping AS m
ON m.OldCustomerID = i.CustomerID
) AS s
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (CustomerID, Date)
VALUES (s.CustomerID, s.Date)
OUTPUT s.InvoiceID, inserted.InvoiceID
INTO @InvoiceMapping (OldInvoiceID, NewInvoiceID);
You can then use your invoice mappings to insert your line items:
INSERT LineItems (InvoiceID, Item, Price, Qty)
SELECT m.NewInvoceID,
li.Item,
li.Price,
li.Qty
FROM LineItems AS li
INNER JOIN @InvoiceMapping AS m
ON m.OldInvoiceID = li.InvoiceID;
A standard INSERT will do here since the output is not required (unless LineItems have further children, in which case you'll need to use MERGE again and capture the output as before.
It is necessary to do the fake MERGE that only inserts in order to capture both the new and the old IDs, with a normal INSERT you only have access to the inserted fields in the OUTPUT clause.
There are always (justifiable) concerns about using MERGE in SQL Server due to the number of bugs, however none of the bugs that I am aware of impact this scenario as long as you are using a table as the target and only inserting new data.
Example on db<>Fiddle