unit frm_Albaranes;

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, Vcl.ComCtrls;

type
  TfrmAlbaranes = class(TfrmBaseList)
    btnFacturar: TSpeedButton;
    btnExportXmlEdicom: TSpeedButton;
    btnSendWsEdicom: TSpeedButton;
    btnDocsRelacionados: TSpeedButton;
    btnEtiquetasBultoECI: TSpeedButton;
    cbSeries: TComboBox;
    cbPeriodo: TComboBox;

    procedure FormShow(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject); override;
    procedure btnNewClick(Sender: TObject); override;
    procedure btnEditClick(Sender: TObject); override;
    procedure btnDeleteClick(Sender: TObject); override;
    procedure cbFilterChange(Sender: TObject);
    procedure btnFacturarClick(Sender: TObject);
    procedure btnExportXmlEdicomClick(Sender: TObject);
    procedure btnSendWsEdicomClick(Sender: TObject);
    procedure btnDocsRelacionadosClick(Sender: TObject);
    procedure btnEtiquetasBultoECIClick(Sender: TObject);
    procedure btnPrintClick(Sender: TObject); override;
  protected
    procedure ApplyAllFilters; override;
  private
  public
    { Public declarations }
    chkAnulada: TCheckBox;
  end;

var
  frmAlbaranes: TfrmAlbaranes;

implementation

uses
  dmg_Main, frm_AlbaranesEditor, frm_FacturasEditor, System.DateUtils, uDbErrorHandler, System.UITypes,
  System.StrUtils, uEdicomDesadvExporter, uEdicomWsClient, frm_DocumentosRelacionados, uAlbaranPrintService,
  uEtiquetaBultoECIService;

{$R *.dfm}

procedure TfrmAlbaranes.FormShow(Sender: TObject);
var
  Qry: TFDQuery;
begin
  DateFilterField := 'fecha';
  StatusFilterField := 'facturado';
  StatusValues.Add('Facturado');
  StatusValues.Add('Pendiente');
  AlphaFilterField := 'nombre_fiscal';

  inherited;
  
  chkAnulada := TCheckBox.Create(Self);
  chkAnulada.Parent := grpStatuses;
  chkAnulada.Align := alTop;
  chkAnulada.Caption := 'ANULADAS';
  chkAnulada.Checked := False; // Desmarcado por defecto
  chkAnulada.OnClick := StatusFilterClick;
  chkAnulada.Padding.Top := 3;
  chkAnulada.Padding.Bottom := 3;
  
  grpStatuses.Height := grpStatuses.Height + 25;

  pnlSearch.Visible := True;
  lblSearch.Caption := 'Buscar Albarán:';

  // Cargar series reales desde ge_series
  cbSeries.Items.Clear;
  cbSeries.Items.Add('Todas Series');
  cbSeries.ItemIndex := 0;

  Qry := TFDQuery.Create(nil);
  try
    Qry.Connection := dmgMain.dbConn;
    Qry.SQL.Text := 'SELECT DISTINCT serie, por_defecto FROM ge_series WHERE activo = 1 AND empresa_id = :emp ORDER BY serie';
    Qry.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
    Qry.Open;
    while not Qry.Eof do
    begin
      cbSeries.Items.Add(Qry.FieldByName('serie').AsString);
      if Qry.FieldByName('por_defecto').AsInteger <> 0 then
        cbSeries.ItemIndex := cbSeries.Items.Count - 1;
      Qry.Next;
    end;
  finally
    Qry.Free;
  end;

  // Refrescar para aplicar el filtro de la serie por defecto inmediatamente
  btnRefreshClick(nil);
end;

procedure TfrmAlbaranes.btnDocsRelacionadosClick(Sender: TObject);
begin
  if FQryMain.IsEmpty or (FQryMain.FindField('id') = nil) or (FQryMain.FieldByName('id').AsString = '') then
  begin
    ShowMessage('Debe seleccionar un albarán para consultar sus documentos relacionados.');
    Exit;
  end;

  TfrmDocumentosRelacionados.MostrarDocumentos(Self, dvtAlbaran, FQryMain.FieldByName('id').AsString,
    FQryMain.FieldByName('serie').AsString + '/' + FQryMain.FieldByName('numero').AsString);
