unit uDetallesPresupuestoController;

interface

uses
  Classes, uDADataTable,
  uControllerDetallesBase, uBizDetallesPresupuesto, uIDataModulePresupuestos;

const
  CAMPO_DESCUENTO = 'DESCUENTO';

  TIPO_DETALLE_DTO = 'Descuento';

type
  IDetallesPresupuestoController = interface(IControllerDetallesBase)
  ['{30AE4357-BA51-49D1-8113-A03C97B78F32}']
    procedure AsignarID(ADetalles: IBizDetallesPresupuesto; IDNuevo: Integer; AEsNuevo:Boolean);
    procedure AddCapitulo(ADataTable: IDAStronglyTypedDataTable);
  end;

  TDetallesPresupuestoController = class(TControllerDetallesBase, IDetallesPresupuestoController)
  private
    FDataModule : IDataModulePresupuestos;
    FImporteSubtotal: Double;

  protected
    procedure TratamientoDetalleDTO(DataTable: TDADataTable); virtual;
    procedure CalculoDetalleDTO(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual;

    procedure TratamientoDetalleSalto(DataTable: TDADataTable); override;
    procedure TratamientoDetalleTitulo(DataTable: TDADataTable); override;
    procedure TratamientoDetalleSubtotal(DataTable: TDADataTable); override;
    procedure TratamientoDetalleConcepto(DataTable: TDADataTable); override;

    procedure validarCampos(DataTable: TDADataTable); override;
    procedure CalculoDetalleSubtotal(DataTable: TDADataTable; var ImporteAcumulado : Double;
                                     var ImporteTotal : Double); override;
    function CalcularTotalesHijos(Modificar: Boolean; DataTable: TDADataTable;
                                  var ImporteAcumulado: Double; var ImporteTotal: Double): Double; override;
  public
    procedure Move(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray; Posiciones: Integer); override;
    procedure Add(ADataTable: IDAStronglyTypedDataTable; TipoConcepto: Variant); override;
    procedure Delete(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray); override;

    procedure AddCapitulo(ADataTable: IDAStronglyTypedDataTable);

    procedure AsignarID(ADetalles: IBizDetallesPresupuesto; IDNuevo: Integer; AEsNuevo:Boolean);
    constructor Create; override;
    destructor Destroy; override;
    function DarListaTiposDetalle: TStringList; override;
  end;

implementation

uses
  SysUtils, uDAInterfaces, Variants, uDataModulePresupuestos;

{ TDetallesPresupuestoController }

procedure TDetallesPresupuestoController.Add(ADataTable: IDAStronglyTypedDataTable; TipoConcepto: Variant);
var
  AuxNumOrden : Variant;

begin
//Sobreescribimos el método porque si añadimos teniendo el puntero en un registro
//de tipo subtotal, comprobamos si tiene descuento para añadir el nuevo registro
//despues de dicho concepto y no entre medias.
//inherited;

  AuxNumOrden := Null;
  BeginUpdate(ADataTable);
  try
    with ADataTable do
    begin
      if not DataTable.IsEmpty then
      begin
        AuxNumOrden := DataTable.FieldByName(CAMPO_POSICION).AsVariant;
        if  (getTipo(ADataTable, AuxNumOrden) = TIPO_DETALLE_SUBTOTAL)
        and (getTipo(ADataTable, (AuxNumOrden + 1)) = TIPO_DETALLE_DTO) then
        Inc(AuxNumOrden);
      end;

      AuxNumOrden := desplazarNPosiciones(DataTable, AuxNumOrden, 1);

      DataTable.Insert;
      DataTable.FieldByName(CAMPO_POSICION).AsInteger := AuxNumOrden;
      DataTable.FieldByName(CAMPO_TIPO).AsString := TipoConcepto;
      if (TipoConcepto = TIPO_DETALLE_TITULO)
      then DataTable.FieldByName(CAMPO_CONCEPTO).AsVariant := '{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Comic Sans MS;}}\viewkind4\uc1\pard\lang3082\ul\b\f0\fs20 Cap\''edtulo de ...\par}'
      else if (TipoConcepto = TIPO_DETALLE_SUBTOTAL)
      then DataTable.FieldByName(CAMPO_CONCEPTO).AsVariant := '{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Comic Sans MS;}{\f1\fnil Tahoma;}}\viewkind4\uc1\pard\lang3082\b\f0\fs20 Total cap\''edtulo de ...\b0\f1\fs16\par}'
      else if (TipoConcepto = TIPO_DETALLE_DTO)
      then DataTable.FieldByName(CAMPO_CONCEPTO).AsVariant := '{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Comic Sans MS;}{\f1\fnil Tahoma;}}\viewkind4\uc1\pard\lang3082\b\f0\fs20 Descuento especial ...\b0\f1\fs16\par}';
      DataTable.post;
    end;
  finally
    EndUpdate(ADataTable);
  end;
