unit frm_MateriasPrimasEditor;

interface

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

type
  TfrmMateriasPrimasEditor = class(TfrmBase)
    lblCodigo: TLabel;
    edtCodigo: TEdit;
    lblDescripcion: TLabel;
    edtDescripcion: TEdit;
    lblFamilia: TLabel;
    cbFamilia: TComboBox;
    lblCodigoBarras: TLabel;
    edtCodigoBarras: TEdit;
    chkActivo: TCheckBox;
    chkInventariable: TCheckBox;
    lblStockMinimo: TLabel;
    edtStockMinimo: TEdit;
    lblStockActual: TLabel;
    edtStockActual: TEdit;
    lblObservaciones: TLabel;
    memObservaciones: TMemo;
    lblPeso: TLabel;
    edtPeso: TEdit;
    lblDimensiones: TLabel;
    edtDimensiones: TEdit;
    lblUltimoPrecio: TLabel;
    edtUltimoPrecio: TEdit;
    lblValorStock: TLabel;
    edtValorStock: TEdit;
    pnlImage: TPanel;
    imgMateriaPrima: TImage;
    btnSubirImagen: TButton;
    btnEliminarImagen: TButton;

    // Controles para la pestaña de Artículos de Proveedores
    FTsProveedores: TTabSheet;
    FDbgProveedores: TDBGrid;
    FQryProveedores: TFDQuery;
    FDsProveedores: TDataSource;

    // Controles para la pestaña de Líneas de Albaranes de Proveedores
    FTsAlbaranesLineas: TTabSheet;
    FDbgAlbaranesLineas: TDBGrid;
    FQryAlbaranesLineas: TFDQuery;
    FDsAlbaranesLineas: TDataSource;

    // Controles para la pestaña de Documentación
    FTsDocumentos: TTabSheet;
    FDbgDocumentos: TDBGrid;
    FPnlDocActions: TPanel;
    FBtnSubirDoc: TButton;
    FBtnVerDoc: TButton;
    FBtnEliminarDoc: TButton;
    FQryDocumentos: TFDQuery;
    FDsDocumentos: TDataSource;
    FOdDocumento: TOpenDialog;

    procedure FormShow(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnSalirClick(Sender: TObject);
    procedure btnEliminarClick(Sender: TObject);
    procedure cbFamiliaChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnSubirImagenClick(Sender: TObject);
    procedure btnEliminarImagenClick(Sender: TObject);
    procedure btnSubirDocClick(Sender: TObject);
    procedure btnVerDocClick(Sender: TObject);
    procedure btnEliminarDocClick(Sender: TObject);
    procedure dbgDocumentosDblClick(Sender: TObject);
    procedure dbgDocumentosTitleClick(Column: TColumn);
    procedure dbgProveedoresTitleClick(Column: TColumn);
    procedure dbgAlbaranesLineasTitleClick(Column: TColumn);
  protected
    procedure DoShow; override;
    procedure DoAnadir; override;
    procedure DoModificar; override;
    procedure DoPrimero; override;
    procedure DoAnterior; override;
    procedure DoSiguiente; override;
    procedure DoUltimo; override;
  private
    FMateriaPrimaId: string;
    FFamiliaIds: TStringList;
    FParentForm: TForm;
    FInitialDescripcion: string;
    FInitialCodigoBarras: string;
    FLastSortColAlbaranes: string;
    FLastSortDirAlbaranes: string;

    procedure InitArticulosProveedoresTab;
    procedure InitAlbaranesLineasTab;
    procedure InitDocumentosTab;
    procedure LoadData;
    procedure LoadLookups;
    procedure LoadMateriaPrimaImage;
    procedure LoadArticulosProveedores;
    procedure ConfigurarColumnasGridProveedores;
    procedure LoadAlbaranesLineas;
    procedure ConfigurarColumnasGridAlbaranesLineas;
    procedure UltPrecioGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    procedure CantidadBultoGetText(Sender: TField; var Text: string; DisplayText: Boolean);

    procedure LoadDocuments;
    procedure ConfigurarColumnasGridDocumentos;
  public
    property MateriaPrimaId: string read FMateriaPrimaId write FMateriaPrimaId;
    property ParentForm: TForm read FParentForm write FParentForm;
    property InitialDescripcion: string read FInitialDescripcion write FInitialDescripcion;
    property InitialCodigoBarras: string read FInitialCodigoBarras write FInitialCodigoBarras;
  end;

var
  frmMateriasPrimasEditor: TfrmMateriasPrimasEditor;

implementation

uses
  dmg_Main, uDbErrorHandler, frm_MateriasPrimas, Vcl.Imaging.jpeg, Vcl.Imaging.pngimage,
  Winapi.ShellAPI, System.IOUtils;

{$R *.dfm}

procedure TfrmMateriasPrimasEditor.FormShow(Sender: TObject);
begin
  // Handled in DoShow
end;

procedure TfrmMateriasPrimasEditor.DoShow;
begin
  inherited DoShow;
  if not Assigned(FFamiliaIds) then
    FFamiliaIds := TStringList.Create
  else
    FFamiliaIds.Clear;

  // Aplicar tema estándar
  TAppTheme.ApplyToForm(Self);

  edtCodigo.ReadOnly := True;
  edtCodigo.Color := clBtnFace;
  edtStockActual.ReadOnly := True;
  edtStockActual.Color := clBtnFace;
  edtUltimoPrecio.ReadOnly := True;
  edtUltimoPrecio.Color := clBtnFace;
  edtValorStock.ReadOnly := True;
  edtValorStock.Color := clBtnFace;

  // Vincular eventos a la barra superior heredada
  btnGuardar.OnClick := btnSaveClick;
  btnSalir.OnClick := btnSalirClick;

  // Vincular eventos de cambio interactivos
  cbFamilia.OnChange := cbFamiliaChange;
  chkActivo.OnClick := cbFamiliaChange;
  chkInventariable.OnClick := cbFamiliaChange;
  btnSubirImagen.OnClick := btnSubirImagenClick;
  btnEliminarImagen.OnClick := btnEliminarImagenClick;

  // Ocultar componentes/paneles heredados innecesarios para mantenimiento simple
  pgcDetails.Visible := True;
  TsListado.TabVisible := False;
  tsDatosEnvio.Caption := 'Datos Generales';
  pnlSideToolbar.Visible := False;
  dbgItems.Visible := False;
  pnlFooter.Visible := False;

  // Inicializar pestañas de Artículos de Proveedores, Líneas de Albaranes y Documentación
  InitArticulosProveedoresTab;
  InitAlbaranesLineasTab;
  InitDocumentosTab;
  pgcDetails.ActivePage := tsDatosEnvio;

  // Cargar maestros (Familias)
  LoadLookups;

  // Cargar datos
  if FMateriaPrimaId <> '' then
    LoadData
  else
  begin
    edtCodigo.Clear;
    edtDescripcion.Text := FInitialDescripcion;
    edtCodigoBarras.Text := FInitialCodigoBarras;
    cbFamilia.ItemIndex := -1;
    chkActivo.Checked := True;
    chkInventariable.Checked := True;
    edtStockMinimo.Clear;
    edtStockActual.Text := '0';
    edtUltimoPrecio.Text := '---';
    edtValorStock.Text := '0,00 €';
    memObservaciones.Clear;
    edtPeso.Clear;
    edtDimensiones.Clear;
    imgMateriaPrima.Picture := nil;
    cbFamiliaChange(nil);
    LoadArticulosProveedores;
    LoadAlbaranesLineas;
    LoadDocuments;
  end;
end;

procedure TfrmMateriasPrimasEditor.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FFamiliaIds);
end;

