Como Traer los Encabezados con sus Detalles en un solo Store Procedure?
En una ocasión vi un demo parecido de mi Profe de Isil "Luis Dueñas" para mi uno de los Mejores en Net =D, donde se traía las Categorias con sus Diferentes Productos que le correspondia, pero este caso es de Factura con su Ordenes de Compra que lo vamos a llamar "Detalles" para que suene como el Titulo.
Aca hay varios Requerimientos
1.- Listar todas las Facturas y sus Detalles en un Solo Store Procedure (de un Golpe "1 conexion")2.- Visualizar con la imagen (+) y contraer con el (∇) el panel del Detalle sin Cargar la Pagina
------------ Un Agregado-----------------
3.- Seleccionar y Deseleccionar todos los CheckBox con un Check Principal en la Cabecera.
4.-
Recorrer y guardar los valores de los CheckBox en una Cadena para luego mandarlo a un Store Procedure donde busque por todos esos Valores para un Reporte, pero eso es otra Historia.
______________________________________________________________________
Veamos el Store
______________________________________________________________________CREATE PROCEDURE WS_FIND2_FCDETALLES_BY_CLIENTE
@Fec_CreaIni datetime,
@Fec_CreaFin datetime,
@COD_CLIENTE AS COD_CLIENTE,
@COD_EMPRESA AS COD_EMPRESA
as
-----------1RA CONSULTA-----------------------------------------
Select
A.Id_FacturaUSA AS ID,
A.Cod_SerFacUSA + '-'+ A.Cod_FacturaUSA AS Factura,
A.TipoFactura as Tipo,
[dbo].[UF_WS_TRAER_NOMBRECLIENTE](A.Cod_Cliente) as Cliente,
isnull(D.Direc_Linea1,'') as Direccion,
convert(varchar(10),A.Fec_Creacion,110) as Fecha, --A.Fec_Emision,
A.ImpTotalFacturado,
E.DesEstado
From TG_FacturaUSA A
LEFT JOIN TG_Cliente_Direccion D ON(A.Cod_Cliente=D.Cod_Cliente AND A.Cod_BillTo=D.Cod_Direccion)
LEFT JOIN TG_EstadoFacturaUSA E ON(A.Flg_Status=E.CodEstado)
Where A.Fec_Creacion BETWEEN @Fec_CreaIni AND @Fec_CreaFin
AND A.Cod_Cliente = @COD_CLIENTE AND A.cod_empresa = @Cod_Empresa
AND Cod_FacturaUSA <> 0
AND A.TipoFactura = 'PO'
AND A.Flg_Status='E'
-----------2DA CONSULTA-----------------------------------------
SELECT
a.Id_FacturaUSA as Id,
a.Cod_PurOrd as Po,
a.Cod_LotPurOrd Lot,
a.cod_estcli as Style,
b.numverstl as [Version],
convert(varchar(10),b.Fec_DespachoOri,110)AS fechaDespacho,
--a.cod_cliente,
b.Num_PreReq as QtyReq,
a.Num_PreDes as QtyShip,
a.Precio as Precio,
--b.Imp_TotalPre as ShipAmount,
ShipAmount=round((a.Num_PreDes*a.Precio),2)
FROM TG_FacturaUSA_LotEst a INNER JOIN tg_lotest b ON(a.cod_empresa = b.cod_empresa and a.cod_cliente=b.cod_cliente and a.cod_purord=b.cod_purord and a.cod_lotpurord=b.cod_lotpurord and a.cod_estcli=b.cod_estcli)
Where A.Fec_Creacion BETWEEN @Fec_CreaIni AND @Fec_CreaFin
AND A.Cod_Cliente = @COD_CLIENTE AND A.cod_empresa = @Cod_Empresa
go
pero esto se puede Resumir como ejemplo para Guiarse para que se entienda mejor, claro que no saldra nada en el SProcedure pero para ayudar a la Lectura Veloz
CREATE PROCEDURE WS_FIND2_FCDETALLES_BY_CLIENTE
@Fec_CreaIni datetime,
@Fec_CreaFin datetime,
@COD_CLIENTE AS COD_CLIENTE
as
-----------Lista de Encabezado de la Factura
Select id, Factura, Cliente, ImporteFacturado
From TG_FacturaUSA
Where A.Fec_Creacion BETWEEN @Fec_CreaIni AND @Fec_CreaFin
AND A.Cod_Cliente = @COD_CLIENTE
-----------Lista de los Detalles de la Factura
Select Id, Po, Estilo, Precio, Cantidad, Monto
from A.Fec_Creacion BETWEEN @Fec_CreaIni AND @Fec_CreaFin
AND A.Cod_Cliente = @COD_CLIENTE
go
______________________________________________________________________
Veamos el Metodo
______________________________________________________________________using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using BE;
namespace BLL.RGS
{
public class FacturaClienteSearchLogic: BL_Log
{
public beFCHeaderDetalles listar_FacturaCliente_by_Fechas(string Fecha1, string Fecha2,string CodCliente, string CodEmpresa)
{
DatabaseHelper db = new DatabaseHelper(Util.Default);//Conexion x Default
List<beFCHeader> listaHeader = new List<beFCHeader>(); //1ra Lista
List<beFCDetalles> listaDetalles = new List<beFCDetalles>(); //2da Lista
beFCHeaderDetalles objHeaderDetalles = new beFCHeaderDetalles();// Objeto de Retorno
beFCHeader objHeader = null;
beFCDetalles objDetalles = null;
string sqlStatement = "WS_FIND2_FCDETALLES_BY_CLIENTE";
db.AddParameter("@FEC_CREAINI", Fecha1);
db.AddParameter("@FEC_CREAFIN", Fecha2);
db.AddParameter("@COD_CLIENTE", CodCliente);
db.AddParameter("@COD_EMPRESA", CodEmpresa);
try
{
using (DbDataReader rdr = db.ExecuteReader(sqlStatement, CommandType.StoredProcedure))
{
if (rdr.HasRows)
{
while (rdr.Read())
{
objHeader = new beFCHeader();
objHeader.Id = rdr.GetInt32(0); //
objHeader.Invoice = rdr.GetString(1); //
objHeader.Type = rdr.GetString(2);//
objHeader.Cliente = rdr.GetString(3);//
objHeader.BillTo = rdr.GetString(4);//
objHeader.Date = rdr.GetString(5);//
objHeader.Amount = rdr.GetDecimal(6);//
objHeader.Status = rdr.GetString(7);
objHeader.numFactura = rdr.GetString(8);
//numFactura
listaHeader.Add(objHeader);
}
}
rdr.NextResult(); //Recorrer la Siguiente Consulta
if (rdr.HasRows)
{
while (rdr.Read())
{
objDetalles = new beFCDetalles();
objDetalles.IdDetalles = rdr.GetInt32(0); //
objDetalles.Po = rdr.GetString(1); //
objDetalles.LotPo = rdr.GetString(2);//
objDetalles.Style = rdr.GetString(3);//
objDetalles.Version = rdr.GetString(4);//
objDetalles.Date = rdr.GetString(5);//
objDetalles.QtyReq = rdr.GetDecimal(6);//
objDetalles.QtyShip = rdr.GetInt32(7);//
objDetalles.Price = rdr.GetDecimal(8);
objDetalles.Amount = rdr.GetDecimal(9);
listaDetalles.Add(objDetalles);
}
}
}
objHeaderDetalles.listaFCHeader = listaHeader; //Objeto q recibe la Lista "Encabezado"
objHeaderDetalles.listaFCDetalles = listaDetalles; //Objeto q recibe la Lista "Detalles"
}
catch (Exception ex)
{
GrabarLog(ex);
}
return objHeaderDetalles;
}
}
}
Claro para esto se tenia que crear las Clases (beFCHeader, beFCDetalles y el beFCHeaderDetalles que contiene los 2 solo para ejemplo este ultima clase
using System;
using System.Collections.Generic;
namespace BE
{
public class beFCHeaderDetalles
{
public List<beFCDetalles> listaFCDetalles { get; set; }
public List<beFCHeader> listaFCHeader { get; set; }
}
}
y ahora lo bueno...
___________________________________________________________________________
en el Codigo CS
___________________________________________________________________________
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Text;
using BLL.RGS;
using BE;
public partial class FacturaUSA_page_FacturaUSA_Find2 : basePageSessionExpire
{
private BLL.RGS.ClienteLogic objBlCliente = new BLL.RGS.ClienteLogic();
private FacturaClienteSearchLogic objBlFacturaC = new FacturaClienteSearchLogic();
private List<beFCDetalles> listaFCDetalles = new List<beFCDetalles>();
private List<beFCHeader> listaFCHeader = new List<beFCHeader>();
private beFCHeaderDetalles objHeaderDetalles = new beFCHeaderDetalles();
private int Id = 0;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) {
ddlCliente.DataSource = objBlCliente.GetAllCliente_2();
ddlCliente.DataTextField = "Abr_Cliente";
ddlCliente.DataValueField = "Cod_Cliente";
ddlCliente.DataBind();
this.txtFecha1.Attributes.Add("onKeyUp", "this.value=formateafecha(this.value);");
this.txtFecha2.Attributes.Add("onKeyUp", "this.value=formateafecha(this.value);");
}
}
//Obtener el Detalle (Filtrando por el Id de la factura que corresponde) Linq
//Obtener el monto del Detalle
protected decimal montoDetalles = 0;
protected List<beFCDetalles> FiltrarDetalles(int IdFacturaCliente) {
listaFCDetalles = new List<beFCDetalles>();
listaFCDetalles = Session["fcDetalles"] as List<beFCDetalles>;
Id = IdFacturaCliente;
List<beFCDetalles> detalles = new List<beFCDetalles>();
detalles= listaFCDetalles.FindAll((x) => {return (x.IdDetalles == IdFacturaCliente);}); //Lista del Detalle
montoDetalles = 0;// Monto del Detalle
foreach (beFCDetalles be in detalles)
{
montoDetalles += be.Amount;
}
return detalles;
}
//Metodo Principal que trae las "Facturas y sus Detalles"para el Repeater
protected void listarFacturasCliente(object sender, EventArgs e)
{
//Objeto Principal con las 2 Listas obtenidas
objHeaderDetalles = objBlFacturaC.listar_FacturaCliente_by_Fechas(CUtiles.ConvierteFormatoANSI(txtFecha1.Text.Trim()), CUtiles.ConvierteFormatoANSI(txtFecha2.Text.Trim()), ddlCliente.SelectedValue.ToString(),
Session["cod_empresa"].ToString());
listaFCDetalles= objHeaderDetalles.listaFCDetalles; //2da Lista Encabezado
Session["fcDetalles"] = listaFCDetalles;
dlFacturaC.DataSource = objHeaderDetalles.listaFCHeader; dlFacturaC.DataBind();
//Nola al asignar al Repeater Detalles correspondiente
}
//Asignar el Valor de la Fila correspondiente al CheckBox (Nola al Castear, y al asignar al CheckBox =D )
protected void dlFacturaC_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
BE.beFCHeader item = e.Item.DataItem as BE.beFCHeader;
if (item == null) return;
HtmlInputCheckBox cbItem = e.Item.FindControl("chk") as HtmlInputCheckBox;
cbItem.Value = item.numFactura;
}
}
______________________________________________________________________________
en el Codigo Aspx
____________<asp:Repeater ID="dlFacturaC" runat="server" OnItemDataBound="dlFacturaC_ItemDataBound">
<HeaderTemplate>
<table width="880px" class="Grid" >
<tr class="encabezado" >
<td style="width:5px"><asp:CheckBox ID="cbCheckAll" runat="server" Text="All" OnClick="checkAll(this)" /></td>
<td style="width:20px"></td>
<td style="width:60px">Invoice</td>
<td style="width:20px">Type</td>
<td style="width:100px">Client</td>
<td style="width:110px">Bill To</td>
<td style="width:100px">Invoice Date</td>
<td style="width:50px">Amount</td>
<td style="width:20px">Status</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr class="item">
<td style="width:5px">
<%--<asp:CheckBox ID="chk" runat="server" /> No se podía asignar el Value a este control que raro.. habra una Forma??--%>
<input type="checkbox" id="chk" runat="server" />
</td>
<td style="width:20px">
<span style="cursor:pointer"> <img src="../img/expand.png" onclick="VerDetalles(this,<%#Container.ItemIndex%>);" width="20px" height="20px" alt="" /> </span>
</td>
<td style="width:60px">
<%# ((BE.beFCHeader)Container.DataItem).Invoice %>
</td>
<td style="width:20px">
<%# ((BE.beFCHeader)Container.DataItem).Type %>
</td>
<td style="width:100px">
<%# ((BE.beFCHeader)Container.DataItem).Cliente %>
</td>
<td style="width:30px">
<%# ((BE.beFCHeader)Container.DataItem).BillTo %>
</td>
<td style="width:100px">
<%# ((BE.beFCHeader)Container.DataItem).Date %>
</td>
<td style="width:50px">
<%# String.Format("{0:0,0.0}",((BE.beFCHeader)Container.DataItem).Amount) %>
</td>
<td style="width:20px">
<%# ((BE.beFCHeader)Container.DataItem).Status %>
</td>
</tr>
<tr>
<td></td>
<td colspan="6">
<!-- Asignar el Index al id del Div , esto ayudo mucho para ubicarlo en Jscript, Nola =D -->
<div id="<%#Container.ItemIndex%>" style ="display:none;" >
<div id="divSubGrid">
<!-- Filtrar Detalles -->
<asp:Repeater ID="dlDetalles" DataSource='<%#FiltrarDetalles(((BE.beFCHeader)Container.DataItem).Id)%>' runat="server"><HeaderTemplate>
<table width="500px" class="SubGrid">
<tr class="encabezadoSubGrid">
<td style="width:70px">Po</td>
<td style="width:150px">Style</td>
<td style="width:20px">Version</td>
<td style="width:100px">Ori.Date</td>
<td style="width:30px">QtyReq</td>
<td style="width:30px">QtyShip</td>
<td style="width:30px">Price</td>
<td style="width:50px">Amount</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr class="item" >
<td style="width:70px"><%# ((BE.beFCDetalles)Container.DataItem).Po %> </td>
<td style="width:150px"><%# ((BE.beFCDetalles)Container.DataItem).Style %> </td>
<td style="width:20px"><%# ((BE.beFCDetalles)Container.DataItem).Version %> </td>
<td style="width:100px"><%# ((BE.beFCDetalles)Container.DataItem).Date %> </td>
<td style="width:30px"><%# ((BE.beFCDetalles)Container.DataItem).QtyReq %> </td>
<td style="width:30px"><%# ((BE.beFCDetalles)Container.DataItem).QtyShip %> </td>
<td style="width:30px"><%# ((BE.beFCDetalles)Container.DataItem).Price %> </td>
<td style="width:50px"><%# String.Format("{0:0,0.0}",((BE.beFCDetalles)Container.DataItem).Amount) %> </td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr class="SubGridFinal">
<td colspan="7">
</td>
<!-- Asignar el Monto del Detalle -->
<td><%# String.Format("{0:0,0.0}",montoDetalles ) %> </td>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</div>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
--metodo en JScript
//Metodo para ver y ocultar el Panel Detalles
function VerDetalles(img, indice) {
var tbl = document.getElementById(indice);
if (tbl != null) {
if (tbl.style.display == "none") {
tbl.style.display = "inline";
img.src = "../img/collapse.png";
}
else {
tbl.style.display = "none";
img.src = "../img/expand.png";
}
}
}
//Metodo para Activar y Desactivar los CheckBox
function checkAll(cb) {
var ctrls = document.getElementsByTagName('input');
for (var i = 0; i < ctrls.length; i++) {
var cbox = ctrls[i];
if (cbox.type == "checkbox") {
cbox.checked = cb.checked;
}
}
}
__________________________________________________________________
------------------------
bueno para explicar todo me faltara tiempo ... upss
para el que lo leyó y lo entendió le servirá de ejemplo, que si se puede con un poco de pasciencia y perseverancia. (Ahi ta Wachiturro Ricardo Jimenez" )
en este ejemplo hago uso de enableviewstate="true" en el Repeater porqueeee, ya que luego este recorreria para obtener los Checbox activos, lo ideal era con JavaScript y mandarlo recién al servidor, pero como dije al comienzo eso es otra Historia pero no muy lejana =D .
espero que les haya gustado un poco de lo que aprendí Gracias a la Comunidad de Internet y a mi Profe que me compartio una parte de su conocimiento.
Sls neteros.

Necesitamos la fuente fanfarron :D
ResponderEliminarY como se ocultaria el expand png, si este no tuviera sub registros, quedo atento a tus comentarios
ResponderEliminar