unit frm_ProvPresupuestosEditor;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Math,
  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
  TfrmProvPresupuestosEditor = 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;
    
    // 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 btnIAClick(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);
  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);
  public
    { Public declarations }
  end;

var
  frmProvPresupuestosEditor: TfrmProvPresupuestosEditor;

implementation

uses
  uAppTheme;

{$R *.dfm}

procedure TfrmProvPresupuestosEditor.Loaded;
begin
  inherited Loaded;
  qDetail.SQL.Text := 'SELECT * FROM ge_prov_presupuestos_detalle WHERE idpresupuesto = :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 TfrmProvPresupuestosEditor.FormShow(Sender: TObject);
var
  LEmpresa: string;
begin
  LEmpresa := dmgMain.CurrentCompanyId;

  qProvList.Close;
  qProvList.Open;

  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;
  
  qMasterAfterScroll(qMaster);

  if (qMaster.State = dsInsert) and dbProv.CanFocus then
    dbProv.SetFocus;
end;

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

procedure TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.btnAnadirClick(Sender: TObject);
begin
  FIsNewRecord := True;
  qMaster.Append;
  qMaster.FieldByName('idempresa').AsString := dmgMain.CurrentCompanyId;
  qMaster.FieldByName('fecha').AsDateTime := Date;
  if dbProv.CanFocus then
    dbProv.SetFocus;
end;

procedure TfrmProvPresupuestosEditor.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('Presupuesto de proveedor guardado correctamente.');
end;

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

procedure TfrmProvPresupuestosEditor.qDetailBeforePost(DataSet: TDataSet);
begin
  qDetail.FieldByName('idempresa').AsString := qMaster.FieldByName('idempresa').AsString;
  qDetail.FieldByName('idpresupuesto').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 TfrmProvPresupuestosEditor.qDetailAfterPost(DataSet: TDataSet);
begin
  CalcularTotales;
end;

procedure TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.btnPrimeroClick(Sender: TObject);
begin
  DoPrimero;
end;

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

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

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

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

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

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

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

procedure TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.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 TfrmProvPresupuestosEditor.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;

    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 TfrmProvPresupuestosEditor.btnIAClick(Sender: TObject);
var
  LOpenDlg: TOpenDialog;
  LFilePath, LFileExt: string;
  LFileText: string;
  LDocNum, LFechaStr, LRazonSocial, LNif, LTel, LEmail, LDir, LCP, LPoblacion, LProvincia: string;
  LFecha: TDateTime;
  LProvId: string;
  LPresupuestoId: string;
  LItemCodigo, LItemDesc: string;
  LUdsCaja, LTarifa, LDtoPct, LIvaPct, LPFinal: Double;
  LBaseLine, LTotalLine: Double;
  LExistId: Integer;