procedure TfrmMateriasPrimasEditor.InitArticulosProveedoresTab;
begin
  if Assigned(FTsProveedores) then Exit;

  FTsProveedores := TTabSheet.Create(Self);
  FTsProveedores.PageControl := pgcDetails;
  FTsProveedores.Caption := 'Artículos de Proveedores';

  FQryProveedores := TFDQuery.Create(Self);
  FQryProveedores.Connection := dmgMain.dbConn;

  FDsProveedores := TDataSource.Create(Self);
  FDsProveedores.DataSet := FQryProveedores;

  FDbgProveedores := TDBGrid.Create(Self);
  FDbgProveedores.Parent := FTsProveedores;
  FDbgProveedores.Align := alClient;
  FDbgProveedores.DataSource := FDsProveedores;
  FDbgProveedores.ReadOnly := True;
  FDbgProveedores.Options := [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick];
  FDbgProveedores.OnTitleClick := dbgProveedoresTitleClick;
end;

procedure TfrmMateriasPrimasEditor.InitDocumentosTab;
begin
  if Assigned(FTsDocumentos) then Exit;

  FTsDocumentos := TTabSheet.Create(Self);
  FTsDocumentos.PageControl := pgcDetails;
  FTsDocumentos.Caption := 'Documentación';

  FOdDocumento := TOpenDialog.Create(Self);
  FOdDocumento.Filter := 'Todos los archivos (*.*)|*.*|Documentos PDF (*.pdf)|*.pdf|Imágenes (*.jpg;*.jpeg;*.png)|*.jpg;*.jpeg;*.png';

  FPnlDocActions := TPanel.Create(Self);
  FPnlDocActions.Parent := FTsDocumentos;
  FPnlDocActions.Align := alTop;
  FPnlDocActions.Height := 40;
  FPnlDocActions.BevelOuter := bvNone;

  FBtnSubirDoc := TButton.Create(Self);
  FBtnSubirDoc.Parent := FPnlDocActions;
  FBtnSubirDoc.Left := 8;
  FBtnSubirDoc.Top := 8;
  FBtnSubirDoc.Width := 110;
  FBtnSubirDoc.Caption := 'Subir Doc';
  FBtnSubirDoc.OnClick := btnSubirDocClick;

  FBtnVerDoc := TButton.Create(Self);
  FBtnVerDoc.Parent := FPnlDocActions;
  FBtnVerDoc.Left := 126;
  FBtnVerDoc.Top := 8;
  FBtnVerDoc.Width := 110;
  FBtnVerDoc.Caption := 'Ver Doc';
  FBtnVerDoc.OnClick := btnVerDocClick;

  FBtnEliminarDoc := TButton.Create(Self);
  FBtnEliminarDoc.Parent := FPnlDocActions;
  FBtnEliminarDoc.Left := 244;
  FBtnEliminarDoc.Top := 8;
  FBtnEliminarDoc.Width := 110;
  FBtnEliminarDoc.Caption := 'Eliminar Doc';
  FBtnEliminarDoc.OnClick := btnEliminarDocClick;

  FQryDocumentos := TFDQuery.Create(Self);
  FQryDocumentos.Connection := dmgMain.dbConn;

  FDsDocumentos := TDataSource.Create(Self);
  FDsDocumentos.DataSet := FQryDocumentos;

  FDbgDocumentos := TDBGrid.Create(Self);
  FDbgDocumentos.Parent := FTsDocumentos;
  FDbgDocumentos.Align := alClient;
  FDbgDocumentos.DataSource := FDsDocumentos;
  FDbgDocumentos.ReadOnly := True;
  FDbgDocumentos.Options := [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick];
  FDbgDocumentos.OnDblClick := dbgDocumentosDblClick;
  FDbgDocumentos.OnTitleClick := dbgDocumentosTitleClick;
end;

procedure TfrmMateriasPrimasEditor.LoadLookups;
begin
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT id, descripcion FROM ge_familias ORDER BY descripcion';
  dmgMain.qryExec.Open;
  
  cbFamilia.Items.Clear;
  FFamiliaIds.Clear;
  
  while not dmgMain.qryExec.Eof do
  begin
    cbFamilia.Items.Add(dmgMain.qryExec.FieldByName('descripcion').AsString);
    FFamiliaIds.Add(dmgMain.qryExec.FieldByName('id').AsString);
    dmgMain.qryExec.Next;
  end;
  dmgMain.qryExec.Close;
end;

