CREATE PROCEDURE [dbo].[DeleteOldCustomers](@DeletedFrom TinyInt) AS
/*
    Created by Uffe Wassmann 20070608
    Last Changed:

	MLJ 2021-01-04 Set lock_timeout to avoid deadlocks. Added 3 more related tables to delete from.
    vd 20171205 Further optimization on order book
    vd 20171113 Another go
    vd 20171109 Another go
    vd 20171003 Thoroughly revised. Has not beeen working for a long time
    KJ 20091209: Ext: Removed Debtors
    20070720UW: Added check for reservations too recent to create turnover in orderbook.
    20070716UW: Added check for customernumber in obsolete table related to the bonus card system.
    20070716UW: Added transaction dropping in case of errors.
    20070716UW: Added check against customers owning a room or house that the hotel rents out for them.
    20070625UW: Added nolocks to avoid the searches being locked out, and a transaction flag to the loop to avoid locking others out for too long.
    20070618UW: New 'Techotel' criteria, and Max deletions made functional.

    Parameters: -
    Input: Uses values from Hotel Setup/Basic/Database Clean/'Customer': int, 'Turnover': int
    Effect: Deletes all customers older than x months for which there has been no turnover inside y months.
	       Also takes into consideration if the customers have a bonuscard, and cleans the customer from related tables.
    Output: Number of customers deleted, total number of rows affected, and measured runtime.
    Notes: Procedure is split into two parts; finding relevant customers and the actual deletion. This is to ease a later separation into two procedures.
*/

SET NOCOUNT ON;
SET LOCK_TIMEOUT 100;

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--                 Initialization
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-- Something will always go awry
Declare @debug int
Set @debug = 5
Declare @debugint int
Set @debugint = 0
Declare @debugstr char
Set @debugstr = ''

-- Find out when the order book was last updated. 
-- Before that date, the activity of customers will be seen as turnover in OrderBook, and afterwards, it'll be necessary to search through recent reservations.
Declare @LastOrderBookUpdate DateTime
SELECT @LastOrderBookUpdate = KeyValue 
FROM HOTELSETUP WITH (Nolock) 
WHERE Searchkey = 'LastOrderBookUpdate'

-- Find comparison dates
Declare @SystemDateAsString char(8)
Select @SystemDateAsString = SYSTEMDATE FROM System WITH (nolock) WHERE NUMBER = 1 -- Only one record  
Declare @SystemDate DateTime
Set @SystemDate = Convert(DateTime, @SystemDateAsString) 

-- Read Hotel settings
-- Number of months since customers' creation date that has to have gone by before the customers are considered ripe for deletion.
Declare @KeepMonthCustomers int
Select @KeepMonthCustomers=KeyValue from HotelSetup WITH (NOLOCK) where SearchKey='KeepCustomerMonths'
If IsNull (@KeepMonthCustomers, 0) = 0
  Set @KeepMonthCustomers = 60

-- Number of months into the past that any kind of turnover will exempt an old customer from deletion. Revenue-makers shouldn't be deleted, after all..
DECLARE @AllowedMonthsInactivity INTEGER
SELECT @AllowedMonthsInactivity = ISNULL((SELECT KeyValue FROM HotelSetup WITH (NOLOCK) WHERE SearchKey = 'AllowedCustomerInactivityMonths'), 60)
  
-- Restricts number of deletions done in one run.
DECLARE @MaximumDeletes INTEGER
SELECT @MaximumDeletes = ISNULL((SELECT KeyValue FROM HotelSetup WITH (NOLOCK) WHERE SearchKey = 'MaximumDeletesCustomers'), 3000)

DECLARE @OldCreationDate DATETIME
Set @OldCreationDate = DATEADD(month, -1 * @KeepMonthCustomers, @SystemDate)

DECLARE @AllowedInactivityDate DATETIME
Set @AllowedInactivityDate = DATEADD(month, -1 * @AllowedMonthsInactivity, @SystemDate)

DECLARE @MaxCustomer INTEGER
SELECT @MaxCustomer = MAX(CUSTOMERNUMBER) FROM CUSTOMER WITH (NOLOCK) WHERE EstablishDate < @OldCreationDate


