{
===============================================================================
  Copyright (©) 2001. Rodax Software.
===============================================================================
  Los contenidos de este fichero son propiedad de Rodax Software titular del
  copyright. Este fichero sólo podrá ser copiado, distribuido y utilizado,
  en su totalidad o en parte, con el permiso escrito de Rodax Software, o de
  acuerdo con los términos y condiciones establecidas en el acuerdo/contrato
  bajo el que se suministra.
  -----------------------------------------------------------------------------
  Web:  www.rodax-software.com
===============================================================================
  Fecha primera versión: 01-10-2001
  Versión actual: 1.1.2
  Fecha versión actual: 04-10-2004
===============================================================================
  Modificaciones:

    Fecha         Comentarios
    ---------------------------------------------------------------------------
    06-10-2001    Eliminados algunos SetFocus porque hay veces que la ventana
                  todavía está oculta.
    09-10-2001    Cambiar mensaje NoExisteArticulo. Cambiar el BuscarPedido
                  con el fin de que si no hay código almacén en el pedido
                  se cargue la direccion y persona de contacto que tenga.

                  RellenarLineasDetalle el orden debe ser descending para luego
                  ir introduciendo en la lista y que el orden no sea inverso al
                  introducido en el pedido.

    12-10-2001    No permitir que al recibir un pedido se introduzcan cantidades
                  mayores de las pedidas, y no queden registrados los movimientos
                  de 0 unidades

    21-10-2001    Comprobar en la transaccion de la operacion de recibir pedido
                  y devolver pedido que éste no ha sido recibido ya por otro
                  usuario.

    21-10-2001    Se ha añadido la fecha de recepción del pedido.

    03-11-2001    Modificación de la politica de realizar entradas y salidas.

    27-11-2001    En realizar Entrada y Salida se comprueba si el pedido no ha
                  sido ya recibido o devuelto en el momento en el que se
                  realiza la operacion. Esto se consigue mejorando las
                  sentencias SQL de DevolverPedido. Y los titulos de las
                  etiquetas dependiendo de las entidades.

    08-01-2002    Falla 'RealizarEntrada'.

    02-03-2002    En el contenido se pueden añadir muchos materiales a la vez.

    16-03-2002    Al eliminar un concepto o todos los conceptos de la lista
                  detalle que consulte confirmación, recordar el último codigo
                  de articulo seleccionado para la siguiente selección en
                  la lista detalle estos cambios se ha producido en los
                  procedimientos bEliminar, bEliminarTodo, VerModal.

    04-04-2002    La tabla de memoria se genera dinamicamente mediante una
                  tabla virtual en el función 'copiarTablaVirtual',
                  el metodo 'BorrarLineasDetalle' desaparece del frame y
                  solo es de esta unidad, literales se convierten en
                  constantes y limpieza de comentarios.

    07-04-2002    Se ha adaptado a una unica transacción.

    09-05-2002    Cambio de grid en lugar de utilizar UltimDBGrid, utilizaremos
                  dxDBGrid.

    20-08-2002    P237. Revisar los botones de 'Eliminar' y 'Eliminar todo'
                  para que al pulsar 'Esc' cancele la operación y no la
                  ejecute como ahora.

    06-03-2004    Se ha eliminado el tratamiento de perfiles de usuario.

    09-04-2004    P272. Adaptación a multiempresa.

    04-10-2004    Cambio CambiarEntidad para que soporte otro color.      
===============================================================================
}

unit MovimientoAlmacenes;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, DBGrids, RdxBotones, StdCtrls, IB,
  RdxMemo, RdxCampos, ExtCtrls, Tipos, IBCustomDataSet,
  RdxComboBox, Db, DBTables, RXDBCtrl, RdxTitulos, RdxPaneles, IBSQL,
  RdxBarras, DBCtrls, RxMemDS, RdxCheckBox, StrFunc, RdxFrameMovimientos,
  Mensajes, dxCntner, dxTL, dxDBCtrl, dxDBGrid, dxDBTLCL, dxEditor,
  dxExEdtr, dxEdLib, dxDBELib, cxControls, cxContainer, cxEdit, cxTextEdit,
  cxMaskEdit, cxDropDownEdit, cxCalendar, cxButtonEdit, cxDBEdit;

type
  TfrMovimientoAlmacenes = class(TRdxFrameMovimientos)
    pnlDatos: TRdxScrollPanel;
    pnlArticulos: TRdxPanel;
    cMateriales: TRdxCabecera;
    dsTablaDetalle: TDataSource;
    dsTablaPedidos: TDataSource;
    pnlGridArticulos: TPanel;
    pnlBarraGrid: TRdxPanel;
    bAnadir: TRdxBoton;
    bEliminar: TRdxBoton;
    bElminarTodo: TRdxBoton;
    pnlPedido: TRdxPanel;
    cPedido: TRdxCabecera;
    eCodigoPedido: TLabel;
    eNombreProv: TLabel;
    eCIFNIFProv: TLabel;
    eNombreAlmacen: TLabel;
    eCodigoAlmacen: TLabel;
    eCalle: TLabel;
    ePersona: TLabel;
    pnlAlmacenes: TRdxPanel;
    cAlmacenes: TRdxCabecera;
    eAlmacenOrigen: TLabel;
    eAlmacenDestino: TLabel;
    pnlAlmacen: TRdxPanel;
    cAlmacen: TRdxCabecera;
    eAlmacen: TLabel;
    pnlDetalle: TRdxPanel;
    cDetalle: TRdxCabecera;
    eTipoOperacion: TLabel;
    eTipoMovimiento: TLabel;
    eCodCliente: TLabel;
    eNombreCliente: TLabel;
    eCausa: TLabel;
    eCIFNIF: TLabel;
    cbAsociar: TRdxCheckBox;
    NombreAlmacen: TRdxEdit;
    Calle: TRdxEdit;
    TipoOperacion: TRdxEdit;
    TipoMovimiento: TRdxEdit;
    cbxAlmacen: TRdxComboBox;
    Causa: TRdxMemo;
    NifCifCliente: TRdxEdit;
    NombreCliente: TRdxEdit;
    brDoble: TRdxBarraInferior;
    bAceptar: TRdxBoton;
    bCancelar: TRdxBoton;
    Titulo: TRdxPanelTituloOperacion;
    NifCifProveedor: TRdxDBEdit;
    NombreProveedor: TRdxDBEdit;
    cbxOrigen: TRdxComboBox;
    cbxDestino: TRdxComboBox;
    gridDetalles: TdxDBGrid;
    FechaRecepcion: TcxDateEdit;
    CodPedido: TcxDBButtonEdit;
    CodigoAlmacen: TcxButtonEdit;
    CodCliente: TcxButtonEdit;

    procedure bAceptarClick(Sender: TObject);
    procedure bCancelarClick(Sender: TObject);

    procedure bAnadirClick(Sender: TObject);
    procedure bEliminarClick(Sender: TObject);
    procedure bElminarTodoClick(Sender: TObject);

    procedure cbxAlmacenEntradaChange(Sender: TObject);
    procedure cbxAlmacenSalidaChange(Sender: TObject);
    procedure cbxAlmacenOrigenChange(Sender: TObject);
    procedure cbxAlmacenDestinoChange(Sender: TObject);
    procedure cbAsociarClick(Sender: TObject);

    procedure GridBotonClick(Sender: TObject);
    procedure CodigoArticuloDetalleSetText(Sender: TField; const Text: String);
    procedure CantidadDetalleSetText(Sender: TField; const Text: String);
    procedure StocksDetalleSetText(Sender: TField; const Text: String);
    procedure gridDetallesExit(Sender: TObject);
    procedure CodPedidoPropertiesButtonClick(Sender: TObject;
      AButtonIndex: Integer);
    procedure CodigoAlmacenPropertiesButtonClick(Sender: TObject;
      AButtonIndex: Integer);
    procedure CodigoAlmacenPropertiesValidate(Sender: TObject;
      var DisplayValue: Variant; var ErrorText: TCaption;
      var Error: Boolean);
    procedure CodClientePropertiesButtonClick(Sender: TObject;
      AButtonIndex: Integer);
    procedure CodClientePropertiesValidate(Sender: TObject;
      var DisplayValue: Variant; var ErrorText: TCaption;
      var Error: Boolean);
  private
    FCodigoAlmacen : Variant;
    procedure ActivarCamposDetalle;
    procedure DesactivarCamposDetalle;
    procedure ActivarCampoCodigo;
    procedure DesactivarCampoCodigo;
    procedure RecalcularExistencias;
    function RealizarEntrada: Boolean;
    function RealizarSalida: Boolean;
    function EjecutarEntrada: Boolean;
    function EjecutarSalida: Boolean;
    function EjecutarTraslado: Boolean;

  protected
    procedure ActivarModoTraslado; override;
    procedure ActivarModoEntrada; override;
    procedure ActivarModoEntradaPedido; override;
    procedure ActivarModoSalida; override;
    procedure ActivarModoSalidaDevolucion; override;

    procedure VerModal; override;
    procedure SetCodigoArticulo (Value : Variant); override;
    procedure FreeContenido; override;

    procedure BuscarAlmacenOrigen; override;
    procedure BuscarAlmacenDestino; override;
    procedure BuscarPedido;  override;
    procedure BuscarCliente; override;

    procedure BorrarLineasDetalle;
    function ComprobarDatos : Boolean;
    procedure RellenarLineasDetalle;
    procedure CambiarModo(ModoAnterior, Modo : TRdxModo); override;
    function CambiarEntidad (EntidadAnterior, Entidad : TRdxEntidad): Boolean; override;
    procedure AfterRefreshPedidos(DataSet: TDataSet);

  public
    constructor Create (AOwner : TComponent); override;
    destructor Destroy; override;

  published
    property TablaPedidos;
    property TablaDetalleMem;
    property CodigoAlmacenOrigen;
    property CodigoAlmacenDestino;
    property CodigoArticulo;
    property CodigoPedido;
    property CodigoCliente;
  end;

