unit uBizFacturasCliente;

interface

uses
  uDAInterfaces, uDADataTable, Classes, uBizContacto, DB,
  uBizImportesDetalleBase, uBizImportesCabeceraBase, uDBSelectionList,
  uExceptions, uBizInformesBase, schFacturasClienteClient_Intf;

const
  BIZ_FACTURASCLIENTE = 'Client.FacturasCliente';
  BIZ_DETALLESFACTURASCLIENTE = 'Client.DetallesFacturasCliente';

  SITUACION_PENDIENTE = 'N';
  SITUACION_PAGADO = 'S';

  CTE_NORMAL = 'N';
  CTE_ABONO = 'A';
  CTE_PROFORMA = 'P';

  CTE_IVA1 = 16;
  CTE_IVA2 = 1.16;

type
  IBizDetallesFacturasCliente = interface(IDetallesFacturasCliente)
  ['{A628B3DE-2EF0-4A6E-AAFD-8BE582BDEE9A}']
    procedure CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente);
    procedure cambiarSigno;
  end;

  IBizFacturasCliente = interface(IFacturasCliente)
  ['{CE8B2685-0FA5-404C-B8FA-90618A815BFF}']
    function GetDetalles: IBizDetallesFacturasCliente;
    procedure SetDetalles(Value: IBizDetallesFacturasCliente);
    property Detalles: IBizDetallesFacturasCliente read GetDetalles write
            SetDetalles;

    function GetCliente: IBizContacto;
    procedure SetCliente(Value: IBizContacto);
    property Cliente: IBizContacto read GetCliente write SetCliente;

    procedure Show;

    procedure setReferencia(const ClaseFactura: String);
    procedure CopyFrom(AFacturaCliente : IBizFacturasCliente; ShowMensaje: Boolean = True);
    procedure cambiarSigno;
  end;


  TBizDetallesFacturasCliente = class(TDetallesFacturasClienteDataTableRules,
                                  IBizDetallesFacturasCliente,
                                  IBizImportesDetalle,
                                  IBizPuntosDetalle,
                                  IBizVisibleDetalle,
                                  IParche)    // PARCHE ***********************
  private
    FIsAppend : Boolean;
    FPosicionNueva : Integer;
    FPuedoLanzarEvento : Boolean; // PARCHE ***********************

  protected
    procedure SetCANTIDADValue(const aValue: Integer); override;
    procedure SetIMPORTEUNIDADValue(const aValue: Currency); override;
    procedure SetIMPORTETOTALValue(const aValue: Currency); override;

    procedure OnNewRecord(Sender: TDADataTable); override;
    procedure BeforeInsert(Sender: TDADataTable); override;
    procedure AfterPost(Sender: TDADataTable); override;
    procedure AfterInsert(Sender: TDADataTable); override;
    procedure AfterDelete(Sender: TDADataTable); override;
    procedure BeforeDelete(Sender: TDADataTable); override;

    // PARCHE ***********************
    procedure ActivarEventos;
    procedure DesactivarEventos;
    procedure Refrescar;
    function PuedoLanzarEvento : Boolean;
  public
    procedure RecalcularImporte;
    function DarSumaTotalImportes : Currency;
    constructor Create(aDataTable: TDADataTable); override;
    procedure CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente);
    procedure CambiarSigno;
  end;


  TBizFacturasCliente = class(TFacturasClienteDataTableRules, IBizFacturasCliente,
                            IBizImportesCabecera, ISelectedRowList,
                            IApplyUpdateFailedException, IBizInformesAware)
  private
    FCliente : IBizContacto;
    FDetalles: IBizDetallesFacturasCliente;
    FDetallesLink: TDADataSource;
    FSelectedRows : TSelectedRowList;

  protected
    procedure ShowApplyUpdateFailed (const Error: EDAApplyUpdateFailed);
    function GetDetalles: IBizDetallesFacturasCliente;
    procedure SetDetalles(Value: IBizDetallesFacturasCliente);

    function GetCliente: IBizContacto;
    procedure SetCliente(Value: IBizContacto);

    procedure OnNewRecord(Sender: TDADataTable); override;

    function GetSelectedRows : TSelectedRowList;
    procedure BeforeDelete(Sender: TDADataTable); override;
    procedure AfterDelete(Sender: TDADataTable); override;
    procedure OnPostError(DataTable: TDADataTable; Error: EDatabaseError;
      var Action: TDataAction); override;
    procedure BeforeApplyUpdates(Sender : TDADataTable; const Delta : IDADelta);

  public
    procedure RecalcularImporte;
    property Cliente: IBizContacto read GetCliente write SetCliente;
    property Detalles: IBizDetallesFacturasCliente read GetDetalles write SetDetalles;
    constructor Create(aDataTable: TDADataTable); override;
    destructor Destroy; override;

    procedure Show; virtual;
    procedure Preview;
    procedure Print;
    property SelectedRows : TSelectedRowList read GetSelectedRows;
    procedure CopyFrom(AFacturaCliente : IBizFacturasCliente; ShowMensaje: Boolean = True);
    procedure cambiarSigno;
    procedure setReferencia(const ClaseFactura: String);
  end;

  procedure ValidarFacturaCliente (const AFacturaCliente : IBizFacturasCliente);


