feat: Implement mfr truncation fix and enhance invoice handling
Playwright Tests / test (push) Canceled after 0s
Playwright Tests / test (push) Canceled after 0s
- 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.
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
-- =============================================
|
||||
CREATE PROCEDURE [dbo].[fds__setInvoiceFinal]
|
||||
@Id varchar(8),
|
||||
@authuser varchar(25)
|
||||
@authuser varchar(25),
|
||||
@backdate bit = 0 -- 1: date the invoice to the last working day of the previous month (ADR 0032)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
@@ -13,10 +14,33 @@ BEGIN
|
||||
THROW 60000, N'not authorized', 1;
|
||||
|
||||
INSERT INTO [dbo].[fds__admin_activity] ([activity] ,[authuser] ,[info])
|
||||
VALUES ('fds__setInvoiceFinal' ,@authuser , (SELECT * FROM (VALUES(@Id, @authuser)) as z ([id],[authuser]) FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER));
|
||||
VALUES ('fds__setInvoiceFinal' ,@authuser , (SELECT * FROM (VALUES(@Id, @authuser, @backdate)) as z ([id],[authuser],[backdate]) FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER));
|
||||
|
||||
DECLARE @now datetime = GETUTCDATE();
|
||||
|
||||
-- Finalizations are serialized: the "no later invoice" check of fds__fn_invoiceBackdate and the
|
||||
-- numbering UPDATE below must not interleave with a concurrent finalization, or a higher invoice
|
||||
-- number could end up with an earlier DateFinalized (ADR 0032). The app lock is transaction-owned
|
||||
-- and released at COMMIT/ROLLBACK; every finalization (backdated or not) takes it.
|
||||
SET XACT_ABORT ON;
|
||||
BEGIN TRY
|
||||
BEGIN TRANSACTION;
|
||||
DECLARE @lock int;
|
||||
EXEC @lock = sp_getapplock @Resource = N'fds__setInvoiceFinal', @LockMode = N'Exclusive', @LockOwner = N'Transaction', @LockTimeout = 15000;
|
||||
IF @lock < 0
|
||||
THROW 60002, N'invoice finalization lock not acquired', 1;
|
||||
|
||||
-- Invoice date: now, or (admins, fds_inv > 2) the last working day of the previous month as long as
|
||||
-- no invoice has been finalized after it. Noon keeps the UTC-stored value on the same calendar day.
|
||||
DECLARE @finalized datetime = @now, @backdateDay date = NULL;
|
||||
IF ISNULL(@backdate, 0) = 1
|
||||
BEGIN
|
||||
SET @backdateDay = [dbo].[fds__fn_invoiceBackdate](@authuser);
|
||||
IF @backdateDay IS NULL
|
||||
THROW 60001, N'backdating not allowed', 1;
|
||||
SET @finalized = DATEADD(HOUR, 12, CAST(@backdateDay AS datetime));
|
||||
END
|
||||
|
||||
DECLARE @ids as TABLE([Id] varchar(10));
|
||||
|
||||
DECLARE @Replaces TABLE([RefInvID] varchar(10));
|
||||
@@ -24,7 +48,9 @@ BEGIN
|
||||
DECLARE @Replaces_InvId varchar(50) = CASE WHEN EXISTS(SELECT * FROM @Replaces) THEN STUFF( (SELECT ',' + [refinvid] as [Text] FROM @replaces FOR XML PATH, TYPE).value('.', 'nvarchar(max)'), 1, 1, '') ELSE NULL END;
|
||||
|
||||
|
||||
UPDATE [dbo].[fds__invoices] SET [DateFinalized] = @now, [UserFinalized] = @authuser, [DateModified] = @now, [UserModified] = @authuser, [invoiceid] = [dbo].[fds__newInvoiceId](Year(@now)), [version] = (ISNULL([version],0) + 1), [Replaces_InvId] = @Replaces_InvId
|
||||
UPDATE [dbo].[fds__invoices] SET [DateFinalized] = @finalized, [UserFinalized] = @authuser, [DateModified] = @now, [UserModified] = @authuser, [invoiceid] = [dbo].[fds__newInvoiceId](Year(@finalized)),
|
||||
[DueDate] = CASE WHEN @backdateDay IS NULL THEN [DueDate] -- same term arithmetic as fds__setInvoice, anchored on the backdated invoice date
|
||||
ELSE DATEADD(DAY, ISNULL(TRY_PARSE(REPLACE(REPLACE(ISNULL([PaymentTerm], '10wd'), 'w', ''), 'd', '') AS tinyint), 10), @backdateDay) END, [version] = (ISNULL([version],0) + 1), [Replaces_InvId] = @Replaces_InvId
|
||||
OUTPUT inserted.[id]
|
||||
INTO @ids
|
||||
WHERE [Id] = @Id AND @Id is not null and [isFinal] = 0 and [isSent] = 0;
|
||||
@@ -32,35 +58,54 @@ BEGIN
|
||||
SELECT iv.* FROM [dbo].[fds__invoices] as iv JOIN @ids as ids on iv.[id] = ids.Id; --this is necessary to get calculated fields also
|
||||
|
||||
|
||||
-- update references of intermediate invoices
|
||||
-- update references of intermediate invoices (ADR 0034)
|
||||
WITH IntInvoiceAllocs as(
|
||||
SELECT [tgtInvId] = @Id, [reference_InvId] FROM [dbo].[fds__invoice_items] where [Type] = 'IntInvoiceAllocation' AND [InvId] = @Id
|
||||
SELECT DISTINCT [tgtInvId] = @Id, [reference_InvId] FROM [dbo].[fds__invoice_items] where [Type] = 'IntInvoiceAllocation' AND [InvId] = @Id
|
||||
AND ISNULL([reference_InvId], '') <> '' -- a NULL reference used to abort this MERGE (515 on fds__invoice_details.InvId)
|
||||
-- a Stornorechnung (copy) mirrors the cancelled invoice's credit lines but consumes no Abschlagsrechnung
|
||||
AND EXISTS (SELECT * FROM [dbo].[fds__invoices] as me WHERE me.[Id] = @Id AND me.[InvoiceType] <> 'c')
|
||||
), existing as (
|
||||
SELECT intermediates.Id
|
||||
FROM [dbo].[fds__invoice_details] as id
|
||||
JOIN [dbo].[fds__invoices] as intermediates on id.[InvId] = intermediates.[id]
|
||||
-- intermediates already credited on ANOTHER *live* invoice: a credit on a cancelled invoice, or on the
|
||||
-- invoice this one replaces (storno + recreate), is void and may be taken over
|
||||
SELECT intermediates.Id
|
||||
FROM [dbo].[fds__invoice_details] as id
|
||||
JOIN [dbo].[fds__invoices] as intermediates on id.[InvId] = intermediates.[id]
|
||||
JOIN IntInvoiceAllocs as iia on iia.[reference_InvId] = intermediates.[Id]
|
||||
JOIN [dbo].[fds__invoices] as tgt on tgt.[Id] = id.[AllocatedTo_InvId] -- (a dangling AllocatedTo_InvId counts as free)
|
||||
WHERE ISNULL(intermediates.[iscanceled], 0) = 0 AND isnull(intermediates.[isfinal],0) = 1 -- reallocation is allowed for cancelled inv.
|
||||
and ISNULL(id.[AllocatedTo_InvId], '6AA609E8-1') <> @id
|
||||
and tgt.[Id] <> @Id -- (an unallocated one used to count as taken, so nothing was ever allocated)
|
||||
and ISNULL(tgt.[IsCanceled], 0) = 0
|
||||
and NOT EXISTS (SELECT * FROM @Replaces as r WHERE r.[RefInvID] = tgt.[Id])
|
||||
)
|
||||
MERGE [dbo].[fds__invoice_details] as TARGET
|
||||
USING IntInvoiceAllocs as iia ON TARGET.[InvID] = iia.[reference_InvId]
|
||||
WHEN NOT MATCHED BY TARGET
|
||||
AND NOT EXISTS (SELECT * FROM existing WHERE existing.[id] = iia.[reference_InvId]) THEN
|
||||
WHEN NOT MATCHED BY TARGET
|
||||
AND NOT EXISTS (SELECT * FROM existing WHERE existing.[id] = iia.[reference_InvId]) THEN
|
||||
INSERT ([InvId] ,[AllocatedTo_InvId])
|
||||
VALUES (iia.[reference_InvId], iia.[tgtInvId])
|
||||
WHEN NOT MATCHED BY SOURCE THEN
|
||||
UPDATE
|
||||
SET [AllocatedTo_InvId] = NULL
|
||||
WHEN MATCHED -- free, ours already, or held by a cancelled/replaced invoice -> (re)allocate to this one
|
||||
AND NOT EXISTS (SELECT * FROM existing WHERE existing.[id] = iia.[reference_InvId]) THEN
|
||||
UPDATE
|
||||
SET [AllocatedTo_InvId] = iia.[tgtInvId]
|
||||
WHEN NOT MATCHED BY SOURCE AND TARGET.[AllocatedTo_InvId] = @Id THEN -- only this invoice's own stale allocations (was: every row of the table)
|
||||
UPDATE
|
||||
SET [AllocatedTo_InvId] = NULL
|
||||
;
|
||||
|
||||
--updated replaced invoices
|
||||
UPDATE refinv
|
||||
SET [DateCancelled] = @now, [UserCancelled] = @authuser, [version] = (ISNULL([version],0) + 1)
|
||||
SET [DateCancelled] = @finalized, [UserCancelled] = @authuser, [version] = (ISNULL([version],0) + 1)
|
||||
FROM [dbo].[fds__invoices] as refinv JOIN @Replaces as r on refinv.Id = r.[RefInvID]
|
||||
WHERE refinv.[isFinal] = 1 and refinv.[IsCanceled] = 0;
|
||||
|
||||
|
||||
--update replaced invoices (by stornos)
|
||||
EXECUTE [dbo].[fds__maint__updateCancelledStatus];
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
THROW; -- keeps the original number (60001 = backdating not allowed) for the caller
|
||||
END CATCH;
|
||||
END
|
||||
Reference in New Issue
Block a user