unit frm_ProvAlbaranesEditor;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uBaseForm, Data.DB,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error,
  FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.DApt,
  FireDAC.Comp.Client, Vcl.Buttons, Vcl.Grids, Vcl.DBGrids, Vcl.ExtCtrls,
  Vcl.ComCtrls, Vcl.StdCtrls, Vcl.Mask, Vcl.DBCtrls, dmg_Main,
  FireDAC.Stan.Async, FireDAC.Comp.DataSet, Winapi.ShellAPI, System.IOUtils, System.Generics.Collections;

type
  TfrmProvAlbaranesEditor = class(TfrmBase)
    qMaster: TFDQuery;
    dsMaster: TDataSource;
    qDetail: TFDQuery;
    dsDetail: TDataSource;
    qProvList: TFDQuery;
    dsProvList: TDataSource;
    qSelectedProv: TFDQuery;
    dsSelectedProv: TDataSource;

    // Totals in Footer
    Label4: TLabel;
    dbBase: TDBEdit;
    Label6: TLabel;
    dbTotal: TDBEdit;
    sgIvaDesglose: TStringGrid;

    // Header controls
    lblProveedor: TLabel;
    dbProv: TDBLookupComboBox;
    lblFecha: TLabel;
    dbFecha: TDBEdit;
    lblDoc: TLabel;
    dbDoc: TDBEdit;
    chkValidado: TDBCheckBox;
    chkConsolidado: TDBCheckBox;
    
    // Supplier info panel
    pnlDatosProveedor: TPanel;
    lblDatoNombre: TLabel;
    lblDatoDireccion: TLabel;
    lblDatoTelefono: TLabel;
    lblDatoEmail: TLabel;
    dbTextNombre: TDBText;
    dbTextDireccion: TDBText;
    dbTextTelefono: TDBText;
    dbTextEmail: TDBText;
    lbid: TLabel;
    DBText1: TDBText;
    lblCodigoPostal: TLabel;
    dbTextCP: TDBText;
    lblPoblacion: TLabel;
    DBTextPoblacion: TDBText;
    lblProvincia: TLabel;
    DBTextProvincia: TDBText;
    qSelectedProvDireccionCompleta: TWideStringField;
    qSelectedProvnombre: TWideStringField;
    qSelectedProvdireccion: TWideStringField;
    qSelectedProvpoblacion: TWideStringField;
    qSelectedProvcodpost: TWideStringField;
    qSelectedProvprovincia: TWideStringField;
    qSelectedProvtelefono: TWideStringField;
    qSelectedProvemail: TWideStringField;
    dbgListado: TDBGrid;

    procedure FormShow(Sender: TObject);
    procedure btnAnadirClick(Sender: TObject);
    procedure btnGuardarClick(Sender: TObject);
    procedure btnEliminarClick(Sender: TObject);
    procedure qDetailAfterPost(DataSet: TDataSet);
    procedure qDetailBeforePost(DataSet: TDataSet);
    procedure qMasterAfterScroll(DataSet: TDataSet);
    procedure qSelectedProvCalcFields(DataSet: TDataSet);
    procedure btnPrimeroClick(Sender: TObject);
    procedure btnAnteriorClick(Sender: TObject);
    procedure btnSiguienteClick(Sender: TObject);
    procedure btnUltimoClick(Sender: TObject);
    procedure btnImprimirClick(Sender: TObject);
  protected
    procedure Loaded; override;
    function IsNewRecord: Boolean; override;
    procedure DoPrimero; override;
    procedure DoAnterior; override;
    procedure DoSiguiente; override;
    procedure DoUltimo; override;
  private
    FIsNewRecord: Boolean;
    procedure CalcularTotales;
    procedure qDetailBultosChange(Sender: TField);
    procedure qDetailCantidadBultoChange(Sender: TField);
    procedure qDetailDescuentoPorcentajeChange(Sender: TField);
    procedure qDetailDescuentoPrecioChange(Sender: TField);
    procedure qDetailPrecioChange(Sender: TField);
    procedure FormatNumericFields(DataSet: TDataSet);
    procedure btnEnviarEntradasClick(Sender: TObject);
    procedure dbgItemsDrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
  public
    { Public declarations }
  end;

var
  frmProvAlbaranesEditor: TfrmProvAlbaranesEditor;

implementation

uses
  uAppTheme, uProvPreciosService;