implementation

uses
  Windows, Dialogs, uDACDSDataTable, SysUtils, uEditorUtils, Variants, Math,
  uDataModuleContactos, Controls, Forms, LiteralesFacturasCliente,
  uDataModuleBase, uDataTableUtils, uDataModuleFacturasCliente, cxControls;

var
  FMasterDeleting : Boolean;
  FCancelInsert : Boolean;

procedure ValidarFacturaCliente (const AFacturaCliente : IBizFacturasCliente);
begin
  if (FloatToStr(AFacturaCliente.FECHAFACTURA) = '0') then
    raise Exception.Create(msgLitFechaObligatorio);

  if not Assigned(AFacturaCliente.Cliente) or
      (AFacturaCliente.Cliente.DataTable.IsEmpty) then
    raise Exception.Create(msgLitClienteObligatorio);
end;

constructor TBizFacturasCliente.Create(aDataTable: TDADataTable);
begin
  inherited;
  FCliente := NIL;
  FDetallesLink := TDADataSource.Create(NIL);
  FSelectedRows := TSelectedRowList.Create(aDataTable);
  aDataTable.OnBeforeApplyUpdates := BeforeApplyUpdates;
end;

destructor TBizFacturasCliente.Destroy;
begin
  FCliente := NIL;
  FDetalles := NIL;
  FDetallesLink.Free;
  FSelectedRows.Free;  
  inherited;
end;

function TBizFacturasCliente.GetCliente: IBizContacto;
begin
  ShowHourglassCursor;
  try
    if not Assigned(FCliente) or
      ((CODIGOCONTACTO <> FCliente.Codigo) and
      not (FCliente.DataTable.State in dsEditModes)) then
    begin
      FCliente := dmContactos.GetContacto(CODIGOCONTACTO);
      if not FCliente.DataTable.Active then
        FCliente.DataTable.Active := True;
      if not FCliente.DataTable.IsEmpty then  // Está vacío si la factura es nueva y no tiene cliente
      begin
        FCliente.Edit;
        FCliente.NOMBRE := NOMBRE;
        FCliente.NIFCIF := NIFCIF;
        FCliente.CALLE := CALLE;
        FCliente.CODIGOPOSTAL := CODIGOPOSTAL;
        FCliente.PROVINCIA := PROVINCIA;
        FCliente.POBLACION := POBLACION;
        FCliente.Post;
      end;
    end;
    Result := FCliente;
  finally
    HideHourglassCursor;
  end;
end;

function TBizFacturasCliente.GetDetalles: IBizDetallesFacturasCliente;
begin
  Result := FDetalles;
end;

procedure TBizFacturasCliente.CopyFrom(AFacturaCliente : IBizFacturasCliente; ShowMensaje: Boolean = True);
begin
  if not (State in dsEditModes) then
    Edit;

  ShowHourglassCursor;
  Application.ProcessMessages;
  try
    CODIGO := dmFacturasCliente.GetNextAutoinc;
    FECHAALTA := Date;
    USUARIO := 'eeee';  //dmUsuarios.LoginInfo.UserID;
    CLASEFACTURA := AFacturaCliente.CLASEFACTURA;

    if (CLASEFACTURA = CTE_NORMAL)
    or (CLASEFACTURA = CTE_ABONO) then
       setReferencia(CTE_NORMAL)
    else
       setReferencia(CTE_PROFORMA);
