unit uCalidadHitosService;

interface

uses
  System.SysUtils, System.Classes, System.UITypes, Data.DB, FireDAC.Comp.Client,
  FireDAC.Stan.Param, Vcl.Forms, Vcl.Dialogs;

type
  TTipoOrigenHito = (tohGeneral, tohEnvioRecogida, tohAlbaranCompra, tohLoteFabricacion, tohAlbaranVenta);

  TCalidadHitosService = class
  public
    /// <summary>
    /// Dispara un hito operativo, crea las tareas en ge_calidad_agenda y opcionalmente abre el formulario de captura.
    /// </summary>
    class function DispararHito(
      const ACodigoHito: string;
      const AIdOrigen: Integer;
      const AReferenciaOrigen: string;
      const ATipoOrigen: TTipoOrigenHito = tohGeneral;
      const AAbrirFormulario: Boolean = True
    ): Boolean;

    /// <summary>
    /// Comprueba si todos los documentos obligatorios de un hito para un origen concreto han sido completados.
    /// </summary>
    class function EsHitoCompletado(
      const ACodigoHito: string;
      const AIdOrigen: Integer;
      const ATipoOrigen: TTipoOrigenHito
    ): Boolean;

    /// <summary>
    /// Registra manualmente o sincroniza una tarea en la agenda de calidad para un hito y documento.
    /// </summary>
    class function RegistrarTareaAgenda(
      const AIdHito, AIdDocumento, AIdOrigen: Integer;
      const AReferenciaOrigen: string;
      const ATipoOrigen: TTipoOrigenHito;
      const AMargenHoras: Integer = 24
    ): Integer;

    /// <summary>
    /// Abre el editor de registros de calidad para cumplimentar un documento asociado a la agenda.
    /// </summary>
    class function AbrirRegistroCalidad(
      const AIdDocumento, AIdAgenda: Integer;
      const AIdRegistroCab: Integer = 0
    ): Boolean;
  end;

implementation

uses
  dmg_Main, frm_CalidadRegistroEditor;

class function TCalidadHitosService.RegistrarTareaAgenda(
  const AIdHito, AIdDocumento, AIdOrigen: Integer;
  const AReferenciaOrigen: string;
  const ATipoOrigen: TTipoOrigenHito;
  const AMargenHoras: Integer
): Integer;
var
  Qry: TFDQuery;
  LFechaLim: TDateTime;
  LColOrigen: string;
