unit uControllerDetallesBase; interface uses Classes, Variants, uDACDSDataTable, uDADataTable, uControllerBase, uCalculosUtils; const TIPO_DETALLE_CONCEPTO = 'Concepto'; TIPO_DETALLE_TITULO = 'Titulo'; TIPO_DETALLE_TITULO_OPCIONAL = 'Titulo opcional'; TIPO_DETALLE_SUBTOTAL = 'Subtotal'; TIPO_DETALLE_SALTO = 'Salto'; TIPO_DETALLE_DESCUENTO = 'Descuento'; CTE_DESC_SALTO = 'SALTO DE PAGINA >>'; type TIntegerArray = array of Integer; IControllerDetallesBase = interface(ISujeto) ['{F0B0E714-EC0D-4B6B-98B1-76F72F70B735}'] function GetTipo(ADataTable: IDAStronglyTypedDataTable; pPosicion: Integer): String; procedure Clear(ADataTable: IDAStronglyTypedDataTable); procedure Add(ADataTable: IDAStronglyTypedDataTable; TipoConcepto: Variant); procedure Delete(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray); procedure Move(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray; Posiciones: Integer); procedure BeginUpdate(ADataTable: IDAStronglyTypedDataTable); procedure EndUpdate(ADataTable: IDAStronglyTypedDataTable); // procedure Copy(SMExport: TSMExportToClipboard); // procedure Paste; procedure ActualizarTotales(ADataTable: IDAStronglyTypedDataTable); function DarTotalImporteTotal(ADataTable: IDAStronglyTypedDataTable): Double; function LocalizarPosicion(ADataTable: IDAStronglyTypedDataTable; const APosicion: Integer): Boolean; procedure Renumerar(DataTable: TDADataTable; LocalizaPosicion: Integer); function DarListaTiposDetalle: TStringList; procedure ValidarCierreCapitulos(ADataTable: IDAStronglyTypedDataTable); end; TControllerDetallesBase = class (TSujeto, IControllerDetallesBase) private fUpdateCount: Integer; function CalcularTotales(Modificar: boolean; DataTable: TDADataTable): Double; protected procedure Renumerar(DataTable: TDADataTable; LocalizaPosicion: Integer); function DesplazarNPosiciones(DataTable: TDADataTable; NumOrdenIni: Variant; NPosiciones: Variant): Integer; procedure Mover(DataTable: TDADataTable; Posicion: Integer; NumPosiciones: Integer); //Si en los hijos existen campos a tener en cuenta se sobreescribira este metodo procedure ValidarCampos(DataTable: TDADataTable); virtual; //Si sobreescribimos este método podremos tener en cuenta otras columnas para el calculo del importe total de un concepto function CalcularImporteTotalConcepto(DataTable: TDADataTable): Double; virtual; procedure TratamientoDetalleConcepto(DataTable: TDADataTable); virtual; procedure CalculoDetalleConcepto(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double; const Opcional: Boolean); virtual; procedure TratamientoDetalleSalto(DataTable: TDADataTable); virtual; procedure CalculoDetalleSalto(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual; procedure TratamientoDetalleTitulo(DataTable: TDADataTable); virtual; procedure CalculoDetalleTitulo(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual; procedure TratamientoDetalleTituloOpcional(DataTable: TDADataTable); virtual; procedure CalculoDetalleTituloOpcional(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual; procedure TratamientoDetalleSubtotal(DataTable: TDADataTable); virtual; procedure CalculoDetalleSubtotal(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual; procedure TratamientoDetalleDescuento(DataTable: TDADataTable); virtual; procedure CalculoDetalleDescuento(DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double); virtual; //Si sobreescribimos este metodo es para continuar el CalcularTotales segun los tipos de concepto de los hijos function CalcularTotalesHijos(Modificar: boolean; DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double): Double; virtual; public constructor Create; override; destructor Destroy; override; function GetTipo(ADataTable: IDAStronglyTypedDataTable; pPosicion: Integer): String; procedure Clear(ADataTable: IDAStronglyTypedDataTable); procedure Add(ADataTable: IDAStronglyTypedDataTable; TipoConcepto: Variant); virtual; procedure Delete(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray); virtual; procedure Move(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray; Posiciones: Integer); virtual; procedure BeginUpdate(ADataTable: IDAStronglyTypedDataTable); procedure EndUpdate(ADataTable: IDAStronglyTypedDataTable); // procedure Copy(SMExport: TSMExportToClipboard); // procedure Paste; procedure ActualizarTotales(ADataTable: IDAStronglyTypedDataTable); function DarTotalImporteTotal(ADataTable: IDAStronglyTypedDataTable): Double; function DarListaTiposDetalle: TStringList; virtual; function LocalizarPosicion(ADataTable: IDAStronglyTypedDataTable; const APosicion: Integer): Boolean; // Comprueba que todos los capitulos tengan su subtotal de cierre procedure ValidarCierreCapitulos(ADataTable: IDAStronglyTypedDataTable); end; implementation { TControllerDetallesBase } uses cxControls, SysUtils, DB, uDAInterfaces, Dialogs; procedure TControllerDetallesBase.ActualizarTotales(ADataTable: IDAStronglyTypedDataTable); begin BeginUpdate(ADataTable); try CalcularTotales(True, ADataTable.DataTable); finally EndUpdate(ADataTable); end; end; procedure TControllerDetallesBase.Add(ADataTable: IDAStronglyTypedDataTable; TipoConcepto: Variant); var AuxNumOrden : Integer; begin BeginUpdate(ADataTable); try with ADataTable do begin AuxNumOrden := desplazarNPosiciones(DataTable, DataTable.FieldByName(CAMPO_POSICION).AsVariant, 1); DataTable.Insert; DataTable.FieldByName(CAMPO_POSICION).AsInteger := AuxNumOrden; DataTable.FieldByName(CAMPO_TIPO).AsVariant := TipoConcepto; DataTable.Post; end; finally EndUpdate(ADataTable); end; end; procedure TControllerDetallesBase.BeginUpdate(ADataTable: IDAStronglyTypedDataTable); begin ShowHourglassCursor; Inc(fUpdateCount); ADataTable.DataTable.DisableControls; end; function TControllerDetallesBase.CalcularImporteTotalConcepto(DataTable: TDADataTable): Double; begin with DataTable do Result := FieldByName(CAMPO_CANTIDAD).asInteger * FieldByName(CAMPO_IMPORTE_UNIDAD).AsFloat; end; function TControllerDetallesBase.CalcularTotales(Modificar: boolean; DataTable: TDADataTable): Double; { funcion que recalcula todos los detalles de la tabla pasada por parametro y devuelve la cantidad total de los mismos } var AuxPosicionIni : Integer; AuxPosicion : Integer; AuxImporteAcumulado : Double; AuxImporteTotal : Double; SemaforoOpcional: Boolean; begin if (DataTable.State in dsEditModes) then DataTable.Post; ValidarCampos(DataTable); DataTable.DisableControls; AuxPosicionIni := DataTable.FieldByName(CAMPO_POSICION).AsInteger; AuxPosicion := 0; AuxImporteAcumulado := 0; AuxImporteTotal := 0; SemaforoOpcional := False; try DataTable.First; while DataTable.Locate(CAMPO_POSICION, AuxPosicion, []) do begin //SALTOS DE LINEA if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_SALTO) then begin if Modificar then TratamientoDetalleSalto(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos en hijos CalculoDetalleSalto(DataTable, AuxImporteAcumulado, AuxImporteTotal); //Se podrá sobreescribir para posibles nuevos calculos de los hijos end //TITULOS else if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_TITULO) then begin SemaforoOpcional := False; if Modificar then TratamientoDetalleTitulo(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos en hijos CalculoDetalleTitulo(DataTable, AuxImporteAcumulado, AuxImporteTotal); //Se podrá sobreescribir para posibles nuevos calculos de los hijos end //TITULOS OPCIONALES else if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_TITULO_OPCIONAL) then begin SemaforoOpcional := True; if Modificar then TratamientoDetalleTituloOpcional(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos en hijos CalculoDetalleTituloOpcional(DataTable, AuxImporteAcumulado, AuxImporteTotal); //Se podrá sobreescribir para posibles nuevos calculos de los hijos end //SUBTOTAL else if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_SUBTOTAL) then begin if Modificar then TratamientoDetalleSubtotal(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos CalculoDetalleSubtotal(DataTable, AuxImporteAcumulado, AuxImporteTotal); //Se podrá sobreescribir para posibles nuevos calculos de los hijos SemaforoOpcional := False; end //DESCUENTO DE CAPITULO else if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_DESCUENTO) then begin if Modificar then TratamientoDetalleDescuento(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos CalculoDetalleDescuento(DataTable, AuxImporteAcumulado, AuxImporteTotal); //Se podrá sobreescribir para posibles nuevos calculos de los hijos end //CONCEPTOS else if (DataTable.FieldByName(CAMPO_TIPO).AsString = TIPO_DETALLE_CONCEPTO) then begin if Modificar then TratamientoDetalleConcepto(DataTable); //Se podrá sobreescribir para que se tengan en cuenta nuevos campos CalculoDetalleConcepto(DataTable, AuxImporteAcumulado, AuxImporteTotal, SemaforoOpcional); //Se podrá sobreescribir para posibles nuevos calculos de los hijos end //HIJOS else CalcularTotalesHijos(Modificar, DataTable, AuxImporteAcumulado, AuxImporteTotal); Inc(AuxPosicion); DataTable.First; end; finally //Dejamos el puntero en la misma posición que la que partió DataTable.Locate(CAMPO_POSICION, AuxPosicionIni, []); DataTable.EnableControls; end; Result := AuxImporteTotal; end; function TControllerDetallesBase.CalcularTotalesHijos(Modificar: boolean; DataTable: TDADataTable; var ImporteAcumulado : Double; var ImporteTotal : Double): Double; begin // Result := 0; end; procedure TControllerDetallesBase.CalculoDetalleConcepto(DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double; const Opcional: Boolean); begin with DataTable do begin ImporteAcumulado := ImporteAcumulado + FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat; if not Opcional then ImporteTotal := ImporteTotal + FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat; end; end; procedure TControllerDetallesBase.CalculoDetalleDescuento( DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double); var ImporteDto : Double; begin with DataTable do begin if not Editing then Edit; ImporteDto := (-1)*((ImporteTotal * DataTable.FieldByName(CAMPO_DESCUENTO).AsFloat) / 100); ImporteTotal := ImporteTotal + ImporteDto; FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat := ImporteTotal; Post; end; end; procedure TControllerDetallesBase.CalculoDetalleSalto(DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double); begin with DataTable do begin if not Editing then Edit; FieldByName(CAMPO_CANTIDAD).AsVariant := Null; FieldByName(CAMPO_IMPORTE_UNIDAD).AsVariant := Null; FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null; Post; end; end; procedure TControllerDetallesBase.CalculoDetalleSubtotal(DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double); begin with DataTable do begin if not Editing then Edit; FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat := ImporteAcumulado; Post; end; ImporteAcumulado := 0; end; procedure TControllerDetallesBase.CalculoDetalleTitulo(DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double); begin // end; procedure TControllerDetallesBase.CalculoDetalleTituloOpcional( DataTable: TDADataTable; var ImporteAcumulado, ImporteTotal: Double); begin // end; procedure TControllerDetallesBase.Clear(ADataTable: IDAStronglyTypedDataTable); begin // end; constructor TControllerDetallesBase.Create; begin inherited; end; function TControllerDetallesBase.DarListaTiposDetalle: TStringList; begin Result := TStringList.Create; Result.Values[TIPO_DETALLE_CONCEPTO] := TIPO_DETALLE_CONCEPTO; Result.Values[TIPO_DETALLE_TITULO] := TIPO_DETALLE_TITULO; Result.Values[TIPO_DETALLE_TITULO_OPCIONAL] := TIPO_DETALLE_TITULO_OPCIONAL; Result.Values[TIPO_DETALLE_SUBTOTAL] := TIPO_DETALLE_SUBTOTAL; // Result.Values[TIPO_DETALLE_DESCUENTO] := TIPO_DETALLE_DESCUENTO; // Result.Values[TIPO_DETALLE_SALTO] := TIPO_DETALLE_SALTO; end; function TControllerDetallesBase.DarTotalImporteTotal(ADataTable: IDAStronglyTypedDataTable): Double; begin Result := CalcularTotales(False, ADataTable.DataTable); end; procedure TControllerDetallesBase.Delete(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray); var i: integer; AField: TDAField; DeletePosicion: Integer; begin DeletePosicion := 0; 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, DeletePosicion, []) then DataTable.Delete; end; Renumerar(DataTable, DeletePosicion); end; finally EndUpdate(ADataTable); end; end; function TControllerDetallesBase.DesplazarNPosiciones(DataTable: TDADataTable; NumOrdenIni: Variant; NPosiciones: Variant): Integer; { Función que desplaza NPosiciones el numero de orden a partir del elemento con el número de orden dado. Devuelve el numero de orden del primer elemento del hueco generado } var AuxNumOrden: Integer; AuxNumPos: Integer; AField: TDAField; begin AField := DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (desplazarNPosiciones)'); if VarIsNull(NPosiciones) then AuxNumPos := 1 else AuxNumPos := NPosiciones; if VarIsNull(NumOrdenIni) then AuxNumOrden := 0 else AuxNumOrden := NumOrdenIni + 1; //Añadimos por abajo siempre Result := AuxNumOrden; with DataTable do begin First; while not EOF do begin if (FieldByName(CAMPO_POSICION).AsInteger >= AuxNumOrden) then begin if not Editing then Edit; FieldByName(CAMPO_POSICION).AsInteger := FieldByName(CAMPO_POSICION).AsInteger + AuxNumPos; Post; end; Next; end; end; end; destructor TControllerDetallesBase.Destroy; begin inherited; end; procedure TControllerDetallesBase.EndUpdate(ADataTable: IDAStronglyTypedDataTable); begin Dec(fUpdateCount); if fUpdateCount = 0 then begin CalcularTotales(True, ADataTable.DataTable); AvisarObservadores(ADataTable); end; ADataTable.DataTable.EnableControls; HideHourglassCursor; end; function TControllerDetallesBase.getTipo(ADataTable: IDAStronglyTypedDataTable; pPosicion: Integer): String; var posIni: integer; AField: TDAField; begin AField := ADataTable.DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (getTipo)'); Result := ''; BeginUpdate(ADataTable); try with ADataTable do begin //Guardamos la posicion en la que estamos posIni := DataTable.FieldByName(CAMPO_POSICION).AsInteger; DataTable.First; if DataTable.Locate(CAMPO_POSICION, pPosicion, []) then Result := DataTable.FieldByName(CAMPO_TIPO).AsString; //Volvemos a posicionar el puntero donde estaba DataTable.First; if not DataTable.Locate(CAMPO_POSICION, posIni, []) then raise Exception.Create('La posición ' + IntToStr(posIni) + ' no existe (getTipo)'); end; finally EndUpdate(ADataTable); end; end; function TControllerDetallesBase.LocalizarPosicion( ADataTable: IDAStronglyTypedDataTable; const APosicion: Integer): Boolean; var AField: TDAField; begin AField := ADataTable.DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (LocalizarPosicion)'); with ADataTable.DataTable do begin DisableControls; DisableEventHandlers; ShowHourglassCursor; try First; Result := Locate(CAMPO_POSICION, APosicion, []); finally HideHourglassCursor; EnableEventHandlers; EnableControls; end; end; end; procedure TControllerDetallesBase.Move(ADataTable: IDAStronglyTypedDataTable; Posicion: TIntegerArray; Posiciones: Integer); var i:Integer; begin BeginUpdate(ADataTable); try with ADataTable do begin //Empezamos desde abajo if Posiciones > 0 then for i:= High(POSICION) downto 0 do Mover(DataTable, POSICION[i], Posiciones) else //Empezamos desde arriba for i:= 0 to High(POSICION) do Mover(DataTable, POSICION[i], Posiciones); end; finally EndUpdate(ADataTable); end; end; procedure TControllerDetallesBase.Mover(DataTable: TDADataTable; Posicion: Integer; NumPosiciones: Integer); { procedimiento que desplaza el número de posiciones (NumPosiciones) pasados por parametro a la posicion (Posicion) dada, en caso de ser negativo será hacia arriba y positivo hacia abajo } var AuxOrden : Integer; AuxID : Integer; AField: TDAField; begin AField := DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (mover)'); AField := DataTable.FindField(CAMPO_ID); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_ID + ' no encontrado (mover)'); //Buscamos el elemento con la posicion pasada por parametro DataTable.First; if not DataTable.Locate(CAMPO_POSICION, Posicion, []) then raise Exception.Create('Error, no se ha encontrado la POSICION [' + IntToStr(Posicion) + '] (mover)'); //Guardamos el id del elemento a cambiar de posicion y calculamos su nueva posicion AuxID := DataTable.FieldByName(CAMPO_ID).AsInteger; AuxOrden := Posicion + NumPosiciones; DataTable.First; if DataTable.Locate(CAMPO_POSICION, AuxOrden, []) then begin if not DataTable.Editing then DataTable.Edit; DataTable.FieldByName(CAMPO_POSICION).AsInteger := DataTable.FieldByName(CAMPO_POSICION).AsInteger - NumPosiciones; //Se hace dentro por si es el ultimo o el primero DataTable.First; if not DataTable.Locate(CAMPO_ID, AuxID, []) then raise Exception.Create('Error, no se ha encontrado el ID [' + IntToStr(AuxID) + '] (mover)'); if not DataTable.Editing then DataTable.Edit; DataTable.FieldByName(CAMPO_POSICION).AsInteger := AuxOrden; DataTable.Post; end; //Colocamos el puntero en la posición en la que estaba DataTable.First; DataTable.Locate(CAMPO_ID, AuxID, []); end; procedure TControllerDetallesBase.Renumerar(DataTable: TDADataTable; LocalizaPosicion: Integer); { procedimiento que renumera todos los conceptos de la tabla dada por parametro } var i, j : Integer; AField: TDAField; begin AField := DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (renumerar)'); with DataTable do begin for i:=0 to RecordCount-1 do begin First; if not Locate(CAMPO_POSICION, i, []) then begin j := i; First; while not Locate(CAMPO_POSICION, j, []) do begin Inc(j); First; end; if not Editing then Edit; FieldByName(CAMPO_POSICION).AsInteger := i; Post; end; end; //Posicionamos el puntero en la posición dada por parametro Locate(CAMPO_POSICION, LocalizaPosicion, []); end; end; procedure TControllerDetallesBase.TratamientoDetalleConcepto(DataTable: TDADataTable); begin with DataTable do begin if not Editing then Edit; //Si alguno de los campos de calculo de total es nulo el total tambien será nulo if (VarIsNull(FieldByName(CAMPO_CANTIDAD).AsVariant) or VarIsNull(FieldByName(CAMPO_IMPORTE_UNIDAD).AsVariant)) then FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null else FieldByName(CAMPO_IMPORTE_TOTAL).AsFloat := CalcularImporteTotalConcepto(DataTable); Post; end; end; procedure TControllerDetallesBase.TratamientoDetalleDescuento( 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; FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null; Post; end; end; procedure TControllerDetallesBase.TratamientoDetalleSalto(DataTable: TDADataTable); begin with DataTable do begin if not Editing then Edit; FieldByName(CAMPO_CONCEPTO).AsString := CTE_DESC_SALTO; FieldByName(CAMPO_CANTIDAD).AsVariant := Null; FieldByName(CAMPO_IMPORTE_UNIDAD).AsVariant := Null; FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null; Post; end; end; procedure TControllerDetallesBase.TratamientoDetalleSubtotal(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 TControllerDetallesBase.TratamientoDetalleTitulo(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; FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null; Post; end; end; procedure TControllerDetallesBase.TratamientoDetalleTituloOpcional(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; FieldByName(CAMPO_IMPORTE_TOTAL).AsVariant := Null; Post; end; end; procedure TControllerDetallesBase.ValidarCampos(DataTable: TDADataTable); var AField: TDAField; begin //Validamos la existencia de todos los campos necesarios AField := DataTable.FindField(CAMPO_POSICION); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_POSICION + ' no encontrado (validarCampos)'); AField := DataTable.FindField(CAMPO_TIPO); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_TIPO + ' no encontrado (validarCampos)'); AField := DataTable.FindField(CAMPO_CANTIDAD); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_CANTIDAD + ' no encontrado (validarCampos)'); AField := DataTable.FindField(CAMPO_IMPORTE_UNIDAD); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_IMPORTE_UNIDAD + ' no encontrado (validarCampos)'); AField := DataTable.FindField(CAMPO_IMPORTE_TOTAL); if not Assigned(AField) then raise Exception.Create('Campo ' + CAMPO_IMPORTE_TOTAL + ' no encontrado (validarCampos)'); end; procedure TControllerDetallesBase.ValidarCierreCapitulos( ADataTable: IDAStronglyTypedDataTable); var ATipoCampo : String; bEnCapitulo : Boolean; AuxPosicionIni : Integer; AuxPosicion : Integer; begin bEnCapitulo := False; ATipoCampo := ''; if (ADataTable.DataTable.State in dsEditModes) then ADataTable.DataTable.Post; ValidarCampos(ADataTable.DataTable); AuxPosicionIni := ADataTable.DataTable.FieldByName(CAMPO_POSICION).AsInteger; AuxPosicion := 0; BeginUpdate(ADataTable); try ADataTable.DataTable.First; while ADataTable.DataTable.Locate(CAMPO_POSICION, AuxPosicion, []) do begin ATipoCampo := ADataTable.DataTable.FieldByName(CAMPO_TIPO).AsString; if (ATipoCampo = TIPO_DETALLE_SUBTOTAL) and bEnCapitulo then bEnCapitulo := False; if ((ATipoCampo = TIPO_DETALLE_TITULO) or (ATipoCampo = TIPO_DETALLE_TITULO_OPCIONAL)) then begin if bEnCapitulo then begin ADataTable.DataTable.First; if ADataTable.DataTable.Locate(CAMPO_POSICION, AuxPosicion-1, []) then begin Add(ADataTable, TIPO_DETALLE_SUBTOTAL); bEnCapitulo := False; end; end else bEnCapitulo := True; end; Inc(AuxPosicion); ADataTable.DataTable.First; end; // ¿Se ha acabado la tabla y queda algún capítulo sin cerrar? if bEnCapitulo then if ADataTable.DataTable.Locate(CAMPO_POSICION, AuxPosicion-1, []) then begin Add(ADataTable, TIPO_DETALLE_SUBTOTAL); bEnCapitulo := False; end; finally //Dejamos el puntero en la misma posición que la que partió ADataTable.DataTable.Locate(CAMPO_POSICION, AuxPosicionIni, []); EndUpdate(ADataTable); end; end; end.