//    REFERENCIA := AFacturaCliente.REFERENCIA;

    FECHAFACTURA := AFacturaCliente.FECHAFACTURA;
    PAGADA := AFacturaCliente.PAGADA;
    BASEIMPONIBLE := AFacturaCliente.BASEIMPONIBLE;
    IVA := AFacturaCliente.IVA;
    OBSERVACIONES := AFacturaCliente.OBSERVACIONES;
    FORMAPAGO := AFacturaCliente.FORMAPAGO;
    CODIGOCONTACTO := AFacturaCliente.CODIGOCONTACTO;
    Cliente := AFacturaCliente.Cliente;

    if not (AFacturaCliente.DataTable.FieldByName(fld_FacturasClienteCODIGOOBRA).AsVariant = null) then
       CODIGOOBRA := AFacturaCliente.CODIGOOBRA;

    if not (AFacturaCliente.DataTable.FieldByName(fld_FacturasClienteFECHAVENCIMIENTO).AsVariant = null) then
       FECHAVENCIMIENTO := AFacturaCliente.FECHAVENCIMIENTO;

    Detalles.CopyFrom(AFacturaCliente.Detalles);
    RecalcularImporte;
    Post;
    if ShowMensaje then
       MessageBox(0, 'Se ha duplicado correctamente la factura elegida.', 'Factura duplicada', MB_ICONWARNING or MB_OK);
  finally
    HideHourglassCursor;
  end;
end;

procedure TBizFacturasCliente.OnNewRecord(Sender: TDADataTable);
begin
  inherited;
  USUARIO := dmBase.Usuario;
  FECHAALTA := dmBase.Fecha;
  FECHAFACTURA := dmBase.Fecha;
  CODIGO := dmFacturasCliente.GetNextAutoinc;
  IVA := CTE_IVA1;
end;

procedure TBizFacturasCliente.Preview;
begin
  dmFacturasCliente.Preview(Self.CODIGO);
end;

procedure TBizFacturasCliente.SetCliente(Value: IBizContacto);
var
  bEnEdicion : Boolean;
begin
  ShowHourglassCursor;
  try
    bEnEdicion := (DataTable.State in dsEditModes);
    if not bEnEdicion then
      Edit;
    FCliente := Value;
    if Assigned(FCliente) then
    begin
      CODIGOCONTACTO := FCliente.CODIGO;
      NOMBRE := FCliente.NOMBRE;
      NIFCIF := FCliente.NIFCIF;
      CALLE := FCliente.CALLE;
      CODIGOPOSTAL := FCliente.CODIGOPOSTAL;
      PROVINCIA := FCliente.PROVINCIA;
      POBLACION := FCliente.POBLACION;
      if not bEnEdicion then
        Post;
    end
  finally
    HideHourglassCursor;
  end;
end;

procedure TBizFacturasCliente.SetDetalles(Value: IBizDetallesFacturasCliente);
begin
  FDetalles := Value;
  FDetallesLink.DataTable := Self.DataTable;
  FDetalles.DataTable.MasterSource := FDetallesLink;
end;

procedure TBizFacturasCliente.Show;
begin
  ShowEditor(IBizFacturasCliente, Self, etItem);
end;

procedure TBizDetallesFacturasCliente.ActivarEventos;
begin
  FPuedoLanzarEvento := True;
end;

procedure TBizDetallesFacturasCliente.AfterDelete(Sender: TDADataTable);
var
  ACabecera : IBizImportesCabecera;
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  if (not FMasterDeleting) and (not FCancelInsert) then
  begin
    ReasignarPosiciones(Self.DataTable);
    if Assigned(DataTable.MasterSource) and
      Supports(DataTable.MasterSource.DataTable, IBizImportesCabecera, ACabecera) then
      ACabecera.RecalcularImporte;
  end;
  FCancelInsert := False;
end;

procedure TBizDetallesFacturasCliente.AfterInsert(Sender: TDADataTable);
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  FIsAppend := DataTable.EOF;
  Post;
  Edit; // Para volver a dejarlo en modo de edición
end;

procedure TBizDetallesFacturasCliente.AfterPost(Sender: TDADataTable);
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  if POSICION < 0 then
    AsignarPosicion(Self.DataTable, FIsAppend);

  if NUMCONCEPTO < 0  then
    AsignarNumConcepto(Self.DataTable);

  FIsAppend := False;
end;