var
  frMovimientoAlmacenes: TfrMovimientoAlmacenes;

implementation
{$R *.DFM}

uses
  BaseDatos, Articulos, ArticulosAlmacen, TablaMovimientos, DateFunc, 
  RdxFrameArticulosAlmacen, TablaArticulos, RdxFrameArticulos, NumFunc, 
  TablaPedidosProveedor, RdxFramePedidosProveedor, PedidosProveedores,
  TablaAlmacenes, TablaClientes, RdxFrameAlmacenes, Almacenes,
  clientes, RdxFrameClientes, TablaArticulosAlmacen, Excepciones,
  IBErrorCodes, Configuracion, RdxEmpresaActiva, Literales;

constructor TfrMovimientoAlmacenes.Create(AOwner : TComponent);
var
  TablaDetalle : TIBDataSet;

begin
  inherited Create(AOwner);
  ConfigurarFrame(Self, Self.Entidad);

  BaseDatos := dmBaseDatos.BD;
  Transaccion := dmBaseDatos.Transaccion;

  TablaDetalle := TIBDataSet.Create(Self);
  TablaDetalleMem := TRxMemoryData.Create(Self);
  //Tabla Pedidos valida solo para los modos EntradaPedido y SalidaDevolucion
  TablaPedidos := TIBDataSet.Create(Self);

  dsTablaDetalle.DataSet := TablaDetalleMem;
  dsTablaPedidos.DataSet := TablaPedidos;

  with TablaDetalle do
  begin
    Database := BaseDatos;
    Transaction := Transaccion;
    SelectSQL.Assign(dmTablaMovimientos.sqlConsultarDetalles);
    RefreshSQL.Assign(dmTablaMovimientos.sqlConsultarDetalles);
  end;

  with TablaPedidos do
  begin
    DataBase := BaseDatos;
    Transaction := Transaccion;
    SelectSQL.Assign(dmTablaMovimientos.sqlConsultarPedido);
    RefreshSQL.Assign(dmTablaMovimientos.sqlConsultarPedido);
  end;

  try
    TablaDetalleMem.CopyStructure(TablaDetalle);
    TablaDetalleMem.FieldByName('CODIGOPEDIDO').Required := False;
    TablaDetalleMem.FieldByName('NUMCONCEPTO').Required := False;
    TablaDetalleMem.FieldByName('CODIGOARTICULO').OnSetText := CodigoArticuloDetalleSetText;
  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;

  dmTablaMovimientos.InicializarGridDetalles(gridDetalles);
  (gridDetalles.ColumnByFieldName('CODIGOARTICULO') as TdxDBTreeListButtonColumn).OnEditButtonClick := GridBotonClick;

  FechaRecepcion.Date := Date;  
end;

destructor TfrMovimientoAlmacenes.Destroy;
begin
  TablaDetalleMem.Close;
  TablaPedidos.Close;
  TablaPedidos.UnPrepare;
  TablaPedidos.Free;
  inherited;
end;

procedure TfrMovimientoAlmacenes.VerModal;
begin
  if (ContenidoModal is TRdxFrameArticulosAlmacen) then
  begin
    (ContenidoModal as TRdxFrameArticulosAlmacen).CodigoAlmacen := CodigoAlmacenOrigen;
    (ContenidoModal as TRdxFrameArticulosAlmacen).CodigoArticulo := CodigoArticulo;
    //Aunque desactive Almacen no se asegura que pueda darse un cambio de almacen
    (ContenidoModal as TfrArticulosAlmacen).cbxAlmacen.Enabled := False;
    (ContenidoModal as TfrArticulosAlmacen).bFinalizar.Visible := False;
  end;
  inherited VerModal;
end;

procedure TfrMovimientoAlmacenes.ActivarModoTraslado;
begin
  TablaDetalleMem.FieldByName('CANTIDAD').OnSetText := CantidadDetalleSetText;

  pnlDetalle.Visible := False;
  pnlArticulos.Visible := False;
  pnlPedido.Visible := False;
  pnlAlmacen.Visible := False;
  pnlAlmacenes.Visible := False;

  TipoOperacion.Text := etiMovEntradaSalida;
  TipoMovimiento.Text := etiMovTraslado;

  pnlAlmacenes.Visible := True;
  pnlArticulos.Visible := True;
  pnlDetalle.Visible := True;

  CodCliente.Properties.ReadOnly := True;
  CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  cbAsociar.Checked := False;

  Causa.Clear;
  Causa.Lines.Add(etiMovEntSalTraslado);

  pnlBarraGrid.Visible := True;
  DesactivarCamposDetalle;
  ActivarCampoCodigo;
  Visible := True;
end;

procedure TfrMovimientoAlmacenes.ActivarModoEntrada;
begin
  TablaDetalleMem.FieldByName('CANTIDAD').OnSetText := CantidadDetalleSetText;
  TablaDetalleMem.FieldByName('STOCKMIN').OnSetText := StocksDetalleSetText;
  TablaDetalleMem.FieldByName('STOCKMAX').OnSetText := StocksDetalleSetText;

  cbxAlmacen.OnChange:= cbxAlmacenEntradaChange;

  pnlDetalle.Visible := False;
  pnlArticulos.Visible := False;
  pnlPedido.Visible := False;
  pnlAlmacen.Visible := False;
  pnlAlmacenes.Visible := False;

  TipoOperacion.Text := etiMovEntrada;
  TipoMovimiento.Text := etiMovRegularizacion;

  pnlAlmacen.Visible := True;
  pnlArticulos.Visible := True;
  pnlDetalle.Visible := True;

  CodCliente.Properties.ReadOnly := True;
  CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  cbAsociar.Checked := False;

  Causa.Clear;
  Causa.Lines.Add(etiMovEntRegularizacion);

  pnlBarraGrid.Visible := True;
  DesactivarCamposDetalle;
  ActivarCampoCodigo;
  Visible := True;
end;

procedure TfrMovimientoAlmacenes.ActivarModoEntradaPedido;
begin
  TablaDetalleMem.FieldByName('CANTIDAD').OnSetText := CantidadDetalleSetText;
  TablaPedidos.AfterOpen := AfterRefreshPedidos;

  pnlDetalle.Visible := False;
  pnlArticulos.Visible := False;
  pnlPedido.Visible := False;
  pnlAlmacen.Visible := False;
  pnlAlmacenes.Visible := False;

  eCodigoAlmacen.Caption := etiMovCodigoAlmObrDestino;
  eNombreAlmacen.Caption := etiMovNombreAlmObrDestino;
  TipoOperacion.Text := etiMovEntrada;
  TipoMovimiento.Text := etiMovPedido;

  pnlPedido.Visible := True;
  pnlArticulos.Visible := True;
  pnlDetalle.Visible := True;

  CodCliente.Properties.ReadOnly := True;
  CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  cbAsociar.Checked := False;
  Causa.Clear;

  pnlBarraGrid.Visible := False;
  DesactivarCamposDetalle;
  DesActivarCampoCodigo;
  Visible := True;
end;

procedure TfrMovimientoAlmacenes.ActivarModoSalida;
begin
  TablaDetalleMem.FieldByName('CANTIDAD').OnSetText := CantidadDetalleSetText;
  cbxAlmacen.OnChange:= cbxAlmacenSalidaChange;

  pnlDetalle.Visible := False;
  pnlArticulos.Visible := False;
  pnlPedido.Visible := False;
  pnlAlmacen.Visible := False;
  pnlAlmacenes.Visible := False;

  TipoOperacion.Text := etiMovSalida;
  TipoMovimiento.Text := etiMovRegularizacion;

  pnlAlmacen.Visible := True;
  pnlArticulos.Visible := True;
  pnlDetalle.Visible := True;

  CodCliente.Properties.ReadOnly := True;
  CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  cbAsociar.Checked := False;

  Causa.Clear;
  Causa.Lines.Add(etiMovSalidaRegularizacion);

  pnlBarraGrid.Visible := True;
  DesactivarCamposDetalle;
  ActivarCampoCodigo;
  Visible := True;