-- Tables used for storing entries to be deleted 
Declare @TmpDeleteFromFollowUp table (
FollowUpNumber int
)
Declare @TmpDeleteFromCard table (
FK_Card int
)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Find old customers to be deleted
-- Output: Table of customers to delete
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
-- 2:  Avoid customers that have bonuscards ('old friends')
SELECT DISTINCT CCX.CustomerNumber
Into #HasBonusCard
From CustomerCardXTab CCX WITH (nolock)
Inner join CardTypeXTab CTX WITH (nolock)
on CCX.FK_Card = CTX.FK_Card
Inner join CardType CT WITH (nolock)
on CTX.FK_CardType = CT.PK_CardType
Inner join Customer C WITH (nolock)
on CCX.CustomerNumber = C.CustomerNumber
--WHERE ISNULL(C.EstablishDate, @OldCreationDate) < @OldCreationDate
WHERE C.CustomerNumber < @MaxCustomer
  AND CT.Name = 'Bonus'
  AND CT.Enabled = 1

-- Debug/Verification  
if @debug >= 1
begin
	PRINT 'Number of customers with bonuscard:'
	set @debugint = (select count (*) from #HasBonusCard )
	PRINT @debugint
end

-- 3: Try to avoid deleting customer 'Techotel', on NB's suggestion.
--      Using the criteria of   'Customer with the lowest customernumber doesn't always work.
--      Using the name is somewhat better, but doesn't allow for the eventuality of a name-change of the comany.
SELECT DISTINCT C.CustomerNumber
Into #IsTechotel
From Customer C WITH (nolock)
Where Lastname like '%AK Techotel%' and CustomerNumber > 0 and CustomerTypenumber = 2

-- Debug/Verification  
if @debug >= 1
begin
  PRINT 'Number of Techotel customers:'
  set @debugint = ( select count (*) from #IsTechotel )
  PRINT @debugint
end

-- 4: Avoid customers that owns a room or house at the hotel.
SELECT DISTINCT R.CustomerNumber
Into #HasRoom
From Rooms R WITH (nolock)
Where CustomerNumber is not null

-- Debug/Verification  
if @debug >= 1
begin
  PRINT 'Number of customers who are also room-owners:'
  set @debugint = ( select count (*) from #HasRoom )
  PRINT @debugint
end

-- 5: Avoid customers that has activity not shown in OrderBook.. which mostly get updated about once a week.
--     Looking through recent reservations instead.
SELECT DISTINCT RA.Link, RA.ContactpersonCustomerNumber
into #RecentlyActive2
From Reservations R WITH (nolock)
inner join reservationaddressxtab RAX WITH (nolock)
on RAX.ReferenceNumber=R.Referencenumber and R.ArrivalDate >= @LastOrderBookUpdate
inner join reservationaddress RA WITH (nolock)
on RA.AddressNumber=RAX.Addressnumber

Select Link as CustomerNumber
Into #RecentlyActive
From #RecentlyActive2
union
select ContactPersonCustomerNumber as CustomerNumber
From #RecentlyActive2
drop table #RecentlyActive2

-- Debug/Verification  
if @debug >= 1
begin
  set @debugint = ( select count (*) from #RecentlyActive )
  PRINT cast(@debugint as varchar(10)) + ' customers with reservations not yet registered in orderbook.'
end

-- 6: Choose all candidates for deletion by customer type and creation date being before the other calculated date.
--     If a creation date is missing (equals Null), this is because the field was introduced later; some hotel databases may lack be older than the introduction of it.
--    Output: Table of customer numbers proposed for deletion
SELECT DISTINCT C.CustomerNumber
Into #OldCustomersForDeletionAll
From Customer C WITH (nolock)
WHERE C.CustomerTypeNumber IN ( 1, 2, 3, 6, 8, 9) -- Avoiding to delete SalesChannels, CompanyCorps and AgencyCorps.
  --AND (C.EstablishDate <= @OldCreationDate Or IsNull(C.EstablishDate, 0) = 0)
  --AND ISNULL(C.EstablishDate, @OldCreationDate) < @OldCreationDate
  AND C.CustomerNumber < @MaxCustomer

-- Debug/Verification
If @debug >= 1
Begin
	PRINT 'Number of customers for deletion irrespective of turnover, bonuscard or likely Techotel customer:'
	set @debugint = (select count (*) from #OldCustomersForDeletionAll)
	PRINT @debugint
End

-- 7: Filter the candidates generated above. Customers for deletion should have no bonuscard, nor any turnover from the given date.
-- Input: Tables of customer numbers to exclude from deletion
-- Output: Filtered list of customers to delete
SET Rowcount @MaximumDeletes -- Limit number of deletions planned to @MaximumDeletes, given via the interface.

SELECT DISTINCT OCFDA.CustomerNumber
INTO #OldCustomersForDeletion
FROM #OldCustomersForDeletionAll OCFDA
  LEFT OUTER JOIN #RecentlyActive RA   -- UW: Using 'Not in <select statement>' doesn't work for this table, for some reason. Selecting by 'left outer join'/'is null' is probably faster anyway.
ON OCFDA.CustomerNumber = RA.CustomerNumber
--WHERE OCFDA.CustomerNumber NOT IN (SELECT CustomerNumber FROM #HasTurnover)

WHERE OCFDA.CustomerNumber NOT IN (SELECT CustomerNumber FROM CustomerOrdreBook WITH (NOLOCK)
                                   WHERE CustomerNumber < @MaxCustomer
                                     AND DateForValue >= @AllowedInactivityDate
                                     AND TurnoverRooms + TurnoverDayMeetings + TurnoverHalls + TurnoverRestaurant +TurnoverOther > 0)

  AND OCFDA.CustomerNumber NOT IN (SELECT CustomerNumber FROM #HasBonusCard)
  AND OCFDA.CustomerNumber NOT IN (SELECT CustomerNumber FROM #IsTechotel)
  AND OCFDA.CustomerNumber NOT IN (SELECT CustomerNumber FROM #HasRoom)
  AND RA.CustomerNumber IS NULL

SET Rowcount 0 -- Resetting rowcount

-- Debug/Verification
If @debug >= 1
Begin
	PRINT 'Number of customers for deletion:'
	set @debugint = (select count (*) from #OldCustomersForDeletion)
	PRINT @debugint
End

-- Update date for last time this procedure was run
Declare @CustomersDeletedBefore SMALLDATETIME
Declare @tmpString CHAR(8)

Select @tmpString=IsNull(KeyValue,0) From HotelSetup WITH (NOLOCK) Where SearchKey='CustomersDeletedBefore'
If IsNull(@tmpString, 0) = 0
  Set @tmpString = '19800101' -- BEFORE: first picasso installation and before data in first converted installation
Set @CustomersDeletedBefore = Convert(DATETIME, @tmpString, 112)
Select @SystemDate=Convert(DATETIME, SystemDate, 112) From System WITH (NoLock) Where Number=1

-- Write Delete date if the newly calculated delete-date is the more recent.
If @OldCreationDate > @CustomersDeletedBefore
Begin
  Update HotelSetup Set KeyValue=Convert(CHAR(8), @OldCreationDate, 112) Where SearchKey Like 'CustomersDeletedBefore'
  If @@rowcount = 0
    Insert Into HotelSetup (SearchKey, KeyValue) Values ('CustomersDeletedBefore', Convert(CHAR(8), @OldCreationDate, 112))
End

-- Cleanup
--Drop table #HasTurnover
Drop table #HasBonusCard
Drop table #IsTechotel
Drop table #HasRoom
Drop table #RecentlyActive
Drop table #OldCustomersForDeletionAll


-----------------------------------------------------------------------------------------------------------------------------
---         Data division point: Only a table with customers to be deleted should pass this place.        ---
---                                            This way, the procedure may be split up later, into two.                       ---
---                                            Plus it's easier to read.                                                                            ---
-----------------------------------------------------------------------------------------------------------------------------

DECLARE @StartDate DATETIME
SET @StartDate = GetDate() 

-- Measure changes
Declare @TotalRowsAffected int  -- Measure total number of rows affected, for interface feedback to user
Set @TotalRowsAffected = 0
Declare @TmpRowsAffected int
Set @TmpRowsAffected = 0
Declare @RowsAffected int  -- Measure rows affected pr. customer, for DeleteLog
Set @RowsAffected = 0
Declare @CustomersDeleted int  -- Measure number of customers deleted, for interface feedback to user
Set @CustomersDeleted = 0
Declare @Errors int
Set @Errors = 0

-- Delete customers
-- Input: Table of customernumbers to be deleted.

-- 1: Verify if the customers in question are allowed to be deleted.
-- Input: Table of customers to be deleted.
-- Output: Table of customers that are verified deletable
declare CustomerDeletionCursor cursor for
select OC.customernumber 
from #OldCustomersForDeletion OC
inner join customer C WITH (nolock)
on OC.CustomerNumber=C.CustomerNumber
where C.CustomerTypeNumber in ( 1, 2, 3, 6, 8, 9) -- Not deleting CompanyCorps, AgencyCorps or SalesChannels.

if @debug >= 1
begin
  PRINT 'Deletion start.'
end

-- 2: Delete from related tables.
-- Input: Verified table of customers for deletion
-- Post: Related tables with all customers to be deleted shrunken away from them.
Declare @CustomerToDelete int
Open CustomerDeletionCursor
FETCH NEXT FROM CustomerDeletionCursor
Into @CustomerToDelete
Set @debugint = 0
WHILE @@FETCH_STATUS = 0
BEGIN
	Set @Errors = 0
	If @debug >= 3
	Begin
		Set @debugint = @debugint + 1
		PRINT 'Customer count: ' + Cast(@debugint As varchar(10))
	End
	Begin Transaction -- Delete one customer at a time instead of blocking entire database for eons.

	-- Reset RowsAffected for current customer
	Set @RowsAffected = 0

	-- InetCustomer: Used to store login data when customer books via internet
	Delete from InetCustomer
	Where InetCustomer.CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'InetCustomer'
	IF @@error <> 0
		Set @Errors = @Errors + 1

	-- UniversalLog: Used to log changes to customers
	Delete from UniversalLog
	Where UniversalLog.Link=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'UniversalLog'
	If @@error <> 0
		Set @Errors = @Errors + 1

	-- CustomerLinks: Used to link e.g. Company to Guest or Company Corp to Company when they have a business relationship
	Delete from CustomerLinks
	Where CustomerLinks.CustomerNumber=@CustomerToDelete
	   OR CustomerLinks.CustomerLinkNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerLinks'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- CustomerGeographic: Used to store nationality of customer
	Delete from CustomerGeographic
	Where CustomerGeographic.CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerGeographic'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	Delete from CustomerHidden
	Where FKCustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerHidden'
	If @@error <> 0
		Set @Errors = @Errors + 1

	Delete from [dbo].[MailAddressesHidden]
	Where CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT '[MailAddressesHidden]'
	If @@error <> 0
		Set @Errors = @Errors + 1

	Delete from [dbo].CustomerEANS
	Where CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerEANS'
	If @@error <> 0
		Set @Errors = @Errors + 1

	-- CustomerPayTV: The configuration of PayTV as related to this customer. To have it ot not, to charge or not
	Delete from CustomerPayTV
	Where CustomerPayTV.FK_Customer=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerPayTV'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- NoteContents: Likely the 'Notes' tab in Marketings view of a customer
	Delete from NoteContents
	Where NoteContents.CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'NoteContents'
	If @@error <> 0
		Set @Errors = @Errors + 1

--New section vd start

    DELETE FROM CustomerAllotmentsXTab
	WHERE CustomerNumber=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'GDPRApproveCustomerXTab'
	IF @@error <> 0
	  SET @Errors = @Errors + 1

	DELETE FROM CustomerFieldsPerHotel
	WHERE CustomerFieldsPerHotel.CustomerNumber=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerFieldsPerHotel'
	IF @@error <> 0
	  SET @Errors = @Errors + 1

    DELETE FROM GDPRApproveCustomerXTab
	WHERE FK_CustomerNumber=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'GDPRApproveCustomerXTab'
	IF @@error <> 0
	  SET @Errors = @Errors + 1

    DELETE FROM MailAddresses
	WHERE CustomerNumber=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'MailAddresses'
	IF @@error <> 0
	  SET @Errors = @Errors + 1

    DELETE FROM CustomerOTA
	WHERE CustomerID=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerOTA'
	IF @@error <> 0
	  SET @Errors = @Errors + 1 
	  
    DELETE FROM CustomerOTAAction
	WHERE FKCustomerId=@CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerOTAAction'
	IF @@error <> 0
	  SET @Errors = @Errors + 1 	   

--New section vd end

	-- HotelSetupLog: 
	Delete from HotelSetupLog
	Where HotelSetupLog.CustomerNumber=@CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'HotelSetupLog'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- ReservationAddress: Used to store the address of the customer making a booking.
	-- From ReservationAddress, Customers are related by Link if they're a customer making their own reservation, and/or by ContactPersonCustomerNumber if they're a
	-- contactperson making a reservation on behalf of someone else.
	-- Setting these fields to Null removes the relation while not interfering with the reservations otherwise. The new relation is consistent with a customer not in the database 
	-- having made a walk-in reservation.
	Update ReservationAddress
	Set ReservationAddress.Link = Null
	Where ReservationAddress.Link = @CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'ReservationAddress'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	Update ReservationAddress
	Set ReservationAddress.ContactPersonCustomerNumber = Null
	Where ReservationAddress.ContactPersonCustomerNumber = @CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'ReservationAddress'
	If @@error <> 0
		Set @Errors = @Errors + 1

	-- FollowUp: Used to store to-do notes that some personel has responsibility to perform in regards to a specific customer.
	--                  The obvious trouble is of course that when a customer is deleted, all its followups must also be deleted, or errors will be likely.
	--                  The not so obvious trouble is that, while deleting, an employee may be perusing the customer and/or the followup in question.
	--                  To avoid errors, it is (according to KJ and TL) needed to delete the crosstables first, then the followups, then the customer.
	--                  The followups will be stored here for deletion later, the crosstables deleted, and deleting the customers happens at the last in this loop.
	Insert into @TmpDeleteFromFollowUp ( FollowUpNumber )
	Select FU.FollowUpNumber
	From FollowUp FU WITH (nolock)
	Inner join CustomerXTabFollowUp CXF WITH (nolock)
	On FU.FollowUpNumber = CXF.FollowUpNumber
	Where CXF.CustomerNumber = @CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount  -- The number of rows inserted here equals the number of rows that will later be deleted from FollowUp.  And it needs to be registered here, for DeleteLog.
	IF @debug >= 5
	  PRINT 'TmpDeleteFromFollowUp'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- Using the just-made temporary table to avoid yet another join
	Delete from PersonalFollowUpXTab
	Where PersonalFollowUpXTab.FollowUpNumber in (Select FollowUpNumber From @TmpDeleteFromFollowUp)
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'PersonalFollowUpXTab'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	Delete from CustomerXTabFollowUp
	Where CustomerXTabFollowUp.CustomerNumber = @CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerXTabFollowUp'
	If @@error <> 0
		Set @Errors = @Errors + 1
	

    DELETE FROM CustomerAttachmentXTab 
    WHERE Customernumber = @CustomerToDelete
	SET @RowsAffected = @RowsAffected + @@RowCount
		
	-- Card: Used to store information about any card which might be used in connection to the customer. Bonuscard, customercard, etc. Can also occur in relation to reservations.
	--           Perhaps both the customer and a reservation relates to a particular card. In this case
	--           the relation between customer and card should be removed.
	--           Or, there might be just the relation between the customer and the card, and both the relation and the card ought to be deleted.
	--           Deleting the crosstable first, card later, for the same reasons as for those concerning FollowUp.
	Insert into @TmpDeleteFromCard (FK_Card)
	Select CCX.FK_Card
	From CustomerCardXTab CCX WITH (nolock)
	Where CCX.CustomerNumber = @CustomerToDelete
	  And CCX.FK_Card not in (select FK_Card from ReservationCardXTab WITH (nolock) )
	Set @RowsAffected = @RowsAffected + @@RowCount  -- The number of rows inserted here equals the number of rows that will later be deleted from Card.  And it needs to be registered here, for DeleteLog.
	IF @debug >= 5
	  PRINT 'TmpDeleteFromCard'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- Regardless of reservation relations to card, the customer-card relation should be removed.
	Delete from CustomerCardXTab
	Where CustomerCardXTab.CustomerNumber = @CustomerToDelete
	Set @RowsAffected = @RowsAffected + @@RowCount
	IF @debug >= 5
	  PRINT 'CustomerCardXTab'
	If @@error <> 0
		Set @Errors = @Errors + 1

	-- Customer: Used to store basic customer information. Some of this information over time split into other tables for performance, 
	--                   and yet other tables added ad-hoc through use of foreign keys.
	Delete from Customer
	Where Customer.CustomerNumber = @CustomerToDelete
	Set @TmpRowsAffected = @@RowCount
	Set @CustomersDeleted = @CustomersDeleted + @TmpRowsAffected
    Set @RowsAffected = @RowsAffected + @TmpRowsAffected
	IF @debug >= 5
	  PRINT 'Customer'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	-- Write to DeleteLog, and update TotalRowsAffected
	-- The reason for the possible update is that the script may have been interrupted before, by e.g. a locking error.
	-- In that case, when the script is re-run, it should update with the proper information and not crash (once more) due to primary key violation.
	If exists (Select ObjectPrimaryKey From DeleteLog Where Type=5 and ObjectPrimaryKey=@CustomerToDelete)
		Update DeleteLog 
		Set DateStamp = @SystemDate+ CONVERT(DATETIME, CONVERT(CHAR, GETDATE(), 108), 108),
		    DeletedBy = @DeletedFrom,
			RowsAffected = @RowsAffected
		Where Type=5 And ObjectPrimaryKey=@CustomerToDelete  -- 5 = Customer. Number defined by SystemTL, TDeleteRecord.
	else
		Insert Into DeleteLog(Type, ObjectPrimaryKey, DateStamp, DeletedBy, RowsAffected) 
		Values (5, @CustomerToDelete, @SystemDate+ CONVERT(DATETIME, CONVERT(CHAR, GETDATE(), 108), 108), @DeletedFrom, @RowsAffected) -- 5 = Customer. Number defined by SystemTL, TDeleteRecord.
	Set @TotalRowsAffected = @TotalRowsAffected + @RowsAffected
	IF @debug >= 5
	  PRINT 'DeleteLog'
	If @@error <> 0
		Set @Errors = @Errors + 1
	
	FETCH NEXT FROM CustomerDeletionCursor
	into @CustomerToDelete

	If @Errors > 0
		Rollback Transaction  -- Cancel deletion for a single customer in case of trouble, allowing the loop to continue. (UW: But this is untested since I've had no errors since this rewrite)
	Else
		Commit Transaction -- Delete one customer at a time instead of blocking entire database for eons.
END
Close CustomerDeletionCursor
Deallocate CustomerDeletionCursor


-- 3: Perform deletions postponed earlier in order to avoid interdependency problems.

-- Deletion from FollowUp
Declare @FollowUpToDelete int

Declare FollowUpDeletionCursor cursor for
Select FollowUpNumber 
From @TmpDeleteFromFollowUp

Open FollowUpDeletionCursor
Fetch next from FollowUpDeletionCursor
Into @FollowUpToDelete
Set @debugint = 0
While @@FETCH_STATUS = 0
Begin
	If @debug >= 3
	Begin
		Set @debugint = @debugint + 1
		PRINT 'Followup count: ' + Cast(@debugint As VarChar(10))
	End
	Delete from FollowUp
	Where FollowUp.FollowUpNumber = @FollowUpToDelete
	Fetch next from FollowUpDeletionCursor
	Into @FollowUpToDelete	
	-- Affected rows already accounted for
End
Close FollowUpDeletionCursor
Deallocate FollowUpDeletionCursor

-- Deletion from Card
Declare @CardToDelete int

Declare CardDeletionCursor cursor for
Select FK_Card
From @TmpDeleteFromCard

Open CardDeletionCursor
Fetch next from CardDeletionCursor
Into @CardToDelete
Set @debugint = 0
While @@FETCH_STATUS = 0
Begin
	If @debug >= 3
	Begin
		Set @debugint = @debugint + 1
		PRINT 'Card count: ' + Cast(@debugint As VarChar(10))
	End
	Delete from Card
	Where Card.PK_Card = @CardToDelete
	Fetch next from CardDeletionCursor
	Into @CardToDelete
	-- Affected rows already accounted for
End
Close CardDeletionCursor
Deallocate CardDeletionCursor

-- Cleanup	
drop table #OldCustomersForDeletion

-- Deletion from Attachment
DELETE FROM Attachment  
WHERE 
  AttachmentNumber NOT IN (SELECT FK_CertificateAttachmentID 
                           FROM ProductCertificate WITH (NOLOCK)) AND 
  AttachmentNumber NOT IN (SELECT AttachmentNumber
                           FROM CustomerAttachmentXTab WITH (NOLOCK)) AND
  AttachmentNumber NOT IN (SELECT AttachmentNumber
                           FROM ReservationAttachmentXTab WITH (NOLOCK))
SET @RowsAffected = @RowsAffected + @@RowCount

DELETE FROM CustomerOrdreBook 
WHERE CustomerNumber NOT IN (SELECT CustomerNumber FROM Customer (NOLOCK))
SET @RowsAffected = @RowsAffected + @@RowCount

If @debug >= 1
Begin
   PRINT 'Deletion end.'
End

SELECT 
  @CustomersDeleted AS Deleted, 
  @TotalRowsAffected AS TotalRowsAffected, 
  GetDate() - @StartDate AS TimeSpent

SET NOCOUNT OFF