end;

procedure TDetallesPresupuestoController.AddCapitulo(ADataTable: IDAStronglyTypedDataTable);
begin
  BeginUpdate(ADataTable);
  try
    with ADataTable do
    begin
      Add(ADataTable, TIPO_DETALLE_TITULO);
      Add(ADataTable, TIPO_DETALLE_CONCEPTO);
      Add(ADataTable, TIPO_DETALLE_CONCEPTO);
      Add(ADataTable, TIPO_DETALLE_CONCEPTO);
      Add(ADataTable, TIPO_DETALLE_SUBTOTAL);
      Add(ADataTable, TIPO_DETALLE_DTO);
    end;
  finally
    EndUpdate(ADataTable);
  end;
end;

procedure TDetallesPresupuestoController.AsignarID(ADetalles: IBizDetallesPresupuesto;
  IDNuevo: Integer; AEsNuevo:Boolean);
begin
  with ADetalles do
  begin
    DataTable.DisableControls;
    try
      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 sí que hay que hacer un recorrido
        total de las tuplas de detalle.
      }

      if AEsNuevo then
      begin
        while RecordCount > 0 do
        begin
          DataTable.First;
          Edit;
          ID := FDataModule.GetNextID(DataTable.LogicalName);
          ID_PRESUPUESTO := IDNuevo;
          Post
        end
      end
      else
      begin
        DataTable.First;
        while not DataTable.EOF do
        begin
          if not EsNuevo then
          begin
            Edit;
            ID := FDataModule.GetNextID(DataTable.LogicalName);
            ID_PRESUPUESTO := IDNuevo;
            Post
          end;
          DataTable.Next
        end;
      end;
    finally
      DataTable.EnableControls;
    end;
  end;
end;

function TDetallesPresupuestoController.CalcularTotalesHijos(Modificar: Boolean;
                                                             DataTable: TDADataTable;
                                                             var ImporteAcumulado, ImporteTotal: Double): Double;
begin
  if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_DTO) then
  begin
    if Modificar then
       TratamientoDetalleDTO(DataTable);
    CalculoDetalleDTO(DataTable, ImporteAcumulado, ImporteTotal);
  end
end;

procedure TDetallesPresupuestoController.CalculoDetalleDTO(DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double);
var
  ImporteDto: Double;
begin
  ImporteDto := (-1)*((FImporteSubtotal * DataTable.FieldByName(CAMPO_DESCUENTO).AsFloat) / 100);

  with DataTable do
  begin
    if not Editing then Edit;
    FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat := ImporteDto;
    Post;
  end;

  FImporteSubtotal := 0;
  ImporteTotal := ImporteTotal + ImporteDto;  
end;

procedure TDetallesPresupuestoController.CalculoDetalleSubtotal(
  DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double);
begin
  FImporteSubtotal := ImporteAcumulado;
  inherited;
end;

constructor TDetallesPresupuestoController.Create;
begin
  inherited;
  FDataModule := TDataModulePresupuestos.Create(Nil);
  FImporteSubtotal := 0;
end;

function TDetallesPresupuestoController.DarListaTiposDetalle: TStringList;
begin
  Result := inherited DarListaTiposDetalle;
  Result.Values[TIPO_DETALLE_DTO] := 'Descuento';
end;

procedure TDetallesPresupuestoController.Delete(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray);
var
  i: integer;
  AField: TDAField;
  DeletePosicion: Integer;
begin
//inherited

  AField := ADataTable.DataTable.FindField(CAMPO_POSICION);
  if not Assigned(AField) then
     raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (Delete)');

  BeginUpdate(ADataTable);
  try
    with ADataTable do
    begin
      for i := 0 to High(POSICION) do
      begin
        DataTable.First;
        DeletePosicion := POSICION[i];
        if DataTable.Locate(CAMPO_POSICION, IntToStr(DeletePosicion), []) then
        begin
          if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_SUBTOTAL) then
          begin
            DataTable.Delete;
            DataTable.First;
            if  (DataTable.Locate(CAMPO_POSICION, IntToStr((DeletePosicion + 1)), []))
            and (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_DTO) then
                DataTable.Delete;
          end
          else DataTable.Delete;
        end;
      end;
      renumerar(DataTable, DeletePosicion);
    end;
  finally
    EndUpdate(ADataTable);
  end;
