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.