unit uAlbaranesProveedorController;

interface


uses
  SysUtils, uDADataTable, uEditorDBItem,
  uControllerBase, uIDataModuleAlbaranesProveedor, uProveedoresController,
  uDetallesAlbaranProveedorController, uBizAlbaranesProveedor,
  uBizDireccionesContacto, uBizInventario;

type
  IAlbaranesProveedorController = interface(IControllerBase)
  ['{E2FA2312-EDBD-4E80-B3D7-A8784F6A4E39}']
    function GetProveedorController: IProveedoresController;
    procedure SetProveedorController(const Value: IProveedoresController);
    property ProveedorController: IProveedoresController read GetProveedorController write SetProveedorController;

    function GetDetallesController: IDetallesAlbaranProveedorController;
    procedure SetDetallesController(const Value: IDetallesAlbaranProveedorController);
    property DetallesController: IDetallesAlbaranProveedorController read GetDetallesController write SetDetallesController;

    function Buscar(const ID: Integer): IBizAlbaranProveedor;
    function BuscarTodos: IBizAlbaranProveedor; overload;
    function BuscarTodos(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarAlbaranesNormales: IBizAlbaranProveedor; overload;
    function BuscarAlbaranesNormales(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarAlbaranesDevolucion: IBizAlbaranProveedor; overload;
    function BuscarAlbaranesDevolucion(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarSinFacturar : IBizAlbaranProveedor;
    procedure Ver(AAlbaran : IBizAlbaranProveedor);
    procedure VerTodosAlbaranes(AAlbarans: IBizAlbaranProveedor;
      const AVerModal : Boolean = False; const AWindowCaption: String = '';
      const AHeaderText: String = '');
    procedure VerTodosAlbaranesDev(AAlbarans: IBizAlbaranProveedor;
      const AVerModal : Boolean = False; const AWindowCaption: String = '';
      const AHeaderText: String = '');
    procedure VerDireccionEntrega(AAlbaran : IBizAlbaranProveedor);
    function Nuevo : IBizAlbaranProveedor;
    function Anadir(AAlbaran : IBizAlbaranProveedor) : Boolean; overload;
    function AnadirAlbaranDev(AAlbaran : IBizAlbaranProveedor) : Boolean;
    function Anadir(AAlbaran : IBizAlbaranProveedor; const IDPedido : Integer; AInventarioRecibido: IBizInventario): Boolean; overload;

    function Eliminar(const ID : Integer): Boolean; overload;
    function Eliminar(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false): Boolean; overload;
    function Guardar(AAlbaran : IBizAlbaranProveedor): Boolean;
    procedure DescartarCambios(AAlbaran : IBizAlbaranProveedor);
    function Existe(const ID: Integer) : Boolean;
    function Duplicar(AAlbaran: IBizAlbaranProveedor): IBizAlbaranProveedor;

    function ExtraerSeleccionados(AAlbaran: IBizAlbaranProveedor) : IBizAlbaranProveedor;
    function ElegirAlbaranes(AAlbaran: IBizAlbaranProveedor; AMensaje: String; AMultiSelect: Boolean): IBizAlbaranProveedor;

    procedure CopiarDireccionEnvio (const ADireccionEnvio: IBizDireccionesContacto; AAlbaran: IBizAlbaranProveedor);
    procedure QuitarDireccionEnvio(AAlbaran: IBizAlbaranProveedor);
    procedure RecuperarProveedor(AAlbaran: IBizAlbaranProveedor);

    procedure RecalcularImportes(AAlbaran: IBizAlbaranProveedor);
    function EsModificable(AAlbaran: IBizAlbaranProveedor): Boolean;
    function EsEliminable(AAlbaran: IBizAlbaranProveedor): Boolean;

    procedure Preview(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false);
    procedure Print(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false);

    function GenerarDevolucion(AAlbaran : IBizAlbaranProveedor) : IBizAlbaranProveedor;    
  end;

  TAlbaranesProveedorController = class(TControllerBase, IAlbaranesProveedorController)
  protected
    FDataModule : IDataModuleAlbaranesProveedor;
    FProveedorController : IProveedoresController;
    FDetallesController : IDetallesAlbaranProveedorController;

    function GetProveedorController: IProveedoresController;
    procedure SetProveedorController(const Value: IProveedoresController);
    procedure RecuperarProveedor(AAlbaran : IBizAlbaranProveedor);
    function GetDetallesController: IDetallesAlbaranProveedorController;
    procedure SetDetallesController(const Value: IDetallesAlbaranProveedorController);

    //Estos son los tres métodos a sobre escribir si se desea heredar toda la logica de
    //este controller
    procedure AsignarDataModule; virtual;
    procedure RecuperarObjetos(AAlbaran: IBizAlbaranProveedor); virtual;

    procedure RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable); override;
    function CreateEditor(const AName : String; const IID: TGUID; out Intf): Boolean;
    procedure FiltrarEmpresa(AAlbaran: IBizAlbaranProveedor);
    function _Vacio : IBizAlbaranProveedor;
    function ValidarAlbaran(AAlbaran: IBizAlbaranProveedor): Boolean;

  public
    property ProveedorController: IProveedoresController read GetProveedorController write SetProveedorController;
    property DetallesController: IDetallesAlbaranProveedorController read GetDetallesController write SetDetallesController;

    constructor Create; override;
    destructor Destroy; override;

    function Eliminar(const ID : Integer): Boolean; overload;
    function Eliminar(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false): Boolean; overload;
    function Guardar(AAlbaran : IBizAlbaranProveedor): Boolean;
    procedure DescartarCambios(AAlbaran : IBizAlbaranProveedor); virtual;
    function Existe(const ID: Integer) : Boolean; virtual;
    function Anadir(AAlbaran : IBizAlbaranProveedor) : Boolean; overload;
    function AnadirAlbaranDev(AAlbaran : IBizAlbaranProveedor) : Boolean;
    function Anadir(AAlbaran : IBizAlbaranProveedor; const IDPedido : Integer; AInventarioRecibido: IBizInventario): Boolean; overload;

    function Buscar(const ID: Integer): IBizAlbaranProveedor;
    function BuscarTodos: IBizAlbaranProveedor; overload;
    function BuscarTodos(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarAlbaranesNormales: IBizAlbaranProveedor; overload;
    function BuscarAlbaranesNormales(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarAlbaranesDevolucion: IBizAlbaranProveedor; overload;
    function BuscarAlbaranesDevolucion(const ID_Proveedor: Integer): IBizAlbaranProveedor; overload;
    function BuscarSinFacturar : IBizAlbaranProveedor;

    function Nuevo : IBizAlbaranProveedor;

    procedure Ver(AAlbaran : IBizAlbaranProveedor);
    procedure VerTodosAlbaranes(AAlbarans: IBizAlbaranProveedor;
      const AVerModal : Boolean = False; const AWindowCaption: String = '';
      const AHeaderText: String = '');
    procedure VerTodosAlbaranesDev(AAlbarans: IBizAlbaranProveedor;
      const AVerModal : Boolean = False; const AWindowCaption: String = '';
      const AHeaderText: String = '');
    procedure VerDireccionEntrega(AAlbaran : IBizAlbaranProveedor);
    function Duplicar(AAlbaran: IBizAlbaranProveedor): IBizAlbaranProveedor;
    procedure CopiarDireccionEnvio (const ADireccionEnvio: IBizDireccionesContacto;
      AAlbaran: IBizAlbaranProveedor);

    procedure QuitarDireccionEnvio(AAlbaran: IBizAlbaranProveedor);
    function ExtraerSeleccionados(AAlbaran: IBizAlbaranProveedor) : IBizAlbaranProveedor;
    function ElegirAlbaranes(AAlbaran: IBizAlbaranProveedor; AMensaje: String; AMultiSelect: Boolean): IBizAlbaranProveedor;

    procedure RecalcularImportes(AAlbaran: IBizAlbaranProveedor);
    function EsModificable(AAlbaran: IBizAlbaranProveedor): Boolean;
    function EsEliminable(AAlbaran: IBizAlbaranProveedor): Boolean;

    procedure Preview(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false);
    procedure Print(AAlbaran : IBizAlbaranProveedor; AllItems: Boolean = false);

    function GenerarDevolucion(AAlbaran : IBizAlbaranProveedor) : IBizAlbaranProveedor;
  end;

implementation

uses
  uROTypes, Controls, cxControls, DB, uEditorRegistryUtils, uEditorPreview, DateUtils,
  uIEditorAlbaranesProveedor, uIEditorAlbaranesDevProveedor, uDataModuleAlbaranesProveedor,
  uBizDetallesAlbaranProveedor, uBizContactos, uDataTableUtils, uDataModuleUsuarios,
  schAlbaranesProveedorClient_Intf, uDAInterfaces, uDateUtils, uIEditorAlbaranProveedor,
  uIEditorElegirAlbaranesProveedor, uIEditorDireccionEntregaAlbaranProveedor,
  schContactosClient_Intf, Forms,  schPedidosProveedorClient_Intf, uFactuGES_App,
  uBizPedidosProveedor, uPedidosProveedorController,
  schInventarioClient_Intf, uStringsUtils, uIntegerListUtils,
  uArticulosController, uBizArticulos, uControllerDetallesBase, Classes,
  schArticulosClient_Intf, uControllerDetallesArticulos, Variants, uDialogUtils,
  uIEditorAlbaranDevProveedor, uAlbaranesProveedorReportController;


{ Métodos auxiliares }
procedure CopiarPedidoAAlbaran(APedido: IBizPedidoProveedor;
  AAlbaran : IBizAlbaranProveedor);
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (CopiarPedidoAAlbaran)');

  if not Assigned(APedido) then
    raise Exception.Create ('Pedido no asignado (CopiarPedidoAAlbaran)');

  if not APedido.DataTable.Active then
    APedido.DataTable.Active := True;

  // El albarán tiene que venir ya abierto y posicionado donde hay que copiar
  AAlbaran.ID_PROVEEDOR := APedido.ID_PROVEEDOR;
  AAlbaran.ID_PEDIDO := APedido.ID;
  AAlbaran.CALLE := APedido.CALLE;
  AAlbaran.CODIGO_POSTAL := APedido.CODIGO_POSTAL;
  AAlbaran.POBLACION := APedido.POBLACION;
  AAlbaran.PROVINCIA := APedido.PROVINCIA;
  AAlbaran.PERSONA_CONTACTO := APedido.PERSONA_CONTACTO;
  AAlbaran.TELEFONO := APedido.TELEFONO;
  AAlbaran.BASE_IMPONIBLE := APedido.BASE_IMPONIBLE;
  AAlbaran.IMPORTE_NETO := APedido.IMPORTE_NETO;
  AAlbaran.IMPORTE_PORTE := APedido.IMPORTE_PORTE;
  AAlbaran.IMPORTE_DESCUENTO := APedido.IMPORTE_DESCUENTO;
  AAlbaran.IMPORTE_IVA := APedido.IMPORTE_IVA;
  AAlbaran.IMPORTE_TOTAL := APedido.IMPORTE_TOTAL;
  AAlbaran.DESCUENTO := APedido.DESCUENTO;
  AAlbaran.IVA := APedido.IVA;
  AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorOBSERVACIONES).AsVariant := APedido.DataTable.FieldByName(fld_PedidosProveedorOBSERVACIONES).AsVariant;
  AAlbaran.ID_FORMA_PAGO := APedido.ID_FORMA_PAGO;
  AAlbaran.ID_ALMACEN := APedido.ID_ALMACEN;
  AAlbaran.TIPO_ALMACEN := APedido.TIPO_ALMACEN;
  AAlbaran.NOMBRE_ALMACEN := APedido.NOMBRE_ALMACEN;

end;

procedure CopiarArticulosAAlbaran(APedido: IBizPedidoProveedor; AAlbaran: IBizAlbaranProveedor;
    AInventarioRecibido: IBizInventario);
var
  i : integer;
  ADetalles : IBizDetallesAlbaranProveedor;
  ADetallesController : IDetallesAlbaranProveedorController;
  AArticulosController : IArticulosController;
  AArticulo : IBizArticulo;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (CopiarArticulosAAlbaran)');

  if not Assigned(APedido) then
    raise Exception.Create ('Pedido no asignado (CopiarArticulosAAlbaran)');

  //Si esta asignado el inventario recibido es por una entrada de articulos de un pedido a almacén
  if Assigned(AInventarioRecibido) then
  begin
    if not AInventarioRecibido.DataTable.Active then
      AInventarioRecibido.DataTable.Active := True;

    // El albarán tiene que venir ya abierto y posicionado donde hay que copiar

    ADetalles := AAlbaran.Detalles;
    ADetallesController := TDetallesAlbaranProveedorController.Create;
    AArticulosController := TArticulosController.Create;
    try
      //OJO IMPORTANTE
      //Siempre que vayamos a trabajar con los detalles debemos hacer un beginupdate de los mismos y un endupdate para
      //obligarle siempre a recalcular los detalles una sola vez
      ADetallesController.BeginUpdate(ADetalles);

      AInventarioRecibido.DataTable.First;
      for i := 0 to AInventarioRecibido.DataTable.RecordCount - 1 do
      begin
        AArticulo := AArticulosController.Buscar(AInventarioRecibido.ID_ARTICULO);
        if not Assigned(AArticulo) then
          raise Exception.Create(Format('No se ha encontrado el artículo con ID %d (CopiarArticulosAAlbaran)', [AInventarioRecibido.ID_ARTICULO]));

        if (AInventarioRecibido.CANTIDAD <> 0) then
        begin
          ADetallesController.Add(ADetalles, TIPO_DETALLE_CONCEPTO);
//Se cambia porque puede haber articulos del pedido que no tengan referencia de proveedor
//          ADetallesController.AnadirArticulo(ADetalles, AInventarioRecibido.REFERENCIA, tCliente);

          if AInventarioRecibido.ID_ARTICULO = 0 then
          begin
            ADetalles.Edit;
            ADetalles.CONCEPTO := AInventarioRecibido.DESCRIPCION;
          end
          else
            ADetallesController.AnadirArticulo(ADetalles, AInventarioRecibido.ID_ARTICULO);

          ADetalles.Edit;

          APedido.Detalles.First;
//Se cambia porque puede haber articulos del pedido que no tengan referencia de proveedor
//          if not APedido.Detalles.DataTable.Locate(fld_ArticulosREFERENCIA, ADetalles.REFERENCIA, []) then
          if not APedido.Detalles.DataTable.Locate(fld_INVENTARIOID_ARTICULO, ADetalles.ID_ARTICULO, []) then
             raise Exception.Create(Format('No se ha encontrado el artículo del pedido con REFERENCIA %s (CopiarArticulosAAlbaran)', [ADetalles.REFERENCIA]));
          ADetalles.IMPORTE_UNIDAD := APedido.Detalles.IMPORTE_UNIDAD;
          ADetalles.DESCUENTO := APedido.Detalles.DESCUENTO;
          ADetalles.IMPORTE_PORTE := APedido.Detalles.IMPORTE_PORTE;
          ADetalles.CANTIDAD := AInventarioRecibido.CANTIDAD;
          ADetalles.ID_PEDIDO := APedido.Detalles.ID_PEDIDO;
          ADetalles.Post;
        end;

        AInventarioRecibido.Next;
      end;
    finally
      ADetallesController.EndUpdate(ADetalles);
      ADetallesController := NIL;
      AArticulosController := NIL;
    end;
  end
  else begin
    // AInventarioRecibido = NIL

    // El albarán tiene que venir ya abierto y posicionado donde hay que copiar

    ADetalles := AAlbaran.Detalles;
    ADetallesController := TDetallesAlbaranProveedorController.Create;
    AArticulosController := TArticulosController.Create;
    try
      //OJO IMPORTANTE
      //Siempre que vayamos a trabajar con los detalles debemos hacer un beginupdate de los mismos y un endupdate para
      //obligarle siempre a recalcular los detalles una sola vez
      ADetallesController.BeginUpdate(ADetalles);

      APedido.Detalles.DataTable.First;
      for i := 0 to APedido.Detalles.DataTable.RecordCount - 1 do
      begin
        AArticulo := AArticulosController.Buscar(APedido.Detalles.ID_ARTICULO);
        if not Assigned(AArticulo) then
          raise Exception.Create(Format('No se ha encontrado el artículo con ID %d (CopiarArticulosAAlbaran)', [APedido.Detalles.ID_ARTICULO]));

          if (APedido.Detalles.CANTIDAD <> 0) then
          begin
            ADetallesController.Add(ADetalles, TIPO_DETALLE_CONCEPTO);
            ADetallesController.AnadirArticulo(ADetalles, APedido.Detalles.REFERENCIA, tCliente);
            ADetalles.Edit;
            ADetalles.IMPORTE_UNIDAD := APedido.Detalles.IMPORTE_UNIDAD;
            ADetalles.DESCUENTO := APedido.Detalles.DESCUENTO;
            ADetalles.IMPORTE_PORTE := APedido.Detalles.IMPORTE_PORTE;
            ADetalles.CANTIDAD := APedido.Detalles.CANTIDAD;
            ADetalles.ID_PEDIDO := APedido.Detalles.ID_PEDIDO;
            ADetalles.Post;
          end;
        APedido.Detalles.Next;
      end;

    finally
      ADetallesController.EndUpdate(ADetalles);
      ADetallesController := NIL;
      AArticulosController := NIL;
    end;
  end;
end;

{ TAlbaranesProveedorController }

function TAlbaranesProveedorController.Anadir(AAlbaran: IBizAlbaranProveedor) : Boolean;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (Anadir)');

  if not AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  AAlbaran.Insert;
  Result := True;
end;


function TAlbaranesProveedorController.Anadir(AAlbaran: IBizAlbaranProveedor; const IDPedido: Integer;
  AInventarioRecibido: IBizInventario): Boolean;
var
  APedidosController : IPedidosProveedorController;
  APedido : IBizPedidoProveedor;
begin

  Result := False;
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (Anadir)');

  if (IDPedido < 0) or (IDPedido = 0) then
    raise Exception.Create (Format('ID de pedido (%d) incorrecto (Anadir)', [IDPedido]));

  if not AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  ShowHourglassCursor;
  Application.ProcessMessages;

  APedido := NIL;
  APedidosController := TPedidosProveedorController.Create;
  try
    APedido := APedidosController.Buscar(IDPedido);
    if not Assigned(APedido) then
      raise Exception.Create (Format('No se ha encontrado un pedido de proveedor con ID %d (Anadir)', [IDPedido]));

    APedido.DataTable.Active := True;

    if not Assigned(AInventarioRecibido) then
    begin
      if (APedido.ID_ALMACEN > 0) then
        raise Exception.Create ('Inventario recibido no asignado (Anadir)')
    end
    else begin
      if not AInventarioRecibido.DataTable.Active then
        AInventarioRecibido.DataTable.Active := True;
    end;

    CopiarPedidoAAlbaran(APedido, AAlbaran);
    CopiarArticulosAAlbaran(APedido, AAlbaran, AInventarioRecibido);
    RecalcularImportes(AAlbaran);
    Result := True;
  finally
    APedido := NIL;
    APedidosController := NIL;
    HideHourglassCursor;
    Application.ProcessMessages;
  end;
end;

function TAlbaranesProveedorController.AnadirAlbaranDev(AAlbaran: IBizAlbaranProveedor): Boolean;
begin
  Result := Anadir(AAlbaran);
  AAlbaran.TIPO := CTE_TIPO_ALBARAN_DEV;
end;

procedure TAlbaranesProveedorController.AsignarDataModule;
begin
  FDataModule := TDataModuleAlbaranesProveedor.Create(Nil);
end;

function TAlbaranesProveedorController.Buscar(const ID: Integer): IBizAlbaranProveedor;
begin
  Result := FDataModule.GetItem(ID);
  FiltrarEmpresa(Result);
end;

function TAlbaranesProveedorController.BuscarAlbaranesDevolucion: IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarTodos;

    with Result.DataTable.DynamicWhere do
    begin
      // (TIPO = DEVOLUCION)
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorTIPO), NewConstant(CTE_TIPO_ALBARAN_DEV, datString), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;
  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.BuscarAlbaranesNormales: IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarTodos;

    with Result.DataTable.DynamicWhere do
    begin
      // (TIPO = ALBARAN)
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorTIPO), NewConstant(CTE_TIPO_ALBARAN, datString), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;

  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.BuscarTodos: IBizAlbaranProveedor;
begin
  Result := FDataModule.GetItems;
  FiltrarEmpresa(Result);
end;

function TAlbaranesProveedorController.BuscarSinFacturar: IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarAlbaranesNormales;

    with Result.DataTable.DynamicWhere do
    begin
      // (ID_FACTURA = NULL)
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorID_FACTURA), NewNull(), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;

  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.BuscarTodos(
  const ID_Proveedor: Integer): IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarTodos;

    with Result.DataTable.DynamicWhere do
    begin
      // ID_CLIENTE
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorID_PROVEEDOR),
       NewConstant(ID_Proveedor, datInteger), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;
  finally
    HideHourglassCursor;
  end;
