unit uFacturasClienteController;

interface


uses
  Classes, SysUtils, uDADataTable, uControllerBase, uIDataModuleFacturasCliente,
  uClientesController, uDetallesFacturaClienteController,
  uBizFacturasCliente;

type
  IFacturasClienteController = interface(IObservador)
  ['{CAD20B4E-6D0B-4A1C-9306-B195824B6CAD}']
    function GetClienteController: IClientesController;
    procedure SetClienteController(const Value: IClientesController);
    property ClienteController: IClientesController read GetClienteController write SetClienteController;

    function GetDetallesController: IDetallesFacturaClienteController;
    procedure SetDetallesController(const Value: IDetallesFacturaClienteController);
    property DetallesController: IDetallesFacturaClienteController read GetDetallesController write SetDetallesController;

    function Buscar(const ID: Integer): IBizFacturaCliente;
    function BuscarTodos: IBizFacturaCliente;
    procedure Ver(AFactura : IBizFacturaCliente);
    procedure VerTodos(AFacturas: IBizFacturaCliente);
    function Nuevo : IBizFacturaCliente;
    procedure Anadir(AFactura : IBizFacturaCliente);
    procedure Eliminar(const ID : Integer); overload;
    procedure Eliminar(AFactura : IBizFacturaCliente); overload;
    procedure Guardar(AFactura : IBizFacturaCliente);
    procedure DescartarCambios(AFactura : IBizFacturaCliente);
    function Existe(const ID: Integer) : Boolean;
    procedure RecuperarCliente(AFactura : IBizFacturaCliente);
    function Duplicar(AFactura: IBizFacturaCliente): IBizFacturaCliente;
    function CambiarSituacion(AFactura: IBizFacturaCliente; ASituacion : String; AFechaPagado: TDateTime = 0; DoPost : Boolean = True): Boolean; overload;
    function CambiarSituacion(AFactura: IBizFacturaCliente): Boolean; overload;
    procedure Preview(AFactura : IBizFacturaCliente);
    procedure Print(AFactura : IBizFacturaCliente);        
    //
    function DarListaSituaciones: TStringList;
  end;

  TFacturasClienteController = class(TObservador, IFacturasClienteController)
  private
    FDataModule : IDataModuleFacturasCliente;
    FClienteController : IClientesController;
    FDetallesController : IDetallesFacturaClienteController;

    function GetClienteController: IClientesController;
    procedure SetClienteController(const Value: IClientesController);

    function GetDetallesController: IDetallesFacturaClienteController;
    procedure SetDetallesController(const Value: IDetallesFacturaClienteController);

    procedure RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable); override;
    procedure AsignarID(AFactura: IBizFacturaCliente; const NuevoID:Integer);
    function CreateEditor(const AName : String; const IID: TGUID; out Intf): Boolean;
    function _Vacio : IBizFacturaCliente;

    procedure FiltrarEmpresa(AFactura: IBizFacturaCliente);

    function ValidarFactura(AFactura: IBizFacturaCliente): Boolean;
    function ValidarSituacion(ASituacion: String; AFechaFactura: TDateTime;
      var AFechaPagado: TDateTime): Boolean;
  public
    property ClienteController: IClientesController read GetClienteController write SetClienteController;
    property DetallesController: IDetallesFacturaClienteController read GetDetallesController write SetDetallesController;

    constructor Create;
    destructor Destroy; override;

    procedure Eliminar(const ID : Integer); overload;
    procedure Eliminar(AFactura : IBizFacturaCliente); overload;
    procedure Guardar(AFactura : IBizFacturaCliente);
    procedure DescartarCambios(AFactura : IBizFacturaCliente); virtual;
    function Existe(const ID: Integer) : Boolean; virtual;
    procedure Anadir(AFactura : IBizFacturaCliente);
    function Buscar(const ID: Integer): IBizFacturaCliente;
    function BuscarTodos: IBizFacturaCliente;
    function Nuevo : IBizFacturaCliente;
    procedure Ver(AFactura : IBizFacturaCliente);
    procedure VerTodos(AFacturas: IBizFacturaCliente);
    function Duplicar(AFactura: IBizFacturaCliente): IBizFacturaCliente;
    function CambiarSituacion(AFactura: IBizFacturaCliente; ASituacion : String; AFechaPagado: TDateTime = 0; DoPost : Boolean = True): Boolean; overload;
    function CambiarSituacion(AFactura: IBizFacturaCliente): Boolean; overload;
    procedure RecuperarCliente(AFactura : IBizFacturaCliente);
    procedure Preview(AFactura : IBizFacturaCliente);
    procedure Print(AFactura : IBizFacturaCliente);
    function DarListaSituaciones: TStringList;
  end;

