SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- [PRCUST_SET_JSON_INVOICE_DATA]
-- =================================================================
-- Imports invoice JSON payload into relational tables and returns
-- normalized JSON response for all imported documents.
-- 
-- Returns: RESULTSET; success/message/data
-- =================================================================
-- Copyright (c) 2026 by Digital Data GmbH
--
-- Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim
-- Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works
-- =================================================================
-- Creation Date / Author:    26.02.2026 / MK
-- Version Date / Editor:     26.02.2026 / MK
-- Version Number:            1.2.0.0
-- =================================================================
-- History:
-- 26.02.2026 / MK - Initial version based on JSON invoice import
-- 26.02.2026 / MK - Style harmonized to PRDEX convention
-- 26.02.2026 / MK - #3 CAST(NULL AS NVARCHAR(MAX)) for consistent data type
-- 26.02.2026 / MK - #4 RETURN @RETURN_STATUS for proper return codes
-- 26.02.2026 / MK - #5 Defensive cursor cleanup in CATCH block
-- 26.02.2026 / MK - #7 Reject empty purchaseDocuments array
-- 26.02.2026 / MK - #12 Extended error diagnostics in CATCH
-- 26.02.2026 / MK - #16 success column as BIT
-- 26.02.2026 / MK - PaymentDiscountIR-Feld hinzugefügt (Request-Abgleich)
-- 26.02.2026 / MK - JSON-Ausgabe: alle Feldnamen auf camelCase umgestellt
-- 26.02.2026 / MK - OPENJSON WITH: explizite camelCase JSON-Pfade für Input-Parsing