end;


procedure TAlbaranesProveedorController.CopiarDireccionEnvio(
  const ADireccionEnvio: IBizDireccionesContacto; AAlbaran: IBizAlbaranProveedor);
var
  bEnEdicion : Boolean;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albaran no asignado (CopiarDireccionEnvio)');

  if not Assigned(ADireccionEnvio) then
    raise Exception.Create ('No se ha indicado la dirección (CopiarDireccionEnvio)');

  if AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  if ADireccionEnvio.DataTable.Active then
    ADireccionEnvio.DataTable.Active := True;


  bEnEdicion := (AAlbaran.DataTable.State in dsEditModes);
  if not bEnEdicion then
    AAlbaran.Edit;

  ShowHourglassCursor;
  AAlbaran.Edit;
  try
    AAlbaran.CALLE := ADireccionEnvio.CALLE;
    AAlbaran.POBLACION := ADireccionEnvio.POBLACION;
    AAlbaran.CODIGO_POSTAL := ADireccionEnvio.CODIGO_POSTAL;
    AAlbaran.PROVINCIA := ADireccionEnvio.PROVINCIA;
    AAlbaran.TELEFONO := ADireccionEnvio.TELEFONO;
    AAlbaran.PERSONA_CONTACTO := ADireccionEnvio.PERSONA_CONTACTO;

    if not bEnEdicion then
      AAlbaran.Post;
  finally
    HideHourglassCursor;
  end;