procedure TfrmMateriasPrimasEditor.LoadData;
begin
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT * FROM ge_materias_primas WHERE id = :id';
  dmgMain.qryExec.ParamByName('id').AsString := FMateriaPrimaId;
  dmgMain.qryExec.Open;
  
  if not dmgMain.qryExec.IsEmpty then
  begin
    edtCodigo.Text := dmgMain.qryExec.FieldByName('id').AsString;
    edtDescripcion.Text := dmgMain.qryExec.FieldByName('descripcion').AsString;
    edtCodigoBarras.Text := dmgMain.qryExec.FieldByName('codigo_barras').AsString;
    chkActivo.Checked := dmgMain.qryExec.FieldByName('activo').AsInteger <> 0;
    chkInventariable.Checked := dmgMain.qryExec.FieldByName('inventariable').AsInteger <> 0;
    memObservaciones.Text := dmgMain.qryExec.FieldByName('observaciones').AsString;
    
    if not dmgMain.qryExec.FieldByName('stock_minimo').IsNull then
      edtStockMinimo.Text := FloatToStr(dmgMain.qryExec.FieldByName('stock_minimo').AsFloat)
    else
      edtStockMinimo.Clear;

    if not dmgMain.qryExec.FieldByName('peso').IsNull then
      edtPeso.Text := FloatToStr(dmgMain.qryExec.FieldByName('peso').AsFloat)
    else
      edtPeso.Clear;

    edtDimensiones.Text := dmgMain.qryExec.FieldByName('dimensiones').AsString;

    // Seleccionar Familia en Combo
    if not dmgMain.qryExec.FieldByName('familia_id').IsNull then
      cbFamilia.ItemIndex := FFamiliaIds.IndexOf(dmgMain.qryExec.FieldByName('familia_id').AsString)
    else
      cbFamilia.ItemIndex := -1;
      
    cbFamiliaChange(nil);
  end;
  dmgMain.qryExec.Close;

  // Cargar imagen
  LoadMateriaPrimaImage;

  // Calcular y cargar stock actual, último precio y valor del stock
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 
    'SELECT ' +
    '  COALESCE(SUM(stock_restante), 0.0) AS stock_actual, ' +
    '  (SELECT precio_compra FROM ge_entradas_materias_primas WHERE id_materia_prima = :mp ORDER BY fecha_entrada DESC, id DESC LIMIT 1) AS ultimo_precio_compra, ' +
    '  COALESCE(SUM(stock_restante * precio_compra), 0.0) AS valor_stock ' +
    'FROM ge_entradas_materias_primas ' +
    'WHERE id_materia_prima = :mp';
  dmgMain.qryExec.ParamByName('mp').AsString := FMateriaPrimaId;
  dmgMain.qryExec.Open;
  
  if not dmgMain.qryExec.IsEmpty then
  begin
    edtStockActual.Text := FormatFloat('#,##0.0000', dmgMain.qryExec.FieldByName('stock_actual').AsFloat);
    
    if not dmgMain.qryExec.FieldByName('ultimo_precio_compra').IsNull then
      edtUltimoPrecio.Text := FormatFloat('#,##0.0000', dmgMain.qryExec.FieldByName('ultimo_precio_compra').AsFloat) + ' €'
    else
      edtUltimoPrecio.Text := '---';
      
    edtValorStock.Text := FormatFloat('#,##0.00', dmgMain.qryExec.FieldByName('valor_stock').AsFloat) + ' €';
  end
  else
  begin
    edtStockActual.Text := '0';
    edtUltimoPrecio.Text := '---';
    edtValorStock.Text := '0,00 €';
  end;
  dmgMain.qryExec.Close;

  // Cargar artículos de proveedores en la pestaña dedicada
  LoadArticulosProveedores;

  // Cargar líneas de albaranes de proveedores en la pestaña dedicada
  LoadAlbaranesLineas;

  // Cargar documentos adjuntos en la pestaña dedicada
  LoadDocuments;
end;

procedure TfrmMateriasPrimasEditor.btnSaveClick(Sender: TObject);
var
  LIsNew: Boolean;
begin
  if Trim(edtDescripcion.Text) = '' then
  begin
    ShowMessage('La descripción de la materia prima es obligatoria.');
    Exit;
  end;

  LIsNew := (FMateriaPrimaId = '');

  dmgMain.qryExec.Close;
  if LIsNew then
  begin
    dmgMain.qryExec.SQL.Text :=
      'INSERT INTO ge_materias_primas (familia_id, descripcion, codigo_barras, ' +
      '  activo, observaciones, inventariable, stock_minimo, peso, dimensiones, id_user_creator, id_user_update) ' +
      'VALUES (:fam, :desc, :bar, :act, :obs, :inventariable, :stock_min, :peso, :dims, :user, :user)';
  end
  else
  begin
    dmgMain.qryExec.SQL.Text :=
      'UPDATE ge_materias_primas SET familia_id = :fam, descripcion = :desc, ' +
      '  codigo_barras = :bar, activo = :act, observaciones = :obs, ' +
      '  inventariable = :inventariable, stock_minimo = :stock_min, peso = :peso, ' +
      '  dimensiones = :dims, id_user_update = :user ' +
      'WHERE id = :id';
    dmgMain.qryExec.ParamByName('id').AsString := FMateriaPrimaId;
  end;

  // Asignar parámetros con seguridad de tipo para nulos
  if cbFamilia.ItemIndex <> -1 then
    dmgMain.qryExec.ParamByName('fam').AsString := FFamiliaIds[cbFamilia.ItemIndex]
  else
  begin
    dmgMain.qryExec.ParamByName('fam').Clear;
    dmgMain.qryExec.ParamByName('fam').DataType := ftString;
  end;

  dmgMain.qryExec.ParamByName('desc').AsString := edtDescripcion.Text;
  
  if Trim(edtCodigoBarras.Text) <> '' then
    dmgMain.qryExec.ParamByName('bar').AsString := edtCodigoBarras.Text
  else
  begin
    dmgMain.qryExec.ParamByName('bar').Clear;
    dmgMain.qryExec.ParamByName('bar').DataType := ftString;
  end;
    
  dmgMain.qryExec.ParamByName('act').AsBoolean := chkActivo.Checked;
  dmgMain.qryExec.ParamByName('obs').AsString := memObservaciones.Text;
  
  if chkInventariable.Checked then
    dmgMain.qryExec.ParamByName('inventariable').AsInteger := 1
  else
    dmgMain.qryExec.ParamByName('inventariable').AsInteger := 0;
    
  if Trim(edtStockMinimo.Text) <> '' then
    dmgMain.qryExec.ParamByName('stock_min').AsFloat := StrToFloatDef(edtStockMinimo.Text, 0)
  else
  begin
    dmgMain.qryExec.ParamByName('stock_min').Clear;
    dmgMain.qryExec.ParamByName('stock_min').DataType := ftFloat;
  end;

  if Trim(edtPeso.Text) <> '' then
    dmgMain.qryExec.ParamByName('peso').AsFloat := StrToFloatDef(edtPeso.Text, 0)
  else
  begin
    dmgMain.qryExec.ParamByName('peso').Clear;
    dmgMain.qryExec.ParamByName('peso').DataType := ftFloat;
  end;

  if Trim(edtDimensiones.Text) <> '' then
    dmgMain.qryExec.ParamByName('dims').AsString := edtDimensiones.Text
  else
  begin
    dmgMain.qryExec.ParamByName('dims').Clear;
    dmgMain.qryExec.ParamByName('dims').DataType := ftString;
  end;
    
  dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;

  try
    dmgMain.qryExec.ExecSQL;
    
    if LIsNew then
    begin
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'SELECT LAST_INSERT_ID()';
      dmgMain.qryExec.Open;
      FMateriaPrimaId := dmgMain.qryExec.Fields[0].AsString;
      dmgMain.qryExec.Close;
    end;

    ResetChangeTracking;
    
    // Refrescar listado del padre inmediatamente y posicionarse en el registro modificado
    if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
    begin
      TfrmMateriasPrimas(FParentForm).btnRefreshClick(nil);
      TfrmMateriasPrimas(FParentForm).QryMain.Locate('id', FMateriaPrimaId, []);
    end;
    ShowMessage('Materia prima guardada correctamente.');
  except
    on E: Exception do
      TDbErrorHandler.HandleException(E, dmgMain.qryExec.SQL.Text);
  end;