procedure TBizDetallesFacturasCliente.BeforeDelete(Sender: TDADataTable);
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  if (DataTable.State in dsEditModes) then
    DataTable.Cancel;

  if not FMasterDeleting then
    FCancelInsert := not (DataTable.MasterSource.DataTable.FieldByName('CODIGO').AsInteger = CODIGOFACTURA);
end;

procedure TBizDetallesFacturasCliente.BeforeInsert(Sender: TDADataTable);
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  if Assigned(DataTable.MasterSource) and
    (DataTable.MasterSource.DataTable.State in dsEditModes) then
    DataTable.MasterSource.DataTable.Post;

  if GetRecordCount = 0 then
    FPosicionNueva := -1
  else
    FPosicionNueva := POSICION * (-1);
end;

procedure TBizDetallesFacturasCliente.CambiarSigno;
begin
  ShowHourglassCursor;
  try
    DataTable.First;
    while not DataTable.EOF do
    begin
  //    DataTable.DisableEventHandlers;
      if not (DataTable.State in dsEditModes) then
         DataTable.Edit;
      if not DataTable.FieldByName(fld_DetallesFacturasClienteIMPORTEUNIDAD).IsNull then
         IMPORTEUNIDAD := (-1)* IMPORTEUNIDAD;
  //    if not ADetallesPedido.DataTable.FieldByName(fld_DetallesPedidosProveedorIMPORTETOTAL).IsNull then
  //       IMPORTETOTAL := ADetallesPedido.IMPORTETOTAL;
  //    DataTable.EnableEventHandlers;
  //    DataTable.Post;
      DataTable.Next;
    end;
  finally
    HideHourglassCursor;
  end;
end;

procedure TBizDetallesFacturasCliente.CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente);
begin
  ShowHourglassCursor;
  try
    ADetallesFacturas.First;
    while not ADetallesFacturas.EOF do
    begin
      Append;
      DataTable.DisableControls;
      DataTable.DisableEventHandlers;
      try
        TIPO := ADetallesFacturas.TIPO;
        DESCRIPCION := ADetallesFacturas.DESCRIPCION;
        CANTIDAD := ADetallesFacturas.CANTIDAD;
        IMPORTEUNIDAD := ADetallesFacturas.IMPORTEUNIDAD;
        IMPORTETOTAL := ADetallesFacturas.IMPORTETOTAL;
        POSICION := ADetallesFacturas.POSICION;
      finally
        DataTable.EnableControls;
        DataTable.EnableEventHandlers;
      end;
      Post;
      ADetallesFacturas.Next;
    end;
  finally
    HideHourglassCursor;
  end;
end;

constructor TBizDetallesFacturasCliente.Create(aDataTable: TDADataTable);
begin
  inherited;
  FPosicionNueva := 1;  // Los conceptos empiezan a contar en 1

  // PARCHE *******************
  FPuedoLanzarEvento := True;
end;

function TBizDetallesFacturasCliente.DarSumaTotalImportes: Currency;
begin
  Result := DarTotalDetalles(Self.DataTable, True, False);
end;

procedure TBizDetallesFacturasCliente.DesactivarEventos;
begin
  FPuedoLanzarEvento := False;
end;

procedure TBizDetallesFacturasCliente.OnNewRecord(Sender: TDADataTable);
begin
  inherited;

  // PARCHE *******************
  if not PuedoLanzarEvento then
    Exit;

  POSICION := FPosicionNueva;
  NUMCONCEPTO := -1;
  TIPO := TIPODETALLE_CONCEPTO;

  Self.DataTable.DisableEventHandlers;
  try
    CANTIDAD := 1;
  finally
    Self.DataTable.EnableEventHandlers;
  end;
end;


function TBizFacturasCliente.GetSelectedRows: TSelectedRowList;
begin
  Result := FSelectedRows;
end;

procedure TBizFacturasCliente.ShowApplyUpdateFailed(const Error: EDAApplyUpdateFailed);
begin
  if (Pos(AUF_FKVIOLATION, Error.Message) > 0) then
    MessageBox(0, PChar(msgLitErrorBorrar), 'Atención', MB_ICONWARNING or MB_OK);
end;


procedure TBizFacturasCliente.BeforeDelete(Sender: TDADataTable);
begin
  inherited;
  FMasterDeleting := True;  // Para que los detalles de borren de golpe y no recalcule posiciones ni totales