end;

constructor TAlbaranesProveedorController.Create;
begin
  inherited;
  AsignarDataModule;
  
  FProveedorController := TProveedoresController.Create;
  FDetallesController := TDetallesAlbaranProveedorController.Create;
  FDetallesController.addObservador(Self);
end;

function TAlbaranesProveedorController.CreateEditor(const AName: String;
  const IID: TGUID; out Intf): Boolean;
begin
  Result := Supports(EditorRegistry.CreateEditor(AName), IID, Intf);
end;

procedure TAlbaranesProveedorController.DescartarCambios(AAlbaran: IBizAlbaranProveedor);
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado');

  ShowHourglassCursor;
  try
    if (AAlbaran.State in dsEditModes) then
      AAlbaran.Cancel;

    AAlbaran.DataTable.CancelUpdates;
  finally
    HideHourglassCursor;
  end;
end;

destructor TAlbaranesProveedorController.Destroy;
begin
  FDataModule := Nil;
  FProveedorController := Nil;
  FDetallesController := Nil;
  inherited;
end;

function TAlbaranesProveedorController.Duplicar(AAlbaran: IBizAlbaranProveedor): IBizAlbaranProveedor;
begin
  Result := Self._Vacio;
  ShowHourglassCursor;
  try
    DuplicarRegistros(AAlbaran.DataTable, Result.DataTable, mdrActual);
    DuplicarRegistros(AAlbaran.Detalles.DataTable, Result.Detalles.DataTable, mdrTodos);

    // Hay que dejar algunos campos como si fuera un presupuesto nuevo
    Result.Edit;
    with Result do
    begin
      ID_EMPRESA := AppFactuGES.EmpresaActiva.ID;
      USUARIO := AppFactuGES.UsuarioActivo.UserName;
      FECHA_ALBARAN := DateOf(Now);
      INCIDENCIAS_ACTIVAS := 0;
      REFERENCIA := '';
      ID_PEDIDO := 0;
      ID_FACTURA := 0;
      ID_ALBARAN := 0;
      REFERENCIA_ALBARAN := '';
    end;
    Result.Post;
  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.ValidarAlbaran(AAlbaran: IBizAlbaranProveedor): Boolean;