end;

procedure TfrmMateriasPrimasEditor.btnEliminarClick(Sender: TObject);
var
  LNextId: string;
  LParent: TfrmMateriasPrimas;
begin
  if FMateriaPrimaId = '' then
  begin
    ShowMessage('No hay ningún registro guardado para eliminar.');
    Exit;
  end;

  if MessageDlg('¿Está seguro de que desea eliminar esta materia prima?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    LNextId := '';
    LParent := nil;
    if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
      LParent := TfrmMateriasPrimas(FParentForm);

    if Assigned(LParent) then
    begin
      // Intentar buscar el siguiente registro en el listado antes de borrar
      LParent.QryMain.Next;
      if not LParent.QryMain.Eof then
      begin
        LNextId := LParent.QryMain.FieldByName('id').AsString;
        LParent.QryMain.Prior; // Regresar a la posición actual
      end
      else
      begin
        // Si no hay siguiente, retroceder al anterior
        LParent.QryMain.Prior; // Volver al actual
        LParent.QryMain.Prior; // Ir al anterior
        if not LParent.QryMain.Bof then
        begin
          LNextId := LParent.QryMain.FieldByName('id').AsString;
          LParent.QryMain.Next; // Volver a la posición del actual
        end
        else
        begin
          LParent.QryMain.Next; // Volver a la posición del actual
        end;
      end;
    end;

    try
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_materias_primas WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsString := FMateriaPrimaId;
      dmgMain.qryExec.ExecSQL;
      
      // Refrescar listado del padre inmediatamente
      if Assigned(LParent) then
      begin
        LParent.btnRefreshClick(nil);
        if LNextId <> '' then
        begin
          LParent.QryMain.Locate('id', LNextId, []);
          FMateriaPrimaId := LNextId;
          LoadData;
        end;
      end;
      
      // Si no queda ningún registro, cerramos el formulario de forma limpia
      if LNextId = '' then
      begin
        ShowMessage('Se ha eliminado el único registro disponible.');
        ModalResult := mrOk;
      end;
    except
      on E: Exception do
        ShowMessage('Error al eliminar la materia prima: ' + E.Message);
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.cbFamiliaChange(Sender: TObject);
var
  LIsEnvases: Boolean;
begin
  LIsEnvases := (cbFamilia.ItemIndex <> -1) and SameText(cbFamilia.Text, 'ENVASES');
  
  lblPeso.Visible := LIsEnvases;
  edtPeso.Visible := LIsEnvases;
  lblDimensiones.Visible := LIsEnvases;
  edtDimensiones.Visible := LIsEnvases;
  
  UpdateActionButtonsState;
end;

procedure TfrmMateriasPrimasEditor.btnSalirClick(Sender: TObject);
begin
  ModalResult := mrCancel;
  Close;
end;

procedure TfrmMateriasPrimasEditor.DoAnadir;
begin
  FMateriaPrimaId := '';
  edtCodigo.Text := 'AUTO';
  edtDescripcion.Clear;
  edtCodigoBarras.Clear;
  cbFamilia.ItemIndex := -1;
  chkActivo.Checked := True;
  chkInventariable.Checked := True;
  edtPeso.Clear;
  edtDimensiones.Clear;
  edtStockActual.Text := '0';
  edtUltimoPrecio.Text := '---';
  edtValorStock.Text := '0,00 €';
  imgMateriaPrima.Picture := nil;
  memObservaciones.Clear;
  cbFamiliaChange(nil);
  LoadArticulosProveedores;
  LoadAlbaranesLineas;
  LoadDocuments;
  edtDescripcion.SetFocus;
end;

procedure TfrmMateriasPrimasEditor.DoModificar;
begin
  edtDescripcion.SetFocus;
end;

procedure TfrmMateriasPrimasEditor.DoPrimero;
var
  LParent: TfrmMateriasPrimas;
begin
  LParent := nil;
  if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
    LParent := TfrmMateriasPrimas(FParentForm)
  else if Assigned(frmMateriasPrimas) then
    LParent := frmMateriasPrimas;

  if Assigned(LParent) and not LParent.QryMain.IsEmpty then
  begin
    LParent.QryMain.First;
    FMateriaPrimaId := LParent.QryMain.FieldByName('id').AsString;
    LoadData;
  end;
end;

procedure TfrmMateriasPrimasEditor.DoAnterior;
var
  LParent: TfrmMateriasPrimas;
begin
  LParent := nil;
  if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
    LParent := TfrmMateriasPrimas(FParentForm)
  else if Assigned(frmMateriasPrimas) then
    LParent := frmMateriasPrimas;

  if Assigned(LParent) and not LParent.QryMain.IsEmpty then
  begin
    LParent.QryMain.Prior;
    FMateriaPrimaId := LParent.QryMain.FieldByName('id').AsString;
    LoadData;
  end;
end;

procedure TfrmMateriasPrimasEditor.DoSiguiente;
var
  LParent: TfrmMateriasPrimas;
begin
  LParent := nil;
  if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
    LParent := TfrmMateriasPrimas(FParentForm)
  else if Assigned(frmMateriasPrimas) then
    LParent := frmMateriasPrimas;

  if Assigned(LParent) and not LParent.QryMain.IsEmpty then
  begin
    LParent.QryMain.Next;
    FMateriaPrimaId := LParent.QryMain.FieldByName('id').AsString;
    LoadData;
  end;
end;

procedure TfrmMateriasPrimasEditor.DoUltimo;
var
  LParent: TfrmMateriasPrimas;
begin
  LParent := nil;
  if Assigned(FParentForm) and (FParentForm is TfrmMateriasPrimas) then
    LParent := TfrmMateriasPrimas(FParentForm)
  else if Assigned(frmMateriasPrimas) then
    LParent := frmMateriasPrimas;

  if Assigned(LParent) and not LParent.QryMain.IsEmpty then
  begin
    LParent.QryMain.Last;
    FMateriaPrimaId := LParent.QryMain.FieldByName('id').AsString;
    LoadData;
  end;
end;

procedure TfrmMateriasPrimasEditor.LoadMateriaPrimaImage;
var
  Qry: TFDQuery;
  Stream: TMemoryStream;
begin
  imgMateriaPrima.Picture := nil;
  if FMateriaPrimaId = '' then Exit;
  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT archivo FROM ge_documentos WHERE id_materia_prima = :id_mp AND descripcion = ''IMAGEN_MATERIA_PRIMA'' LIMIT 1';
    Qry.ParamByName('id_mp').AsString := FMateriaPrimaId;
    Qry.Open;
    if not Qry.IsEmpty and not TBlobField(Qry.FieldByName('archivo')).IsNull then
    begin
      Stream := TMemoryStream.Create;
      try
        TBlobField(Qry.FieldByName('archivo')).SaveToStream(Stream);
        Stream.Position := 0;
        if Stream.Size > 0 then
          imgMateriaPrima.Picture.LoadFromStream(Stream);
      finally
        Stream.Free;
      end;
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmMateriasPrimasEditor.btnSubirImagenClick(Sender: TObject);
var
  OpenDlg: TOpenDialog;
  Qry: TFDQuery;
  Stream: TMemoryStream;
  LFileName, LExtension: string;
begin
  if FMateriaPrimaId = '' then
  begin
    ShowMessage('Guarde primero la materia prima antes de asignarle una imagen.');
    Exit;
  end;
  OpenDlg := TOpenDialog.Create(nil);
  try
    OpenDlg.Filter := 'Imágenes (*.png;*.jpg;*.jpeg)|*.png;*.jpg;*.jpeg';
    if OpenDlg.Execute then
    begin
      LFileName := ExtractFileName(OpenDlg.FileName);
      LExtension := LowerCase(ExtractFileExt(OpenDlg.FileName));
      if LExtension <> '' then
        LExtension := Copy(LExtension, 2, Length(LExtension) - 1);
      
      Stream := TMemoryStream.Create;
      try
        Stream.LoadFromFile(OpenDlg.FileName);
        Stream.Position := 0;
        
        Qry := TFDQuery.Create(nil);
        try
          Qry.Connection := dmgMain.dbConn;
          Qry.SQL.Text := 'SELECT id FROM ge_documentos WHERE id_materia_prima = :id_mp AND descripcion = ''IMAGEN_MATERIA_PRIMA'' LIMIT 1';
          Qry.ParamByName('id_mp').AsString := FMateriaPrimaId;
          Qry.Open;
          
          if Qry.IsEmpty then
          begin
            Qry.Close;
            Qry.SQL.Text := 
              'INSERT INTO ge_documentos (id_empresa, fecha, descripcion, archivo, nombre_archivo, extension, id_materia_prima, id_user_creator, id_user_update) ' +
              'VALUES (:id_empresa, :fecha, ''IMAGEN_MATERIA_PRIMA'', :archivo, :nombre_archivo, :extension, :id_materia_prima, :user, :user)';
          end
          else
          begin
            LFileName := Qry.FieldByName('id').AsString;
            Qry.Close;
            Qry.SQL.Text := 
              'UPDATE ge_documentos SET fecha = :fecha, archivo = :archivo, nombre_archivo = :nombre_archivo, ' +
              '  extension = :extension, id_user_update = :user, update_at = CURRENT_TIMESTAMP ' +
              'WHERE id = :id';
            Qry.ParamByName('id').AsString := LFileName;
          end;
          
          Qry.ParamByName('fecha').AsDate := Date;
          Qry.ParamByName('archivo').LoadFromStream(Stream, ftBlob);
          Qry.ParamByName('nombre_archivo').DataType := ftWideString;
          Qry.ParamByName('nombre_archivo').AsWideString := ExtractFileName(OpenDlg.FileName);
          Qry.ParamByName('extension').DataType := ftWideString;
          Qry.ParamByName('extension').AsWideString := LExtension;
          Qry.ParamByName('user').AsString := dmgMain.CurrentUserId;
          if Qry.SQL.Text.StartsWith('INSERT') then
          begin
            Qry.ParamByName('id_empresa').AsString := dmgMain.CurrentCompanyId;
            Qry.ParamByName('id_materia_prima').AsString := FMateriaPrimaId;
          end;
          
          Qry.ExecSQL;
        finally
          Qry.Free;
        end;
        
        LoadMateriaPrimaImage;
      finally
        Stream.Free;
      end;
    end;
  finally
    OpenDlg.Free;
  end;
end;

procedure TfrmMateriasPrimasEditor.btnEliminarImagenClick(Sender: TObject);
var
  Qry: TFDQuery;
begin
  if FMateriaPrimaId = '' then Exit;
  if MessageDlg('¿Está seguro de que desea eliminar la imagen de esta materia prima?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    Qry := TFDQuery.Create(nil);
    try
      Qry.Connection := dmgMain.dbConn;
      Qry.SQL.Text := 'DELETE FROM ge_documentos WHERE id_materia_prima = :id_mp AND descripcion = ''IMAGEN_MATERIA_PRIMA''';
      Qry.ParamByName('id_mp').AsString := FMateriaPrimaId;
      Qry.ExecSQL;
    finally
      Qry.Free;
    end;
    imgMateriaPrima.Picture := nil;
  end;
end;

// ---------------------------------------------------------------------------
// Pestaña de Artículos de Proveedores vinculados a la Materia Prima
// ---------------------------------------------------------------------------
procedure TfrmMateriasPrimasEditor.LoadArticulosProveedores;
begin
  if (FMateriaPrimaId <> '') and (FMateriaPrimaId <> 'AUTO') then
  begin
    FQryProveedores.Close;
    FQryProveedores.SQL.Text :=
      'SELECT ' +
      '  pm.id, ' +
      '  pm.id_proveedor, ' +
      '  COALESCE(p.nombre, ''- SIN PROVEEDOR -'') AS nombre_proveedor, ' +
      '  p.nif, ' +
      '  pm.codigo_articulo_proveedor, ' +
      '  pm.descripcion AS descripcion_articulo_proveedor, ' +
      '  pm.ult_precio, ' +
      '  pm.cantidad_por_bulto, ' +
      '  pm.activo, ' +
      '  pm.updated_at ' +
      'FROM ge_proveedores_materias_primas pm ' +
      'LEFT JOIN ge_proveedores p ON pm.id_proveedor = p.id ' +
      'WHERE pm.id_materia_prima = :mp ' +
      'ORDER BY p.nombre, pm.codigo_articulo_proveedor';
    FQryProveedores.ParamByName('mp').AsString := FMateriaPrimaId;
    FQryProveedores.Open;

    var LFieldPrecio := FQryProveedores.FindField('ult_precio');
    if LFieldPrecio <> nil then
      LFieldPrecio.OnGetText := UltPrecioGetText;

    var LFieldBulto := FQryProveedores.FindField('cantidad_por_bulto');
    if LFieldBulto <> nil then
      LFieldBulto.OnGetText := CantidadBultoGetText;
  end
  else
  begin
    FQryProveedores.Close;
  end;

  ConfigurarColumnasGridProveedores;
end;

procedure TfrmMateriasPrimasEditor.UltPrecioGetText(Sender: TField; var Text: string; DisplayText: Boolean);
begin
  if Sender.IsNull then
    Text := '---'
  else
    Text := FormatFloat('#,##0.0000 €', Sender.AsFloat);
end;

procedure TfrmMateriasPrimasEditor.CantidadBultoGetText(Sender: TField; var Text: string; DisplayText: Boolean);
begin
  if Sender.IsNull then
    Text := '1,00'
  else
    Text := FormatFloat('#,##0.00', Sender.AsFloat);
end;

procedure TfrmMateriasPrimasEditor.ConfigurarColumnasGridProveedores;
var
  i: Integer;
  Col: TColumn;
begin
  if not Assigned(FDbgProveedores) or not Assigned(FDbgProveedores.DataSource) or not FQryProveedores.Active then Exit;

  for i := 0 to FDbgProveedores.Columns.Count - 1 do
  begin
    Col := FDbgProveedores.Columns[i];

    if SameText(Col.FieldName, 'nombre_proveedor') then
    begin
      Col.Title.Caption := 'Proveedor';
      Col.Width := 220;
    end
    else if SameText(Col.FieldName, 'nif') or SameText(Col.FieldName, 'cif_nif') then
    begin
      Col.Title.Caption := 'NIF / CIF';
      Col.Width := 100;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'codigo_articulo_proveedor') then
    begin
      Col.Title.Caption := 'Cód. Art. Proveedor';
      Col.Width := 140;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'descripcion_articulo_proveedor') then
    begin
      Col.Title.Caption := 'Descripción Artículo Proveedor';
      Col.Width := 260;
    end
    else if SameText(Col.FieldName, 'ult_precio') then
    begin
      Col.Title.Caption := 'Últ. Precio';
      Col.Width := 110;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'cantidad_por_bulto') then
    begin
      Col.Title.Caption := 'Cant. / Bulto';
      Col.Width := 110;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'activo') then
    begin
      Col.Title.Caption := 'Activo';
      Col.Width := 60;
      Col.Alignment := taCenter;
    end
    else
    begin
      Col.Visible := False;
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.dbgProveedoresTitleClick(Column: TColumn);
begin
  if Assigned(FQryProveedores) and FQryProveedores.Active then
  begin
    try
      FQryProveedores.IndexFieldNames := Column.FieldName;
    except
    end;
  end;
