Files
2021-03-26 17:17:28 +01:00

1355 lines
38 KiB
Transact-SQL

set xact_abort on;
go
begin transaction;
go
alter table AnagFasi alter column
nomeFase nvarchar(250);
go
alter table AnagFasi add
codClasse nvarchar(10);
go
exec sp_addextendedproperty 'MS_Description', 'codice univoco', 'SCHEMA', 'dbo', 'TABLE', 'AnagFasi', 'COLUMN', 'codClasse';
go
alter table AnagFasi add
codExt nvarchar(50);
go
exec sp_addextendedproperty 'MS_Description', 'codice esterno', 'SCHEMA', 'dbo', 'TABLE', 'AnagFasi', 'COLUMN', 'codExt';
go
alter table AnagFasi add
constraint FK_AnagFasi_AnagClassiOrarie foreign key(codClasse) references AnagClassiOrarie(codClasse) on update cascade;
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
-- =============================================
-- Author: S.E. Locatelli
-- Create date: 2012.10.31
-- Description: Gestione trigger calcolo codFase e idxProgetto
-- =============================================
alter TRIGGER trg_AF_calcCodFaseIdxProj
ON AnagFasi
AFTER INSERT,UPDATE
AS
BEGIN
IF (@@ROWCOUNT = 0) -- SE NESSUNA RIGA AGGIORNATA O INSERITA ESCO
RETURN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- calcolo il codFase ed idxProgetto x record corrente
UPDATE AF
SET AF.codFase = dbo.f_calcolaCodFase(i.idxFase)
,AF.idxProgetto = CASE WHEN dbo.f_calcolaIdxProgetto(i.idxFase) > 0
THEN dbo.f_calcolaIdxProgetto(i.idxFase)
ELSE i.idxProgetto
END
FROM AnagFasi AF
INNER JOIN inserted i ON AF.idxFase = i.idxFase
-- aggiorno IN CASCATA i record child x idxProgetto!!!
UPDATE AF
SET AF.idxProgetto = CASE WHEN dbo.f_calcolaIdxProgetto(i.idxFase) > 0
THEN dbo.f_calcolaIdxProgetto(i.idxFase)
ELSE i.idxProgetto
END
,AF.codClasse = i.codClasse
FROM AnagFasi AF
INNER JOIN inserted i ON AF.idxFaseAncest = i.idxFase
END
go
commit;
go
set xact_abort on;
go
begin transaction;
go
alter table AnagProgetti add
codExt nvarchar(50);
go
exec sp_addextendedproperty 'MS_Description', 'codice esterno', 'SCHEMA', 'dbo', 'TABLE', 'AnagProgetti', 'COLUMN', 'codExt';
go
set ANSI_NULLS on;
go
/**********************************************************
* STORED stp_AP_delete
*
* elimina un progetto da anagrafica
*
* mod: S.E.L. 2012.10.16
*
**********************************************************/
alter PROCEDURE stp_AP_delete
(
@Original_idxProgetto int
)
AS
SET NOCOUNT OFF;
-- controllo: se ci sono fasi NON cancello ma archivio...
DECLARE @trovate INT;
SET @trovate = ( SELECT ISNULL(COUNT(*),0) FROM AnagFasi WHERE idxProgetto = @Original_idxProgetto )
IF(@trovate > 0)
BEGIN
UPDATE AnagProgetti
SET Attivo = 0
WHERE idxProgetto = @Original_idxProgetto
END
ELSE
BEGIN
DELETE
FROM AnagProgetti
WHERE idxProgetto = @Original_idxProgetto
END
go
commit;
go
set xact_abort on;
go
begin transaction;
go
alter table Dipendenti add
codDipendenteExt nvarchar(50);
go
exec sp_addextendedproperty 'MS_Description', 'nome/codice dipendente per sistema esterno da importare', 'SCHEMA', 'dbo', 'TABLE', 'Dipendenti', 'COLUMN', 'codDipendenteExt';
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
alter TRIGGER trg_upsertPeriodiLav
ON PeriodiLav
/*----------------------------------
* Trigger su insert/update
*
* Aggiorna la tab delle timbrature esplicitate al cambio dei periodi lavorati ricalcolando la giornata del record interessato
*
* mod: S.E.L 2012.02.06
*----------------------------------*/
FOR INSERT, UPDATE
AS
-- dichiarazione variabili
DECLARE @dataRif datetime
DECLARE @idxDipendente INT
-- potrei avere update mutipli, uso cursore...
DECLARE cursPLav CURSOR FOR SELECT DISTINCT CONVERT(DATE,i.entrata), i.idxDipendente FROM inserted i
OPEN cursPLav
FETCH NEXT FROM cursPLav INTO @dataRif, @idxDipendente
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC stp_ricalcolaTimbratureExpl @idxDipendente, @dataRif
FETCH NEXT FROM cursPLav INTO @dataRif, @idxDipendente
END
CLOSE cursPLav
DEALLOCATE cursPLav
/*
-- recupero valori x inserting
SET @dataRif = ( SELECT CONVERT(DATE,i.entrata) FROM inserted i )
SET @idxDipendente = ( SELECT i.idxDipendente FROM inserted i )
--chiamo la stored di ricalcolo
EXEC stp_ricalcolaTimbratureExpl @idxDipendente, @dataRif
*/
go
alter TRIGGER trg_deletePeriodiLav
ON PeriodiLav
/*----------------------------------
* Trigger su insert/update
*
* Aggiorna la tab delle timbrature esplicitate al cambio dei periodi lavorati ricalcolando la giornata del record interessato
*
* mod: S.E.L 2012.02.06
*----------------------------------*/
FOR DELETE
AS
-- dichiarazione variabili
DECLARE @dataRif datetime
DECLARE @idxDipendente INT
-- potrei avere update mutipli, uso cursore...
DECLARE cursPLav CURSOR FOR SELECT DISTINCT CONVERT(DATE,d.entrata), d.idxDipendente FROM deleted d
OPEN cursPLav
FETCH NEXT FROM cursPLav INTO @dataRif, @idxDipendente
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC stp_ricalcolaTimbratureExpl @idxDipendente, @dataRif
FETCH NEXT FROM cursPLav INTO @dataRif, @idxDipendente
END
CLOSE cursPLav
DEALLOCATE cursPLav
/*
-- recupero valori x inserting
SET @dataRif = ( SELECT CONVERT(DATE,d.entrata) FROM deleted d )
SET @idxDipendente = ( SELECT d.idxDipendente FROM deleted d )
--chiamo la stored di ricalcolo
EXEC stp_ricalcolaTimbratureExpl @idxDipendente, @dataRif
*/
go
commit;
go
set xact_abort on;
go
begin transaction;
go
create table RegAttivitaExpl(
dataLav date not null,
idxDipendente int not null,
CognomeNome nvarchar(100) constraint DF_RegAttivitaExpl_CognomeNome default (''),
minRegAtt int constraint DF_RegAttivitaExpl_minRegAtt default ((0)),
descrProj nvarchar(500) constraint DF_RegAttivitaExpl_descrProj default (''),
constraint PK_RegAttivitaExpl primary key(dataLav,idxDipendente)
);
go
exec sp_addextendedproperty 'MS_Description', N'totale dei minuti di attività registrate per la giornata (da trigger suRegAttivita)', 'SCHEMA', 'dbo', 'TABLE', 'RegAttivitaExpl', 'COLUMN', 'minRegAtt';
go
commit;
go
create index ix_inizio on RegAttivita(inizio,idxDipendente)
include(fine);
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
create TRIGGER trg_RegAtt_upsert
ON RegAttivita
/*----------------------------------
* Trigger su insert/update
*
* Aggiorna la tab dei RegAttivitaExpl
*
* creato da: S.E. Locatelli - 2013.01.21
* ultima modifica:
*----------------------------------*/
FOR INSERT, UPDATE
AS
IF(UPDATE(idxDipendente) OR UPDATE(inizio) OR UPDATE(fine) OR UPDATE(idxFase))
BEGIN
-- dichiarazione variabili
DECLARE @inizio DATETIME
DECLARE @fine DATETIME
DECLARE @idxDipendente INT
-- recupero valori x inserting
SELECT @inizio = CONVERT(DATE,i.inizio), @fine = DATEADD(DAY,1,CONVERT(DATE,i.inizio)), @idxDipendente = i.idxDipendente FROM inserted i
--chiamo la stored di ricalcolo
EXEC stp_ricalcolaRegAttivitaExpl_byPeriodoUser @idxDipendente, @inizio, @fine
END
go
create TRIGGER trg_RegAtt_delete
ON RegAttivita
/*----------------------------------
* Trigger su delete
*
* Aggiorna la tab RegAttivitaExpl ricalcolando la giornata del record interessato
*
* creato da: S.E. Locatelli - 2013.01.21
* ultima modifica:
*----------------------------------*/
FOR DELETE
AS
-- dichiarazione variabili
DECLARE @inizio DATETIME
DECLARE @fine DATETIME
DECLARE @idxDipendente INT
-- recupero valori x inserting
SELECT @inizio = CONVERT(DATE,i.inizio), @fine = DATEADD(DAY,1,CONVERT(DATE,i.inizio)), @idxDipendente = i.idxDipendente FROM inserted i
--chiamo la stored di ricalcolo
EXEC stp_ricalcolaRegAttivitaExpl_byPeriodoUser @idxDipendente, @inizio, @fine
go
commit;
go
create index ix_idxDip on TimbratureExpl(idxDipendente)
include(dataLav,CognomeNome,h_lav,minOrd,minStra,minPerm,minFer,block,isOkApp,isOkTim);
go
set xact_abort on;
go
begin transaction;
go
create table Contatori(
codContatore nvarchar(50) not null constraint PK_Contatori primary key,
valore int
);
go
exec sp_addextendedproperty 'MS_Description', 'codice di un contatore per uso specifico cliente', 'SCHEMA', 'dbo', 'TABLE', 'Contatori', 'COLUMN', 'codContatore';
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
alter VIEW v_resocAttiv
AS
SELECT idxDipendente, CONVERT(DATE, inizio) AS data, SUM(oreTot) AS oreTot, dbo.f_ProgettiByDate(idxDipendente, CONVERT(DATE, inizio)) AS progetti
FROM dbo.RegAttivita
GROUP BY idxDipendente, CONVERT(DATE, inizio)
go
commit;
go
-- functions!
set ANSI_NULLS on;
go
/******************************************************************************
* Function f_minuteInterval
*
* calcola intervallo IN MINUTI tra 2 date/time
*******************************************************************************/
create FUNCTION f_minuteInterval
(
@inizio DATETIME,
@fine DATETIME
)
RETURNS INT
AS
BEGIN
-- calcolo dataora attuale
DECLARE @adesso DATETIME
SET @adesso = '19000101'
-- valore differenza trovato...
DECLARE @interv DATETIME
DECLARE @mm INT
-- verifico non nulli valori input
SET @inizio = ( SELECT ISNULL(@inizio, @adesso) )
SET @fine = ( SELECT ISNULL(@fine, @adesso) )
-- calcolo differenza timespan
SET @interv = ( SELECT @fine - @inizio )
-- diff in minuti
SET @mm = ( SELECT dbo.TimeSpanUnits('m',@interv) )
-- sommo e restituisco tot ore
RETURN @mm
END
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
/******************************************************************************
* Function f_fasiProgetto
* fornisce elenco progetti dato idxDipendente e data...
*
* mod: S.E.L. 2012.10.31
*
*******************************************************************************/
alter FUNCTION f_ProgettiByDate
(
@idxDipendente INT,
@dataRif DATETIME
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE @output AS NVARCHAR(MAX)
;WITH myCTE AS
(
SELECT DISTINCT CAST(ap.nomeProj AS NVARCHAR(50)) as valore
FROM RegAttivita ra INNER JOIN AnagFasi af ON ra.idxFase = af.idxFase INNER JOIN AnagProgetti ap ON af.idxProgetto = ap.idxProgetto
WHERE CONVERT(DATE,ra.inizio) = @dataRif
AND ra.idxDipendente = @idxDipendente
)
SELECT @output = COALESCE(@output + ',', '') + valore
FROM myCTE
RETURN @output
END
go
commit;
go
-- stored!!!
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
/******************************************************************************
* FUNCTION stp_nextValContatore
*
* stacca ed occupa il nuovo valore (progressivo intero)del contatore in oggetto
*
* mod: S.E.L. 2013.01.24
*
*******************************************************************************/
create PROCEDURE stp_nextValContatore
(
@codContatore NVARCHAR(50) = ''
)
AS
BEGIN TRAN
DECLARE @output AS INT;
-- in primis cerco se ci sia questo contatore (se non c'è lo inizializzo), incremento, prendo valore e fornisco risposta
DECLARE @lastVal INT;
SET @lastVal = ISNULL( ( SELECT valore FROM Contatori WHERE codContatore = @codContatore ),0)
IF (@lastVal = 0)
BEGIN
-- inserisco nuovo valore!
INSERT INTO Contatori
VALUES(@codContatore,1)
END
ELSE
BEGIN
-- aggiorno contatore!
UPDATE Contatori
SET valore = valore + 1
WHERE codContatore = @codContatore
END
-- rileggo!
SET @output = ( SELECT valore FROM Contatori WHERE codContatore = @codContatore )
COMMIT TRAN
SELECT @output
RETURN
go
/**********************************************************
* STORED stp_RAD_Expl_getByIdxDipData
*
* recupera elenco attività (con Expl dei dati da anagrafica) da idxDipendente + dataRif
*
* mod: S.E.L. 2013.01.23
*
**********************************************************/
create PROCEDURE stp_RAD_Expl_getByIdxDipData
(
@idxDipendente INT,
@dataRif DATETIME
)
AS
SELECT RegAttivita.idxDipendente, RegAttivita.inizio, RegAttivita.fine, CASE WHEN ISNULL(descrizione, '') = '' THEN '-' ELSE ISNULL(descrizione, '')
END AS descrizione, RegAttivita.oreTot, RegAttivita.importo, AnagClienti.RagSociale, AnagProgetti.nomeProj, AnagFasi.nomeFase
FROM RegAttivita INNER JOIN
AnagFasi ON RegAttivita.idxFase = AnagFasi.idxFase INNER JOIN
AnagProgetti ON AnagFasi.idxProgetto = AnagProgetti.idxProgetto INNER JOIN
AnagClienti ON AnagProgetti.idxCliente = AnagClienti.idxCliente
WHERE (RegAttivita.idxDipendente = @idxDipendente) AND (CAST(RegAttivita.inizio AS DATE) = CAST(@dataRif AS DATE) OR
CAST(RegAttivita.fine AS DATE) = CAST(@dataRif AS DATE))
ORDER BY RegAttivita.inizio
go
/**********************************************************
* STORED stp_RA_pivot_getByIdxDipData
*
* recupera elenco attività da idxDipendente + date in modalità x pivot
*
* mod: S.E.L. 2012.11.06
*
**********************************************************/
create PROCEDURE stp_RA_pivot_getByIdxDipData
(
@idxDipendente INT = 0,
@dataFrom DATETIME,
@dataTo DATETIME
)
AS
SELECT ac.RagSociale, ap.nomeProj, af.nomeFase, dip.Cognome +' '+ dip.Nome as CognomeNome, ra.inizio, ra.fine, CASE WHEN ISNULL(descrizione, '')
= '' THEN '-' ELSE ISNULL(descrizione, '') END AS descrizione, ra.oreTot, ra.importo
FROM RegAttivita AS ra INNER JOIN
Dipendenti AS dip ON ra.idxDipendente = dip.idxDipendente INNER JOIN
AnagFasi AS af ON ra.idxFase = af.idxFase INNER JOIN
AnagProgetti AS ap ON af.idxProgetto = ap.idxProgetto INNER JOIN
AnagClienti AS ac ON ap.idxCliente = ac.idxCliente
WHERE (dip.idxDipendente = @idxDipendente) AND (ra.inizio BETWEEN @dataFrom AND @dataTo) OR
(dip.idxDipendente = @idxDipendente) AND (ra.fine BETWEEN @dataFrom AND @dataTo) OR
(ra.inizio BETWEEN @dataFrom AND @dataTo) AND (@idxDipendente = 0) OR
(ra.fine BETWEEN @dataFrom AND @dataTo) AND (@idxDipendente = 0)
go
/*----------------------------------
* stp_ricalcolaRegAttivitaExpl_byPeriodoUser
*
* Ricalcola le RegAttivitaExpl x utente e intervallo date
* mod: S.E.L. - 2013.01.21
*----------------------------------*/
create PROCEDURE stp_ricalcolaRegAttivitaExpl_byPeriodoUser
(
@idxDipendente INT = 0,
@inizio DATETIME,
@fine DATETIME
)
AS
-- dichiaro variabili x calcolo tempo execuzione e num records!
DECLARE @startProc DATETIME = GETDATE()
DECLARE @totRecords INT = 0
BEGIN
-- faccio una tab temporanea con tallyTable x calcolo periodo intero...
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @inizio, @fine ) )
-- crea eventuali record mancanti: data, dip, cognome-nome
INSERT INTO RegAttivitaExpl(dataLav, idxDipendente)
SELECT ty.Data, ty.idxDipendente
FROM (
SELECT CONVERT(DATE,DATEADD(dd, -N, @fine)) as Data, idxDipendente
FROM Tally, Dipendenti
WHERE N <= @numD -- solo dati desiderati
AND (idxDipendente = @idxDipendente OR @idxDipendente = 0) --filtro su dipendenti
) ty LEFT OUTER JOIN RegAttivitaExpl rae ON ty.Data=rae.dataLav AND ty.idxDipendente=rae.idxDipendente
WHERE rae.dataLav IS NULL -- righe mancanti!
-- tab temp del record date progetti
;WITH myCteRA AS(
SELECT idxDipendente, CONVERT(DATE,inizio) AS data, SUM(oreTot) AS oreTot, SUM(dbo.f_minuteInterval(inizio, fine)) as minTot, dbo.f_ProgettiByDate(idxDipendente, CONVERT(DATE,inizio)) AS progetti
FROM dbo.RegAttivita
WHERE (idxDipendente = @idxDipendente OR @idxDipendente = 0) AND (inizio >= DATEADD(DAY,-1,@inizio) AND inizio <= @fine)
GROUP BY idxDipendente, CONVERT(DATE,inizio)
)
-- aggiorno la tab delle RegAttivitaExpl
UPDATE rae
SET minRegAtt = minTot
,descrProj = ISNULL(progetti,'')
FROM RegAttivitaExpl rae
INNER JOIN myCteRA vra ON rae.dataLav = vra.data AND rae.idxDipendente=vra.idxDipendente
-- aggiorno cognome nome
BEGIN TRY
UPDATE rae
SET CognomeNome = ISNULL(vcn.CognomeNome,'')
FROM RegAttivitaExpl rae INNER JOIN v_cognomeNome vcn ON rae.idxDipendente = vcn.idxDipendente
WHERE ISNULL(rae.CognomeNome,'') = ''
END TRY
BEGIN CATCH
-- non faccio nulla
END CATCH
END
SELECT @numD = @numD * COUNT(*) FROM Dipendenti WHERE (idxDipendente = @idxDipendente OR @idxDipendente = 0)
SELECT @startProc AS startTime, GETDATE() AS endTime, @numD AS numRec, DATEDIFF(ms, @startProc, GETDATE()) AS totMs, CAST(@numD AS FLOAT) * 1000 / DATEDIFF(ms, @startProc, GETDATE()) AS recPerSec
RETURN
go
/*----------------------------------
* stp_ricalcolaTimbExpl_byPeriodoUser
*
* Ricalcola le TimbratureExpl x utente e intervallo date
* mod: S.E.L. - 2012.09.17
*----------------------------------*/
create PROCEDURE stp_ricalcolaTimbExpl_byPeriodoUser_OLD
(
@idxDipendente INT = 0,
@inizio DATETIME,
@fine DATETIME
)
AS
-- dichiaro variabili x calcolo tempo execuzione e num records!
DECLARE @startProc DATETIME = GETDATE()
DECLARE @totRecords INT = 0
SET DATEFIRST 1
-- quanti giorni mi servono? conto giorni del mese
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @inizio, @fine) )
-- contatore per ciclo
DECLARE @currRow INT = 0
DECLARE @currIdxDip INT = 0
DECLARE @currDate DATETIME
-- creo la tabella temporanea con riga incrementale
DECLARE @dateAndUser TABLE
(
Riga int IDENTITY(1,1) NOT NULL,
DATA DATETIME NULL,
idxDipendente INT NULL
)
-- elenco delle date DEL MESE (compresi sab/dom) fino a max data odierna
;WITH myCTE AS
(
SELECT dbo.DateOnly(DATEADD(dd, (N-1), @inizio)) as Data, idxDipendente
FROM Tally, Dipendenti
WHERE N <= @numD
AND (idxDipendente = @idxDipendente OR @idxDipendente = 0)
)
-- inserisco valori cte in temp table!
INSERT INTO @dateAndUser
SELECT * FROM myCTE
-- eseguo update! Iterazione sulle righe della CTE
WHILE 1 = 1
BEGIN
-- Get next customerId
SELECT TOP 1 @currRow=Riga, @currIdxDip=idxDipendente, @currDate=Data
FROM @dateAndUser
WHERE Riga > @currRow
ORDER BY Riga
-- Exit loop if no more customers
IF @@ROWCOUNT = 0 BREAK;
-- X GIANCARLO: se commenti le seguenti hai SOLO il tempo di creare tab temporanea e spazzarla...
-- ESEGUO!
--select @currIdxDip, @currDate
EXEC stp_ricalcolaTimbratureExpl @currIdxDip, @currDate
SELECT @totRecords = @totRecords+1
END
SELECT @startProc AS startTime, GETDATE() AS endTime, @totRecords AS numRec, DATEDIFF(ms, @startProc, GETDATE()) AS totMs, CAST(@totRecords AS FLOAT) * 1000 / DATEDIFF(ms, @startProc, GETDATE()) AS recPerSec
RETURN
go
/**********************************************************
* STORED stp_lcuByDipDate
*
* elenco dati COMPLETI di timbratura e commessa per utente/periodo
*
* mod: S.E.L. 2012.11.02
*
**********************************************************/
create PROCEDURE stp_TE_RA_ByUserDate
(
@idxDipendente INT,
@dataFrom DATETIME,
@dataTo DATETIME,
@showWE BIT = 1, -- imposto visualizzazione week-end a true di default
@maxErrMin INT = 0, -- minuti massimi di scostamento consentiti per DIFETTO, default 0
@maxErrPlus INT = 0 -- minuti massimi di scostamento consentiti per ECCESSO, default 0
)
with recompile
AS
BEGIN
-- imposto LUN x inizio settimana
SET DATEFIRST 1
-- faccio una tab temporanea con tallyTable x calcolo periodo intero...
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @dataFrom, @dataTo ) )
-- tab dei record RegAttività progetti
;WITH myCteRA AS
(
SELECT idxDipendente, dataLav, minRegAtt, descrProj
FROM dbo.RegAttivitaExpl
WHERE (idxDipendente = @idxDipendente OR @idxDipendente = 0) AND (dataLav >= DATEADD(DAY,-1,@dataFrom) AND dataLav <= @dataTo)
)
SELECT mc.Data as dataLav
,mc.idxDipendente
,ISNULL(te.CognomeNome,'') AS CognomeNome
-- ore base + progetti
,ISNULL(te.h_lav,0) AS h_lav
,ISNULL(te.h_giust,0) AS h_giust
,CAST(ISNULL(vra.minRegAtt,0) AS DECIMAL(9,3))/60 AS h_com
,ISNULL(vra.descrProj,'') as progetti
-- check coerenza
,CASE WHEN ISNULL(vra.minRegAtt,0)-ISNULL(te.h_lav,0)*60 >= @maxErrMin AND ISNULL(vra.minRegAtt,0)-ISNULL(te.h_lav,0)*60 <= @maxErrPlus THEN 1 ELSE 0 END AS okLavCom
,ISNULL(te.isOkTim,0) AS isOkTim
,ISNULL(te.isOkApp,0) AS isOkApp
,ISNULL(te.isOkLav,0) AS isOkLav
,ISNULL(te.isOk,0) AS isOk
-- check blocco
,ISNULL(te.block,0) AS block
-- dettaglio minuti
,ISNULL(te.minOrd,0) AS minOrd
,ISNULL(te.minNonLav,0) AS minNonLav
,ISNULL(te.minStra,0) AS minStra
,ISNULL(te.minPerm,0) AS minPerm
,ISNULL(te.minFer,0) AS minFer
,ISNULL(te.minMal,0) AS minMal
,ISNULL(te.minFest,0) AS minFest
,ISNULL(vra.minRegAtt,0) AS minRegAtt
-- dettaglio entrate
, te.entrata_1, te.uscita_1
, te.entrata_2, te.uscita_2
, te.entrata_3, te.uscita_3
, te.entrata_4, te.uscita_4
FROM (SELECT CONVERT(DATE,DATEADD(dd, -N, @dataTo)) as Data, idxDipendente
FROM Tally, Dipendenti
WHERE N <= @numD
AND (idxDipendente = @idxDipendente OR @idxDipendente = 0)
) AS mc
LEFT OUTER JOIN myCteRA vra ON mc.Data = vra.dataLav AND mc.idxDipendente=vra.idxDipendente
LEFT OUTER JOIN TimbratureExpl te ON mc.Data=te.dataLav AND te.idxDipendente=mc.idxDipendente
WHERE (te.idxDipendente = @idxDipendente OR @idxDipendente = 0)
AND DATEPART(dw, mc.Data) <= CASE WHEN @showWE=0 THEN 5 ELSE 7 END --solo lun-ven, 1-5
ORDER BY mc.Data DESC, mc.idxDipendente
END
RETURN
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
/**********************************************************
* STORED stp_import_paymoRaw
*
* importa tracciato dati da paymo x utente e date indicate
*
* mod: S.E.L. 2012.11.20
*
**********************************************************/
create PROCEDURE stp_import_paymoRaw
(
@inizio DATETIME,
@fine DATETIME
)
AS
/**************************************************
* inizio importando eventuali clienti...
**************************************************/
;WITH myCteClienti AS
(
SELECT DISTINCT Cliente FROM zzz_RawDataPaymo
)
INSERT INTO AnagClienti(RagSociale, nota, Attivo)
SELECT cte.Cliente, cte.Cliente, 1
FROM AnagClienti anag
RIGHT OUTER JOIN myCteClienti cte ON anag.RagSociale = cte.Cliente
WHERE anag.idxCliente IS NULL
/**************************************************
* importo eventuali progetti
**************************************************/
;WITH myCteProgetti AS
(
SELECT DISTINCT Cliente, Progetto FROM zzz_RawDataPaymo
)
INSERT INTO AnagProgetti(idxCliente, nomeProj, descrProj, Attivo)
SELECT cli.idxCliente, cte.Progetto, cte.Progetto, 1
FROM AnagProgetti anag
RIGHT OUTER JOIN myCteProgetti cte ON anag.nomeProj = cte.Progetto INNER JOIN AnagClienti Cli ON Cli.RagSociale = cte.Cliente
WHERE anag.idxProgetto IS NULL
/**************************************************
* importo eventuali fasi
**************************************************/
;WITH myCteFasi AS
(
SELECT DISTINCT Cliente, Progetto, TaskList FROM zzz_RawDataPaymo
)
INSERT INTO AnagFasi(idxProgetto, codFase, nomeFase, descrizioneFase, enableTime, enableMoney, Attivo)
SELECT Pro.idxProgetto, '', cte.TaskList, cte.TaskList, 1, 0, 1
FROM myCteFasi cte INNER JOIN AnagClienti Cli ON cte.Cliente = Cli.RagSociale INNER JOIN AnagProgetti Pro ON cte.Progetto = Pro.nomeProj
LEFT OUTER JOIN AnagFasi anag ON anag.nomeFase = cte.TaskList
WHERE anag.idxFase IS NULL
/**************************************************
* importo eventuali sottofasi
**************************************************/
;WITH myCteSubFasi AS
(
SELECT DISTINCT Cliente, Progetto, TaskList, Task FROM zzz_RawDataPaymo
)
INSERT INTO AnagFasi(idxProgetto, idxFaseAncest, codFase, nomeFase, descrizioneFase, enableTime, enableMoney, Attivo)
SELECT Pro.idxProgetto, Fas.idxFase as idxFaseAncest, '', cte.task, cte.task, 1, 0, 1
FROM myCteSubFasi cte
INNER JOIN AnagClienti Cli ON cte.Cliente = Cli.RagSociale
INNER JOIN AnagProgetti Pro ON cte.Progetto = Pro.nomeProj -- and pro.idxCliente=cli.idxCliente
INNER JOIN AnagFasi Fas ON Fas.nomeFase = cte.TaskList AND Fas.idxProgetto = Pro.idxProgetto AND Fas.idxFaseAncest=0
LEFT OUTER JOIN AnagFasi anag ON anag.nomeFase = cte.Task AND anag.idxFaseAncest = Fas.idxFase
WHERE anag.idxFaseAncest IS NULL
/**************************************************
* importo records!
**************************************************/
INSERT INTO RegAttivita(idxDipendente, idxFase, inizio, fine, descrizione)
SELECT Dip.idxDipendente, Fas.idxFase, cte.Inizio, cte.Fine, cte.Descrizione
FROM zzz_RawDataPaymo cte
INNER JOIN Dipendenti Dip ON cte.Dipendente = dip.codDipendenteExt
INNER JOIN AnagClienti Cli ON cte.Cliente = Cli.RagSociale
INNER JOIN AnagProgetti Pro ON cte.Progetto = Pro.nomeProj -- and pro.idxCliente=cli.idxCliente
INNER JOIN AnagFasi Fas ON Fas.nomeFase = cte.TaskList AND Fas.idxProgetto = Pro.idxProgetto AND Fas.idxFaseAncest=0
INNER JOIN AnagFasi SFas ON SFas.nomeFase = cte.Task AND SFas.idxProgetto = Pro.idxProgetto AND SFas.idxFaseAncest=fas.idxFase
LEFT OUTER JOIN RegAttivita dati ON dati.inizio = cte.Inizio AND dati.fine = cte.Fine AND dati.descrizione = cte.Descrizione AND dati.idxDipendente = Dip.idxDipendente
WHERE dati.idxRA IS NULL
RETURN
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
/*************************************
* STORED PROCEDURE stp_getLastDays
*
* usando tally table fornisce ultimi numD giorni dalla data richiesta
*
* mod : 24/05/2012
* aut : S.E. Locatelli
**************************************/
alter PROCEDURE stp_getLastDays
(
@lastDate DATETIME,
@numD int = 15 -- numero di giorni da restituire
)
AS
--SELECT Data, DATEPART(dd, Data) as gg
--FROM
--(
SELECT CONVERT(NVARCHAR,CONVERT(DATE,DATEADD(dd, -(N-1), @lastDate)),105) as Data
FROM Tally
WHERE N <= @numD
--) as tblDate
RETURN
go
/**********************************************************
* STORED stp_lcuByDipDate
*
* elenco dati di timbratura e commessa per utente/periodo
*
* mod: S.E.L. 2012.11.02
*
**********************************************************/
alter PROCEDURE stp_lcuByDipDate
(
@idxDipendente INT,
@dataFrom DATETIME,
@dataTo DATETIME,
@maxErrMin INT = 0, -- minuti massimi di scostamento consentiti per DIFETTO, default 0
@maxErrPlus INT = 0 -- minuti massimi di scostamento consentiti per ECCESSO, default 0
)
with recompile
AS
BEGIN
-- faccio una tab temporanea con tallyTable x calcolo periodo intero...
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @dataFrom, @dataTo ) )
-- tab dei record RegAttività progetti
;WITH myCteRA AS(
SELECT idxDipendente, dataLav, minRegAtt, descrProj
FROM dbo.RegAttivitaExpl
WHERE (idxDipendente = @idxDipendente OR @idxDipendente = 0) AND (dataLav >= DATEADD(DAY,-1,@dataFrom) AND dataLav <= @dataTo)
)
SELECT mc.Data, mc.idxDipendente, ISNULL(te.CognomeNome,'') AS CognomeNome, ISNULL(te.h_lav,0) AS h_lav, CAST(ISNULL(vra.minRegAtt,0) AS DECIMAL(9,3))/60 AS h_com,
ISNULL(te.isOk,0) AS okTimbr,
CASE WHEN ISNULL(vra.minRegAtt,0)-ISNULL(te.h_lav,0)*60 >= @maxErrMin AND ISNULL(vra.minRegAtt,0)-ISNULL(te.h_lav,0)*60 <= @maxErrPlus THEN 1 ELSE 0 END AS okLavCom,
ISNULL(vra.descrProj,'') AS progetti, ISNULL(te.minOrd,0) AS minOrd, ISNULL(te.minStra,0) AS minStra, ISNULL(te.minPerm,0) AS minPerm, ISNULL(te.minFer,0) AS minFer
FROM (SELECT CONVERT(DATE,DATEADD(dd, -N, @dataTo)) as Data, idxDipendente
FROM Tally, Dipendenti
WHERE N <= @numD
AND (idxDipendente = @idxDipendente OR @idxDipendente = 0)
) AS mc
LEFT OUTER JOIN myCteRA vra ON mc.Data = vra.dataLav AND mc.idxDipendente=vra.idxDipendente
LEFT OUTER JOIN TimbratureExpl te ON mc.Data=te.dataLav AND te.idxDipendente=mc.idxDipendente
WHERE (te.idxDipendente = @idxDipendente OR @idxDipendente = 0)
ORDER BY mc.Data DESC
END
RETURN
go
/**********************************************************
* STORED stp_RA_clona
*
* clona un attività inserendola in coda alle altre del giorno x lo stesso utente, per pari durata
*
* mod: S.E.L. 2013.01.17
*
**********************************************************/
alter PROCEDURE stp_RA_clona
(
@idxRA INT
)
AS
-- variabili
DECLARE @inizio DATETIME
DECLARE @dataRif DATETIME
DECLARE @idxDip INT
-- calcolo dati attività indicata
SELECT @idxDip=idxDipendente, @dataRif=CONVERT(DATE,inizio)
FROM RegAttivita
WHERE idxRA=@idxRA
-- calcolo fine ultima attività del giorno x l'utente
SELECT TOP 1 @inizio = fine
FROM RegAttivita
WHERE idxDipendente = @idxDip
AND CONVERT(DATE,fine) = @dataRif
ORDER BY fine DESC
-- ora inserisco la vera nuova attività!
INSERT INTO RegAttivita(idxDipendente, idxFase, inizio, fine, descrizione, importo)
--SELECT idxDipendente, idxFase, @inizio, DATEADD(minute, CEILING(oreTot*60/5)*5, @inizio), descrizione, importo -- forzato arrotondamento ai 5 minuti x il clona
SELECT idxDipendente, idxFase, @inizio, DATEADD(minute, DATEDIFF(MINUTE, inizio, fine), @inizio), descrizione, importo
FROM RegAttivita
WHERE (idxRA = @idxRA)
go
commit;
go
set xact_abort on;
go
begin transaction;
go
set ANSI_NULLS on;
go
----------------------------------
-- stp_ricalcolaTimbExpl_byPeriodoUser
--
-- Ricalcola le TimbratureExpl x utente e intervallo date
-- mod: S.E.L. - 2012.09.17
----------------------------------
alter PROCEDURE stp_ricalcolaTimbExpl_byPeriodoUser
(
@idxDipendente INT = 0,
@inizio DATETIME,
@fine DATETIME
)
AS
SET NOCOUNT ON;
-- dichiaro variabili x calcolo tempo execuzione e num records!
DECLARE @startProc DATETIME = GETDATE()
DECLARE @lapTime DATETIME = GETDATE()
DECLARE @totRecords INT = 0
-- verifico fine <= domani...
IF(@fine>GETDATE())
BEGIN
SELECT @fine = DATEADD(DAY,1,CONVERT(DATE,GETDATE()))
END
SET DATEFIRST 1
-- quanti giorni mi servono? conto giorni del mese
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @inizio, @fine) )
-- contatore per ciclo
DECLARE @currRow INT = 0
DECLARE @currIdxDip INT = 0
DECLARE @currDate DATETIME
-- creo la tabella temporanea con riga incrementale
DECLARE @dateAndUser TABLE
(
Riga int IDENTITY(1,1) NOT NULL,
DATA DATETIME NULL,
idxDipendente INT NULL
)
-- elenco delle date DEL MESE (compresi sab/dom) fino a max data odierna
;WITH myCTE AS
(
SELECT CONVERT(DATE,DATEADD(dd, (N-1), @inizio)) as Data, idxDipendente
FROM Tally, Dipendenti
WHERE N <= @numD
AND (idxDipendente = @idxDipendente OR @idxDipendente = 0)
)
-- inserisco valori cte in temp table!
INSERT INTO @dateAndUser
SELECT * FROM myCTE
-- SELECT COUNT(*) FROM @dateAndUser
SELECT @numD = COUNT(*) FROM @dateAndUser
-- eseguo update! Iterazione sulle righe della CTE
WHILE @currRow < @numD
BEGIN
SET @currRow = @currRow + 1
-- Get next customerId
SELECT @currRow=Riga, @currIdxDip=idxDipendente, @currDate=Data
FROM @dateAndUser
WHERE Riga = @currRow
---- Exit loop if no more customers
--IF @@ROWCOUNT = 0 BREAK;
-- X GIANCARLO: se commenti le seguenti hai SOLO il tempo di creare tab temporanea e spazzarla...
-- ESEGUO!
EXEC stp_ricalcolaTimbratureExpl @currIdxDip, @currDate
--SELECT @currIdxDip, @currDate, DATEDIFF(ms, @lapTime, GETDATE()) AS msLastExec
--SELECT @lapTime=GETDATE()
END
SELECT @totRecords = @numD
SELECT @startProc AS startTime, GETDATE() AS endTime, @totRecords AS numRec, DATEDIFF(ms, @startProc, GETDATE()) AS totMs, CAST(@totRecords AS FLOAT) * 1000 / DATEDIFF(ms, @startProc, GETDATE()) AS recPerSec
RETURN
go
/*----------------------------------
* stp_ricalcolaTimbratureExpl
*
* Aggiorna la tab timbrature expl ricalcolando la giornata del record interessato
* mod: S.E.L. - 2012.02.06
*----------------------------------*/
alter PROCEDURE stp_ricalcolaTimbratureExpl
(
@idxDipendente AS INT,
@dataRif AS DATETIME
)
AS
-- variabili
DECLARE @minArr INT
SELECT @minArr=dbo.f_minArrotDip(@idxDipendente)
-- CONTROLLO SE IL RECORD NON SIA BLOCCATO!!!
DECLARE @block BIT
SET @block = ( SELECT ISNULL(( SELECT block FROM TimbratureExpl WHERE (idxDipendente = @idxDipendente) AND CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif) ),0) )
IF(@block = 0)
BEGIN
-- elimino i periodi della data indicata...
DELETE FROM TimbratureExpl
WHERE (idxDipendente = @idxDipendente) AND CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)
-- inserisco i dati minimi per la data indicata
INSERT INTO TimbratureExpl(idxDipendente, dataLav)
VALUES(@idxDipendente, CONVERT(DATE,@dataRif))
-- aggiorno cognome nome
BEGIN TRY
UPDATE te
SET CognomeNome = ISNULL(vcn.CognomeNome,'')
FROM TimbratureExpl te INNER JOIN v_cognomeNome vcn ON te.idxDipendente = vcn.idxDipendente
WHERE CONVERT(DATE,te.dataLav) = CONVERT(DATE,@dataRif) AND te.idxDipendente = @idxDipendente
END TRY
BEGIN CATCH
-- non faccio nulla
END CATCH
-- aggiorno 1° timbratura
UPDATE te
SET te.entrata_1 = pl.entrata,
te.uscita_1 = pl.uscita
FROM TimbratureExpl te INNER JOIN PeriodiLav pl ON
te.idxDipendente = pl.idxDipendente AND
(CONVERT(DATE,te.dataLav) = CONVERT(DATE,pl.entrata))
WHERE pl.rowNum = 1 AND te.idxDipendente = @idxDipendente AND CONVERT(DATE,pl.entrata) = CONVERT(DATE,@dataRif)
-- aggiorno 2° timbratura
UPDATE te
SET te.entrata_2 = pl.entrata,
te.uscita_2 = pl.uscita
FROM TimbratureExpl te INNER JOIN PeriodiLav pl ON te.idxDipendente = pl.idxDipendente
AND (CONVERT(DATE,te.dataLav) = CONVERT(DATE,pl.entrata))
WHERE pl.rowNum = 2 AND te.idxDipendente = @idxDipendente AND CONVERT(DATE,pl.entrata) = CONVERT(DATE,@dataRif)
-- aggiorno 3° timbratura
UPDATE te
SET te.entrata_3 = pl.entrata,
te.uscita_3 = pl.uscita
FROM TimbratureExpl te INNER JOIN PeriodiLav pl ON te.idxDipendente = pl.idxDipendente
AND (CONVERT(DATE,te.dataLav) =CONVERT(DATE,pl.entrata))
WHERE pl.rowNum = 3 AND te.idxDipendente = @idxDipendente AND CONVERT(DATE,pl.entrata) = CONVERT(DATE,@dataRif)
-- aggiorno 4° timbratura
UPDATE te
SET te.entrata_4 = pl.entrata,
te.uscita_4 = pl.uscita
FROM TimbratureExpl te INNER JOIN PeriodiLav pl ON te.idxDipendente = pl.idxDipendente
AND (CONVERT(DATE,te.dataLav) =CONVERT(DATE,pl.entrata))
WHERE pl.rowNum = 4 AND te.idxDipendente = @idxDipendente AND CONVERT(DATE,pl.entrata) = CONVERT(DATE,@dataRif)
-- calcolo ore totali...
UPDATE TimbratureExpl
SET h_lav = CAST(FLOOR((ISNULL(DATEDIFF(n,entrata_1,uscita_1),0) + ISNULL(DATEDIFF(n,entrata_2,uscita_2),0) + ISNULL(DATEDIFF(n,entrata_3,uscita_3),0) + ISNULL(DATEDIFF(n,entrata_4,uscita_4),0))/@minArr) AS FLOAT)*@minArr/60
FROM TimbratureExpl
WHERE (CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)) AND idxDipendente = @idxDipendente
-- verifico: se c'è anche solo 1 timbratura non approvata metto flag isOkApp a falso
UPDATE TimbratureExpl
SET isOkApp = ISNULL((
SELECT MIN(CASE WHEN Approv=0 THEN 0 ELSE 1 END) AS isOk
FROM Timbrature
WHERE idxDipendente = @idxDipendente
AND (CONVERT(DATE,dataOra) = CONVERT(DATE,@dataRif))
GROUP BY CONVERT(DATE,dataOra)
),1)
FROM TimbratureExpl
WHERE (CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)) AND idxDipendente = @idxDipendente
-- verifico: se entrate <> uscite flag isOkTim a falso
UPDATE TimbratureExpl
SET isOkTim = (
SELECT CASE WHEN
ISNULL(
(SELECT COUNT(*) as timb
FROM Timbrature AS t
WHERE t.idxDipendente = @idxDipendente
AND (CONVERT(DATE,dataOra) = CONVERT(DATE,@dataRif))
GROUP BY t.idxDipendente , CONVERT(DATE,t.dataOra)
HAVING COUNT ( CASE Entrata WHEN 1 THEN dataOra END ) <> COUNT ( CASE Entrata WHEN 0 THEN dataOra END )
),0) > 0 THEN 0 ELSE 1 END
)
FROM TimbratureExpl
WHERE (CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)) AND idxDipendente = @idxDipendente
-- calcolo resoconto MINUTI giornaliero, imposto LUN x inizio settimana
SET DATEFIRST 1
-- MIN lavorati, ordinari (con arrotondamento...)
UPDATE TimbratureExpl
SET minLav = CAST((ISNULL(DATEDIFF(n,entrata_1,uscita_1),0) + ISNULL(DATEDIFF(n,entrata_2,uscita_2),0) + ISNULL(DATEDIFF(n,entrata_3,uscita_3),0) + ISNULL(DATEDIFF(n,entrata_4,uscita_4),0)) AS FLOAT)
,minOrd = dbo.f_oreOrdDip(idxDipendente,dataLav) * 60 -- da schema orario dip attivo
FROM TimbratureExpl
WHERE (CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)) AND idxDipendente = @idxDipendente
-- MIN non lavorati, straordinari (con arrotondamento...)
UPDATE TimbratureExpl
SET minNonLav = (CEILING(CONVERT(FLOAT,CASE WHEN minOrd <= minLav THEN (0) ELSE minOrd-minLav END)/@minArr)*@minArr)
,minStra = (FLOOR(CONVERT(FLOAT,CASE WHEN minOrd < minLav AND minLav > (0) THEN minLav-minOrd ELSE (0) END)/@minArr)*@minArr)
FROM TimbratureExpl
WHERE (CONVERT(DATE,dataLav) = CONVERT(DATE,@dataRif)) AND idxDipendente = @idxDipendente
-- MIN da tab giustificativi (feste, ferie, malattia, permessi)
UPDATE TimbratureExpl
SET minMal = CASE WHEN g.codGiust='MAL' THEN g.minuti ELSE minMal END,
minFer = CASE WHEN g.codGiust='FER' THEN g.minuti ELSE minFer END,
minFest = CASE WHEN g.codGiust='FEST' THEN g.minuti ELSE minFest END,
minPerm = CASE WHEN g.codGiust='PERM' THEN g.minuti ELSE minPerm END
FROM TimbratureExpl te INNER JOIN Giustificativi g ON te.idxDipendente = g.idxDipendente AND te.dataLav = g.dataLav
WHERE (CONVERT(DATE,te.dataLav) = CONVERT(DATE,@dataRif)) AND te.idxDipendente = @idxDipendente
END
RETURN
go
/**********************************************************
* STORED stp_timbratureExplFillDate
*
* inserisce nelle TimbratureExpl le date mancanti x ogni dipendente, dato il mese indicato (fino a max data corrente...)
*
* mod: S.E.L. 2012.11.21
*
**********************************************************/
alter PROCEDURE stp_timbratureExplFillDate
(
@inizio DATETIME,
@fine DATETIME
)
AS
SET DATEFIRST 1
-- quanti giorni mi servono? conto giorni del mese
DECLARE @numD INT
SET @numD = ( SELECT DATEDIFF(dd, @inizio, @fine) )
-- elenco delle date DEL MESE (compresi sab/dom) fino a max data odierna
;WITH myCTE AS
(
SELECT CONVERT(DATE,DATEADD(dd, (N-1), @inizio)) as Data, idxDipendente, Cognome + ' ' + Nome + ' [' + Matricola + ']' as CognomeNome
FROM Tally, Dipendenti
WHERE N <= @numD
)
-- INSERISCO le mancanti con i min da lavorare da tab orari x ogni dipendente e le festività
INSERT INTO TimbratureExpl(dataLav, idxDipendente, CognomeNome, h_lav, minOrd, minFest, minPerm)
SELECT mc.Data, mc.idxDipendente, mc.CognomeNome, 0, dbo.f_oreOrdDip(mc.idxDipendente,mc.Data) * 60, dbo.f_oreFerieDip(mc.idxDipendente,mc.Data) * 60, dbo.f_oreOrdDip(mc.idxDipendente,mc.Data) * 60
FROM myCte as mc LEFT OUTER JOIN TimbratureExpl te
ON mc.idxDipendente=te.idxDipendente AND mc.Data = te.dataLav
WHERE te.dataLav IS NULL
RETURN
go
commit;
go
-- registro versione...
INSERT INTO [dbo].[LogUpdateDb] ([Versione],[Data]) VALUES(250, GETDATE())
GO
SELECT * FROM LogUpdateDb ORDER BY Versione DESC