end;

procedure TfrMovimientoAlmacenes.ActivarModoSalidaDevolucion;
begin
  TablaPedidos.AfterOpen := AfterRefreshPedidos;
  TablaDetalleMem.FieldByName('CANTIDAD').OnSetText := CantidadDetalleSetText;

  pnlDetalle.Visible := False;
  pnlArticulos.Visible := False;
  pnlPedido.Visible := False;
  pnlAlmacen.Visible := False;
  pnlAlmacenes.Visible := False;

  eCodigoAlmacen.Caption := etiMovCodigoAlmObrOrigen;
  eNombreAlmacen.Caption := etiMovNombreAlmObrOrigen;
  TipoOperacion.Text := etiMovSalida;
  TipoMovimiento.Text := etiMovDevolucion;

  pnlPedido.Visible := True;
  pnlArticulos.Visible := True;
  pnlDetalle.Visible := True;

  CodCliente.Properties.ReadOnly := True;
  CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  cbAsociar.Checked := False;
  Causa.Clear;

  pnlBarraGrid.Visible := False;
  DesactivarCamposDetalle;
  DesactivarCampoCodigo;
  Visible := True;
end;

procedure TfrMovimientoAlmacenes.bAnadirClick(Sender: TObject);
begin
  try
    TablaDetalleMem.Append;
    gridDetalles.SetFocus;
  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;
end;

procedure TfrMovimientoAlmacenes.bEliminarClick(Sender: TObject);
begin
  if (VerMensajePregunta(msgDeseaBorrar) <> IDYES) then
    Exit;
  try
    if TablaDetalleMem.RecordCount <> 0 then
      TablaDetalleMem.Delete
    else
      { Hacemos un cancel de la tabla por si el registro actual estuviera
        recien creado }
      TablaDetalleMem.Cancel;

    if TablaDetalleMem.RecordCount = 0 then
      DesactivarCamposDetalle;

  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;
end;

procedure TfrMovimientoAlmacenes.bElminarTodoClick(Sender: TObject);
begin
  if (VerMensajePregunta(msgDeseaBorrarTodo) <> IDYES) then
    Exit;
  BorrarLineasDetalle;
end;

procedure TfrMovimientoAlmacenes.SetCodigoArticulo(Value: Variant);
var
  DatosArticulo : TDatosArticulo;
begin
  inherited;
  if VarIsNull(Value) then
    Exit;

  DatosArticulo := TDatosArticulo.Create;

  if Modo in [Traslado, Salida, SalidaDevolucion] then
  begin
    if not EsCadenaVacia(CodigoAlmacenOrigen) then
      DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
  end
  else
  begin
    if not EsCadenaVacia(CodigoAlmacenDestino) then
      DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
  end;

  try
    DatosArticulo.Codigo := CodigoArticulo;
    dmTablaArticulos.darDatosArticulo(DatosArticulo);
    dmTablaArticulosAlmacen.DarDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo);

    with TablaDetalleMem do
    begin
      FieldByName('CODIGOARTICULO').AsString := DatosArticulo.Codigo;
      FieldByName('FAMILIA').AsString := DatosArticulo.Familia;
      FieldByName('DESCRIPCION').AsString := DatosArticulo.Descripcion;
      FieldByName('UNIDADESMEDIDA').AsString := DatosArticulo.Unidadesmedida;
      FieldByName('EXISTENCIAS').AsString := DatosArticulo.Existencias;
      FieldByName('CANTIDAD').AsString := '0';
      FieldByName('STOCKMIN').AsString := DatosArticulo.StockMin;
      FieldByName('STOCKMAX').AsString := DatosArticulo.StockMax;
      FieldByName('PRECIO').AsString := DatosArticulo.PrecioRecepcion;
      FieldByName('PRECIORECEPCION').AsString := DatosArticulo.PrecioRecepcion;
      FieldByName('FECHARECEPCION').AsString := DatosArticulo.FechaRecepcion;
    end;
  finally
    DatosArticulo.Free;
    ActivarCamposDetalle;
  end;
end;

procedure TfrMovimientoAlmacenes.FreeContenido;
var
  Contador : integer;
  ListaCodigos : TStringList;
begin
  if (ContenidoModal is TRdxFramePedidosProveedor) then
    CodigoPedido := (ContenidoModal as TRdxFramePedidosProveedor).CodigoPedido;

  if (ContenidoModal is TRdxFrameArticulosAlmacen) then
  begin
    if CodigoAlmacenOrigen = (ContenidoModal as TRdxFrameArticulosAlmacen).CodigoAlmacen then
    begin
      ListaCodigos := (ContenidoModal as TRdxFrameArticulosAlmacen).ListaArticulos;
      for Contador := 0 to ListaCodigos.Count - 1 do
      begin
        CodigoArticulo := ListaCodigos.Strings[Contador];
        bAnadirClick(Self);
      end;
      TablaDetalleMem.Cancel;
    end
    else
      VerMensaje(msgMovArtAlmIncorrecto);
  end;

  if (ContenidoModal is TRdxFrameArticulos) then
  begin
    ListaCodigos := (ContenidoModal as TRdxFrameArticulos).ListaArticulos;
    for Contador := 0 to ListaCodigos.Count - 1 do
    begin
      CodigoArticulo := ListaCodigos.Strings[Contador];
      bAnadirClick(Self);
    end;
    TablaDetalleMem.Cancel;
  end;

  if (ContenidoModal is TRdxFrameAlmacenes) then
  begin
    Case Modo of
      EntradaPedido : CodigoAlmacenDestino := (ContenidoModal as TRdxFrameAlmacenes).CodigoAlmacen;
      SalidaDevolucion : CodigoAlmacenOrigen := (ContenidoModal as TRdxFrameAlmacenes).CodigoAlmacen;
    end;
  end;

  if (ContenidoModal is TRdxFrameClientes) then
    CodigoCliente := (ContenidoModal as TRdxFrameClientes).CodigoCliente;

  inherited FreeContenido;
end;

procedure TfrMovimientoAlmacenes.BorrarLineasDetalle;
begin
  try
    TablaDetalleMem.DisableControls;
    TablaDetalleMem.Close;
    TablaDetalleMem.Open;
    TablaDetalleMem.EnableControls;
    DesactivarCamposDetalle;
  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;
end;

procedure TfrMovimientoAlmacenes.BuscarAlmacenOrigen;
var
  Datos : TDatosAlmacen;
  Existe : Boolean;
begin
  //Por seguridad, nunca se llamará a buscar almacen origen en una entrada
  if Modo in [Entrada, EntradaPedido] then
    Exit;

  Datos := TDatosAlmacen.Create;
  Datos.CodigoEmpresa := EmpresaActiva.Codigo;
  Datos.Codigo := CodigoAlmacenOrigen;
  Existe := dmTablaAlmacenes.darDatosAlmacen(Datos);
  CodigoCliente := Datos.CodigoCliente;

  Case Modo of
    Salida: cbxAlmacen.ItemIndex := cbxAlmacen.Items.IndexOf(FListaCodigosAlmacen.Values[CodigoAlmacenOrigen]);
    Traslado: cbxOrigen.ItemIndex := cbxOrigen.Items.IndexOf(FListaCodigosAlmacen.Values[CodigoAlmacenOrigen]);
    SalidaDevolucion : begin
                         CodigoAlmacen.Text := CodigoAlmacenOrigen;
                         NombreAlmacen.Text := Datos.Nombre;
                         Calle.Text := Datos.Calle;

                         if (length(CodigoAlmacenOrigen) <> 0) and (not Existe) then begin
                           VerMensajeFmt(msgAlmCodAlmNoExiste, [CodigoAlmacenOrigen]);
                           CodigoAlmacen.SetFocus;
                         end;
                       end;
  end;
  recalcularExistencias;
  Datos.free;
end;

procedure TfrMovimientoAlmacenes.BuscarAlmacenDestino;
var
  Datos : TDatosAlmacen;
  Existe : Boolean;
