unit uPedidoPrintService;

interface

uses
  System.SysUtils, System.Classes, System.IOUtils, Vcl.Forms, Vcl.Dialogs,
  Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Param, frxClass, frxDBSet, Vcl.DBGrids;

type
  TPedidoPrintService = class
  private
    class function PrepararPedido(
      AReport: TfrxReport;
      const APedidoId: string;
      AClear: Boolean;
      out ASerieYNum: string;
      out AEmailCliente: string;
      out ATelefonoCliente: string;
      QryCabecera, QryDetalle, QryIvas, QryVencimientos: TFDQuery
    ): Boolean;
  public
    class procedure ImprimirPedidos(AOwner: TComponent; const APedidoIds: TArray<string>; AGrid: TDBGrid = nil);
    class procedure ImprimirPedido(AOwner: TComponent; const APedidoId: string; AGrid: TDBGrid = nil);
  end;

implementation

uses
  dmg_Main, frm_PrintOptions, frm_BaseList, Vcl.Imaging.jpeg, Vcl.Imaging.pngimage, frxExportPDF,
  uFormatosImpresionService;

const
  DEFAULT_PIE_DOCUMENTOS =
    '“Conforme con lo establecido en el Reglamento (UE) 2016/679 de 27 de Abril de 2016 ' +
    'y en la Ley Orgánica 3/2018 de 5 de Diciembre, sus datos serán incluidos en nuestro ' +
    'sistema de tratamiento para un mejor servicio. Puede ejercer sus derechos de acceso, ' +
    'rectificación, cancelación, supresión u olvido, limitación de tratamiento, portabilidad ' +
    'y oposición al tratamiento de sus datos de carácter personal, dirigiéndose por escrito ' +
    'a Raúl Asencio Pastelerías S.L.”';

function SanitizeFileName(const AFileName: string): string;
var
  C: Char;
