@@ -157,7 +157,7 @@ export const cadastro = (request, response, next) => {
157157 }
158158 return undefined ;
159159 } )
160- // //////////////CRIA COLECOES ANEXAS///////////
160+ // //////////////CRIA COLECOES ANEXAS///////////
161161 . then ( ( ) => {
162162 if ( colecoesAnexas ) {
163163 const object = pick ( colecoesAnexas , [ 'tipo' , 'observacoes' ] ) ;
@@ -174,7 +174,7 @@ export const cadastro = (request, response, next) => {
174174 }
175175 return undefined ;
176176 } )
177- // ///////// VALIDA A TAXONOMIA E A INSERE NO NOME CIENTIFICO //////////
177+ // ///////// VALIDA A TAXONOMIA E A INSERE NO NOME CIENTIFICO //////////
178178 . then ( ( ) => {
179179 if ( taxonomia && taxonomia . familia_id ) {
180180 return Familia . findOne ( {
@@ -296,7 +296,7 @@ export const cadastro = (request, response, next) => {
296296 }
297297 return undefined ;
298298 } )
299- // /////////// CADASTRA TOMBO /////////////
299+ // /////////// CADASTRA TOMBO /////////////
300300 . then ( ( ) => {
301301 let jsonTombo = {
302302 numero_coleta : principal . numero_coleta ,
@@ -373,7 +373,7 @@ export const cadastro = (request, response, next) => {
373373 }
374374 return Tombo . create ( jsonTombo , { transaction } ) ;
375375 } )
376- // //////////// CADASTRA A ALTERACAO ///////////
376+ // //////////// CADASTRA A ALTERACAO ///////////
377377 . then ( tombo => {
378378 if ( ! tombo ) {
379379 throw new BadRequestExeption ( 408 ) ;
@@ -419,7 +419,7 @@ export const cadastro = (request, response, next) => {
419419 return alteracaoTomboCriado ;
420420 } ) ;
421421 } )
422- // /////////////// CADASTRA O INDETIFICADOR ///////////////
422+ // /////////////// CADASTRA O INDETIFICADOR ///////////////
423423 . then ( alteracaoTomboCriado => {
424424 if ( ! alteracaoTomboCriado ) {
425425 throw new BadRequestExeption ( 409 ) ;
@@ -1782,4 +1782,193 @@ export const verificarCoordenada = async (request, response, next) => {
17821782 }
17831783} ;
17841784
1785+ export const relatorioPorPeriodo = async ( request , response , next ) => {
1786+ try {
1787+ const { data_inicio, data_fim, granularidade } = request . query ;
1788+
1789+ if ( ! data_inicio || ! data_fim || ! granularidade ) {
1790+ return response . status ( 400 ) . json ( {
1791+ error : {
1792+ message : 'Parâmetros obrigatórios: data_inicio, data_fim, granularidade' ,
1793+ } ,
1794+ } ) ;
1795+ }
1796+
1797+ // Validar granularidade
1798+ if ( ! [ 'dia' , 'semana' , 'mes' , 'ano' ] . includes ( granularidade ) ) {
1799+ return response . status ( 400 ) . json ( {
1800+ error : {
1801+ message : 'Granularidade inválida. Use: dia, semana, mes ou ano' ,
1802+ } ,
1803+ } ) ;
1804+ }
1805+
1806+ const dataInicio = new Date ( data_inicio ) ;
1807+ const dataFim = new Date ( data_fim ) ;
1808+
1809+ if ( isNaN ( dataInicio . getTime ( ) ) || isNaN ( dataFim . getTime ( ) ) ) {
1810+ return response . status ( 400 ) . json ( {
1811+ error : {
1812+ message : 'Datas inválidas. Use o formato YYYY-MM-DD' ,
1813+ } ,
1814+ } ) ;
1815+ }
1816+
1817+ if ( dataInicio > dataFim ) {
1818+ return response . status ( 400 ) . json ( {
1819+ error : {
1820+ message : 'Data de início deve ser menor ou igual à data de fim' ,
1821+ } ,
1822+ } ) ;
1823+ }
1824+
1825+ // Calcular diferenças para validar granularidade
1826+ const diffMs = dataFim - dataInicio ;
1827+ const diffDias = Math . ceil ( diffMs / ( 1000 * 60 * 60 * 24 ) ) ;
1828+ const diffSemanas = Math . ceil ( diffDias / 7 ) ;
1829+ const diffMeses = Math . ceil ( ( dataFim . getFullYear ( ) - dataInicio . getFullYear ( ) ) * 12 + ( dataFim . getMonth ( ) - dataInicio . getMonth ( ) ) ) ;
1830+ const diffAnos = dataFim . getFullYear ( ) - dataInicio . getFullYear ( ) ;
1831+
1832+ // Validar granularidade baseada no período
1833+ let granularidadePermitida = 'ano' ;
1834+ if ( diffDias <= 30 ) {
1835+ granularidadePermitida = 'dia' ;
1836+ } else if ( diffSemanas <= 30 ) {
1837+ granularidadePermitida = 'semana' ;
1838+ } else if ( diffMeses <= 30 ) {
1839+ granularidadePermitida = 'mes' ;
1840+ } else {
1841+ granularidadePermitida = 'ano' ;
1842+ }
1843+
1844+ // Mapear granularidades para verificação hierárquica
1845+ const granularidadeHierarquia = { dia : 0 , semana : 1 , mes : 2 , ano : 3 } ;
1846+ const granularidadeSolicitada = granularidadeHierarquia [ granularidade ] ;
1847+ const granularidadeMaximaPermitida = granularidadeHierarquia [ granularidadePermitida ] ;
1848+
1849+ if ( granularidadeSolicitada < granularidadeMaximaPermitida ) {
1850+ return response . status ( 400 ) . json ( {
1851+ error : {
1852+ message : `Período muito grande para granularidade '${ granularidade } '. Máximo: ${ diffDias } dias. Use granularidade '${ granularidadePermitida } ' ou maior.` ,
1853+ restricoes : {
1854+ difDias : diffDias ,
1855+ difSemanas : diffSemanas ,
1856+ difMeses : diffMeses ,
1857+ difAnos : diffAnos ,
1858+ granularidadePermitida : granularidadePermitida ,
1859+ } ,
1860+ } ,
1861+ } ) ;
1862+ }
1863+
1864+ let query = '' ;
1865+
1866+ if ( granularidade === 'dia' ) {
1867+ query = `
1868+ WITH date_series AS (
1869+ SELECT generate_series($1::date, $2::date, '1 day'::interval)::date AS data_p
1870+ ),
1871+ counts AS (
1872+ SELECT
1873+ TO_DATE(CONCAT(data_coleta_ano, '-', LPAD(data_coleta_mes::text, 2, '0'), '-', LPAD(data_coleta_dia::text, 2, '0')), 'YYYY-MM-DD') AS data_c,
1874+ COUNT(*) AS qtd
1875+ FROM tombos
1876+ WHERE rascunho = false
1877+ AND data_coleta_ano IS NOT NULL
1878+ AND data_coleta_mes IS NOT NULL
1879+ AND data_coleta_dia IS NOT NULL
1880+ GROUP BY 1
1881+ )
1882+ SELECT
1883+ TO_CHAR(ds.data_p, 'DD/MM/YYYY') AS periodo,
1884+ COALESCE(c.qtd, 0) AS quantidade
1885+ FROM date_series ds
1886+ LEFT JOIN counts c ON ds.data_p = c.data_c
1887+ ORDER BY ds.data_p ASC
1888+ ` ;
1889+ } else if ( granularidade === 'semana' ) {
1890+ query = `
1891+ WITH date_series AS (
1892+ SELECT generate_series(date_trunc('week', $1::date), date_trunc('week', $2::date), '1 week'::interval)::date AS data_p
1893+ ),
1894+ counts AS (
1895+ SELECT
1896+ date_trunc('week', TO_DATE(CONCAT(data_coleta_ano, '-', LPAD(data_coleta_mes::text, 2, '0'), '-', LPAD(data_coleta_dia::text, 2, '0')), 'YYYY-MM-DD'))::date AS data_c,
1897+ COUNT(*) AS qtd
1898+ FROM tombos
1899+ WHERE rascunho = false
1900+ AND data_coleta_ano IS NOT NULL
1901+ AND data_coleta_mes IS NOT NULL
1902+ AND data_coleta_dia IS NOT NULL
1903+ GROUP BY 1
1904+ )
1905+ SELECT
1906+ 'Semana ' || LPAD((ROW_NUMBER() OVER(ORDER BY ds.data_p))::text, 2, '0') AS periodo,
1907+ COALESCE(c.qtd, 0) AS quantidade
1908+ FROM date_series ds
1909+ LEFT JOIN counts c ON ds.data_p = c.data_c
1910+ ORDER BY ds.data_p ASC
1911+ ` ;
1912+ } else if ( granularidade === 'mes' ) {
1913+ query = `
1914+ WITH date_series AS (
1915+ SELECT generate_series(date_trunc('month', $1::date), date_trunc('month', $2::date), '1 month'::interval)::date AS data_p
1916+ ),
1917+ counts AS (
1918+ SELECT
1919+ date_trunc('month', TO_DATE(CONCAT(t.data_coleta_ano, '-', LPAD(t.data_coleta_mes::text, 2, '0'), '-01'), 'YYYY-MM-DD'))::date AS data_c,
1920+ COUNT(*) AS qtd
1921+ FROM tombos t
1922+ WHERE t.rascunho = false
1923+ AND t.data_coleta_ano IS NOT NULL
1924+ AND t.data_coleta_mes IS NOT NULL
1925+ GROUP BY 1
1926+ )
1927+ SELECT
1928+ TO_CHAR(ds.data_p, 'MM/YYYY') AS periodo,
1929+ COALESCE(c.qtd, 0) AS quantidade
1930+ FROM date_series ds
1931+ LEFT JOIN counts c ON ds.data_p = c.data_c
1932+ ORDER BY ds.data_p ASC
1933+ ` ;
1934+ } else if ( granularidade === 'ano' ) {
1935+ query = `
1936+ WITH date_series AS (
1937+ SELECT generate_series(date_trunc('year', $1::date), date_trunc('year', $2::date), '1 year'::interval)::date AS data_p
1938+ ),
1939+ counts AS (
1940+ SELECT
1941+ data_coleta_ano AS ano_c,
1942+ COUNT(*) AS qtd
1943+ FROM tombos t
1944+ WHERE t.rascunho = false
1945+ AND t.data_coleta_ano IS NOT NULL
1946+ GROUP BY 1
1947+ )
1948+ SELECT
1949+ TO_CHAR(ds.data_p, 'YYYY') AS periodo,
1950+ COALESCE(c.qtd, 0) AS quantidade
1951+ FROM date_series ds
1952+ LEFT JOIN counts c ON EXTRACT(YEAR FROM ds.data_p) = c.ano_c
1953+ ORDER BY ds.data_p ASC
1954+ ` ;
1955+ }
1956+
1957+ const resultado = await sequelize . query ( query , {
1958+ bind : [ data_inicio , data_fim ] ,
1959+ type : models . Sequelize . QueryTypes . SELECT ,
1960+ } ) ;
1961+
1962+ // Mapear resultados para o formato esperado
1963+ const dados = resultado . map ( item => ( {
1964+ periodo : item . periodo ,
1965+ quantidade : parseInt ( item . quantidade , 10 ) ,
1966+ } ) ) ;
1967+
1968+ return response . status ( 200 ) . json ( dados ) ;
1969+ } catch ( err ) {
1970+ return next ( err ) ;
1971+ }
1972+ } ;
1973+
17851974export default { } ;
0 commit comments