end;

// ---------------------------------------------------------------------------
// Pestaña de Líneas de Albaranes de Proveedores (ge_prov_albaranes_detalle)
// ---------------------------------------------------------------------------
procedure TfrmMateriasPrimasEditor.InitAlbaranesLineasTab;
begin
  if Assigned(FTsAlbaranesLineas) then Exit;

  FTsAlbaranesLineas := TTabSheet.Create(Self);
  FTsAlbaranesLineas.PageControl := pgcDetails;
  FTsAlbaranesLineas.Caption := 'Líneas Albaranes Proveedor';

  FQryAlbaranesLineas := TFDQuery.Create(Self);
  FQryAlbaranesLineas.Connection := dmgMain.dbConn;

  FDsAlbaranesLineas := TDataSource.Create(Self);
  FDsAlbaranesLineas.DataSet := FQryAlbaranesLineas;

  FDbgAlbaranesLineas := TDBGrid.Create(Self);
  FDbgAlbaranesLineas.Parent := FTsAlbaranesLineas;
  FDbgAlbaranesLineas.Align := alClient;
  FDbgAlbaranesLineas.DataSource := FDsAlbaranesLineas;
  FDbgAlbaranesLineas.ReadOnly := True;
  FDbgAlbaranesLineas.Options := [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick];
  FDbgAlbaranesLineas.OnTitleClick := dbgAlbaranesLineasTitleClick;