implementation

uses
  cxControls, DB, uEditorRegistryUtils, schFacturasClienteClient_Intf,
  uBizContactos, uIEditorFacturasCliente, uIEditorFacturaCliente,
  uDataModuleFacturasCliente, uBizDetallesFacturaCliente,
  uDataModuleUsuarios, uDAInterfaces, uDataTableUtils, uDateUtils,
  uIEditorSituacionFacturaCliente,
  uROTypes, uFacturasClienteReportController, DateUtils;

{ TFacturasClienteController }

procedure TFacturasClienteController.Anadir(AFactura: IBizFacturaCliente);
begin
  AFactura.Insert;
  AFactura.Cliente := (FClienteController.Nuevo as IBizCliente);  
end;

procedure TFacturasClienteController.AsignarID(AFactura: IBizFacturaCliente; const NuevoID:Integer);
begin
  if not Assigned(AFactura) then
    raise Exception.Create ('Factura no asignada');

  if not Assigned(FDetallesController) then
    raise Exception.Create ('Controller detalles no asignado');

  { ¡¡¡ 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_FACTURA <--
  }

  FDetallesController.AsignarID(AFactura.Detalles, NuevoID, AFactura.EsNuevo);

  if AFactura.EsNuevo then
  begin
    AFactura.Edit;
    AFactura.ID := NuevoID;
    AFactura.Post;
  end;

end;

function TFacturasClienteController.Buscar(const ID: Integer): IBizFacturaCliente;
begin
  Result := (FDataModule as IDataModuleFacturasCliente).GetItem(ID);
  FiltrarEmpresa(Result);  
end;

function TFacturasClienteController.BuscarTodos: IBizFacturaCliente;
begin
  Result := FDataModule.GetItems;
  FiltrarEmpresa(Result);  
end;

function TFacturasClienteController.CambiarSituacion(
  AFactura: IBizFacturaCliente): Boolean;
var
  AEditor : IEditorSituacionFacturaCliente;
begin
  AEditor := NIL;
  ShowHourglassCursor;
  try
    RecuperarCliente(AFactura);
    CreateEditor('EditorSituacionFacturaCliente', IEditorSituacionFacturaCliente, AEditor);
    with AEditor do
    begin
      Controller := Self; //OJO ORDEN MUY IMPORTANTE
      FacturaCliente := AFactura;
    end;
  finally
    HideHourglassCursor;
  end;

  if Assigned(AEditor) then
    AEditor.ShowModal;
end;

function TFacturasClienteController.CambiarSituacion(
  AFactura: IBizFacturaCliente; ASituacion : String; AFechaPagado: TDateTime; DoPost : Boolean): Boolean; 
begin
  Result := False;

  // Validar la situación de la factura
  if ValidarSituacion(ASituacion, AFactura.FECHA_FACTURA, AFechaPagado) then
  begin
    ShowHourglassCursor;
    AFactura.DataTable.DisableControls;
    try
      AFactura.Edit;
      AFactura.SITUACION := ASituacion;
      if AFechaPagado <> AFactura.FECHA_PAGADO then
        AFactura.FECHA_PAGADO := AFechaPagado;
      AFactura.Post;
      if DoPost then
        Guardar(AFactura);
      Result := True;
    finally
      AFactura.DataTable.EnableControls;
      HideHourglassCursor;
    end;
  end;
end;

constructor TFacturasClienteController.Create;
begin
  FDataModule := TDataModuleFacturasCliente.Create(Nil);
  FClienteController := TClientesController.Create;  
  FDetallesController := TDetallesFacturaClienteController.Create;
  FDetallesController.addObservador(Self);
end;

function TFacturasClienteController.CreateEditor(const AName: String;
  const IID: TGUID; out Intf): Boolean;
begin
  Result := Supports(EditorRegistry.CreateEditor(AName), IID, Intf);
end;

function TFacturasClienteController.DarListaSituaciones: TStringList;
begin
  Result := TStringList.Create;
  with Result do
  begin
    Add(SITUACION_PENDIENTE);
    Add(SITUACION_PAGADO);
  end;
end;

procedure TFacturasClienteController.DescartarCambios(AFactura: IBizFacturaCliente);
begin
  if not Assigned(AFactura) then
    raise Exception.Create ('Factura no asignada');

  ShowHourglassCursor;
  try
    if (AFactura.State in dsEditModes) then
      AFactura.Cancel;

    AFactura.DataTable.CancelUpdates;
  finally
    HideHourglassCursor;
  end;
end;