begin
  Result := 0;
  if (AIdDocumento <= 0) or not dmgMain.dbConn.Connected then Exit;

  LFechaLim := Now + (AMargenHoras / 24.0);

  LColOrigen := '';
  case ATipoOrigen of
    tohEnvioRecogida:   LColOrigen := 'id_envio_recogida';
    tohAlbaranCompra:   LColOrigen := 'id_albaran_compra';
    tohLoteFabricacion: LColOrigen := 'id_lote_fabricacion';
    tohAlbaranVenta:    LColOrigen := 'id_albaran_venta';
  end;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;

    // 1. Comprobar si ya existe una tarea para este documento y origen
    if (LColOrigen <> '') and (AIdOrigen > 0) then
    begin
      Qry.SQL.Text := Format(
        'SELECT id, id_registro_cab, estado FROM ge_calidad_agenda ' +
        'WHERE id_documento = :doc AND %s = :orig LIMIT 1', [LColOrigen]);
      Qry.ParamByName('doc').AsInteger := AIdDocumento;
      Qry.ParamByName('orig').AsInteger := AIdOrigen;
      Qry.Open;
      if not Qry.IsEmpty then
      begin
        Result := Qry.FieldByName('id').AsInteger;
        Exit;
      end;
      Qry.Close;
    end;

    // 2. Insertar nueva tarea en la agenda
    if LColOrigen <> '' then
    begin
      Qry.SQL.Text := Format(
        'INSERT INTO ge_calidad_agenda (' +
        '  id_empresa, id_documento, id_hito, %s, referencia_origen, ' +
        '  fecha_programada, fecha_limite_toma, estado, created_at, id_user_creator ' +
        ') VALUES (' +
        '  :emp, :doc, :hito, :orig, :ref, ' +
        '  CURDATE(), :lim, ''PENDIENTE'', NOW(), :user ' +
        ')', [LColOrigen]);
      Qry.ParamByName('orig').AsInteger := AIdOrigen;
    end
    else
    begin
      Qry.SQL.Text :=
        'INSERT INTO ge_calidad_agenda (' +
        '  id_empresa, id_documento, id_hito, referencia_origen, ' +
        '  fecha_programada, fecha_limite_toma, estado, created_at, id_user_creator ' +
        ') VALUES (' +
        '  :emp, :doc, :hito, :ref, ' +
        '  CURDATE(), :lim, ''PENDIENTE'', NOW(), :user ' +
        ')';
    end;

    Qry.ParamByName('emp').AsInteger := StrToIntDef(dmgMain.CurrentCompanyId, 1);
    Qry.ParamByName('doc').AsInteger := AIdDocumento;
    if AIdHito > 0 then
      Qry.ParamByName('hito').AsInteger := AIdHito
    else
      Qry.ParamByName('hito').Clear;
    Qry.ParamByName('ref').AsString := Copy(AReferenciaOrigen, 1, 100);
    Qry.ParamByName('lim').AsDateTime := LFechaLim;
    Qry.ParamByName('user').AsInteger := StrToIntDef(dmgMain.CurrentUserId, 1);
    Qry.ExecSQL;

    // Recuperar el ID insertado
    Qry.Close;
    Qry.SQL.Text := 'SELECT LAST_INSERT_ID() AS new_id';
    Qry.Open;
    Result := Qry.FieldByName('new_id').AsInteger;
  finally
    Qry.Free;
  end;
end;

class function TCalidadHitosService.DispararHito(
  const ACodigoHito: string;
  const AIdOrigen: Integer;
  const AReferenciaOrigen: string;
  const ATipoOrigen: TTipoOrigenHito;
  const AAbrirFormulario: Boolean
): Boolean;
var
  QryHito, QryDocs: TFDQuery;
  LIdHito, LIdDoc, LMargen: Integer;
  LEsBloqueante: Boolean;
  LIdAgenda: Integer;
  LAllCompleted: Boolean;
begin
  Result := True;
  if (Trim(ACodigoHito) = '') or not dmgMain.dbConn.Connected then Exit;

  QryHito := TFDQuery.Create(nil);
  QryDocs := TFDQuery.Create(nil);
  try
    QryHito.Connection := dmgMain.dbConn;
    QryHito.SQL.Text := 'SELECT id, nombre, es_bloqueante, activo FROM ge_calidad_hitos WHERE codigo = :cod AND activo = 1 LIMIT 1';
    QryHito.ParamByName('cod').AsString := Trim(ACodigoHito);
    QryHito.Open;

    if QryHito.IsEmpty then Exit; // Hito no configurado o inactivo

    LIdHito := QryHito.FieldByName('id').AsInteger;
    LEsBloqueante := QryHito.FieldByName('es_bloqueante').AsBoolean;

    // Obtener documentos asociados al hito
    QryDocs.Connection := dmgMain.dbConn;
    QryDocs.SQL.Text :=
      'SELECT hd.id_documento, hd.es_obligatorio, hd.orden, d.codigo_doc, d.nombre_doc, d.margen_registro ' +
      'FROM ge_calidad_hitos_documentos hd ' +
      'JOIN ge_calidad_documentos d ON hd.id_documento = d.id ' +
      'WHERE hd.id_hito = :hito AND hd.activo = 1 AND d.activo = 1 ' +
      'ORDER BY hd.orden ASC, d.codigo_doc ASC';
    QryDocs.ParamByName('hito').AsInteger := LIdHito;
    QryDocs.Open;

    if QryDocs.IsEmpty then Exit;

    LAllCompleted := True;

    while not QryDocs.Eof do
    begin
      LIdDoc := QryDocs.FieldByName('id_documento').AsInteger;
      LMargen := QryDocs.FieldByName('margen_registro').AsInteger;
      if LMargen <= 0 then LMargen := 24;

      // Registrar tarea en la agenda
      LIdAgenda := RegistrarTareaAgenda(LIdHito, LIdDoc, AIdOrigen, AReferenciaOrigen, ATipoOrigen, LMargen);

      // Si se solicita abrir el formulario interactivo
      if AAbrirFormulario and (LIdAgenda > 0) then
      begin
        if not AbrirRegistroCalidad(LIdDoc, LIdAgenda, 0) then
        begin
          if LEsBloqueante and QryDocs.FieldByName('es_obligatorio').AsBoolean then
            LAllCompleted := False;
        end;
      end;

      QryDocs.Next;
    end;

    Result := LAllCompleted;
  finally
    QryHito.Free;
    QryDocs.Free;
  end;
