unit frm_Clientes;

interface

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

type
  TfrmClientes = class(TfrmBaseList)
    chkSoloAsegurados: TCheckBox;
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject); override;
    procedure btnNewClick(Sender: TObject); override;
    procedure btnEditClick(Sender: TObject); override;
    procedure btnDeleteClick(Sender: TObject); override;
    procedure dbGridMainCellClick(Column: TColumn);
    procedure dbGridMainKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure dbGridMainKeyPress(Sender: TObject; var Key: Char);
    procedure chkSoloAseguradosClick(Sender: TObject);
  private
    FGrupoIds: TStringList;
    FGrupoNames: TStringList;
    procedure GrupoNombreChange(Sender: TField);
    procedure BooleanGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    procedure BooleanSetText(Sender: TField; const Text: string);
  public
    { Public declarations }
  end;

var
  frmClientes: TfrmClientes;

implementation

uses
  dmg_Main, frm_ClientesEditor, uDbErrorHandler;

{$R *.dfm}

procedure TfrmClientes.FormShow(Sender: TObject);
begin
  AlphaFilterField := 'nombre_filtro';
  StatusFilterField := 'activo';
  StatusValues.Add('Activo');
  StatusValues.Add('Inactivo');
  DateFilterField := 'created_at';

  FGrupoIds := TStringList.Create;
  FGrupoNames := TStringList.Create;

  // Cargar los grupos de clientes de la base de datos
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT id, nombre FROM ge_clientes_grupos WHERE activo = 1 ORDER BY nombre';
  dmgMain.qryExec.Open;
  while not dmgMain.qryExec.Eof do
  begin
    FGrupoNames.Add(dmgMain.qryExec.FieldByName('nombre').AsString);
    FGrupoIds.Add(dmgMain.qryExec.FieldByName('id').AsString);
    dmgMain.qryExec.Next;
  end;
  dmgMain.qryExec.Close;

  inherited;
  frmClientes := Self;
  OnDestroy := FormDestroy;
  pnlSearch.Visible := True;
  lblSearch.Caption := 'Buscar Cliente:';
  
  dbGridMain.Options := dbGridMain.Options + [dgEditing] - [dgRowSelect, dgMultiSelect];
  dbGridMain.OnCellClick := dbGridMainCellClick;
  dbGridMain.OnKeyDown := dbGridMainKeyDown;
  dbGridMain.OnKeyPress := dbGridMainKeyPress;
end;

procedure TfrmClientes.dbGridMainKeyPress(Sender: TObject; var Key: Char);
begin
  // traducir el punto al separador decimal del sistema cuando se introducen importes numéricos (ej: crédito)
  if (Key = '.') and (dbGridMain.SelectedField <> nil) and 
     (dbGridMain.SelectedField.DataType in [ftFloat, ftCurrency, ftBCD, ftFMTBcd, ftSingle, ftExtended]) then
  begin
    Key := FormatSettings.DecimalSeparator;
  end;
end;

procedure TfrmClientes.FormDestroy(Sender: TObject);
begin
  if Assigned(FGrupoIds) then
    FreeAndNil(FGrupoIds);

  if Assigned(FGrupoNames) then
    FreeAndNil(FGrupoNames);

  if frmClientes = Self then
    frmClientes := nil;
end;

procedure TfrmClientes.GrupoNombreChange(Sender: TField);
var
  LIndex: Integer;
begin
  if Sender.DataSet.State in [dsEdit, dsInsert] then
  begin
    if Sender.AsString = '' then
      Sender.DataSet.FieldByName('clientesgrupo_id').Clear
    else
    begin
      LIndex := FGrupoNames.IndexOf(Sender.AsString);
      if LIndex >= 0 then
        Sender.DataSet.FieldByName('clientesgrupo_id').AsString := FGrupoIds[LIndex]
      else
        Sender.DataSet.FieldByName('clientesgrupo_id').Clear;
    end;
  end;
end;

procedure TfrmClientes.BooleanGetText(Sender: TField; var Text: string; DisplayText: Boolean);
begin
  Text := '';
end;

procedure TfrmClientes.BooleanSetText(Sender: TField; const Text: string);
begin
  // No-op para evitar entrada de texto manual y dejar el control exclusivo por click/espacio
end;

procedure TfrmClientes.btnRefreshClick(Sender: TObject);
var
  LSql: string;