begin
  //Por seguridad, nunca se llamará a buscar almacen destino en una salida
  if Modo in [Salida, SalidaDevolucion] then
    Exit;

  Datos := TDatosAlmacen.Create;
  Datos.CodigoEmpresa := EmpresaActiva.Codigo;
  Datos.Codigo := CodigoAlmacenDestino;
  Existe := dmTablaAlmacenes.darDatosAlmacen(Datos);
  CodigoCliente := Datos.CodigoCliente;

  case Modo of
    Entrada: cbxAlmacen.ItemIndex := cbxAlmacen.Items.IndexOf(FListaCodigosAlmacen.Values[CodigoAlmacenDestino]);
    Traslado: cbxDestino.ItemIndex := cbxDestino.Items.IndexOf(FListaCodigosAlmacen.Values[CodigoAlmacenDestino]);
    EntradaPedido : begin
                      CodigoAlmacen.Text := CodigoAlmacenDestino;
                      NombreAlmacen.Text := Datos.Nombre;
                      Calle.Text := Datos.Calle;

                      if (length(CodigoAlmacenDestino) <> 0) and (not Existe) then
                      begin
                        VerMensajeFmt(msgAlmCodAlmNoExiste, [CodigoAlmacenDestino]);
                        Exit
                      end;
                    end;
  end;
  RecalcularExistencias;
  Datos.free;
end;

procedure TfrMovimientoAlmacenes.BuscarPedido;
var
  Datos : TDatosPedidoProveedor;
begin
  if VarIsNull(CodigoPedido) then
    Exit;

  try
    with TablaPedidos do
    begin
      Close;
      Params.ByName('CODIGO').AsString := CodigoPedido;
      Params.ByName('CODIGOEMPRESA').AsInteger := EmpresaActiva.Codigo;
      Prepare;
      Open;
      case Modo of
        EntradaPedido : Causa.Lines.Add(etiMovEntradaPedido + CodigoPedido);
        SalidaDevolucion : Causa.Lines.Add(etiMovSalidaDevolucion + CodigoPedido);
      end
    end;
  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;


  Datos := TDatosPedidoProveedor.Create;
  Datos.Codigo := CodigoPedido;
  Datos.CodigoEmpresa := EmpresaActiva.Codigo;

  // Aunque siempre va a existir por si concurrentemente de muy mala suerte alguien borra el pedido
  if not dmTablaPedidosProveedor.darDatosPedido(Datos) then
  begin
    VerMensajeFmt(msgMovNoExistePedido,[CodigoPedido]);
    CodigoAlmacen.SetFocus
  end
  else
  begin
    BorrarLineasDetalle;
    RellenarLineasDetalle;
  end;

  if EsCadenaVacia(Datos.CodigoAlmacen) then
  begin
    NombreAlmacen.Text := Datos.NombreAlmacen;
    Calle.Text := Datos.CalleAlmacen;
    exit;
  end;

  Case Modo of
    EntradaPedido : CodigoAlmacenDestino := Datos.CodigoAlmacen;
    SalidaDevolucion : CodigoAlmacenOrigen := Datos.CodigoAlmacen;
  end;
  Datos.free;
end;

procedure TfrMovimientoAlmacenes.BuscarCliente;
var
  Datos : TDatosCliente;
  Existe : Boolean;
begin
  if (Length(Trim(CodigoCliente)) = 0) then
  begin
    CodCliente.Text := '';
    NifCifCliente.Text := '';
    NombreCliente.Text := '';
    cbAsociar.Checked := False;
    CodCliente.Properties.Buttons[0].Visible := False;
    CodCliente.Properties.ReadOnly := True;
    CodCliente.Properties.OnValidate := nil;
    Exit;
  end;

  CodCliente.Text := CodigoCliente;
  Datos := TDatosCliente.Create;
  Datos.Codigo := CodigoCliente;
  Existe := dmTablaClientes.darDatosCliente(Datos);

  NifCifCliente.Text := Datos.Nifcif;
  NombreCliente.Text := Datos.Nombre;
  Datos.Free;

  if not Existe then
  begin
    VerMensajeFmt(msgMovNoExistePedido,[CodigoCliente]);
    CodCliente.SetFocus
  end
  else
    begin
      cbAsociar.Checked := True;
      CodCliente.Properties.Buttons[0].Visible := True;
      CodCliente.Properties.ReadOnly := False;
      CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
    end;
end;

procedure TfrMovimientoAlmacenes.cbxAlmacenOrigenChange(Sender: TObject);
begin
  CodigoAlmacenOrigen := FListaAlmacenesCodigo.Values[cbxOrigen.Text];
end;

procedure TfrMovimientoAlmacenes.cbxAlmacenDestinoChange(Sender: TObject);
begin
  CodigoAlmacenDestino := FListaAlmacenesCodigo.Values[cbxDestino.Text];
end;

procedure TfrMovimientoAlmacenes.cbxAlmacenEntradaChange(Sender: TObject);
begin
  CodigoAlmacenDestino := FListaAlmacenesCodigo.Values[cbxAlmacen.Text];
end;

procedure TfrMovimientoAlmacenes.cbxAlmacenSalidaChange(Sender: TObject);
begin
  CodigoAlmacenOrigen := FListaAlmacenesCodigo.Values[cbxAlmacen.Text];
end;

procedure TfrMovimientoAlmacenes.cbAsociarClick(Sender: TObject);
begin
  if cbAsociar.Checked = True then
  begin
    CodigoCliente := NULL;
    CodCliente.Clear;
    NifCifCliente.Clear;
    NombreCliente.Clear;
    CodCliente.Properties.Buttons[0].Visible := False;
    CodCliente.Properties.ReadOnly := True;
    CodCliente.Properties.OnValidate := nil;
  end
  else begin
    CodCliente.Properties.Buttons[0].Visible := True;
    CodCliente.Properties.ReadOnly := False;
    CodCliente.Properties.OnValidate := CodClientePropertiesValidate;
  end
end;

function TfrMovimientoAlmacenes.EjecutarTraslado: Boolean;
var
  DatosArticulo : TDatosArticulo;
  DatosMovimiento : TDatosMovimiento;
  Cantidad : String;
begin
  Result := False;
  if Transaccion = NIL then
    exit;

  try

    TablaDetalleMem.DisableControls;
    if not RealizarSalida then
    begin
      Rollback;
      TablaDetalleMem.EnableControls;
      Exit
    end;

    if not RealizarEntrada then
    begin
      Rollback;
      TablaDetalleMem.EnableControls;
      Exit
    end;

    Commit;
    BorrarLineasDetalle;
    Result := True;
  except
    on E : EIBError do
    begin
      case E.IBErrorCode of
        isc_lock_conflict : begin
                              Rollback;
                              VerMensaje(msgAlmBloqueado);
                            end
        else begin
          Rollback;
          TratarExcepcion(E);
        end;
      end;
    end;
    on E : Exception do begin
      Rollback;
      TratarExcepcion(E);
    end;
  end;
  TablaDetalleMem.EnableControls;
end;

function TfrMovimientoAlmacenes.RealizarEntrada: Boolean;
{Se utiliza una misma transaccion para todas las operaciones, se comprueba si
 el almacén destino sigue existiendo (por si en el periodo de introduccion de
 los articulos otro usuario a eliminado el almacen y todos sus articulos), para
 cada articulo comprobamos si existe articulos por si al igual que en el caso
 anterior ha sido dado de baja en cuyo caso se avisara al usuario y se saltará
 el movimiento de ese artículo, en el caso que la cantidad del articulo a insertar
 sea 0 se insertará con dicha cantidad}

var
  CodigoPedido : String;
  FechaRecep : String;
  DatosArticulo : TDatosArticuloAlmacen;
  DatosDetallePedidoProveedor : TDatosDetallePedidoProveedor;