begin
  Result := AFileName;
  for C in ['/', '\', ':', '*', '?', '"', '<', '>', '|'] do
    Result := StringReplace(Result, C, '-', [rfReplaceAll]);
  Result := Trim(Result);
end;

class function TPedidoPrintService.PrepararPedido(
  AReport: TfrxReport;
  const APedidoId: string;
  AClear: Boolean;
  out ASerieYNum: string;
  out AEmailCliente: string;
  out ATelefonoCliente: string;
  QryCabecera, QryDetalle, QryIvas, QryVencimientos: TFDQuery
): Boolean;
var
  LFormaPago: string;
  LCuentaEmpresa, LPieDocumentos: string;
  LIvaTipos, LIvaBases, LIvaDtos, LIvaCuotas, LIvaRe: string;
  LVencFechas, LVencImportes: string;
  LPorc: Double;
  LImgObj, LLblObj: TfrxComponent;
  LStream: TMemoryStream;
begin
  Result := False;
  ASerieYNum := '';
  AEmailCliente := '';
  ATelefonoCliente := '';

  if Trim(APedidoId) = '' then Exit;

  QryCabecera.Close;
  QryCabecera.ParamByName('id').AsString := APedidoId;
  QryCabecera.Open;
  if QryCabecera.IsEmpty then Exit;

  ASerieYNum := QryCabecera.FieldByName('serie_numero').AsString;
  AEmailCliente := Trim(QryCabecera.FieldByName('email_cliente').AsString);
  ATelefonoCliente := Trim(QryCabecera.FieldByName('telefono').AsString);

  LFormaPago := QryCabecera.FieldByName('formapago').AsString;
  LCuentaEmpresa := Trim(QryCabecera.FieldByName('cuenta_empresa').AsString);
  if LCuentaEmpresa = '' then
    LCuentaEmpresa := 'CC SABADELL ES91 0081 1486 7800 0206 3013';

  QryDetalle.Close;
  QryDetalle.ParamByName('id').AsString := APedidoId;
  QryDetalle.Open;

  QryIvas.Close;
  QryIvas.ParamByName('id').AsString := APedidoId;
  QryIvas.Open;

  LIvaTipos := '';
  LIvaBases := '';
  LIvaDtos := '';
  LIvaCuotas := '';
  LIvaRe := '';

  if QryIvas.IsEmpty and (QryCabecera.FieldByName('neto').AsFloat > 0) then
  begin
    LPorc := 10.0;
    if QryCabecera.FieldByName('neto').AsFloat > 0 then
      LPorc := (QryCabecera.FieldByName('totaliva').AsFloat / QryCabecera.FieldByName('neto').AsFloat) * 100.0;
    LIvaTipos := FormatFloat('0.00 %', LPorc);
    LIvaBases := FormatFloat('#,##0.00 €', QryCabecera.FieldByName('neto').AsFloat);
    LIvaDtos := '0,00 €';
    LIvaCuotas := FormatFloat('#,##0.00 €', QryCabecera.FieldByName('totaliva').AsFloat);
    LIvaRe := '0,00 €';
  end
  else
  begin
    QryIvas.First;
    while not QryIvas.Eof do
    begin
      if LIvaTipos <> '' then
      begin
        LIvaTipos := LIvaTipos + sLineBreak;
        LIvaBases := LIvaBases + sLineBreak;
        LIvaDtos := LIvaDtos + sLineBreak;
        LIvaCuotas := LIvaCuotas + sLineBreak;
        LIvaRe := LIvaRe + sLineBreak;
      end;
      LIvaTipos := LIvaTipos + FormatFloat('0.00 %', QryIvas.FieldByName('tipo').AsFloat);
      LIvaBases := LIvaBases + FormatFloat('#,##0.00 €', QryIvas.FieldByName('base').AsFloat);
      LIvaDtos := LIvaDtos + FormatFloat('#,##0.00 €', QryIvas.FieldByName('descuento').AsFloat);
      LIvaCuotas := LIvaCuotas + FormatFloat('#,##0.00 €', QryIvas.FieldByName('cuota_iva').AsFloat);
      LIvaRe := LIvaRe + FormatFloat('#,##0.00 €', QryIvas.FieldByName('cuota_re').AsFloat);
      QryIvas.Next;
    end;
  end;
  QryIvas.First;

  QryVencimientos.Close;
  QryVencimientos.ParamByName('id').AsString := APedidoId;
  QryVencimientos.Open;

  LVencFechas := '';
  LVencImportes := '';
  if not QryVencimientos.IsEmpty then
  begin
    LVencFechas := FormatDateTime('dd-mm-yyyy', QryVencimientos.FieldByName('fecha').AsDateTime);
    LVencImportes := FormatFloat('#,##0.00 €', QryVencimientos.FieldByName('importe').AsFloat);
  end;

  // Asignar variables para la plantilla
  AReport.Variables['FORMAPAGO'] := '''' + LFormaPago + '''';
  AReport.Variables['CUENTA_EMPRESA'] := '''' + LCuentaEmpresa + '''';
  AReport.Variables['IVATIPOS'] := '''' + LIvaTipos + '''';
  AReport.Variables['IVABASES'] := '''' + LIvaBases + '''';
  AReport.Variables['IVADTOS'] := '''' + LIvaDtos + '''';
  AReport.Variables['IVACUOTAS'] := '''' + LIvaCuotas + '''';
  AReport.Variables['IVARE'] := '''' + LIvaRe + '''';
  AReport.Variables['VENC_FECHAS'] := '''' + LVencFechas + '''';
  AReport.Variables['VENC_IMPORTES'] := '''' + LVencImportes + '''';

  if QryCabecera.FindField('pie_documentos') <> nil then
    LPieDocumentos := Trim(QryCabecera.FieldByName('pie_documentos').AsString)
  else
    LPieDocumentos := '';
  if LPieDocumentos = '' then
    LPieDocumentos := DEFAULT_PIE_DOCUMENTOS;
  AReport.Variables['PIE_DOCUMENTOS'] := '''' + StringReplace(StringReplace(LPieDocumentos, '''', '''''', [rfReplaceAll]), sLineBreak, '''#13#10''', [rfReplaceAll]) + '''';

  // Cargar logo de empresa en la plantilla
  LImgObj := AReport.FindObject('imgLogo');
  LLblObj := AReport.FindObject('lblLogoFallback');
  if Assigned(LImgObj) and (LImgObj is TfrxPictureView) then
  begin
    if (QryCabecera.FindField('logo_empresa') <> nil) and
       not TBlobField(QryCabecera.FieldByName('logo_empresa')).IsNull then
    begin
      LStream := TMemoryStream.Create;
      try
        TBlobField(QryCabecera.FieldByName('logo_empresa')).SaveToStream(LStream);
        LStream.Position := 0;
        if LStream.Size > 0 then
        begin
          TfrxPictureView(LImgObj).Picture.LoadFromStream(LStream);
          TfrxPictureView(LImgObj).Visible := True;
          if Assigned(LLblObj) and (LLblObj is TfrxMemoView) then
            TfrxMemoView(LLblObj).Visible := False;
        end
        else
        begin
          TfrxPictureView(LImgObj).Visible := False;
          if Assigned(LLblObj) and (LLblObj is TfrxMemoView) then
            TfrxMemoView(LLblObj).Visible := True;
        end;
      finally
        LStream.Free;
      end;
    end
    else
    begin
      TfrxPictureView(LImgObj).Visible := False;
      if Assigned(LLblObj) and (LLblObj is TfrxMemoView) then
        TfrxMemoView(LLblObj).Visible := True;
    end;
  end;

  // Concatenar informe en PreviewPages
  AReport.PrepareReport(AClear);
  Result := True;
end;

class procedure TPedidoPrintService.ImprimirPedido(AOwner: TComponent; const APedidoId: string; AGrid: TDBGrid = nil);
begin
  if Trim(APedidoId) = '' then
  begin
    ShowMessage('Debe seleccionar o guardar un pedido antes de imprimir.');
    Exit;
  end;
  ImprimirPedidos(AOwner, [APedidoId], AGrid);
end;

class procedure TPedidoPrintService.ImprimirPedidos(AOwner: TComponent; const APedidoIds: TArray<string>; AGrid: TDBGrid = nil);
var
  LReportPath: string;
  QryCabecera: TFDQuery;
  QryDetalle: TFDQuery;
  QryIvas: TFDQuery;
  QryVencimientos: TFDQuery;
  FrxDBCabecera: TfrxDBDataset;
  FrxDBDetalle: TfrxDBDataset;
  FrxDBIvas: TfrxDBDataset;
  FrxDBVencimientos: TfrxDBDataset;
  LSerieYNum, LSingleSerieYNum: string;
  Idx, LPreparedCount: Integer;
  LReportName: string;
  LLastEmail, LLastTelefono: string;
  LEmailCliente, LTelefonoCliente: string;
  LIndividualCallback: TIndividualDocExportCallback;
begin
  if Length(APedidoIds) = 0 then
  begin
    ShowMessage('Debe seleccionar al menos un pedido para imprimir.');
    Exit;
  end;

  if (AGrid = nil) and Assigned(AOwner) and (AOwner is TfrmBaseList) then
    AGrid := TfrmBaseList(AOwner).dbGridMain;

  // 1. Localizar la plantilla por defecto desde ge_formatosimpresion
  var LEmpresaId: Integer := 1;
  if Assigned(dmgMain) and (Trim(dmgMain.CurrentCompanyId) <> '') then
    LEmpresaId := StrToIntDef(dmgMain.CurrentCompanyId, 1);

  LReportPath := TFormatosImpresionService.GetRutaPlantillaDefecto('PEDIDO', LEmpresaId);
  if (LReportPath = '') or not FileExists(LReportPath) then
    LReportPath := TFormatosImpresionService.ResolverRutaArchivo('Pedido.fr3');

  if not FileExists(LReportPath) then
  begin
    ShowMessage('No se ha encontrado la plantilla de pedido en:' + sLineBreak + LReportPath);
    Exit;
  end;

  // 2. Instanciar Queries de datos
  QryCabecera := TFDQuery.Create(nil);
  QryDetalle := TFDQuery.Create(nil);
  QryIvas := TFDQuery.Create(nil);
  QryVencimientos := TFDQuery.Create(nil);

  FrxDBCabecera := TfrxDBDataset.Create(nil);
  FrxDBDetalle := TfrxDBDataset.Create(nil);
  FrxDBIvas := TfrxDBDataset.Create(nil);
  FrxDBVencimientos := TfrxDBDataset.Create(nil);

  try
    QryCabecera.Connection := dmgMain.dbConn;
    QryDetalle.Connection := dmgMain.dbConn;
    QryIvas.Connection := dmgMain.dbConn;
    QryVencimientos.Connection := dmgMain.dbConn;

    // --- QUERY CABECERA (inffactura) ---
    QryCabecera.SQL.Text :=
      'SELECT ' +
      '  p.id, ' +
      '  p.numero_pedido, ' +
      '  CAST(p.numero_pedido AS CHAR) AS serie_numero, ' +
      '  p.fecha_pedido AS fecha, ' +
      '  p.cliente_id AS idcliente, ' +
      '  COALESCE(c.nombre_fiscal, c.nombre_comercial, '''') AS nomcliente, ' +
      '  COALESCE(c.nif, '''') AS nif, ' +
      '  COALESCE(cd.direccion, c.domicilio, '''') AS direccion, ' +
      '  COALESCE(cd.cp, c.codigo_postal, '''') AS codpost, ' +
      '  COALESCE(cd.poblacion, c.poblacion, '''') AS poblacion, ' +
      '  COALESCE(cd.provincia, c.provincia, '''') AS provincia, ' +
      '  COALESCE(c.telefono, '''') AS telefono, ' +
      '  COALESCE(c.email, '''') AS email_cliente, ' +
      '  COALESCE(p.observaciones, '''') AS observaciones, ' +
      '  COALESCE(fp.descripcion, '''') AS formapago, ' +
      '  COALESCE(NULLIF(p.base_imponible, 0), ' +
      '    (SELECT SUM(COALESCE(pl.total, pl.cantidad * pl.precio)) FROM ge_pedidos_lineas pl WHERE pl.id_pedido = p.id), 0) AS neto, ' +
      '  COALESCE(NULLIF(p.iva, 0), ' +
      '    (SELECT SUM(COALESCE(pl.total, pl.cantidad * pl.precio) * (COALESCE(t.tipo, 10.0) / 100.0)) ' +
      '     FROM ge_pedidos_lineas pl LEFT JOIN ge_articulos a ON pl.id_articulo = a.id ' +
      '     LEFT JOIN ge_tipos_iva t ON a.id_tipo_iva = t.id WHERE pl.id_pedido = p.id), 0) AS totaliva, ' +
      '  COALESCE(NULLIF(p.total, 0), ' +
      '    (SELECT SUM(COALESCE(pl.total, pl.cantidad * pl.precio) + ' +
      '      (COALESCE(pl.total, pl.cantidad * pl.precio) * (COALESCE(t.tipo, 10.0) / 100.0))) ' +
      '     FROM ge_pedidos_lineas pl LEFT JOIN ge_articulos a ON pl.id_articulo = a.id ' +
      '     LEFT JOIN ge_tipos_iva t ON a.id_tipo_iva = t.id WHERE pl.id_pedido = p.id), 0) AS total, ' +
      '  COALESCE(e.nombre, ''RAUL ASENCIO COOP. V.'') AS nombre_empresa, ' +
      '  COALESCE(e.cif_nif, ''F23839327'') AS cif_empresa, ' +
      '  COALESCE(e.direccion, ''AV. ALCALDE CARLOS CARBONELL 130'') AS direccion_empresa, ' +
      '  CONCAT(COALESCE(e.cp, ''03680''), '' '', COALESCE(e.poblacion, ''Aspe''), '' - '', COALESCE(e.provincia, ''Alicante'')) AS cp_poblacion_empresa, ' +
      '  COALESCE(e.telefono1, ''965493373'') AS telefono_empresa, ' +
      '  COALESCE(e.cuenta, '''') AS cuenta_empresa, ' +
      '  COALESCE(NULLIF(TRIM(e.pie_documentos), ''''), ' +
      '    ''“Conforme con lo establecido en el Reglamento (UE) 2016/679 de 27 de Abril de 2016 '' ' +
      '    ''y en la Ley Orgánica 3/2018 de 5 de Diciembre, sus datos serán incluidos en nuestro '' ' +
      '    ''sistema de tratamiento para un mejor servicio. Puede ejercer sus derechos de acceso, '' ' +
      '    ''rectificación, cancelación, supresión u olvido, limitación de tratamiento, portabilidad '' ' +
      '    ''y oposición al tratamiento de sus datos de carácter personal, dirigiéndose por escrito '' ' +
      '    ''a Raúl Asencio Pastelerías S.L.”'') AS pie_documentos, ' +
      '  e.logo AS logo_empresa ' +
      'FROM ge_pedidos p ' +
      'LEFT JOIN ge_clientes c ON p.cliente_id = c.id ' +
      'LEFT JOIN ge_clientes_destinos cd ON p.clientedestino_id = cd.id ' +
      'LEFT JOIN ge_formas_pago fp ON c.id_forma_pago = fp.id ' +
      'LEFT JOIN ge_empresas e ON p.empresa_id = e.id ' +
      'WHERE p.id = :id';

    // --- QUERY LÍNEAS / DETALLE (infdetallefactura) ---
    QryDetalle.SQL.Text :=
      'SELECT ' +
      '  pl.id, ' +
      '  pl.id_pedido AS albaran_id, ' +
      '  CAST(p.numero_pedido AS CHAR) AS nalbaran, ' +
      '  DATE_FORMAT(p.fecha_pedido, ''%d/%m/%Y'') AS fechaalbaran, ' +
      '  COALESCE(NULLIF(p.edi_identicket, ''''), CAST(p.numero_pedido AS CHAR), '''') AS refalbaran, ' +
      '  COALESCE(NULLIF(pl.ean_producto, ''''), NULLIF(a.ean, ''''), CAST(pl.id_articulo AS CHAR), '''') AS idarticulo, ' +
      '  COALESCE(NULLIF(pl.descripcion, ''''), a.descripcion, '''') AS descripcion, ' +
      '  COALESCE(NULLIF(pl.descripcion, ''''), a.descripcion, '''') AS desarticulo, ' +
      '  '''' AS lotes, ' +
      '  NULL AS fecha_caducidad, ' +
      '  COALESCE(pl.cantidad, 0) AS cantidad, ' +
      '  COALESCE(pl.precio, 0) AS precio, ' +
      '  (COALESCE(pl.cantidad, 0) * COALESCE(pl.precio, 0)) AS subtotal, ' +
      '  0.0 AS descuento, ' +
      '  COALESCE(pl.total, (COALESCE(pl.cantidad, 0) * COALESCE(pl.precio, 0))) AS total, ' +
      '  COALESCE(t.tipo, 10.0) AS tipo_iva ' +
      'FROM ge_pedidos_lineas pl ' +
      'JOIN ge_pedidos p ON pl.id_pedido = p.id ' +
      'LEFT JOIN ge_articulos a ON pl.id_articulo = a.id ' +
      'LEFT JOIN ge_tipos_iva t ON a.id_tipo_iva = t.id ' +
      'WHERE pl.id_pedido = :id ' +
      'ORDER BY pl.id';

    // --- QUERY DESGLOSE IVAS (inffacturaivas) ---
    QryIvas.SQL.Text :=
      'SELECT ' +
      '  COALESCE(t.tipo, 10.0) AS tipo, ' +
      '  SUM(COALESCE(pl.total, (COALESCE(pl.cantidad, 0) * COALESCE(pl.precio, 0)))) AS base, ' +
      '  SUM(COALESCE(pl.total, (COALESCE(pl.cantidad, 0) * COALESCE(pl.precio, 0))) * (COALESCE(t.tipo, 10.0) / 100.0)) AS cuota_iva, ' +
      '  0.0 AS descuento, ' +
      '  0.0 AS cuota_re ' +
      'FROM ge_pedidos_lineas pl ' +
      'LEFT JOIN ge_articulos a ON pl.id_articulo = a.id ' +
      'LEFT JOIN ge_tipos_iva t ON a.id_tipo_iva = t.id ' +
      'WHERE pl.id_pedido = :id ' +
      'GROUP BY COALESCE(t.tipo, 10.0) ' +
      'ORDER BY tipo';

    // --- QUERY VENCIMIENTOS (infvencimientos) ---
    QryVencimientos.SQL.Text :=
      'SELECT ' +
      '  p.fecha_pedido AS fecha, ' +
      '  p.total AS importe ' +
      'FROM ge_pedidos p ' +
      'WHERE p.id = :id';

    // 3. Vincular frxDBDatasets
    FrxDBCabecera.UserName := 'inffactura';
    FrxDBCabecera.DataSet := QryCabecera;

    FrxDBDetalle.UserName := 'infdetallefactura';
    FrxDBDetalle.DataSet := QryDetalle;

    FrxDBIvas.UserName := 'inffacturaivas';
    FrxDBIvas.DataSet := QryIvas;

    FrxDBVencimientos.UserName := 'infvencimientos';
    FrxDBVencimientos.DataSet := QryVencimientos;

    // 4. Configurar frxInforme
    dmgMain.frxInforme.Clear;
    dmgMain.frxInforme.LoadFromFile(LReportPath);

    dmgMain.frxInforme.DataSets.Clear;
    dmgMain.frxInforme.DataSets.Add(FrxDBCabecera);
    dmgMain.frxInforme.DataSets.Add(FrxDBDetalle);
    dmgMain.frxInforme.DataSets.Add(FrxDBIvas);
    dmgMain.frxInforme.DataSets.Add(FrxDBVencimientos);

    dmgMain.frxInforme.EnabledDataSets.Clear;
    dmgMain.frxInforme.EnabledDataSets.Add(FrxDBCabecera);
    dmgMain.frxInforme.EnabledDataSets.Add(FrxDBDetalle);
    dmgMain.frxInforme.EnabledDataSets.Add(FrxDBIvas);
    dmgMain.frxInforme.EnabledDataSets.Add(FrxDBVencimientos);

    LPreparedCount := 0;
    LSerieYNum := '';
    LLastEmail := '';
    LLastTelefono := '';

    for Idx := 0 to High(APedidoIds) do
    begin
      if PrepararPedido(dmgMain.frxInforme, APedidoIds[Idx], LPreparedCount = 0,
           LSingleSerieYNum, LEmailCliente, LTelefonoCliente,
           QryCabecera, QryDetalle, QryIvas, QryVencimientos) then
      begin
        LSerieYNum := LSingleSerieYNum;
        if LLastEmail = '' then LLastEmail := LEmailCliente;
        if LLastTelefono = '' then LLastTelefono := LTelefonoCliente;
        Inc(LPreparedCount);
      end;
    end;

    if LPreparedCount = 0 then
    begin
      ShowMessage('No se encontraron datos para los pedidos seleccionados.');
      Exit;
    end;

    if LPreparedCount = 1 then
      LReportName := 'Pedido_' + SanitizeFileName(LSerieYNum)
    else
      LReportName := 'Pedidos_Seleccionados_' + IntToStr(LPreparedCount);

    // Preparar callback para generación de archivos individuales
    LIndividualCallback := nil;
    if Length(APedidoIds) > 1 then
    begin
      LIndividualCallback := function(const AOutputDir: string; out AFileNames: TArray<string>): Boolean
      var
        I: Integer;
        LDocSerieYNum, LEmail, LTelf: string;
        LFilePath: string;
        LPdfExport: TfrxPDFExport;
      begin
        Result := False;
        SetLength(AFileNames, 0);
        LPdfExport := TfrxPDFExport.Create(nil);
        try
          LPdfExport.ShowDialog := False;
          LPdfExport.ShowProgress := False;
          LPdfExport.OpenAfterExport := False;
          LPdfExport.Background := True;
          LPdfExport.Compressed := False;

          for I := 0 to High(APedidoIds) do
          begin
            if PrepararPedido(dmgMain.frxInforme, APedidoIds[I], True,
                 LDocSerieYNum, LEmail, LTelf,
                 QryCabecera, QryDetalle, QryIvas, QryVencimientos) then
            begin
              LDocSerieYNum := SanitizeFileName(LDocSerieYNum);
              LFilePath := IncludeTrailingPathDelimiter(AOutputDir) + 'Pedido_' + LDocSerieYNum + '.pdf';
              LPdfExport.FileName := LFilePath;
              dmgMain.frxInforme.Export(LPdfExport);
              if FileExists(LFilePath) then
              begin
                SetLength(AFileNames, Length(AFileNames) + 1);
                AFileNames[High(AFileNames)] := LFilePath;
              end;
            end;
          end;
          Result := Length(AFileNames) > 0;
        finally
          LPdfExport.Free;
        end;
      end;
    end;

    // Callback para recargar el informe si el usuario cambia de formato en frmPrintOptions
    var LOnFormatChange: TChangeReportFormatCallback :=
      procedure(const ANewReportPath: string)
      begin
        if FileExists(ANewReportPath) then
        begin
          dmgMain.frxInforme.LoadFromFile(ANewReportPath);
          LPreparedCount := 0;
          for var K := 0 to High(APedidoIds) do
          begin
            if PrepararPedido(dmgMain.frxInforme, APedidoIds[K], (K = 0),
                 LSingleSerieYNum, LEmailCliente, LTelefonoCliente,
                 QryCabecera, QryDetalle, QryIvas, QryVencimientos) then
              Inc(LPreparedCount);
          end;
        end;
      end;

    // 5. Mostrar diálogo estándar de opciones de impresión
    TfrmPrintOptions.ShowOptions(
      AOwner,
      dmgMain.frxInforme,
      AGrid,
      LReportName,
      LLastEmail,
      LLastTelefono,
      Length(APedidoIds),
      LIndividualCallback,
      nil,
      'PEDIDO',
      LEmpresaId,
      0,
      LOnFormatChange
    );

  finally
    FrxDBVencimientos.Free;
    FrxDBIvas.Free;
    FrxDBDetalle.Free;
    FrxDBCabecera.Free;

    QryVencimientos.Free;
    QryIvas.Free;
    QryDetalle.Free;
    QryCabecera.Free;
  end;
end;

end.
