unit uContactosController; interface uses Windows, Forms, Classes, Controls, Contnrs, SysUtils, uDADataTable, uBizContactos, uBizContactosDatosBancarios, uIDataModuleContactos, uViewGridBase; type IContactosController = interface ['{43F9A4ED-6563-4F95-9A56-330CF9C66731}'] function Buscar(const ID: Integer): IBizContacto; function BuscarTodos: IBizContacto; procedure Ver(AContacto : IBizContacto); procedure VerTodos(AContactos: IBizContacto); function Nuevo : IBizContacto; procedure Anadir(AContacto : IBizContacto); procedure Eliminar(const ID : Integer); overload; function Eliminar(AContacto : IBizContacto): Boolean; overload; function Guardar(AContacto : IBizContacto): Boolean; procedure DescartarCambios(AContacto : IBizContacto); function Existe(const ID: Integer) : Boolean; function Duplicar(AContacto : IBizContacto) : IBizContacto; function Localizar(AContactos: IBizContacto; const ID : Integer): Boolean; function ElegirContacto(AContactos : IBizContacto; AMensaje: String; AMultiSelect: Boolean): IBizContacto; function ExtraerSeleccionados(AContactos: IBizContacto; Const ViewGrid: IViewGridBase = Nil) : IBizContacto; procedure Preview(AContactos : IBizContacto); function Print(AContactos : IBizContacto): Boolean; end; TContactosController = class(TInterfacedObject, IContactosController) protected FDataModule : IDataModuleContactos; procedure AsignarID(AContacto: IBizContacto; const IDNuevo : Integer); virtual; procedure AsignarIDDetalles(AContacto: IBizContacto; const IDCabecera : Integer; ADataTable : TDADataTable); procedure FiltrarEmpresa(AContacto : IBizContacto); virtual; function _Vacio : IBizContacto; virtual; function ValidarContacto(AContacto : IBizContacto): Boolean; virtual; public constructor Create; virtual; destructor Destroy; override; procedure Eliminar(const ID : Integer); overload; virtual; function Eliminar(AContacto : IBizContacto): Boolean; overload; virtual; function Guardar(AContacto : IBizContacto): Boolean; procedure DescartarCambios(AContacto : IBizContacto); virtual; function Existe(const ID: Integer) : Boolean; virtual; procedure Anadir(AContacto : IBizContacto); virtual; function Duplicar(AContacto: IBizContacto): IBizContacto; virtual; function ExtraerSeleccionados(AContactos: IBizContacto; Const ViewGrid: IViewGridBase = Nil) : IBizContacto; virtual; function Localizar(AContactos: IBizContacto; const ID : Integer): Boolean; // Se implementa en los controladores hijos function Buscar(const ID: Integer): IBizContacto; virtual; abstract; function BuscarTodos: IBizContacto; virtual; abstract; function Nuevo : IBizContacto; virtual; abstract; procedure Ver(AContacto : IBizContacto); virtual; abstract; procedure VerTodos(AContactos: IBizContacto); virtual; abstract; function ElegirContacto(AContactos : IBizContacto; AMensaje: String; AMultiSelect: Boolean): IBizContacto; virtual; abstract; procedure Preview(AContactos : IBizContacto); function Print(AContactos : IBizContacto): Boolean; end; implementation uses Dialogs, uEditorRegistryUtils, cxControls, DB, uDAInterfaces, uDataTableUtils, uDataModuleUsuarios, schContactosClient_Intf, uEtiquetasContactosReportController; { TContactosController } procedure TContactosController.Anadir(AContacto: IBizContacto); begin AContacto.Insert; end; procedure TContactosController.AsignarID(AContacto: IBizContacto; const IDNuevo : Integer); begin if not Assigned(AContacto) then raise Exception.Create ('Contacto no asignado'); if AContacto.EsNuevo then begin { ¡¡¡ OJO !!! Primero cambiamos el ID de las tablas detalles porque si cambiamos antes el ID de la cabecera deja de funcionar la relacion M/D y no encontraríamos las filas detalle. --> MASTER.ID = DETAIL.ID_CONTACTO <-- } with AContacto._Categorias do begin if not DataTable.Active then DataTable.Active := True; Insert; ID_CONTACTO := IDNuevo; ID_CATEGORIA := AContacto.ID_CATEGORIA; Post; end; end; { Los datos bancarios hay que comprobarlos siempre tanto en inserción como en modificación. } if Assigned(AContacto.DatosBancarios) then AsignarIDDetalles(AContacto, IDNuevo, AContacto.DatosBancarios.DataTable); { Las direcciones extras hay que comprobarlas siempre tanto en inserción como en modificación. } if Assigned(AContacto.Direcciones) then AsignarIDDetalles(AContacto, IDNuevo, AContacto.Direcciones.DataTable); if AContacto.EsNuevo then begin AContacto.Edit; AContacto.ID := IDNuevo; AContacto.ID_EMPRESA := dmUsuarios.IDEmpresaActual; AContacto.Post; end; end; function TContactosController.Duplicar(AContacto: IBizContacto): IBizContacto; begin Result := Self._Vacio; ShowHourglassCursor; try DuplicarRegistros(AContacto.DataTable, Result.DataTable, mdrActual); DuplicarRegistros(AContacto.DatosBancarios.DataTable, Result.DatosBancarios.DataTable, mdrTodos); if Assigned(AContacto.Direcciones) then DuplicarRegistros(AContacto.Direcciones.DataTable, Result.Direcciones.DataTable, mdrTodos); // Hay que dejar algunos campos como si fuera un contacto nuevo Result.Edit; with Result do begin ID_EMPRESA := dmUsuarios.IDEmpresaActual; USUARIO := dmUsuarios.LoginInfo.Usuario; end; Result.Post; finally HideHourglassCursor; end; end; constructor TContactosController.Create; begin FDataModule := NIL; end; procedure TContactosController.DescartarCambios(AContacto: IBizContacto); begin if not Assigned(AContacto) then raise Exception.Create ('Contacto no asignado'); ShowHourglassCursor; try if (AContacto.State in dsEditModes) then AContacto.Cancel; AContacto.DataTable.CancelUpdates; finally HideHourglassCursor; end; end; destructor TContactosController.Destroy; begin FDataModule := NIL; inherited; end; function TContactosController.Eliminar(AContacto: IBizContacto): Boolean; begin Result := False; if not Assigned(AContacto) then raise Exception.Create ('Contacto no asignado'); ShowHourglassCursor; try if (AContacto.State in dsEditModes) then AContacto.Cancel; AContacto.Delete; AContacto.DataTable.ApplyUpdates; HideHourglassCursor; Result := True; except on E: Exception do begin AContacto.DataTable.CancelUpdates; HideHourglassCursor; if (Pos('FOREIGN KEY', E.Message) > 0) then MessageBox(0, 'No se puede borrar este contacto porque tiene documentos dados de alta (como presupuestos, albaranes, facturas, etc)', 'Atención', MB_ICONWARNING or MB_OK); end; end; end; procedure TContactosController.Eliminar(const ID: Integer); var AContacto : IBizContacto; begin AContacto := Buscar(ID); if not Assigned(AContacto) then raise Exception.Create(Format('No se ha encontrado el contacto con ID = %d', [ID])); Eliminar(AContacto); AContacto := NIL; end; function TContactosController.Existe(const ID: Integer): Boolean; var AContacto : IBizContacto; begin try AContacto := Buscar(ID); Result := Assigned(AContacto) and (AContacto.ID = ID); finally AContacto := NIL; end; end; function TContactosController.ExtraerSeleccionados(AContactos: IBizContacto; Const ViewGrid: IViewGridBase = Nil): IBizContacto; var ASeleccionados : IBizContacto; begin if Assigned(ViewGrid) then begin //Guardamos la situación porque el CopyDataTable nos cambia la posicion ViewGrid.SaveGridStatus; ViewGrid.DesactivarGrid; end; ASeleccionados := Self.Buscar(ID_NULO); CopyDataTable(AContactos.DataTable, ASeleccionados.DataTable, True); Result := ASeleccionados; if Assigned(ViewGrid) then begin ViewGrid.ActivarGrid; ViewGrid.RestoreGridStatus; end; end; function TContactosController.Guardar(AContacto: IBizContacto): Boolean; var NuevoID : Integer; begin Result := False; if ValidarContacto(AContacto) then begin ShowHourglassCursor; try if AContacto.EsNuevo then NuevoID := FDataModule.GetNextID(AContacto.DataTable.LogicalName) else NuevoID := AContacto.ID; AsignarID(AContacto, NuevoID); AContacto.DataTable.ApplyUpdates; Result := True; finally HideHourglassCursor; end; end; end; function TContactosController.Localizar(AContactos: IBizContacto; const ID: Integer): Boolean; begin Result := True; ShowHourglassCursor; try with AContactos.DataTable do begin DisableControls; First; if not Locate(fld_ContactosID, ID, []) then Result := False; EnableControls; end; finally HideHourglassCursor; end; end; procedure TContactosController.Preview(AContactos: IBizContacto); var AReportController : IEtiquetasContactosReportController; ID_Contactos: TStringList; begin AReportController := TEtiquetasContactosReportController.Create; try ID_Contactos := TStringList.Create; with AContactos.DataTable do begin First; while not EOF do begin ID_Contactos.Add(IntToStr(AContactos.ID)); Next; end; end; AReportController.Preview(ID_Contactos.CommaText); finally AReportController := NIL; ID_Contactos.Free; end; end; function TContactosController.Print(AContactos: IBizContacto): Boolean; var AReportController : IEtiquetasContactosReportController; ID_Contactos: TStringList; begin Result := False; AReportController := TEtiquetasContactosReportController.Create; try ID_Contactos := TStringList.Create; with AContactos.DataTable do begin First; while not EOF do begin ID_Contactos.Add(IntToStr(AContactos.ID)); Next; end; end; Result := AReportController.Print(ID_Contactos.CommaText); finally AReportController := NIL; ID_Contactos.Free; end; end; procedure TContactosController.AsignarIDDetalles (AContacto: IBizContacto; const IDCabecera : Integer; ADataTable : TDADataTable); var AContador : Integer; begin if not ADataTable.Active then ADataTable.Active := True; if AContacto.EsNuevo then begin { ¡¡¡¡ 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_CONTACTO. 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. } while ADataTable.RecordCount > 0 do begin ADataTable.First; ADataTable.Edit; ADataTable.FieldByName('ID').AsInteger := FDataModule.GetNextID(ADataTable.LogicalName); // ADataTable.FieldByName('ID_CONTACTO').AsInteger := IDCabecera; //OJO SI UN DIA SON VARIOS LOS CAMPOS FORANEOS, SOLO FUNCIONA CUANDO ES UN ÚNICO CAMPO DE CLAVE FORANEA ADataTable.FieldByName(ADataTable.DetailFields).AsInteger := IDCabecera; ADataTable.Post; end; end else begin { En este caso es un recorrido normal y corriente. } ADataTable.First; AContador := ADataTable.RecordCount; while (AContador > 0) do begin // ¿Es nuevo? if (ADataTable.FieldByName('ID').AsInteger < 0) then begin ADataTable.Edit; ADataTable.FieldByName('ID').AsInteger := FDataModule.GetNextID(ADataTable.LogicalName); ADataTable.Post; end; AContador := AContador - 1; ADataTable.Next; end; ADataTable.First; end; end; function TContactosController.ValidarContacto(AContacto: IBizContacto): Boolean; begin Result := False; if not Assigned(AContacto) then raise Exception.Create ('Contacto no asignado'); if (AContacto.DataTable.State in dsEditModes) then AContacto.DataTable.Post; if Length(AContacto.NOMBRE) = 0 then raise Exception.Create('Debe indicar al menos el nombre de este contacto.'); // Asegurarse de valores en campos "automáticos" AContacto.Edit; AContacto.USUARIO := dmUsuarios.LoginInfo.Usuario; AContacto.Post; Result := True; end; function TContactosController._Vacio: IBizContacto; begin Result := Buscar(ID_NULO); end; procedure TContactosController.FiltrarEmpresa(AContacto: IBizContacto); begin if AContacto.DataTable.Active then AContacto.DataTable.Active := False; // Filtrar los contactos actuales por empresa with AContacto.DataTable.Where do begin if NotEmpty then AddOperator(opAND); AddCondition(fld_ContactosID_EMPRESA, cEqual, dmUsuarios.IDEmpresaActual); end; end; end.