end;

procedure TBizFacturasCliente.AfterDelete(Sender: TDADataTable);
begin
  inherited;
  FMasterDeleting := False;
end;

procedure TBizFacturasCliente.Print;
begin
  dmFacturasCliente.Print(Self.CODIGO);
end;

procedure TBizFacturasCliente.OnPostError(DataTable: TDADataTable;
  Error: EDatabaseError; var Action: TDataAction);
begin
  inherited;
  Action := daAbort;
  if (Pos('Nombre', Error.Message) > 0) then
     MessageBox(0, PChar(msgLitClienteObligatorio), 'Atención', MB_ICONWARNING or MB_OK)
  else if (Pos('Fecha', Error.Message) > 0) then
     MessageBox(0, PChar(msgLitFechaObligatorio), 'Atención', MB_ICONWARNING or MB_OK)
  else
     raise Error;
end;

procedure TBizFacturasCliente.BeforeApplyUpdates(Sender: TDADataTable;
  const Delta: IDADelta);
var
  i : integer;
begin
  for i := 0 to Delta.Count - 1 do
    case Delta.Changes[i].ChangeType of
      ctInsert, ctUpdate : ValidarFacturaCliente(Self);
      //ctDelete : 
    end;
end;

function TBizDetallesFacturasCliente.PuedoLanzarEvento: Boolean;
begin
  Result := FPuedoLanzarEvento;
end;

procedure TBizDetallesFacturasCliente.RecalcularImporte;
begin
  RecalcularImporteDetalle(Self.DataTable, True, False);
end;

procedure TBizFacturasCliente.RecalcularImporte;
begin
  RecalcularImportesCabecera(Self.DataTable, Detalles.DataTable);
end;

procedure TBizDetallesFacturasCliente.Refrescar;
begin
  DataTable.Refresh;
end;

procedure TBizDetallesFacturasCliente.SetCANTIDADValue(const aValue: Integer);
begin
  if aValue = 0 then
    DataTable.Fields[idx_DetallesFacturasClienteCANTIDAD].AsVariant := Null
  else
    inherited;
end;

procedure TBizDetallesFacturasCliente.SetIMPORTETOTALValue(const aValue: Currency);
begin
  if aValue = 0 then
    DataTable.Fields[idx_DetallesFacturasClienteIMPORTETOTAL].AsVariant := Null
  else
    inherited;
end;

procedure TBizDetallesFacturasCliente.SetIMPORTEUNIDADValue(const aValue: Currency);
begin
  if aValue = 0 then
    DataTable.Fields[idx_DetallesFacturasClienteIMPORTEUNIDAD].AsVariant := Null
  else
    inherited;
end;

procedure TBizFacturasCliente.setReferencia(const ClaseFactura: String);
begin
  if not (Self.DataTable.State in dsEditModes) then
     Self.DataTable.Edit;

  if ClaseFactura = CTE_PROFORMA then
    Self.REFERENCIA := dmFacturasCliente.DarNuevaReferencia(CTE_PROFORMA)
  else
    Self.REFERENCIA := dmFacturasCliente.DarNuevaReferencia(CTE_NORMAL);
end;

{ TBizClaseFacturaFieldRules }

procedure TBizFacturasCliente.CambiarSigno;
begin
  if not (State in dsEditModes) then
    Edit;

  ShowHourglassCursor;
  Application.ProcessMessages;
  try

    Detalles.CambiarSigno;
    RecalcularImporte;

  finally
    HideHourglassCursor;
  end;
end;

{procedure TBizFacturasCliente.SetCLASEFACTURAValue(const aValue: String);
begin
  showmessage('Antiguo valor' + CLASEFACTURA);
  showmessage('Nuevo valor' + aValue);

  inherited;
end;}


{ TBizFacturasObra }

{procedure TBizFacturasObra.Show;
begin
  inherited;
  ShowEditor(IBizFacturasObra, Self, etItem);
end;}

initialization
  FMasterDeleting := False;
  RegisterDataTableRules(BIZ_DETALLESFACTURASCLIENTE, TBizDetallesFacturasCliente);
  RegisterDataTableRules(BIZ_FACTURASCLIENTE, TBizFacturasCliente);
 // RegisterDataTableRules(BIZ_FACTURASOBRA, TBizFacturasObra);

finalization

end.