begin
  if not Assigned(AAlbaran) then
     raise Exception.Create ('Albarán no asignado');

  if (AAlbaran.DataTable.State in dsEditModes) then
      AAlbaran.DataTable.Post;

  //Tambien hacemos post de sus tablas hija
  if (AAlbaran.Detalles.DataTable.State in dsEditModes) then
      AAlbaran.Detalles.DataTable.Post;

  if (AAlbaran.ID_PROVEEDOR < 0) or (AAlbaran.ID_PROVEEDOR = 0) then
  // Si hay altas automáticas no hay objeto Proveedor pero sí hay ID_PROVEEDOR
{     (not Assigned(AAlbaran.Proveedor)) or
     (AAlbaran.Proveedor.IsEmpty) then}
    raise Exception.Create('Debe indicar el proveedor de este albarán');

  if (EsFechaVacia(AAlbaran.FECHA_ALBARAN)) then
    raise Exception.Create('Debe indicar la fecha de este albarán');

  if (Length(AAlbaran.CALLE) = 0) and (AAlbaran.ID_ALMACEN = 0) then
    raise Exception.Create('Debe indicar una dirección de entrega o almacén para este albarán');

  //Tambien hacemos post de sus tablas hija
  if (AAlbaran.Detalles.DataTable.RecordCount = 0) then
    raise Exception.Create('El albarán de de tener al menos un concepto en su contenido');

  //En caso de ser un Albarán no podra tener un importe total negativo
  if (AAlbaran.TIPO = CTE_TIPO_ALBARAN) then
    if (AAlbaran.IMPORTE_TOTAL < 0) then
      raise Exception.Create('Un albarán no puede tener un importe total negativo');

  //En caso de ser una orden de devolución no podra tener un importe total positivo
  if (AAlbaran.TIPO = CTE_TIPO_ALBARAN_DEV) then
  begin
    if (AAlbaran.IMPORTE_TOTAL > 0) then
      raise Exception.Create('Una orden de devolución no puede tener un importe total positivo');

    //Una orden de devolución debe tener relleno el almacén de salida de material
    if (Length(AAlbaran.CALLE) = 0) and (AAlbaran.ID_ALMACEN = 0) then
      raise Exception.Create('Debe indicar una dirección de salida o almacén para esta orden de devolución');
  end;

  { Asegurarse de valores en campos "automáticos" tanto
    en MODIFICACIÓN como en INSERCIÓN. }
  AAlbaran.Edit;
  try
    AAlbaran.USUARIO := AppFactuGES.UsuarioActivo.UserName;

    if Assigned(AAlbaran.Proveedor)
    and (AAlbaran.ID_PROVEEDOR <> AAlbaran.Proveedor.ID) then
      AAlbaran.ID_PROVEEDOR := AAlbaran.Proveedor.ID;

    Result := True;
  finally
    AAlbaran.Post;
  end;
