unit uGenerarFacturasProvAlbProvUtils;

interface

uses
 Windows, SysUtils, Classes,
 uBizAlbaranesProveedor,
 uBizFacturasProveedor,
 pngimage, JSDialog;

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

  function GenerarFacturaProvAlbProv(const IDAlbaran : Integer; const CopiarDetalles: Boolean = True) : Boolean; overload;
  function GenerarFacturaProvAlbProv(AAlbaran : IBizAlbaranProveedor; const CopiarDetalles: Boolean = True) : Boolean; overload;
  function GenerarFacturaProvAlbProv : Boolean; overload;

  function GenerarFacturas(AFacturas : IBizFacturaProveedor; AListaAlbaranes : IBizAlbaranProveedor; const CopiarDetalles: Boolean = True): Boolean;

implementation

{$R *.dfm}

uses
  DB, uDialogUtils, uDADataTable,
  schFacturasProveedorClient_Intf,
  schAlbaranesProveedorClient_Intf,

  uBizDetallesFacturaProveedor,
  uAlbaranesProveedorController, uDetallesFacturaProveedorController,

  uBizDetallesAlbaranProveedor, uControllerDetallesBase,

  uFacturasProveedorController;

//  ,
//  , uProveedoresController,
//  , uControllerDetallesBase,


//  , uBizContactos, schPedidosClienteClient_Intf,
//  schAlbaranesProveedorClient_Intf;

var
  dmGenerarFacturasProv: TdmGenerarFacturasProvAlbProv;
  AFacturasProveedorController : IFacturasProveedorController;
  AAlbaranesProveedorController : IAlbaranesProveedorController;
  ADetallesAlbaranesProveedorController : IDetallesFacturaProveedorController;
//  AProveedoresController : IProveedoresController;

