unit uGenerarFacturasCliPreCliUtils;

interface

uses
 Windows, SysUtils, Classes, uBizFacturasCliente, uBizPresupuestosCliente, pngimage, JSDialog;

type
  TdmGenerarFacturasCliPre = class(TDataModule)
    JsListaFacturasGeneradas: TJSDialog;
  end;

  function GenerarFacturaCliPre(const IDPresupuesto : Integer; const CopiarDetalles: Boolean = True) : Boolean; overload;
  function GenerarFacturaCliPre(APresupuestos : IBizPresupuestoCliente; const CopiarDetalles: Boolean = True) : Boolean; overload;
  function GenerarFacturaCliPre : Boolean; overload;

  function GenerarFacturas(AFacturas : IBizFacturaCliente; AListaPresupuestos : IBizPresupuestoCliente; const CopiarDetalles: Boolean = True): Boolean;

implementation

{$R *.dfm}

uses
  uDialogUtils, uDADataTable, uControllerDetallesBase,
  uBizDetallesPresupuestoCliente, schPresupuestosClienteClient_Intf,
  schFacturasClienteClient_Intf, uDetallesFacturaClienteController,
  uPresupuestosClienteController, uDetallesPresupuestoClienteController,
  uFacturasClienteController, uBizDetallesFacturaCliente;

var
  dmGenerarFacturasCliPre: TdmGenerarFacturasCliPre;
  APresupuestosClienteController : IPresupuestosClienteController;
  ADetallesPresupuestosClienteController : IDetallesPresupuestoClienteController;
  AFacturasClienteController : IFacturasClienteController;
//  AProveedoresController : IProveedoresController;

{ Métodos auxiliares }
{procedure CopiarPedidoAPedido(APedido: IBizPedidoCliente;
  APedido : IBizFacturaProveedor);
begin
  if not Assigned(APedido) then
    raise Exception.Create ('Presupuesto no asignado (CopiarPedidoAPedido)');

  if not Assigned(APedido) then
    raise Exception.Create ('Pedido no asignado (CopiarPedidoAPedido)');

  if not APedido.DataTable.Active then
    APedido.DataTable.Active := True;

  // El presupuesto tiene que venir ya abierto y posicionado donde hay que copiar
  APedido.ID_CLIENTE := APedido.ID_CLIENTE;
  APedido.ID_PEDIDO := APedido.ID;
  APedido.CALLE := APedido.CALLE;
  APedido.CODIGO_POSTAL := APedido.CODIGO_POSTAL;
  APedido.POBLACION := APedido.POBLACION;
  APedido.PROVINCIA := APedido.PROVINCIA;
  APedido.PERSONA_CONTACTO := APedido.PERSONA_CONTACTO;
  APedido.TELEFONO := APedido.TELEFONO;
  APedido.IMPORTE_NETO := APedido.IMPORTE_NETO;
  APedido.IMPORTE_PORTE := APedido.IMPORTE_PORTE;
  APedido.DESCUENTO := APedido.DESCUENTO;
  APedido.IMPORTE_DESCUENTO := APedido.IMPORTE_DESCUENTO;
  APedido.BASE_IMPONIBLE := APedido.BASE_IMPONIBLE;
  APedido.IVA := APedido.IVA;
  APedido.IMPORTE_IVA := APedido.IMPORTE_IVA;
  APedido.IMPORTE_TOTAL := APedido.IMPORTE_TOTAL;
  APedido.DataTable.FieldByName(fld_PedidosClienteOBSERVACIONES).AsVariant := APedido.DataTable.FieldByName(fld_PedidosClienteOBSERVACIONES).AsVariant;
  APedido.ID_FORMA_PAGO := APedido.ID_FORMA_PAGO;
end;

procedure CopiarArticulosPendAPedido(
  APedido: IBizPedidoCliente;
  APedido: IBizFacturaProveedor;
  AArticulosPendientes: IBizPedidoClienteArticulosPend);
var
  i : integer;
  ADetalles : IBizDetallesFacturaProveedor;
  ADetallesController : IDetallesFacturaProveedorController;
begin
  if not Assigned(APedido) then
    raise Exception.Create ('Presupuesto no asignado (CopiarArticulosPendAPedido)');

  if not Assigned(APedido) then
    raise Exception.Create ('Pedido no asignado (CopiarArticulosPendAPedido)');

  if not Assigned(AArticulosPendientes) then
    raise Exception.Create ('Artículos pendientes no asignado (CopiarArticulosPendAPedido)');

  if not AArticulosPendientes.DataTable.Active then
    AArticulosPendientes.DataTable.Active := True;

  // El presupuesto tiene que venir ya abierto y posicionado donde hay que copiar

  ADetalles := APedido.Detalles;
  ADetallesController := TDetallesFacturaProveedorController.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(ADetalles);

    AArticulosPendientes.DataTable.First;
    for i := 0 to AArticulosPendientes.DataTable.RecordCount - 1 do
    begin
      APedido.Detalles.First;
      if (APedido.Detalles.Locate('ID_ARTICULO', AArticulosPendientes.ID_ARTICULO, [])) then
        if (AArticulosPendientes.CANTIDAD_PENDIENTE > 0) then
        begin
          ADetallesController.Add(ADetalles, TIPO_DETALLE_CONCEPTO);
          ADetalles.Edit;
          ADetalles.REFERENCIA := APedido.Detalles.REFERENCIA;
          ADetalles.ID_ARTICULO := APedido.Detalles.ID_ARTICULO;
          ADetalles.CONCEPTO := APedido.Detalles.CONCEPTO;
          ADetalles.CANTIDAD := AArticulosPendientes.CANTIDAD_PENDIENTE;
          ADetalles.IMPORTE_UNIDAD := APedido.Detalles.IMPORTE_UNIDAD;
          ADetalles.IMPORTE_TOTAL := APedido.Detalles.IMPORTE_TOTAL;
          ADetalles.DESCUENTO := APedido.Detalles.DESCUENTO;
          ADetalles.IMPORTE_PORTE := APedido.Detalles.IMPORTE_PORTE;
          ADetalles.VISIBLE := APedido.Detalles.VISIBLE;
          ADetalles.REFERENCIA_PROVEEDOR := APedido.Detalles.REFERENCIA_PROVEEDOR;
          ADetalles.Post;
          AArticulosPendientes.Next;
        end;
    end;
  finally
    ADetallesController.EndUpdate(ADetalles);
    ADetallesController := NIL;
  end;
end;  }