end;

class function TCalidadHitosService.AbrirRegistroCalidad(
  const AIdDocumento, AIdAgenda: Integer;
  const AIdRegistroCab: Integer
): Boolean;
var
  LFrm: TfrmCalidadRegistroEditor;
begin
  Result := False;
  if AIdDocumento <= 0 then Exit;

  LFrm := TfrmCalidadRegistroEditor.Create(Application);
  try
    LFrm.DocumentoId := AIdDocumento;
    LFrm.RegistroId := AIdRegistroCab;
    if LFrm.ShowModal = mrOk then
    begin
      Result := True;
      // Si se completó y tenemos id_agenda, asegurar estado COMPLETADO en ge_calidad_agenda
      if AIdAgenda > 0 then
      begin
        dmgMain.qryExec.Close;
        dmgMain.qryExec.SQL.Text :=
          'UPDATE ge_calidad_agenda ' +
          'SET estado = ''COMPLETADO'', updated_at = NOW(), id_user_update = :user ' +
          'WHERE id = :id';
        dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
        dmgMain.qryExec.ParamByName('id').AsInteger := AIdAgenda;
        dmgMain.qryExec.ExecSQL;
      end;
    end;
  finally
    LFrm.Free;
  end;
end;

class function TCalidadHitosService.EsHitoCompletado(
  const ACodigoHito: string;
  const AIdOrigen: Integer;
  const ATipoOrigen: TTipoOrigenHito
): Boolean;
var
  Qry: TFDQuery;
  LColOrigen: string;
begin
  Result := True;
  if (AIdOrigen <= 0) or (Trim(ACodigoHito) = '') or not dmgMain.dbConn.Connected then Exit;

  LColOrigen := '';
  case ATipoOrigen of
    tohEnvioRecogida:   LColOrigen := 'a.id_envio_recogida';
    tohAlbaranCompra:   LColOrigen := 'a.id_albaran_compra';
    tohLoteFabricacion: LColOrigen := 'a.id_lote_fabricacion';
    tohAlbaranVenta:    LColOrigen := 'a.id_albaran_venta';
  end;

  if LColOrigen = '' then Exit;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := Format(
      'SELECT COUNT(*) AS total_pendientes ' +
      'FROM ge_calidad_agenda a ' +
      'JOIN ge_calidad_hitos h ON a.id_hito = h.id ' +
      'JOIN ge_calidad_hitos_documentos hd ON (hd.id_hito = h.id AND hd.id_documento = a.id_documento) ' +
      'WHERE h.codigo = :cod AND %s = :orig AND hd.es_obligatorio = 1 AND a.estado <> ''COMPLETADO''', [LColOrigen]);
    Qry.ParamByName('cod').AsString := Trim(ACodigoHito);
    Qry.ParamByName('orig').AsInteger := AIdOrigen;
    Qry.Open;

    Result := (Qry.FieldByName('total_pendientes').AsInteger = 0);
  finally
    Qry.Free;
  end;
end;

end.
