unit uControllerDetallesArticulos;

interface

uses Classes, Variants, uDACDSDataTable, uDADataTable, uControllerDetallesDTO,
     uBizArticulos, uArticulosController;

const
  CAMPO_ID_ARTICULOS = 'ID_ARTICULO';
  CAMPO_REFERENCIA = 'REFERENCIA'; //Campo ficticio
  CAMPO_REFERENCIA_PROVEEDOR = 'REFERENCIA_PROVEEDOR'; //Campo ficticio

type
  TEnumReferencia = (tCliente, tProveedor);

  IControllerDetallesArticulos = interface(IControllerDetallesDTO)
  ['{6E156796-DB1F-4727-BBFB-FBAEF2E5C098}']
    procedure AsignarID(ADetalles: IDAStronglyTypedDataTable; IDCabecera: Integer; AEsNuevo:Boolean);
    procedure AnadirArticulos(ADetalles: IDAStronglyTypedDataTable);
    function AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; Referencia: String; TipoReferencia: TEnumReferencia; AClienteID: Integer = -1): Boolean; overload;
    function AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; IDArticulo: Integer; AClienteID: Integer = -1): Boolean; overload;
    procedure ActualizarDetalles(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
    procedure CambiarSignoDetalles(ADetalles: IDAStronglyTypedDataTable);
    procedure AnadirConceptoInicial(ADetalles: IDAStronglyTypedDataTable; AConcepto: String);
    procedure DesglosarPorteDetalles(ImportePorte: Currency; ADetalles: IDAStronglyTypedDataTable);
  end;

  TControllerDetallesArticulos = class (TControllerDetallesDTO, IControllerDetallesArticulos)
  protected
    FArticulosController: IArticulosController;

    procedure EliminarArticulosProveedor(ADetalles: IDAStronglyTypedDataTable);
    procedure RellenarDescuento(ADetalles: IDAStronglyTypedDataTable; ADescuento : Float);

    procedure AsignarDatos(ADetalles: IDAStronglyTypedDataTable; IDCabecera: Integer); virtual;
    procedure RellenarDescuentos(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo); virtual;
    procedure RellenarImportes(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo); virtual;
    procedure RellenarGenerales(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo); virtual;
    procedure RellenarDetalle(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo); virtual;
    procedure Add(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo); overload;

    procedure AsignarController; virtual;

  public
    procedure AsignarID(ADetalles: IDAStronglyTypedDataTable; IDCabecera: Integer; AEsNuevo:Boolean);
    function AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; Referencia: String; TipoReferencia: TEnumReferencia; AClienteID: Integer = -1): Boolean; overload;
    function AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; IDArticulo: Integer; AClienteID: Integer = -1): Boolean; overload;
    procedure AnadirArticulos(ADetalles: IDAStronglyTypedDataTable); virtual;
    procedure ActualizarDetalles(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
    procedure CambiarSignoDetalles(ADetalles: IDAStronglyTypedDataTable);
    procedure AnadirConceptoInicial(ADetalles: IDAStronglyTypedDataTable; AConcepto: String);
    procedure DesglosarPorteDetalles(ImportePorte: Currency; ADetalles: IDAStronglyTypedDataTable);

    constructor Create; override;
    destructor Destroy; override;
  end;


implementation

{ TControllerDetallesArticulos }

uses DB, Dialogs, cxControls, SysUtils, uDAInterfaces, uControllerDetallesBase, schArticulosClient_Intf;


{ TControllerDetallesArticulos }

procedure TControllerDetallesArticulos.ActualizarDetalles(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
  if (Assigned(ADetalles) and Assigned(AArticulos)) then
  begin
    if not AArticulos.DataTable.Active then
       AArticulos.DataTable.Active := True;

    BeginUpdate(ADetalles);
    try
      ADetalles.DataTable.First;
      with ADetalles.DataTable do
      begin
        while not Eof do
        begin
          Edit;
          AArticulos.DataTable.First;
          try
            if AArticulos.DataTable.Locate(CAMPO_ID, FieldByName(CAMPO_ID_ARTICULOS).AsVariant, []) then
              RellenarDetalle(ADetalles, AArticulos)
            else
              RellenarDetalle(ADetalles, Nil);
          except
            on E: Exception do
              ShowMessage(E.Message);
          end;
          Next;
        end;
        //Volvemos a dejarlo al principio
        ADetalles.DataTable.First;
      end;
    finally
      EndUpdate(ADetalles);
    end;
  end;
end;

procedure TControllerDetallesArticulos.Add(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
  if Assigned(ADetalles) and Assigned(AArticulos) then
  begin
    try
      ShowHourglassCursor;
      BeginUpdate(ADetalles);

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

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

      with AArticulos.DataTable do
      begin
        First;
        while not EOF do
        begin
          Self.Add(ADetalles, TIPO_DETALLE_CONCEPTO);
          RellenarDetalle(ADetalles, AArticulos);
          Next;
        end;
      end;
    finally
      EndUpdate(ADetalles);
      HideHourglassCursor;
    end;
  end;
end;

function TControllerDetallesArticulos.AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; IDArticulo: Integer; AClienteID: Integer = -1): Boolean;
var
  AArticulo: IBizArticulo;
begin
{
  Añade el artículo con la referencia pasada por parametro a los detalles pasados por parametro
  En el caso de asignar un AClienteID el articulo debe contener el descuento para ese cliente
}

  Result := False;
  BeginUpdate(ADetalles);
  ShowHourglassCursor;
  try
    AArticulo := FArticulosController.Buscar(IDArticulo, AClienteID);

    if Assigned(AArticulo) then
    begin
      AArticulo.DataTable.Active := True;
      if (AArticulo.RecordCount > 0) then
      begin
        RellenarDetalle(ADetalles, AArticulo);
        Result := True;
      end;
    end;
  finally
    EndUpdate(ADetalles);
    HideHourglassCursor;
  end;
end;

procedure TControllerDetallesArticulos.AnadirArticulos(ADetalles: IDAStronglyTypedDataTable);
var
  AArticulos: IBizArticulo;
begin
  if Assigned(ADetalles) then
  begin
    try
      AArticulos := (FArticulosController.BuscarTodos as IBizArticulo);
      AArticulos := FArticulosController.ElegirArticulos(AArticulos, 'Elija los artículos que desea añadir', True);
      Add(ADetalles, AArticulos);
    finally
      AArticulos := Nil;
    end;
  end;
end;

procedure TControllerDetallesArticulos.AnadirConceptoInicial(ADetalles: IDAStronglyTypedDataTable; AConcepto: String);
begin
  if Assigned(ADetalles) then
  begin
    With ADetalles.DataTable do
    begin
      First;
      Add(ADetalles, TIPO_DETALLE_TITULO);
      Edit;
      FieldByName(CAMPO_CONCEPTO).AsString := AConcepto;
      Post;
      //Subimos la linea insertada al inicio de los detalles
      Self.Mover(ADetalles.DataTable, 1, -1);
      //Añadimos linea en blanco
      Add(ADetalles, TIPO_DETALLE_CONCEPTO);
      Edit;
      FieldByName(CAMPO_CONCEPTO).AsString := '';
      Post;
//      First;
    end;
  end;
end;

procedure TControllerDetallesArticulos.AsignarController;
begin
  FArticulosController := TArticulosController.Create;
end;

procedure TControllerDetallesArticulos.AsignarDatos(ADetalles: IDAStronglyTypedDataTable; IDCabecera: Integer);
begin
//
end;

procedure TControllerDetallesArticulos.AsignarID(ADetalles: IDAStronglyTypedDataTable; IDCabecera: Integer; AEsNuevo: Boolean);
begin
  with ADetalles do
  begin
    DataTable.DisableControls;
    try
      begin
        if not DataTable.Active then
          DataTable.Active := True;

        { ¡¡¡¡ OJO !!!!
          Para asignar el ID en los detalles hay
          que tener en cuenta una cosa:
          Si se cambia el ID, ese detalle ya no
          pertenece a esa cabecera porque ya no se
          cumple la condición de la relacion:
            Master.ID = Detail.ID_PRESUPUESTO.

          Por esa razón no sirve hacer un recorrido
          desde el principio hasta el final porque
          las detalles van desapareciendo según asignamos
          el valor al campo ID y nos mueve aleatoriamente
          la posición del registro actual.

          Es mejor hacer un bucle sencillo hasta que
          "se gasten" todos los detalles. Cuando el
          RecordCount llegue a 0 quiere decir que hemos
          tratado todos los detalles.

          El bucle cambia en el caso de ser llamada esta funcion desde modificar
          un presupuesto ya que en ese caso si que hay que hacer un recorrido
          total de las tuplas de detalle.
        }

      if AEsNuevo then
      begin
        while RecordCount > 0 do
        begin
          DataTable.First;
          AsignarDatos(ADetalles, IDCabecera);
        end
      end
      else
      begin
        DataTable.First;
        while not DataTable.EOF do
        begin
          if DataTable.FieldByName('ID').AsInteger < 0  then
             AsignarDatos(ADetalles, IDCabecera);
          DataTable.Next
        end;
      end;
    end;
    finally
      DataTable.EnableControls;
    end;
  end;
end;

procedure TControllerDetallesArticulos.CambiarSignoDetalles(ADetalles: IDAStronglyTypedDataTable);
begin
//Cambia de signo los detalles de la pasados por parámetro
  if Assigned(ADetalles) then
  begin
    try
      BeginUpdate(ADetalles);

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

      with ADetalles.DataTable do
      begin
        First;
        while not EOF do
        begin
          if (FieldByName(CAMPO_CANTIDAD).AsInteger <> 0) then
          begin
            Edit;
            FieldByName(CAMPO_CANTIDAD).AsInteger := -1;
          end;
          Next;
        end;
      end;
    finally
      EndUpdate(ADetalles);
    end;
  end;
end;

constructor TControllerDetallesArticulos.Create;
begin
  inherited;
  AsignarController;
end;

procedure TControllerDetallesArticulos.DesglosarPorteDetalles(ImportePorte: Currency; ADetalles: IDAStronglyTypedDataTable);
var
  Unidades: Integer;
  ImporteUnidad: Currency;
  ABookmark : TBookmark;

begin
  try
    ABookmark := ADetalles.DataTable.GetBookMark;
    ADetalles.DataTable.DisableControls;
    ADetalles.DataTable.DisableEventHandlers;

    ADetalles.DataTable.First;
    Unidades := 0;
    while not ADetalles.DataTable.eof do
    begin
      if (ADetalles.DataTable.FieldByName(CAMPO_ID_ARTICULOS).AsInteger > 0) then
         Unidades := Unidades + ADetalles.DataTable.FieldByName(CAMPO_CANTIDAD).AsInteger;
      ADetalles.DataTable.Next;
    end;

    if Unidades > 0 then
       ImporteUnidad := ImportePorte / Unidades;

    ADetalles.DataTable.First;
    while not ADetalles.DataTable.eof do
    begin
      if (ADetalles.DataTable.FieldByName(CAMPO_ID_ARTICULOS).AsInteger > 0) then
      begin
        ADetalles.DataTable.Edit;
        ADetalles.DataTable.FieldByName(CAMPO_IMPORTE_PORTE).AsCurrency := ImporteUnidad * ADetalles.DataTable.FieldByName(CAMPO_CANTIDAD).AsInteger;
        ADetalles.DataTable.Post;
      end;
      ADetalles.DataTable.Next;
    end;

  finally
    ADetalles.DataTable.EnableEventHandlers;
    ADetalles.DataTable.GotoBookmark(ABookmark);
    ADetalles.DataTable.FreeBookmark(ABookmark);
    ADetalles.DataTable.EnableControls;
  end;

  ActualizarTotales(ADetalles);
end;

destructor TControllerDetallesArticulos.Destroy;
begin
  FArticulosController := Nil;
  inherited;
end;

procedure TControllerDetallesArticulos.EliminarArticulosProveedor(ADetalles: IDAStronglyTypedDataTable);
begin
  if Assigned(ADetalles) then
  begin
    with ADetalles.DataTable do
    begin
      First;
      while not Eof do
        if not FieldByName(CAMPO_ID_ARTICULOS).IsNull then
          Delete
        else
          Next;
    end;
  end;
end;

function TControllerDetallesArticulos.AnadirArticulo(ADetalles: IDAStronglyTypedDataTable; Referencia: String; TipoReferencia: TEnumReferencia; AClienteID: Integer = -1): Boolean;
var
  AArticulo: IBizArticulo;
begin
{
  Añade el artículo con la referencia pasada por parametro a los detalles pasados por parametro
  En el caso de asignar un AClienteID el articulo debe contener el descuento para ese cliente
}
  Result := False;
  BeginUpdate(ADetalles);
  ShowHourglassCursor;
  try
    case TipoReferencia of
      tCliente: AArticulo := FArticulosController.BuscarReferencia(Referencia, AClienteID);
      tProveedor: AArticulo := FArticulosController.BuscarReferenciaProveedor(Referencia, AClienteID)
    end;

    if Assigned(AArticulo) then
    begin
      AArticulo.DataTable.Active := True;
      if (AArticulo.RecordCount > 0) then
      begin
        RellenarDetalle(ADetalles, AArticulo);
        Result := True;
      end;
    end;
  finally
    EndUpdate(ADetalles);
    HideHourglassCursor;
  end;
end;

procedure TControllerDetallesArticulos.RellenarDescuento(ADetalles: IDAStronglyTypedDataTable; ADescuento: Float);
begin
  ADetalles.DataTable.FieldByName(CAMPO_DESCUENTO).AsFloat := ADescuento;
end;

procedure TControllerDetallesArticulos.RellenarDescuentos(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
//   Procedimiento que en los hijos se sobreescribirá para rellenar el campo descuento según necesidades
end;

procedure TControllerDetallesArticulos.RellenarDetalle(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
  if Assigned(ADetalles) then
  begin
    if not ADetalles.DataTable.Editing then
       ADetalles.DataTable.Edit;

    RellenarGenerales(ADetalles, AArticulos);
    RellenarImportes(ADetalles, AArticulos);
    RellenarDescuentos(ADetalles, AArticulos);

    ADetalles.DataTable.Post;
  end;
end;

procedure TControllerDetallesArticulos.RellenarGenerales(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
  if Assigned(AArticulos) then
  begin
    ADetalles.DataTable.FieldByName(CAMPO_ID_ARTICULOS).AsVariant := AArticulos.ID;
    ADetalles.DataTable.FieldByName(CAMPO_REFERENCIA).AsVariant := AArticulos.REFERENCIA;
    ADetalles.DataTable.FieldByName(CAMPO_REFERENCIA_PROVEEDOR).AsVariant := AArticulos.REFERENCIA_PROV;
    ADetalles.DataTable.FieldByName(CAMPO_CONCEPTO).AsVariant := AArticulos.DESCRIPCION;
    ADetalles.DataTable.FieldByName(CAMPO_CANTIDAD).AsInteger := 1;
  end;
end;

procedure TControllerDetallesArticulos.RellenarImportes(ADetalles: IDAStronglyTypedDataTable; AArticulos: IBizArticulo);
begin
//   Procedimiento que en los hijos se sobreescribirá para rellenar el campo importe según necesidades
end;

end.