destructor TFacturasClienteController.Destroy;
begin
  FDataModule := Nil;
  FClienteController := Nil;
  FDetallesController := Nil;
  inherited;
end;

function TFacturasClienteController.Duplicar(
  AFactura: IBizFacturaCliente): IBizFacturaCliente;
begin
  Result := Self._Vacio;
  ShowHourglassCursor;
  try
    DuplicarRegistros(AFactura.DataTable, Result.DataTable, mdrActual);
    DuplicarRegistros(AFactura.Detalles.DataTable, Result.Detalles.DataTable, mdrTodos);

    // Hay que dejar algunos campos como si fuera una factura nueva
    Result.Edit;
    with Result do
    begin
      ID_EMPRESA := dmUsuarios.IDEmpresaActual;
      USUARIO := dmUsuarios.LoginInfo.Usuario;
      REFERENCIA := ''; //Para que se asigne una nueva
      FECHA_FACTURA := DateOf(Now);
      SITUACION := SITUACION_PENDIENTE;
    end;

    Result.Post;
  finally
    HideHourglassCursor;
  end;
end;

function TFacturasClienteController.ValidarFactura(AFactura: IBizFacturaCliente): Boolean;
var
  AFechaPagado : TDateTime;
begin
  Result := False;

  if not Assigned(AFactura) then
    raise Exception.Create ('Factura no asignada');

  if not Assigned(FDetallesController) then
    raise Exception.Create ('Controller detalles no asignado');

  if (AFactura.DataTable.State in dsEditModes) then
    AFactura.DataTable.Post;

  //Tambien hacemos post de sus tablas hija
  if (AFactura.Detalles.DataTable.State in dsEditModes) then
     AFactura.Detalles.DataTable.Post;

  //VALIDACION
  if (AFactura.ID_CLIENTE < 0) or
     (not Assigned(AFactura.Cliente)) or
     (AFactura.Cliente.IsEmpty) then
    raise Exception.Create('Debe indicar el cliente de esta factura');

  if (EsFechaVacia(AFactura.FECHA_FACTURA)) then
    raise Exception.Create('Debe indicar la fecha de esta factura');

  // Validar la situación del presupuesto
  AFechaPagado := AFactura.FECHA_PAGADO;
  if not ValidarSituacion(AFactura.SITUACION, AFactura.FECHA_FACTURA, AFechaPagado) then
    Exit;

  // Asegurarse de valores en campos "automáticos"
  AFactura.Edit;
  try
    if AFechaPagado <> AFactura.FECHA_PAGADO then
      AFactura.FECHA_PAGADO := AFechaPagado;

    AFactura.USUARIO := dmUsuarios.LoginInfo.Usuario;

    if Assigned(AFactura.Cliente) then
      AFactura.ID_CLIENTE := AFactura.Cliente.ID;

    Result := True;
  finally
    AFactura.Post;
  end;
end;

function TFacturasClienteController.ValidarSituacion(ASituacion: String;
  AFechaFactura: TDateTime; var AFechaPagado: TDateTime): Boolean;
begin
  Result := False;
  if ASituacion = SITUACION_PENDIENTE then
    AFechaPagado := 0
  else begin
    if (EsFechaVacia(AFechaPagado)) then
      raise Exception.Create('Hay que indicar una fecha de pago')
    else if (AFechaPagado < AFechaFactura) then
      raise Exception.Create('La fecha de pago debe ser posterior a la fecha de la factura');
  end;
  Result := True;
end;

procedure TFacturasClienteController.Ver(AFactura: IBizFacturaCliente);
var
  AEditor : IEditorFacturaCliente;
begin
  AEditor := NIL;
  ShowHourglassCursor;
  try
    RecuperarCliente(AFactura);
    CreateEditor('EditorFacturaCliente', IEditorFacturaCliente, AEditor);
    with AEditor do
    begin
      Controller := Self; //OJO ORDEN MUY IMPORTANTE
      Factura := AFactura;
    end;
  finally
    HideHourglassCursor;
  end;

  if Assigned(AEditor) then
    AEditor.ShowModal;
end;

procedure TFacturasClienteController.VerTodos(AFacturas: IBizFacturaCliente);
var
  AEditor : IEditorFacturasCliente;
begin
  AEditor := NIL;
  ShowHourglassCursor;
  try
    CreateEditor('EditorFacturasCliente', IEditorFacturasCliente, AEditor);
    with AEditor do
    begin
      Controller := Self; //OJO ORDEN MUY IMPORTANTE
      Facturas := AFacturas;
    end;
  finally
    HideHourglassCursor;
  end;

  if Assigned(AEditor) then
    AEditor.ShowEmbedded;
end;