end;

procedure TfrmAlbaranes.cbFilterChange(Sender: TObject);
begin
  btnRefreshClick(nil);
end;

procedure TfrmAlbaranes.btnRefreshClick(Sender: TObject);
var
  LSql: string;
  LSearch: string;
  LDateStart, LDateEnd: TDateTime;
  LUseDateFilter: Boolean;
begin
  if dmgMain.CurrentCompanyId = '' then
    Exit;

  LSearch := Trim(edtSearch.Text);
  FQryMain.Close;

  LSql := 'SELECT a.id, a.serie, a.numero, a.fecha, c.nombre_fiscal, a.total, ' +
          'COALESCE(a.edi_estado_envio, ''NO_ENVIADO'') as estado_edi, a.edi_message_id, a.facturado, f.numero as num_fact, a.anulado ' +
          'FROM ge_albaranes a ' +
          'LEFT JOIN ge_clientes c ON a.cliente_id = c.id ' +
          'LEFT JOIN ge_facturas f ON a.id_factura = f.id ' +
          'WHERE a.empresa_id = :empresa ';

  if LSearch <> '' then
  begin
    LSql := LSql + 'AND (c.nombre_fiscal LIKE :search OR a.numero LIKE :search) ';
  end;

  // Filtro de Serie
  if Assigned(cbSeries) and (cbSeries.ItemIndex > 0) then
    LSql := LSql + 'AND a.serie = :serie_filter ';

  // Filtro de Periodo
  LUseDateFilter := False;
  if Assigned(cbPeriodo) and (cbPeriodo.ItemIndex > 0) then
  begin
    LUseDateFilter := True;
    LDateEnd := Date;
    case cbPeriodo.ItemIndex of
      1: // Este Mes
        LDateStart := StartOfAMonth(YearOf(Date), MonthOf(Date));
      2: // Mes Anterior
        begin
          LDateStart := IncMonth(StartOfAMonth(YearOf(Date), MonthOf(Date)), -1);
          LDateEnd := EndOfAMonth(YearOf(LDateStart), MonthOf(LDateStart));
        end;
      3: // Este Trimestre
        begin
          case MonthOf(Date) of
            1..3: LDateStart := StartOfAMonth(YearOf(Date), 1);
            4..6: LDateStart := StartOfAMonth(YearOf(Date), 4);
            7..9: LDateStart := StartOfAMonth(YearOf(Date), 7);
            else LDateStart := StartOfAMonth(YearOf(Date), 10);
          end;
        end;
      4: // Este Año
        LDateStart := StartOfAMonth(YearOf(Date), 1);
      5: // Año Anterior
        begin
          LDateStart := StartOfAMonth(YearOf(Date) - 1, 1);
          LDateEnd := EncodeDate(YearOf(Date) - 1, 12, 31);
        end;
    end;
    LSql := LSql + 'AND a.fecha >= :date_start AND a.fecha <= :date_end ';
  end;

  LSql := LSql + 'ORDER BY a.fecha DESC, a.numero DESC';

  FQryMain.SQL.Text := LSql;
  FQryMain.ParamByName('empresa').AsString := dmgMain.CurrentCompanyId;

  if LSearch <> '' then
    FQryMain.ParamByName('search').AsString := '%' + LSearch + '%';

  if Assigned(cbSeries) and (cbSeries.ItemIndex > 0) then
    FQryMain.ParamByName('serie_filter').AsString := cbSeries.Text;

  if LUseDateFilter then
  begin
    FQryMain.ParamByName('date_start').AsDate := LDateStart;
    FQryMain.ParamByName('date_end').AsDate := LDateEnd;
  end;

  FQryMain.Open;
  //  LSql := 'SELECT a.id,a.serie,a.numero, a.fecha, c.nombre_fiscal, a.total, a.facturado, f.numero