end;

procedure TAlbaranesProveedorController.Ver(AAlbaran: IBizAlbaranProveedor);
var
  AEditor : IEditorAlbaranProveedor;
begin
  AEditor := NIL;

  RecuperarObjetos(AAlbaran);

  if (AAlbaran.TIPO = CTE_TIPO_ALBARAN) then
      CreateEditor('EditorAlbaranProveedor', IEditorAlbaranProveedor, AEditor)
  else
      CreateEditor('EditorAlbaranDevProveedor', IEditorAlbaranDevProveedor, AEditor);

  if Assigned(AEditor) then
    try
      AEditor.Controller := Self; //OJO ORDEN MUY IMPORTANTE
      AEditor.Albaran := AAlbaran;

      //MODO CONSULTAR
      if not EsModificable(AAlbaran) then
      begin
         SetDataTableReadOnly(AAlbaran.DataTable, True);
         AEditor.ReadOnly := True;
      end;

      AEditor.ShowModal;

      //MODO CONSULTAR (Se deja la tabla como estaba)
      if AEditor.ReadOnly then
         SetDataTableReadOnly(AAlbaran.DataTable, False);
    finally
      AEditor.Release;
      AEditor := NIL;
    end;
end;

procedure TAlbaranesProveedorController.VerDireccionEntrega(AAlbaran: IBizAlbaranProveedor);
var
  AEditor : IEditorDireccionEntregaAlbaranProveedor;