end;

procedure TfrmMateriasPrimasEditor.LoadAlbaranesLineas;
begin
  if (FMateriaPrimaId <> '') and (FMateriaPrimaId <> 'AUTO') then
  begin
    FQryAlbaranesLineas.Close;
    FQryAlbaranesLineas.SQL.Text :=
      'SELECT ' +
      '  d.id, ' +
      '  d.idalbaran, ' +
      '  alb.documento AS num_albaran, ' +
      '  alb.fecha AS fecha_albaran, ' +
      '  COALESCE(p.nombre, ''- SIN PROVEEDOR -'') AS nombre_proveedor, ' +
      '  d.codigo, ' +
      '  d.descripcion, ' +
      '  d.lote, ' +
      '  d.fecha_caducidad, ' +
      '  d.bultos, ' +
      '  d.cantidad_bulto, ' +
      '  d.cantidad, ' +
      '  d.stock, ' +
      '  d.precio, ' +
      '  d.total, ' +
      '  d.validado ' +
      'FROM ge_prov_albaranes_detalle d ' +
      'LEFT JOIN ge_prov_albaranes alb ON d.idalbaran = alb.id ' +
      'LEFT JOIN ge_proveedores p ON d.idproveedor = p.id ' +
      'WHERE d.id_propio = :mp ' +
      'ORDER BY alb.fecha DESC, d.id DESC';
    FQryAlbaranesLineas.ParamByName('mp').AsString := FMateriaPrimaId;
    FQryAlbaranesLineas.Open;

    var LFieldPrecio := FQryAlbaranesLineas.FindField('precio');
    if LFieldPrecio <> nil then
      LFieldPrecio.OnGetText := UltPrecioGetText;

    var LFieldTotal := FQryAlbaranesLineas.FindField('total');
    if LFieldTotal <> nil then
      LFieldTotal.OnGetText := UltPrecioGetText;

    var LFieldBulto := FQryAlbaranesLineas.FindField('cantidad_bulto');
    if LFieldBulto <> nil then
      LFieldBulto.OnGetText := CantidadBultoGetText;
  end
  else
  begin
    FQryAlbaranesLineas.Close;
  end;

  ConfigurarColumnasGridAlbaranesLineas;