dbGridMain.Columns.Clear;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'serie';
    Title.Caption := 'Serie';
    Width := 50;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'numero';
    Title.Caption := 'Nº Albaran';
    Width := 80;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'fecha';
    Title.Caption := 'Fecha';
    Width := 80;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'nombre_fiscal';
    Title.Caption := 'Cliente';
    Width := 250;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'total';
    Title.Caption := 'Total';
    Width := 80;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'facturado';
    Title.Caption := 'Facturado';
    Width := 60;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'num_fact';
    Title.Caption := 'Nº Fra';
    Width := 50;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'anulado';
    Title.Caption := 'Anulado';
    Width := 60;
    Alignment := taCenter;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'estado_edi';
    Title.Caption := 'Estado EDI';
    Width := 90;
  end;
  with dbGridMain.Columns.Add do
  begin
    FieldName := 'edi_message_id';
    Title.Caption := 'ID Msg EDI';
    Width := 150;
  end;
  if FQryMain.FindField('id') <> nil then
    FQryMain.FieldByName('id').Visible := False;
end;

procedure TfrmAlbaranes.btnFacturarClick(Sender: TObject);
var
  LAlbaranId, LClienteId, LSerieAlb, LNumAlb, LRefAlb: string;
  LSerieFra: string;
  LNumFra: Integer;
  LBase, LIva, LTotal: Double;
  LFormaPagoId: Variant;
  LFacturaId: string;
  LQryAlb: TFDQuery;