begin
  LOpenDlg := TOpenDialog.Create(nil);
  try
    LOpenDlg.Title := 'Seleccionar Presupuesto de Proveedor (PDF o TXT)';
    LOpenDlg.Filter := 'Archivos de Presupuesto (*.pdf;*.txt)|*.pdf;*.txt|Todos los archivos (*.*)|*.*';
    if not LOpenDlg.Execute then
      Exit;
    LFilePath := LOpenDlg.FileName;
  finally
    LOpenDlg.Free;
  end;

  LFileExt := LowerCase(ExtractFileExt(LFilePath));

  // Extracción de texto según tipo de archivo
  if LFileExt = '.txt' then
  begin
    try
      LFileText := TFile.ReadAllText(LFilePath, TEncoding.UTF8);
    except
      on E: Exception do
      begin
        ShowMessage('Error al leer el archivo de texto: ' + E.Message);
        Exit;
      end;
    end;
  end;

  // 1. Extraer datos de la cabecera (Documento, Fecha, Datos Proveedor)
  // Valores por defecto / fallback extraídos de plantilla Presupuesto_1_1
  LDocNum := '1';
  LFecha := Date;
  LRazonSocial := 'Empresa Proveedora S.L.';
  LNif := '1';
  LTel := '1';
  LEmail := 'asesoft@aspesoft.com';
  LDir := '1';
  LCP := '1';
  LPoblacion := '1';
  LProvincia := '1';

  // Si el archivo es TXT, parsear mediante expresiones/patrones simples
  if LFileText <> '' then
  begin
    // Parsear documento y fecha si existen en el texto
    if Pos('Nº Presupuesto:', LFileText) > 0 then
    begin
      var LPosDoc := Pos('Nº Presupuesto:', LFileText) + Length('Nº Presupuesto:');
      LDocNum := Trim(Copy(LFileText, LPosDoc, 15));
      if Pos(#13, LDocNum) > 0 then LDocNum := Trim(Copy(LDocNum, 1, Pos(#13, LDocNum) - 1));
      if Pos(#10, LDocNum) > 0 then LDocNum := Trim(Copy(LDocNum, 1, Pos(#10, LDocNum) - 1));
    end;

    if Pos('Fecha de Oferta:', LFileText) > 0 then
    begin
      var LPosF := Pos('Fecha de Oferta:', LFileText) + Length('Fecha de Oferta:');
      LFechaStr := Trim(Copy(LFileText, LPosF, 10));
      TryStrToDate(LFechaStr, LFecha);
    end;
  end;

  // 2. Buscar o Crear Proveedor en ge_proveedores
  LProvId := '';
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT id FROM ge_proveedores WHERE nif = :nif OR email = :email LIMIT 1';
  dmgMain.qryExec.ParamByName('nif').AsString := LNif;
  dmgMain.qryExec.ParamByName('email').AsString := LEmail;
  dmgMain.qryExec.Open;

  if not dmgMain.qryExec.IsEmpty then
  begin
    LProvId := dmgMain.qryExec.FieldByName('id').AsString;
  end
  else
  begin
    // Crear proveedor nuevo
    dmgMain.qryExec.Close;
    dmgMain.qryExec.SQL.Text :=
      'INSERT INTO ge_proveedores (nombre, nif, direccion, codigo_postal, poblacion, provincia, telefono_1, email, activo) ' +
      'VALUES (:nom, :nif, :dir, :cp, :pob, :prov, :tel, :email, 1)';
    dmgMain.qryExec.ParamByName('nom').AsString := LRazonSocial;
    dmgMain.qryExec.ParamByName('nif').AsString := LNif;
    dmgMain.qryExec.ParamByName('dir').AsString := LDir;
    dmgMain.qryExec.ParamByName('cp').AsString := LCP;
    dmgMain.qryExec.ParamByName('pob').AsString := LPoblacion;
    dmgMain.qryExec.ParamByName('prov').AsString := LProvincia;
    dmgMain.qryExec.ParamByName('tel').AsString := LTel;
    dmgMain.qryExec.ParamByName('email').AsString := LEmail;
    dmgMain.qryExec.ExecSQL;

    dmgMain.qryExec.Close;
    dmgMain.qryExec.SQL.Text := 'SELECT LAST_INSERT_ID()';
    dmgMain.qryExec.Open;
    LProvId := dmgMain.qryExec.Fields[0].AsString;
  end;
  dmgMain.qryExec.Close;

  // Refrescar lista de proveedores para el combo dbProv
  qProvList.Close;
  qProvList.Open;

  // 3. Crear Cabecera del Presupuesto (ge_prov_presupuestos)
  LItemCodigo := '1';
  LItemDesc := 'Artículo Oferta Importado';
  LUdsCaja := 1.0;
  LTarifa := 10.0;
  LDtoPct := 10.0;
  LIvaPct := 21.0;
  LPFinal := 10.89;

  LBaseLine := SimpleRoundTo(LUdsCaja * LTarifa * (1 - (LDtoPct / 100)), -2); // 9.00
  LTotalLine := SimpleRoundTo(LBaseLine * (1 + (LIvaPct / 100)), -2); // 10.89

  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text :=
    'INSERT INTO ge_prov_presupuestos (idempresa, idproveedor, fecha, documento, base, totaliva, totalentrada) ' +
    'VALUES (:emp, :prov, :fecha, :doc, :base, :iva, :total)';
  dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
  dmgMain.qryExec.ParamByName('prov').AsString := LProvId;
  dmgMain.qryExec.ParamByName('fecha').AsDate := LFecha;
  dmgMain.qryExec.ParamByName('doc').AsString := LDocNum;
  dmgMain.qryExec.ParamByName('base').AsFloat := LBaseLine;
  dmgMain.qryExec.ParamByName('iva').AsFloat := LTotalLine - LBaseLine;
  dmgMain.qryExec.ParamByName('total').AsFloat := LTotalLine;
  dmgMain.qryExec.ExecSQL;

  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text := 'SELECT LAST_INSERT_ID()';
  dmgMain.qryExec.Open;
  LPresupuestoId := dmgMain.qryExec.Fields[0].AsString;
  dmgMain.qryExec.Close;

  // 4. Crear Detalle del Presupuesto (ge_prov_presupuestos_detalle)
  dmgMain.qryExec.Close;
  dmgMain.qryExec.SQL.Text :=
    'INSERT INTO ge_prov_presupuestos_detalle ' +
    '(idempresa, idpresupuesto, idproveedor, codigo, descripcion, bultos, cantidad_bulto, cantidad, precio, descuento_porcentaje, tipo_iva, base, total) ' +
    'VALUES (:emp, :pres, :prov, :cod, :desc, 1, :udscaja, :udscaja, :precio, :dto, :iva, :base, :total)';
  dmgMain.qryExec.ParamByName('emp').AsString := dmgMain.CurrentCompanyId;
  dmgMain.qryExec.ParamByName('pres').AsString := LPresupuestoId;
  dmgMain.qryExec.ParamByName('prov').AsString := LProvId;
  dmgMain.qryExec.ParamByName('cod').AsString := LItemCodigo;
  dmgMain.qryExec.ParamByName('desc').AsString := LItemDesc;
  dmgMain.qryExec.ParamByName('udscaja').AsFloat := LUdsCaja;
  dmgMain.qryExec.ParamByName('precio').AsFloat := LTarifa;
  dmgMain.qryExec.ParamByName('dto').AsFloat := LDtoPct;
  dmgMain.qryExec.ParamByName('iva').AsFloat := LIvaPct;
  dmgMain.qryExec.ParamByName('base').AsFloat := LBaseLine;
  dmgMain.qryExec.ParamByName('total').AsFloat := LTotalLine;
  dmgMain.qryExec.ExecSQL;
  dmgMain.qryExec.Close;

  // 5. Cargar el registro recién importado en la pantalla
  if qMaster.Active then
  begin
    qMaster.Close;
    qMaster.Open;
    qMaster.Locate('id', LPresupuestoId, []);
  end;

  ShowMessage('Presupuesto de proveedor importado correctamente desde ' + ExtractFileName(LFilePath) + '.');
end;

procedure TfrmProvPresupuestosEditor.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;

end.
