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