begin
  Result := False;

  CodigoPedido := CodPedido.Text;
  FechaRecep := FechaRecepcion.Text;

  try
    DatosDetallePedidoProveedor := TDatosDetallePedidoProveedor.Create;
    DatosDetallePedidoProveedor.CodigoPedido := CodigoPedido;
    DatosDetallePedidoProveedor.CodigoEmpresa := EmpresaActiva.Codigo;

    DatosArticulo := TDatosArticuloAlmacen.Create;

    //Datos generales del movimiento de cada artículo
    DatosArticulo.TipoOperacion := etiMovEntrada;
    DatosArticulo.TipoMovimiento := TipoMovimiento.Text;

    //Para saber de donde se han recibido los artículos en un entrada por traslado
    if Modo = Traslado then
      DatosArticulo.Causa := etiMovIniTrasladoEnt + CodigoAlmacenOrigen
                             + etiMovMedTraslado + FListaCodigosAlmacen.Values[CodigoAlmacenOrigen]
                             + etiMovFinTraslado
    else
      DatosArticulo.Causa := Causa.Lines.Text;

    DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
    DatosArticulo.NombreCliente := NombreCliente.Text;

    if Modo = EntradaPedido then
      if not dmTablaPedidosProveedor.BloquearPedido(EmpresaActiva.Codigo, CodigoPedido, FechaRecep) then
      begin
        VerMensajeFmt(msgMovPedidoRecibido,[CodigoPedido]);
        exit;
      end;

    // Para cada articulo
    TablaDetalleMem.First;
    while not TablaDetalleMem.Eof do
    begin
      {En el caso de entrada de pedido se tiene que recalcular las unidades
       pendientes de recibir de cada uno de los articulos del pedido}
      if Modo = EntradaPedido then
      begin
        DatosDetallePedidoProveedor.CodigoEmpresa := EmpresaActiva.Codigo;
        DatosDetallePedidoProveedor.CodigoArticulo := TablaDetalleMem.FieldByName('CODIGOARTICULO').AsString;;
        DatosDetallePedidoProveedor.NumConcepto := TablaDetalleMem.FieldByName('NUMCONCEPTO').AsInteger;
        DatosDetallePedidoProveedor.Cantidad := TablaDetalleMem.FieldByName('CANTIDAD').AsString;

        dmTablaPedidosProveedor.ModificarUnidadesPendientesArticulo(DatosDetallePedidoProveedor);
      end;

      DatosArticulo.Codigo := TablaDetalleMem.FieldByName('CODIGOARTICULO').AsString;
      //Solo modifico existencias de articulos existentes en l aplicación
      if not esCadenaVacia (DatosArticulo.Codigo) then begin
        //Aprovecho el campo existencias de la estructura de datos de articulo
        //para darle en el la cantidad
        DatosArticulo.Existencias := TablaDetalleMem.FieldByName('CANTIDAD').AsString;
        DatosArticulo.StockMin := TablaDetalleMem.FieldByName('STOCKMIN').AsString;
        DatosArticulo.StockMax := TablaDetalleMem.FieldByName('STOCKMAX').AsString;

        //Comprobamos el precio del articulo si existe en almacén para asignarle el
        //nuevo precio o mantener el que tiene
        if (TablaDetalleMem.FieldByName('FECHARECEPCION').AsDateTime < StrToDate(FechaRecep)) then
        begin
           DatosArticulo.PrecioRecepcion := TablaDetalleMem.FieldByName('PRECIO').AsString;
           DatosArticulo.FechaRecepcion := FechaRecep
        end
        else
        begin
           DatosArticulo.PrecioRecepcion := TablaDetalleMem.FieldByName('PRECIORECEPCION').AsString;
           DatosArticulo.FechaRecepcion := TablaDetalleMem.FieldByName('FECHARECEPCION').AsString;
        end;

        dmTablaArticulosAlmacen.ModificarExistenciasEntrada(EmpresaActiva.Codigo, DatosArticulo);
      end;
      TablaDetalleMem.Next;
    end;

    //Cambiamos de situacion el pedido si ya no hay unidades pendientes de recibir
    if Modo = EntradaPedido then
    begin
      if dmTablaPedidosProveedor.darUnidadesPendientes(EmpresaActiva.Codigo, CodigoPedido) = '0' then
        dmTablaPedidosProveedor.RecibirTotalmentePedido(EmpresaActiva.Codigo, CodigoPedido, FechaRecep)
      else
        dmTablaPedidosProveedor.RecibirParcialmentePedido(EmpresaActiva.Codigo, CodigoPedido, FechaRecep);
    end;
    Result := True;

  finally
    DatosArticulo.free;
    DatosDetallePedidoProveedor.free;
  end;
end;

function TfrMovimientoAlmacenes.RealizarSalida: Boolean;
var
  CodigoPedido : String;
  FechaDevolucion : String;
  DatosArticulo : TDatosArticuloAlmacen;
  DatosDetallePedidoProveedor : TDatosDetallePedidoProveedor;
begin
  Result := False;

  //No es necesaria la fecha de devolución pero por si en un futuro la piden
  FechaDevolucion := FormatDateTime('dd/mm/yyyy', Now);

  CodigoPedido := CodPedido.Text;

  try
    DatosDetallePedidoProveedor := TDatosDetallePedidoProveedor.Create;
    DatosDetallePedidoProveedor.CodigoPedido := CodigoPedido;
    DatosDetallePedidoProveedor.CodigoEmpresa := EmpresaActiva.Codigo;    

    DatosArticulo := TDatosArticuloAlmacen.Create;

    //Datos generales del movimiento de cada artículo
    DatosArticulo.TipoOperacion := etiMovSalida;
    DatosArticulo.TipoMovimiento := TipoMovimiento.Text;

    //Para saber de donde se han ido los artículo en un salida por traslado
    if Modo = Traslado then
      DatosArticulo.Causa := etiMovIniTrasladoSal + CodigoAlmacenDestino
                             + etiMovMedTraslado + FListaCodigosAlmacen.Values[CodigoAlmacenDestino]
                             + etiMovFinTraslado
    else
      DatosArticulo.Causa := Causa.Lines.Text;

    DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
    DatosArticulo.NombreCliente := NombreCliente.Text;

    if Modo = SalidaDevolucion then
      if not dmTablaPedidosProveedor.BloquearPedido(EmpresaActiva.Codigo, CodigoPedido, FechaDevolucion) then
      begin
        VerMensajeFmt(msgMovPedidoDevuelto,[CodigoPedido]);
        exit;
      end;

    // Para cada articulo
    TablaDetalleMem.First;
    while not TablaDetalleMem.Eof do
    begin
      DatosArticulo.Codigo := TablaDetalleMem.FieldByName('CODIGOARTICULO').AsString;
      //Solo modifico existencias de articulos existentes en la aplicación
      if length(DatosArticulo.Codigo) <> 0 then begin
        //Aprovecho el campo existencias de la estructura de datos de articulo
        //para darle en el la cantidad
        DatosArticulo.Existencias := TablaDetalleMem.FieldByName('CANTIDAD').AsString;
        DatosArticulo.StockMin := TablaDetalleMem.FieldByName('STOCKMIN').AsString;
        DatosArticulo.StockMax := TablaDetalleMem.FieldByName('STOCKMAX').AsString;

        dmTablaArticulosAlmacen.ModificarExistenciasSalida(EmpresaActiva.Codigo, DatosArticulo);
      end;
      TablaDetalleMem.Next;
    end;

    if Modo = SalidaDevolucion then
      dmTablaPedidosProveedor.DevolverPedido(EmpresaActiva.Codigo, CodigoPedido, FechaDevolucion);

    Result:= True;
  except
    on E : Exception do begin
      raise;
    end;
  end;
  DatosArticulo.free;
  DatosDetallePedidoProveedor.free;
end;

procedure TfrMovimientoAlmacenes.bAceptarClick(Sender: TObject);
begin
  if not ComprobarDatos then
    Exit;

  Case Modo of
    Entrada,
    EntradaPedido     : if not EjecutarEntrada then Exit;
    Salida,
    SalidaDevolucion  : if not EjecutarSalida then Exit;
    Traslado          : if not EjecutarTraslado then Exit;
  end;
  CloseFrame;
end;

procedure TfrMovimientoAlmacenes.bCancelarClick(Sender: TObject);
begin
  inherited;
  CloseFrame;
end;

procedure TfrMovimientoAlmacenes.RellenarLineasDetalle;
var
  oSQL : TIBSQL;
  DatosArticulo : TDatosArticulo;
