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:
@@ -22,7 +22,11 @@
|
||||
<SqlServerVerification>False</SqlServerVerification>
|
||||
<IncludeCompositeObjects>True</IncludeCompositeObjects>
|
||||
<TargetDatabaseSet>True</TargetDatabaseSet>
|
||||
<SuppressTSqlWarnings>71501</SuppressTSqlWarnings>
|
||||
<!-- 71501: unresolved reference (dynamic SQL / cross-database objects); 71558: identifier differs only by case,
|
||||
harmless under the CI model collation. Every other T-SQL warning is a build error (ADR 0036) so an
|
||||
ambiguous or unresolved column never reaches a database. -->
|
||||
<SuppressTSqlWarnings>71501;71558</SuppressTSqlWarnings>
|
||||
<TreatTSqlWarningsAsErrors>True</TreatTSqlWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
@@ -148,6 +152,7 @@
|
||||
<Build Include="Security\fds_rwe.sql" />
|
||||
<Build Include="Security\fuchs_web.sql" />
|
||||
<Build Include="Security\fuchs_fds.sql" />
|
||||
<Build Include="Security\Logins.sql" />
|
||||
<Build Include="dbo\Functions\vat_val.sql" />
|
||||
<Build Include="dbo\Functions\strings_replaceSpecialCharsInHTML.sql" />
|
||||
<Build Include="dbo\Functions\strings_removeLeading0_255.sql" />
|
||||
@@ -205,6 +210,10 @@
|
||||
<Build Include="dbo\Functions\fds__fn_InvoiceIDs_mfr.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_InvoiceIdByName.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_invoice_id.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_invoiceBackdate.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_invoiceDate.sql" />
|
||||
<Build Include="dbo\Functions\ocms_fn_generatePassword.sql" />
|
||||
<Build Include="dbo\Functions\ocms_fn_randbetween.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_invoice_customerid.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_IntermediateIsAllocatedToOther.sql" />
|
||||
<Build Include="dbo\Functions\fds__fn_DocumentName.sql" />
|
||||
@@ -411,5 +420,16 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Scripts\2026-09-16_fis_widgets_set_based_payment_amounts.sql" />
|
||||
<None Include="Scripts\2026-09-30_backfill_intermediate_invoice_allocations.sql" />
|
||||
<None Include="Scripts\2026-09-30_mfr_truncation_fix.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<!-- System catalog (sys.*, INFORMATION_SCHEMA, sp_executesql, sp_getapplock, ...) so the model binds
|
||||
cleanly and every remaining SQL71502 is a genuine unresolved/ambiguous reference (ADR 0036). -->
|
||||
<ArtifactReference Include="$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\170\SQLSchemas\master.dacpac">
|
||||
<HintPath>$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\170\SQLSchemas\master.dacpac</HintPath>
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
-- =============================================
|
||||
-- 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;
|
||||
@@ -0,0 +1,504 @@
|
||||
-- =============================================
|
||||
-- 2026-09-30 mfr sync: "String or binary data would be truncated" (msg 8152)
|
||||
--
|
||||
-- Root cause: mfr's free-text [Note] on ServiceObject and Company has no length limit
|
||||
-- (live mfr data on 2026-09-30: ServiceObject.Note up to 753 chars, Company.Note up to 723),
|
||||
-- but [mfr__serviceobjects].[Note], [mfr__companies].[Note] and their table types
|
||||
-- [mfr__tt__serviceobjects] / [mfr__tt__companies] were NVARCHAR(255). The merge procs
|
||||
-- [mfr__updt__serviceobjects] / [mfr__updt__companies] copy the staging table into the type,
|
||||
-- so every batch containing such a row (the entity's own sync and every Company/Contact/
|
||||
-- ServiceRequest/ServiceObject sync that $expands it) was rejected as a whole.
|
||||
--
|
||||
-- This script (idempotent, safe to re-run; one transaction, all-or-nothing):
|
||||
-- 1. patches the [@company] table variable in fds__getRequest_details, fds__prepInvoice
|
||||
-- and fds__prepStorno_recreate (they INSERT ... SELECT cy.* from [mfr__companies]):
|
||||
-- [Note] -> nvarchar(max) (without this, widening the table would move the truncation
|
||||
-- into invoice/request preparation for customers with a long note), and
|
||||
-- [CustomValues] / [MainContactId] -> NULL (both nullable in [mfr__companies]; the
|
||||
-- NOT NULL declaration raised 515 for a company without main contact). Patched IN
|
||||
-- PLACE on the deployed definition (only the @company block changes), so other edits
|
||||
-- to those procs already deployed on this server are kept. Same result as the SSDT
|
||||
-- versions of these procs.
|
||||
-- 2. ALTERs [mfr__serviceobjects].[Note] and [mfr__companies].[Note] to NVARCHAR(MAX).
|
||||
-- 3. Drops/recreates the table types [mfr__tt__serviceobjects] / [mfr__tt__companies]
|
||||
-- (a table type cannot be ALTERed). The procs that DECLARE them,
|
||||
-- [mfr__updt__serviceobjects] / [mfr__updt__companies], are dropped first and recreated
|
||||
-- from the SSDT source; explicit GRANT/DENY on proc and type are captured and re-applied.
|
||||
-- 4. Queues a re-sync (fds__mfr_updaterequests) for the rows that were rejected, because
|
||||
-- the short sync only fetches rows newer than the local watermark and would skip them.
|
||||
--
|
||||
-- Recommended: stop the Fuchs_DataService worker while the script runs.
|
||||
-- Matches SSDT (Fuchs_Database) as of 2026-09-30.
|
||||
-- =============================================
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET ANSI_NULLS ON;
|
||||
|
||||
DECLARE @type_so nvarchar(max) = N'CREATE TYPE [dbo].[mfr__tt__serviceobjects] AS TABLE (
|
||||
[setid] VARCHAR (5) NULL,
|
||||
[Id] BIGINT NOT NULL,
|
||||
[Location#ID] BIGINT NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[CustomValues] NVARCHAR (MAX) NOT NULL,
|
||||
[DateModified] DATETIME NOT NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
[MappingId] NVARCHAR (255) NULL,
|
||||
[QuickSearch] NVARCHAR (255) NULL,
|
||||
[Version] INT NOT NULL,
|
||||
[CreateGeoLocation] BIT NOT NULL,
|
||||
[IsWarehouse] BIT NOT NULL,
|
||||
[ParentServiceObjectId] BIGINT NOT NULL,
|
||||
[CompanyId] BIGINT NOT NULL,
|
||||
[ProductId] BIGINT NOT NULL);';
|
||||
|
||||
DECLARE @proc_so nvarchar(max) = N'-- =============================================
|
||||
-- Author: <Author,,Name>
|
||||
-- Create date: <Create Date,,>
|
||||
-- Description: <Description,,>
|
||||
-- =============================================
|
||||
CREATE PROCEDURE [dbo].[mfr__updt__serviceobjects]
|
||||
@tblname as nvarchar(50)
|
||||
, @referencetable nvarchar(50)
|
||||
, @tgtid bigint
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
BEGIN TRY
|
||||
|
||||
DECLARE @now datetime = GETUTCDATE();
|
||||
DECLARE @tmp [dbo].[mfr__tt__serviceobjects];
|
||||
|
||||
DECLARE @tmp_cmd nvarchar(1000) = N''SELECT DISTINCT * FROM '' + @tblname
|
||||
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
IF @referencetable = ''mfr__serviceobjects''
|
||||
BEGIN
|
||||
DELETE p
|
||||
--OUTput deleted.*, @now INTO [dbo].[mfr__d_*PartnerSet]
|
||||
FROM [dbo].[mfr__*PartnerSet] as p
|
||||
WHERE p.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = p.[EntityId]);
|
||||
DELETE cv
|
||||
--OUTput deleted.*, @now INTO [dbo].[mfr__d_#customvalues]
|
||||
FROM [dbo].[mfr__#customvalues] as cv
|
||||
WHERE cv.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cv.[EntityId]);
|
||||
DELETE lc
|
||||
--OUTPUT deleted.*, @now INTO [dbo].[mfr__d_#locations]
|
||||
FROM [dbo].[mfr__#locations] as lc
|
||||
WHERE lc.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = lc.[EntityId]);
|
||||
DELETE itm
|
||||
--OUTPUT deleted.*, @now INTO [dbo].[mfr__d_items]
|
||||
FROM [dbo].[mfr__items] as itm
|
||||
WHERE EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = itm.[ServiceObjectId] and ISNULL(itm.[ServiceRequestId],0) = 0);
|
||||
END
|
||||
|
||||
|
||||
MERGE [dbo].[mfr__serviceobjects] as TARGET
|
||||
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
|
||||
WHEN MATCHED AND (TARGET.[Version] <= SOURCE.[Version] OR TARGET.[DateModified] <= SOURCE.[DateModified]) THEN
|
||||
UPDATE
|
||||
SET [Id] = SOURCE.[Id]
|
||||
,[Location#ID] = SOURCE.[Location#ID]
|
||||
,[Name] = SOURCE.[Name]
|
||||
,[Note] = SOURCE.[Note]
|
||||
,[CustomValues] = SOURCE.[CustomValues]
|
||||
,[DateModified] = SOURCE.[DateModified]
|
||||
,[ExternalId] = SOURCE.[ExternalId]
|
||||
,[MappingId] = SOURCE.[MappingId]
|
||||
,[QuickSearch] = SOURCE.[QuickSearch]
|
||||
,[Version] = SOURCE.[Version]
|
||||
,[CreateGeoLocation] = SOURCE.[CreateGeoLocation]
|
||||
,[IsWarehouse] = SOURCE.[IsWarehouse]
|
||||
,[ParentServiceObjectId] = SOURCE.[ParentServiceObjectId]
|
||||
,[CompanyId] = SOURCE.[CompanyId]
|
||||
,[ProductId] = SOURCE.[ProductId]
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT
|
||||
( [Id]
|
||||
,[Location#ID]
|
||||
,[Name]
|
||||
,[Note]
|
||||
,[CustomValues]
|
||||
,[DateModified]
|
||||
,[ExternalId]
|
||||
,[MappingId]
|
||||
,[QuickSearch]
|
||||
,[Version]
|
||||
,[CreateGeoLocation]
|
||||
,[IsWarehouse]
|
||||
,[ParentServiceObjectId]
|
||||
,[CompanyId]
|
||||
,[ProductId])
|
||||
VALUES
|
||||
(SOURCE.[Id]
|
||||
,SOURCE.[Location#ID]
|
||||
,SOURCE.[Name]
|
||||
,SOURCE.[Note]
|
||||
,SOURCE.[CustomValues]
|
||||
,SOURCE.[DateModified]
|
||||
,SOURCE.[ExternalId]
|
||||
,SOURCE.[MappingId]
|
||||
,SOURCE.[QuickSearch]
|
||||
,SOURCE.[Version]
|
||||
,SOURCE.[CreateGeoLocation]
|
||||
,SOURCE.[IsWarehouse]
|
||||
,SOURCE.[ParentServiceObjectId]
|
||||
,SOURCE.[CompanyId]
|
||||
,SOURCE.[ProductId]);
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
|
||||
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
|
||||
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
|
||||
|
||||
THROW;
|
||||
END CATCH;
|
||||
END';
|
||||
|
||||
DECLARE @type_co nvarchar(max) = N'CREATE TYPE [dbo].[mfr__tt__companies] AS TABLE (
|
||||
[setid] VARCHAR (5) NULL,
|
||||
[Id] BIGINT NOT NULL,
|
||||
[Location#ID] BIGINT NULL,
|
||||
[Version] INT NOT NULL,
|
||||
[IsPhysicalPerson] BIT NOT NULL,
|
||||
[IsOwner] BIT NOT NULL,
|
||||
[IsEmailInvoicingActive] BIT NOT NULL,
|
||||
[IsSupplier] BIT NOT NULL,
|
||||
[MappingId] NVARCHAR (255) NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[SupportTelephone] NVARCHAR (255) NULL,
|
||||
[SupportFax] NVARCHAR (255) NULL,
|
||||
[SupportMail] NVARCHAR (255) NULL,
|
||||
[QuickSearch] NVARCHAR (255) NULL,
|
||||
[DateModified] DATETIME NOT NULL,
|
||||
[CustomValues] NVARCHAR (MAX) NULL,
|
||||
[MainContactId] BIGINT NULL);';
|
||||
|
||||
DECLARE @proc_co nvarchar(max) = N'-- =============================================
|
||||
-- Description: mfr sync — merges one staged batch of Company entities into [mfr__companies].
|
||||
-- @tblname is the bulk-copied staging table (same layout as [mfr__tt__companies]);
|
||||
-- [setid] is the batch marker. A full entity reset (@referencetable = ''mfr__companies'')
|
||||
-- first drops the company''s dependent partner/custom-value/location/comment rows so
|
||||
-- they are rebuilt from the same batch. Duplicate Ids within one batch (paged reads
|
||||
-- overlapping a concurrent mfr edit) are collapsed to the highest Version before the
|
||||
-- MERGE, otherwise MERGE raises 8672.
|
||||
-- =============================================
|
||||
CREATE PROCEDURE [dbo].[mfr__updt__companies]
|
||||
@tblname as nvarchar(50)
|
||||
, @referencetable nvarchar(50)
|
||||
, @tgtid bigint
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
BEGIN TRY
|
||||
|
||||
DECLARE @tmp [dbo].[mfr__tt__companies];
|
||||
|
||||
DECLARE @tmp_cmd nvarchar(1000) = N''SELECT DISTINCT * FROM '' + @tblname
|
||||
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
|
||||
|
||||
-- Wrapping the DELETEs + MERGE below in one transaction gives all-or-nothing atomicity for
|
||||
-- this entity''s sync batch (same pattern in every mfr__updt__* proc). During a large full
|
||||
-- reset this can hold locks on the affected tables for as long as the batch takes to run —
|
||||
-- an accepted, documented risk, not an oversight. See
|
||||
-- Docs/Decisions/0022-mfr-sync-transaction-blocking-accepted-risk.md before changing this.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
IF @referencetable = ''mfr__companies''
|
||||
BEGIN
|
||||
DELETE p
|
||||
--OUTput deleted.*, @now INTO [dbo].[mfr__d_*PartnerSet]
|
||||
FROM [dbo].[mfr__*PartnerSet] as p
|
||||
WHERE p.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = p.[EntityId]);
|
||||
DELETE cv
|
||||
--OUTput deleted.*, @now INTO [dbo].[mfr__d_#customvalues]
|
||||
FROM [dbo].[mfr__#customvalues] as cv
|
||||
WHERE cv.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cv.[EntityId]);
|
||||
DELETE lc
|
||||
--OUTPUT deleted.*, @now INTO [dbo].[mfr__d_#locations]
|
||||
FROM [dbo].[mfr__#locations] as lc
|
||||
WHERE lc.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = lc.[EntityId]);
|
||||
DELETE cm
|
||||
--OUTPUT deleted.*, @now INTO [dbo].[mfr__d_comments]
|
||||
FROM [dbo].[mfr__comments] as cm
|
||||
WHERE EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cm.[CompanyId] AND ISNULL(cm.[ServiceRequestId],0) = 0);
|
||||
END;
|
||||
|
||||
WITH tmp as (
|
||||
SELECT *, ROW_NUMBER() OVER (PARTITION BY [Id] ORDER BY [Version] DESC, [DateModified] DESC) as ''#'' FROM @tmp
|
||||
), SOURCE as (
|
||||
SELECT * FROM tmp WHERE [#] = 1
|
||||
)
|
||||
MERGE [dbo].[mfr__companies] as TARGET
|
||||
USING SOURCE ON TARGET.[Id] = SOURCE.[Id]
|
||||
WHEN MATCHED AND (TARGET.[Version] <= SOURCE.[Version] OR TARGET.[DateModified] <= SOURCE.[DateModified]) THEN
|
||||
UPDATE
|
||||
SET [Id] = SOURCE.[Id]
|
||||
,[Location#ID] = SOURCE.[Location#ID]
|
||||
,[Version] = SOURCE.[Version]
|
||||
,[IsPhysicalPerson] = SOURCE.[IsPhysicalPerson]
|
||||
,[IsOwner] = SOURCE.[IsOwner]
|
||||
,[IsEmailInvoicingActive] = SOURCE.[IsEmailInvoicingActive]
|
||||
,[IsSupplier] = SOURCE.[IsSupplier]
|
||||
,[MappingId] = SOURCE.[MappingId]
|
||||
,[ExternalId] = SOURCE.[ExternalId]
|
||||
,[Name] = SOURCE.[Name]
|
||||
,[Note] = SOURCE.[Note]
|
||||
,[SupportTelephone] = SOURCE.[SupportTelephone]
|
||||
,[SupportFax] = SOURCE.[SupportFax]
|
||||
,[SupportMail] = SOURCE.[SupportMail]
|
||||
,[QuickSearch] = SOURCE.[QuickSearch]
|
||||
,[DateModified] = SOURCE.[DateModified]
|
||||
,[CustomValues] = SOURCE.[CustomValues]
|
||||
,[MainContactId] = SOURCE.[MainContactId]
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT
|
||||
( [Id]
|
||||
,[Location#ID]
|
||||
,[Version]
|
||||
,[IsPhysicalPerson]
|
||||
,[IsOwner]
|
||||
,[IsEmailInvoicingActive]
|
||||
,[IsSupplier]
|
||||
,[MappingId]
|
||||
,[ExternalId]
|
||||
,[Name]
|
||||
,[Note]
|
||||
,[SupportTelephone]
|
||||
,[SupportFax]
|
||||
,[SupportMail]
|
||||
,[QuickSearch]
|
||||
,[DateModified]
|
||||
,[CustomValues]
|
||||
,[MainContactId])
|
||||
VALUES
|
||||
(SOURCE.[Id]
|
||||
,SOURCE.[Location#ID]
|
||||
,SOURCE.[Version]
|
||||
,SOURCE.[IsPhysicalPerson]
|
||||
,SOURCE.[IsOwner]
|
||||
,SOURCE.[IsEmailInvoicingActive]
|
||||
,SOURCE.[IsSupplier]
|
||||
,SOURCE.[MappingId]
|
||||
,SOURCE.[ExternalId]
|
||||
,SOURCE.[Name]
|
||||
,SOURCE.[Note]
|
||||
,SOURCE.[SupportTelephone]
|
||||
,SOURCE.[SupportFax]
|
||||
,SOURCE.[SupportMail]
|
||||
,SOURCE.[QuickSearch]
|
||||
,SOURCE.[DateModified]
|
||||
,SOURCE.[CustomValues]
|
||||
,SOURCE.[MainContactId]);
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
|
||||
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
|
||||
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
|
||||
|
||||
THROW;
|
||||
END CATCH;
|
||||
END';
|
||||
|
||||
DECLARE @perms TABLE ([stmt] nvarchar(max) NOT NULL);
|
||||
DECLARE @stmt nvarchar(max);
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
-- ---- 1. the [@company] table variable in the invoice/request preparation procs
|
||||
-- Three column changes, matching the SSDT versions of these procs:
|
||||
-- [Note] nvarchar(255) -> nvarchar(max) (this fix)
|
||||
-- [CustomValues] NOT NULL -> NULL (nullable in [mfr__companies]; NOT NULL here
|
||||
-- [MainContactId] NOT NULL -> NULL raised 515 on INSERT ... SELECT cy.* for a
|
||||
-- company without main contact / custom values)
|
||||
-- Only the "DECLARE @company TABLE ( ... );" block is edited: the same column names occur in other
|
||||
-- table variables of these procs (e.g. @SReq.[CustomValues] NOT NULL), which must stay as they are.
|
||||
DECLARE @procname sysname, @def nvarchar(max), @pos int, @i int, @blkStart int, @blkEnd int, @blk nvarchar(max), @blkNew nvarchar(max);
|
||||
DECLARE @blkMarker nvarchar(50) = N'DECLARE @company TABLE';
|
||||
DECLARE proc_cur CURSOR LOCAL FAST_FORWARD FOR
|
||||
SELECT v.[name] FROM (VALUES (N'fds__getRequest_details'), (N'fds__prepInvoice'), (N'fds__prepStorno_recreate')) AS v([name]);
|
||||
OPEN proc_cur;
|
||||
FETCH NEXT FROM proc_cur INTO @procname;
|
||||
WHILE @@FETCH_STATUS = 0
|
||||
BEGIN
|
||||
SET @def = OBJECT_DEFINITION(OBJECT_ID(N'[dbo].' + QUOTENAME(@procname)));
|
||||
-- Locate the CREATE keyword of "CREATE <whitespace> PROCEDURE" (deployed headers vary in spacing).
|
||||
SET @pos = CHARINDEX(N'PROCEDURE', ISNULL(@def, N''));
|
||||
SET @i = @pos - 1;
|
||||
WHILE @i > 0 AND SUBSTRING(@def, @i, 1) IN (N' ', NCHAR(9), NCHAR(10), NCHAR(13)) SET @i -= 1;
|
||||
SET @pos = CASE WHEN @pos > 0 AND @i >= 6 AND SUBSTRING(@def, @i - 5, 6) = N'CREATE' THEN @i - 5 ELSE 0 END;
|
||||
-- Locate the single "DECLARE @company TABLE ( ... );" block (ends at the first ');' after the marker).
|
||||
SET @blkStart = CHARINDEX(@blkMarker, ISNULL(@def, N''));
|
||||
SET @blkEnd = CASE WHEN @blkStart > 0 THEN CHARINDEX(N');', @def, @blkStart) ELSE 0 END;
|
||||
IF @def IS NULL
|
||||
RAISERROR(N'WARNING: [dbo].[%s] not found - skipped.', 10, 1, @procname) WITH NOWAIT;
|
||||
ELSE IF @pos = 0 OR @blkStart = 0 OR @blkEnd = 0 OR CHARINDEX(@blkMarker, @def, @blkStart + 1) > 0
|
||||
RAISERROR(N'WARNING: [dbo].[%s] could not be patched automatically (no CREATE PROCEDURE header, or the @company TABLE block is missing/ambiguous) - edit its @company declaration by hand: [Note] nvarchar(max), [CustomValues] NULL, [MainContactId] NULL.', 10, 1, @procname) WITH NOWAIT;
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @blk = SUBSTRING(@def, @blkStart, @blkEnd - @blkStart + 2);
|
||||
SET @blkNew = REPLACE(@blk, N'[Note] [nvarchar](255) NULL', N'[Note] [nvarchar](max) NULL');
|
||||
SET @blkNew = REPLACE(@blkNew, N'[CustomValues] [nvarchar](max) NOT NULL', N'[CustomValues] [nvarchar](max) NULL');
|
||||
SET @blkNew = REPLACE(@blkNew, N'[MainContactId] [bigint] NOT NULL', N'[MainContactId] [bigint] NULL');
|
||||
IF @blkNew = @blk
|
||||
PRINT CONCAT(N'[', @procname, N'] @company block already matches SSDT - skipped.');
|
||||
ELSE
|
||||
BEGIN
|
||||
-- block first (it lies after the header), then CREATE -> ALTER (changes the length before it)
|
||||
SET @def = STUFF(@def, @blkStart, DATALENGTH(@blk) / 2, @blkNew);
|
||||
SET @def = STUFF(@def, @pos, LEN(N'CREATE'), N'ALTER');
|
||||
EXEC (@def);
|
||||
PRINT CONCAT(N'[', @procname, N'] @company block patched: [Note] nvarchar(max), [CustomValues] NULL, [MainContactId] NULL.');
|
||||
END
|
||||
END
|
||||
FETCH NEXT FROM proc_cur INTO @procname;
|
||||
END
|
||||
CLOSE proc_cur;
|
||||
DEALLOCATE proc_cur;
|
||||
|
||||
-- ---- 2. target tables
|
||||
IF EXISTS (SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'[dbo].[mfr__serviceobjects]') AND [name] = N'Note' AND [max_length] <> -1)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[mfr__serviceobjects] ALTER COLUMN [Note] NVARCHAR (MAX) NULL;
|
||||
PRINT N'[mfr__serviceobjects].[Note] widened to NVARCHAR(MAX).';
|
||||
END
|
||||
ELSE PRINT N'[mfr__serviceobjects].[Note] already NVARCHAR(MAX) - skipped.';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'[dbo].[mfr__companies]') AND [name] = N'Note' AND [max_length] <> -1)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[mfr__companies] ALTER COLUMN [Note] NVARCHAR (MAX) NULL;
|
||||
PRINT N'[mfr__companies].[Note] widened to NVARCHAR(MAX).';
|
||||
END
|
||||
ELSE PRINT N'[mfr__companies].[Note] already NVARCHAR(MAX) - skipped.';
|
||||
|
||||
-- ---- 3. table types (+ the merge procs that declare them)
|
||||
|
||||
-- ---- [mfr__tt__serviceobjects] + [mfr__updt__serviceobjects]
|
||||
IF EXISTS (SELECT * FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id]
|
||||
WHERE tt.[schema_id] = SCHEMA_ID(N'dbo') AND tt.[name] = N'mfr__tt__serviceobjects' AND c.[name] = N'Note' AND c.[max_length] <> -1)
|
||||
BEGIN
|
||||
-- A table type cannot be ALTERed. [mfr__updt__serviceobjects] DECLAREs it, so the proc is dropped
|
||||
-- first and recreated from the SSDT source afterwards; explicit grants are re-applied.
|
||||
INSERT INTO @perms ([stmt])
|
||||
SELECT CONCAT(CASE p.[state] WHEN 'D' THEN N'DENY ' ELSE N'GRANT ' END, p.[permission_name],
|
||||
CASE p.[class] WHEN 1 THEN N' ON OBJECT::[dbo].' + QUOTENAME(o.[name]) ELSE N' ON TYPE::[dbo].' + QUOTENAME(t.[name]) END,
|
||||
N' TO ', QUOTENAME(USER_NAME(p.[grantee_principal_id])),
|
||||
CASE p.[state] WHEN 'W' THEN N' WITH GRANT OPTION' ELSE N'' END, N';')
|
||||
FROM sys.database_permissions AS p
|
||||
LEFT JOIN sys.objects AS o ON p.[class] = 1 AND o.[object_id] = p.[major_id]
|
||||
LEFT JOIN sys.types AS t ON p.[class] = 6 AND t.[user_type_id] = p.[major_id]
|
||||
WHERE (p.[class] = 1 AND o.[object_id] = OBJECT_ID(N'[dbo].[mfr__updt__serviceobjects]'))
|
||||
OR (p.[class] = 6 AND t.[name] = N'mfr__tt__serviceobjects' AND t.[schema_id] = SCHEMA_ID(N'dbo'));
|
||||
|
||||
IF OBJECT_ID(N'[dbo].[mfr__updt__serviceobjects]', N'P') IS NOT NULL DROP PROCEDURE [dbo].[mfr__updt__serviceobjects];
|
||||
DROP TYPE [dbo].[mfr__tt__serviceobjects];
|
||||
EXEC (@type_so);
|
||||
EXEC (@proc_so);
|
||||
PRINT N'[mfr__tt__serviceobjects] recreated with [Note] NVARCHAR(MAX); [mfr__updt__serviceobjects] recreated.';
|
||||
END
|
||||
ELSE PRINT N'[mfr__tt__serviceobjects].[Note] already NVARCHAR(MAX) - skipped.';
|
||||
|
||||
-- ---- [mfr__tt__companies] + [mfr__updt__companies]
|
||||
IF EXISTS (SELECT * FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id]
|
||||
WHERE tt.[schema_id] = SCHEMA_ID(N'dbo') AND tt.[name] = N'mfr__tt__companies' AND c.[name] = N'Note' AND c.[max_length] <> -1)
|
||||
BEGIN
|
||||
-- A table type cannot be ALTERed. [mfr__updt__companies] DECLAREs it, so the proc is dropped
|
||||
-- first and recreated from the SSDT source afterwards; explicit grants are re-applied.
|
||||
INSERT INTO @perms ([stmt])
|
||||
SELECT CONCAT(CASE p.[state] WHEN 'D' THEN N'DENY ' ELSE N'GRANT ' END, p.[permission_name],
|
||||
CASE p.[class] WHEN 1 THEN N' ON OBJECT::[dbo].' + QUOTENAME(o.[name]) ELSE N' ON TYPE::[dbo].' + QUOTENAME(t.[name]) END,
|
||||
N' TO ', QUOTENAME(USER_NAME(p.[grantee_principal_id])),
|
||||
CASE p.[state] WHEN 'W' THEN N' WITH GRANT OPTION' ELSE N'' END, N';')
|
||||
FROM sys.database_permissions AS p
|
||||
LEFT JOIN sys.objects AS o ON p.[class] = 1 AND o.[object_id] = p.[major_id]
|
||||
LEFT JOIN sys.types AS t ON p.[class] = 6 AND t.[user_type_id] = p.[major_id]
|
||||
WHERE (p.[class] = 1 AND o.[object_id] = OBJECT_ID(N'[dbo].[mfr__updt__companies]'))
|
||||
OR (p.[class] = 6 AND t.[name] = N'mfr__tt__companies' AND t.[schema_id] = SCHEMA_ID(N'dbo'));
|
||||
|
||||
IF OBJECT_ID(N'[dbo].[mfr__updt__companies]', N'P') IS NOT NULL DROP PROCEDURE [dbo].[mfr__updt__companies];
|
||||
DROP TYPE [dbo].[mfr__tt__companies];
|
||||
EXEC (@type_co);
|
||||
EXEC (@proc_co);
|
||||
PRINT N'[mfr__tt__companies] recreated with [Note] NVARCHAR(MAX); [mfr__updt__companies] recreated.';
|
||||
END
|
||||
ELSE PRINT N'[mfr__tt__companies].[Note] already NVARCHAR(MAX) - skipped.';
|
||||
|
||||
DECLARE perm_cur CURSOR LOCAL FAST_FORWARD FOR SELECT [stmt] FROM @perms;
|
||||
OPEN perm_cur;
|
||||
FETCH NEXT FROM perm_cur INTO @stmt;
|
||||
WHILE @@FETCH_STATUS = 0
|
||||
BEGIN
|
||||
EXEC (@stmt);
|
||||
PRINT N'Re-applied: ' + @stmt;
|
||||
FETCH NEXT FROM perm_cur INTO @stmt;
|
||||
END
|
||||
CLOSE perm_cur;
|
||||
DEALLOCATE perm_cur;
|
||||
|
||||
-- ---- 4. queue a re-sync of the rejected rows
|
||||
-- Rows with a >255-char Note in mfr on 2026-09-30 (read-only scan of the mfr OData API).
|
||||
DECLARE @resync TABLE ([entity_name] varchar(255) NOT NULL, [Id] bigint NOT NULL);
|
||||
INSERT INTO @resync ([entity_name], [Id]) VALUES
|
||||
('ServiceObject', 24296783880), ('ServiceObject', 34080915468), ('ServiceObject', 38722404355)
|
||||
,('ServiceObject', 105289089024), ('ServiceObject', 105292464132), ('ServiceObject', 105292464134)
|
||||
,('Company', 104430043136), ('Company', 105288990720);
|
||||
-- Plus every row of the last rejected batch kept by the data service (if present).
|
||||
IF OBJECT_ID(N'[dbo].[t_err_mfr__serviceobjects]', N'U') IS NOT NULL
|
||||
INSERT INTO @resync EXEC (N'SELECT DISTINCT ''ServiceObject'', CAST([Id] AS bigint) FROM [dbo].[t_err_mfr__serviceobjects] WHERE [Id] IS NOT NULL;');
|
||||
IF OBJECT_ID(N'[dbo].[t_err_mfr__companies]', N'U') IS NOT NULL
|
||||
INSERT INTO @resync EXEC (N'SELECT DISTINCT ''Company'', CAST([Id] AS bigint) FROM [dbo].[t_err_mfr__companies] WHERE [Id] IS NOT NULL;');
|
||||
|
||||
INSERT INTO [dbo].[fds__mfr_updaterequests] ([entity_name], [Id])
|
||||
SELECT DISTINCT r.[entity_name], r.[Id] FROM @resync AS r
|
||||
WHERE NOT EXISTS (SELECT * FROM [dbo].[fds__mfr_updaterequests] AS q
|
||||
WHERE q.[entity_name] = r.[entity_name] AND q.[Id] = r.[Id]);
|
||||
PRINT CONCAT(N'Re-sync requests queued: ', @@ROWCOUNT);
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
PRINT N'mfr truncation fix applied.';
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
THROW;
|
||||
END CATCH;
|
||||
GO
|
||||
|
||||
-- ---- verification
|
||||
-- Expect max_length = -1 on all four rows.
|
||||
SELECT [object] = o.[name], [column] = c.[name], c.[max_length]
|
||||
FROM sys.columns AS c JOIN sys.objects AS o ON o.[object_id] = c.[object_id]
|
||||
WHERE c.[name] = N'Note' AND o.[name] IN (N'mfr__serviceobjects', N'mfr__companies')
|
||||
UNION ALL
|
||||
SELECT tt.[name], c.[name], c.[max_length]
|
||||
FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id]
|
||||
WHERE c.[name] = N'Note' AND tt.[name] IN (N'mfr__tt__serviceobjects', N'mfr__tt__companies');
|
||||
-- Expect no row (every @company block widened and made nullable; other table variables are not inspected).
|
||||
SELECT [still_unpatched] = o.[name], [company_block] = b.[blk]
|
||||
FROM sys.sql_modules AS m JOIN sys.objects AS o ON o.[object_id] = m.[object_id]
|
||||
CROSS APPLY (SELECT [s] = CHARINDEX(N'DECLARE @company TABLE', m.[definition])) AS p
|
||||
CROSS APPLY (SELECT [blk] = CASE WHEN p.[s] > 0 THEN SUBSTRING(m.[definition], p.[s], CHARINDEX(N');', m.[definition], p.[s]) - p.[s] + 2) ELSE N'<no @company block>' END) AS b
|
||||
WHERE o.[name] IN (N'fds__getRequest_details', N'fds__prepInvoice', N'fds__prepStorno_recreate')
|
||||
AND (b.[blk] LIKE N'%[[]Note] [[]nvarchar](255)%'
|
||||
OR b.[blk] LIKE N'%[[]CustomValues] [[]nvarchar](max) NOT NULL%'
|
||||
OR b.[blk] LIKE N'%[[]MainContactId] [[]bigint] NOT NULL%'
|
||||
OR p.[s] = 0);
|
||||
-- Once the worker has run, the queue drains and the counts approach mfr's
|
||||
-- (2026-09-30: 5531 ServiceObjects, 4625 Companies).
|
||||
SELECT [queued] = COUNT(*) FROM [dbo].[fds__mfr_updaterequests] WHERE [entity_name] IN ('ServiceObject', 'Company');
|
||||
SELECT [serviceobjects] = (SELECT COUNT(*) FROM [dbo].[mfr__serviceobjects]), [companies] = (SELECT COUNT(*) FROM [dbo].[mfr__companies]);
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Application logins (SQL authentication). They already exist on every environment and their real
|
||||
-- passwords live in Azure Key Vault; nothing here is a secret.
|
||||
--
|
||||
-- The file exists so the users in this folder (fuchs_fds, fuchs_web) bind to a login when the project is
|
||||
-- built: SSDT reports a user whose login is not in the model as error SQL71501, which is what kept the
|
||||
-- database project from building at all (ADR 0036). On an existing server the deployment sees the login
|
||||
-- as present and leaves it alone (SSDT never compares passwords); on a fresh server it is created with
|
||||
-- an expired placeholder that must be changed before the login can be used.
|
||||
CREATE LOGIN [fuchs_fds] WITH PASSWORD = N'Change-Me-Before-First-Use-1!' MUST_CHANGE, CHECK_EXPIRATION = ON, CHECK_POLICY = ON;
|
||||
GO
|
||||
CREATE LOGIN [fuchs_web] WITH PASSWORD = N'Change-Me-Before-First-Use-1!' MUST_CHANGE, CHECK_EXPIRATION = ON, CHECK_POLICY = ON;
|
||||
GO
|
||||
@@ -12,9 +12,13 @@ CREATE FUNCTION [dbo].[fds__fn_IntermediateIsAllocatedToOther]
|
||||
RETURNS bit
|
||||
AS
|
||||
BEGIN
|
||||
DECLARE @IsA bit = CASE WHEN EXISTS (SELECT * FROM [dbo].[fds__invoices] as intermediates JOIN [dbo].[fds__invoice_details] as id on intermediates.[Id] = id.[InvId] and id.[AllocatedTo_InvId] is not null
|
||||
-- An allocation only counts while the crediting invoice is alive: once that invoice is cancelled
|
||||
-- (storno) its credit line is void and the Abschlagsrechnung must be offered again (ADR 0034).
|
||||
DECLARE @IsA bit = CASE WHEN EXISTS (SELECT 1 FROM [dbo].[fds__invoices] as intermediates JOIN [dbo].[fds__invoice_details] as id on intermediates.[Id] = id.[InvId] and id.[AllocatedTo_InvId] is not null
|
||||
JOIN [dbo].[fds__invoices] as non_intermediates ON id.[AllocatedTo_InvId] = non_intermediates.[Id] and id.[AllocatedTo_InvId] is not null
|
||||
WHERE intermediates.[Id] = @Intermediate_InvID and (@Target_InvID is null or non_intermediates.[Id] <> @Target_InvID))
|
||||
WHERE intermediates.[Id] = @Intermediate_InvID
|
||||
and ISNULL(non_intermediates.[IsCanceled], 0) = 0
|
||||
and (@Target_InvID is null or non_intermediates.[Id] <> @Target_InvID))
|
||||
THEN 1 ELSE 0 END;
|
||||
|
||||
RETURN @isA;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
-- =============================================
|
||||
-- Description: Invoice backdating (ADR 0032). Returns the date an invoice may be backdated to
|
||||
-- (the last working day Mon-Fri of the previous month, Europe/Berlin), or NULL when
|
||||
-- backdating is not allowed:
|
||||
-- * @authuser needs fds_inv > 2, and
|
||||
-- * no invoice may have been finalized after that date yet (invoice numbers are
|
||||
-- issued in date order; DateFinalized is stored in UTC, hence the 2h margin).
|
||||
-- Single source for fds__setInvoiceFinal (@backdate = 1) and the preview dialog.
|
||||
-- The check is only race-free inside fds__setInvoiceFinal, which serializes all
|
||||
-- finalizations with a transaction-owned app lock; the preview call is advisory.
|
||||
-- =============================================
|
||||
CREATE FUNCTION [dbo].[fds__fn_invoiceBackdate]
|
||||
(
|
||||
@authuser varchar(25)
|
||||
)
|
||||
RETURNS date
|
||||
AS
|
||||
BEGIN
|
||||
IF [dbo].[fis_getModuleAuth]('fds_inv', @authuser) <= 2
|
||||
RETURN NULL;
|
||||
|
||||
DECLARE @today date = CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'W. Europe Standard Time' AS date);
|
||||
DECLARE @eom date = EOMONTH(@today, -1);
|
||||
-- DATEFIRST-independent weekday: 0 = Monday ... 5 = Saturday, 6 = Sunday
|
||||
DECLARE @bd date = DATEADD(day, CASE DATEDIFF(day, '19000101', @eom) % 7 WHEN 5 THEN -1 WHEN 6 THEN -2 ELSE 0 END, @eom);
|
||||
|
||||
IF EXISTS (SELECT 0 FROM [dbo].[fds__invoices]
|
||||
WHERE [DateFinalized] IS NOT NULL
|
||||
AND [DateFinalized] >= DATEADD(HOUR, -2, CAST(DATEADD(DAY, 1, @bd) AS datetime)))
|
||||
RETURN NULL;
|
||||
|
||||
RETURN @bd;
|
||||
END
|
||||
@@ -0,0 +1,18 @@
|
||||
-- =============================================
|
||||
-- Description: The invoice date ("Rechnungsdatum", ADR 0032) as a German calendar day:
|
||||
-- DateFinalized (the finalisation - or, when backdated, the backdated - moment),
|
||||
-- falling back to DateCreated for drafts. Both columns are stored in UTC.
|
||||
-- SQL twin of Fuchs/code/InvoiceDates.IssueDate. Every list, report, export and
|
||||
-- reminder that shows or groups by an invoice date uses this function (ADR 0035).
|
||||
-- Also used for reminders (fds__reminder has the same DateFinalized/DateCreated pair).
|
||||
-- =============================================
|
||||
CREATE FUNCTION [dbo].[fds__fn_invoiceDate]
|
||||
(
|
||||
@DateFinalized datetime,
|
||||
@DateCreated datetime
|
||||
)
|
||||
RETURNS date
|
||||
AS
|
||||
BEGIN
|
||||
RETURN CAST(ISNULL(@DateFinalized, @DateCreated) AT TIME ZONE 'UTC' AT TIME ZONE 'W. Europe Standard Time' AS date);
|
||||
END
|
||||
@@ -20,11 +20,11 @@ BEGIN
|
||||
|
||||
WITH inv as (
|
||||
SELECT
|
||||
[Dateofcreation] = i.[DateFinalized]
|
||||
[Dateofcreation] = [dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated])
|
||||
,i.[InvoiceBalance]
|
||||
,[InvoiceBalanceNetto] = i.[InvoiceBalance_net]
|
||||
FROM [dbo].[fds__invoices] as i
|
||||
WHERE i.[isFinal] = 1 AND [dbo].[date_monthfirst](i.[DateFinalized]) = @thismonthfirst
|
||||
WHERE i.[isFinal] = 1 AND [dbo].[date_monthfirst]([dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated])) = @thismonthfirst
|
||||
)
|
||||
SELECT TOP(1) @ret = FORMAT( SUM( ISNULL(inv.[InvoiceBalanceNetto],0) ) * 0.001, '#,0 k€', 'de') FROM inv;
|
||||
END
|
||||
|
||||
@@ -14,7 +14,7 @@ BEGIN
|
||||
DECLARE @groups TABLE([usergroup_id] varchar(5));
|
||||
INSERT INTO @groups SELECT * from STRING_SPLIT(@usergroup_id,',');
|
||||
|
||||
DECLARE @isMember bit = CASE WHEN EXISTS (SELECT * FROM [dbo].[fis_usergroups] as g
|
||||
DECLARE @isMember bit = CASE WHEN EXISTS (SELECT 1 FROM [dbo].[fis_usergroups] as g
|
||||
JOIN @groups as groupstotest On groupstotest.[usergroup_id] = g.[usergroup_id]
|
||||
JOIN [dbo].[fis_usergroups_members] as gm on g.[usergroup_id] = gm.[usergroup_id] and gm.[DateDeleted] is null AND g.[DateDeleted] is null
|
||||
JOIN [dbo].[fis_useraccounts] as u on gm.[useraccount_id] = u.[useraccount_id]
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
|
||||
CREATE FUNCTION [dbo].[ocms_fn_generatePassword] (
|
||||
@length int
|
||||
, @specialChars bit = 1
|
||||
, @nums bit = 1
|
||||
, @lowerCase bit = 1
|
||||
)
|
||||
RETURNS varchar(255)
|
||||
AS
|
||||
BEGIN
|
||||
DECLARE @randInt int;
|
||||
DECLARE @NewCharacter varchar(1);
|
||||
DECLARE @NewPassword TABLE([typ] char(1), [char] char(1), [order] int);
|
||||
|
||||
SET @length = CASE WHEN @length > 255 THEN 255 ELSE @length END;
|
||||
|
||||
DECLARE @cnt int = 0, @position as int;
|
||||
--random characters
|
||||
WHILE (@cnt < @length)
|
||||
BEGIN
|
||||
SELECT @randInt = [dbo].[ocms_fn_randbetween](33,122)
|
||||
|
||||
IF (CASE WHEN (@randInt>=33 AND @randInt<=47) AND @specialChars = 1 THEN 1
|
||||
WHEN (@randInt>=48 AND @randInt<=57) AND @nums = 1 THEN 1
|
||||
WHEN (@randInt>=58 AND @randInt<=64) AND @specialChars = 1 THEN 1
|
||||
WHEN (@randInt>=65 AND @randInt<=90) THEN 1
|
||||
WHEN (@randInt>=91 AND @randInt<=93) AND @specialChars = 1 THEN 1
|
||||
WHEN (@randInt>=97 AND @randInt<=122) AND @lowerCase = 1 THEN 1
|
||||
ELSE 0
|
||||
END) = 1
|
||||
BEGIN
|
||||
SET @cnt = @cnt + 1;
|
||||
|
||||
INSERT INTO @NewPassword
|
||||
VALUES (CASE WHEN (@randInt>=33 AND @randInt<=47) THEN 's'
|
||||
WHEN (@randInt>=48 AND @randInt<=57) THEN 'n'
|
||||
WHEN (@randInt>=58 AND @randInt<=64) THEN 's'
|
||||
WHEN (@randInt>=65 AND @randInt<=90) THEN 'u'
|
||||
WHEN (@randInt>=91 AND @randInt<=93) THEN 's'
|
||||
WHEN (@randInt>=97 AND @randInt<=122) THEN 'l'
|
||||
END,
|
||||
CHAR(@randInt), @cnt);
|
||||
END
|
||||
END
|
||||
|
||||
DECLARE @min_n int = CASE WHEN @length > 4 AND @length < 11 THEN 1 ELSE FLOOR(@length * 0.1) END;
|
||||
DECLARE @min_s int = FLOOR(@length * 0.1);
|
||||
DECLARE @min_l int = FLOOR(@length * 0.4);
|
||||
DECLARE @min_u int = FLOOR(@length * 0.4);
|
||||
|
||||
WHILE (SELECT COUNT(*) FROM @NewPassword WHERE [typ] = 'n') < @min_n AND @length > 0 AND @nums = 1
|
||||
BEGIN
|
||||
SET @position = [dbo].[ocms_fn_randbetween](1,@length);
|
||||
UPDATE @NewPassword SET [char] = CHAR([dbo].[ocms_fn_randbetween](48,57)), [typ] = 'n' WHERE [order] = @position;
|
||||
END
|
||||
|
||||
WHILE (SELECT COUNT(*) FROM @NewPassword WHERE [typ] = 'l') < @min_l AND @length > 0 AND @lowerCase = 1
|
||||
BEGIN
|
||||
SET @position = [dbo].[ocms_fn_randbetween](1,@length);
|
||||
UPDATE @NewPassword SET [char] = CHAR([dbo].[ocms_fn_randbetween](97,122)), [typ] = 'l' WHERE [order] = @position;
|
||||
END
|
||||
|
||||
WHILE (SELECT COUNT(*) FROM @NewPassword WHERE [typ] = 'u') < @min_n AND @length > 0
|
||||
BEGIN
|
||||
SET @position = [dbo].[ocms_fn_randbetween](1,@length);
|
||||
UPDATE @NewPassword SET [char] = CHAR([dbo].[ocms_fn_randbetween](65,90)), [typ] = 'u' WHERE [order] = @position;
|
||||
END
|
||||
|
||||
WHILE (SELECT COUNT(*) FROM @NewPassword WHERE [typ] = 's') < @min_s AND @length > 0 AND @specialChars = 1
|
||||
BEGIN
|
||||
SET @position = [dbo].[ocms_fn_randbetween](1,@length);
|
||||
DECLARE @nc char(1), @nt char(1);
|
||||
select @randInt=[dbo].[ocms_fn_randbetween](33,122)
|
||||
|
||||
UPDATE @NewPassword SET [char] = CHAR(@randint), [typ] = 's' WHERE [order] = @position AND (CASE WHEN (@randInt>=33 AND @randInt<=47) THEN 's'
|
||||
WHEN (@randInt>=48 AND @randInt<=57) THEN 'n'
|
||||
WHEN (@randInt>=58 AND @randInt<=64) THEN 's'
|
||||
WHEN (@randInt>=65 AND @randInt<=90) THEN 'u'
|
||||
WHEN (@randInt>=91 AND @randInt<=93) THEN 's'
|
||||
WHEN (@randInt>=97 AND @randInt<=122) THEN 'l'
|
||||
END) = 's';
|
||||
END
|
||||
|
||||
|
||||
|
||||
RETURN (Cast((SELECT [CHAR] as [text()] FROM @NewPassword FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') as varchar(255)));
|
||||
END;
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
CREATE FUNCTION [dbo].[ocms_fn_randbetween](@bottom integer, @top integer)
|
||||
RETURNS integer
|
||||
AS
|
||||
BEGIN
|
||||
RETURN (select cast(round((@top-@bottom) * [rn] +@bottom,0) as integer) from [dbo].[ocms_fn_rand])
|
||||
END
|
||||
@@ -96,7 +96,7 @@ BEGIN
|
||||
SELECT TOP(1)
|
||||
o.*
|
||||
, [InvoiceId] = inv.[InvoiceId]
|
||||
, [InvoiceDate] = inv.[DateFinalized]
|
||||
, [InvoiceDate] = [dbo].[fds__fn_invoiceDate](inv.[DateFinalized], inv.[DateCreated])
|
||||
, [hasFile] = CAST ( CASE WHEN o.[file] is null THEN 0 ELSE 1 END as bit)
|
||||
, [UserNameFinalized] = [dbo].[fis_admin_getUserName_byID](o.[UserFinalized])
|
||||
, [UserEmailFinalized] = [dbo].[fis_admin_getUserEmail_byID](o.[UserFinalized])
|
||||
|
||||
@@ -43,11 +43,16 @@ BEGIN
|
||||
;
|
||||
|
||||
|
||||
-- The invoice date (fds__fn_invoiceDate: DateFinalized as German day, ADR 0035) drives both the export
|
||||
-- period and the Belegdatum; DateCreated is only the draft's creation and may lie in a later month for
|
||||
-- a backdated invoice.
|
||||
-- [DateOfCreation] keeps its name for the callers (HandleDatev / FdsMfr.GetDatevZip) but carries that day.
|
||||
DECLARE @inv TABLE([Id] varchar(20), [InvoiceID] varchar(255), [DocumentName] nvarchar(255), [DateOfCreation] date, [fds] bit, [file_guid] varchar(36));
|
||||
INSERT INTO @inv
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0), [file_guid]
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = d.[InvoiceDate],[fds] = IIF([isExternal] = 0,1,0), [file_guid]
|
||||
FROM [dbo].[fds__invoices]
|
||||
where CAST([DateCreated] as date) between @startdate AND @enddate and [isfinal] = 1;
|
||||
CROSS APPLY (SELECT [InvoiceDate] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])) as d
|
||||
where [isfinal] = 1 and d.[InvoiceDate] between @startdate AND @enddate;
|
||||
|
||||
SELECT
|
||||
inv.[Id],inv.[InvoiceID],inv.[DocumentName],inv.[fds]
|
||||
@@ -78,15 +83,15 @@ BEGIN
|
||||
,[deb_cred] = CASE WHEN [invoiceBalance] >= 0 THEN 'S' ELSE 'H' END
|
||||
,[customer] = (SELECT TOP(1) [ExternalId] FROM [dbo].[mfr__companies] as c WHERE c.[Id] = iv.[CustomerId])
|
||||
,[contra_account] = ca.[account]
|
||||
,[Belegdatum] = iv.[DateCreated]
|
||||
,[Belegdatum] = i.[DateOfCreation] -- the invoice date (DateFinalized as German day), see @inv
|
||||
,[Buchungstext] = (SELECT TOP(1) SReq.[name] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id])
|
||||
,[DateOfCreation] = iv.[DateCreated]
|
||||
,[file_guid]
|
||||
from [dbo].[fds__invoices] as iv
|
||||
,[DateOfCreation] = i.[DateOfCreation]
|
||||
,[file_guid] = iv.[file_guid]
|
||||
from [dbo].[fds__invoices] as iv
|
||||
JOIN @inv as i on iv.[Id] = i.[Id]
|
||||
LEFT JOIN [dbo].[fds__invoice_details] as d on iv.[Id] = d.[InvId]
|
||||
LEFT JOIN sc on iv.[id] = sc.[invoice.Id]
|
||||
LEFT JOIN @contraaccounts as ca ON (CASE WHEN d.InvoiceOptions like '%§13b%' and RIGHT(ca.[name], 3) = '13b' then 1 WHEN d.InvoiceOptions like '%§13b%' THEN 0 when ISNULL(iv.[InvoiceVAT_1], 19.0) = ca.[vat] THEN 1 ELSE 0 END) = 1
|
||||
where iv.Id in (SELECT [Id] FROM @inv)
|
||||
)
|
||||
select
|
||||
[Umsatz (ohne Soll/Haben-Kz)] = ABS([balance])--CAST(FORMAT(ABS([balance]), '0.00', 'de') as varchar(13))
|
||||
|
||||
@@ -16,15 +16,15 @@ BEGIN
|
||||
IF @type = 'invoice'
|
||||
BEGIN
|
||||
SELECT TOP(1)
|
||||
[Id] = CAST([Id] as varchar(20)),[type] = @type, [InvoiceID],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = CAST( 1 as bit), [file]
|
||||
,[order] = ROW_NUMBER() OVER (ORDER BY [DateCreated] ASC)
|
||||
[Id] = CAST([Id] as varchar(20)),[type] = @type, [InvoiceID],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = CAST( 1 as bit), [file]
|
||||
,[order] = ROW_NUMBER() OVER (ORDER BY [DateFinalized] ASC)
|
||||
FROM [dbo].[fds__invoices] WHERE [id] = @reportid AND [isFinal] = 1;
|
||||
END
|
||||
ELSE IF @type = 'reminder'
|
||||
BEGIN
|
||||
SELECT TOP(1)
|
||||
[Id] = CAST([Id] as varchar(20)),[type] = @type, [ReminderID] = '',[DocumentName],[isInvoice] = 1,[DateOfCreation] = [DateCreated],[fds] = CAST( 1 as bit), [file]
|
||||
,[order] = ROW_NUMBER() OVER (ORDER BY [DateCreated] ASC)
|
||||
[Id] = CAST([Id] as varchar(20)),[type] = @type, [ReminderID] = '',[DocumentName],[isInvoice] = 1,[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = CAST( 1 as bit), [file]
|
||||
,[order] = ROW_NUMBER() OVER (ORDER BY [DateFinalized] ASC)
|
||||
FROM [dbo].[fds__reminder] WHERE [id] = @reportid AND [isFinal] = 1;
|
||||
END
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ BEGIN
|
||||
|
||||
|
||||
DECLARE @inv TABLE([Id] varchar(15), [InvoiceID] varchar(255), [URI] nvarchar(255), [DocumentName] nvarchar(255), [DateOfCreation] date, [fds] bit);
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0)
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = IIF([isExternal] = 0,1,0)
|
||||
FROM [dbo].[fds__invoices]
|
||||
where CAST([DateCreated] as date) between @startdate AND @enddate and (ISNULL(@include_drafts,0) = 1 OR [isfinal] = 1);
|
||||
where [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]) between @startdate AND @enddate and (ISNULL(@include_drafts,0) = 1 OR [isfinal] = 1);
|
||||
|
||||
DECLARE @SReq TABLE ([id] bigint, [DateOfCreation] datetime, [name] nvarchar(255), [invoice.Id] varchar(15), [customer.ExternalId] bigint, [customer.Id] bigint, [ExternalId] nvarchar(255), [fds] bit);
|
||||
INSERT INTO @SReq
|
||||
@@ -87,9 +87,9 @@ BEGIN
|
||||
,[deb_cred] = CASE WHEN [invoiceBalance] >= 0 THEN 'S' ELSE 'H' END
|
||||
,[customer] = (SELECT TOP(1) [customer.ExternalId] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id] and [customer.ExternalId] is not null ORDER BY SReq.DateOfCreation ASC)
|
||||
,[contra_account] = ca.[account]
|
||||
,[Belegdatum] = FORMAT(iv.[DateCreated], 'dd.MM.yy')
|
||||
,[Belegdatum] = FORMAT([dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated]), 'dd.MM.yy')
|
||||
,[Buchungstext] = (SELECT TOP(1) SReq.[name] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id])
|
||||
,[DateOfCreation] = iv.[DateCreated]
|
||||
,[DateOfCreation] = [dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated])
|
||||
,[DueDate]
|
||||
,[PaymentTerm] = ISNULL([PaymentTerm], @stage1)
|
||||
,[PaymentStatus]
|
||||
|
||||
@@ -69,23 +69,23 @@ BEGIN
|
||||
IF @searchmode = 'i'
|
||||
BEGIN
|
||||
insert into @inv
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0)
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = IIF([isExternal] = 0,1,0)
|
||||
FROM [dbo].[fds__invoices]
|
||||
where [InvoiceID] = @search OR [InvoiceID] like ('R20[0-9][0-9][-]' + @search);
|
||||
END
|
||||
ELSE IF @searchmode = 'c'
|
||||
BEGIN
|
||||
insert into @inv
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0)
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = IIF([isExternal] = 0,1,0)
|
||||
FROM [dbo].[fds__invoices]
|
||||
WHERE [CustomerId] IN (SELECT [CustomerId] fROM @customerfilter);
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
insert into @inv
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0)
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = IIF([isExternal] = 0,1,0)
|
||||
FROM [dbo].[fds__invoices]
|
||||
WHERE CAST([DateCreated] as date) between @startdate AND @enddate and (ISNULL(@include_drafts,0) = 1 OR [isfinal] = 1);
|
||||
WHERE [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]) between @startdate AND @enddate and (ISNULL(@include_drafts,0) = 1 OR [isfinal] = 1);
|
||||
END;
|
||||
|
||||
|
||||
@@ -126,9 +126,9 @@ BEGIN
|
||||
,[deb_cred] = CASE WHEN [invoiceBalance] >= 0 THEN 'S' ELSE 'H' END
|
||||
--,[customer] = (SELECT TOP(1) [customer.ExternalId] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id] and [customer.ExternalId] is not null ORDER BY SReq.DateOfCreation ASC)
|
||||
,[contra_account] = ca.[account]
|
||||
,[Belegdatum] = FORMAT(iv.[DateCreated], 'dd.MM.yy')
|
||||
,[Belegdatum] = FORMAT([dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated]), 'dd.MM.yy')
|
||||
,[Buchungstext] = (SELECT TOP(1) SReq.[name] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id])
|
||||
,[DateOfCreation] = iv.[DateCreated]
|
||||
,[DateOfCreation] = [dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated])
|
||||
,[DueDate]
|
||||
,[PaymentTerm] = ISNULL([PaymentTerm], @stage1)
|
||||
,[PaymentStatus]
|
||||
|
||||
@@ -74,9 +74,9 @@ BEGIN
|
||||
|
||||
DECLARE @inv TABLE([Id] varchar(15), [InvoiceID] varchar(255), [DocumentName] nvarchar(255), [DateOfCreation] date, [fds] bit);
|
||||
INSERT INTO @inv
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [DateCreated],[fds] = IIF([isExternal] = 0,1,0)
|
||||
SELECT [Id],[invoiceid],[DocumentName],[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = IIF([isExternal] = 0,1,0)
|
||||
FROM [dbo].[fds__invoices]
|
||||
where CAST([DateCreated] as date) between @startdate AND @enddate
|
||||
where [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]) between @startdate AND @enddate
|
||||
and (ISNULL(@include_drafts,0) = 1 OR [isfinal] = 1)
|
||||
and (@only_drafts = 0 OR [isfinal] = 0)
|
||||
and (@only_akonto = 0 OR [InvoiceType] = 'i')
|
||||
@@ -120,9 +120,9 @@ BEGIN
|
||||
,[deb_cred] = CASE WHEN [invoiceBalance] >= 0 THEN 'S' ELSE 'H' END
|
||||
,[customer] = (SELECT TOP(1) [customer.ExternalId] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id] and [customer.ExternalId] is not null ORDER BY SReq.DateOfCreation ASC)
|
||||
,[contra_account] = ca.[account]
|
||||
,[Belegdatum] = FORMAT(iv.[DateCreated], 'dd.MM.yy')
|
||||
,[Belegdatum] = FORMAT([dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated]), 'dd.MM.yy')
|
||||
,[Buchungstext] = (SELECT TOP(1) SReq.[name] FROM @SReq as sreq WHERE iv.[Id] = sreq.[invoice.Id])
|
||||
,[DateOfCreation] = iv.[DateCreated]
|
||||
,[DateOfCreation] = [dbo].[fds__fn_invoiceDate](iv.[DateFinalized], iv.[DateCreated])
|
||||
,[DueDate]
|
||||
,[PaymentTerm] = ISNULL([PaymentTerm], @stage1)
|
||||
,[PaymentStatus]
|
||||
|
||||
@@ -68,7 +68,7 @@ BEGIN
|
||||
)
|
||||
SELECT TOP(1) o.*
|
||||
,inv.[InvoiceId]
|
||||
,[InvoiceDate] = inv.[DateFinalized]
|
||||
,[InvoiceDate] = [dbo].[fds__fn_invoiceDate](inv.[DateFinalized], inv.[DateCreated])
|
||||
,[InvoiceFileName] = inv.[DocumentName]
|
||||
,[InvoiceFile] = CASE WHEN ISNULL(@includefile,0) = 1 THEN inv.[file] else NULL END -- do not return file if not explicitly requested
|
||||
,[hasFile] = CAST ( CASE WHEN o.[file] is null THEN 0 ELSE 1 END as bit)
|
||||
|
||||
@@ -13,9 +13,9 @@ BEGIN
|
||||
DECLARE @idbigint bigint = TRY_CAST(@reportid as bigint);
|
||||
|
||||
SELECT TOP(1)
|
||||
[Id] = CAST([Id] as varchar(20)),[InvoiceID],[URI] = '',[DocumentName],[isInvoice] = 1,[DateOfCreation] = [DateCreated],[fds] = CAST( 1 as bit)
|
||||
[Id] = CAST([Id] as varchar(20)),[InvoiceID],[URI] = '',[DocumentName],[isInvoice] = 1,[DateOfCreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]),[fds] = CAST( 1 as bit)
|
||||
, [file] = ISNULL(iv.[file], (SELECT TOP(1) mfrf.[file] FROM [dbo].[fds__mfr_invoicefiles] as mfrf where iv.[IsExternal] = 1 and mfrf.[id] = iv.[externalid]))
|
||||
, [order] = ROW_NUMBER() OVER (ORDER BY [DateCreated] ASC)
|
||||
, [order] = ROW_NUMBER() OVER (ORDER BY [DateFinalized] ASC)
|
||||
FROM [dbo].[fds__invoices] as iv
|
||||
WHERE [id] = @reportid AND [isFinal] = 1;
|
||||
;
|
||||
|
||||
@@ -216,14 +216,14 @@ BEGIN
|
||||
[MappingId] [nvarchar](255) NULL,
|
||||
[ExternalId] [nvarchar](255) NULL,
|
||||
[Name] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](max) NULL,
|
||||
[SupportTelephone] [nvarchar](255) NULL,
|
||||
[SupportFax] [nvarchar](255) NULL,
|
||||
[SupportMail] [nvarchar](255) NULL,
|
||||
[QuickSearch] [nvarchar](255) NULL,
|
||||
[DateModified] [datetime] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NOT NULL,
|
||||
[MainContactId] [bigint] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NULL,
|
||||
[MainContactId] [bigint] NULL,
|
||||
[address] nvarchar(1000) null
|
||||
);
|
||||
INSERT INTO @company SELECT DISTINCT cy.*, [address] = [dbo].[fds__getCompanyAddress](cy.[id]) FROM [mfr__companies] as cy JOIN @SReq as s on cy.id = s.[CustomerId];
|
||||
@@ -377,6 +377,7 @@ BEGIN
|
||||
)
|
||||
SELECT [Id] = CAST(i.[Id] as varchar(20))
|
||||
,i.[DateCreated]
|
||||
,[InvoiceDate] = [dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated])
|
||||
,[isFinal]
|
||||
,[isSent]
|
||||
,i.[DocumentName]
|
||||
@@ -387,10 +388,10 @@ BEGIN
|
||||
WHEN [DueDate] is null THEN 'op'
|
||||
WHEN [invoiceBalance] < 0 THEN ''
|
||||
WHEN @today >= [DueDate] THEN 'due'
|
||||
WHEN @stage1 <> '' AND ISNULL(rem.[#],0) = 0 AND @today > [dbo].[date_addcustom](ISNULL(DueDate, i.[DateCreated]), @stage1) THEN 'ovd'
|
||||
WHEN @stage1 <> '' AND ISNULL(rem.[#],0) = 0 AND @today > [dbo].[date_addcustom](ISNULL(DueDate, [dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated])), @stage1) THEN 'ovd'
|
||||
WHEN ISNULL(rem.[#],0) > 0 THEN 'rem'
|
||||
END
|
||||
,[order] = ROW_NUMBER() OVER ( ORDER BY inv.[isChild], inv.[baseorder], i.[isFinal] DESC, i.[datecreated] ASC )
|
||||
,[order] = ROW_NUMBER() OVER ( ORDER BY inv.[isChild], inv.[baseorder], i.[isFinal] DESC, [dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated]) ASC )
|
||||
FROM [dbo].[fds__invoices] as i
|
||||
JOIN inv on i.[Id] = inv.[Id]
|
||||
LEFT JOIN reminder as rem on inv.id = rem.[InvId]
|
||||
|
||||
@@ -189,14 +189,14 @@ BEGIN
|
||||
[MappingId] [nvarchar](255) NULL,
|
||||
[ExternalId] [nvarchar](255) NULL,
|
||||
[Name] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](max) NULL,
|
||||
[SupportTelephone] [nvarchar](255) NULL,
|
||||
[SupportFax] [nvarchar](255) NULL,
|
||||
[SupportMail] [nvarchar](255) NULL,
|
||||
[QuickSearch] [nvarchar](255) NULL,
|
||||
[DateModified] [datetime] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NOT NULL,
|
||||
[MainContactId] [bigint] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NULL,
|
||||
[MainContactId] [bigint] NULL,
|
||||
[address] nvarchar(1000) null
|
||||
);
|
||||
INSERT INTO @company SELECT DISTINCT cy.*, [address] = [dbo].[fds__getCompanyAddress](cy.[id]) FROM [mfr__companies] as cy JOIN @SReq as s on cy.id = s.[CustomerId];
|
||||
@@ -228,6 +228,7 @@ BEGIN
|
||||
-- Add virtual items for intermediate invoices
|
||||
With associated_interims as (
|
||||
SELECT inv.InvoiceVAT_1
|
||||
,[InvId] = inv.[Id] --carried as reference_InvId so fds__setInvoiceFinal can record the allocation
|
||||
,inv.InvoiceBalance_net
|
||||
,inv.DocumentName
|
||||
,inv.DateCreated
|
||||
@@ -259,7 +260,7 @@ BEGIN
|
||||
,[NameOrNumber] = REPLACE(ai.DocumentName, '.pdf','')
|
||||
,[CustomValues] = NULL
|
||||
,[Note] = '<p>' + 'Anrechnung der ' + REPLACE(ai.DocumentName, '.pdf','') + ' vom ' + FORMAT(ai.[dateFinalized], 'dd.MM.yyyy', 'de-de') + '</p>'
|
||||
,[ExternalId] = NULL
|
||||
,[ExternalId] = ai.[InvId] --referenced intermediate invoice (-> reference_InvId)
|
||||
,[Discount] = NULL
|
||||
,[VAT] = ai.InvoiceVAT_1
|
||||
,[IsManual] = 1
|
||||
@@ -289,7 +290,7 @@ BEGIN
|
||||
,[NameOrNumber] = 'offener Betrag aus ' + REPLACE(ai.DocumentName, '.pdf','')
|
||||
,[CustomValues] = NULL
|
||||
,[Note] = '<p>' + 'Berechnung des offenen Betrags aus ' + REPLACE(ai.DocumentName, '.pdf','') + ' vom ' + FORMAT(ai.[dateFinalized], 'dd.MM.yyyy', 'de-de') + '</p><p>bereits gezahlt (brutto): ' + FORMAT([payed], '0.00', 'de') + '€.</p>'
|
||||
,[ExternalId] = NULL
|
||||
,[ExternalId] = ai.[InvId] --referenced intermediate invoice (-> reference_InvId)
|
||||
,[Discount] = NULL
|
||||
,[VAT] = ai.InvoiceVAT_1
|
||||
,[IsManual] = 1
|
||||
@@ -393,6 +394,7 @@ BEGIN
|
||||
, [Note] = IIF(ISNULL([Discount], 0) > 0, '<b>Rabatt '+ FORMAT([discount]*0.01,'#.0%','de-de') + '</b>', '') + ISNULL(itm.[Note],'')
|
||||
, [order] = (itm.[baseorder] * 100) + itm.[order]
|
||||
, [SetItmId]
|
||||
, [reference_InvId] = CASE WHEN itm.[Type] IN ('IntInvoiceAllocation', 'IntInvoiceAllocationDiff') THEN CAST(itm.[ExternalId] as varchar(20)) ELSE NULL END
|
||||
FROM @itm as itm
|
||||
LEFT JOIN [dbo].[fds__custom_vat] as vat ON itm.[Id] = vat.[EntityId] and vat.[EntityType] = 'item';
|
||||
END
|
||||
|
||||
@@ -49,7 +49,7 @@ BEGIN
|
||||
END
|
||||
, [type] = @type
|
||||
, [invoiceid] = inv.[Invoiceid]
|
||||
, [invoicedate] = inv.[DateFinalized]
|
||||
, [invoicedate] = [dbo].[fds__fn_invoiceDate](inv.[DateFinalized], inv.[DateCreated])
|
||||
, [amount] = inv.[InvoiceBalance]
|
||||
, [amount_payed] = CAST( ISNULL([dbo].[fds__fn_InvoicePaymentAmount_full](inv.[Id]),0.0) as numeric(10,2))
|
||||
FROM inv;
|
||||
|
||||
@@ -188,14 +188,14 @@ BEGIN
|
||||
[MappingId] [nvarchar](255) NULL,
|
||||
[ExternalId] [nvarchar](255) NULL,
|
||||
[Name] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](255) NULL,
|
||||
[Note] [nvarchar](max) NULL,
|
||||
[SupportTelephone] [nvarchar](255) NULL,
|
||||
[SupportFax] [nvarchar](255) NULL,
|
||||
[SupportMail] [nvarchar](255) NULL,
|
||||
[QuickSearch] [nvarchar](255) NULL,
|
||||
[DateModified] [datetime] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NOT NULL,
|
||||
[MainContactId] [bigint] NOT NULL,
|
||||
[CustomValues] [nvarchar](max) NULL,
|
||||
[MainContactId] [bigint] NULL,
|
||||
[address] nvarchar(1000) null
|
||||
);
|
||||
INSERT INTO @company SELECT DISTINCT cy.*, [address] = [dbo].[fds__getCompanyAddress](cy.[id]) FROM [mfr__companies] as cy JOIN @SReq as s on cy.id = s.[CustomerId];
|
||||
@@ -236,12 +236,12 @@ BEGIN
|
||||
,[NameOrNumber] = REPLACE(inv.DocumentName, '.pdf','')
|
||||
,[CustomValues] = NULL
|
||||
,[Note] = '<p>' + 'Anrechnung der ' + REPLACE(inv.DocumentName, '.pdf','') + ' vom ' + FORMAT(inv.[dateFinalized], 'dd.MM.yyyy', 'de-de') + '</p>'
|
||||
,[ExternalId] = NULL
|
||||
,[ExternalId] = inv.[Id] --referenced intermediate invoice (-> reference_InvId)
|
||||
,[Discount] = NULL
|
||||
,[VAT] = inv.InvoiceVAT_1
|
||||
,[IsManual] = 1
|
||||
,[SortOrder] = ROW_NUMBER() OVER (PARTITION BY srq.[mfr__servicerequest] ORDER BY inv.[dateFinalized], inv.DateCreated)
|
||||
,[Type] = 'PartialInvoice'
|
||||
,[Type] = 'IntInvoiceAllocation' -- same line type as fds__prepInvoice: only this type is taken over as an allocation by fds__setInvoiceFinal
|
||||
,[ServiceRequestId] = -2 --s.[id]
|
||||
,[ServiceObjectId] = Null
|
||||
,[CreatorId] = NULL
|
||||
@@ -292,7 +292,7 @@ BEGIN
|
||||
,[NameOrNumber] = REPLACE(inv.DocumentName, '.pdf','')
|
||||
,[CustomValues] = NULL
|
||||
,[Note] = '<p>' + 'Storno der ' + REPLACE(inv.DocumentName, '.pdf','') + ' vom ' + FORMAT(inv.[dateFinalized], 'dd.MM.yyyy', 'de-de') + '</p>'
|
||||
,[ExternalId] = NULL
|
||||
,[ExternalId] = inv.[Id] --replaced invoice (-> reference_InvId; fds__setInvoiceFinal builds Replaces_InvId from it)
|
||||
,[Discount] = NULL
|
||||
,[VAT] = inv.InvoiceVAT_1
|
||||
,[IsManual] = 1
|
||||
@@ -387,6 +387,7 @@ BEGIN
|
||||
, itm.[Note]
|
||||
, [order] = (itm.[baseorder] * 100) + itm.[order]
|
||||
, [SetItmId]
|
||||
, [reference_InvId] = CASE WHEN itm.[Type] IN ('IntInvoiceAllocation', 'CanceledInvoice') THEN CAST(itm.[ExternalId] as varchar(20)) ELSE NULL END
|
||||
FROM @itm as itm
|
||||
LEFT JOIN [dbo].[fds__custom_vat] as vat ON itm.[Id] = vat.[EntityId] and vat.[EntityType] = 'item';
|
||||
END
|
||||
|
||||
@@ -35,7 +35,7 @@ BEGIN
|
||||
|
||||
WITH inv as (
|
||||
SELECT
|
||||
[Dateofcreation] = i.[DateCreatEd]
|
||||
[Dateofcreation] = [dbo].[fds__fn_invoiceDate](i.[DateFinalized], i.[DateCreated])
|
||||
,i.[InvoiceBalance]
|
||||
,[InvoiceBalanceNetto] = i.[InvoiceBalance_net]
|
||||
FROM [dbo].[fds__invoices] as i
|
||||
|
||||
@@ -39,21 +39,21 @@ BEGIN
|
||||
WITH inv as(
|
||||
select
|
||||
[InvoiceBalance] = SUM( ISNULL(_i.[InvoiceBalance],0) )
|
||||
,[Dateofcreation] = CAST(_i.[DateCreated] as date)
|
||||
,[Dateofcreation] = [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated])
|
||||
,_i.CustomerId
|
||||
,[customerName] = cy.Name
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY CAST(_i.[DateCreated] as date))
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
FROM [dbo].[fds__invoices] as _i
|
||||
JOIN [dbo].[mfr__companies] as cy on _i.CustomerId = cy.id
|
||||
where YEAR(_i.[DateCreated]) = @year and _i.[isfinal] = 1
|
||||
GROUP BY CAST(_i.[DateCreated] as date), CustomerId, cy.name
|
||||
where YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated])) = @year and _i.[isfinal] = 1
|
||||
GROUP BY [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]), CustomerId, cy.name
|
||||
), yr1 as (
|
||||
SELECT
|
||||
[year] = YEAR( _i.[DateCreated] )
|
||||
[year] = YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
, [total] = SUM( ISNULL(_i.[InvoiceBalance],0) )
|
||||
FROM [dbo].[fds__invoices] as _i
|
||||
WHERE _i.[isfinal] = 1
|
||||
GROUP BY YEAR( _i.[DateCreated])
|
||||
GROUP BY YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
), yr as (
|
||||
SELECT [year], [total] = SUM([total])
|
||||
from yr1 GROUP BY [year]
|
||||
|
||||
@@ -40,20 +40,20 @@ BEGIN
|
||||
WITH inv as(
|
||||
SELECT
|
||||
[itmPrice] = SUM( ISNULL(itm.[value_total],0) )
|
||||
,[Dateofcreation] = CAST(_i.[DateCreated] as date)
|
||||
,[Dateofcreation] = [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated])
|
||||
,[material] = itm.[text]
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY CAST(_i.[DateCreated] as date))
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
FROM [dbo].[fds__invoices] as _i
|
||||
JOIN [fds__invoice_items] as itm on itm.[invid] = _i.[id] AND itm.[Type] like 'Material'
|
||||
where YEAR(_i.[DateCreated]) = @year and _i.[isfinal] = 1
|
||||
GROUP BY CAST(_i.[DateCreated] as date), itm.[text]
|
||||
where YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated])) = @year and _i.[isfinal] = 1
|
||||
GROUP BY [dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]), itm.[text]
|
||||
), yr1 as (
|
||||
SELECT
|
||||
[year] = YEAR( _i.[DateCreated] )
|
||||
[year] = YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
, [total] = SUM( ISNULL(_i.[InvoiceBalance],0) )
|
||||
FROM [dbo].[fds__invoices] as _i
|
||||
WHERE _i.[isfinal] = 1
|
||||
GROUP BY YEAR( _i.[DateCreated])
|
||||
GROUP BY YEAR([dbo].[fds__fn_invoiceDate](_i.[DateFinalized], _i.[DateCreated]))
|
||||
), yr as (
|
||||
SELECT [year], [total] = SUM([total])
|
||||
from yr1 GROUP BY [year]
|
||||
|
||||
@@ -47,10 +47,10 @@ BEGIN
|
||||
with inv as(
|
||||
select
|
||||
[InvoiceBalance] = SUM( ISNULL(_i.[InvoiceBalance],0) )
|
||||
,[Dateofcreation] = CAST([Datecreated] as date)
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY CAST([Datecreated] as date))
|
||||
FROM [dbo].[fds__invoices] as _i where _i.IsFinal = 1 and month([Datecreated]) = @Month and YEAR([Datecreated]) = @year
|
||||
GROUP BY CAST([Datecreated] as date)
|
||||
,[Dateofcreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]))
|
||||
FROM [dbo].[fds__invoices] as _i where _i.IsFinal = 1 and MONTH([dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])) = @Month and YEAR([dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])) = @year
|
||||
GROUP BY [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])
|
||||
)
|
||||
SELECT
|
||||
--[Monat] = FORMAT( inv.[Dateofcreation], 'yyyy - MM', 'de')
|
||||
|
||||
@@ -46,10 +46,10 @@ BEGIN
|
||||
with inv as(
|
||||
select
|
||||
[InvoiceBalance] = SUM( ISNULL(_i.[InvoiceBalance],0) )
|
||||
,[Dateofcreation] = CAST([Datecreated] as date)
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY CAST([Datecreated] as date))
|
||||
FROM [dbo].[fds__invoices] as _i where _i.IsFinal = 1 and YEAR([Datecreated]) = @year
|
||||
GROUP BY CAST([Datecreated] as date)
|
||||
,[Dateofcreation] = [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])
|
||||
,[seq] = ROW_NUMBER() OVER (ORDER BY [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated]))
|
||||
FROM [dbo].[fds__invoices] as _i where _i.IsFinal = 1 and YEAR([dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])) = @year
|
||||
GROUP BY [dbo].[fds__fn_invoiceDate]([DateFinalized], [DateCreated])
|
||||
)
|
||||
SELECT
|
||||
--[Monat] = FORMAT( inv.[Dateofcreation], 'yyyy - MM', 'de')
|
||||
|
||||
@@ -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
|
||||
@@ -9,7 +9,7 @@
|
||||
[MappingId] NVARCHAR (255) NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[SupportTelephone] NVARCHAR (255) NULL,
|
||||
[SupportFax] NVARCHAR (255) NULL,
|
||||
[SupportMail] NVARCHAR (255) NULL,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[Id] BIGINT NOT NULL,
|
||||
[Location#ID] BIGINT NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[CustomValues] NVARCHAR (MAX) NOT NULL,
|
||||
[DateModified] DATETIME NOT NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[MappingId] NVARCHAR (255) NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[SupportTelephone] NVARCHAR (255) NULL,
|
||||
[SupportFax] NVARCHAR (255) NULL,
|
||||
[SupportMail] NVARCHAR (255) NULL,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[Id] BIGINT NOT NULL,
|
||||
[Location#ID] BIGINT NULL,
|
||||
[Name] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (255) NULL,
|
||||
[Note] NVARCHAR (MAX) NULL,
|
||||
[CustomValues] NVARCHAR (MAX) NOT NULL,
|
||||
[DateModified] DATETIME NOT NULL,
|
||||
[ExternalId] NVARCHAR (255) NULL,
|
||||
|
||||
Reference in New Issue
Block a user