unit frm_RegistrarOperarioZona;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.UITypes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.Buttons,
  Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Param, uAppTheme;

type
  TfrmRegistrarOperarioZona = class(TForm)
    pnlTop: TPanel;
    lblTitle: TLabel;
    pnlClient: TPanel;
    pnlBottom: TPanel;
    btnGuardar: TBitBtn;
    btnCancelar: TBitBtn;
    lblTrabajador: TLabel;
    cbbTrabajador: TComboBox;
    lblZona: TLabel;
    cbbZona: TComboBox;
    lblFecha: TLabel;
    dtpFecha: TDateTimePicker;
    lblHoraInicio: TLabel;
    dtpHoraInicio: TDateTimePicker;
    lblHoraFin: TLabel;
    dtpHoraFin: TDateTimePicker;
    chkEnCurso: TCheckBox;
    lblCosteInfo: TLabel;
    lblObservaciones: TLabel;
    mmoObservaciones: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure chkEnCursoClick(Sender: TObject);
    procedure cbbTrabajadorChange(Sender: TObject);
    procedure dtpHoraChange(Sender: TObject);
    procedure btnGuardarClick(Sender: TObject);
    procedure btnCancelarClick(Sender: TObject);
  private
    FEditId: Int64;
    FIdZonaFija: Integer;
    procedure CargarTrabajadores;
    procedure CargarZonas;
    procedure CargarDatos;
    procedure RecalcularCostePrevio;
  public
    property EditId: Int64 read FEditId write FEditId;
    property IdZonaFija: Integer read FIdZonaFija write FIdZonaFija;
  end;

var
  frmRegistrarOperarioZona: TfrmRegistrarOperarioZona;

implementation

uses
  dmg_Main, uDbErrorHandler;

{$R *.dfm}

procedure TfrmRegistrarOperarioZona.FormCreate(Sender: TObject);
begin
  FEditId := 0;
  FIdZonaFija := 0;
end;

procedure TfrmRegistrarOperarioZona.FormShow(Sender: TObject);
begin
  TAppTheme.ApplyToForm(Self);

  CargarTrabajadores;
  CargarZonas;

  if FEditId > 0 then
  begin
    lblTitle.Caption := '✏️ Editar Imputación de Operario en Zona #' + IntToStr(FEditId);
    CargarDatos;
  end
  else
  begin
    lblTitle.Caption := '⏱️ Registrar Operario en Zona';
    dtpFecha.Date := Date;
    dtpHoraInicio.Time := Time;
    dtpHoraFin.Time := Time;
    chkEnCurso.Checked := True;
    dtpHoraFin.Enabled := False;
    mmoObservaciones.Clear;
  end;

  RecalcularCostePrevio;
end;

procedure TfrmRegistrarOperarioZona.chkEnCursoClick(Sender: TObject);
begin
  dtpHoraFin.Enabled := not chkEnCurso.Checked;
  RecalcularCostePrevio;
end;

procedure TfrmRegistrarOperarioZona.cbbTrabajadorChange(Sender: TObject);
begin
  RecalcularCostePrevio;
end;

procedure TfrmRegistrarOperarioZona.dtpHoraChange(Sender: TObject);
begin
  RecalcularCostePrevio;
end;

procedure TfrmRegistrarOperarioZona.CargarTrabajadores;
var
  Qry: TFDQuery;
