Ciao a tutti.
Volevo riprendere l’argomento in quanto anche noi all’inizio abbiamo avuto un po’ di perplessità su come approcciare il problema.
Premetto che abbiamo il 99% delle soluzioni senza device mobile (per ora) per cui il problema della sincronizzazione non è molto sentito.
Inizialmente e per il progetto SMART (il nostro nuovo ERP) abbiamo adottato l’utilizzo del ID di SQL progressivo contatore, ma nei nuovi progetti adotteremo il DocID di INDE.
Questa doppia scelta è stata dettata dalle necessità.
La gestione di SMART (il nostro ERP in Saas) nasce da un progetto inizialmente sviluppato con CodeOnTime, dove le relazioni sono per ID (contatore SQL), per cui abbiamo ereditato lo schema del DB completo con tutte le relazioni sull’ID, e cambiare lo schema, stored procedure e funzioni SQL era un lavoraccio, nonché il fatto che alcune parti del SW “vecchio” dovevano convivere su alcuni clienti.
C’è però un’altra “bega” che ci assilla e provo a spiegare meglio:
INDE ha un problema noto e riconosciuto, che se sulle tabelle ci sono definiti dei trigger SQL (e noi ne usiamo parecchi), e se si estendono le entità di base con delle proprie classi derivate da ID_DOCUMENT, nella propagazione delle proprietà si perde il valore dell’ultimo ID inserito in tabella, per cui si fa un macello quando si inseriscono i dati. Questo succede perché INDE per recuperare l’ultimo ID inserito usa lo variabile globale SQL @@IDENTITY anziché la funzione SCOPE_IDENTITY(). La prima viene valorizzata con l’ultimo ID inserito nella sessione, mentre la seconda ritorna il valore dell’ultimo ID della tabella. Per ovviare al problema (vedi anche nel forum) si vanno a scrivere due righe di codice nell’evento after save della classe così da valorizzare l’ID correttamente, e se il trigger è su una sola tabella problemi non ce ne sono, ma iniziano quando le tabelle dell’entità sono più di una (4 o 5 come nel nostro caso…) perché l’evento after save viene notificato per tutte le classi dell’entità nello stesso momento, quindi l’ID che si recupera non è detto che sia quello della classe padre.
Per farla breve il tutto si risolve utilizzando il DocID di INDE dove viene generato correttamente e propagato seguendo le relazioni delle PK.
Resta il problema che non si può fare manutenzione “extra INDE” ad esempio da SQL manager o da un’altra applicazione scritta con un linguaggio diverso INDE.
Per questo ci siamo scritti tre funzioni SQL per la generazione del DocID e la trasformazione del DocID in GUID e viceversa, così da poter fare in SQL quello che si fa in INDE…
Di seguito lo script di generazione di una vista che serve all’interno delle funzioni, dal momento che in una funzione SQL non si può richiamare la funzione di sistema NEWID()
CREATE view [dbo].[vw_NewGuid]
as
SELECT NEWID() AS NewGuid
GO
– =============================================
– Author: MADA Service - Tamara Cocchi
– Create date: 2022-08-11
– Description: Funzione che converte un Doc ID di INDE in GUID
– =============================================
CREATE function [dbo].[DecodeDocID]
(
@DocId varchar(20)
)
returns varchar(36) as
BEGIN
declare @guid varchar(36) = ''
declare @restoPrecedente as bigint = 0
DECLARE @Counter INT ,
@giro int = 1,
@moltiplicatore INT = 0,
@guidTemp varchar(36)
SET @Counter=1
WHILE ( @Counter <= LEN(@DocId))
BEGIN
set @restoPrecedente = 0
declare @subGiro int = 1
WHILE @subGiro <= 5
BEGIN
SELECT @moltiplicatore = CASE @subGiro
WHEN 1
THEN 1
WHEN 2
THEN 85
WHEN 3
THEN 7225
WHEN 4
THEN 614125
ELSE
52200625
END
declare @valore bigint
SET @valore = (CONVERT(varbinary,SUBSTRING(@docID,@counter,1))-40)*CONVERT(BIGINT,@moltiplicatore)+@restoPrecedente
set @restoPrecedente = @valore
SET @Counter = @Counter + 1
SET @subGiro = @subGiro + 1
END
SET @guidTemp = CONVERT(VARCHAR(1000), CONVERT(VARBINARY(4),CAST(@restoPrecedente AS BIGINT)),2)
SET @guid = @guid + CASE @giro
WHEN 1
THEN @guidTemp + '-'
WHEN 2
THEN SUBSTRING(@guidTemp,5,4)+'-'+SUBSTRING(@guidTemp,1,4)+'-'
WHEN 3
THEN SUBSTRING(@guidTemp,7,2)+SUBSTRING(@guidTemp,5,2)+'-'+SUBSTRING(@guidTemp,3,2)+SUBSTRING(@guidTemp,1,2)
ELSE
SUBSTRING(@guidTemp,7,2)+SUBSTRING(@guidTemp,5,2)+SUBSTRING(@guidTemp,3,2)+SUBSTRING(@guidTemp,1,2)
END
SET @giro = @giro + 1
END
return @guid
END
– =============================================
– Author: MADA Service - Tamara Cocchi
– Create date: 2022-08-11
– Description: Funzione che converte un GUID in un Doc ID come INDE comanda
– =============================================
CREATE function [dbo].[EncodeDocID]
(
@guid varchar(36)
)
returns varchar(20) as
BEGIN
declare @DocId varchar(20) = ''
declare @numero32bit as bigint
DECLARE @Counter INT ,
@giro int = 1,
@tentativi INT = 0,
@divisore INT = 0,
@docIDTemp varchar(20) = ''
SET @guid = REPLACE(@guid,'-','')
SET @Counter=1
WHILE ( @Counter <= LEN(@guid))
BEGIN
declare @stringa varchar (50) = ''
SELECT @stringa = CASE @giro
WHEN 1
THEN SUBSTRING(@guid,@counter,8)
WHEN 2
THEN SUBSTRING(@guid,@counter+4,4)+SUBSTRING(@guid,@counter,4)
ELSE
SUBSTRING(@guid,@counter+6,2)+SUBSTRING(@guid,@counter+4,2)+SUBSTRING(@guid,@counter+2,2)+SUBSTRING(@guid,@counter,2)
END
SELECT @numero32bit = CONVERT(bigint, convert(varbinary, '0x' + @stringa, 1))
set @docIDTemp = ''
declare @subGiro int = 1
WHILE @subGiro <= 5
BEGIN
SELECT @divisore = CASE @subGiro
WHEN 1
THEN 52200625
WHEN 2
THEN 614125
WHEN 3
THEN 7225
WHEN 4
THEN 85
ELSE
1
END
declare @valore char
SET @valore = CHAR(@numero32bit / @divisore + 40)
select @docIDTemp = @valore + @docIDTemp
set @numero32bit = @numero32bit % @divisore
SET @subGiro = @subGiro + 1
END
SET @docID = @docID + @docIDTemp
SET @giro = @giro + 1
SET @Counter = @Counter + 8
END
return @docID
END
– =============================================
– Author: MADA Service - Tamara Cocchi
– Create date: 2022-08-11
– Description: Funzione che crea un nuovo un Doc ID come INDE comanda
– =============================================
CREATE function [dbo].[GetNewDocID]
()
returns varchar(20) as
BEGIN
declare @guid as varchar(36) = '',
@DocId varchar(20) = ''
SELECT @guid = NewGuid
FROM [vw_NewGuid]
declare @numero32bit as bigint
DECLARE @Counter INT ,
@giro int = 1,
@trovato BIT = 0,
@tentativi INT = 0,
@divisore INT = 0,
@docIDTemp varchar(20) = ''
WHILE @docID = '' and @tentativi < 100
BEGIN
SELECT @DocId = [dbo].[EncodeDocID](@guid)
if CHARINDEX('\',@docID) > 0
BEGIN
SELECT @guid = NewGuid
FROM [vw_NewGuid]
set @docID = ''
END
set @tentativi = @tentativi + 1
END
return @docID
END