{$R *.dfm}

procedure TfrmProvAlbaranesEditor.Loaded;
begin
  inherited Loaded;
  qDetail.SQL.Text := 'SELECT * FROM ge_prov_albaranes_detalle WHERE idalbaran = :id AND idempresa = :idempresa';
  qSelectedProv.SQL.Text := 'SELECT id, nombre, direccion, poblacion, codigo_postal AS codpost, provincia, telefono_1 AS telefono, email FROM ge_proveedores WHERE id = :idproveedor';
end;

procedure TfrmProvAlbaranesEditor.FormShow(Sender: TObject);
var
  LEmpresa: string;
begin
  LEmpresa := dmgMain.CurrentCompanyId;

  qProvList.Close;
  qProvList.Open;

  btnImprimir.OnClick := btnImprimirClick;

  if not qMaster.Active then
  begin
    qMaster.Close;
    qMaster.ParamByName('Idempresa').AsString := LEmpresa;
    qMaster.Open;
  end;

  FIsNewRecord := qMaster.Active and (qMaster.State = dsInsert);
  
  // Configurar sgIvaDesglose
  sgIvaDesglose.ColCount := 3;
  sgIvaDesglose.RowCount := 2;
  sgIvaDesglose.FixedRows := 1;
  sgIvaDesglose.FixedCols := 0;
  sgIvaDesglose.Cells[0, 0] := '% IVA';
  sgIvaDesglose.Cells[1, 0] := 'Base Imponible';
  sgIvaDesglose.Cells[2, 0] := 'Cuota IVA';
  sgIvaDesglose.ColWidths[0] := 60;
  sgIvaDesglose.ColWidths[1] := 140;
  sgIvaDesglose.ColWidths[2] := 140;

  qDetail.Close;
  qDetail.AfterDelete := qDetailAfterPost;
  qSelectedProv.Close;
  
  // Configurar botón para enviar a entradas de materias primas
  btnBuscarArticulo.Visible := True;
  btnBuscarArticulo.Caption := 'Enviar Entradas';
  btnBuscarArticulo.OnClick := btnEnviarEntradasClick;
  
  // Habilitar selección múltiple en el grid de detalles
  dbgItems.Options := dbgItems.Options + [dgMultiSelect];
  dbgItems.OnDrawColumnCell := dbgItemsDrawColumnCell;
  
  qMasterAfterScroll(qMaster);
end;

function TfrmProvAlbaranesEditor.IsNewRecord: Boolean;
begin
  Result := FIsNewRecord;
end;

procedure TfrmProvAlbaranesEditor.qMasterAfterScroll(DataSet: TDataSet);
begin
  if (qMaster.State = dsBrowse) and (not qMaster.FieldByName('id').IsNull) then
  begin
    qDetail.Close;
    qDetail.ParamByName('id').AsString := qMaster.FieldByName('id').AsString;
    qDetail.ParamByName('Idempresa').AsString := qMaster.FieldByName('idempresa').AsString;
    qDetail.Open;
    FormatNumericFields(qDetail);
    AdjustFormWidthToGrid(dbgItems);
    
    qDetail.FieldByName('bultos').OnChange := qDetailBultosChange;
    qDetail.FieldByName('cantidad_bulto').OnChange := qDetailCantidadBultoChange;
    qDetail.FieldByName('descuento_porcentaje').OnChange := qDetailDescuentoPorcentajeChange;
    qDetail.FieldByName('descuento_precio').OnChange := qDetailDescuentoPrecioChange;
    qDetail.FieldByName('precio').OnChange := qDetailPrecioChange;
    
    CalcularTotales;

    qSelectedProv.Close;
    qSelectedProv.ParamByName('idproveedor').AsString := qMaster.FieldByName('idproveedor').AsString;
    qSelectedProv.Open;
  end
  else
  begin
    qDetail.Close;
    qSelectedProv.Close;
  end;
end;

procedure TfrmProvAlbaranesEditor.btnAnadirClick(Sender: TObject);
begin
  FIsNewRecord := True;
  qMaster.Append;
  qMaster.FieldByName('idempresa').AsString := dmgMain.CurrentCompanyId;
  qMaster.FieldByName('fecha').AsDateTime := Date;
  dbProv.SetFocus;
end;