begin
  AEditor := NIL;
  //RecuperarObjetos(AAlbaran); <- No descomentar. No hace falta
  CreateEditor('EditorDireccionEntregaAlbaranProveedor', IEditorDireccionEntregaAlbaranProveedor, AEditor);
  if Assigned(AEditor) then
    try
      AEditor.Albaran := AAlbaran;
      AEditor.ShowModal;
    finally
      AEditor.Release;
      AEditor := NIL;
    end;
end;

procedure TAlbaranesProveedorController.VerTodosAlbaranes(AAlbarans: IBizAlbaranProveedor;
  const AVerModal : Boolean = False; const AWindowCaption: String = '';
  const AHeaderText: String = '');
var
  AEditor : IEditorAlbaranesProveedor;
begin
  AEditor := NIL;
  CreateEditor('EditorAlbaranesProveedor', IEditorAlbaranesProveedor, AEditor);
  if Assigned(AEditor) then
    try
      if not EsCadenaVacia(AWindowCaption) then
        AEditor.WindowCaption := AWindowCaption;

      if not EsCadenaVacia(AHeaderText) then
        AEditor.HeaderText := AHeaderText;

      AEditor.Controller := Self; //OJO ORDEN MUY IMPORTANTE
      AEditor.Albaranes := AAlbarans;
      AEditor.MultiSelect := True;
      if AVerModal then
        AEditor.ShowModal
      else
        AEditor.ShowEmbedded;
    finally
      if AVerModal then
        AEditor.Release;
      AEditor := NIL;
    end;
end;

procedure TAlbaranesProveedorController.VerTodosAlbaranesDev(AAlbarans: IBizAlbaranProveedor;
  const AVerModal : Boolean = False; const AWindowCaption: String = '';
  const AHeaderText: String = '');
var
  AEditor : IEditorAlbaranesDevProveedor;
begin
  AEditor := NIL;
  CreateEditor('EditorAlbaranesDevProveedor', IEditorAlbaranesDevProveedor, AEditor);
  if Assigned(AEditor) then
    try
      if not EsCadenaVacia(AWindowCaption) then
        AEditor.WindowCaption := AWindowCaption;

      if not EsCadenaVacia(AHeaderText) then
        AEditor.HeaderText := AHeaderText;

      AEditor.Controller := Self; //OJO ORDEN MUY IMPORTANTE
      AEditor.Albaranes := AAlbarans;
      AEditor.MultiSelect := True;
      if AVerModal then
        AEditor.ShowModal
      else
        AEditor.ShowEmbedded;
    finally
      if AVerModal then
        AEditor.Release;
      AEditor := NIL;
    end;
end;

function TAlbaranesProveedorController._Vacio: IBizAlbaranProveedor;
begin
  Result := Buscar(ID_NULO);
end;

function TAlbaranesProveedorController.Eliminar(const ID: Integer): Boolean;
var
  AAlbaran : IBizAlbaranProveedor;
begin
  AAlbaran := Buscar(ID);

  if not Assigned(AAlbaran) then
    raise Exception.Create(Format('No se ha encontrado el albarán con ID = %d', [ID]));

  Result := Eliminar(AAlbaran);
  AAlbaran := NIL;
end;

function TAlbaranesProveedorController.ElegirAlbaranes(AAlbaran: IBizAlbaranProveedor; AMensaje: String; AMultiSelect: Boolean): IBizAlbaranProveedor;
var
  AEditor : IEditorElegirAlbaranesProveedor;
begin
  Result := NIL;
  CreateEditor('EditorElegirAlbaranesProveedor', IEditorElegirAlbaranesProveedor, AEditor);
  if Assigned(AEditor) then
    try
       AEditor.Controller := Self;
       AEditor.Albaranes := AAlbaran;
       AEditor.MultiSelect := AMultiSelect;
       AEditor.Mensaje := AMensaje;
       if IsPositiveResult(AEditor.ShowModal) then
         Result := AEditor.AlbaranesProveedorSeleccionados;
    finally
       AEditor.Release;
       AEditor := NIL;
    end;
end;

function TAlbaranesProveedorController.Eliminar(AAlbaran: IBizAlbaranProveedor; AllItems: Boolean = false): Boolean;
//En el caso de eliminar almenos un elemento del conjunto se devuelve true
var
  bEliminado: Boolean;