CREATE OR ALTER PROCEDURE [dbo].[PRCUST_SET_JSON_INVOICE_DATA](
	@json				NVARCHAR(MAX)
)
AS
BEGIN TRY

	SET NOCOUNT ON;
	SET XACT_ABORT ON;

	-- declare runtime vars
	DECLARE @MY_PROCEDURE_NAME				NVARCHAR(128)	= OBJECT_NAME(@@PROCID),
			@RETURN_STATUS					INT			= 0,
			@RETURN_STATUS_TEXT				NVARCHAR(MAX)	= CONCAT('START PROCEDURE [',OBJECT_NAME(@@PROCID),'] @ ',CONVERT(VARCHAR(50),GETDATE(),120)),
			@RETURN_ERROR_TEXT				NVARCHAR(MAX)	= N'';

	DECLARE @doc						NVARCHAR(MAX),
			@line						NVARCHAR(MAX),
			@DocumentID					INT,
			@LineID						INT,
			@output						NVARCHAR(MAX);

	DECLARE @docs					TABLE (doc NVARCHAR(MAX));
	DECLARE @lines					TABLE (line NVARCHAR(MAX));
	DECLARE @InsertedDocuments	TABLE (DocumentID INT PRIMARY KEY);

	PRINT '====================================================================================================';
	PRINT @RETURN_STATUS_TEXT;
	PRINT '';

	--==============================================-- Validate JSON payload --==============================================--
	IF dbo.FN_VALIDATE_JSON_INVOICE_DATA(@json) = 0 BEGIN

		SET @RETURN_STATUS		= 40001;
		SET @RETURN_STATUS_TEXT	= N'JSON validation failed.';
		SET @RETURN_ERROR_TEXT	= N'Ungültige JSON-Struktur.';

		PRINT 'ERROR: ' + @RETURN_ERROR_TEXT;

		SELECT
			  success = CAST(0 AS BIT)
			, message = @RETURN_ERROR_TEXT
			, data = CAST(NULL AS NVARCHAR(MAX));

		RETURN @RETURN_STATUS;
	END;
	-----------------------------------------------------------------------------------------------------------------------------------

	PRINT 'INFO: JSON-Validierung erfolgreich.';

	BEGIN TRANSACTION;

	--===========================================-- Extract purchaseDocuments array --===========================================--
	INSERT INTO @docs(doc)
	SELECT [value]
	FROM OPENJSON(@json, '$.purchaseDocuments');
	-----------------------------------------------------------------------------------------------------------------------------------

	--=========================================-- Reject empty purchaseDocuments array --==========================================--
	IF NOT EXISTS (SELECT 1 FROM @docs) BEGIN

		ROLLBACK TRANSACTION;

		SET @RETURN_STATUS		= 40002;
		SET @RETURN_STATUS_TEXT	= N'Empty purchaseDocuments array.';
		SET @RETURN_ERROR_TEXT	= N'purchaseDocuments-Array ist leer. Mindestens ein Dokument erforderlich.';

		PRINT 'ERROR: ' + @RETURN_ERROR_TEXT;

		SELECT
			  success = CAST(0 AS BIT)
			, message = @RETURN_ERROR_TEXT
			, data = CAST(NULL AS NVARCHAR(MAX));

		RETURN @RETURN_STATUS;
	END;
	-----------------------------------------------------------------------------------------------------------------------------------

	DECLARE doc_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT doc FROM @docs;
	OPEN doc_cursor;
	FETCH NEXT FROM doc_cursor INTO @doc;

	WHILE @@FETCH_STATUS = 0 BEGIN

		PRINT 'INFO: Füge PurchaseDocument ein...';

		--=============================================-- Insert PurchaseDocument row --=============================================--
		INSERT INTO dbo.PurchaseDocument (
			  DocumentType
			, No_
			, NoSeries
			, ProcessIDTransfer
			, BuyFromVendorNo
			, PayToVendorNo
			, PostingDate
			, PaymentTermsCode
			, PaymentTermsCodeIR
			, DueDate
			, PmtDiscountDate
			, PaymentDiscount
			, PaymentDiscountIR
			, CurrencyCode
			, InvoiceDiscCode
			, PostingDescription
			, PaymentMethodCode
			, VendorInvoiceNo
			, VendorCrMemoNo
			, PhrVendorBankAccountCode
			, PhrBankBranchNo
			, PhrBankAccountNo
			, PhrIBAN
			, PhrSWIFTCode
			, PhrBankName
			, PhrBankAccountEntryPriority
			, PhrRMCashDiscountReceived
			, PhrRMAmountIncludingVAT
			, PhrRMAmountLessDiscount
		)
		SELECT
			  DocumentType
			, No
			, NoSeries
			, ProcessIDTransfer
			, BuyFromVendorNo
			, PayToVendorNo
			, PostingDate
			, PaymentTermsCode
			, PaymentTermsCodeIR
			, DueDate
			, PmtDiscountDate
			, PaymentDiscount
			, PaymentDiscountIR
			, CurrencyCode
			, InvoiceDiscCode
			, PostingDescription
			, PaymentMethodCode
			, VendorInvoiceNo
			, VendorCrMemoNo
			, PhrVendorBankAccountCode
			, PhrBankBranchNo
			, PhrBankAccountNo
			, PhrIBAN
			, PhrSWIFTCode
			, PhrBankName
			, PhrBankAccountEntryPriority
			, PhrRMCashDiscountReceived
			, PhrRMAmountIncludingVAT
			, PhrRMAmountLessDiscount
		FROM OPENJSON(@doc)
		WITH (
			  DocumentType INT '$.documentType'
			, No NVARCHAR(50) '$.no'
			, NoSeries NVARCHAR(50) '$.noSeries'
			, ProcessIDTransfer NVARCHAR(50) '$.processIDTransfer'
			, BuyFromVendorNo NVARCHAR(50) '$.buyFromVendorNo'
			, PayToVendorNo NVARCHAR(50) '$.payToVendorNo'
			, PostingDate DATE '$.postingDate'
			, PaymentTermsCode NVARCHAR(20) '$.paymentTermsCode'
			, PaymentTermsCodeIR NVARCHAR(20) '$.paymentTermsCodeIR'
			, DueDate DATE '$.dueDate'
			, PmtDiscountDate DATE '$.pmtDiscountDate'
			, PaymentDiscount DECIMAL(10,2) '$.paymentDiscount'
			, PaymentDiscountIR DECIMAL(10,2) '$.paymentDiscountIR'
			, CurrencyCode NVARCHAR(10) '$.currencyCode'
			, InvoiceDiscCode NVARCHAR(50) '$.invoiceDiscCode'
			, PostingDescription NVARCHAR(250) '$.postingDescription'
			, PaymentMethodCode NVARCHAR(20) '$.paymentMethodCode'
			, VendorInvoiceNo NVARCHAR(50) '$.vendorInvoiceNo'
			, VendorCrMemoNo NVARCHAR(50) '$.vendorCrMemoNo'
			, PhrVendorBankAccountCode NVARCHAR(50) '$.phrVendorBankAccountCode'
			, PhrBankBranchNo NVARCHAR(50) '$.phrBankBranchNo'
			, PhrBankAccountNo NVARCHAR(50) '$.phrBankAccountNo'
			, PhrIBAN NVARCHAR(50) '$.phrIBAN'
			, PhrSWIFTCode NVARCHAR(50) '$.phrSWIFTCode'
			, PhrBankName NVARCHAR(100) '$.phrBankName'
			, PhrBankAccountEntryPriority BIT '$.phrBankAccountEntryPriority'
			, PhrRMCashDiscountReceived BIT '$.phrRMCashDiscountReceived'
			, PhrRMAmountIncludingVAT DECIMAL(18,2) '$.phrRMAmountIncludingVAT'
			, PhrRMAmountLessDiscount DECIMAL(18,2) '$.phrRMAmountLessDiscount'
			, purchaseLines NVARCHAR(MAX) AS JSON
		);
		-----------------------------------------------------------------------------------------------------------------------------------

		SET @DocumentID = SCOPE_IDENTITY();
		INSERT INTO @InsertedDocuments (DocumentID) VALUES (@DocumentID);

		PRINT 'INFO: PurchaseDocument eingefügt. DocumentID = ' + CAST(@DocumentID AS NVARCHAR(20));

		--=============================================-- Extract purchaseLines array --==============================================--
		DELETE FROM @lines;
		INSERT INTO @lines(line)
		SELECT [value]
		FROM OPENJSON(@doc, '$.purchaseLines');
		-----------------------------------------------------------------------------------------------------------------------------------

		DECLARE line_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT line FROM @lines;
		OPEN line_cursor;
		FETCH NEXT FROM line_cursor INTO @line;

		WHILE @@FETCH_STATUS = 0 BEGIN

			PRINT 'INFO: Füge PurchaseLine ein...';

			INSERT INTO dbo.PurchaseLine (
				  DocumentID
				, AttachedToLineNo
				, Type
				, No_
				, Description
				, Description2
				, LocationCode
				, VariantCode
				, Quantity
				, UnitOfMeasureCode
				, DirectUnitCost
				, LineDiscount
				, AbleToDiscount
				, DiscountCalculated
				, WorkOrderNo
				, GenBusPostingGroup
				, GenProdPostingGroup
				, VatBusPostingGroup
				, VatProdPostingGroup
				, ApplToItemEntry
				, PostingWithoutApply
				, AnticipatedPayment
				, ReceiptNo
				, ReceiptLineNo
				, IrOrderNo
				, IrOrderLineNo
			)
			SELECT
				  @DocumentID
				, AttachedToLineNo
				, Type
				, No
				, Description
				, Description2
				, LocationCode
				, VariantCode
				, Quantity
				, UnitOfMeasureCode
				, DirectUnitCost
				, LineDiscount
				, AbleToDiscount
				, DiscountCalculated
				, WorkOrderNo
				, GenBusPostingGroup
				, GenProdPostingGroup
				, VatBusPostingGroup
				, VatProdPostingGroup
				, ApplToItemEntry
				, PostingWithoutApply
				, AnticipatedPayment
				, ReceiptNo
				, ReceiptLineNo
				, IrOrderNo
				, IrOrderLineNo
			FROM OPENJSON(@line)
			WITH (
				  AttachedToLineNo INT '$.attachedToLineNo'
				, Type INT '$.type'
				, No NVARCHAR(50) '$.no'
				, Description NVARCHAR(250) '$.description'
				, Description2 NVARCHAR(250) '$.description2'
				, LocationCode NVARCHAR(50) '$.locationCode'
				, VariantCode NVARCHAR(50) '$.variantCode'
				, Quantity DECIMAL(18,4) '$.quantity'
				, UnitOfMeasureCode NVARCHAR(20) '$.unitOfMeasureCode'
				, DirectUnitCost DECIMAL(18,4) '$.directUnitCost'
				, LineDiscount DECIMAL(18,4) '$.lineDiscount'
				, AbleToDiscount BIT '$.ableToDiscount'
				, DiscountCalculated BIT '$.discountCalculated'
				, WorkOrderNo NVARCHAR(50) '$.workOrderNo'
				, GenBusPostingGroup NVARCHAR(20) '$.genBusPostingGroup'
				, GenProdPostingGroup NVARCHAR(20) '$.genProdPostingGroup'
				, VatBusPostingGroup NVARCHAR(20) '$.vatBusPostingGroup'
				, VatProdPostingGroup NVARCHAR(20) '$.vatProdPostingGroup'
				, ApplToItemEntry INT '$.applToItemEntry'
				, PostingWithoutApply BIT '$.postingWithoutApply'
				, AnticipatedPayment DECIMAL(18,2) '$.anticipatedPayment'
				, ReceiptNo NVARCHAR(50) '$.receiptNo'
				, ReceiptLineNo INT '$.receiptLineNo'
				, IrOrderNo NVARCHAR(50) '$.irOrderNo'
				, IrOrderLineNo INT '$.irOrderLineNo'
				, dimSetEntries NVARCHAR(MAX) AS JSON
				, itemChargeAssignmentLines NVARCHAR(MAX) AS JSON
			);

			SET @LineID = SCOPE_IDENTITY();
			PRINT 'INFO: PurchaseLine eingefügt. LineID = ' + CAST(@LineID AS NVARCHAR(20));

			--===============================================-- Insert dimSetEntries --===============================================--
			INSERT INTO dbo.DimSetEntry (LineID, DimensionCode, DimensionValueCode)
			SELECT @LineID, DimensionCode, DimensionValueCode
			FROM OPENJSON(@line, '$.dimSetEntries')
			WITH (
				  DimensionCode NVARCHAR(50) '$.dimensionCode'
				, DimensionValueCode NVARCHAR(50) '$.dimensionValueCode'
			);
			-----------------------------------------------------------------------------------------------------------------------------------

			--=========================================-- Insert itemChargeAssignmentLines --==========================================--
			INSERT INTO dbo.ItemChargeAssignment (
				  LineID
				, ItemChargeNo
				, UnitCost
				, AppliesToDocType
				, AppliesToDocNo
				, AppliesToDocLineNo
				, ItemNo
				, Description
				, QtyToAssign
				, QtyAssigned
				, AmountToAssign
				, AppliesToDocLineAmount
			)
			SELECT
				  @LineID
				, ItemChargeNo
				, UnitCost
				, AppliesToDocType
				, AppliesToDocNo
				, AppliesToDocLineNo
				, ItemNo
				, Description
				, QtyToAssign
				, QtyAssigned
				, AmountToAssign
				, AppliesToDocLineAmount
			FROM OPENJSON(@line, '$.itemChargeAssignmentLines')
			WITH (
				  ItemChargeNo NVARCHAR(50) '$.itemChargeNo'
				, UnitCost DECIMAL(18,4) '$.unitCost'
				, AppliesToDocType INT '$.appliesToDocType'
				, AppliesToDocNo NVARCHAR(50) '$.appliesToDocNo'
				, AppliesToDocLineNo INT '$.appliesToDocLineNo'
				, ItemNo NVARCHAR(50) '$.itemNo'
				, Description NVARCHAR(250) '$.description'
				, QtyToAssign DECIMAL(18,4) '$.qtyToAssign'
				, QtyAssigned DECIMAL(18,4) '$.qtyAssigned'
				, AmountToAssign DECIMAL(18,4) '$.amountToAssign'
				, AppliesToDocLineAmount DECIMAL(18,4) '$.appliesToDocLineAmount'
			);
			-----------------------------------------------------------------------------------------------------------------------------------

			FETCH NEXT FROM line_cursor INTO @line;
		END;

		CLOSE line_cursor;
		DEALLOCATE line_cursor;

		FETCH NEXT FROM doc_cursor INTO @doc;
	END;

	CLOSE doc_cursor;
	DEALLOCATE doc_cursor;

	PRINT 'INFO: Erzeuge JSON-Ausgabe...';

	--===============================================-- Build output JSON result --===============================================--
	SELECT @output =
	(
		SELECT
			JSON_QUERY(
				COALESCE(
					(
						SELECT
							  [documentType] = pd.DocumentType
							, [no] = pd.No_
							, [noSeries] = pd.NoSeries
							, [processIDTransfer] = pd.ProcessIDTransfer
							, [buyFromVendorNo] = pd.BuyFromVendorNo
							, [payToVendorNo] = pd.PayToVendorNo
							, [postingDate] = pd.PostingDate
							, [paymentTermsCode] = pd.PaymentTermsCode
							, [paymentTermsCodeIR] = pd.PaymentTermsCodeIR
							, [dueDate] = pd.DueDate
							, [pmtDiscountDate] = pd.PmtDiscountDate
							, [paymentDiscount] = pd.PaymentDiscount
							, [paymentDiscountIR] = pd.PaymentDiscountIR
							, [currencyCode] = pd.CurrencyCode
							, [invoiceDiscCode] = pd.InvoiceDiscCode
							, [postingDescription] = pd.PostingDescription
							, [paymentMethodCode] = pd.PaymentMethodCode
							, [vendorInvoiceNo] = pd.VendorInvoiceNo
							, [vendorCrMemoNo] = pd.VendorCrMemoNo
							, [phrVendorBankAccountCode] = pd.PhrVendorBankAccountCode
							, [phrBankBranchNo] = pd.PhrBankBranchNo
							, [phrBankAccountNo] = pd.PhrBankAccountNo
							, [phrIBAN] = pd.PhrIBAN
							, [phrSWIFTCode] = pd.PhrSWIFTCode
							, [phrBankName] = pd.PhrBankName
							, [phrBankAccountEntryPriority] = pd.PhrBankAccountEntryPriority
							, [phrRMCashDiscountReceived] = pd.PhrRMCashDiscountReceived
							, [phrRMAmountIncludingVAT] = pd.PhrRMAmountIncludingVAT
							, [phrRMAmountLessDiscount] = pd.PhrRMAmountLessDiscount
							, purchaseLines = JSON_QUERY(
								COALESCE(
									(
										SELECT
											  [attachedToLineNo] = pl.AttachedToLineNo
											, [type] = pl.Type
											, [no] = pl.No_
											, [description] = pl.Description
											, [description2] = pl.Description2
											, [locationCode] = pl.LocationCode
											, [variantCode] = pl.VariantCode
											, [quantity] = pl.Quantity
											, [unitOfMeasureCode] = pl.UnitOfMeasureCode
											, [directUnitCost] = pl.DirectUnitCost
											, [lineDiscount] = pl.LineDiscount
											, [ableToDiscount] = pl.AbleToDiscount
											, [discountCalculated] = pl.DiscountCalculated
											, [workOrderNo] = pl.WorkOrderNo
											, [genBusPostingGroup] = pl.GenBusPostingGroup
											, [genProdPostingGroup] = pl.GenProdPostingGroup
											, [vatBusPostingGroup] = pl.VatBusPostingGroup
											, [vatProdPostingGroup] = pl.VatProdPostingGroup
											, [applToItemEntry] = pl.ApplToItemEntry
											, [postingWithoutApply] = pl.PostingWithoutApply
											, [anticipatedPayment] = pl.AnticipatedPayment
											, [receiptNo] = pl.ReceiptNo
											, [receiptLineNo] = pl.ReceiptLineNo
											, [irOrderNo] = pl.IrOrderNo
											, [irOrderLineNo] = pl.IrOrderLineNo
											, dimSetEntries = JSON_QUERY(
												COALESCE(
													(
														SELECT
															  [dimensionCode] = d.DimensionCode
															, [dimensionValueCode] = d.DimensionValueCode
														FROM dbo.DimSetEntry d
														WHERE d.LineID = pl.LineID
														FOR JSON PATH
													)
													, '[]'
												)
											)
											, itemChargeAssignmentLines = JSON_QUERY(
												COALESCE(
													(
														SELECT
															  [itemChargeNo] = ia.ItemChargeNo
															, [unitCost] = ia.UnitCost
															, [appliesToDocType] = ia.AppliesToDocType
															, [appliesToDocNo] = ia.AppliesToDocNo
															, [appliesToDocLineNo] = ia.AppliesToDocLineNo
															, [itemNo] = ia.ItemNo
															, [description] = ia.Description
															, [qtyToAssign] = ia.QtyToAssign
															, [qtyAssigned] = ia.QtyAssigned
															, [amountToAssign] = ia.AmountToAssign
															, [appliesToDocLineAmount] = ia.AppliesToDocLineAmount
														FROM dbo.ItemChargeAssignment ia
														WHERE ia.LineID = pl.LineID
														FOR JSON PATH
													)
													, '[]'
												)
											)
										FROM dbo.PurchaseLine pl
										WHERE pl.DocumentID = pd.DocumentID
										FOR JSON PATH
									)
									, '[]'
								)
							)
						FROM dbo.PurchaseDocument pd
						INNER JOIN @InsertedDocuments id ON id.DocumentID = pd.DocumentID
						ORDER BY pd.DocumentID
						FOR JSON PATH
					)
					, '[]'
				)
			) AS purchaseDocuments
		FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
	);
	-----------------------------------------------------------------------------------------------------------------------------------

	COMMIT TRANSACTION;

	SET @RETURN_STATUS		= 0;
	SET @RETURN_STATUS_TEXT	= CONCAT('END PROCEDURE [',@MY_PROCEDURE_NAME,'] @ ',CONVERT(NVARCHAR(50),GETDATE(),120));

	PRINT '';
	PRINT @RETURN_STATUS_TEXT;
	PRINT '====================================================================================================';

	SELECT
		  success = CAST(1 AS BIT)
		, message = N'Daten erfolgreich verarbeitet.'
		, data = @output;

	RETURN @RETURN_STATUS;