procedure CopiarArticulosPresupuesto(AOrigen: IBizDetallesPresupuestoCliente;
  ADestino : IBizDetallesFacturaCliente);
var
  i : integer;
  ADetallesController : IDetallesFacturaClienteController;
begin
  if not Assigned(AOrigen) then
    raise Exception.Create ('Origen no asignado (CopiarArticulosPresupuesto)');

  if not Assigned(ADestino) then
    raise Exception.Create ('Destino no asignado (CopiarArticulosPresupuesto)');

  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;


procedure Inicializar;
begin
  dmGenerarFacturasCliPre := TdmGenerarFacturasCliPre.Create(nil);
  APresupuestosClienteController := TPresupuestosClienteController.Create;
  ADetallesPresupuestosClienteController := TDetallesPresupuestoClienteController.Create;
  AFacturasClienteController := TFacturasClienteController.Create;
end;

procedure Finalizar;
begin
  FreeAndNIL(dmGenerarFacturasCliPre);
  APresupuestosClienteController := nil;
  ADetallesPresupuestosClienteController := nil;
  AFacturasClienteController := nil;
end;


function GenerarFacturaCliPre(const IDPresupuesto : Integer; const CopiarDetalles: Boolean = True) : Boolean; overload;
var
  APresupuesto : IBizPresupuestoCliente;
begin
  Result := False;
  if not Assigned(APresupuestosClienteController) then
     Inicializar;

  APresupuesto := APresupuestosClienteController.Buscar(IDPresupuesto);
  Result := GenerarFacturaCliPre(APresupuesto, CopiarDetalles);

  if Assigned(APresupuestosClienteController) then
     Finalizar;
end;

function GenerarFacturaCliPre(APresupuestos : IBizPresupuestoCliente; const CopiarDetalles: Boolean = True) : Boolean; overload;
var
  ARespuesta : Integer;
  AFacturasNuevas : IBizFacturaCliente;
  i: Integer;