begin
  FQryMain.Close;
  LSql := 'SELECT c.id, c.nif, c.nombre_fiscal, c.nombre_comercial, c.clientesgrupo_id, ' +
          'COALESCE(NULLIF(c.nombre_comercial, ''''), c.nombre_fiscal) AS nombre_filtro, ' +
          'c.telefono, c.email, c.poblacion, c.credito, c.bloquear, c.asegurado, c.activo, c.created_at, ' +
          'g.nombre as grupo_nombre, ' +
          'COALESCE((SELECT SUM(a.total) FROM ge_albaranes a WHERE a.cliente_id = c.id AND (a.facturado = 0 OR a.facturado IS NULL)), 0) AS albaranes_pendientes, ' +
          'COALESCE((SELECT SUM(f.total) FROM ge_facturas f WHERE f.cliente_id = c.id AND (f.pagada = 0 OR f.pagada IS NULL)), 0) AS facturas_pendientes ' +
          'FROM ge_clientes c ' +
          'LEFT JOIN ge_clientes_grupos g ON c.clientesgrupo_id = g.id ';
  
  var LWhereClause := '';
  if Trim(edtSearch.Text) <> '' then
  begin
    LWhereClause := '(c.nombre_fiscal LIKE :search ' +
                    'OR c.nombre_comercial LIKE :search ' +
                    'OR c.nif LIKE :search ' +
                    'OR c.telefono LIKE :search ' +
                    'OR c.email LIKE :search ' +
                    'OR g.nombre LIKE :search)';
  end;

  if Assigned(chkSoloAsegurados) and chkSoloAsegurados.Checked then
  begin
    if LWhereClause <> '' then
      LWhereClause := LWhereClause + ' AND COALESCE(c.asegurado, 0) = 1'
    else
      LWhereClause := 'COALESCE(c.asegurado, 0) = 1';
  end;

  if LWhereClause <> '' then
    LSql := LSql + 'WHERE ' + LWhereClause + ' ';

  LSql := LSql + 'ORDER BY c.nombre_comercial, c.nombre_fiscal';
  
  FQryMain.SQL.Text := LSql;
  if Trim(edtSearch.Text) <> '' then
    FQryMain.ParamByName('search').AsString := '%' + Trim(edtSearch.Text) + '%';
    
  FQryMain.Open;
  
  if FQryMain.FindField('credito') <> nil then
    TNumericField(FQryMain.FieldByName('credito')).DisplayFormat := '#,##0.00 €';
  if FQryMain.FindField('albaranes_pendientes') <> nil then
    TNumericField(FQryMain.FieldByName('albaranes_pendientes')).DisplayFormat := '#,##0.00 €';
  if FQryMain.FindField('facturas_pendientes') <> nil then
    TNumericField(FQryMain.FieldByName('facturas_pendientes')).DisplayFormat := '#,##0.00 €';

  // Configurar FireDAC para permitir edición de ge_clientes
  FQryMain.UpdateOptions.UpdateTableName := 'ge_clientes';
  FQryMain.UpdateOptions.KeyFields := 'id';
  FQryMain.UpdateOptions.ReadOnly := False;
  
  if FQryMain.FindField('nombre_filtro') <> nil then
    FQryMain.FieldByName('nombre_filtro').ProviderFlags := [];
  if FQryMain.FindField('albaranes_pendientes') <> nil then
    FQryMain.FieldByName('albaranes_pendientes').ProviderFlags := [];
  if FQryMain.FindField('facturas_pendientes') <> nil then
    FQryMain.FieldByName('facturas_pendientes').ProviderFlags := [];

  if FQryMain.FindField('grupo_nombre') <> nil then
  begin
    FQryMain.FieldByName('grupo_nombre').ProviderFlags := [];
    FQryMain.FieldByName('grupo_nombre').ReadOnly := False;
    FQryMain.FieldByName('grupo_nombre').OnChange := GrupoNombreChange;
  end;

  if FQryMain.FindField('clientesgrupo_id') <> nil then
    FQryMain.FieldByName('clientesgrupo_id').ReadOnly := False;
  
  if FQryMain.FindField('credito') <> nil then
  begin
    TNumericField(FQryMain.FieldByName('credito')).DisplayFormat := '#,##0.00 €';
    FQryMain.FieldByName('credito').ReadOnly := False;
  end;

  if FQryMain.FindField('bloquear') <> nil then
  begin
    FQryMain.FieldByName('bloquear').ReadOnly := False;
    FQryMain.FieldByName('bloquear').OnGetText := BooleanGetText;
    FQryMain.FieldByName('bloquear').OnSetText := BooleanSetText;
  end;

  if FQryMain.FindField('asegurado') <> nil then
  begin
    FQryMain.FieldByName('asegurado').ReadOnly := False;
    FQryMain.FieldByName('asegurado').OnGetText := BooleanGetText;
    FQryMain.FieldByName('asegurado').OnSetText := BooleanSetText;
  end;

  if FQryMain.FindField('activo') <> nil then
  begin
    FQryMain.FieldByName('activo').ReadOnly := False;
    FQryMain.FieldByName('activo').OnGetText := BooleanGetText;
    FQryMain.FieldByName('activo').OnSetText := BooleanSetText;
  end;

  dbGridMain.Columns.Clear;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'nif';
    Title.Caption := 'NIF / CIF';
    Width := 100;
    ReadOnly := True;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'nombre_fiscal';
    Title.Caption := 'Nombre Fiscal';
    Width := 220;
    ReadOnly := True;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'grupo_nombre';
    Title.Caption := 'Grupo de Cliente';
    Width := 140;
    ReadOnly := False;

    PickList.Clear;
    PickList.Add('');
    for var I := 0 to FGrupoNames.Count - 1 do
      PickList.Add(FGrupoNames[I]);
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'telefono';
    Title.Caption := 'Teléfono';
    Width := 110;
    ReadOnly := False;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'email';
    Title.Caption := 'Email';
    Width := 170;
    ReadOnly := False;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'poblacion';
    Title.Caption := 'Población';
    Width := 100;
    ReadOnly := True;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'credito';
    Title.Caption := 'Crédito';
    Width := 90;
    ReadOnly := False;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'albaranes_pendientes';
    Title.Caption := 'Alb. Pends.';
    Width := 90;
    ReadOnly := True;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'facturas_pendientes';
    Title.Caption := 'Fact. Pends.';
    Width := 90;
    ReadOnly := True;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'bloquear';
    Title.Caption := 'Bloquear';
    Width := 65;
    ReadOnly := False;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'asegurado';
    Title.Caption := 'Asegurado';
    Width := 75;
    ReadOnly := False;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'activo';
    Title.Caption := 'Activo';
    Width := 50;
    ReadOnly := False;
  end;
end;

procedure TfrmClientes.btnEditClick(Sender: TObject);
var
  LSelectedId: string;
begin
  if FQryMain.IsEmpty then Exit;
  
  if not Assigned(frmClientesEditor) then
    Application.CreateForm(TfrmClientesEditor, frmClientesEditor);
  
  LSelectedId := FQryMain.FieldByName('id').AsString;
  frmClientesEditor.ParentForm := Self;
  frmClientesEditor.ClienteId := LSelectedId;
  frmClientesEditor.ShowModal;
  btnRefreshClick(nil);
  if LSelectedId <> '' then
    FQryMain.Locate('id', LSelectedId, []);
end;

procedure TfrmClientes.btnNewClick(Sender: TObject);
var
  LSelectedId: string;
begin
  if not Assigned(frmClientesEditor) then
    Application.CreateForm(TfrmClientesEditor, frmClientesEditor);
  
  frmClientesEditor.ParentForm := Self;
  frmClientesEditor.ClienteId := '';
  frmClientesEditor.ShowModal;
  LSelectedId := frmClientesEditor.ClienteId;
  btnRefreshClick(nil);
  if LSelectedId <> '' then
    FQryMain.Locate('id', LSelectedId, []);
end;

procedure TfrmClientes.btnDeleteClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if MessageDlg('¿Está seguro de que desea eliminar este cliente?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    try
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_clientes WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
      dmgMain.qryExec.ExecSQL;
      btnRefreshClick(nil);
    except
      on E: Exception do
        TDbErrorHandler.HandleException(E, 'Error al eliminar el cliente');
    end;
  end;
end;

procedure TfrmClientes.dbGridMainCellClick(Column: TColumn);
var
  LIsActive: Boolean;
begin
  if (Column.Field <> nil) and 
     (SameText(Column.FieldName, 'bloquear') or 
      SameText(Column.FieldName, 'asegurado') or 
      SameText(Column.FieldName, 'activo')) then
  begin
    if not (FQryMain.State in [dsEdit]) then
      FQryMain.Edit;
      
    try
      LIsActive := Column.Field.AsInteger <> 0;
      if LIsActive then
        Column.Field.AsInteger := 0
      else
        Column.Field.AsInteger := 1;
    except
      Column.Field.AsBoolean := not Column.Field.AsBoolean;
    end;
    
    FQryMain.Post;
  end;
end;

procedure TfrmClientes.dbGridMainKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  LIsActive: Boolean;
begin
  if (Key = VK_SPACE) and (dbGridMain.SelectedField <> nil) and 
     (SameText(dbGridMain.SelectedField.FieldName, 'bloquear') or 
      SameText(dbGridMain.SelectedField.FieldName, 'asegurado') or 
      SameText(dbGridMain.SelectedField.FieldName, 'activo')) then
  begin
    if not (FQryMain.State in [dsEdit]) then
      FQryMain.Edit;
      
    try
      LIsActive := dbGridMain.SelectedField.AsInteger <> 0;
      if LIsActive then
        dbGridMain.SelectedField.AsInteger := 0
      else
        dbGridMain.SelectedField.AsInteger := 1;
    except
      dbGridMain.SelectedField.AsBoolean := not dbGridMain.SelectedField.AsBoolean;
    end;
    
    FQryMain.Post;
    Key := 0; // Consumir tecla
  end;
end;

procedure TfrmClientes.chkSoloAseguradosClick(Sender: TObject);
begin
  btnRefreshClick(nil);
end;

end.