begin
  oSQL := TIBSQL.Create(Self);

  with oSQL do
  begin
    Database := dmBaseDatos.BD;
    Transaction := Transaccion;
    SQL.Add('select CODIGOALMACEN, CODIGOPEDIDO, CODIGOARTICULO, NUMCONCEPTO, DESCRIPCION, ');
    SQL.Add('CANTIDAD, PENDIENTES, PRECIO ');
    SQL.Add('from PEDIDOSPROVEEDOR PED, DETALLESPEDIDOSPROVEEDOR DEP');
    SQL.Add('where (PED.CODIGOEMPRESA = DEP.CODIGOEMPRESA)');
    SQL.Add('and (CODIGO = CODIGOPEDIDO)');
    SQL.Add('and (CODIGOPEDIDO = :CODIGOPEDIDO) and ');
    SQL.Add('(PED.CODIGOEMPRESA = :CODIGOEMPRESA) ');
    SQL.Add('order by NUMCONCEPTO descending');
  end;

  try
    with oSQL do
    begin
      ParamByName('CODIGOEMPRESA').AsInteger := EmpresaActiva.Codigo;
      ParamByName('CODIGOPEDIDO').AsString := CodigoPedido;
      Prepare;
      ExecQuery;

      // Relleno la estructura de datos con el almacén sobre el que se va a
      // realizar el movimiento
      DatosArticulo := TDatosArticulo.Create;
      //Por defecto se carga el almacen que tenga seleccionado el pedido
      DatosArticulo.CodigoAlmacen := FieldByName('CODIGOALMACEN').AsString;
      Case Modo of
        EntradaPedido   : if not VarIsNull(CodigoAlmacenDestino) then
                            DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
        SalidaDevolucion: if not VarIsNull(CodigoAlmacenOrigen) then
                            DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
      end;

      TablaDetalleMem.DisableControls;
      while not EOF do
      begin

        DatosArticulo.Codigo := FieldByName('CODIGOARTICULO').AsString;
        // Recupera los datos del articulo, si no existe le asigna la
        // descripcion de ese artículo en el pedido
        if not dmTablaArticulos.darDatosArticulo(DatosArticulo) then
          DatosArticulo.Descripcion := FieldByName('Descripcion').AsString;

        dmTablaArticulosAlmacen.darDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo);

        TablaDetalleMem.Insert;
        TablaDetalleMem.FieldByName('CODIGOPEDIDO').AsString := FieldByName('CODIGOPEDIDO').AsString;
        TablaDetalleMem.FieldByName('CODIGOARTICULO').AsString := DatosArticulo.Codigo;
        TablaDetalleMem.FieldByName('NUMCONCEPTO').AsInteger := FieldByName('NUMCONCEPTO').AsInteger;
        TablaDetalleMem.FieldByName('FAMILIA').AsString := DatosArticulo.Familia;
        TablaDetalleMem.FieldByName('DESCRIPCION').AsString := DatosArticulo.Descripcion;
        TablaDetalleMem.FieldByName('UNIDADESMEDIDA').AsString := DatosArticulo.Unidadesmedida;
        TablaDetalleMem.FieldByName('EXISTENCIAS').AsString := DatosArticulo.Existencias;
        TablaDetalleMem.FieldByName('STOCKMIN').AsString := DatosArticulo.StockMin;
        TablaDetalleMem.FieldByName('STOCKMAX').AsString := DatosArticulo.StockMax;
        TablaDetalleMem.FieldByName('PRECIO').AsString := FieldByName('PRECIO').AsString;
        TablaDetalleMem.FieldByName('PRECIORECEPCION').AsString := DatosArticulo.PrecioRecepcion;
        TablaDetalleMem.FieldByName('FECHARECEPCION').AsString := DatosArticulo.FechaRecepcion;

        Case Modo of
          EntradaPedido   : begin
                              TablaDetalleMem.FieldByName('CANTIDAD').AsFloat := FieldByName('PENDIENTES').AsFloat;
                              TablaDetalleMem.FieldByName('PENDIENTES').AsFloat := FieldByName('PENDIENTES').AsFloat;
                            end;
          SalidaDevolucion: begin
                             TablaDetalleMem.FieldByName('CANTIDAD').AsFloat := FieldByName('CANTIDAD').AsFloat;
                             TablaDetalleMem.FieldByName('PENDIENTES').AsFloat := FieldByName('CANTIDAD').AsFloat;
                            end;
        end;

        TablaDetalleMem.Post;
        Next;
      end;
      TablaDetalleMem.EnableControls;
      if (Modo <> SalidaDevolucion) AND (TablaDetalleMem.RecordCount > 0) then
        ActivarCamposDetalle;
    end;
  except
    on E : EIBError do
      TratarExcepcion(E);
    on E : Exception do
      TratarExcepcion(E);
  end;
  oSQL.Close;
  oSQL.Free;
end;

procedure TfrMovimientoAlmacenes.CambiarModo(ModoAnterior, Modo: TRdxModo);
var
  i : Integer;
begin
  BorrarLineasDetalle;

  Case Entidad of
    entArticulosAlmacen : dmTablaAlmacenes.darAlmacenes(EmpresaActiva.Codigo, FListaAlmacenesCodigo,FListaCodigosAlmacen);
    entArticulosAlmacenFisico : dmTablaAlmacenes.darAlmacenesFisicos(EmpresaActiva.Codigo, FListaAlmacenesCodigo,FListaCodigosAlmacen);
    entArticulosAlmacenObra : dmTablaAlmacenes.darAlmacenesObras(EmpresaActiva.Codigo, FListaAlmacenesCodigo,FListaCodigosAlmacen);
  end;

  Case Modo of
  Entrada,
  Salida   : begin
               cbxAlmacen.Items.Clear;
               for i:=0 to FListaAlmacenesCodigo.Count-1 do
                 cbxAlmacen.Items.Add(FListaAlmacenesCodigo.Names[i]);

               cbxAlmacen.ItemIndex := 0;
               if Modo = Entrada
               then CodigoAlmacenDestino := FListaAlmacenesCodigo.Values[cbxAlmacen.Text]
               else CodigoAlmacenOrigen := FListaAlmacenesCodigo.Values[cbxAlmacen.Text]
             end;
  Traslado : begin
               //En el almacén origen se estableceran los fisicos o de obras dependiendo de la entidad en la que estemos
               cbxOrigen.Items.Clear;
               cbxDestino.Items.Clear;
               for i:=0 to FListaAlmacenesCodigo.Count-1 do
                 cbxOrigen.Items.Add(FListaAlmacenesCodigo.Names[i]);

               //En el almacén destino se establecen todos los almacenes
               dmTablaAlmacenes.darAlmacenes(EmpresaActiva.Codigo, FListaAlmacenesCodigo,FListaCodigosAlmacen);
               for i:=0 to FListaAlmacenesCodigo.Count-1 do
                 cbxDestino.Items.Add(FListaAlmacenesCodigo.Names[i]);

               cbxOrigen.ItemIndex := 0;
               cbxDestino.ItemIndex := 0;
               CodigoAlmacenOrigen := FListaAlmacenesCodigo.Values[cbxOrigen.Text];
               CodigoAlmacenDestino := FListaAlmacenesCodigo.Values[cbxDestino.Text];
             end;
  end;
  inherited;
end;

function TfrMovimientoAlmacenes.ComprobarDatos: Boolean;
begin
  Result := False;

  if (TablaDetalleMem.RecordCount = 0) then
  begin
    VerMensaje(msgMovNoArticulos);
    Exit;
  end;

  case Modo of
  Entrada         : begin
                      if EsCadenaVacia(CodigoAlmacenDestino) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenDestino);
                        Exit;
                      end;
                    end;
  EntradaPedido   : begin
                      if EsCadenaVacia(CodigoAlmacenDestino) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenDestino);
                        CodigoAlmacen.SetFocus;
                        Exit;
                      end;

                      if EsCadenaVacia(FechaRecepcion.Text) then
                      begin
                        VerMensaje(msgPedFaltaFecRecepcion);
                        FechaRecepcion.SetFocus;
                        Exit;
                      end;

                    end;
  Salida          : begin
                      if EsCadenaVacia(CodigoAlmacenOrigen) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenOrigen);
                        Exit;
                      end;
                    end;
  SalidaDevolucion: begin
                      if EsCadenaVacia(CodigoAlmacenOrigen) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenOrigen);
                        CodigoAlmacen.SetFocus;
                        Exit;
                      end;
                    end;
  Traslado        : begin
                      if EsCadenaVacia(CodigoAlmacenDestino) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenDestino);
                        cbxDestino.SetFocus;
                        Exit;
                      end;
                      if EsCadenaVacia(CodigoAlmacenOrigen) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenOrigen);
                        cbxOrigen.SetFocus;
                        Exit;
                      end;
                      if (CodigoAlmacenOrigen = CodigoAlmacenDestino) then
                      begin
                       VerMensaje(msgMovMismoAlmacen);
                       cbxOrigen.SetFocus;
                       Exit;
                      end;
                    end;
  end;
  Result:= True;
end;

procedure TfrMovimientoAlmacenes.ActivarCamposDetalle;
{Activa las columnas del grid de detalle que nos interesan}
begin
  //Si ya están activados salimos
  if gridDetalles.ColumnByFieldName('CANTIDAD').DisableEditor then
  begin
    // Para todos los modos Cantidad la activamos
    gridDetalles.ColumnByFieldName('CANTIDAD').DisableEditor := False;

    if Modo = Entrada then
    begin
      // StockMin, StockMax
      gridDetalles.ColumnByFieldName('STOCKMIN').DisableEditor := False;
      gridDetalles.ColumnByFieldName('STOCKMAX').DisableEditor := False;
    end;
  end;
end;

procedure TfrMovimientoAlmacenes.DesactivarCamposDetalle;
{Desactiva las columnas del grid de detalle que nos interesan}
begin
  // Para todos los modos Cantidad la desactivamos
  gridDetalles.ColumnByFieldName('DESCRIPCION').DisableEditor := True;
  gridDetalles.ColumnByFieldName('EXISTENCIAS').DisableEditor := True;
  gridDetalles.ColumnByFieldName('CANTIDAD').DisableEditor := True;
  gridDetalles.ColumnByFieldName('STOCKMIN').DisableEditor := True;
  gridDetalles.ColumnByFieldName('STOCKMAX').DisableEditor := True;