begin
  cbbTrabajador.Items.Clear;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text :=
      'SELECT id, CONCAT(nombre, '' '', COALESCE(apellidos, ''''), '' ('', COALESCE(coste_hora, 0), '' €/h)'') AS trab_desc ' +
      'FROM ge_trabajadores ' +
      'WHERE id_empresa = :emp AND activo = 1 ' +
      'ORDER BY nombre ASC, apellidos ASC';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbbTrabajador.Items.AddObject(Qry.FieldByName('trab_desc').AsString, TObject(IntPtr(Qry.FieldByName('id').AsInteger)));
      Qry.Next;
    end;

    if cbbTrabajador.Items.Count > 0 then
      cbbTrabajador.ItemIndex := 0;
  finally
    Qry.Free;
  end;
end;

procedure TfrmRegistrarOperarioZona.CargarZonas;
var
  Qry: TFDQuery;
begin
  cbbZona.Items.Clear;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text :=
      'SELECT z.id, CONCAT(CASE WHEN z.tipo_centro = ''OBRADOR'' THEN ''🏭 '' ELSE ''📦 '' END, z.codigo, '' - '', z.nombre) AS zona_desc ' +
      'FROM ge_centros_zonas z ' +
      'WHERE z.id_empresa = :emp AND z.activo = 1 ' +
      'ORDER BY z.tipo_centro ASC, z.orden ASC, z.nombre ASC';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbbZona.Items.AddObject(Qry.FieldByName('zona_desc').AsString, TObject(IntPtr(Qry.FieldByName('id').AsInteger)));
      Qry.Next;
    end;

    if cbbZona.Items.Count > 0 then
    begin
      cbbZona.ItemIndex := 0;
      if FIdZonaFija > 0 then
      begin
        for var i := 0 to cbbZona.Items.Count - 1 do
        begin
          if Integer(IntPtr(cbbZona.Items.Objects[i])) = FIdZonaFija then
          begin
            cbbZona.ItemIndex := i;
            Break;
          end;
        end;
      end;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmRegistrarOperarioZona.CargarDatos;
var
  Qry: TFDQuery;
begin
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT * FROM ge_control_tiempos_zonas WHERE id = :id';
    Qry.ParamByName('id').AsLargeInt := FEditId;
    Qry.Open;

    if not Qry.IsEmpty then
    begin
      var LTrabId := Qry.FieldByName('id_trabajador').AsInteger;
      for var i := 0 to cbbTrabajador.Items.Count - 1 do
      begin
        if Integer(IntPtr(cbbTrabajador.Items.Objects[i])) = LTrabId then
        begin
          cbbTrabajador.ItemIndex := i;
          Break;
        end;
      end;

      var LZonaId := Qry.FieldByName('id_zona').AsInteger;
      for var i := 0 to cbbZona.Items.Count - 1 do
      begin
        if Integer(IntPtr(cbbZona.Items.Objects[i])) = LZonaId then
        begin
          cbbZona.ItemIndex := i;
          Break;
        end;
      end;

      dtpFecha.Date := Qry.FieldByName('fecha').AsDateTime;
      dtpHoraInicio.Time := StrToTimeDef(Qry.FieldByName('hora_inicio').AsString, Time);
      if Qry.FieldByName('hora_fin').IsNull or (Trim(Qry.FieldByName('hora_fin').AsString) = '') then
      begin
        chkEnCurso.Checked := True;
        dtpHoraFin.Enabled := False;
      end
      else
      begin
        chkEnCurso.Checked := False;
        dtpHoraFin.Enabled := True;
        dtpHoraFin.Time := StrToTimeDef(Qry.FieldByName('hora_fin').AsString, Time);
      end;

      mmoObservaciones.Text := Qry.FieldByName('observaciones').AsString;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmRegistrarOperarioZona.RecalcularCostePrevio;
var
  LTrabId: Integer;
  LCosteHora: Double;
  Qry: TFDQuery;
  LMinutos: Integer;
  LHorasDec: Double;
begin
  if cbbTrabajador.ItemIndex < 0 then Exit;
  LTrabId := Integer(IntPtr(cbbTrabajador.Items.Objects[cbbTrabajador.ItemIndex]));
  LCosteHora := 0.0;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT COALESCE(coste_hora, 0) AS coste FROM ge_trabajadores WHERE id = :id';
    Qry.ParamByName('id').AsInteger := LTrabId;
    Qry.Open;
    if not Qry.IsEmpty then
      LCosteHora := Qry.FieldByName('coste').AsFloat;
  finally
    Qry.Free;
  end;

  if chkEnCurso.Checked then
  begin
    lblCosteInfo.Caption := Format('Coste/Hora: %.2f €/h  |  Estado: Actividad EN CURSO (Abierta)', [LCosteHora]);
  end
  else
  begin
    LMinutos := Round((dtpHoraFin.Time - dtpHoraInicio.Time) * 1440);
    if LMinutos < 0 then LMinutos := 0;
    LHorasDec := LMinutos / 60.0;
    lblCosteInfo.Caption := Format('Duración: %d min (%.2f h)  |  Coste Repercutido: %.2f € (a %.2f €/h)', [LMinutos, LHorasDec, LHorasDec * LCosteHora, LCosteHora]);
  end;
end;

procedure TfrmRegistrarOperarioZona.btnGuardarClick(Sender: TObject);
var
  LTrabId, LZonaId: Integer;
  LCosteHora, LHorasDec, LCosteTot: Double;
  LMinutos: Integer;
  Qry: TFDQuery;
  LHoraIniStr, LHoraFinStr: string;
begin
  if cbbTrabajador.ItemIndex < 0 then
  begin
    MessageDlg('Debe seleccionar el trabajador u operario.', mtWarning, [mbOK], 0);
    cbbTrabajador.SetFocus;
    Exit;
  end;

  if cbbZona.ItemIndex < 0 then
  begin
    MessageDlg('Debe seleccionar la zona operativa.', mtWarning, [mbOK], 0);
    cbbZona.SetFocus;
    Exit;
  end;

  LTrabId := Integer(IntPtr(cbbTrabajador.Items.Objects[cbbTrabajador.ItemIndex]));
  LZonaId := Integer(IntPtr(cbbZona.Items.Objects[cbbZona.ItemIndex]));
  LCosteHora := 0.0;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT COALESCE(coste_hora, 0) AS coste FROM ge_trabajadores WHERE id = :id';
    Qry.ParamByName('id').AsInteger := LTrabId;
    Qry.Open;
    if not Qry.IsEmpty then
      LCosteHora := Qry.FieldByName('coste').AsFloat;
  finally
    Qry.Free;
  end;

  LHoraIniStr := FormatDateTime('hh:nn:ss', dtpHoraInicio.Time);
  if chkEnCurso.Checked then
  begin
    LMinutos := 0;
    LHorasDec := 0.0;
    LCosteTot := 0.0;
    LHoraFinStr := '';
  end
  else
  begin
    LHoraFinStr := FormatDateTime('hh:nn:ss', dtpHoraFin.Time);
    LMinutos := Round((dtpHoraFin.Time - dtpHoraInicio.Time) * 1440);
    if LMinutos < 0 then LMinutos := 0;
    LHorasDec := LMinutos / 60.0;
    LCosteTot := LHorasDec * LCosteHora;
  end;

  try
    dmgMain.qryExec.Close;
    if FEditId > 0 then
    begin
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_control_tiempos_zonas SET ' +
        '  id_empresa = :emp, fecha = :fec, id_trabajador = :trab, id_zona = :zona, ' +
        '  hora_inicio = :h_ini, hora_fin = :h_fin, minutos_totales = :mins, ' +
        '  horas_decimal = :h_dec, coste_hora_aplicado = :chora, coste_total = :ctot, ' +
        '  observaciones = :obs, id_user_update = :user ' +
        'WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsLargeInt := FEditId;
    end
    else
    begin
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_control_tiempos_zonas (id_empresa, fecha, id_trabajador, id_zona, hora_inicio, hora_fin, minutos_totales, horas_decimal, coste_hora_aplicado, coste_total, observaciones, id_user_creator, id_user_update) ' +
        'VALUES (:emp, :fec, :trab, :zona, :h_ini, :h_fin, :mins, :h_dec, :chora, :ctot, :obs, :user, :user)';
    end;

    dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    dmgMain.qryExec.ParamByName('fec').AsDate := dtpFecha.Date;
    dmgMain.qryExec.ParamByName('trab').AsInteger := LTrabId;
    dmgMain.qryExec.ParamByName('zona').AsInteger := LZonaId;
    dmgMain.qryExec.ParamByName('h_ini').AsString := LHoraIniStr;
    if LHoraFinStr <> '' then
      dmgMain.qryExec.ParamByName('h_fin').AsString := LHoraFinStr
    else
    begin
      dmgMain.qryExec.ParamByName('h_fin').Clear;
      dmgMain.qryExec.ParamByName('h_fin').DataType := ftString;
    end;
    dmgMain.qryExec.ParamByName('mins').AsInteger := LMinutos;
    dmgMain.qryExec.ParamByName('h_dec').AsFloat := LHorasDec;
    dmgMain.qryExec.ParamByName('chora').AsFloat := LCosteHora;
    dmgMain.qryExec.ParamByName('ctot').AsFloat := LCosteTot;
    dmgMain.qryExec.ParamByName('obs').AsString := Trim(mmoObservaciones.Text);
    dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
    dmgMain.qryExec.ExecSQL;

    ModalResult := mrOk;
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, 'Error al guardar la imputación de tiempos');
  end;
end;

procedure TfrmRegistrarOperarioZona.btnCancelarClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

end.