begin
  Result := False;

  if not Assigned(APresupuestos) then
     raise Exception.Create('Presupuesto de cliente no asignado (GenerarFacturaCliPre)');

  if not APresupuestos.DataTable.Active then
     APresupuestos.DataTable.Active := True;

  if not Assigned(APresupuestosClienteController) then
     Inicializar;

  try
    AFacturasNuevas := AFacturasClienteController.Nuevo(False);
    if GenerarFacturas(AFacturasNuevas, APresupuestos, CopiarDetalles) then
    begin
      if AFacturasNuevas.DataTable.RecordCount = 1 then
      begin
          if AFacturasNuevas.TIPO = CTE_TIPO_FACTURA then
             ShowInfoMessage('La factura se ha dado de alta con el código ' + AFacturasNuevas.REFERENCIA)
          else
             ShowInfoMessage('El abono se ha dado de alta con el código ' + AFacturasNuevas.REFERENCIA);
      end
      else begin
         with dmGenerarFacturasCliPre.JsListaFacturasGeneradas.Content do
         begin
           Clear;
           AFacturasNuevas.DataTable.Last;
           for i := 0 to AFacturasNuevas.DataTable.RecordCount - 1 do
           begin
             if Length(AFacturasNuevas.REFERENCIA) > 0 then
                Add(AFacturasNuevas.REFERENCIA + ': ' + AFacturasNuevas.NOMBRE);
             AFacturasNuevas.DataTable.Prior;
           end;
         end;
         dmGenerarFacturasCliPre.JsListaFacturasGeneradas.Execute;
      end;

      Result := True;
    end;

   finally
     AFacturasNuevas := NIL;
     if Assigned(APresupuestosClienteController) then
        Finalizar;
   end;
end;

function GenerarFacturaCliPre : Boolean; overload;
var
  APresupuestos : IBizPresupuestoCliente;