end;

procedure TfrMovimientoAlmacenes.GridBotonClick(Sender: TObject);
{Controla cuando se introduce mediante el boton derecho un articulo en el grid detalle}
var
  Campo : TField;
  NombreCampo : String;
begin
    TablaDetalleMem.Edit;
    Case Modo of
    Entrada,
    EntradaPedido : begin
                      EntidadModal := entArticulo;
                      ContenidoModal := TfrArticulos.Create(Self);
                    end;
    Salida,
    SalidaDevolucion,
    Traslado      : begin
                      if VarIsNull(CodigoAlmacenOrigen) or EsCadenaVacia(CodigoAlmacenOrigen) then
                      begin
                        VerMensaje(msgMovFaltaAlmacenOrigen);
                        Exit;
                      end;
                      //dependiendo del tipo de entidad en el que nos encontremos
                      //entAlmacen,AlmacenFisico o de obra se adaptará
                      EntidadModal := Entidad;
                      ContenidoModal := TfrArticulosAlmacen.Create(Self);
                    end;
    end;
end;

procedure TfrMovimientoAlmacenes.CodigoArticuloDetalleSetText(Sender: TField; const Text: String);
{Controla cuando se introduce manualmente un codigo de articulo en el grid detalle}
var
  Codigo : String;
  DatosArticulo : TDatosArticulo;
begin
  Codigo := Trim(Text);
  if EsCadenaVacia(Codigo) then
    exit;

  if (dmTablaArticulos.validarCodigo(Codigo)) then
    Codigo := dmTablaArticulos.formatearCodigo(Codigo)
  else
    raise Exception.CreateFmt(msgArtCodArtIncorrecto, [Codigo]);

  DatosArticulo := TDatosArticulo.Create;
  DatosArticulo.Codigo := Codigo;

  Case Modo of
  Entrada,
  EntradaPedido    : DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
  Salida,
  SalidaDevolucion,
  Traslado         : Begin
                       //En el caso de salida se requiere un almacen origen
                       if VarIsNull(codigoAlmacenOrigen) or EsCadenaVacia(CodigoAlmacenOrigen) then
                       begin
                         verMensaje(msgMovFaltaAlmacenOrigen);
                         Exit;
                       end;
                       DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
                     end;
  end;

  try
    Case Modo of
    Entrada,
    EntradaPedido: begin
                    if not dmTablaArticulos.darDatosArticulo(DatosArticulo) then
                      raise Exception.CreateFmt(msgArtCodArtNoExiste, [Codigo]);
                    dmTablaArticulosAlmacen.DarDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo);
                   end;
    Salida,
    SalidaDevolucion,
    Traslado     :  begin
                     dmTablaArticulos.darDatosArticulo(DatosArticulo);
                     if not dmTablaArticulosAlmacen.DarDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo) then
                       raise Exception.CreateFmt(msgMovCodArtAlmNoExiste,[DatosArticulo.Codigo, DatosArticulo.Descripcion, FListaCodigosAlmacen.Values[CodigoAlmacenOrigen]]);
                    end;
    end;
    with TablaDetalleMem do
    begin
      FieldByName('CODIGOARTICULO').AsString := DatosArticulo.Codigo;
      FieldByName('FAMILIA').AsString := DatosArticulo.Familia;
      FieldByName('DESCRIPCION').AsString := DatosArticulo.Descripcion;
      FieldByName('UNIDADESMEDIDA').AsString := DatosArticulo.Unidadesmedida;
      FieldByName('EXISTENCIAS').AsString := DatosArticulo.Existencias;
      FieldByName('CANTIDAD').AsString := '0';
      FieldByName('STOCKMIN').AsString := DatosArticulo.StockMin;
      FieldByName('STOCKMAX').AsString := DatosArticulo.StockMax;
      FieldByName('PRECIO').AsString := DatosArticulo.PrecioRecepcion;
      FieldByName('PRECIORECEPCION').AsString := DatosArticulo.PrecioRecepcion;
      FieldByName('FECHARECEPCION').AsString := DatosArticulo.FechaRecepcion;
    end;
    Sender.AsString := Codigo;
  finally
    DatosArticulo.Free;
  end;
  ActivarCamposDetalle;

//Para que el campo introducido se guarde
  TablaDetalleMem.Append;
  TablaDetalleMem.Cancel;
  gridDetalles.SetFocus;
end;

procedure TfrMovimientoAlmacenes.CantidadDetalleSetText(Sender: TField; const Text: String);
{Controla cuando se introduce manualmente una cantidad en el grid detalle}
begin
  try
    if Length(Text) = 0 then
    begin
      Sender.AsString := '0';
      Exit;
    end;

    if StrToFloat(Text) < 0 then
      raise Exception.Create(msgMovCantMayorCero);

    if Modo in [Salida, SalidaDevolucion, Traslado] then
      if StrToFloat(Text) > TablaDetalleMem.FieldByName('EXISTENCIAS').AsFloat then
        raise Exception.Create(msgMovNoExistencias);

    if Modo = EntradaPedido then
      if StrToFloat(Text) > TablaDetalleMem.FieldByName('PENDIENTES').AsFloat then
        raise Exception.CreateFmt(msgMovCantRecibidaMayor,[TablaDetalleMem.FieldByName('PENDIENTES').AsString]);

    Sender.AsString := Text
  except
    on E : EConvertError do
      VerMensaje(msgCantidadNoValida);
    on E : EDataBaseError do
      VerMensaje(msgCantidadNoValida);
  end;
end;

procedure TfrMovimientoAlmacenes.RecalcularExistencias;
var
  DatosArticulo : TDatosArticulo;
begin
  if (TablaDetalleMem.RecordCount = 0) then
     Exit;

  DatosArticulo := TDatosArticulo.Create;
  case Modo of
    Entrada,
    EntradaPedido: begin
                     if (VarIsNull(CodigoAlmacenDestino)) then
                       Exit;
                     DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
                   end;
    Salida,
    SalidaDevolucion,
    Traslado:      begin
                     if (VarIsNull(CodigoAlmacenOrigen)) then
                       Exit;
                     DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
                   end;
  end;

  //En el caso de que el almacén no este asignado
  if length(DatosArticulo.CodigoAlmacen) = 0 then
  begin
    TablaDetalleMem.DisableControls;
    TablaDetalleMem.first;
    while not TablaDetalleMem.Eof do
    begin
      TablaDetalleMem.Edit;
      TablaDetalleMem.FieldByName('EXISTENCIAS').AsString := '0';
      TablaDetalleMem.FieldByName('STOCKMIN').AsString := '0';
      TablaDetalleMem.FieldByName('STOCKMAX').AsString := '1000';
      TablaDetalleMem.FieldByName('PRECIORECEPCION').AsString := '0';
      TablaDetalleMem.FieldByName('FECHARECEPCION').AsString := '';
      TablaDetalleMem.Post;
      TablaDetalleMem.Next;
    end;
    TablaDetalleMem.First;
    TablaDetalleMem.EnableControls;
    Exit;
  end;

  //Reestablece los valores actuales de todos los artículo de la lista en el
  //almacén dado
  TablaDetalleMem.DisableControls;
  TablaDetalleMem.First;
  while not TablaDetalleMem.Eof do
  begin
    DatosArticulo.Codigo := TablaDetalleMem.FieldByName('CODIGOARTICULO').AsString;
    DatosArticulo.Descripcion := TablaDetalleMem.FieldByName('DESCRIPCION').AsString;
    dmTablaArticulosAlmacen.darDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo);

    TablaDetalleMem.Edit;
    TablaDetalleMem.FieldByName('EXISTENCIAS').AsString := DatosArticulo.Existencias;
    TablaDetalleMem.FieldByName('STOCKMIN').AsString := DatosArticulo.StockMin;
    TablaDetalleMem.FieldByName('STOCKMAX').AsString := DatosArticulo.StockMax;

    if Modo = Traslado then
    begin
    DatosArticulo.CodigoAlmacen := CodigoAlmacenDestino;
    dmTablaArticulosAlmacen.darDatosArticuloAlmacen(EmpresaActiva.Codigo, DatosArticulo);
    TablaDetalleMem.FieldByName('PRECIORECEPCION').AsString := DatosArticulo.PrecioRecepcion;
    TablaDetalleMem.FieldByName('FECHARECEPCION').AsString := DatosArticulo.FechaRecepcion;
    DatosArticulo.CodigoAlmacen := CodigoAlmacenOrigen;
    end
    else
    begin
      TablaDetalleMem.FieldByName('PRECIORECEPCION').AsString := DatosArticulo.PrecioRecepcion;
      TablaDetalleMem.FieldByName('FECHARECEPCION').AsString := DatosArticulo.FechaRecepcion;
    end;

    TablaDetalleMem.Post;

    TablaDetalleMem.Next;
  end;
  TablaDetalleMem.First;
  TablaDetalleMem.EnableControls;