{ Métodos auxiliares }
{procedure CopiarPedidoAAlbaran(AAlbaran: IBizPedidoCliente;
  AAlbaran : IBizAlbaranProveedor);
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (CopiarPedidoAAlbaran)');

  if not Assigned(AAlbaran) then
    raise Exception.Create ('Pedido no asignado (CopiarPedidoAAlbaran)');

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

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

procedure CopiarArticulosPendAAlbaran(
  AAlbaran: IBizPedidoCliente;
  AAlbaran: IBizAlbaranProveedor;
  AArticulosPendientes: IBizPedidoClienteArticulosPend);
var
  i : integer;
  ADetalles : IBizDetallesFacturaProveedor;
  ADetallesController : IDetallesFacturaProveedorController;
begin
  if not Assigned(AAlbaran) then
    raise Exception.Create ('Albarán no asignado (CopiarArticulosPendAAlbaran)');

  if not Assigned(AAlbaran) then
    raise Exception.Create ('Pedido no asignado (CopiarArticulosPendAAlbaran)');

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

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

  // El albarán tiene que venir ya abierto y posicionado donde hay que copiar

  ADetalles := AAlbaran.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
      AAlbaran.Detalles.First;
      if (AAlbaran.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 := AAlbaran.Detalles.REFERENCIA;
          ADetalles.ID_ARTICULO := AAlbaran.Detalles.ID_ARTICULO;
          ADetalles.CONCEPTO := AAlbaran.Detalles.CONCEPTO;
          ADetalles.CANTIDAD := AArticulosPendientes.CANTIDAD_PENDIENTE;
          ADetalles.IMPORTE_UNIDAD := AAlbaran.Detalles.IMPORTE_UNIDAD;
          ADetalles.IMPORTE_TOTAL := AAlbaran.Detalles.IMPORTE_TOTAL;
          ADetalles.DESCUENTO := AAlbaran.Detalles.DESCUENTO;
          ADetalles.IMPORTE_PORTE := AAlbaran.Detalles.IMPORTE_PORTE;
          ADetalles.VISIBLE := AAlbaran.Detalles.VISIBLE;
          ADetalles.REFERENCIA_PROVEEDOR := AAlbaran.Detalles.REFERENCIA_PROVEEDOR;
          ADetalles.Post;
          AArticulosPendientes.Next;
        end;
    end;
  finally
      ADetallesController.EndUpdate(ADetalles);
    ADetallesController := NIL;
  end;
end;  }

procedure CopiarArticulosAlbaran(AOrigen: IBizDetallesAlbaranProveedor;
  ADestino : IBizDetallesFacturaProveedor);
var
  i : integer;
  ADetallesController : IDetallesFacturaProveedorController;
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 := 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(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
  dmGenerarFacturasProv := TdmGenerarFacturasProvAlbProv.Create(nil);
  AFacturasProveedorController := TFacturasProveedorController.Create;
  AAlbaranesProveedorController := TAlbaranesProveedorController.Create;
  ADetallesAlbaranesProveedorController := TDetallesFacturaProveedorController.Create;
//  AProveedoresController := TProveedoresController.Create;
end;

procedure Finalizar;
begin
  FreeAndNIL(dmGenerarFacturasProv);
  AFacturasProveedorController := nil;  
  AAlbaranesProveedorController := nil;
  ADetallesAlbaranesProveedorController := nil;
//  AProveedoresController := nil;
end;


function GenerarFacturaProvAlbProv(const IDAlbaran : Integer; const CopiarDetalles: Boolean = True) : Boolean; overload;
var
  AAlbaran : IBizAlbaranProveedor;
begin
  Result := False;
  try
    if not Assigned(AAlbaranesProveedorController) then
       Inicializar;

    AAlbaran := AAlbaranesProveedorController.Buscar(IDAlbaran);
    if Assigned(AAlbaran) then
    begin
      if (ShowConfirmMessage('Generar factura', Format('¿Desea copiar todos los conceptos del albaran seleccionados a su factura correspondiente?', [])) = IDYES) then
        Result := GenerarFacturaProvAlbProv(AAlbaran)
      else
        Result := GenerarFacturaProvAlbProv(AAlbaran, False);
    end;

    if Assigned(AAlbaranesProveedorController) then
       Finalizar;

  finally
    AAlbaran := NIL;
  end;
end;

function GenerarFacturaProvAlbProv(AAlbaran : IBizAlbaranProveedor; const CopiarDetalles: Boolean = True) : Boolean; overload;
var
  AFacturasNuevas : IBizFacturaProveedor;
  i: Integer;
begin
  Result := False;

  if not Assigned(AAlbaran) then
     raise Exception.Create('Albarán de proveedor no asignado (GenerarFacturaProvAlbProv)');

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

  if not Assigned(AAlbaranesProveedorController) then
     Inicializar;

  try
     AFacturasNuevas := AFacturasProveedorController.Nuevo(False);
     if GenerarFacturas(AFacturasNuevas, AAlbaran, 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 dmGenerarFacturasProv.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;
          dmGenerarFacturasProv.JsListaFacturasGeneradas.Execute;
        end;
      end;

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

function GenerarFacturaProvAlbProv : Boolean; overload;
var
  AAlbaranes : IBizAlbaranProveedor;
begin
  Result := False;
  try
    if not Assigned(AAlbaranesProveedorController) then
       Inicializar;

    AAlbaranes := AAlbaranesProveedorController.ElegirAlbaranes(AAlbaranesProveedorController.BuscarSinFacturar,
                  'Elija el albarán o albaranes de proveedor que desea utilizar para dar de alta la factura.'
                  + #10#13 + 'Si elige albaranes de proveedores diferentes se dará de alta una factura por cada uno de ellos.' , True);

    if Assigned(AAlbaranes) then
    begin
      if (ShowConfirmMessage('Generar factura', Format('¿Desea copiar todos los conceptos del/los albaran/es seleccionados a sus facturas correspondientes?', [])) = IDYES) then
        Result := GenerarFacturaProvAlbProv(AAlbaranes)
      else
        Result := GenerarFacturaProvAlbProv(AAlbaranes, False);
    end;

    if Assigned(AAlbaranesProveedorController) then
       Finalizar;
  finally
    AAlbaranes := Nil;
  end;
end;


function GenerarFacturas(AFacturas: IBizFacturaProveedor; AListaAlbaranes: IBizAlbaranProveedor; const CopiarDetalles: Boolean = True): Boolean;
var
  AFacturaActual : IBizFacturaProveedor;
  AAlbaranesController : IAlbaranesProveedorController;
  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 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;

  AAlbaranesController := TAlbaranesProveedorController.Create;
  try
    // Ordenar por fecha de albaran
    AListaAlbaranes.DataTable.Sort([fld_AlbaranesProveedorFECHA_ALBARAN], [uDADataTable.sdAscending]);
    AListaAlbaranes.First;
    for I := 0 to AListaAlbaranes.DataTable.RecordCount - 1 do
    begin
      AListaAlbaranes._Proveedor := NIL;
      AFacturaActual := NIL;

      // Busco si hay alguna factura ya hecha de ese Proveedor
      AFacturas.DataTable.First;
      if AFacturas.DataTable.Locate(fld_FacturasProveedorID_PROVEEDOR, AListaAlbaranes.ID_PROVEEDOR, []) then
      begin
        AFacturaActual := AFacturas;
        AFacturasProveedorController.RecuperarProveedor(AFacturaActual);
        AFacturaActual.Proveedor.DataTable.Active := True;
      end
      else begin
        // No hay factura de ese Proveedor. Creo una nueva
        AFacturasProveedorController.Anadir(AFacturas);
        AAlbaranesController.RecuperarProveedor(AListaAlbaranes);
        AListaAlbaranes.Proveedor.DataTable.Active := True;
        AFacturas.Proveedor := AListaAlbaranes.Proveedor;

        //Siempre asignaremos por defecto la referencia de factura de proveedor que tenga el primer albaran del proveedor
//      bEnEdicion := (AFacturas.DataTable.State in dsEditModes);
//      if not bEnEdicion then
//         AFacturas.Edit;
//      AFacturas.REFERENCIA_PROVEEDOR := AListaAlbaranes.REF_FACTURA_PROV;
//      AFacturas.Post;
//      if bEnEdicion then
//         AFacturas.Edit;

        //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;

      //Se pone la referencia del pedido y el total y nos olvidamos de los detalles
      if not CopiarDetalles then
      begin
        AFacturasProveedorController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_CONCEPTO);
        with AFacturaActual.Detalles do
        begin
          Edit;
          CONCEPTO := 'Albaran ' + AListaAlbaranes.REFERENCIA;
          CANTIDAD := 1;
          IMPORTE_UNIDAD := AListaAlbaranes.IMPORTE_TOTAL;    //noviseda no utiliza el IVA en los pedidos por lo tanto el
                                                                //importe total es lo que queremos (IMPORTE_NETO+IMPORTE_PORTE)
          Post;
        end;
      end
      //Se copian los detalles del pedido a la factura
      else
      begin
        // Añado el título
        AFacturasProveedorController.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);

  {En los albaranes a proveedor el porte es a nivel del artículo
        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
        AFacturasProveedorController.DetallesController.Add(AFacturaActual.Detalles, TIPO_DETALLE_SUBTOTAL);
        with AFacturaActual.Detalles do
        begin
          Edit;
          CONCEPTO := 'Total del albarán ' + AListaAlbaranes.REFERENCIA;
          Post;
        end;
      end;

      // Añado una línea en blanco
      AFacturasProveedorController.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;
      AFacturasProveedorController.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;
  end;
end;


end.