procedure TfrmProvAlbaranesEditor.btnGuardarClick(Sender: TObject);
begin
  if qMaster.State in [dsInsert, dsEdit] then
    qMaster.Post;
  if qDetail.State in [dsInsert, dsEdit] then
    qDetail.Post;
  FIsNewRecord := False;
  ResetChangeTracking;
  ShowMessage('Albarán de proveedor guardado correctamente.');
end;

procedure TfrmProvAlbaranesEditor.btnEliminarClick(Sender: TObject);
begin
  if MessageDlg('¿Desea eliminar este albarán?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    qMaster.Delete;
    ModalResult := mrOk;
  end;
end;

procedure TfrmProvAlbaranesEditor.qDetailBeforePost(DataSet: TDataSet);
begin
  qDetail.FieldByName('idempresa').AsString := qMaster.FieldByName('idempresa').AsString;
  qDetail.FieldByName('idalbaran').AsString := qMaster.FieldByName('id').AsString;
  qDetail.FieldByName('idproveedor').AsString := qMaster.FieldByName('idproveedor').AsString;
  
  // Calculate line total
  qDetail.FieldByName('base').AsFloat := qDetail.FieldByName('cantidad').AsFloat * (qDetail.FieldByName('precio').AsFloat - Abs(qDetail.FieldByName('descuento_precio').AsFloat));
  qDetail.FieldByName('total').AsFloat := qDetail.FieldByName('base').AsFloat * (1 + (qDetail.FieldByName('tipo_iva').AsInteger / 100));
end;

procedure TfrmProvAlbaranesEditor.qDetailAfterPost(DataSet: TDataSet);
var
  LPrecioFinal: Double;
  LVarResult: TVariacionPrecioResult;
  LEmpresaId, LProvId, LDocId, LLineaId, LMatId, LUserId: Integer;
  LQryAux: TFDQuery;
begin
  CalcularTotales;

  if not qDetail.IsEmpty then
  begin
    LEmpresaId := StrToIntDef(qMaster.FieldByName('idempresa').AsString, 1);
    LProvId := qMaster.FieldByName('idproveedor').AsInteger;
    LDocId := qMaster.FieldByName('id').AsInteger;
    LLineaId := qDetail.FieldByName('id').AsInteger;
    LMatId := qDetail.FieldByName('id_propio').AsInteger;
    LUserId := StrToIntDef(dmgMain.CurrentUserId, 1);

    if LLineaId <= 0 then
    begin
      LQryAux := TFDQuery.Create(nil);
      try
        LQryAux.Connection := dmgMain.dbConn;
        LQryAux.SQL.Text := 'SELECT LAST_INSERT_ID() AS last_id';
        LQryAux.Open;
        LLineaId := LQryAux.FieldByName('last_id').AsInteger;
      finally
        LQryAux.Free;
      end;
    end;

    if LMatId <= 0 then
    begin
      LQryAux := TFDQuery.Create(nil);
      try
        LQryAux.Connection := dmgMain.dbConn;
        LQryAux.SQL.Text :=
          'SELECT id_materia_prima FROM ge_proveedores_materias_primas ' +
          'WHERE id_proveedor = :prov AND codigo_articulo_proveedor = :cod LIMIT 1';
        LQryAux.ParamByName('prov').AsInteger := LProvId;
        LQryAux.ParamByName('cod').AsString := Trim(qDetail.FieldByName('codigo').AsString);
        LQryAux.Open;
        if not LQryAux.IsEmpty then
          LMatId := LQryAux.FieldByName('id_materia_prima').AsInteger;
      finally
        LQryAux.Free;
      end;
    end;

    LPrecioFinal := TProvPreciosService.CalcularPrecioFinalUnitario(
      qDetail.FieldByName('precio').AsFloat,
      qDetail.FieldByName('descuento_precio').AsFloat,
      qDetail.FieldByName('descuento_porcentaje').AsFloat,
      qDetail.FieldByName('cantidad').AsFloat
    );

    if (LDocId > 0) and (LLineaId > 0) and (LPrecioFinal > 0) then
    begin
      LVarResult := TProvPreciosService.ComprobarYRegistrarVariacion(
        dmgMain.dbConn,
        LEmpresaId,
        LProvId,
        qDetail.FieldByName('codigo').AsString,
        qDetail.FieldByName('descripcion').AsString,
        LMatId,
        'ALBARAN',
        LDocId,
        LLineaId,
        qMaster.FieldByName('documento').AsString,
        qMaster.FieldByName('fecha').AsDateTime,
        LPrecioFinal,
        LUserId
      );
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.CalcularTotales;
var
  LBase, LIVA, LTotal: Double;
  Bookmark: TBookmark;
  LVatBases: TDictionary<Double, Double>;
  LVatAmounts: TDictionary<Double, Double>;
  LRate: Double;
  LKey: Double;
  LLineBase, LLineIva: Double;
  LRow: Integer;
begin
  LBase := 0;
  LIVA := 0;
  LTotal := 0;
  
  LVatBases := TDictionary<Double, Double>.Create;
  LVatAmounts := TDictionary<Double, Double>.Create;
  try
    Bookmark := qDetail.GetBookmark;
    qDetail.DisableControls;
    try
      qDetail.First;
      while not qDetail.Eof do
      begin
        LLineBase := qDetail.FieldByName('base').AsFloat;
        LBase := LBase + LLineBase;
        LTotal := LTotal + qDetail.FieldByName('total').AsFloat;
        LLineIva := qDetail.FieldByName('total').AsFloat - LLineBase;
        
        LRate := qDetail.FieldByName('tipo_iva').AsFloat;
        if LVatBases.ContainsKey(LRate) then
        begin
          LVatBases[LRate] := LVatBases[LRate] + LLineBase;
          LVatAmounts[LRate] := LVatAmounts[LRate] + LLineIva;
        end
        else
        begin
          LVatBases.Add(LRate, LLineBase);
          LVatAmounts.Add(LRate, LLineIva);
        end;
        qDetail.Next;
      end;
      LIVA := LTotal - LBase;
      
      if qMaster.State = dsbrowse then qMaster.Edit;
      qMaster.FieldByName('base').AsFloat := LBase;
      qMaster.FieldByName('totaliva').AsFloat := LIVA;
      qMaster.FieldByName('totalentrada').AsFloat := LTotal;
      qMaster.Post;
    finally
      qDetail.GotoBookmark(Bookmark);
      qDetail.FreeBookmark(Bookmark);
      qDetail.EnableControls;
    end;

    // Rellenar sgIvaDesglose
    sgIvaDesglose.RowCount := 2;
    sgIvaDesglose.Cells[0, 1] := '';
    sgIvaDesglose.Cells[1, 1] := '';
    sgIvaDesglose.Cells[2, 1] := '';

    LRow := 1;
    for LKey in LVatAmounts.Keys do
    begin
      if LRow >= sgIvaDesglose.RowCount then
        sgIvaDesglose.RowCount := LRow + 1;
      sgIvaDesglose.Cells[0, LRow] := FormatFloat('0.#', LKey) + '%';
      sgIvaDesglose.Cells[1, LRow] := FormatFloat('#,##0.00', LVatBases[LKey]);
      sgIvaDesglose.Cells[2, LRow] := FormatFloat('#,##0.00', LVatAmounts[LKey]);
      Inc(LRow);
    end;
  finally
    LVatBases.Free;
    LVatAmounts.Free;
  end;
end;

procedure TfrmProvAlbaranesEditor.qSelectedProvCalcFields(DataSet: TDataSet);
var
  FullAddr: string;
begin
  FullAddr := DataSet.FieldByName('direccion').AsString;
  if DataSet.FieldByName('poblacion').AsString <> '' then
    FullAddr := FullAddr + ', ' + DataSet.FieldByName('poblacion').AsString;
  if DataSet.FieldByName('codpost').AsString <> '' then
    FullAddr := FullAddr + ' (' + DataSet.FieldByName('codpost').AsString + ')';
  if DataSet.FieldByName('provincia').AsString <> '' then
    FullAddr := FullAddr + ' - ' + DataSet.FieldByName('provincia').AsString;
    
  DataSet.FieldByName('DireccionCompleta').AsString := FullAddr;
end;

procedure TfrmProvAlbaranesEditor.btnPrimeroClick(Sender: TObject);
begin
  DoPrimero;
end;

procedure TfrmProvAlbaranesEditor.btnAnteriorClick(Sender: TObject);
begin
  DoAnterior;
end;

procedure TfrmProvAlbaranesEditor.btnSiguienteClick(Sender: TObject);
begin
  DoSiguiente;
end;

procedure TfrmProvAlbaranesEditor.btnUltimoClick(Sender: TObject);
begin
  DoUltimo;
end;

procedure TfrmProvAlbaranesEditor.DoPrimero;
begin
  qMaster.First;
end;

procedure TfrmProvAlbaranesEditor.DoAnterior;
begin
  qMaster.Prior;
end;

procedure TfrmProvAlbaranesEditor.DoSiguiente;
begin
  qMaster.Next;
end;

procedure TfrmProvAlbaranesEditor.DoUltimo;
begin
  qMaster.Last;
end;

procedure TfrmProvAlbaranesEditor.btnImprimirClick(Sender: TObject);
var
  Qry: TFDQuery;
  BlobField: TBlobField;
  TempPath, TempFile: string;
  Stream: TMemoryStream;
  LAlbaranId: string;
  DocNombre: string;
begin
  LAlbaranId := qMaster.FieldByName('id').AsString;
  if LAlbaranId = '' then
  begin
    ShowMessage('No hay ningún registro de albarán seleccionado para buscar su documento.');
    Exit;
  end;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT archivo, nombre_archivo, extension FROM ge_documentos WHERE id_albaran_proveedor = :id LIMIT 1';
    Qry.ParamByName('id').AsString := LAlbaranId;
    Qry.Open;

    if Qry.IsEmpty then
    begin
      MessageDlg('No existe el documento original almacenado en documentos para este albarán.', mtInformation, [mbOK], 0);
      Exit;
    end;

    DocNombre := Qry.FieldByName('nombre_archivo').AsString;
    if MessageDlg('Existe el documento original almacenado: ' + DocNombre + sLineBreak + sLineBreak + '¿Desea abrir el documento?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      BlobField := TBlobField(Qry.FieldByName('archivo'));
      if BlobField.IsNull or (BlobField.BlobSize = 0) then
      begin
        ShowMessage('El documento asociado está vacío.');
        Exit;
      end;

      TempPath := TPath.GetTempPath;
      TempFile := TPath.Combine(TempPath, DocNombre);
      if TempFile = '' then
        TempFile := TPath.Combine(TempPath, 'albaran_' + LAlbaranId + '.' + 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 TfrmProvAlbaranesEditor.qDetailDescuentoPorcentajeChange(Sender: TField);
var
  LPrecio: Double;
  LPorc: Double;
  LDescPrecio: Double;
begin
  if qDetail.State in [dsEdit, dsInsert] then
  begin
    LPrecio := qDetail.FieldByName('precio').AsFloat;
    LPorc := Abs(Sender.AsFloat);
    LDescPrecio := LPrecio * (LPorc / 100);
    qDetail.FieldByName('descuento_precio').OnChange := nil;
    try
      qDetail.FieldByName('descuento_precio').AsFloat := LDescPrecio;
    finally
      qDetail.FieldByName('descuento_precio').OnChange := qDetailDescuentoPrecioChange;
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.qDetailDescuentoPrecioChange(Sender: TField);
var
  LPrecio: Double;
  LDescPrecio: Double;
  LPorc: Double;
begin
  if qDetail.State in [dsEdit, dsInsert] then
  begin
    LPrecio := qDetail.FieldByName('precio').AsFloat;
    LDescPrecio := Abs(Sender.AsFloat);
    if LPrecio <> 0 then
      LPorc := (LDescPrecio / LPrecio) * 100
    else
      LPorc := 0;
    qDetail.FieldByName('descuento_porcentaje').OnChange := nil;
    try
      qDetail.FieldByName('descuento_porcentaje').AsFloat := LPorc;
    finally
      qDetail.FieldByName('descuento_porcentaje').OnChange := qDetailDescuentoPorcentajeChange;
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.qDetailPrecioChange(Sender: TField);
var
  LPrecio: Double;
  LPorc: Double;
  LDescPrecio: Double;
begin
  if qDetail.State in [dsEdit, dsInsert] then
  begin
    LPrecio := Sender.AsFloat;
    LPorc := Abs(qDetail.FieldByName('descuento_porcentaje').AsFloat);
    LDescPrecio := LPrecio * (LPorc / 100);
    qDetail.FieldByName('descuento_precio').OnChange := nil;
    try
      qDetail.FieldByName('descuento_precio').AsFloat := LDescPrecio;
    finally
      qDetail.FieldByName('descuento_precio').OnChange := qDetailDescuentoPrecioChange;
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.qDetailBultosChange(Sender: TField);
begin
  if qDetail.State in [dsEdit, dsInsert] then
  begin
    qDetail.FieldByName('cantidad').AsFloat := Sender.AsFloat * qDetail.FieldByName('cantidad_bulto').AsFloat;
  end;
end;

procedure TfrmProvAlbaranesEditor.qDetailCantidadBultoChange(Sender: TField);
var
  LOldCantBulto: Double;
  LNewCantBulto: Double;
  LOldPrecio: Double;
begin
  if qDetail.State in [dsEdit, dsInsert] then
  begin
    qDetail.FieldByName('cantidad').AsFloat := qDetail.FieldByName('bultos').AsFloat * Sender.AsFloat;

    // Recalcular el precio si la cantidad por bulto anterior era exactamente 1
    if not VarIsNull(Sender.OldValue) then
      LOldCantBulto := Sender.OldValue
    else
      LOldCantBulto := 1.0;

    LNewCantBulto := Sender.AsFloat;

    if (LOldCantBulto = 1.0) and (LNewCantBulto > 0) then
    begin
      if not VarIsNull(qDetail.FieldByName('precio').OldValue) then
        LOldPrecio := qDetail.FieldByName('precio').OldValue
      else
        LOldPrecio := qDetail.FieldByName('precio').AsFloat;

      qDetail.FieldByName('precio').AsFloat := LOldPrecio / LNewCantBulto;
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.FormatNumericFields(DataSet: TDataSet);
var
  i: Integer;
  Fld: TField;
begin
  for i := 0 to DataSet.FieldCount - 1 do
  begin
    Fld := DataSet.Fields[i];
    if Fld is TNumericField then
    begin
      if SameText(Fld.FieldName, 'cantidad') then
        TNumericField(Fld).DisplayFormat := '0.##'
      else if SameText(Fld.FieldName, 'bultos') or SameText(Fld.FieldName, 'cantidad_bulto') then
        TNumericField(Fld).DisplayFormat := '0.##'
      else if SameText(Fld.FieldName, 'precio') then
        TNumericField(Fld).DisplayFormat := '#,##0.00##'
      else if SameText(Fld.FieldName, 'descuento_porcentaje') or SameText(Fld.FieldName, 'descuento_precio') then
        TNumericField(Fld).DisplayFormat := '0.##'
      else if SameText(Fld.FieldName, 'base') or SameText(Fld.FieldName, 'total') then
        TNumericField(Fld).DisplayFormat := '#,##0.00';
    end;
  end;
end;

procedure TfrmProvAlbaranesEditor.btnEnviarEntradasClick(Sender: TObject);
var
  i: Integer;
  LDetailId: string;
  LAlmacenId: Integer;
  LProveedorId: string;
  LFechaEntrada: TDateTime;
  LIdPropio: Integer;
  LDesc: string;
  LLote: string;
  LFechaCaducidad: TDateTime;
  LPrecio: Double;
  LCantidad: Double;
  LQryCheck: TFDQuery;
  LQryArticulo: TFDQuery;
  LCount: Integer;
  LArticuloProveedorId: Integer;
begin
  if (not qDetail.Active) or qDetail.IsEmpty then
  begin
    ShowMessage('No hay líneas en el detalle para procesar.');
    Exit;
  end;

  if dbgItems.SelectedRows.Count = 0 then
  begin
    ShowMessage('Debe seleccionar al menos una línea en la rejilla para enviarla a entradas de materias primas.');
    Exit;
  end;

  // Obtener el almacén por defecto (el primero activo de la empresa)
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT id FROM ge_almacenes WHERE id_empresa = :emp AND activo = 1 ORDER BY id LIMIT 1';
  dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
  dmgMain.qryExec.Open;
  if not dmgMain.qryExec.IsEmpty then
    LAlmacenId := dmgMain.qryExec.FieldByName('id').AsInteger
  else
    LAlmacenId := 1;
  dmgMain.qryExec.Close;

  LProveedorId := qMaster.FieldByName('idproveedor').AsString;
  LFechaEntrada := qMaster.FieldByName('fecha').AsDateTime;

  LQryCheck := TFDQuery.Create(nil);
  LQryArticulo := TFDQuery.Create(nil);
  try
    LQryCheck.Connection := dmgMain.dbConn;
    LQryCheck.SQL.Text := 'SELECT COUNT(*) FROM ge_entradas_materias_primas WHERE origen_documento = ''ALBARAN'' AND origen_id = :origen_id';

    LQryArticulo.Connection := dmgMain.dbConn;
    LQryArticulo.SQL.Text := 'SELECT id FROM ge_proveedores_materias_primas WHERE id_proveedor = :id_proveedor AND id_materia_prima = :id_materia_prima LIMIT 1';

    LCount := 0;
    qDetail.DisableControls;
    try
      for i := 0 to dbgItems.SelectedRows.Count - 1 do
      begin
        qDetail.Bookmark := dbgItems.SelectedRows[i];

        LDetailId := qDetail.FieldByName('id').AsString;
        LIdPropio := qDetail.FieldByName('id_propio').AsInteger;
        LDesc := qDetail.FieldByName('descripcion').AsString;
        LLote := qDetail.FieldByName('lote').AsString;
        LFechaCaducidad := qDetail.FieldByName('fecha_caducidad').AsDateTime;
        LPrecio := qDetail.FieldByName('precio').AsFloat;
        LCantidad := qDetail.FieldByName('cantidad').AsFloat;

        if LIdPropio = 0 then
        begin
          ShowMessage('La línea "' + LDesc + '" no tiene una referencia propia (materia prima) asignada. Por favor, asígnela primero.');
          Continue;
        end;

        // Comprobar si ya existe una entrada con este origen_id para evitar duplicados
        LQryCheck.Close;
        LQryCheck.ParamByName('origen_id').AsString := LDetailId;
        LQryCheck.Open;

        if LQryCheck.Fields[0].AsInteger > 0 then
          Continue; // Ya existe la entrada, la saltamos para evitar duplicarla

        // Buscar el id_articulo_proveedor registrado
        LArticuloProveedorId := 0;
        LQryArticulo.Close;
        LQryArticulo.ParamByName('id_proveedor').AsString := LProveedorId;
        LQryArticulo.ParamByName('id_materia_prima').AsInteger := LIdPropio;
        LQryArticulo.Open;
        if not LQryArticulo.IsEmpty then
          LArticuloProveedorId := LQryArticulo.FieldByName('id').AsInteger;

        // Insertar en ge_entradas_materias_primas
        dmgMain.qryExec.Close;
        dmgMain.qryExec.SQL.Text :=
          'INSERT INTO ge_entradas_materias_primas (' +
          '  id_empresa, id_almacen, id_materia_prima, id_proveedor, id_articulo_proveedor, ' +
          '  fecha_entrada, descripcion, lote, fecha_caducidad, precio_compra, ' +
          '  cantidad_compra, stock_restante, origen_documento, origen_id, ' +
          '  id_user_creator, id_user_update' +
          ') VALUES (' +
          '  :id_empresa, :id_almacen, :id_materia_prima, :id_proveedor, :id_articulo_proveedor, ' +
          '  :fecha_entrada, :descripcion, :lote, :fecha_caducidad, :precio_compra, ' +
          '  :cantidad_compra, :stock_restante, ''ALBARAN'', :origen_id, ' +
          '  :user, :user' +
          ')';

        dmgMain.qryExec.ParamByName('id_empresa').AsString := dmgMain.CurrentCompanyId;
        dmgMain.qryExec.ParamByName('id_almacen').AsInteger := LAlmacenId;
        dmgMain.qryExec.ParamByName('id_materia_prima').AsInteger := LIdPropio;
        dmgMain.qryExec.ParamByName('id_proveedor').AsString := LProveedorId;
        dmgMain.qryExec.ParamByName('id_articulo_proveedor').AsInteger := LArticuloProveedorId;
        dmgMain.qryExec.ParamByName('fecha_entrada').AsDate := LFechaEntrada;
        dmgMain.qryExec.ParamByName('descripcion').AsString := LDesc;

        if LLote <> '' then
          dmgMain.qryExec.ParamByName('lote').AsString := LLote
        else
          dmgMain.qryExec.ParamByName('lote').Clear;

        if LFechaCaducidad <> 0 then
          dmgMain.qryExec.ParamByName('fecha_caducidad').AsDate := LFechaCaducidad
        else
          dmgMain.qryExec.ParamByName('fecha_caducidad').Clear;

        dmgMain.qryExec.ParamByName('precio_compra').AsFloat := LPrecio;
        dmgMain.qryExec.ParamByName('cantidad_compra').AsFloat := LCantidad;
        dmgMain.qryExec.ParamByName('stock_restante').AsFloat := LCantidad;
        dmgMain.qryExec.ParamByName('origen_id').AsString := LDetailId;
        dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;

        dmgMain.qryExec.ExecSQL;
        Inc(LCount);

        // Además, marcar la línea de detalle como validada
        if not (qDetail.State in [dsEdit, dsInsert]) then
          qDetail.Edit;
        qDetail.FieldByName('validado').AsInteger := 1;
        qDetail.Post;
      end;
    finally
      qDetail.EnableControls;
    end;

    if LCount > 0 then
      ShowMessage('Se han creado con éxito ' + IntToStr(LCount) + ' entradas en materias primas.')
    else
      ShowMessage('No se insertaron nuevas entradas (las líneas seleccionadas ya habían sido enviadas o no tienen materia prima asignada).');

  finally
    LQryCheck.Free;
    LQryArticulo.Free;
  end;
end;

procedure TfrmProvAlbaranesEditor.dbgItemsDrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  Checked: Boolean;
  DrawRect: TRect;
  DrawState: Cardinal;
  Grid: TDBGrid;
  IsOddRow: Boolean;
  LIsValidated: Boolean;
begin
  Grid := TDBGrid(Sender);

  // Aplicar color si la fila no está seleccionada
  if not (gdSelected in State) then
  begin
    LIsValidated := False;
    if Assigned(qDetail) and qDetail.Active and not qDetail.IsEmpty then
    begin
      LIsValidated := qDetail.FieldByName('validado').AsInteger <> 0;
    end;

    if LIsValidated then
    begin
      // Soft light green background for validated lines
      Grid.Canvas.Brush.Color := $E2F5E2;
    end
    else
    begin
      // Standard alternating row colors
      if Assigned(Grid.DataSource) and Assigned(Grid.DataSource.DataSet) and Grid.DataSource.DataSet.Active then
        IsOddRow := Odd(Grid.DataSource.DataSet.RecNo)
      else
        IsOddRow := False;

      if IsOddRow then
        Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_ODD
      else
        Grid.Canvas.Brush.Color := TAppTheme.COLOR_GRID_ROW_EVEN;
    end;
  end;

  // Draw checkbox for boolean/active/validado fields
  if Assigned(Column.Field) and 
     ((Column.Field is TBooleanField) or 
      (Pos('activo', LowerCase(Column.Field.FieldName)) > 0) or 
      (Pos('active', LowerCase(Column.Field.FieldName)) > 0) or
      (Pos('comprobado', LowerCase(Column.Field.FieldName)) > 0) or
      (Pos('consolidado', LowerCase(Column.Field.FieldName)) > 0) or
      (Pos('validado', LowerCase(Column.Field.FieldName)) > 0)) then
  begin
    Grid.Canvas.Font.Color := Grid.Canvas.Brush.Color;
    Grid.Canvas.FillRect(Rect);
    
    if not Column.ReadOnly then
      Column.ReadOnly := True;
      
    if (Column.Field is TBooleanField) and (TBooleanField(Column.Field).DisplayValues <> ';') then
      TBooleanField(Column.Field).DisplayValues := ';';
    
    if Column.Field is TBooleanField then
      Checked := Column.Field.AsBoolean
    else
    begin
      var LValInt: Integer;
      var LValStr := Trim(Column.Field.AsString);
      if TryStrToInt(LValStr, LValInt) then
        Checked := (LValInt <> 0)
      else
        Checked := SameText(LValStr, 'S') or 
                   SameText(LValStr, 'T') or 
                   SameText(LValStr, 'True') or
                   SameText(LValStr, '1');
    end;
                 
    DrawRect := Rect;
    DrawRect.Left := Rect.Left + (Rect.Width - 14) div 2;
    DrawRect.Right := DrawRect.Left + 14;
    DrawRect.Top := Rect.Top + (Rect.Height - 14) div 2;
    DrawRect.Bottom := DrawRect.Top + 14;
    
    DrawState := DFCS_BUTTONCHECK;
    if Checked then
      DrawState := DrawState or DFCS_CHECKED;
      
    DrawFrameControl(Grid.Canvas.Handle, DrawRect, DFC_BUTTON, DrawState);
  end
  else
  begin
    Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
  end;
end;

end.