end;

procedure TfrMovimientoAlmacenes.StocksDetalleSetText(Sender: TField;const Text: String);
begin
  try
    if Length(Text) = 0 then
    begin
      Sender.AsString := '0';
      Exit;
    end;

    if StrToFloat(Text) < 0 then
      raise Exception.Create(msgMovStockMayorCero);

    Sender.AsString := Text
  except
    on E : EConvertError do
      VerMensaje(msgMovStockNoValido);
    on E : EDataBaseError do
      VerMensaje(msgMovStockNoValido);
  end;
end;

procedure TfrMovimientoAlmacenes.gridDetallesExit(Sender: TObject);
begin
  if TablaDetalleMem.State in [dsEdit, dsInsert] then
    TablaDetalleMem.Post;
end;

function TfrMovimientoAlmacenes.EjecutarEntrada: Boolean;
{Se utiliza una misma transaccion para todas las operaciones, se comprueba si
 el almacén destino sigue existiendo (por si en el periodo de introduccion de
 los articulos otro usuario a eliminado el almacen y todos sus articulos), para
 cada articulo comprobamos si existe articulos por si al igual que en el caso
 anterior ha sido dado de baja en cuyo caso se avisara al usuario y se saltará
 el movimiento de ese artículo, en el caso que la cantidad del articulo a insertar
 sea 0 se insertará con dicha cantidad}
begin
  Result := False;
  if Transaccion = NIL then
    exit;

  try
    TablaDetalleMem.DisableControls;
    if not RealizarEntrada then
    begin
      Rollback;
      TablaDetalleMem.EnableControls;
      Exit
    end;

    Commit;
    BorrarLineasDetalle;
    Result := True;
  except
    on E : EIBError do
    begin
      case E.IBErrorCode of
        isc_foreign_key : begin
                            Rollback;
                            VerMensaje(msgMovNoRealizado);
                          end;
        isc_lock_conflict : begin
                              Rollback;
                              VerMensaje(msgAlmBloqueado);
                            end
        else begin
          Rollback;
          TratarExcepcion(E);
        end;
      end;
    end;
    on E : Exception do begin
      Rollback;
      TratarExcepcion(E);
    end;
  end;
  TablaDetalleMem.EnableControls;
end;

function TfrMovimientoAlmacenes.EjecutarSalida: Boolean;
begin
  Result := False;
  if Transaccion = NIL then
    exit;

  try
    TablaDetalleMem.DisableControls;
    if not RealizarSalida then
    begin
      Rollback;
      TablaDetalleMem.EnableControls;
      Exit
    end;

    Commit;
    BorrarLineasDetalle;
    Result := True;
  except
    on E : EIBError do
    begin
      case E.IBErrorCode of
        isc_lock_conflict : begin
                              Rollback;
                              VerMensaje(msgAlmBloqueado);
                            end
        else begin
          Rollback;
          TratarExcepcion(E);
        end;
      end;
    end;
    on E : Exception do begin
      Rollback;
      TratarExcepcion(E);
    end;
  end;
  TablaDetalleMem.EnableControls;
end;

procedure TfrMovimientoAlmacenes.ActivarCampoCodigo;
begin
  gridDetalles.ColumnByFieldName('CODIGOARTICULO').DisableEditor := False;
//  gridDetalles.InsertKey := True;
//  gridDetalles.DeleteKey := True;
end;

procedure TfrMovimientoAlmacenes.DesactivarCampoCodigo;
begin
  gridDetalles.ColumnByFieldName('CODIGOARTICULO').DisableEditor := True;
//  gridDetalles.InsertKey := False;
//  gridDetalles.DeleteKey := False;
end;

procedure TfrMovimientoAlmacenes.AfterRefreshPedidos(DataSet: TDataSet);
var
  FechaTabla : String;
begin
  FechaTabla := DataSet.FieldByName('FECHARECEPCION').AsString;

  if Modo = SalidaDevolucion then
  begin
    FechaRecepcion.Date := DataSet.FieldByName('FECHARECEPCION').AsDateTime;
    FechaRecepcion.Properties.ReadOnly := True;
    Exit;
  end;

  if not EsCadenaVacia(FechaTabla) then
  begin
    FechaRecepcion.Date := DataSet.FieldByName('FECHARECEPCION').AsDateTime;
    FechaRecepcion.Properties.ReadOnly := True;
  end
  else begin
    FechaRecepcion.Properties.ReadOnly := False;
    if EsFechaNula(FechaRecepcion.Date) then
      FechaRecepcion.Date := Date;
  end;
end;

function TfrMovimientoAlmacenes.CambiarEntidad(EntidadAnterior,Entidad: TRdxEntidad): Boolean;
begin
  if not inherited CambiarEntidad(EntidadAnterior, Entidad) then
    Exit;

  ConfigurarFrame(Self, Self.Entidad);
      
  Case Entidad of
    entArticulosAlmacenObra   : begin
                                 cAlmacen.Caption := 'Obra';
                                 eAlmacen.Caption := 'Obra:';
                                end;
    entArticulosAlmacen       : begin
                                 cAlmacen.Caption := 'Almacén/Obra';
                                 eAlmacen.Caption := 'Almacén/Obra:';
                                end;
  end;
end;

procedure TfrMovimientoAlmacenes.CodPedidoPropertiesButtonClick(
  Sender: TObject; AButtonIndex: Integer);
begin
  ModoModal := Recibir;
  ContenidoModal := TfrPedidosProveedores.Create(Self);
end;

procedure TfrMovimientoAlmacenes.CodigoAlmacenPropertiesButtonClick(
  Sender: TObject; AButtonIndex: Integer);
begin
  Case Entidad of
    entArticulosAlmacenFisico: EntidadModal := entAlmacenFisico;
    entArticulosAlmacenObra: EntidadModal := entAlmacenObra;
    entArticulosAlmacen: EntidadModal := entAlmacen;
  end;
  ContenidoModal := TfrAlmacenes.Create(Self);
end;

procedure TfrMovimientoAlmacenes.CodigoAlmacenPropertiesValidate(
  Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption;
  var Error: Boolean);
begin
  //Por si se deja vacio el campo
  if EsCadenaVacia(DisplayValue) then
  begin
    case Modo of
      EntradaPedido: CodigoAlmacenDestino := '';
      SalidaDevolucion: CodigoAlmacenOrigen := '';
    end;
    Exit;
  end;

  //Valida que el codigo introducido es numérico
  if (not dmTablaAlmacenes.ValidarCodigo(DisplayValue)) then
  begin
    VerMensajeFmt(msgAlmCodAlmIncorrecto,[DisplayValue]);
    CodigoAlmacen.SetFocus;
  end;

  //Comprueba que le numero es lo sufucientemente pequeño para ser un entero
  if not EsSmallInt(DisplayValue) then
  begin
    VerMensajeFmt(msgAlmCodAlmIncorrecto,[DisplayValue]);
    DisplayValue := '0';
    CodigoAlmacen.SetFocus;
    Exit;
  end;

  case Modo of
    EntradaPedido : CodigoAlmacenDestino := DisplayValue;
    SalidaDevolucion : CodigoAlmacenOrigen := DisplayValue;
  end;
end;

procedure TfrMovimientoAlmacenes.CodClientePropertiesButtonClick(
  Sender: TObject; AButtonIndex: Integer);
begin
  ContenidoModal := TfrClientes.Create(Self);
end;

procedure TfrMovimientoAlmacenes.CodClientePropertiesValidate(
  Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption;
  var Error: Boolean);
begin
  if EsCadenaVacia(DisplayValue) then
  begin
    NifCifCliente.Text := '';
    NombreCliente.Text := '';
    Exit
  end;

  if (dmTablaClientes.validarCodigo(DisplayValue)) then begin
    //Comprueba que le numero es lo sufucientemente pequeño para ser un entero
    if not EsInteger(DisplayValue) then
    begin
      VerMensajeFmt(msgCliCodCliIncorrecto,[DisplayValue]);
      DisplayValue := '0';
      CodCliente.SetFocus;
    end
    else
    begin
      DisplayValue := dmTablaClientes.formatearCodigo(DisplayValue);
      if not (dmTablaClientes.ExisteCodigo(DisplayValue)) then
      begin
        VerMensajeFmt(msgCliCodCliNoExiste, [DisplayValue]);
        CodCliente.SetFocus;
      end
      else
        CodigoCliente := DisplayValue;
    end;
  end
  else begin
    VerMensajeFmt(msgCliCodCliIncorrecto, [DisplayValue]);
    CodCliente.SetFocus;
  end;
end;

end.