begin
  bEliminado := False;

  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignada');

  ShowHourglassCursor;
  try
    if (AAlbaran.State in dsEditModes) then
      AAlbaran.Cancel;

    //Siempre eliminaremos el seleccionado
    if EsEliminable(AAlbaran) then
    begin
      AAlbaran.Delete;
      bEliminado := True;
    end;

    //En el caso de querer eliminar todos los items del objeto AAlbaran
    if AllItems then
    begin
       with AAlbaran.DataTable do
       begin
         First;
         while not EOF do
         begin
           if EsEliminable(AAlbaran) then
           begin
              AAlbaran.Delete;
              bEliminado := True
           end
           else Next;
         end;
       end;
    end;

    if bEliminado then
    begin
       AAlbaran.DataTable.ApplyUpdates;
       Result := True;
    end
    else
       Result := False;

  finally
    HideHourglassCursor;
  end;
end;
function TAlbaranesProveedorController.EsEliminable(AAlbaran: IBizAlbaranProveedor): Boolean;
begin
  if not Assigned(AAlbaran) then
     raise Exception.Create ('Albarán no asignado: EsModificable');

  Result := EsModificable(AAlbaran);
end;

function TAlbaranesProveedorController.EsModificable(AAlbaran: IBizAlbaranProveedor): Boolean;
begin
  if not Assigned(AAlbaran) then
     raise Exception.Create ('Albarán no asignado: EsModificable');

  Result := not (AAlbaran.ID_FACTURA > 0);
end;

procedure TAlbaranesProveedorController.RecalcularImportes(
  AAlbaran: IBizAlbaranProveedor);
var
  bEnEdicion : Boolean;
  ADetallePosAct : Integer;  
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (RecalcularImportes)');

  if AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  { Hay que guardar la posición en la que estamos en los detalles por que
    la asignación de valores a los campos IMPORTE_NETO e IMPORTE_PORTE
    (ver más adelante) colocan el puntero en la tabla detalle al principio.
    No he encontrado la razón por la que mueve el puntero. }

  ADetallePosAct := AAlbaran.Detalles.POSICION;

  bEnEdicion := (AAlbaran.DataTable.State in dsEditModes);
  if not bEnEdicion then
    AAlbaran.Edit;

  ShowHourglassCursor;
  try
    AAlbaran.IMPORTE_NETO := FDetallesController.DarTotalImporteTotal(AAlbaran.Detalles);
    AAlbaran.IMPORTE_PORTE := FDetallesController.DarTotalPorteTotal(AAlbaran.Detalles);
    if not bEnEdicion then
      AAlbaran.Post;
  finally
    HideHourglassCursor;
    // Restaurar la posición que teníamos en los detalles.
    FDetallesController.LocalizarPosicion(AAlbaran.Detalles, ADetallePosAct);
  end;
end;

procedure TAlbaranesProveedorController.RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable);
var
  AAlbaran : IBizAlbaranProveedor;
  ADetalles : IBizDetallesAlbaranProveedor;
begin
  inherited;

  if Supports(ADataTable, IBizDetallesAlbaranProveedor, ADetalles) and
     Supports(ADetalles.DataTable.MasterSource.DataTable, IBizAlbaranProveedor, AAlbaran) then
  begin
    RecalcularImportes(AAlbaran);
  end;
end;

procedure TAlbaranesProveedorController.RecuperarObjetos(AAlbaran: IBizAlbaranProveedor);
begin
  RecuperarProveedor(AAlbaran);
end;

procedure TAlbaranesProveedorController.RecuperarProveedor(AAlbaran: IBizAlbaranProveedor);
begin
  AAlbaran._Proveedor := (FProveedorController.Buscar(AAlbaran.ID_PROVEEDOR)  as IBizProveedor);
end;

function TAlbaranesProveedorController.Existe(const ID: Integer): Boolean;
var
  AAlbaran : IBizAlbaranProveedor;
begin
  try
    AAlbaran := Buscar(ID);
    Result := Assigned(AAlbaran) and (AAlbaran.ID = ID);
  finally
    AAlbaran := NIL;
  end;
end;

function TAlbaranesProveedorController.ExtraerSeleccionados(AAlbaran: IBizAlbaranProveedor): IBizAlbaranProveedor;
var
  ASeleccionados : IBizAlbaranProveedor;
begin
  ASeleccionados := (Self.Buscar(ID_NULO) as IBizAlbaranProveedor);
  CopyDataTableDA5(AAlbaran.DataTable, ASeleccionados.DataTable, True);
  Result := ASeleccionados;
end;

procedure TAlbaranesProveedorController.FiltrarEmpresa(AAlbaran: IBizAlbaranProveedor);
var
  Condicion: TDAWhereExpression;
begin
  if AAlbaran.DataTable.Active then
     AAlbaran.DataTable.Active := False;

  // Filtrar los presupuestos actuales por empresa
  with AAlbaran.DataTable.DynamicWhere do
  begin
    // (ID_EMPRESA >= ID)
    Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorID_EMPRESA), NewConstant(AppFactuGES.EmpresaActiva.ID, datInteger), dboEqual);

    if IsEmpty then
       Expression := Condicion
    else
       Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
  end;
end;

procedure TAlbaranesProveedorController.SetProveedorController(const Value: IProveedoresController);
begin
  FProveedorController := Value;
end;

procedure TAlbaranesProveedorController.SetDetallesController(const Value: IDetallesAlbaranProveedorController);
begin
  FDetallesController := Value;
end;