begin
  Result := False;
  try
    if not Assigned(APresupuestosClienteController) then
       Inicializar;

    APresupuestos := APresupuestosClienteController.ElegirPresupuestos(APresupuestosClienteController.BuscarSinFacturar,
                  'Elija el presupuesto o presupuestos de proveedor que desea utilizar para dar de alta la factura.'
                  + #10#13 + 'Si elige presupuestos de proveedores diferentes se dará de alta una factura por cada uno de ellos.' , True);

    if Assigned(APresupuestos) then
    begin
      if (ShowConfirmMessage('Generar factura', Format('¿Desea copiar todos los conceptos de/los presupuesto/s seleccionados a sus facturas correspondientes?', [])) = IDYES) then
         Result := GenerarFacturaCliPre(APresupuestos)
      else
         Result := GenerarFacturaCliPre(APresupuestos, False);
    end;

    if Assigned(APresupuestosClienteController) then
       Finalizar;
  finally
    APresupuestos := Nil;
  end;
end;

function GenerarFacturas(AFacturas : IBizFacturaCliente; AListaPresupuestos : IBizPresupuestoCliente; const CopiarDetalles: Boolean = True): Boolean;
var
  AFacturaActual : IBizFacturaCliente;
  APresupuestosController : IPresupuestosClienteController;
  I: Integer;
  bEnEdicion : Boolean;

begin
    // ATENCIÓN!!! AFacturas tiene que estar vacio para no pisar facturas
    // ya generadas.
    if not Assigned(AFacturas) then
      raise Exception.Create ('Factura no asignado (Anadir)');

    if not Assigned(AListaPresupuestos) then
      raise Exception.Create ('Presupuestos no asignados (Anadir)');

    if not AFacturas.DataTable.Active then
      AFacturas.DataTable.Active := True;

    if not AListaPresupuestos.DataTable.Active then
      AListaPresupuestos.DataTable.Active := True;

    APresupuestosController := TPresupuestosClienteController.Create;
    try
      // Ordenar por fecha de albaran
      AListaPresupuestos.DataTable.Sort([fld_PresupuestosClienteFECHA_PRESUPUESTO], [uDADataTable.sdAscending]);
      AListaPresupuestos.First;
      for I := 0 to AListaPresupuestos.DataTable.RecordCount - 1 do
      begin
        AListaPresupuestos._Cliente := NIL;
        AFacturaActual := NIL;

        // Busco si hay alguna factura ya hecha de ese Cliente
        AFacturas.DataTable.First;
        if AFacturas.DataTable.Locate(fld_FacturasClienteID_CLIENTE, AListaPresupuestos.ID_Cliente, []) then
        begin
          AFacturaActual := AFacturas;
          AFacturasClienteController.RecuperarCliente(AFacturaActual);
          AFacturaActual.Cliente.DataTable.Active := True;
        end
        else begin
          // No hay factura de ese Cliente. Creo una nueva
          AFacturasClienteController.Anadir(AFacturas);
          APresupuestosController.RecuperarCliente(AListaPresupuestos);
          AFacturas.Cliente := AListaPresupuestos.Cliente;
          AFacturas.OBSERVACIONES.Add('Importe de factura por trabajos realizados según indicaciones de nuestro presupuesto ' + AListaPresupuestos.REFERENCIA + ' con fecha ' + DateToStr(AListaPresupuestos.FECHA_PRESUPUESTO));
          AFacturaActual := AFacturas;
        end;

        // Ya tengo la factura. Le añado los conceptos del presupuesto
        AFacturaActual.Detalles.DataTable.Last;

        //Se pone la referencia del presupuesto y el total y nos olvidamos de los detalles
        if not CopiarDetalles then
        begin
          AFacturasClienteController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO);
          with AFacturaActual.Detalles do
          begin
            Edit;
            CONCEPTO := 'Presupuesto ' + AListaPresupuestos.REFERENCIA;
            CANTIDAD := 1;
            IMPORTE_UNIDAD := AListaPresupuestos.IMPORTE_TOTAL; //Tecsitel no utiliza el IVA en los presupuestos por lo tanto el
                                                                //importe total es lo que queremos (IMPORTE_NETO+IMPORTE_PORTE)
            Post;
          end;
        end
        //Se copian los detalles del presupuesto a la factura
        else
        begin
          // Añado el título
          AFacturasClienteController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_TITULO);
          with AFacturaActual.Detalles do
          begin
            Edit;
            CONCEPTO := 'Presupuesto ' + AListaPresupuestos.REFERENCIA + ' del ' + DateToStr(AListaPresupuestos.FECHA_PRESUPUESTO);
            Post;
          end;
          // Añado el contenido del presupuesto
          CopiarArticulosPresupuesto(AListaPresupuestos.Detalles, AFacturaActual.Detalles);

          //En el caso de tener porte el presupuesto se añade una fila con el importe
          if (AListaPresupuestos.IMPORTE_PORTE > 0) then
          begin
            AFacturasClienteController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO);
            with AFacturaActual.Detalles do
            begin
              Edit;
              CONCEPTO := 'Porte del presupuesto';
              CANTIDAD := 1;
              IMPORTE_UNIDAD := AListaPresupuestos.IMPORTE_PORTE;
              Post;
            end;
          end;

          //En el caso de tener descuento (bonificación) el presupuesto se añade una fila con el importe
          if (AListaPresupuestos.IMPORTE_DESCUENTO > 0) then
          begin
            AFacturasClienteController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO);
            with AFacturaActual.Detalles do
            begin
              Edit;
              CONCEPTO := 'Bonificación ' + FloatToStr(AListaPresupuestos.DESCUENTO) + '%';
              CANTIDAD := -1;
              IMPORTE_UNIDAD := AListaPresupuestos.IMPORTE_DESCUENTO;
              Post;
            end;
          end;

          // Añado el resumen
          AFacturasClienteController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_SUBTOTAL);
          with AFacturaActual.Detalles do
          begin
            Edit;
            CONCEPTO := 'Total del presupuesto ' + AListaPresupuestos.REFERENCIA;
            Post;
          end;
        end;

        // Añado una línea en blanco
        AFacturasClienteController.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;
        AFacturasClienteController.Guardar(AFacturaActual);
        // Asocio la factura con el presupuesto
        AListaPresupuestos.Edit;
        AListaPresupuestos.ID_FACTURA := AFacturaActual.ID;
        AListaPresupuestos.Post;
        APresupuestosController.Guardar(AListaPresupuestos);

        AListaPresupuestos.Next;
      end;
      Result := True;
    finally
      APresupuestosController := NIL;
    end;
end;

end.
