let
    Fato_Jornada_Comparacao = let

    // =========================================================
    // FONTE
    // =========================================================

    Jornada = Fato_Jornada_Esperada,

    Intervalos = Fato_Intervalos_Dia,

    Ajustes = Fato_Ajustes_Dia,


    // =========================================================
    // PADRONIZAÇÃO DAS CHAVES
    // =========================================================

    JornadaTipos = Table.TransformColumns(
        Jornada,
        {
            {"FuncionarioID", each Int64.From(_), Int64.Type},
            {"Data", each Date.From(_), type date}
        }
    ),

    IntervalosTipos = Table.TransformColumns(
        Intervalos,
        {
            {"FuncionarioID", each Int64.From(_), Int64.Type},
            {"DataJornada", each Date.From(_), type date}
        }
    ),

    AjustesTipos = Table.TransformColumns(
        Ajustes,
        {
            {"FuncionarioID", each Int64.From(_), Int64.Type},
            {"Data", each Date.From(_), type date}
        }
    ),


    // =========================================================
    // MERGE DOS INTERVALOS REAIS
    // =========================================================

    MesclarIntervalos = Table.NestedJoin(
        JornadaTipos,
        {"FuncionarioID", "Data"},
        IntervalosTipos,
        {"FuncionarioID", "DataJornada"},
        "DadosIntervalos",
        JoinKind.LeftOuter
    ),

    ExpandirIntervalos = Table.ExpandTableColumn(
        MesclarIntervalos,
        "DadosIntervalos",
        {
            "QuantidadeIntervalos",
            "HorasTrabalhadas",
            "Entrada1",
            "Saida1",
            "Entrada2",
            "Saida2",
            "Entrada3",
            "Saida3",
            "Entrada4",
            "Saida4",
            "QuantidadeEntradas",
            "QuantidadeSaidas",
            "Intervalos"
        },
        {
            "QuantidadeIntervalos",
            "HorasTrabalhadas",
            "Entrada1Real",
            "Saida1Real",
            "Entrada2Real",
            "Saida2Real",
            "Entrada3Real",
            "Saida3Real",
            "Entrada4Real",
            "Saida4Real",
            "QuantidadeEntradas",
            "QuantidadeSaidas",
            "Intervalos"
        }
    ),


    // =========================================================
    // MERGE DOS AJUSTES
    // =========================================================

    MesclarAjustes = Table.NestedJoin(
        ExpandirIntervalos,
        {"FuncionarioID", "Data"},
        AjustesTipos,
        {"FuncionarioID", "Data"},
        "DadosAjustes",
        JoinKind.LeftOuter
    ),

    ExpandirAjustes = Table.ExpandTableColumn(
        MesclarAjustes,
        "DadosAjustes",
        {
            "QuantidadeAjustes",
            "TemAjusteAbonado",
            "TemAjusteDiaCompleto",
            "TemAjusteContaComoFalta",
            "HorasAbonadasParciais",
            "HorasFaltaParciais",
            "MotivosAjustes",
            "AjustesDia"
        },
        {
            "QuantidadeAjustes",
            "TemAjusteAbonado",
            "TemAjusteDiaCompleto",
            "TemAjusteContaComoFalta",
            "HorasAbonadasParciais",
            "HorasFaltaParciais",
            "MotivosAjustes",
            "AjustesDia"
        }
    ),

    // =========================================================
    // MERGE DOS FERIADOS
    // =========================================================

    MesclarFeriados = Table.NestedJoin(
        ExpandirAjustes,
        {"Data"},
        Dim_Feriados,
        {"Data"},
        "DadosFeriado",
        JoinKind.LeftOuter
    ),

    ExpandirFeriado = Table.ExpandTableColumn(
        MesclarFeriados,
        "DadosFeriado",
        {
            "FeriadoID",
            "NomeFeriado"
        },
        {
            "FeriadoID",
            "NomeFeriado"
        }
    ),

    IdentificarFeriado = Table.AddColumn(
        ExpandirFeriado,
        "Feriado",
        each [FeriadoID] <> null,
        type logical
    ),

    // =========================================================
    // VALORES PADRÃO
    // =========================================================

    Quantidades = Table.TransformColumns(
        IdentificarFeriado,
        {
            {
                "QuantidadeIntervalos",
                each if _ = null then 0 else _,
                Int64.Type
            },
            {
                "QuantidadeEntradas",
                each if _ = null then 0 else _,
                Int64.Type
            },
            {
                "QuantidadeSaidas",
                each if _ = null then 0 else _,
                Int64.Type
            },
            {
                "QuantidadeAjustes",
                each if _ = null then 0 else _,
                Int64.Type
            },
            {
                "HorasTrabalhadas",
                each if _ = null then 0 else _,
                type number
            }
        }
    ),


    // =========================================================
    // QUANTIDADE DE BATIDAS
    // =========================================================

    QuantidadeBatidas = Table.AddColumn(
        Quantidades,
        "QuantidadeBatidas",
        each
            [QuantidadeEntradas] + [QuantidadeSaidas],
        Int64.Type
    ),


    BatidasImpares = Table.AddColumn(
        QuantidadeBatidas,
        "BatidasImpares",
        each
            Number.Mod([QuantidadeBatidas], 2) <> 0,
        type logical
    ),


    BatidasDiferentes4 = Table.AddColumn(
        BatidasImpares,
        "BatidasDiferentes4",
        each
            let
                Esperadas =
                    if [Entrada1Esperada] <> null and [Saida1Esperada] <> null then
                        if [Entrada2Esperada] <> null and [Saida2Esperada] <> null then
                            4
                        else
                            2
                    else
                        0
            in
                [QuantidadeBatidas] <> Esperadas,
        type logical
    ),


    SituacaoBatidas = Table.AddColumn(
        BatidasDiferentes4,
        "SituacaoBatidas",
        each
            let
                Esperadas =
                    if [Entrada1Esperada] <> null and [Saida1Esperada] <> null then
                        if [Entrada2Esperada] <> null and [Saida2Esperada] <> null then
                            4
                        else
                            2
                    else
                        0
            in
                if
                    [TemAjusteContaComoFalta] = true
                    and [QuantidadeBatidas] = 0
                then
                    "Nenhum registro"

                else if Esperadas = 0 and [QuantidadeBatidas] = 0 then
                    "Nenhum registro"

                else if Esperadas = 0 and [QuantidadeBatidas] > 0 then
                    "Trabalho sem jornada prevista"

                else if [QuantidadeBatidas] = Esperadas then
                    "Quantidade correta"

                else if [QuantidadeBatidas] < Esperadas then
                    "Quantidade incompleta"

                else if [BatidasImpares] = true then
                    "Quantidade ímpar de registros"

                else
                    "Quantidade acima do esperado",
        type text
    ),


    // =========================================================
    // FUNÇÃO - COBERTURA DE AJUSTE
    //
    // Verifica quanto do período esperado é coberto
    // por ajustes da API.
    //
    // Ajustes que contam como falta não são considerados
    // como cobertura.
    // =========================================================

    VerificarCoberturaAjuste = (
        InicioEsperado as nullable datetime,
        FimEsperado as nullable datetime,
        ListaAjustes as nullable table
    ) as record =>

        let

            ResultadoVazio = [
                CoberturaSegundos = 0,
                CoberturaCompleta = false,
                Motivos = null
            ],

            Resultado =

                if
                    InicioEsperado = null
                    or FimEsperado = null
                    or ListaAjustes = null
                    or Table.RowCount(ListaAjustes) = 0
                then
                    ResultadoVazio

                else

                    let

                        AjustesValidos = Table.SelectRows(
                            ListaAjustes,
                            each
                                [DataInicio] <> null
                                and [DataFim] <> null
                                and [AjusteContaComoFalta] <> true
                        ),

                        AjustesComSobreposicao =
                            Table.AddColumn(
                                AjustesValidos,
                                "SobreposicaoSegundos",
                                each
                                    let

                                        InicioAjuste =
                                            if [AjusteDiaCompleto] = true
                                            then
                                                DateTime.From(
                                                    Date.From(InicioEsperado)
                                                )
                                            else
                                                [DataInicio],

                                        FimAjuste =
                                            if [AjusteDiaCompleto] = true
                                            then
                                                DateTime.From(
                                                    Date.AddDays(
                                                        Date.From(InicioEsperado),
                                                        1
                                                    )
                                                )
                                            else
                                                [DataFim],

                                        Inicio =
                                            if InicioAjuste > InicioEsperado
                                            then InicioAjuste
                                            else InicioEsperado,

                                        Fim =
                                            if FimAjuste < FimEsperado
                                            then FimAjuste
                                            else FimEsperado

                                    in
                                        if Fim > Inicio
                                        then
                                            Duration.TotalSeconds(
                                                Fim - Inicio
                                            )
                                        else
                                            0,
                                type number
                            ),

                        Cobertura =
                            List.Sum(
                                List.RemoveNulls(
                                    AjustesComSobreposicao[
                                        SobreposicaoSegundos
                                    ]
                                )
                            ),

                        DuracaoEsperada =
                            Duration.TotalSeconds(
                                FimEsperado - InicioEsperado
                            ),

                        Motivos =
                            let
                                AjustesComCobertura =
                                    Table.SelectRows(
                                        AjustesComSobreposicao,
                                        each [SobreposicaoSegundos] > 0
                                    )
                            in
                                if Table.RowCount(AjustesComCobertura) = 0 then
                                    null
                                else
                                    Text.Combine(
                                        List.Distinct(
                                            List.RemoveNulls(
                                                AjustesComCobertura[DescricaoMotivo]
                                            )
                                        ),
                                        " | "
                                    ),

                        CoberturaCompleta =
                            Cobertura >= DuracaoEsperada - 60

                    in

                        [
                            CoberturaSegundos = Cobertura,
                            CoberturaCompleta = CoberturaCompleta,
                            Motivos = Motivos
                        ]

        in
            Resultado,


    // =========================================================
    // COBERTURA DA JANELA 1
    // =========================================================

    CoberturaJanela1 = Table.AddColumn(
        SituacaoBatidas,
        "CoberturaJanela1",
        each
            VerificarCoberturaAjuste(
                [Entrada1Esperada],
                [Saida1Esperada],
                [AjustesDia]
            ),
        type record
    ),


    ExpandirCoberturaJanela1 = Table.ExpandRecordColumn(
        CoberturaJanela1,
        "CoberturaJanela1",
        {
            "CoberturaSegundos",
            "CoberturaCompleta",
            "Motivos"
        },
        {
            "Janela1CoberturaAjusteSegundos",
            "Janela1AjusteCobreTudo",
            "Janela1MotivosAjuste"
        }
    ),


    // =========================================================
    // COBERTURA DA JANELA 2
    // =========================================================

    CoberturaJanela2 = Table.AddColumn(
        ExpandirCoberturaJanela1,
        "CoberturaJanela2",
        each
            VerificarCoberturaAjuste(
                [Entrada2Esperada],
                [Saida2Esperada],
                [AjustesDia]
            ),
        type record
    ),


    ExpandirCoberturaJanela2 = Table.ExpandRecordColumn(
        CoberturaJanela2,
        "CoberturaJanela2",
        {
            "CoberturaSegundos",
            "CoberturaCompleta",
            "Motivos"
        },
        {
            "Janela2CoberturaAjusteSegundos",
            "Janela2AjusteCobreTudo",
            "Janela2MotivosAjuste"
        }
    ),


    // =========================================================
    // COBERTURA GERAL DOS AJUSTES
    // =========================================================

    AjusteCobreJornada = Table.AddColumn(
        ExpandirCoberturaJanela2,
        "AjusteCobreJornada",
        each
            [Janela1AjusteCobreTudo] = true
            or [Janela2AjusteCobreTudo] = true,
        type logical
    ),


    // =========================================================
    // JORNADA PREVISTA
    // =========================================================

    JornadaPrevista = Table.AddColumn(
        AjusteCobreJornada,
        "JornadaPrevista",
        each
            [Entrada1Esperada] <> null
            and [Saida1Esperada] <> null,
        type logical
    ),


    // =========================================================
    // QUANTIDADE DE BATIDAS ESPERADAS
    // =========================================================

    QuantidadeBatidasEsperadas = Table.AddColumn(
        JornadaPrevista,
        "QuantidadeBatidasEsperadas",
        each
            if [Entrada1Esperada] <> null and [Saida1Esperada] <> null then
                if [Entrada2Esperada] <> null and [Saida2Esperada] <> null then
                    4
                else
                    2
            else
                0,
        Int64.Type
    ),


    // =========================================================
    // BATIDAS INCOMPLETAS
    // =========================================================

    BatidasIncompletas = Table.AddColumn(
        QuantidadeBatidasEsperadas,
        "BatidasIncompletas",
        each
            if [QuantidadeBatidas] = 0 then
                false
            else if [QuantidadeBatidas] < [QuantidadeBatidasEsperadas] then
                true
            else
                false,
        type logical
    ),


    // =========================================================
    // BATIDAS ACIMA DO ESPERADO
    // =========================================================

    BatidasAcimaEsperado = Table.AddColumn(
        BatidasIncompletas,
        "BatidasAcimaEsperado",
        each
            if [QuantidadeBatidasEsperadas] = null then
                false
            else
                [QuantidadeBatidas] > [QuantidadeBatidasEsperadas],
        type logical
    ),


    // =========================================================
    // HORAS PREVISTAS
    // =========================================================

    HorasEsperadas = Table.AddColumn(
        BatidasAcimaEsperado,
        "HorasEsperadas",
        each
            if [Feriado] = true then
                0
            else
                let
                    Entrada1 = [Entrada1Esperada],
                    Saida1 = [Saida1Esperada],
                    Entrada2 = [Entrada2Esperada],
                    Saida2 = [Saida2Esperada],

                    Jornada1 =
                        if Entrada1 <> null and Saida1 <> null then
                            let
                                Inicio =
                                    #datetime(
                                        2000, 1, 1,
                                        Time.Hour(Entrada1),
                                        Time.Minute(Entrada1),
                                        Time.Second(Entrada1)
                                    ),

                                FimBase =
                                    #datetime(
                                        2000, 1, 1,
                                        Time.Hour(Saida1),
                                        Time.Minute(Saida1),
                                        Time.Second(Saida1)
                                    ),

                                Fim =
                                    if FimBase < Inicio then
                                        FimBase + #duration(1, 0, 0, 0)
                                    else
                                        FimBase
                            in
                                Duration.TotalSeconds(Fim - Inicio)
                        else
                            0,

                    Jornada2 =
                        if Entrada2 <> null and Saida2 <> null then
                            let
                                Inicio =
                                    #datetime(
                                        2000, 1, 1,
                                        Time.Hour(Entrada2),
                                        Time.Minute(Entrada2),
                                        Time.Second(Entrada2)
                                    ),

                                FimBase =
                                    #datetime(
                                        2000, 1, 1,
                                        Time.Hour(Saida2),
                                        Time.Minute(Saida2),
                                        Time.Second(Saida2)
                                    ),

                                Fim =
                                    if FimBase < Inicio then
                                        FimBase + #duration(1, 0, 0, 0)
                                    else
                                        FimBase
                            in
                                Duration.TotalSeconds(Fim - Inicio)
                        else
                            0

                in
                    (Jornada1 + Jornada2) / 3600,
        type number
    ),


    // =========================================================
    // HORAS ABONADAS
    //
    // Usa a cobertura efetivamente calculada pelos ajustes.
    //
    // Isso é importante porque o mesmo período usado para
    // determinar a cobertura do ajuste será descontado da
    // jornada prevista.
    // =========================================================

	HorasAbonadas = Table.AddColumn(
		HorasEsperadas,
		"HorasAbonadas",
		each
			if [Feriado] = true then
				0

			else if [TemAjusteAbonado] <> true then
				0

			else if
				[TemAjusteDiaCompleto] = true
				and [TemAjusteContaComoFalta] = false
			then
				[HorasEsperadas]

			else if [HorasAbonadasParciais] = null then
				0

			else
				[HorasAbonadasParciais],
		type number
	),


    // =========================================================
    // HORAS FALTA
    // =========================================================

    HorasFalta = Table.AddColumn(
        HorasAbonadas,
        "HorasFalta",
        each
            if [Feriado] = true then
                0

            else if [TemAjusteContaComoFalta] = true then

                if [QuantidadeBatidas] = 0 then
                    [HorasEsperadas]

                else if [HorasFaltaParciais] = null then
                    0

                else
                    [HorasFaltaParciais]

            else
                0,
        type number
    ),


    // =========================================================
    // HORAS PREVISTAS AJUSTADAS
    // =========================================================

	HorasPrevistasAjustadas = Table.AddColumn(
		HorasFalta,
		"HorasPrevistasAjustadas",
		each
			if [Feriado] = true then
				0

			else if
				[TemAjusteDiaCompleto] = true
				and [TemAjusteAbonado] = true
				and [TemAjusteContaComoFalta] = false
			then
				0

			else if [HorasEsperadas] - [HorasAbonadas] < 0 then
				0

			else
				[HorasEsperadas] - [HorasAbonadas],
		type number
	),


	// =========================================================
	// HORAS FICTAS - HORA NOTURNA REDUZIDA
	//
	// Período noturno: 22:00 até 05:00.
	//
	// A função considera tanto:
	// - 22:00 até 00:00 do próprio dia
	// - 00:00 até 05:00 do dia seguinte
	//
	// Isso também permite reconhecer trabalho entre 00:00 e
	// 05:00 mesmo quando a jornada começou no dia anterior.
	// =========================================================

	CalcularHorasFictas =
		(Entrada as nullable datetime, Saida as nullable datetime) as number =>
		let
			Resultado =
				if Entrada = null or Saida = null then
					0
				else
					let

						// Se a saída estiver cronologicamente
						// antes da entrada, considera o dia seguinte.
						SaidaAjustada =
							if Saida < Entrada then
								Saida + #duration(1,0,0,0)
							else
								Saida,

						DataEntrada =
							Date.From(Entrada),

						// =====================================================
						// JANELA NOTURNA DO DIA DA ENTRADA
						// 22:00 até 05:00 do dia seguinte
						// =====================================================

						InicioNoturnoAtual =
							DateTime.From(DataEntrada)
							+ #duration(0,22,0,0),

						FimNoturnoAtual =
							DateTime.From(
								Date.AddDays(DataEntrada, 1)
							)
							+ #duration(0,5,0,0),

						InicioSobreposicaoAtual =
							if Entrada > InicioNoturnoAtual then
								Entrada
							else
								InicioNoturnoAtual,

						FimSobreposicaoAtual =
							if SaidaAjustada < FimNoturnoAtual then
								SaidaAjustada
							else
								FimNoturnoAtual,

						MinutosNoturnosAtual =
							if FimSobreposicaoAtual > InicioSobreposicaoAtual then
								Duration.TotalMinutes(
									FimSobreposicaoAtual
									- InicioSobreposicaoAtual
								)
							else
								0,

						// =====================================================
						// JANELA NOTURNA DO DIA ANTERIOR
						// 22:00 do dia anterior até 05:00 do dia da entrada
						//
						// Necessária para casos como:
						// 00:01 → 07:00
						// =====================================================

						DataAnterior =
							Date.AddDays(
								DataEntrada,
								-1
							),

						InicioNoturnoAnterior =
							DateTime.From(DataAnterior)
							+ #duration(0,22,0,0),

						FimNoturnoAnterior =
							DateTime.From(DataEntrada)
							+ #duration(0,5,0,0),

						InicioSobreposicaoAnterior =
							if Entrada > InicioNoturnoAnterior then
								Entrada
							else
								InicioNoturnoAnterior,

						FimSobreposicaoAnterior =
							if SaidaAjustada < FimNoturnoAnterior then
								SaidaAjustada
							else
								FimNoturnoAnterior,

						MinutosNoturnosAnterior =
							if FimSobreposicaoAnterior > InicioSobreposicaoAnterior then
								Duration.TotalMinutes(
									FimSobreposicaoAnterior
									- InicioSobreposicaoAnterior
								)
							else
								0,

						// Soma das duas possíveis janelas noturnas
						MinutosNoturnos =
							MinutosNoturnosAtual
							+ MinutosNoturnosAnterior,

						// 60 minutos reais = 52,5 minutos fictos.
						// Acréscimo = 60 / 52,5 - 1
						MinutosFictos =
							Number.RoundDown(
								MinutosNoturnos *
								((60 / 52.5) - 1),
								0
							),

						HorasFictas =
							MinutosFictos / 60

					in
						HorasFictas
		in
			Resultado,


	HorasFictasCalculadas =
		Table.AddColumn(
			HorasPrevistasAjustadas,
			"HorasFictas",
			each
				if [Intervalos] = null then
					0
				else
					let
						RegistrosIntervalos =
							Table.ToRecords([Intervalos]),

						FictasPorIntervalo =
							List.Transform(
								RegistrosIntervalos,
								each
									CalcularHorasFictas(
										_[Entrada],
										_[Saida]
									)
							),

						TotalHorasFictas =
							List.Sum(
								List.RemoveNulls(
									FictasPorIntervalo
								)
							)
					in
						TotalHorasFictas,
			type number
		),
		
    // =========================================================
    // FIM DE SEMANA - TRABALHO REALIZADO
    // =========================================================

    TrabalhoFimDeSemana = Table.AddColumn(
        HorasFictasCalculadas,
        "TrabalhoFimDeSemana",
        each
            (
                [DiaSemana] = 1
                or [DiaSemana] = 7
            )
            and [QuantidadeBatidas] > 0,
        type logical
    ),

    FimSemanaFormatacao = Table.AddColumn(
        TrabalhoFimDeSemana,
        "FimSemanaFormatacao",
        each if [TrabalhoFimDeSemana] = true then 1 else 0,
        Int64.Type
    ),


    // =========================================================
    // SALDO DA JORNADA
    //
    // Mantém o saldo baseado na jornada real da escala.
    // Não utilizar este saldo diretamente para determinar
    // atraso/hora extra, pois existem escalas de 08:29.
    // =========================================================

    SaldoHoras = Table.AddColumn(
        FimSemanaFormatacao,
        "SaldoHoras",
        each
            if [TrabalhoFimDeSemana] = true
                and [HorasTrabalhadas] <> null
            then
                Number.Round(
                    [HorasTrabalhadas]
                    + [HorasFictas],
                    6
                )

            else if [JornadaPrevista] = true
                and [HorasTrabalhadas] <> null
            then
                Number.Round(
                    [HorasTrabalhadas]
                    + [HorasFictas]
                    - [HorasPrevistasAjustadas],
                    6
                )

            else
                null,
        type number
    ),


    // =========================================================
    // BASE DE CONTROLE DE ATRASO / HORA EXTRA
    //
    // REGRA ESPECÍFICA:
    // Para jornadas de 08:29, o controle de atraso/hora extra
    // utiliza 08:48 como jornada de referência.
    //
    // Para todas as demais jornadas, mantém a jornada
    // atualmente utilizada pelo modelo.
    // =========================================================

    HorasBaseControle = Table.AddColumn(
        SaldoHoras,
        "HorasBaseControle",
        each
            if [TrabalhoFimDeSemana] = true then
                0

            else if [JornadaPrevista] <> true then
                null

            else if [Feriado] = true then
                0

            else if
                [TemAjusteDiaCompleto] = true
                and [TemAjusteAbonado] = true
                and [TemAjusteContaComoFalta] <> true
            then
                0

            else
                let
                    JornadaMinutos =
                        Number.Round(
                            [HorasEsperadas] * 60,
                            0
                        ),

                    JornadaEh0829 =
                        JornadaMinutos = 509,

                    Base =
                        if JornadaEh0829 then
                            8.8
                        else
                            [HorasPrevistasAjustadas],

                    BaseAjustada =
                        if JornadaEh0829 then
                            if Base - [HorasAbonadas] < 0 then
                                0
                            else
                                Base - [HorasAbonadas]
                        else
                            Base
                in
                    BaseAjustada,
        type number
    ),


    // =========================================================
    // SALDO DE CONTROLE
    //
    // Positivo = horas acima da base
    // Negativo = horas abaixo da base
    //
    // Inclui as horas fictas.
    // =========================================================

    SaldoControleHoras = Table.AddColumn(
        HorasBaseControle,
        "SaldoControleHoras",
        each
            if [TrabalhoFimDeSemana] = true
                and [HorasTrabalhadas] <> null
            then
                Number.Round(
                    [HorasTrabalhadas]
                    + [HorasFictas],
                    6
                )

            else if [JornadaPrevista] = true
                and [HorasTrabalhadas] <> null
            then
                Number.Round(
                    [HorasTrabalhadas]
                    + [HorasFictas]
                    - [HorasBaseControle],
                    6
                )

            else
                null,
        type number
    ),


    // =========================================================
    // ATRASO
    //
    // Tolerância: 10 minutos.
    //
    // Até -00:10 = não caracteriza atraso.
    // Abaixo de -00:10 = atraso.
    // =========================================================

    AtrasoHoras = Table.AddColumn(
        SaldoControleHoras,
        "AtrasoHoras",
        each
            if [JornadaPrevista] <> true
                or [HorasTrabalhadas] = null
            then
                null

            else if
                [TemAjusteContaComoFalta] = true
                and [QuantidadeBatidas] = 0
            then
                0

            else
                let
                    SaldoSegundos =
                        Number.Round(
                            [SaldoControleHoras] * 3600,
                            0
                        )
                in
                    if SaldoSegundos < -600 then
                        Number.Abs([SaldoControleHoras])
                    else
                        0,
        type number
    ),


    AtrasoMinutos = Table.AddColumn(
        AtrasoHoras,
        "AtrasoMinutos",
        each
            if [AtrasoHoras] = null
            then null
            else [AtrasoHoras] * 60,
        type number
    ),


    AtrasoAcimaTolerancia = Table.AddColumn(
        AtrasoMinutos,
        "AtrasoAcimaTolerancia",
        each
            if [AtrasoMinutos] = null
            then false
            else [AtrasoMinutos] > 10,
        type logical
    ),


    // =========================================================
    // HORAS EXTRAS
    //
    // Para jornadas de 08:29:
    // Base = 08:48
    // Tolerância = 10 minutos
    //
    // Para as demais jornadas:
    // mantém a base atual do modelo.
    // =========================================================

    HoraExtraHoras = Table.AddColumn(
        AtrasoAcimaTolerancia,
        "HoraExtraHoras",
        each
            if [HorasTrabalhadas] = null then
                null

            else if [TrabalhoFimDeSemana] = true then
                [HorasTrabalhadas] + [HorasFictas]

            else if [JornadaPrevista] <> true then
                [HorasTrabalhadas] + [HorasFictas]

            else if
                Number.Round(
                    [SaldoControleHoras] * 3600,
                    0
                ) > 600
            then
                [SaldoControleHoras]

            else
                0,
        type number
    ),


    HoraExtraMinutos = Table.AddColumn(
        HoraExtraHoras,
        "HoraExtraMinutos",
        each
            if [HoraExtraHoras] = null
            then null
            else [HoraExtraHoras] * 60,
        type number
    ),


    HoraExtraAcimaTolerancia = Table.AddColumn(
        HoraExtraMinutos,
        "HoraExtraAcimaTolerancia",
        each
            if [HoraExtraMinutos] = null
            then false
            else [HoraExtraMinutos] > 10,
        type logical
    ),


    // =========================================================
    // HORA EXTRA ACIMA DO LIMITE
    //
    // Limite RH: 01:12 (72 minutos).
    // =========================================================

    HoraExtraAcimaLimite = Table.AddColumn(
        HoraExtraAcimaTolerancia,
        "HoraExtraAcimaLimite",
        each
            if [HoraExtraMinutos] = null
            then false
            else [HoraExtraMinutos] > 72,
        type logical
    ),


    // =========================================================
    // SALDO CONSIDERADO
    // =========================================================

    SaldoConsideradoHoras = Table.AddColumn(
        HoraExtraAcimaLimite,
        "SaldoConsideradoHoras",
        each
            if [SaldoHoras] = null then
                null

            else if [JornadaPrevista] <> true then
                [HoraExtraHoras]

            else
                let
                    SaldoSegundos =
                        Number.Round(
                            [SaldoHoras] * 3600,
                            0
                        )
                in
                    if SaldoSegundos < -600 then
                        [SaldoHoras]
                    else if SaldoSegundos > 600 then
                        [SaldoHoras]
                    else
                        0,
        type number
    ),



	// =========================================================
	// SITUAÇÃO DA JORNADA
	// =========================================================

	SituacaoJornada = Table.AddColumn(
		SaldoConsideradoHoras,
		"SituacaoJornada",
		each
			if
				[TemAjusteContaComoFalta] = true
				and [QuantidadeBatidas] = 0
			then
				"Falta não justificada"

			else if
				[TemAjusteContaComoFalta] = true
				and [QuantidadeBatidas] > 0
			then
				"Atraso por jornada não cumprida"

			else if
				[TemAjusteAbonado] = true
				and [AjusteCobreJornada] = true
				and [QuantidadeBatidas] = 0
			then
				"Coberta por ajuste"

			else if
				[Feriado] = true
				and [QuantidadeBatidas] > 0
			then
				"Trabalhou no feriado"

			else if
				[Feriado] = true
				and [QuantidadeBatidas] = 0
			then
				"Feriado"

			else if
				[JornadaPrevista] <> true
				and [QuantidadeBatidas] > 0
			then
				"Trabalhada sem jornada prevista"

			else if
				[JornadaPrevista] <> true
				and [QuantidadeBatidas] = 0
			then
				"Sem jornada prevista"

			else if
				[QuantidadeBatidas] = [QuantidadeBatidasEsperadas]
			then
				"Completa"

			else if
				[QuantidadeBatidas] = 0
			then
				"Sem registro de ponto"

			else if
				[QuantidadeBatidas] < [QuantidadeBatidasEsperadas]
			then
				"Quantidade de batidas incompleta"

			else if
				[QuantidadeBatidas] > [QuantidadeBatidasEsperadas]
			then
				"Quantidade de batidas acima do esperado"

			else
				"Inconsistente",
		type text
	),


    // =========================================================
    // NECESSIDADE DE ANÁLISE DE 4 BATIDAS
    //
    // Uma jornada totalmente coberta por ajuste não deve
    // ser apontada como falta de 4 batidas.
    // =========================================================

    NecessitaAnalise4Batidas = Table.AddColumn(
        SituacaoJornada,
        "NecessitaAnalise4Batidas",
        each
            if [JornadaPrevista] <> true then
                false

            else if [Feriado] = true then
                false

            else if
                [TemAjusteContaComoFalta] = true
                and [QuantidadeBatidas] = 0
            then
                false

            else if
                [AjusteCobreJornada] = true
                and [QuantidadeBatidas] = 0
            then
                false

            else if
                [Feriado] = true
                and [QuantidadeBatidas] = 0
            then
                false

            else
                [QuantidadeBatidas] <> [QuantidadeBatidasEsperadas],
        type logical
    ),


    // =========================================================
    // INTERVALOS EXTRAS
    // =========================================================

    IntervalosExtras = Table.AddColumn(
        NecessitaAnalise4Batidas,
        "IntervalosExtras",
        each
            if [QuantidadeIntervalos] > 2
            then [QuantidadeIntervalos] - 2
            else 0,
        Int64.Type
    ),

    IntervaloAlmocoHoras = Table.AddColumn(
        IntervalosExtras,
        "IntervaloAlmocoHoras",
        each
            if [QuantidadeBatidas] <> 4 then
                null
            else if [Saida1Real] = null or [Entrada2Real] = null then
                null
            else
                Duration.TotalHours(
                    [Entrada2Real] - [Saida1Real]
                ),
        type number
    ),

    AlmocoAbaixoMinimo = Table.AddColumn(
        IntervaloAlmocoHoras,
        "AlmocoAbaixoMinimo",
        each
            if [IntervaloAlmocoHoras] = null then
                false
            else
                [IntervaloAlmocoHoras] < 1,
        type logical
    ),

    InterjornadaSelecionada =
        Table.SelectColumns(
            Fato_Interjornada,
            {
                "FuncionarioID",
                "DataJornadaAtual",
                "IntervaloInterjornadaHoras"
            }
        ),

    InterjornadaRenomeada =
        Table.RenameColumns(
            InterjornadaSelecionada,
            {
                {"DataJornadaAtual", "Data"}
            }
        ),

    MergeInterjornada =
        Table.NestedJoin(
            AlmocoAbaixoMinimo,
            {
                "FuncionarioID",
                "Data"
            },
            InterjornadaRenomeada,
            {
                "FuncionarioID",
                "Data"
            },
            "Interjornada",
            JoinKind.LeftOuter
        ),

    ExpandirInterjornada =
        Table.ExpandTableColumn(
            MergeInterjornada,
            "Interjornada",
            {
                "IntervaloInterjornadaHoras"
            },
            {
                "IntervaloInterjornadaHoras"
            }
        ),

    // =========================================================
    // FIM DE SEMANA - OCORRÊNCIAS
    //
    // Considera somente sábados e domingos pertencentes
    // a meses que não atenderam à regra de fim de semana.
    // =========================================================

    FimDeSemanaOcorrencias =
        Table.SelectRows(
            Fato_FimDeSemana_Detalhe,
            each
                [AtendeuRegraFimDeSemana] = false
        ),

    FimDeSemanaSelecionado =
        Table.SelectColumns(
            FimDeSemanaOcorrencias,
            {
                "FuncionarioID",
                "Data"
            }
        ),

    FimDeSemanaComFlag =
        Table.AddColumn(
            FimDeSemanaSelecionado,
            "FimDeSemanaOcorrencia",
            each true,
            type logical
        ),

    MergeFimDeSemana =
        Table.NestedJoin(
            ExpandirInterjornada,
            {
                "FuncionarioID",
                "Data"
            },
            FimDeSemanaComFlag,
            {
                "FuncionarioID",
                "Data"
            },
            "FimDeSemana",
            JoinKind.LeftOuter
        ),

    ExpandirFimDeSemana =
        Table.ExpandTableColumn(
            MergeFimDeSemana,
            "FimDeSemana",
            {
                "FimDeSemanaOcorrencia"
            },
            {
                "FimDeSemanaOcorrencia"
            }
        ),

    FimDeSemanaOcorrenciaFinal =
        Table.ReplaceValue(
            ExpandirFimDeSemana,
            null,
            false,
            Replacer.ReplaceValue,
            {"FimDeSemanaOcorrencia"}
        )
in
    FimDeSemanaOcorrenciaFinal,
    #"Colunas Renomeadas" = Table.RenameColumns(Fato_Jornada_Comparacao,{{"FimDeSemanaOcorrencia", "Fim de Semana"}})
in
    #"Colunas Renomeadas"

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: