Thank you for the quality of your answer, it's very clear what you want. A stored procedure is not needed but it's very complex. As you have spend a lot of time to ask your question I will spend a lot of time to answer your question.
First we have to understand that we need 2 querys as we have two situations, 1 is with a concern date and 1 is without. The last one is simple.
I've adjusted your example a little bit as concernfrom can contain a period with more then 2 months. I'm not sure it will but if it's happening your query isn't broken
That is why I created a YearMonth table to contain the years and months involved with a technique called CTE, commen table expression.
;WITH Years AS
(
SELECT 2022 AS [Year]
UNION
SELECT 2023 AS [Year]
UNION
SELECT 2024 AS [Year]
), Months AS
(
SELECT 1 AS [Month]
UNION
SELECT 2 AS [Month]
UNION
SELECT 3 AS [Month]
UNION
SELECT 4 AS [Month]
UNION
SELECT 5 AS [Month]
UNION
SELECT 6 AS [Month]
UNION
SELECT 7 AS [Month]
UNION
SELECT 8 AS [Month]
UNION
SELECT 9 AS [Month]
UNION
SELECT 10 AS [Month]
UNION
SELECT 11 AS [Month]
UNION
SELECT 12 AS [Month]
), InvoicePeriod AS
(
SELECT Years.[Year], Months.[Month], Years.[Year] *100 + Months.[Month] AS [InvoicePeriod], DATEFROMPARTS(Years.Year,Months.Month,1) AS BeginOfMonth, EOMONTH(DATEFROMPARTS(Years.Year,Months.Month,1)) EndOfMonth
FROM Years
CROSS JOIN Months
) SELECT * FROM InvoicePeriod;
As you can see we have a table with fields that helps us to calculate the results. At the end we combine 2 query results with UNION so we have 1 result for 2 situations.
This is the final result, it's not perfect as the number of days is not the same as your example but you'll get the idea:
SET ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_PADDING ON;
GO
DROP TABLE IF EXISTS #Invoices;
CREATE TABLE #Invoices
(
Project Varchar(10) NOT NULL
,DocumentCode Varchar(15) NOT NULL
,IssueDate Date NOT NULL
,Cost Decimal(8,2) NOT NULL
,ConcernFrom Date NULL
,ConcernTo Date NULL
);
INSERT INTO #Invoices
VALUES ('Prj-001', 'DOC-50001', '20230601', 500 , NULL , NULL),
('Prj-001', 'DOC-50002', '20230615', 1000 , '20230615' , '20230720'),
('Prj-001', 'DOC-50003', '20230625', 300 , NULL , NULL),
('Prj-002', 'DOC-50004', '20230710', 250 , NULL , NULL),
('Prj-002', 'DOC-50005', '20230731', 100 , NULL , NULL),
('Prj-002', 'DOC-50006', '20231015', 500 , '20231215' , '20240131')
;WITH Years AS
(
SELECT 2022 AS [Year]
UNION
SELECT 2023 AS [Year]
UNION
SELECT 2024 AS [Year]
), Months AS
(
SELECT 1 AS [Month]
UNION
SELECT 2 AS [Month]
UNION
SELECT 3 AS [Month]
UNION
SELECT 4 AS [Month]
UNION
SELECT 5 AS [Month]
UNION
SELECT 6 AS [Month]
UNION
SELECT 7 AS [Month]
UNION
SELECT 8 AS [Month]
UNION
SELECT 9 AS [Month]
UNION
SELECT 10 AS [Month]
UNION
SELECT 11 AS [Month]
UNION
SELECT 12 AS [Month]
), InvoicePeriod AS
(
SELECT
Years.[Year],
Months.[Month], Years.[Year] *100 + Months.[Month] AS [InvoicePeriod],
DATEFROMPARTS(Years.Year,Months.Month,1) AS BeginOfMonth,
EOMONTH(DATEFROMPARTS(Years.Year,Months.Month,1)) EndOfMonth
FROM Years
CROSS JOIN Months
), InvoiceTable AS
(
SELECT
,
CASE WHEN ConcernFrom > BeginOfMonth THEN ConcernFrom ELSE BeginOfMonth END AS StartDate,
CASE WHEN ConcernTo < EndOfMonth THEN ConcernTo ELSE EndOfMonth END AS EndDate,
DATEDIFF(DAY,ConcernFrom,ConcernTo) AS TotalDays,
DATEDIFF(DAY,CASE WHEN ConcernFrom > BeginOfMonth THEN ConcernFrom ELSE BeginOfMonth END,CASE WHEN ConcernTo < EndOfMonth THEN ConcernTo ELSE EndOfMonth END) DaysThisPeriod
FROM #Invoices
INNER JOIN InvoicePeriod ON
InvoicePeriod.[InvoicePeriod] BETWEEN YEAR(ConcernFrom)100 + Month(ConcernFrom) AND YEAR(ConcernTo)100 + Month(ConcernTo)
WHERE ConcernFrom IS NOT NULL
)
SELECT
tbl.Project, tbl.[Year], tbl.[Month], SUM(tbl.Cost)
FROM
(
SELECT I.Project, I.[Year], I.[Month], SUM((I.DaysThisPeriod1.0/I.TotalDays1.0)[Cost]) AS Cost
FROM InvoiceTable I
GROUP BY I.Project,I.[Year], I.[Month]
UNION
SELECT
I.Project, YEAR(I.IssueDate), MONTH(I.IssueDate), SUM(Cost) AS Cost
FROM #Invoices I
WHERE ConcernFrom IS NULL
GROUP BY I.Project, YEAR(I.IssueDate), MONTH(I.IssueDate)
) tbl
GROUP BY Project, [Year], [Month];