function TFacturasClienteController._Vacio: IBizFacturaCliente;
begin
  Result := Buscar(ID_NULO);
end;

procedure TFacturasClienteController.Eliminar(const ID: Integer);
var
  AFactura : IBizFacturaCliente;
begin
  AFactura := Buscar(ID);

  if not Assigned(AFactura) then
    raise Exception.Create(Format('No se ha encontrado la factura con ID = %d', [ID]));

  Eliminar(AFactura);
  AFactura := NIL;
end;

procedure TFacturasClienteController.Eliminar(AFactura: IBizFacturaCliente);
begin
  if not Assigned(AFactura) then
    raise Exception.Create ('Factura no asignada');

  ShowHourglassCursor;
  try
    if (AFactura.State in dsEditModes) then
      AFactura.Cancel;

    AFactura.Delete;
    AFactura.DataTable.ApplyUpdates;
  finally
    HideHourglassCursor;
  end;
end;
procedure TFacturasClienteController.RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable);
var
  AFactura : IBizFacturaCliente;
  ADetalles : IBizDetallesFacturaCliente;
begin
  inherited;

  if Supports(ADataTable, IBizDetallesFacturaCliente, ADetalles) and
     Supports(ADetalles.DataTable.MasterSource.DataTable, IBizFacturaCliente, AFactura) then
  begin
    AFactura.Edit;
    try
      AFactura.BASE_IMPONIBLE := FDetallesController.DarTotalImporteTotal(ADetalles);
    finally
      AFactura.Post;
    end;
  end;
end;

procedure TFacturasClienteController.RecuperarCliente(
  AFactura: IBizFacturaCliente);
begin
  AFactura.Cliente := (FClienteController.Buscar(AFactura.ID_CLIENTE)  as IBizCliente);
end;

function TFacturasClienteController.Existe(const ID: Integer): Boolean;
var
  AFactura : IBizFacturaCliente;
begin
  try
    AFactura := Buscar(ID);
    Result := Assigned(AFactura) and (AFactura.ID = ID);
  finally
    AFactura := NIL;
  end;
end;

procedure TFacturasClienteController.FiltrarEmpresa(AFactura: IBizFacturaCliente);
begin
  if AFactura.DataTable.Active then
    AFactura.DataTable.Active := False;

  // Filtrar las facturas actuales por empresa
  with AFactura.DataTable.Where do
  begin
    if NotEmpty then
      AddOperator(opAND);
    AddCondition(fld_FacturasClienteID_EMPRESA, cEqual, dmUsuarios.IDEmpresaActual);
  end;
end;

procedure TFacturasClienteController.SetClienteController(const Value: IClientesController);
begin
  FClienteController := Value;
end;

procedure TFacturasClienteController.SetDetallesController(const Value: IDetallesFacturaClienteController);
begin
  FDetallesController := Value;
end;

procedure TFacturasClienteController.Guardar(AFactura: IBizFacturaCliente);
var
  NuevoID: Integer;
begin
  if ValidarFactura(AFactura) then
  begin
    ShowHourglassCursor;
    try
      if AFactura.EsNuevo then
         NuevoID := FDataModule.GetNextID(AFactura.DataTable.LogicalName)
      else
         NuevoID := AFactura.ID;

      AsignarID(AFactura, NuevoID);
      AFactura.DataTable.ApplyUpdates;
    finally
      HideHourglassCursor;
    end;
  end;
end;

function TFacturasClienteController.Nuevo: IBizFacturaCliente;
var
  AFactura : IBizFacturaCliente;
begin
  AFactura := FDataModule.NewItem;
  FiltrarEmpresa(AFactura);
  AFactura.DataTable.Active := True;
  AFactura.Insert;
  AFactura.Cliente := (FClienteController.Nuevo as IBizCliente);  
  Result := AFactura;
end;

procedure TFacturasClienteController.Preview(AFactura: IBizFacturaCliente);
var
  AReportController : IFacturasClienteReportController;
begin
  AReportController := TFacturasClienteReportController.Create;
  try
    AReportController.Preview(AFactura.ID);
  finally
    AReportController := NIL;
  end;
end;

procedure TFacturasClienteController.Print(AFactura: IBizFacturaCliente);
var
  AReportController : IFacturasClienteReportController;
begin
  AReportController := TFacturasClienteReportController.Create;
  try
    AReportController.Print(AFactura.ID);
  finally
    AReportController := NIL;
  end;
end;

function TFacturasClienteController.GetClienteController: IClientesController;
begin
  Result := FClienteController;
end;

function TFacturasClienteController.GetDetallesController: IDetallesFacturaClienteController;
begin
  Result := FDetallesController;
end;

end.