begin
  if FQryMain.IsEmpty or (FQryMain.FindField('id') = nil) or (FQryMain.FieldByName('id').AsString = '') then
  begin
    ShowMessage('Debe seleccionar un albarán para facturar.');
    Exit;
  end;

  LAlbaranId := FQryMain.FieldByName('id').AsString;
  LSerieAlb := FQryMain.FieldByName('serie').AsString;
  LNumAlb := FQryMain.FieldByName('numero').AsString;

  // 1. Comprobar si ya está facturado
  if (FQryMain.FieldByName('facturado').AsInteger <> 0) or
     ((FQryMain.FindField('num_fact') <> nil) and (FQryMain.FieldByName('num_fact').AsString <> '')) then
  begin
    var LNumFactura := '';
    if FQryMain.FindField('num_fact') <> nil then
      LNumFactura := FQryMain.FieldByName('num_fact').AsString;

    if MessageDlg('Este albarán ya ha sido facturado' +
                  IfThen(LNumFactura <> '', ' (Factura Nº: ' + LNumFactura + ')', '') + '.' + sLineBreak + sLineBreak +
                  '¿Desea abrir la factura asociada para consultarla?',
                  mtInformation, [mbYes, mbNo], 0) = mrYes then
    begin
      var LQryFact := TFDQuery.Create(nil);
      try
        LQryFact.Connection := dmgMain.dbConn;
        LQryFact.SQL.Text := 'SELECT id_factura FROM ge_albaranes WHERE id = :id';
        LQryFact.ParamByName('id').AsString := LAlbaranId;
        LQryFact.Open;
        if not LQryFact.IsEmpty and (LQryFact.FieldByName('id_factura').AsString <> '') then
        begin
          if not Assigned(frmFacturasEditor) then
            Application.CreateForm(TfrmFacturasEditor, frmFacturasEditor);
          frmFacturasEditor.FacturaId := LQryFact.FieldByName('id_factura').AsString;
          frmFacturasEditor.ShowModal;
        end;
      finally
        LQryFact.Free;
      end;
    end;
    Exit;
  end;

  // 2. Comprobar si está anulado
  if (FQryMain.FindField('anulado') <> nil) and (FQryMain.FieldByName('anulado').AsInteger <> 0) then
  begin
    MessageDlg('No se puede facturar un albarán anulado.', mtWarning, [mbOK], 0);
    Exit;
  end;

  // 3. Obtener Serie y Número siguiente de Factura (usando la serie del albarán como sugerencia)
  LSerieFra := '';
  LNumFra := 0;
  if not dmgMain.GetSerieYNumeroDocumento(dmgMain.CurrentCompanyId, 'FACTURA', Date, LSerieAlb, LSerieFra, LNumFra) then
  begin
    var LQrySer := TFDQuery.Create(nil);
    try
      LQrySer.Connection := dmgMain.dbConn;
      LQrySer.SQL.Text := 'SELECT serie, sig_numero_factura FROM ge_series WHERE activo = 1 AND empresa_id = :emp ORDER BY por_defecto DESC LIMIT 1';
      LQrySer.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
      LQrySer.Open;
      if not LQrySer.IsEmpty then
      begin
        LSerieFra := LQrySer.FieldByName('serie').AsString;
        LNumFra := LQrySer.FieldByName('sig_numero_factura').AsInteger;
      end
      else
      begin
        LSerieFra := 'A';
        LNumFra := StrToIntDef(dmgMain.GetNextIDS('ge_facturas'), 1);
      end;
    finally
      LQrySer.Free;
    end;
  end;

  // 4. Confirmación de facturación mostrando claramente la Factura que se va a emitir
  if MessageDlg('¿Desea generar la Factura Nº ' + LSerieFra + '/' + IntToStr(LNumFra) + ' a partir del albarán ' + LSerieAlb + '/' + LNumAlb + ' (' + FQryMain.FieldByName('nombre_fiscal').AsString + ')?',
                mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
    Exit;

  // 5. Leer datos completos del albarán
  LQryAlb := TFDQuery.Create(nil);
  try
    LQryAlb.Connection := dmgMain.dbConn;
    LQryAlb.SQL.Text := 'SELECT * FROM ge_albaranes WHERE id = :id';
    LQryAlb.ParamByName('id').AsString := LAlbaranId;
    LQryAlb.Open;

    if LQryAlb.IsEmpty then
    begin
      ShowMessage('No se encontraron los datos del albarán.');
      Exit;
    end;

    LClienteId := LQryAlb.FieldByName('cliente_id').AsString;
    LBase := LQryAlb.FieldByName('base_imponible').AsFloat;
    LIva := LQryAlb.FieldByName('iva').AsFloat;
    LTotal := LQryAlb.FieldByName('total').AsFloat;
    LRefAlb := 'Albarán ' + LSerieAlb + '/' + LNumAlb;
    if not LQryAlb.FieldByName('observaciones1').IsNull and (Trim(LQryAlb.FieldByName('observaciones1').AsString) <> '') then
      LRefAlb := LRefAlb + ' - ' + Trim(LQryAlb.FieldByName('observaciones1').AsString);

    if (LQryAlb.FindField('id_forma_pago') <> nil) and not LQryAlb.FieldByName('id_forma_pago').IsNull then
      LFormaPagoId := LQryAlb.FieldByName('id_forma_pago').AsInteger
    else
      LFormaPagoId := Null;

    // 6. Transacción para crear factura, copiar líneas y marcar albarán
    dmgMain.dbConn.StartTransaction;
    try
      // 6.1 Insertar Cabecera en ge_facturas
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_facturas (' +
        '  empresa_id, cliente_id, clientedestino_id, serie, numero, fecha, ' +
        '  base_imponible, iva, total, pagada, id_forma_pago, ' +
        '  referencia, id_user_creator, id_user_update' +
        ') VALUES (' +
        '  :emp, :cli, :dest, :ser, :num, :fec, ' +
        '  :base, :iva, :tot, 0, :fp, ' +
        '  :ref, :user, :user' +
        ')';
      dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
      dmgMain.qryExec.ParamByName('cli').AsString := LClienteId;

      dmgMain.qryExec.ParamByName('dest').DataType := ftInteger;
      if (LQryAlb.FindField('clientedestino_id') <> nil) and not LQryAlb.FieldByName('clientedestino_id').IsNull and (LQryAlb.FieldByName('clientedestino_id').AsInteger > 0) then
        dmgMain.qryExec.ParamByName('dest').AsInteger := LQryAlb.FieldByName('clientedestino_id').AsInteger
      else
        dmgMain.qryExec.ParamByName('dest').Clear;

      dmgMain.qryExec.ParamByName('ser').AsString := LSerieFra;
      dmgMain.qryExec.ParamByName('num').AsInteger := LNumFra;
      dmgMain.qryExec.ParamByName('fec').AsDate := Date;
      dmgMain.qryExec.ParamByName('base').AsFloat := LBase;
      dmgMain.qryExec.ParamByName('iva').AsFloat := LIva;
      dmgMain.qryExec.ParamByName('tot').AsFloat := LTotal;
      dmgMain.qryExec.ParamByName('fp').DataType := ftInteger;
      if not VarIsNull(LFormaPagoId) then
        dmgMain.qryExec.ParamByName('fp').AsInteger := Integer(LFormaPagoId)
      else
        dmgMain.qryExec.ParamByName('fp').Clear;
      dmgMain.qryExec.ParamByName('ref').AsString := LRefAlb;
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ExecSQL;

      // Obtener el ID de la factura recién insertada
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'SELECT LAST_INSERT_ID() as new_id';
      dmgMain.qryExec.Open;
      LFacturaId := dmgMain.qryExec.FieldByName('new_id').AsString;
      dmgMain.qryExec.Close;

      // Incrementar contador de serie
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_series SET sig_numero_factura = sig_numero_factura + 1 ' +
        'WHERE activo = 1 AND empresa_id = :emp AND serie = :ser';
      dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
      dmgMain.qryExec.ParamByName('ser').AsString := LSerieFra;
      dmgMain.qryExec.ExecSQL;

      // 6.2 Copiar líneas de ge_albaranes_lineas a ge_facturas_lineas
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text :=
        'INSERT INTO ge_facturas_lineas (' +
        '  factura_id, posicion, articulo_id, articulo_codigo, descripcion, ' +
        '  bultos, cantidad_bulto, cantidad, precio, descuento_porc, ' +
        '  descuento_cant, tipo_iva, base, iva, total, ' +
        '  lotes, fecha_caducidad, edi_numero_linea_albaran, ' +
        '  id_user_creator, id_user_update' +
        ') ' +
        'SELECT ' +
        '  :factura_id, posicion, articulo_id, articulo_codigo, descripcion, ' +
        '  COALESCE(bultos, 0), COALESCE(cantidad_bulto, 1), cantidad, precio, ' +
        '  COALESCE(descuento_porcentaje, 0), COALESCE(descuento_precio, 0), ' +
        '  tipo_iva, base, (total - base) as iva, total, ' +
        '  lotes, fecha_caducidad, id, ' +
        '  :user, :user ' +
        'FROM ge_albaranes_lineas ' +
        'WHERE albaran_id = :albaran_id ' +
        'ORDER BY posicion, id';
      dmgMain.qryExec.ParamByName('factura_id').AsString := LFacturaId;
      dmgMain.qryExec.ParamByName('albaran_id').AsString := LAlbaranId;
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ExecSQL;

      // 6.3 Actualizar ge_albaranes vinculando la factura
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_albaranes ' +
        'SET facturado = 1, id_factura = :factura_id, id_user_update = :user ' +
        'WHERE id = :albaran_id';
      dmgMain.qryExec.ParamByName('factura_id').AsString := LFacturaId;
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ParamByName('albaran_id').AsString := LAlbaranId;
      dmgMain.qryExec.ExecSQL;

      dmgMain.dbConn.Commit;

      btnRefreshClick(nil);

      if MessageDlg('Factura ' + LSerieFra + '/' + IntToStr(LNumFra) + ' creada con ÉXITO.' + sLineBreak + sLineBreak +
                    '¿Desea abrir el editor de facturas para verla o imprimirla?',
                    mtInformation, [mbYes, mbNo], 0) = mrYes then
      begin
        if not Assigned(frmFacturasEditor) then
          Application.CreateForm(TfrmFacturasEditor, frmFacturasEditor);
        frmFacturasEditor.FacturaId := LFacturaId;
        frmFacturasEditor.ShowModal;
        btnRefreshClick(nil);
      end;

    except
      on E: Exception do
      begin
        dmgMain.dbConn.Rollback;
        TDbErrorHandler.HandleException(E, 'Error al facturar el albarán');
      end;
    end;
  finally
    LQryAlb.Free;
  end;
end;

procedure TfrmAlbaranes.btnExportXmlEdicomClick(Sender: TObject);
var
  LAlbaranId: string;
begin
  if FQryMain.IsEmpty then Exit;

  LAlbaranId := FQryMain.FieldByName('id').AsString;
  ExportarAlbaranXmlEdicom(LAlbaranId, dmgMain.dbConn);
end;

procedure TfrmAlbaranes.btnSendWsEdicomClick(Sender: TObject);
var
  LAlbaranId, LErrMsg, LXmlContent, LResponse: string;
  LClient: TEdicomWsClient;
begin
  if FQryMain.IsEmpty then Exit;

  LAlbaranId := FQryMain.FieldByName('id').AsString;

  // Comprobar si ya fue enviado previamente a EDICOM
  if FQryMain.FindField('estado_edi') <> nil then
  begin
    var LCurEstado := FQryMain.FieldByName('estado_edi').AsString;
    if SameText(LCurEstado, 'ENVIADO') or SameText(LCurEstado, 'CONFIRMADO') then
    begin
      if MessageDlg('Este albarán ya ha sido enviado previamente a EDICOM (Estado: ' + LCurEstado + ').' + sLineBreak + sLineBreak +
                    '¿Desea volver a enviarlo de todos modos?',
                    mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
        Exit;
    end;
  end;

  // 1. Validar Datos Críticos
  if not ValidarAlbaranEdicom(LAlbaranId, dmgMain.dbConn, LErrMsg) then
  begin
    MessageDlg(LErrMsg, mtWarning, [mbOK], 0);
    Exit;
  end;

  // 2. Generar XML DESADV
  LXmlContent := GenerarXmlDesadv(LAlbaranId, dmgMain.dbConn);

  // 3. Enviar mediante WebService REST API
  LClient := TEdicomWsClient.Create(dmgMain.dbConn, dmgMain.CurrentCompanyId);
  try
    Screen.Cursor := crHourGlass;
    try
      if LClient.PublicarAlbaran(LAlbaranId, LXmlContent, LResponse) then
      begin
        MessageDlg('Albarán ' + FQryMain.FieldByName('serie').AsString + '-' + FQryMain.FieldByName('numero').AsString +
                   ' enviado con ÉXITO a EDICOM iPaaS (DESADV).' + sLineBreak + sLineBreak +
                   'Respuesta del Servidor:' + sLineBreak + Copy(LResponse, 1, 300),
                   mtInformation, [mbOK], 0);
        btnRefreshClick(nil);
      end
      else
      begin
        MessageDlg('Error al enviar el albarán a EDICOM iPaaS.' + sLineBreak + sLineBreak +
                   'Respuesta del Servidor:' + sLineBreak + Copy(LResponse, 1, 400),
                   mtError, [mbOK], 0);
        btnRefreshClick(nil);
      end;
    finally
      Screen.Cursor := crDefault;
    end;
  finally
    LClient.Free;
  end;
end;

procedure TfrmAlbaranes.btnEditClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;
  
  if not Assigned(frmAlbaranesEditor) then
    Application.CreateForm(TfrmAlbaranesEditor, frmAlbaranesEditor);
  
  frmAlbaranesEditor.ParentForm := Self;
  frmAlbaranesEditor.AlbaranId := FQryMain.FieldByName('id').AsString;
  if frmAlbaranesEditor.ShowModal = mrOk then
    btnRefreshClick(nil);
end;

procedure TfrmAlbaranes.btnNewClick(Sender: TObject);
begin
  if not Assigned(frmAlbaranesEditor) then
    Application.CreateForm(TfrmAlbaranesEditor, frmAlbaranesEditor);
  
  frmAlbaranesEditor.ParentForm := Self;
  frmAlbaranesEditor.AlbaranId := '';
  if frmAlbaranesEditor.ShowModal = mrOk then
    btnRefreshClick(nil);
end;

procedure TfrmAlbaranes.btnDeleteClick(Sender: TObject);
begin
  if FQryMain.IsEmpty then Exit;

  // Comprobar si el albarán está facturado
  if (FQryMain.FieldByName('facturado').AsInteger <> 0) or
     (FQryMain.FieldByName('num_fact').AsString <> '') then
  begin
    MessageDlg('No se puede eliminar un albarán que ya ha sido facturado.', mtError, [mbOK], 0);
    Exit;
  end;

  if MessageDlg('¿Está seguro de que desea eliminar este albarán? Esta acción no se puede deshacer.', 
                mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    dmgMain.dbConn.StartTransaction;
    try
      // Eliminar líneas primero (para evitar problemas de integridad referencial si no hay ON DELETE CASCADE)
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_albaranes_lineas WHERE albaran_id = :id';
      dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
      dmgMain.qryExec.ExecSQL;

      // Eliminar cabecera
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text := 'DELETE FROM ge_albaranes WHERE id = :id';
      dmgMain.qryExec.ParamByName('id').AsString := FQryMain.FieldByName('id').AsString;
      dmgMain.qryExec.ExecSQL;

      dmgMain.dbConn.Commit;
      btnRefreshClick(nil);
    except
      on E: Exception do
      begin
        dmgMain.dbConn.Rollback;
        TDbErrorHandler.HandleException(E, 'Error al eliminar el albarán');
      end;
    end;
  end;
end;

procedure TfrmAlbaranes.ApplyAllFilters;
var
  LTotal: Double;
  bm: TBookmark;
  LFilter: string;
begin
  inherited ApplyAllFilters;
  
  if FQryMain.Active then
  begin
    LFilter := FQryMain.Filter;
    if Assigned(chkAnulada) and chkAnulada.Checked then
    begin
      if Pos('1 = 0', LFilter) > 0 then
      begin
        LFilter := StringReplace(LFilter, '(1 = 0)', '(anulado = 1)', [rfReplaceAll]);
        LFilter := StringReplace(LFilter, '1 = 0', 'anulado = 1', [rfReplaceAll]);
      end;
      // Si hay otros filtros de estado activos, no añadimos 'anulado = 1'
      // para permitir ver tanto los anulados como los no anulados que cumplan esos estados.
    end
    else
    begin
      if LFilter <> '' then
        LFilter := '(' + LFilter + ') AND (anulado = 0 OR anulado IS NULL)'
      else
        LFilter := 'anulado = 0 OR anulado IS NULL';
    end;
    
    FQryMain.Filter := LFilter;
    FQryMain.Filtered := LFilter <> '';
  end;

  if FQryMain.Active and (FQryMain.FindField('total') <> nil) then
  begin
    LTotal := 0;
    FQryMain.DisableControls;
    bm := FQryMain.GetBookmark;
    try
      FQryMain.First;
      while not FQryMain.Eof do
      begin
        LTotal := LTotal + FQryMain.FieldByName('total').AsFloat;
        FQryMain.Next;
      end;
    finally
      if FQryMain.BookmarkValid(bm) then
        FQryMain.GotoBookmark(bm);
      FQryMain.FreeBookmark(bm);
      FQryMain.EnableControls;
    end;
    
    lblRecordCount.Caption := lblRecordCount.Caption + ' - Importe Total: ' + FormatFloat('#,##0.00 €', LTotal);
  end;
end;

procedure TfrmAlbaranes.btnPrintClick(Sender: TObject);
var
  LIds: TArray<string>;
begin
  LIds := GetSelectedOrCurrentIds('id');
  if Length(LIds) = 0 then
  begin
    ShowMessage('Debe seleccionar al menos un albarán para imprimir.');
    Exit;
  end;

  TAlbaranPrintService.ImprimirAlbaranes(Self, LIds, dbGridMain);
end;

procedure TfrmAlbaranes.btnEtiquetasBultoECIClick(Sender: TObject);
var
  LIds: TArray<string>;
  I: Integer;
begin
  LIds := GetSelectedOrCurrentIds('id');
  if Length(LIds) = 0 then
  begin
    MessageDlg('Debe seleccionar al menos un albarán para imprimir sus etiquetas de bulto ECI.', mtWarning, [mbOK], 0);
    Exit;
  end;

  for I := 0 to High(LIds) do
    TEtiquetaBultoECIService.ImprimirEtiquetasBultoAlbaran(Self, LIds[I], True);
end;

end.
