unit uFacturasClienteController; interface uses Windows, Classes, SysUtils, Variants, uDADataTable, uControllerBase, uIDataModuleFacturasCliente, uClientesController, uDetallesFacturaClienteController, uBizAlbaranesCliente, uBizFacturasCliente, uBizDireccionesContacto; type IFacturasClienteController = interface(IControllerBase) ['{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; function BuscarTodasPendientesComision(IdAgente: Integer; IdComision: Integer; IdFacturasAsociadas: String): IBizFacturaCliente; procedure Ver(AFactura : IBizFacturaCliente); procedure VerTodos(AFacturas: IBizFacturaCliente); function Nuevo (WithInsert: Boolean = True) : IBizFacturaCliente; function Anadir(AFactura : IBizFacturaCliente) : Boolean; overload; function AnadirAbono(AFactura : IBizFacturaCliente) : Boolean; function Anadir(AFacturas : IBizFacturaCliente; AListaAlbaranes : IBizAlbaranCliente): Boolean; overload; function Eliminar(const ID : Integer): Boolean; overload; function Eliminar(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AListaCausas: TStringList = nil): Boolean; overload; function Guardar(AFactura : IBizFacturaCliente): Boolean; procedure DescartarCambios(AFactura : IBizFacturaCliente); function Existe(const ID: Integer) : Boolean; procedure RecuperarCliente(AFactura : IBizFacturaCliente); function Duplicar(AFactura: IBizFacturaCliente): IBizFacturaCliente; function GenerarAbono(AFactura: IBizFacturaCliente): IBizFacturaCliente; procedure Preview(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); procedure Print(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); procedure EnviarFacturaPorEMail(AFactura : IBizFacturaCliente); procedure RecalcularImportes(AFactura: IBizFacturaCliente); function ElegirFacturas(AFacturas : IBizFacturaCliente; AMensaje: String; AMultiSelect: Boolean): IBizFacturaCliente; function ExtraerSeleccionados(ARecibosCliente: IBizFacturaCliente) : IBizFacturaCliente; procedure CopiarDireccion (const ADireccionEnvio: IBizDireccionesContacto; AFactura: IBizFacturaCliente); procedure SetIgnorarContabilidad (AFactura: IBizFacturaCliente; const Ignorar: Integer); function DarListaAnosFacturas: TStringList; procedure FiltrarAno(AFactura: IBizFacturaCliente; ADynWhereDataTable: WideString; const Ano: String); function CambiarSituacion(AFactura : IBizFacturaCliente; AllItems: Boolean = false; ASituacion: String = CTE_PENDIENTE; AListaCausas: TStringList = nil): Boolean; function AsignarLOPD(AFacturas: IBizFacturaCliente): Boolean; end; TFacturasClienteController = class(TControllerBase, IFacturasClienteController) private FDataModule : IDataModuleFacturasCliente; FClienteController : IClientesController; FDetallesController : IDetallesFacturaClienteController; function GetClienteController: IClientesController; procedure SetClienteController(const Value: IClientesController); function GetDetallesController: IDetallesFacturaClienteController; procedure SetDetallesController(const Value: IDetallesFacturaClienteController); function CreateEditor(const AName : String; const IID: TGUID; out Intf): Boolean; function _Vacio : IBizFacturaCliente; procedure FiltrarEmpresa(AFactura: IBizFacturaCliente); function ValidarFactura(AFactura: IBizFacturaCliente): Boolean; procedure GenerarRecibos(AFactura: IBizFacturaCliente); protected procedure RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable); override; public property ClienteController: IClientesController read GetClienteController write SetClienteController; property DetallesController: IDetallesFacturaClienteController read GetDetallesController write SetDetallesController; constructor Create; override; destructor Destroy; override; function Eliminar(const ID : Integer): Boolean; overload; function Eliminar(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AListaCausas: TStringList = nil): Boolean; overload; function Guardar(AFactura : IBizFacturaCliente): Boolean; procedure DescartarCambios(AFactura : IBizFacturaCliente); virtual; function Existe(const ID: Integer) : Boolean; virtual; function Anadir(AFactura : IBizFacturaCliente): Boolean; overload; function AnadirAbono(AFactura : IBizFacturaCliente) : Boolean; function Anadir(AFacturas : IBizFacturaCliente; AListaAlbaranes : IBizAlbaranCliente): Boolean; overload; function Buscar(const ID: Integer): IBizFacturaCliente; function BuscarTodos: IBizFacturaCliente; function BuscarTodasPendientesComision(IdAgente: Integer; IdComision: Integer; IdFacturasAsociadas: String): IBizFacturaCliente; function Nuevo (WithInsert: Boolean = True) : IBizFacturaCliente; procedure Ver(AFactura : IBizFacturaCliente); procedure VerTodos(AFacturas: IBizFacturaCliente); function Duplicar(AFactura: IBizFacturaCliente): IBizFacturaCliente; function GenerarAbono(AFactura: IBizFacturaCliente): IBizFacturaCliente; procedure RecuperarCliente(AFactura : IBizFacturaCliente); procedure Preview(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); procedure Print(AFactura : IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); procedure EnviarFacturaPorEMail(AFactura : IBizFacturaCliente); procedure RecalcularImportes(AFactura: IBizFacturaCliente); function EsModificable(AFactura: IBizFacturaCliente; var AComentario: Variant): Boolean; function EsEliminable(AFactura: IBizFacturaCliente; var AComentario: Variant): Boolean; function ElegirFacturas(AFacturas : IBizFacturaCliente; AMensaje: String; AMultiSelect: Boolean): IBizFacturaCliente; function ExtraerSeleccionados(AFacturasCliente: IBizFacturaCliente) : IBizFacturaCliente; procedure CopiarDireccion (const ADireccionEnvio: IBizDireccionesContacto; AFactura: IBizFacturaCliente); procedure SetIgnorarContabilidad (AFactura: IBizFacturaCliente; const Ignorar: Integer); function DarListaAnosFacturas: TStringList; procedure FiltrarAno(AFactura: IBizFacturaCliente; ADynWhereDataTable: WideString; const Ano: String); function CambiarSituacion(AFactura : IBizFacturaCliente; AllItems: Boolean = false; ASituacion: String = CTE_PENDIENTE; AListaCausas: TStringList = nil): Boolean; function AsignarLOPD(AFacturas: IBizFacturaCliente): Boolean; end; implementation uses uDADelta, uDateUtils, uNumUtils, Controls, cxControls, DB, uEditorRegistryUtils, schFacturasClienteClient_Intf, uBizContactos, uIEditorFacturasCliente, uIEditorFacturaCliente, uFactuGES_App, uDataModuleFacturasCliente, uBizDetallesFacturaCliente, uControllerDetallesBase, uDataModuleUsuarios, uDAInterfaces, uDataTableUtils, uAlbaranesClienteController, schAlbaranesClienteClient_Intf, uROTypes, uDetallesAlbaranClienteController, uBizDetallesAlbaranCliente, uFacturasClienteReportController, DateUtils, Forms, Dialogs, uIntegerListUtils, uFormasPagoController, uBizFormasPago, uIEditorElegirFacturasCliente, uBizEjercicios, uRecibosClienteController, uBizRecibosCliente, uSistemaFunc, uEMailUtils, uDialogElegirEMail, uDialogUtils; //, uDialogUtils, Windows; procedure CopiarArticulosAlbaran(AOrigen: IBizDetallesAlbaranCliente; ADestino : IBizDetallesFacturaCliente); var i : integer; ADetallesController : IDetallesFacturaClienteController; begin if not Assigned(AOrigen) then raise Exception.Create ('Origen no asignado (CopiarArticulosAlbaran)'); if not Assigned(ADestino) then raise Exception.Create ('Destino no asignado (CopiarArticulosAlbaran)'); if not AOrigen.DataTable.Active then AOrigen.DataTable.Active := True; if not ADestino.DataTable.Active then ADestino.DataTable.Active := True; ADetallesController := TDetallesFacturaClienteController.Create; try //OJO IMPORTANTE //Siempre que vayamos a trabajar con los detalles debemos hacer un beginupdate de los mismos y un endupdate para //obligarle siempre a recalcular los detalles una sola vez ADetallesController.BeginUpdate(ADestino); AOrigen.DataTable.First; for i := 0 to AOrigen.DataTable.RecordCount - 1 do begin ADetallesController.Add(ADestino, TIPO_DETALLE_CONCEPTO); ADestino.Edit; // ADestino.REFERENCIA := AOrigen.REFERENCIA; if AOrigen.ID_ARTICULO > 0 then ADestino.ID_ARTICULO := AOrigen.ID_ARTICULO; ADestino.CONCEPTO := AOrigen.CONCEPTO; ADestino.CANTIDAD := AOrigen.CANTIDAD; ADestino.IMPORTE_UNIDAD := AOrigen.IMPORTE_UNIDAD; ADestino.IMPORTE_TOTAL := AOrigen.IMPORTE_TOTAL; ADestino.DESCUENTO := AOrigen.DESCUENTO; ADestino.IMPORTE_PORTE := AOrigen.IMPORTE_PORTE; ADestino.VISIBLE := AOrigen.VISIBLE; // ADestino.REFERENCIA_PROVEEDOR := AOrigen.REFERENCIA_PROVEEDOR; ADestino.Post; AOrigen.Next; end; finally ADetallesController.EndUpdate(ADestino); ADetallesController := NIL; end; end; { TFacturasClienteController } function TFacturasClienteController.Anadir(AFactura: IBizFacturaCliente) : Boolean; begin if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada (Anadir)'); AFactura.Insert; Result := True; end; function TFacturasClienteController.AnadirAbono(AFactura: IBizFacturaCliente): Boolean; begin Anadir(AFactura); AFactura.TIPO := CTE_TIPO_ABONO; Result := True; end; function TFacturasClienteController.AsignarLOPD(AFacturas: IBizFacturaCliente): Boolean; var ACliente : IBizCliente; AResultado : Boolean; begin Result := false; if Assigned(AFacturas) then begin with AFacturas.DataTable do begin First; while not EOF do begin ACliente := (FClienteController.Buscar(AFacturas.ID_Cliente) as IBizCliente); AResultado := FClienteController.AsignarLOPD(ACliente); if not Result then Result := AResultado; Next; end; end; end; end; function TFacturasClienteController.Anadir(AFacturas: IBizFacturaCliente; AListaAlbaranes: IBizAlbaranCliente): Boolean; var AFacturaActual : IBizFacturaCliente; AAlbaranesController : IAlbaranesClienteController; I: Integer; bEnEdicion : Boolean; begin Result := False; // ATENCIÓN!!! AFacturas tiene que estar vacio para no pisar facturas // ya generadas. if not Assigned(AFacturas) then raise Exception.Create ('Factura no asignada (Anadir)'); if not Assigned(AListaAlbaranes) then raise Exception.Create ('Albaranes no asignados (Anadir)'); if not AFacturas.DataTable.Active then AFacturas.DataTable.Active := True; if not AListaAlbaranes.DataTable.Active then AListaAlbaranes.DataTable.Active := True; ShowHourglassCursor; Application.ProcessMessages; AAlbaranesController := TAlbaranesClienteController.Create; try // Ordenar por fecha de albaran AListaAlbaranes.DataTable.Sort([fld_AlbaranesClienteFECHA_ALBARAN], [uDADataTable.sdAscending]); AListaAlbaranes.First; for I := 0 to AListaAlbaranes.DataTable.RecordCount - 1 do begin AListaAlbaranes._Cliente := NIL; AFacturaActual := NIL; // Busco si hay alguna factura ya hecha de ese cliente AFacturas.DataTable.First; if AFacturas.DataTable.Locate(fld_FacturasClienteID_CLIENTE, AListaAlbaranes.ID_CLIENTE, []) then begin AFacturaActual := AFacturas; RecuperarCliente(AFacturaActual); AFacturaActual.Cliente.DataTable.Active := True; end else begin // No hay factura de ese cliente. Creo una nueva Anadir(AFacturas); AAlbaranesController.RecuperarCliente(AListaAlbaranes); AListaAlbaranes.Cliente.DataTable.Active := True; AFacturas.Cliente := AListaAlbaranes.Cliente; //Si el albaran es de tipo devolución hacemos la factura de tipo abono if (AListaAlbaranes.TIPO = CTE_TIPO_ALBARAN_DEV) then begin bEnEdicion := (AFacturas.DataTable.State in dsEditModes); if not bEnEdicion then AFacturas.Edit; AFacturas.TIPO := CTE_TIPO_ABONO; AFacturas.Post; if bEnEdicion then AFacturas.Edit; end; AFacturaActual := AFacturas; end; // Ya tengo la factura. Le añado los conceptos del albarán AFacturaActual.Detalles.DataTable.Last; // Añado el título Self.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_TITULO); with AFacturaActual.Detalles do begin Edit; CONCEPTO := 'Albarán ' + AListaAlbaranes.REFERENCIA + ' del ' + DateToStr(AListaAlbaranes.FECHA_ALBARAN); Post; end; // Añado el contenido del albarán CopiarArticulosAlbaran(AListaAlbaranes.Detalles, AFacturaActual.Detalles); Self.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO); with AFacturaActual.Detalles do begin Edit; CONCEPTO := 'Porte del albarán'; CANTIDAD := 1; IMPORTE_UNIDAD := AListaAlbaranes.IMPORTE_PORTE; Post; end; // Añado el resumen Self.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_SUBTOTAL); with AFacturaActual.Detalles do begin Edit; CONCEPTO := 'Total del albarán ' + AListaAlbaranes.REFERENCIA; Post; end; // Añado una línea en blanco Self.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO); with AFacturaActual.Detalles do begin Edit; CONCEPTO := ''; Post; end; // Guardo la factura que acabo de generar o editar AFacturaActual.CalcularImporteTotal; Self.Guardar(AFacturaActual); // Asocio la factura con el albarán AListaAlbaranes.Edit; AListaAlbaranes.ID_FACTURA := AFacturaActual.ID; AListaAlbaranes.Post; AAlbaranesController.Guardar(AListaAlbaranes); AListaAlbaranes.Next; end; Result := True; finally AAlbaranesController := NIL; HideHourglassCursor end; end; {function TFacturasClienteController.Anadir(AFactura: IBizFacturaCliente; const IDPedido: Integer): Boolean; var APedidosController : IPedidosClienteController; APedido : IBizPedidoCliente; begin Result := False; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada (Anadir)'); if (IDPedido < 0) or (IDPedido = 0) then raise Exception.Create (Format('ID de pedido (%d) incorrecto (Anadir)', [IDPedido])); if not AFactura.DataTable.Active then AFactura.DataTable.Active := True; ShowHourglassCursor; Application.ProcessMessages; APedido := NIL; APedidosController := TPedidosClienteController.Create; try APedido := APedidosController.Buscar(IDPedido); if not Assigned(APedido) then raise Exception.Create (Format('No se ha encontrado un pedido de cliente con ID %d (Anadir)', [IDPedido])); APedido.DataTable.Active := True; APedidosController.RecuperarCliente(APedido); Self.Anadir(AFactura); AFactura.Cliente := APedido.Cliente; CopiarArticulosPedido(APedido.Detalles, AFactura.Detalles); // Guardo la factura que acabo de generar AFactura.CalcularImporteTotal; Self.Guardar(AFactura); Result := True; finally APedido := NIL; APedidosController := NIL; HideHourglassCursor; Application.ProcessMessages; end; end;} function TFacturasClienteController.Buscar(const ID: Integer): IBizFacturaCliente; begin Result := (FDataModule as IDataModuleFacturasCliente).GetItem(ID); FiltrarEmpresa(Result); end; function TFacturasClienteController.BuscarTodasPendientesComision(IdAgente:Integer; IdComision: Integer; IdFacturasAsociadas: String): IBizFacturaCliente; var Condicion: TDAWhereExpression; Condicion1: TDAWhereExpression; Condicion2: TDAWhereExpression; Condicion3: TDAWhereExpression; Condicion4: TDAWhereExpression; Cadena : TStringList; i: Integer; begin ShowHourglassCursor; try Result := BuscarTodos; with Result.DataTable.DynamicWhere do begin //Todas las facturas que sean comisionables Condicion := NewBinaryExpression(NewField('', fld_FacturasClienteSIN_COMISION), NewConstant(0, datInteger), dboEqual); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; with Result.DataTable.DynamicWhere do begin //Todas las facturas de esta liquidación Condicion1 := NewBinaryExpression(NewField('', fld_FacturasClienteID_COMISION_LIQUIDADA), NewConstant(IdComision, datInteger), dboEqual); Condicion2 := nil; //Todas aquellas facturas que ya esten asociadas a la liquidación actual no deben salir if (Length(IdFacturasAsociadas) > 0) then begin Cadena := TStringList.Create; Cadena.CommaText := IdFacturasAsociadas; //Vamos generando todas las where de cada uno de los ID recibidos por parametro for i := 0 to Cadena.Count - 1 do with Result.DataTable.DynamicWhere do begin Condicion4 := NewBinaryExpression(NewField('', fld_FacturasClienteID), NewConstant(StrToInt(Cadena.Strings[i]), datInteger), dboNotEqual); if Condicion2 = nil then Condicion2 := Condicion4 else Condicion2 := NewBinaryExpression(Condicion2, Condicion4, dboAnd); end; Cadena.Free; end; //Si no hay facturas en el editor no las tenemos en cuenta if (Condicion2 <> nil) then Condicion1 := NewBinaryExpression(Condicion1, Condicion2, dboAnd); //Todas aquellas que no esten asociadas a ninguna liquidación, y que tampoco esten en el editor, ya que en cache estan //asociadas a la liquidadcion pero en datos me viene como nula Condicion3 := NewBinaryExpression(NewField('', fld_FacturasClienteID_COMISION_LIQUIDADA), NewNull(), dboEqual); Condicion2 := nil; //Todas aquellas facturas que ya esten asociadas a la liquidación actual no deben salir if (Length(IdFacturasAsociadas) > 0) then begin Cadena := TStringList.Create; Cadena.CommaText := IdFacturasAsociadas; //Vamos generando todas las where de cada uno de los ID recibidos por parametro for i := 0 to Cadena.Count - 1 do with Result.DataTable.DynamicWhere do begin Condicion4 := NewBinaryExpression(NewField('', fld_FacturasClienteID), NewConstant(StrToInt(Cadena.Strings[i]), datInteger), dboNotEqual); if Condicion2 = nil then Condicion2 := Condicion4 else Condicion2 := NewBinaryExpression(Condicion2, Condicion4, dboAnd); end; Cadena.Free; end; //Si no hay facturas en el editor no las tenemos en cuenta if (Condicion2 <> nil) then Condicion3 := NewBinaryExpression(Condicion3, Condicion2, dboAnd); // Todas aquellas facturas asociadas a la comisión actual y que no estén asociadas al editor (IdFacturasAsociadas) //esto es porque se puede agregar y quitar facturas y todos los cambios estan en cache // y por lo tanto al pedir las facturas a seleccionar debemos tener en cuenta que en el //almacén dichos cambios cache todavía no se han producido Condicion := NewBinaryExpression(Condicion1, Condicion3, dboOr); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; finally HideHourglassCursor; end; end; function TFacturasClienteController.BuscarTodos: IBizFacturaCliente; begin Result := FDataModule.GetItems; FiltrarEmpresa(Result); end; function TFacturasClienteController.CambiarSituacion(AFactura: IBizFacturaCliente; AllItems: Boolean; ASituacion: String; AListaCausas: TStringList): Boolean; //Solo devolverá true en el caso de cambiar de situación a todas las facturas var bCambioOK: Boolean; ACausa: Variant; bEnEdicion: Boolean; ARecibosClienteController : IRecibosClienteController; ARecibos: IBizRecibosCliente; begin bCambioOK := True; ARecibosClienteController := TRecibosClienteController.Create; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada'); ShowHourglassCursor; try if not AFactura.DataTable.Active then AFactura.DataTable.Active := True; if (AFactura.State in dsEditModes) then AFactura.Cancel; if not AllItems then begin if not EsModificable(AFactura, ACausa) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%s=%s', [AFactura.REFERENCIA, ACausa])); bCambioOK := False; end else begin //Buscamos si tiene recibos la factura si es así no podrá cambiar de situación ya que son los recibos los que la definen ARecibos := ARecibosClienteController.BuscarRecibosFactura(AFactura.ID); ARecibos.open; if (ARecibos.RecordCount > 0) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%s=%s', [AFactura.REFERENCIA, 'La factura tiene recibos asociados que definen su situación'])); bCambioOK := False; ARecibos.Close; ARecibos := Nil; end else begin bEnEdicion := (AFactura.DataTable.State in dsEditModes); if not bEnEdicion then AFactura.DataTable.Edit; AFactura.SITUACION := ASituacion; AFactura.DataTable.Post; if bEnEdicion then AFactura.DataTable.Edit; end; end end else //En el caso de querer modificar todos los items del objeto AFactura begin with AFactura.DataTable do begin First; while not EOF do begin if not EsEliminable(AFactura, ACausa) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%s=%s', [AFactura.REFERENCIA, ACausa])); bCambioOK := False; end else begin //Buscamos si tiene recibos la factura si es así no podrá cambiar de situación ya que son los recibos los que la definen ARecibos := ARecibosClienteController.BuscarRecibosFactura(AFactura.ID); ARecibos.open; if (ARecibos.RecordCount > 0) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%s=%s', [AFactura.REFERENCIA, 'La factura tiene recibos asociados que definen su situación'])); bCambioOK := False; ARecibos.Close; ARecibos := Nil; end else begin bEnEdicion := (State in dsEditModes); if not bEnEdicion then Edit; AFactura.SITUACION := ASituacion; Post; if bEnEdicion then Edit; end; end; Next; end; end; end; try AFactura.DataTable.ApplyUpdates; Result := bCambioOK; except AFactura.DataTable.CancelUpdates; Result := False; end; finally ARecibosClienteController:= Nil; HideHourglassCursor; end; end; procedure TFacturasClienteController.CopiarDireccion( const ADireccionEnvio: IBizDireccionesContacto; AFactura: IBizFacturaCliente); var bEnEdicion : Boolean; begin if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada (CopiarDireccion)'); if not Assigned(ADireccionEnvio) then raise Exception.Create ('No se ha indicado la dirección (CopiarDireccion)'); if AFactura.DataTable.Active then AFactura.DataTable.Active := True; if ADireccionEnvio.DataTable.Active then ADireccionEnvio.DataTable.Active := True; bEnEdicion := (AFactura.DataTable.State in dsEditModes); if not bEnEdicion then AFactura.Edit; ShowHourglassCursor; AFactura.Edit; try AFactura.CALLE := ADireccionEnvio.CALLE; AFactura.POBLACION := ADireccionEnvio.POBLACION; AFactura.CODIGO_POSTAL := ADireccionEnvio.CODIGO_POSTAL; AFactura.PROVINCIA := ADireccionEnvio.PROVINCIA; AFactura.NIF_CIF := ADireccionEnvio.NIF_CIF; AFactura.NOMBRE := ADireccionEnvio.NOMBRE; if not bEnEdicion then AFactura.Post; finally HideHourglassCursor; end; end; constructor TFacturasClienteController.Create; begin inherited; 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.DarListaAnosFacturas: TStringList; begin Result := FDataModule.GetAnosItems; 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 := AppFactuGES.EmpresaActiva.ID; USUARIO := AppFactuGES.UsuarioActivo.UserName; ID_TIENDA := AppFactuGES.TiendaActiva.ID; TIENDA := AppFactuGES.TiendaActiva.NOMBRE; REFERENCIA := ''; //Para que se asigne una nueva SITUACION := CTE_PENDIENTE; //Al ser una nueva debe de estar pendiente FECHA_FACTURA := DateOf(Now); ID_COMISION_LIQUIDADA := 0; //CONTABILIDAD ID_EJERCICIO := AppFactuGES.EjercicioActivo.ID; ESTADO_EJERCICIO := AppFactuGES.EjercicioActivo.ESTADO; ASIENTO_PUNTEADO := -1; //En el caso de que el ejercicio este cerrado dejamos duplicar factura sin asociar a contabilidad. if ESTADO_EJERCICIO = CTE_CERRADO then begin IGNORAR_CONTABILIDAD := 1; ESTADO_EJERCICIO := ''; end; end; Result.Post; finally HideHourglassCursor; end; end; function TFacturasClienteController.ValidarFactura(AFactura: IBizFacturaCliente): Boolean; var AFormaPago: IBizFormaPago; begin Result := False; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada'); 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; if (AFactura.ID_CLIENTE < 0) or (AFactura.ID_CLIENTE = 0) then // No comprobar el objeto Cliente por que puede fallar la validación // cuando se generan facturas automáticamente. { (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'); if (AFactura.Detalles.DataTable.RecordCount = 0) then raise Exception.Create('La factura debe tener al menos un concepto en su contenido'); if (AFactura.ID_FORMA_PAGO = 0) then raise Exception.Create('Debe indicar una forma de pago para esta factura'); //De esta forma obligaremos siempre a tener un recibo asociado a la factura, //porque si la forma de pago no tiene plazos es obligatorio la fecha de vencimiento //OJO-----En facturas de cliente para acana no es obligatorio porque deben de poder generar facturas sin recibos asociados { with TFormasPagoController.Create do begin try AFormaPago := Buscar(AFactura.ID_FORMA_PAGO); AFormaPago.DataTable.Active := True; if (AFormaPago.Plazos.RecordCount = 0) and (EsFechaVacia(AFactura.FECHA_VENCIMIENTO)) then raise Exception.Create('Debe indicar una fecha de vencimiento para esta factura'); finally AFormaPago := NIL; Free; end; end; } if (AFactura.IGNORAR_CONTABILIDAD = 0) then begin if (AppFactuGES.EjercicioActivo.ESTADO = CTE_CERRADO) then raise Exception.Create('Para que la factura pueda pasar a la parte contable el ejercicio activo debe esta ABIERTO'); if ((AFactura.Cliente.SubCuentas.ID < 1) or (AFactura.Cliente.SubCuentas.ID_EJERCICIO <> AppFactuGES.EjercicioActivo.ID)) then raise Exception.Create('Para que la factura pueda pasar la parte contable es necesario que el cliente tenga asignada una subcuenta'); end; //En caso de ser un Abono no podra tener un importe total positivo if (AFactura.TIPO = CTE_TIPO_ABONO) then if (AFactura.IMPORTE_TOTAL > 0) then raise Exception.Create('Un abono nunca no puede tener un importe positivo, asegurese que las cantidades sean negativas'); { Esta validación puede saltar cuando se generan facturas automáticamente por albaranes o pedidos y el cliente no tiene Tipo de IVA puesto, se cambia, para signar automáticamente por defecto a la factura la primera forma de pago, e iva disponible (CONTADO, IVA16), en la clase de negocio } { if (AFactura.ID_TIPO_IVA = 0) then raise Exception.Create('Debe indicar un tipo de IVA para esta factura');} { Asegurarse de valores en campos "automáticos" tanto en MODIFICACIÓN como en INSERCIÓN. } AFactura.Edit; try AFactura.USUARIO := AppFactuGES.UsuarioActivo.UserName; if Assigned(AFactura.Cliente) and (AFactura.ID_CLIENTE <> AFactura.Cliente.ID) then AFactura.ID_CLIENTE := AFactura.Cliente.ID; Result := True; finally AFactura.Post; end; end; procedure TFacturasClienteController.Ver(AFactura: IBizFacturaCliente); var AEditor : IEditorFacturaCliente; AComentario : Variant; begin AEditor := NIL; RecuperarCliente(AFactura); CreateEditor('EditorFacturaCliente', IEditorFacturaCliente, AEditor); if Assigned(AEditor) then try AEditor.Controller := Self; //OJO ORDEN MUY IMPORTANTE AEditor.Factura := AFactura; //MODO CONSULTAR if not EsModificable(AFactura, AComentario) then begin SetDataTableReadOnly(AFactura.DataTable, True); AEditor.ReadOnly := True; end; if (AComentario <> null) then AEditor.Comentario := AComentario; AEditor.ShowModal; //MODO CONSULTAR (Se deja la tabla como estaba) if AEditor.ReadOnly then SetDataTableReadOnly(AFactura.DataTable, False); finally AEditor.Release; AEditor := NIL; end; end; procedure TFacturasClienteController.VerTodos(AFacturas: IBizFacturaCliente); var AEditor : IEditorFacturasCliente; begin AEditor := NIL; CreateEditor('EditorFacturasCliente', IEditorFacturasCliente, AEditor); if Assigned(AEditor) then with AEditor do begin Controller := Self; //OJO ORDEN MUY IMPORTANTE Facturas := AFacturas; MultiSelect := True; ShowEmbedded; end; end; function TFacturasClienteController._Vacio: IBizFacturaCliente; begin Result := Buscar(ID_NULO); end; function TFacturasClienteController.Eliminar(const ID: Integer): Boolean; 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])); Result := Eliminar(AFactura); AFactura := NIL; end; function TFacturasClienteController.ElegirFacturas(AFacturas: IBizFacturaCliente; AMensaje: String; AMultiSelect: Boolean): IBizFacturaCliente; var AEditor : IEditorElegirFacturasCliente; begin Result := NIL; CreateEditor('EditorElegirFacturasCliente', IEditorElegirFacturasCliente, AEditor); if Assigned(AEditor) then try AEditor.Controller := Self; AEditor.Facturas := AFacturas; AEditor.MultiSelect := AMultiSelect; AEditor.Mensaje := AMensaje; if IsPositiveResult(AEditor.ShowModal) then Result := AEditor.FacturasClienteSeleccionados; finally AEditor.Release; AEditor := NIL; end; end; function TFacturasClienteController.Eliminar(AFactura: IBizFacturaCliente; AllItems: Boolean = false; AListaCausas: TStringList = nil): Boolean; //Solo devolverá true en el caso de eliminar todos y cada uno de los elementos a eliminar var bEliminado: Boolean; ACausa: Variant; begin bEliminado := True; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada'); ShowHourglassCursor; try if not AFactura.DataTable.Active then AFactura.DataTable.Active := True; if (AFactura.State in dsEditModes) then AFactura.Cancel; //Siempre eliminaremos el seleccionado tanto si es una lista de facturas a eliminar como si es la propia lista del editor. if not EsEliminable(AFactura, ACausa) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%d=%s', [AFactura.ID, ACausa])); bEliminado := False; end else AFactura.Delete; //En el caso de querer eliminar todos los items del objeto AAlbaran if AllItems then begin with AFactura.DataTable do begin First; while not EOF do begin if not EsEliminable(AFactura, ACausa) then begin if Assigned(AListaCausas) then AListaCausas.Add(Format('%d=%s', [AFactura.ID, ACausa])); bEliminado := False; Next; end else AFactura.Delete; end; end; end; try AFactura.DataTable.ApplyUpdates; Result := bEliminado; except //En el caso de una factura que tiene recibos con devoluciones hechas no se puede borrar aunque la factura este en situacion de pendiente AFactura.DataTable.CancelUpdates; Result := False; end; finally HideHourglassCursor; end; end; procedure TFacturasClienteController.EnviarFacturaPorEMail(AFactura: IBizFacturaCliente); var AReportController : IFacturasClienteReportController; AFicheroTMP : TFileName; ATituloEnvio : String; AEMail : String; AListaEmail : TStringList; begin if not Assigned(AFactura) then raise Exception.Create ('Factura no asignado (EnviarFacturaPorEMail)'); if AFactura.DataTable.Active then AFactura.DataTable.Active := True; AListaEmail := TStringList.Create; try RecuperarCliente(AFactura); AFactura.Cliente.DataTable.Active := True; if not AFactura.Cliente.EMAIL_1IsNull then AListaEmail.Add(AFactura.Cliente.EMAIL_1); if not AFactura.Cliente.EMAIL_2IsNull then AListaEmail.Add(AFactura.Cliente.EMAIL_2); if not ElegirEMail(AListaEmail, AEMail) then Exit; finally FreeANDNIL(AListaEmail); end; ShowHourglassCursor; ATituloEnvio := 'Factura ' + AFactura.REFERENCIA + ' - ' + AFactura.Cliente.NOMBRE; AFicheroTMP := DarFicheroPDFTemporal(EscapeIllegalChars(ATituloEnvio)); AReportController := TFacturasClienteReportController.Create; try AReportController.ExportToPDF(AFactura.ID, AFicheroTMP); SendMailMAPI(ATituloEnvio, '', AFicheroTMP, '', '', AFactura.Cliente.NOMBRE, AEMail); finally DeleteFile(AFicheroTMP); AReportController := NIL; HideHourglassCursor; end; end; function TFacturasClienteController.EsEliminable(AFactura: IBizFacturaCliente; var AComentario: Variant): Boolean; begin Result := True; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignado: EsEliminable'); if (AFactura.SITUACION <> CTE_PENDIENTE) then begin Result := False; AComentario := 'La factura esta parcial o totalmente pagada, por lo que no puede ser eliminada'; end else if (AFactura.ESTADO_EJERCICIO = CTE_CERRADO) then begin Result := False; AComentario := 'La factura tiene un asiento asociado en la parte contable cuyo ejercicio esta cerrado, por lo que no puede ser eliminada'; end else if (AFactura.ASIENTO_PUNTEADO > 0) then begin Result := False; AComentario := 'La factura tiene un asiento asociado en la parte contable y está punteado, por lo que no puede ser eliminada'; end; end; function TFacturasClienteController.EsModificable(AFactura: IBizFacturaCliente; var AComentario: Variant): Boolean; begin Result := True; AComentario := Null; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignado: EsModificable'); if (AFactura.SITUACION <> CTE_PENDIENTE) then begin if (AppFactuGES.UsuarioActivo.ID_PERFIL = CTE_PERFIL_ADMINISTRADOR) or (AppFactuGES.UsuarioActivo.ID_PERFIL = CTE_PREFIL_GERENCIA) then AComentario := 'ATENCIÓN - La factura esta parcial o totalmente pagada, los recibos se verán modificados si cambia los importes' else begin Result := False; AComentario := 'SOLO LECTURA - La factura esta parcial o totalmente pagada, por lo que no puede ser modificada'; end end else if (AFactura.ESTADO_EJERCICIO = CTE_CERRADO) then begin Result := False; AComentario := 'SOLO LECTURA - La factura tiene un asiento en caja o banco que está cerrado, por lo que no puede ser modificada'; end else if (AFactura.ASIENTO_PUNTEADO > 0) then begin Result := False; AComentario := 'SOLO LECTURA - La factura tiene un asiento en caja o banco que está cerrado, por lo que no puede ser modificada'; end; end; procedure TFacturasClienteController.RecalcularImportes( AFactura: IBizFacturaCliente); var bEnEdicion : Boolean; ADetallePosAct : Integer; begin if not Assigned(AFactura) then raise Exception.Create ('Factura no asignado (RecalcularImportes)'); if AFactura.DataTable.Active then AFactura.DataTable.Active := True; { Hay que guardar la posición en la que estamos en los detalles por que la asignación de valores a los campos IMPORTE_NETO e IMPORTE_PORTE (ver más adelante) colocan el puntero en la tabla detalle al principio. No he encontrado la razón por la que mueve el puntero. } ADetallePosAct := AFactura.Detalles.POSICION; bEnEdicion := (AFactura.DataTable.State in dsEditModes); if not bEnEdicion then AFactura.Edit; ShowHourglassCursor; try AFactura.IMPORTE_NETO := FDetallesController.DarTotalImporteTotal(AFactura.Detalles); AFactura.IMPORTE_PORTE := FDetallesController.DarTotalPorteTotal(AFactura.Detalles); if not bEnEdicion then AFactura.Post; finally HideHourglassCursor; // Restaurar la posición que teníamos en los detalles. FDetallesController.LocalizarPosicion(AFactura.Detalles, ADetallePosAct); 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 RecalcularImportes(AFactura); 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; function TFacturasClienteController.ExtraerSeleccionados(AFacturasCliente: IBizFacturaCliente): IBizFacturaCliente; var ASeleccionados : IBizFacturaCliente; begin ASeleccionados := (Self.Buscar(ID_NULO) as IBizFacturaCliente); CopyDataTableDA5(AFacturasCliente.DataTable, ASeleccionados.DataTable, True); Result := ASeleccionados; end; procedure TFacturasClienteController.FiltrarAno(AFactura: IBizFacturaCliente; ADynWhereDataTable: WideString; const Ano: String); var Condicion: TDAWhereExpression; FechaIni: String; FechaFin: String; begin AFactura.DataTable.DynamicWhere.Clear; AFactura.DataTable.DynamicWhere.Xml := ADynWhereDataTable; if (Ano <> 'Todos') then begin // Filtrar las facturas actuales por empresa FechaIni := '01/01/' + Ano; FechaFin := '31/12/' + Ano; with AFactura.DataTable.DynamicWhere do begin // (FECHA_INICIO between FECHA_FIN) Condicion := NewBinaryExpression(NewField('', fld_FacturasClienteFECHA_FACTURA), NewConstant(FechaIni, datString), dboGreaterOrEqual); Condicion := NewBinaryExpression(NewBinaryExpression(NewField('', fld_FacturasClienteFECHA_FACTURA), NewConstant(FechaFin, datString), dboLessOrEqual), Condicion, dboAnd); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Condicion, Expression, dboAnd); end; end; end; procedure TFacturasClienteController.FiltrarEmpresa(AFactura: IBizFacturaCliente); var Condicion: TDAWhereExpression; begin if AFactura.DataTable.Active then AFactura.DataTable.Active := False; // Filtrar las facturas actuales por empresa with AFactura.DataTable.DynamicWhere do begin // (ID_EMPRESA >= ID) Condicion := NewBinaryExpression(NewField('', fld_FacturasClienteID_EMPRESA), NewConstant(AppFactuGES.EmpresaActiva.ID, datInteger), dboEqual); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; end; procedure TFacturasClienteController.SetClienteController(const Value: IClientesController); begin FClienteController := Value; end; procedure TFacturasClienteController.SetDetallesController(const Value: IDetallesFacturaClienteController); begin FDetallesController := Value; end; procedure TFacturasClienteController.SetIgnorarContabilidad(AFactura: IBizFacturaCliente; const Ignorar: Integer); var EnEdicion: Boolean; begin if Assigned(AFactura) then begin EnEdicion := AFactura.DataTable.Editing; if not AFactura.DataTable.Editing then AFactura.DataTable.Edit; AFactura.IGNORAR_CONTABILIDAD := Ignorar; AFactura.DataTable.Post; if EnEdicion then AFactura.DataTable.Edit; end; end; function TFacturasClienteController.Guardar(AFactura: IBizFacturaCliente): Boolean; var IDNuevo : Integer; EsModificacion: Boolean; HayCambio: TDADeltaChange; regenerarRecibos : Boolean; begin Result := False; regenerarRecibos := True; if not Assigned(AFactura) then raise Exception.Create ('Factura no asignada'); if not Assigned(FDetallesController) then raise Exception.Create ('Controller detalles no asignado'); EsModificacion := (AFactura.ID > 0); if ValidarFactura(AFactura) then begin ShowHourglassCursor; // Asegurarnos de que todos los importes están bien. RecalcularImportes(AFactura); if EsModificacion then begin //Al modificar la factura, si no cambia el importe no tocamos sus recibos. regenerarRecibos := False; //Comprobamos si ha cambiado el importe total, para avisar de la regeneración de los recibos HayCambio := AFactura.DataTable.Delta.FindChange(AFactura.RecNo); if (Assigned(HayCambio)) and (HayCambio.OldValueByName[fld_FacturasClienteIMPORTE_TOTAL] <> 0) then begin // showmessage(currtostr(RoundCurrency(AFactura.DataTable.FieldByName('IMPORTE_TOTAL').asVariant))); // showmessage(currtostr(RoundCurrency(HayCambio.OldValueByName[fld_FacturasClienteIMPORTE_TOTAL]))); if (RoundCurrency(AFactura.DataTable.FieldByName('IMPORTE_TOTAL').asVariant) <> RoundCurrency(HayCambio.OldValueByName[fld_FacturasClienteIMPORTE_TOTAL])) then if (Application.MessageBox('Ha cambiado el importe total, por lo que se regenerarán los recibos según forma de pago asignada, ¿desea continuar?', 'Atención', MB_YESNO) = IDYES) then regenerarRecibos := True else exit; end; end; try AFactura.DataTable.ApplyUpdates; //Se generan los recibos automáticamente a partir de la forma de pago if regenerarRecibos then GenerarRecibos(AFactura); Result := True; finally HideHourglassCursor; end; end; end; function TFacturasClienteController.Nuevo (WithInsert: Boolean = True): IBizFacturaCliente; var AFactura : IBizFacturaCliente; begin AFactura := FDataModule.NewItem; FiltrarEmpresa(AFactura); AFactura.DataTable.Active := True; if WithInsert then AFactura.Insert; Result := AFactura; end; procedure TFacturasClienteController.Preview(AFactura: IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); var AReportController : IFacturasClienteReportController; ID_Facturas: TIntegerList; begin AReportController := TFacturasClienteReportController.Create; ID_Facturas := TIntegerList.Create; try //Si deseamos previsualizar todos los items del objeto albaran if AllItems then begin with AFactura.DataTable do begin First; while not EOF do begin ID_Facturas.Add(AFactura.ID); Next; end; end; end //Solo previsualizamos el item seleccionado else ID_Facturas.Add(AFactura.ID); AReportController.Preview(ID_Facturas, AVerDatosCliente, AEmpresaAcota); finally AReportController := NIL; FreeANDNIL(ID_Facturas); end; end; procedure TFacturasClienteController.Print(AFactura: IBizFacturaCliente; AllItems: Boolean = false; AVerDatosCliente: Boolean = true; AEmpresaAcota: Boolean = true); var AReportController : IFacturasClienteReportController; ID_Facturas: TIntegerList; begin AReportController := TFacturasClienteReportController.Create; ID_Facturas := TIntegerList.Create; try //Si deseamos previsualizar todos los items del objeto albaran if AllItems then begin with AFactura.DataTable do begin First; while not EOF do begin ID_Facturas.Add(AFactura.ID); Next; end; end; end //Solo previsualizamos el item seleccionado else ID_Facturas.Add(AFactura.ID); AReportController.Print(ID_Facturas, AVerDatosCliente, AEmpresaAcota); finally AReportController := NIL; FreeANDNIL(ID_Facturas); end; end; function TFacturasClienteController.GenerarAbono(AFactura: IBizFacturaCliente): IBizFacturaCliente; begin ShowHourglassCursor; try Result := Duplicar(AFactura); //Añade un concepto con los datos de la factura asociada al abono FDetallesController.AnadirDetalleFacturaAsociadaAbono(Result.Detalles, AFactura.REFERENCIA, DateToStr(AFactura.FECHA_FACTURA)); //Convierte todos los articulos de la factura a negativos por se un abono FDetallesController.CambiarSignoDetalles(Result.Detalles); // Hay que dejar algunos campos como si fuera una factura nueva Result.Edit; Result.TIPO := CTE_TIPO_ABONO; Result.Post; finally HideHourglassCursor; end; end; procedure TFacturasClienteController.GenerarRecibos(AFactura: IBizFacturaCliente); var AFormasPagoController : IFormasPagoController; AFormaPago: IBizFormaPago; ARecibosClienteController: IRecibosClienteController; ARecibos: IBizRecibosCliente; ARecibosACuenta: IBizRecibosCliente; AFechaVencimiento: TDateTime; ADiaVencimiento: Integer; ADiasMas: Integer; BSemaforo: Boolean; AImporteTotal: Currency; i: Integer; begin if not Assigned(AFactura) then Exit; i := 1; AImporteTotal := AFactura.IMPORTE_TOTAL; AFormasPagoController := TFormasPagoController.Create; AFormaPago := AFormasPagoController.Buscar(AFactura.ID_FORMA_PAGO); AFormaPago.DataTable.Active := True; if AFormaPago.DataTable.RecordCount <> 1 then Exit; //No hay forma de pago en la factura y por lo tanto no se generan recibos //raise Exception.Create('No existe la forma de pago de la factura'); ARecibosClienteController := TRecibosClienteController.Create; //Eliminamos todos los recibos que tuviera la factura porque sabemos que todos //estarán pendientes (solo permitiremos modificar y eliminar facturas pendientes, //parcialmente pagadas o pagadas no ARecibos := ARecibosClienteController.BuscarRecibosFactura(AFactura.ID); ARecibosClienteController.EliminarTodo(ARecibos); //Miramos si existen recibos a cuenta para ese contrato, si es así los tendremos en cuenta para las facturas ARecibosACuenta := ARecibosClienteController.BuscarRecibosContrato(AFactura.ID_CONTRATO); ARecibosACuenta.DataTable.Active := True; if ARecibosACuenta.DataTable.RecordCount > 0 then begin if (ShowConfirmMessage('Generar factura a cliente', 'Hay un/os recibo/s a cuenta creados para el presupuesto asociado a la factura, desea incluirlo/s') = IDYES) then begin ARecibosACuenta.DataTable.First; while not ARecibosACuenta.DataTable.EOF do begin AImporteTotal := AImporteTotal - ARecibosAcuenta.IMPORTE_TOTAL; ARecibosACuenta.DataTable.Edit; ARecibosAcuenta.REFERENCIA := AFactura.REFERENCIA + ' - ' + IntToStr(i); ARecibosAcuenta.ID_FACTURA := AFactura.ID; ARecibosAcuenta.ID_EMPRESA := AFactura.ID_EMPRESA; ARecibosAcuenta.ID_CLIENTE := AFactura.ID_CLIENTE; ARecibosAcuenta.ID_TIENDA := AFactura.ID_TIENDA; ARecibosAcuenta.NOMBRE := AFactura.NOMBRE; //Es la razon social de la factura ARecibosAcuenta.CALLE := AFactura.CALLE; ARecibosAcuenta.NIF_CIF := AFactura.NIF_CIF; ARecibosAcuenta.POBLACION := AFactura.POBLACION; ARecibosAcuenta.PROVINCIA := AFactura.PROVINCIA; ARecibosAcuenta.CODIGO_POSTAL := AFactura.CODIGO_POSTAL; ARecibosACuenta.DataTable.Post; Inc(i); ARecibosACuenta.DataTable.Next; end; ARecibosACuenta.DataTable.ApplyUpdates; end; end; //Se cambia la lógica a peticion de acana, en el caso de meter una fecha de vencimiento, //los plazos de la forma de pago no tendrán efecto, se generará un recibo con el 100% y fecha de vencimiento //de la factura. (Se permitirá para acana poder dar de alta facturas sin recibos asociados) With AFormaPago.Plazos.DataTable do begin First; repeat //SOLO PARA ACANA (Leer comentario anterior if (AFormaPago.Plazos.RecordCount > 0) or (AFactura.FECHA_VENCIMIENTO <> 0) then begin ARecibos := ARecibosClienteController.Nuevo; ARecibos.Edit; ARecibos.ID_FACTURA := AFactura.ID; ARecibos.ID_EMPRESA := AFactura.ID_EMPRESA; ARecibos.ID_CLIENTE := AFactura.ID_CLIENTE; ARecibos.ID_TIENDA := AFactura.ID_TIENDA; ARecibos.REFERENCIA := AFactura.REFERENCIA + ' - ' + IntToStr(i); ARecibos.NOMBRE := AFactura.NOMBRE; //Es la razon social de la factura ARecibos.CALLE := AFactura.CALLE; ARecibos.NIF_CIF := AFactura.NIF_CIF; ARecibos.POBLACION := AFactura.POBLACION; ARecibos.PROVINCIA := AFactura.PROVINCIA; ARecibos.CODIGO_POSTAL := AFactura.CODIGO_POSTAL; ARecibos.FECHA_EMISION := AFactura.FECHA_FACTURA; if AFormaPago.Plazos.RecordCount < 1 then begin ARecibos.FECHA_VENCIMIENTO := AFactura.FECHA_VENCIMIENTO; ARecibos.IMPORTE := AImporteTotal; end else begin AFechaVencimiento := AFactura.FECHA_FACTURA + AFormaPago.Plazos.NUM_DIAS; ADiasMas := 0; BSemaforo := False; if (AFactura.Cliente.VENCIMIENTO_FACTURAS_1 <> 0) or (AFactura.Cliente.VENCIMIENTO_FACTURAS_2 <> 0) or (AFactura.Cliente.VENCIMIENTO_FACTURAS_3 <> 0) then begin ADiaVencimiento := DayOf(AFechaVencimiento); while (ADiaVencimiento <> AFactura.Cliente.VENCIMIENTO_FACTURAS_1) and (ADiaVencimiento <> AFactura.Cliente.VENCIMIENTO_FACTURAS_2) and (ADiaVencimiento <> AFactura.Cliente.VENCIMIENTO_FACTURAS_3) do begin if ADiaVencimiento = DaysInMonth(AFechaVencimiento) then begin ADiaVencimiento := 1; //Controlamos con una variable semaforo que no nos quedemos en un bucle infinito //intentando encontrar el dia de pago establecido para el cliente (es el caso de tener //asignado el dia de pago 30 y llegar febrero en el que no se encuentra dicho dia, o //tener como dia de pago los días 31 y en el caso de meses de 30 dias no encontrarlo. if BSemaforo then begin ADiasMas := 0; Break end else BSemaforo := True; end else Inc(ADiaVencimiento); Inc(ADiasMas); end; end; AFechaVencimiento := IncDay(AFechaVencimiento, ADiasMas); ARecibos.FECHA_VENCIMIENTO := AFechaVencimiento; ARecibos.IMPORTE := AImporteTotal * (AFormaPago.Plazos.PORCENTAJE / 100); end; ARecibos.DESCRIPCION := 'RECIBO ' + ARecibos.REFERENCIA + ' - ' + CifraToLetras(ARecibos.IMPORTE); ARecibosClienteController.Guardar(ARecibos); end; Inc(i); Next; until (eof); end; //Liberamos AFormasPagoController := Nil; AFormaPago := Nil; ARecibosClienteController := Nil; ARecibos := Nil; ARecibosACuenta := Nil; end; function TFacturasClienteController.GetClienteController: IClientesController; begin Result := FClienteController; end; function TFacturasClienteController.GetDetallesController: IDetallesFacturaClienteController; begin Result := FDetallesController; end; end.