Files
Fuchs_Intranet/Fuchs_Database/Scripts/2026-09-30_backfill_intermediate_invoice_allocations.sql
T
Stefan c741535aec
Playwright Tests / test (push) Canceled after 0s
feat: Implement mfr truncation fix and enhance invoice handling
- Added a new SQL script to address the "String or binary data would be truncated" error in mfr sync by widening the Note fields in service objects and companies to NVARCHAR(MAX).
- Updated the table types and associated stored procedures to accommodate the changes, ensuring compatibility with existing data.
- Queued a re-sync for previously rejected rows due to truncation issues.
- Created application logins for fuchs_fds and fuchs_web with placeholder passwords for initial setup.
- Introduced functions for invoice backdating and invoice date retrieval to streamline invoice processing.
- Developed a password generation function with customizable parameters for length and character types.
- Added a random number generation function to support password creation.
2026-09-30 16:56:08 +02:00

93 lines
4.2 KiB
Transact-SQL

-- =============================================
-- One-off data backfill — run once per environment AFTER deploying the schema that carries
-- reference_InvId through fds__prepInvoice / the editor and fixes the allocation MERGE in
-- fds__setInvoiceFinal (2026-09-30).
--
-- Until then fds__invoice_items.reference_InvId was never written for 'IntInvoiceAllocation' lines
-- ("Anrechnung der Abschlagsrechnung R2025-0244 …"), so
-- * fds__setInvoiceFinal's allocation MERGE failed with 515 (NULL into fds__invoice_details.InvId)
-- for every Schlussrechnung — logged, invoice finalized anyway, allocation never recorded;
-- * fds__invoice_details.AllocatedTo_InvId stayed NULL for every Abschlagsrechnung, so
-- fds__prepInvoice would offer an already credited Abschlagsrechnung again.
-- This script
-- 1. restores reference_InvId from the invoice number in the line text (only unique matches), and
-- 2. records AllocatedTo_InvId for each Abschlagsrechnung that is credited on a finalized,
-- non-cancelled invoice (earliest such invoice wins; nothing already allocated is overwritten).
-- Default is a dry run (@apply = 0) that only reports; set @apply = 1 to write.
-- Idempotent: only rows with an empty reference / allocation are touched.
-- =============================================
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @apply bit = 0; -- 1 = write changes
IF OBJECT_ID('tempdb..#alloc') IS NOT NULL DROP TABLE #alloc;
WITH lines AS (
SELECT it.[Id], it.[InvId],
[num] = SUBSTRING(it.[Text], PATINDEX('%R[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]%', it.[Text]), 10)
FROM [dbo].[fds__invoice_items] AS it
WHERE it.[Type] = 'IntInvoiceAllocation'
AND ISNULL(it.[reference_InvId], '') = ''
AND PATINDEX('%R[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]%', it.[Text]) > 0
), matched AS (
SELECT l.[Id], l.[InvId], l.[num],
[RefInvId] = MIN(i.[Id]), [cand] = COUNT(i.[Id])
FROM lines AS l
LEFT JOIN [dbo].[fds__invoices] AS i ON i.[InvoiceId] = l.[num] AND i.[InvoiceType] = 'i'
GROUP BY l.[Id], l.[InvId], l.[num]
)
SELECT [ItemId] = [Id], [TgtInvId] = [InvId], [num], [RefInvId], [cand]
INTO #alloc
FROM matched;
SELECT [lines_without_reference] = COUNT(*),
[unique_match] = SUM(IIF([cand] = 1, 1, 0)),
[no_match] = SUM(IIF([cand] = 0, 1, 0)),
[ambiguous] = SUM(IIF([cand] > 1, 1, 0))
FROM #alloc;
-- Abschlagsrechnungen credited on more than one finalized, non-cancelled invoice (review manually)
SELECT a.[num], a.[RefInvId], [credited_on] = t.[InvoiceId], t.[DateFinalized], t.[InvoiceType]
FROM #alloc AS a
JOIN [dbo].[fds__invoices] AS t ON t.[Id] = a.[TgtInvId] AND t.[isFinal] = 1 AND ISNULL(t.[IsCanceled], 0) = 0
WHERE a.[cand] = 1
AND a.[RefInvId] IN (SELECT a2.[RefInvId] FROM #alloc AS a2
JOIN [dbo].[fds__invoices] AS t2 ON t2.[Id] = a2.[TgtInvId] AND t2.[isFinal] = 1 AND ISNULL(t2.[IsCanceled], 0) = 0
WHERE a2.[cand] = 1
GROUP BY a2.[RefInvId] HAVING COUNT(DISTINCT a2.[TgtInvId]) > 1)
ORDER BY a.[num], t.[DateFinalized];
IF @apply = 1
BEGIN
BEGIN TRANSACTION;
UPDATE it
SET it.[reference_InvId] = a.[RefInvId]
FROM [dbo].[fds__invoice_items] AS it
JOIN #alloc AS a ON a.[ItemId] = it.[Id] AND a.[cand] = 1
WHERE ISNULL(it.[reference_InvId], '') = '';
PRINT CONCAT('invoice item references restored: ', @@ROWCOUNT);
WITH firstTarget AS (
SELECT a.[RefInvId], a.[TgtInvId],
[rn] = ROW_NUMBER() OVER (PARTITION BY a.[RefInvId] ORDER BY t.[DateFinalized], t.[Id])
FROM #alloc AS a
JOIN [dbo].[fds__invoices] AS t ON t.[Id] = a.[TgtInvId] AND t.[isFinal] = 1 AND ISNULL(t.[IsCanceled], 0) = 0
WHERE a.[cand] = 1
)
MERGE [dbo].[fds__invoice_details] AS TARGET
USING (SELECT [RefInvId], [TgtInvId] FROM firstTarget WHERE [rn] = 1) AS src ON TARGET.[InvId] = src.[RefInvId]
WHEN MATCHED AND ISNULL(TARGET.[AllocatedTo_InvId], '') = '' THEN
UPDATE SET [AllocatedTo_InvId] = src.[TgtInvId]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([InvId], [AllocatedTo_InvId]) VALUES (src.[RefInvId], src.[TgtInvId]);
PRINT CONCAT('Abschlagsrechnung allocations recorded: ', @@ROWCOUNT);
COMMIT TRANSACTION;
END
ELSE
PRINT 'Dry run only — set @apply = 1 to write.';
DROP TABLE #alloc;