function TAlbaranesProveedorController.Guardar(AAlbaran: IBizAlbaranProveedor): Boolean;
begin
  Result := False;

  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignada');

  if not Assigned(FDetallesController) then
    raise Exception.Create ('Controller detalles no asignado');

  if ValidarAlbaran(AAlbaran) then
  begin
    ShowHourglassCursor;
    try
      // Asegurarnos de que todos los importes están bien.
      RecalcularImportes(AAlbaran);

      AAlbaran.DataTable.ApplyUpdates;
      Result := True;
    finally
      HideHourglassCursor;
    end;
  end;
end;

function TAlbaranesProveedorController.Nuevo: IBizAlbaranProveedor;
var
  AAlbaran : IBizAlbaranProveedor;
begin
  AAlbaran := FDataModule.NewItem;
  FiltrarEmpresa(AAlbaran);
  AAlbaran.DataTable.Active := True;
  AAlbaran.Insert;
  Result := AAlbaran;
end;

procedure TAlbaranesProveedorController.Preview(AAlbaran: IBizAlbaranProveedor; AllItems: Boolean = false);
var
  AReportController : IAlbaranesProveedorReportController;
  ID_Albaranes: TIntegerList;
begin
  AReportController := TAlbaranesProveedorReportController.Create;
  ID_Albaranes := TIntegerList.Create;

  try
    //Si deseamos previsualizar todos los items del objeto albaran
    if AllItems then
    begin
     with AAlbaran.DataTable do
     begin
       First;
       while not EOF do
       begin
         ID_Albaranes.Add(AAlbaran.ID);
         Next;
       end;
     end;
    end
    //Solo previsualizamos el item seleccionado
    else
     ID_Albaranes.Add(AAlbaran.ID);

    AReportController.Preview(ID_Albaranes);
  finally
    AReportController := NIL;
    FreeANDNIL(ID_Albaranes)
  end;
end;

procedure TAlbaranesProveedorController.Print(AAlbaran: IBizAlbaranProveedor; AllItems: Boolean = false);
var
  AReportController : IAlbaranesProveedorReportController;
  ID_Albaranes: TIntegerList;
begin
  AReportController := TAlbaranesProveedorReportController.Create;
  ID_Albaranes := TIntegerList.Create;

  try
    //Si deseamos previsualizar todos los items del objeto albaran
    if AllItems then
    begin
     with AAlbaran.DataTable do
     begin
       First;
       while not EOF do
       begin
         ID_Albaranes.Add(AAlbaran.ID);
         Next;
       end;
     end;
    end
    //Solo previsualizamos el item seleccionado
    else
     ID_Albaranes.Add(AAlbaran.ID);

    AReportController.Print(ID_Albaranes);
  finally
    AReportController := NIL;
    FreeANDNIL(ID_Albaranes)
  end;
end;

procedure TAlbaranesProveedorController.QuitarDireccionEnvio(
  AAlbaran: IBizAlbaranProveedor);
var
  bEnEdicion : Boolean;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (QuitarDireccionEnvio)');

  if AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  bEnEdicion := (AAlbaran.DataTable.State in dsEditModes);
  if not bEnEdicion then
    AAlbaran.Edit;

  ShowHourglassCursor;
  AAlbaran.Edit;
  try
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorCALLE).Clear;
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorPOBLACION).Clear;
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorCODIGO_POSTAL).Clear;
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorPROVINCIA).Clear;
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorTELEFONO).Clear;
    AAlbaran.DataTable.FieldByName(fld_AlbaranesProveedorPERSONA_CONTACTO).Clear;

    if not bEnEdicion then
      AAlbaran.Post;
  finally
    HideHourglassCursor;
  end;    
end;

function TAlbaranesProveedorController.GetProveedorController: IProveedoresController;
begin
  Result := FProveedorController;
end;

function TAlbaranesProveedorController.GenerarDevolucion(
  AAlbaran: IBizAlbaranProveedor): IBizAlbaranProveedor;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (QuitarDireccionEnvio)');

  if AAlbaran.DataTable.Active then
    AAlbaran.DataTable.Active := True;

  ShowHourglassCursor;
  try
    Result := Duplicar(AAlbaran);

    //Convierte todos los articulos del albaran a negativos por ser una devolucion
    FDetallesController.CambiarSignoDetalles(Result.Detalles);

    // Hay que dejar algunos campos como si fuera una factura nueva
    Result.Edit;
    Result.TIPO := CTE_TIPO_ALBARAN_DEV;
    Result.Post;

  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.GetDetallesController: IDetallesAlbaranProveedorController;
begin
  Result := FDetallesController;
end;

function TAlbaranesProveedorController.BuscarAlbaranesDevolucion(
  const ID_Proveedor: Integer): IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarAlbaranesDevolucion;

    with Result.DataTable.DynamicWhere do
    begin
      // ID_CLIENTE
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorID_PROVEEDOR),
       NewConstant(ID_Proveedor, datInteger), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;
  finally
    HideHourglassCursor;
  end;
end;

function TAlbaranesProveedorController.BuscarAlbaranesNormales(
  const ID_Proveedor: Integer): IBizAlbaranProveedor;
var
  Condicion: TDAWhereExpression;
begin
  ShowHourglassCursor;
  try
    Result := BuscarAlbaranesNormales;

    with Result.DataTable.DynamicWhere do
    begin
      // ID_CLIENTE
      Condicion := NewBinaryExpression(NewField('', fld_AlbaranesProveedorID_PROVEEDOR),
       NewConstant(ID_Proveedor, datInteger), dboEqual);

      if IsEmpty then
         Expression := Condicion
      else
         Expression := NewBinaryExpression(Expression, Condicion, dboAnd);
    end;
  finally
    HideHourglassCursor;
  end;
end;

end.