end;

procedure TfrmMateriasPrimasEditor.ConfigurarColumnasGridAlbaranesLineas;
var
  i: Integer;
  Col: TColumn;
begin
  if not Assigned(FDbgAlbaranesLineas) or not Assigned(FDbgAlbaranesLineas.DataSource) or not FQryAlbaranesLineas.Active then Exit;

  for i := 0 to FDbgAlbaranesLineas.Columns.Count - 1 do
  begin
    Col := FDbgAlbaranesLineas.Columns[i];

    if SameText(Col.FieldName, 'id') or SameText(Col.FieldName, 'idalbaran') then
    begin
      Col.Visible := False;
    end
    else if SameText(Col.FieldName, 'num_albaran') then
    begin
      Col.Title.Caption := 'Nº Albarán';
      Col.Width := 95;
    end
    else if SameText(Col.FieldName, 'fecha_albaran') then
    begin
      Col.Title.Caption := 'Fecha';
      Col.Width := 80;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'nombre_proveedor') then
    begin
      Col.Title.Caption := 'Proveedor';
      Col.Width := 180;
    end
    else if SameText(Col.FieldName, 'codigo') then
    begin
      Col.Title.Caption := 'Cód. Prov.';
      Col.Width := 90;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'descripcion') then
    begin
      Col.Title.Caption := 'Descripción Línea';
      Col.Width := 200;
    end
    else if SameText(Col.FieldName, 'lote') then
    begin
      Col.Title.Caption := 'Lote';
      Col.Width := 85;
    end
    else if SameText(Col.FieldName, 'fecha_caducidad') then
    begin
      Col.Title.Caption := 'F. Caducidad';
      Col.Width := 85;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'bultos') then
    begin
      Col.Title.Caption := 'Bultos';
      Col.Width := 60;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'cantidad_bulto') then
    begin
      Col.Title.Caption := 'Cant./Bulto';
      Col.Width := 75;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'cantidad') then
    begin
      Col.Title.Caption := 'Cantidad';
      Col.Width := 75;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'stock') then
    begin
      Col.Title.Caption := 'Stock';
      Col.Width := 75;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'precio') then
    begin
      Col.Title.Caption := 'Precio';
      Col.Width := 80;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'total') then
    begin
      Col.Title.Caption := 'Total';
      Col.Width := 85;
      Col.Alignment := taRightJustify;
    end
    else if SameText(Col.FieldName, 'validado') then
    begin
      Col.Title.Caption := 'Validado';
      Col.Width := 60;
      Col.Alignment := taCenter;
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.dbgAlbaranesLineasTitleClick(Column: TColumn);
begin
  if not FQryAlbaranesLineas.Active or FQryAlbaranesLineas.IsEmpty then Exit;

  if FLastSortColAlbaranes = Column.FieldName then
  begin
    if FLastSortDirAlbaranes = 'A' then FLastSortDirAlbaranes := 'D' else FLastSortDirAlbaranes := 'A';
  end
  else
  begin
    FLastSortColAlbaranes := Column.FieldName;
    FLastSortDirAlbaranes := 'A';
  end;

  if FLastSortDirAlbaranes = 'D' then
    FQryAlbaranesLineas.IndexFieldNames := Column.FieldName + ':D'
  else
    FQryAlbaranesLineas.IndexFieldNames := Column.FieldName + ':A';
end;

// ---------------------------------------------------------------------------
// Pestaña de Documentación vinculada a la Materia Prima (ge_documentos)
// ---------------------------------------------------------------------------
procedure TfrmMateriasPrimasEditor.LoadDocuments;
begin
  if (FMateriaPrimaId <> '') and (FMateriaPrimaId <> 'AUTO') then
  begin
    FQryDocumentos.Close;
    FQryDocumentos.SQL.Text :=
      'SELECT id, fecha, descripcion, nombre_archivo, extension ' +
      'FROM ge_documentos ' +
      'WHERE id_materia_prima = :mp ' +
      '  AND (descripcion IS NULL OR descripcion <> ''IMAGEN_MATERIA_PRIMA'') ' +
      'ORDER BY fecha DESC, id DESC';
    FQryDocumentos.ParamByName('mp').AsString := FMateriaPrimaId;
    FQryDocumentos.Open;
  end
  else
  begin
    FQryDocumentos.Close;
  end;

  ConfigurarColumnasGridDocumentos;
end;

procedure TfrmMateriasPrimasEditor.ConfigurarColumnasGridDocumentos;
var
  i: Integer;
  Col: TColumn;