end;

destructor TDetallesPresupuestoController.Destroy;
begin
  FDataModule := Nil;
  inherited;
end;

procedure TDetallesPresupuestoController.Move(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray;
  Posiciones: Integer);
var
  i:Integer;
  AField: TDAField;
  PosicionesAux: Integer;
begin
//Sobreescribimos el método porque en el caso de mover un concepto y ser un subtitulo
//el concepto a intercambiar posición comprobamos si tiene descuento para desplazar dos unidades
//el concepto a mover
//inherited;

  AField := ADataTable.DataTable.FindField(CAMPO_POSICION);
  if not Assigned(AField) then
     raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (Move presupuestos controller)');

  BeginUpdate(ADataTable);
  try
    with ADataTable do
    begin
      //Empezamos desde abajo
      if Posiciones > 0 then
      begin
         for i:= High(POSICION) downto 0 do
         begin
           if  (getTipo(ADataTable, (POSICION[i]+1)) = TIPO_DETALLE_SUBTOTAL)
           and (getTipo(ADataTable, (POSICION[i]+2)) = TIPO_DETALLE_DTO) then
           begin
             Mover(DataTable, POSICION[i], Posiciones);
             Mover(DataTable, POSICION[i]+1, Posiciones)
           end
           else
             Mover(DataTable, POSICION[i], Posiciones);
         end;
      end
      else
      //Empezamos desde arriba
         for i:= 0 to High(POSICION) do
         begin
           if  (getTipo(ADataTable, (POSICION[i]-1)) = TIPO_DETALLE_DTO)
           and (getTipo(ADataTable, (POSICION[i]-2)) = TIPO_DETALLE_SUBTOTAL) then
           begin
             Mover(DataTable, POSICION[i], Posiciones);
             Mover(DataTable, POSICION[i]-1, Posiciones)
           end
           else
             Mover(DataTable, POSICION[i], Posiciones);
         end;
    end;
  finally
    EndUpdate(ADataTable);
  end;
end;

procedure TDetallesPresupuestoController.TratamientoDetalleConcepto(DataTable: TDADataTable);
begin
  inherited;

  with DataTable do
  begin
    if not Editing then Edit;
    FieldByName(CAMPO_DESCUENTO).AsVariant := Null;
    Post;
  end;
end;

procedure TDetallesPresupuestoController.TratamientoDetalleDTO(DataTable: TDADataTable);
begin
  with DataTable do
  begin
    if not Editing then Edit;
    if (FieldByName(CAMPO_CONCEPTO).AsString = CTE_DESC_SALTO) then
       FieldByName(CAMPO_CONCEPTO).AsVariant := Null;
    FieldByName(CAMPO_CANTIDAD).AsVariant := Null;
    FieldByName(CAMPO_IMPORTE_UNIDAD).AsVariant := Null;
    Post;
  end;
end;

procedure TDetallesPresupuestoController.TratamientoDetalleSalto(DataTable: TDADataTable);
begin
  inherited;

  with DataTable do
  begin
    if not Editing then Edit;
    FieldByName(CAMPO_DESCUENTO).AsVariant := Null;
    Post;
  end;
end;

procedure TDetallesPresupuestoController.TratamientoDetalleSubtotal(DataTable: TDADataTable);
begin
  inherited;

  with DataTable do
  begin
    if not Editing then Edit;
    FieldByName(CAMPO_DESCUENTO).AsVariant := Null;
    Post;
  end;
end;

procedure TDetallesPresupuestoController.TratamientoDetalleTitulo(DataTable: TDADataTable);
begin
  inherited;

  with DataTable do
  begin
    if not Editing then Edit;
    FieldByName(CAMPO_DESCUENTO).AsVariant := Null;
    Post;
  end;
end;

procedure TDetallesPresupuestoController.validarCampos(DataTable: TDADataTable);
var
  AField: TDAField;

begin
  inherited;

  //Validamos la existencia de todos los campos necesarios
  AField := DataTable.FindField(CAMPO_DESCUENTO);
  if not Assigned(AField) then
     raise Exception.Create('Campo ' + CAMPO_DESCUENTO + ' no encontrado (validarCampos)');
end;

end.
