unit uBizFacturasCliente; interface uses uDAInterfaces, uDADataTable, Classes, uBizContacto, DB, uBizImportesDetalleBase, uBizImportesCabeceraBase, uBizPresupuestosCliente, uDBSelectionList, uExceptions, uBizInformesBase, schFacturasClienteClient_Intf, uBizAlbaranesCliente; const BIZ_FACTURASCLIENTE = 'Client.FacturasCliente'; BIZ_DETALLESFACTURASCLIENTE = 'Client.DetallesFacturasCliente'; SITUACION_PENDIENTE = 'Pendiente'; SITUACION_PAGADO = 'Pagado'; CTE_IVA1 = 16; CTE_IVA2 = 1.16; type IBizDetallesFacturasCliente = interface(IDetallesFacturasCliente) ['{A628B3DE-2EF0-4A6E-AAFD-8BE582BDEE9A}'] procedure CopyFrom(ADetallesAlbaranes : IBizDetallesAlbaranCliente); overload; procedure CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente); overload; procedure DesglosarIVA(IVA : Double); 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: IBizCliente; procedure SetCliente(Value: IBizCliente); property Cliente: IBizCliente read GetCliente write SetCliente; procedure Show; procedure CopyFrom(AFacturaCliente : IBizFacturasCliente); overload; procedure CopyFrom(AAlbaranCliente : IBizAlbaranesCliente); overload; procedure DesglosarIVA; end; TBizDetallesFacturasCliente = class(TDetallesFacturasClienteDataTableRules, IBizDetallesFacturasCliente, IBizImportesDetalle, IBizPuntosDetalle, IBizVisibleDetalle, IBizValoradoDetalle, IParche) // PARCHE *********************** private FIsAppend : Boolean; FPosicionNueva : Integer; FPuedoLanzarEvento : Boolean; // PARCHE *********************** procedure SetCANTIDADValue(const aValue: Integer); override; procedure SetIMPORTEUNIDADValue(const aValue: Currency); override; procedure SetIMPORTETOTALValue(const aValue: Currency); override; protected 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(ADetallesAlbaranes : IBizDetallesAlbaranCliente); overload; procedure CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente); overload; procedure DesglosarIVA(IVA : Double); end; TBizFacturasCliente = class(TFacturasClienteDataTableRules, IBizFacturasCliente, IBizImportesCabecera, ISelectedRowList, IApplyUpdateFailedException, IBizInformesAware) private FCliente : IBizCliente; FDetalles: IBizDetallesFacturasCliente; FDetallesLink: TDADataSource; FSelectedRows : TSelectedRowList; protected procedure ShowApplyUpdateFailed (const Error: EDAApplyUpdateFailed); function GetDetalles: IBizDetallesFacturasCliente; procedure SetDetalles(Value: IBizDetallesFacturasCliente); function GetCliente: IBizCliente; procedure SetCliente(Value: IBizCliente); 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: IBizCliente read GetCliente write SetCliente; property Detalles: IBizDetallesFacturasCliente read GetDetalles write SetDetalles; procedure Show; procedure Preview; procedure Print; procedure CopyFrom(AFacturaCliente : IBizFacturasCliente); overload; procedure CopyFrom(AAlbaranCliente : IBizAlbaranesCliente); overload; procedure DesglosarIVA; constructor Create(aDataTable: TDADataTable); override; destructor Destroy; override; property SelectedRows : TSelectedRowList read GetSelectedRows; end; procedure ValidarFacturaCliente (const AFacturaCliente : IBizFacturasCliente); implementation uses Windows, Dialogs, uDACDSDataTable, SysUtils, uEditorUtils, Variants, Math, uDataModuleContactos, Controls, Forms, uDataModuleBase, uDataModuleUsuarios, uDataTableUtils, uDataModuleFacturasCliente, uEditarPreguntarIVA; var FMasterDeleting : Boolean; FCancelInsert : Boolean; procedure ValidarFacturaCliente (const AFacturaCliente : IBizFacturasCliente); begin if (FloatToStr(AFacturaCliente.FECHAFACTURA) = '0') then raise Exception.Create('Debe indicar la fecha de esta factura'); if not Assigned(AFacturaCliente.Cliente) or (AFacturaCliente.Cliente.DataTable.IsEmpty) then raise Exception.Create('Debe indicar el cliente de esta factura'); { AFacturaCliente.Edit; if (FloatToStr(AFacturaCliente.FECHAPAGO) = '0') then AFacturaCliente.SITUACION := SITUACION_PENDIENTE else AFacturaCliente.SITUACION := SITUACION_PAGADO; AFacturaCliente.Post;} if (Length(AFacturaCliente.REFERENCIA) = 0) then begin AFacturaCliente.Edit; AFacturaCliente.REFERENCIA := dmFacturasCliente.DarNuevaReferencia; AFacturaCliente.Post; ShowMessage('A esta factura se le ha asignado la referencia ' + AFacturaCliente.REFERENCIA); end; 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: IBizCliente; begin if not Assigned(FCliente) or ((CODIGOCONTACTO <> FCliente.Codigo) and not (FCliente.DataTable.State in dsEditModes)) then begin FCliente := dmContactos.GetCliente(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; end; function TBizFacturasCliente.GetDetalles: IBizDetallesFacturasCliente; begin Result := FDetalles; end; procedure TBizFacturasCliente.CopyFrom(AFacturaCliente : IBizFacturasCliente); var ACursor: TCursor; begin { if not (State in dsEditModes) then Edit; ACursor := Screen.Cursor; Screen.Cursor := crHourGlass; Application.ProcessMessages; try CODIGOEMPRESA := APresupuesto.CODIGOEMPRESA; CODIGOCONTACTO := APresupuesto.CODIGOCONTACTO; BASEIMPONIBLE := APresupuesto.BASEIMPONIBLE; DESCUENTO := APresupuesto.DESCUENTO; IVA := APresupuesto.IVA; OBSERVACIONES := APresupuesto.OBSERVACIONES; FORMAPAGO := APresupuesto.FORMAPAGO; CODIGOPRESUPUESTO := APresupuesto.CODIGO; Cliente := APresupuesto.Cliente; Detalles.CopyFrom(APresupuesto.Detalles); RecalcularImporte; MessageBox(0, 'Se ha copiado correctamente el presupuesto elegido en este albarán.', 'Presupuesto copiado', MB_ICONWARNING or MB_OK); finally Screen.Cursor := ACursor; end;} end; procedure TBizFacturasCliente.OnNewRecord(Sender: TDADataTable); begin inherited; CODIGOEMPRESA := dmBase.CodigoEmpresa; USUARIO := dmUsuarios.LoginInfo.UserID; FECHAALTA := Date; FECHAFACTURA := Date; CODIGO := dmFacturasCliente.GetNextAutoinc; end; procedure TBizFacturasCliente.Preview; begin dmFacturasCliente.Preview(Self.CODIGO); end; procedure TBizFacturasCliente.SetCliente(Value: IBizCliente); var bEnEdicion : Boolean; begin 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 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.CopyFrom(ADetallesFacturas : IBizDetallesFacturasCliente); begin // end; procedure TBizDetallesFacturasCliente.CopyFrom(ADetallesAlbaranes : IBizDetallesAlbaranCliente); var ImporteSinIva : Double; AcumulaSobrante : Double; ImporteSinIvaRedondeado : Currency; begin DeleteAllTable(Self.DataTable); AcumulaSobrante := 0; ADetallesAlbaranes.First; while not ADetallesAlbaranes.EOF do begin Append; DataTable.DisableControls; DataTable.DisableEventHandlers; try TIPO := ADetallesAlbaranes.TIPO; DESCRIPCION := ADetallesAlbaranes.DESCRIPCION; CANTIDAD := ADetallesAlbaranes.CANTIDAD; IMPORTEUNIDAD := ADetallesAlbaranes.IMPORTEUNIDAD; VISIBLE := ADetallesAlbaranes.VISIBLE; VALORADO := ADetallesAlbaranes.VALORADO; finally DataTable.EnableControls; DataTable.EnableEventHandlers; end; RecalcularImporte; Post; ADetallesAlbaranes.Next; end; RecalcularSubtotales(DataTable); 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.DesglosarIVA(IVA : Double); var ImporteSinIva : Double; AcumulaSobrante : Double; ImporteSinIvaRedondeado : Currency; begin AcumulaSobrante := 0; if DataTable.IsEmpty then Exit; DataTable.First; while not DataTable.EOF do begin DataTable.Edit; DataTable.DisableControls; DataTable.DisableEventHandlers; try ImporteSinIva := IMPORTEUNIDAD / (1 + IVA/100); ImporteSinIvaRedondeado := RoundTo(ImporteSinIva, -2); if ImporteSinIva < ImporteSinIvaRedondeado then AcumulaSobrante := AcumulaSobrante - (CANTIDAD*(ImporteSinIvaRedondeado - ImporteSinIva)) else AcumulaSobrante := AcumulaSobrante + (CANTIDAD*(ImporteSinIva - ImporteSinIvaRedondeado)); IMPORTEUNIDAD := ImporteSinIvaRedondeado; finally DataTable.EnableControls; DataTable.EnableEventHandlers; end; RecalcularImporte; DataTable.Post; DataTable.Next; end; // Agregamos al último concepto de los detalles los decimales // que hemos ido acumulando while (not DataTable.BOF) and (TIPO <> TIPODETALLE_CONCEPTO) do DataTable.Prior; Edit; IMPORTEUNIDAD := IMPORTEUNIDAD + AcumulaSobrante; RecalcularSubtotales(DataTable); end; procedure TBizDetallesFacturasCliente.OnNewRecord(Sender: TDADataTable); begin inherited; // PARCHE ******************* if not PuedoLanzarEvento then Exit; POSICION := FPosicionNueva; NUMCONCEPTO := -1; TIPO := TIPODETALLE_CONCEPTO; VISIBLE := VISIBLE_TRUE; VALORADO := VALORADO_TRUE; 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, 'No se puede borrar esta factura porque...', 'Atención', MB_ICONWARNING or MB_OK); end; procedure TBizFacturasCliente.BeforeDelete(Sender: TDADataTable); begin inherited; if not dmFacturasCliente.PuedoEliminarFactura(CODIGO) then raise Exception.Create('No se puede borrar esta factura porque...'); 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(AUF_HAVEVALUE, Error.Message) > 0) then begin if (Pos('contacto', Error.Message) > 0) then MessageBox(0, 'Debe indicar el cliente de esta factura', 'Atención', MB_ICONWARNING or MB_OK) else if (Pos('Fecha', Error.Message) > 0) then MessageBox(0, 'Debe indicar la fecha de esta factura', 'Atención', MB_ICONWARNING or MB_OK) else raise Error; end 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 TBizFacturasCliente.CopyFrom(AAlbaranCliente: IBizAlbaranesCliente); var ACursor: TCursor; begin if not (State in dsEditModes) then Edit; if not (AAlbaranCliente.DataTable.Active) then AAlbaranCliente.DataTable.Active := True; ACursor := Screen.Cursor; Screen.Cursor := crHourGlass; Application.ProcessMessages; try CODIGOEMPRESA := AAlbaranCliente.CODIGOEMPRESA; CODIGOCONTACTO := AAlbaranCliente.CODIGOCONTACTO; NOMBRE := AAlbaranCliente.Cliente.NOMBRE; BASEIMPONIBLE := AAlbaranCliente.BASEIMPONIBLE; DESCUENTO := AAlbaranCliente.DESCUENTO; IVA := AAlbaranCliente.IVA; OBSERVACIONES := AAlbaranCliente.OBSERVACIONES; FORMAPAGO := AAlbaranCliente.FORMAPAGO; CODIGOALBARAN := AAlbaranCliente.CODIGO; Cliente := AAlbaranCliente.Cliente; Detalles.CopyFrom(AAlbaranCliente.Detalles); RecalcularImporte; finally Screen.Cursor := ACursor; end; 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.DesglosarIVA; var aIVA : Double; ACursor: TCursor; begin if (Self.IVA = 0) then aIVA := 21; if PreguntarIVA(aIVA) then begin ACursor := Screen.Cursor; Screen.Cursor := crHourGlass; Application.ProcessMessages; try DataTable.Edit; Self.IVA := aIVA; Detalles.DesglosarIVA(aIVA); RecalcularImporte; finally Screen.Cursor := ACursor; end; MessageBox(0, 'Se ha desglosado el IVA de la factura', 'Información', MB_ICONINFORMATION or MB_OK) end; end; initialization FMasterDeleting := False; RegisterDataTableRules(BIZ_DETALLESFACTURASCLIENTE, TBizDetallesFacturasCliente); RegisterDataTableRules(BIZ_FACTURASCLIENTE, TBizFacturasCliente); finalization end.