END TRY BEGIN CATCH

	--====================================================-- exception / error --====================================================--

	-- defensive cursor cleanup
	IF CURSOR_STATUS('local','line_cursor') >= -1 BEGIN
		IF CURSOR_STATUS('local','line_cursor') >= 0
			CLOSE line_cursor;
		DEALLOCATE line_cursor;
	END;
	IF CURSOR_STATUS('local','doc_cursor') >= -1 BEGIN
		IF CURSOR_STATUS('local','doc_cursor') >= 0
			CLOSE doc_cursor;
		DEALLOCATE doc_cursor;
	END;

	IF (@@TRANCOUNT > 0) BEGIN
		ROLLBACK TRANSACTION;
	END;

	SET @RETURN_STATUS		= 50000;
	SET @RETURN_STATUS_TEXT	= CONCAT('END PROCEDURE [',OBJECT_NAME(@@PROCID),'] @ ',CONVERT(NVARCHAR(50),GETDATE(),120));
	SET @RETURN_ERROR_TEXT	= CONCAT('ERR ',ERROR_NUMBER(),' SEV ',ERROR_SEVERITY(),' STATE ',ERROR_STATE(),' LINE ',ERROR_LINE(),': ',ERROR_MESSAGE());

	PRINT 'ERROR IN PROCEDURE: [' + OBJECT_NAME(@@PROCID) + ']' + CHAR(13) + @RETURN_ERROR_TEXT;
	PRINT @RETURN_STATUS_TEXT;
	PRINT '====================================================================================================';
	-----------------------------------------------------------------------------------------------------------------------------------

	SELECT
		  success = CAST(0 AS BIT)
		, message = ERROR_MESSAGE()
		, data = CAST(NULL AS NVARCHAR(MAX));

	RETURN @RETURN_STATUS;

END CATCH;
GO