begin
  if not Assigned(FDbgDocumentos) or not Assigned(FDbgDocumentos.DataSource) or not FQryDocumentos.Active then Exit;

  for i := 0 to FDbgDocumentos.Columns.Count - 1 do
  begin
    Col := FDbgDocumentos.Columns[i];

    if SameText(Col.FieldName, 'fecha') then
    begin
      Col.Title.Caption := 'Fecha';
      Col.Width := 90;
      Col.Alignment := taCenter;
    end
    else if SameText(Col.FieldName, 'descripcion') then
    begin
      Col.Title.Caption := 'Descripción';
      Col.Width := 250;
    end
    else if SameText(Col.FieldName, 'nombre_archivo') then
    begin
      Col.Title.Caption := 'Nombre de Archivo';
      Col.Width := 280;
    end
    else if SameText(Col.FieldName, 'extension') then
    begin
      Col.Title.Caption := 'Ext.';
      Col.Width := 60;
      Col.Alignment := taCenter;
    end
    else
    begin
      Col.Visible := False;
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.dbgDocumentosTitleClick(Column: TColumn);
begin
  if Assigned(FQryDocumentos) and FQryDocumentos.Active then
  begin
    try
      FQryDocumentos.IndexFieldNames := Column.FieldName;
    except
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.dbgDocumentosDblClick(Sender: TObject);
begin
  btnVerDocClick(Sender);
end;

procedure TfrmMateriasPrimasEditor.btnSubirDocClick(Sender: TObject);
var
  LDesc: string;
  LFileName, LExtension: string;
  Stream: TMemoryStream;
  Qry: TFDQuery;
begin
  if (FMateriaPrimaId = '') or (FMateriaPrimaId = 'AUTO') then
  begin
    ShowMessage('Guarde primero la materia prima antes de asignarle documentos.');
    Exit;
  end;

  LDesc := InputBox('Subir Documento', 'Introduzca una descripción para el documento:', '');
  if Trim(LDesc) = '' then
  begin
    ShowMessage('La descripción es obligatoria.');
    Exit;
  end;

  if FOdDocumento.Execute then
  begin
    LFileName := ExtractFileName(FOdDocumento.FileName);
    LExtension := LowerCase(ExtractFileExt(FOdDocumento.FileName));
    if LExtension <> '' then
      LExtension := Copy(LExtension, 2, Length(LExtension) - 1);

    Stream := TMemoryStream.Create;
    try
      Stream.LoadFromFile(FOdDocumento.FileName);
      Stream.Position := 0;

      Qry := TFDQuery.Create(nil);
      try
        Qry.Connection := dmgMain.dbConn;
        Qry.SQL.Text :=
          'INSERT INTO ge_documentos ' +
          '  (id_empresa, fecha, descripcion, archivo, nombre_archivo, extension, id_materia_prima, id_user_creator, id_user_update) ' +
          'VALUES ' +
          '  (:id_empresa, :fecha, :descripcion, :archivo, :nombre_archivo, :extension, :id_materia_prima, :user, :user)';

        Qry.ParamByName('id_empresa').AsString := dmgMain.CurrentCompanyId;
        Qry.ParamByName('fecha').AsDate := Date;
        Qry.ParamByName('descripcion').DataType := ftWideString;
        Qry.ParamByName('descripcion').AsWideString := LDesc;
        Qry.ParamByName('archivo').LoadFromStream(Stream, ftBlob);
        Qry.ParamByName('nombre_archivo').DataType := ftWideString;
        Qry.ParamByName('nombre_archivo').AsWideString := LFileName;
        Qry.ParamByName('extension').DataType := ftWideString;
        Qry.ParamByName('extension').AsWideString := LExtension;
        Qry.ParamByName('id_materia_prima').AsString := FMateriaPrimaId;
        Qry.ParamByName('user').AsString := dmgMain.CurrentUserId;
        Qry.ExecSQL;
      finally
        Qry.Free;
      end;

      LoadDocuments;
    finally
      Stream.Free;
    end;
  end;
end;

procedure TfrmMateriasPrimasEditor.btnVerDocClick(Sender: TObject);
var
  Qry: TFDQuery;
  BlobField: TBlobField;
  TempPath, TempFile: string;
  Stream: TMemoryStream;
  LDocId: string;
  DocNombre: string;
begin
  if not Assigned(FQryDocumentos) or not FQryDocumentos.Active or FQryDocumentos.IsEmpty then Exit;

  LDocId := FQryDocumentos.FieldByName('id').AsString;
  if LDocId = '' then Exit;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT archivo, nombre_archivo, extension FROM ge_documentos WHERE id = :id';
    Qry.ParamByName('id').AsString := LDocId;
    Qry.Open;
    if not Qry.IsEmpty then
    begin
      BlobField := TBlobField(Qry.FieldByName('archivo'));
      if BlobField.IsNull or (BlobField.BlobSize = 0) then
      begin
        ShowMessage('El documento está vacío.');
        Exit;
      end;

      DocNombre := Qry.FieldByName('nombre_archivo').AsString;
      TempPath := TPath.GetTempPath;
      TempFile := TPath.Combine(TempPath, DocNombre);
      if TempFile = '' then
        TempFile := TPath.Combine(TempPath, 'doc_' + LDocId + '.' + Qry.FieldByName('extension').AsString);

      Stream := TMemoryStream.Create;
      try
        BlobField.SaveToStream(Stream);
        Stream.Position := 0;
        Stream.SaveToFile(TempFile);
      finally
        Stream.Free;
      end;

      ShellExecute(0, 'open', PChar(TempFile), nil, nil, SW_SHOWNORMAL);
    end;
  finally
    Qry.Free;
  end;
end;

procedure TfrmMateriasPrimasEditor.btnEliminarDocClick(Sender: TObject);
var
  LDocId: string;
  Qry: TFDQuery;
begin
  if not Assigned(FQryDocumentos) or not FQryDocumentos.Active or FQryDocumentos.IsEmpty then Exit;

  LDocId := FQryDocumentos.FieldByName('id').AsString;
  if LDocId = '' then Exit;

  if MessageDlg('¿Está seguro de que desea eliminar el documento seleccionado?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    Qry := TFDQuery.Create(nil);
    try
      Qry.Connection := dmgMain.dbConn;
      Qry.SQL.Text := 'DELETE FROM ge_documentos WHERE id = :id';
      Qry.ParamByName('id').AsString := LDocId;
      Qry.ExecSQL;
    finally
      Qry.Free;
    end;

    LoadDocuments;
  end;
end;

end.
