Formulario Maestro-Detalle

Buenas tardes. Les consulto si alguno pudo hacer el ingreso tabular de N registros. es decir, datos generales de factura, y N detalles de factura. Puede ser de otro contexto, pero si alguien lo hizo funcionar con validaciones y todo, que avise!! no logro hacerlo funcionar!! :mellow:

Entiendo que son dos modelos, maestro y detalle, donde se valida todo y luego se almacena.

Desde ya muchas gracias. :)

Yo hice esto

En la vista create




<?php 


    $this->widget('zii.widgets.jui.CJuiTabs', array(

        'tabs'=>array(

          'Info' =>$this->renderPartial("_form", array('model' => $model, 'form'=>$form), $this),

          'Productos'=>$this->renderPartial("_productos", array('modelp' => $modelp, 'form'=>$form), $this),

        ),        

        'options'=>array(

            'collapsible'=>true,

        ),

    ));


?>



vista productos que es donde se maneja el tabular input





<table class="templateFrame grid" cellspacing="0">

                <thead class="templateHead">

                    <tr>

                        <td>

                            <?php echo $form->labelEx(Productos::model(),'prd_id');/* estos son los labels */?>

                        </td>

                        <td>

                            <?php echo $form->labelEx(FacturasHasProductos::model(),'descripcion');?>

                        </td>

                        <td>

                            <?php echo $form->labelEx(FacturasHasProductos::model(),'cantidad');?>

                        </td>

                        <td>

                            <?php echo $form->labelEx(FacturasHasProductos::model(),'valor');?>

                        </td>

                        <td colspan="2">

                            <?php echo $form->labelEx(FacturasHasProductos::model(),'descuento');?>

                        </td>

                    </tr>

                </thead>

                <tbody class="templateTarget">

                <?php 

                         $i = 0;

                         //$products= FacturasHasProductos::model()->findAll(array('index'=>'fhp_id'));//por ahora es vacio pero este por lo general es Person::model()->findAll() con las condiciones que necesites

                         $productos= Productos::model()->findAll(array('order'=>'nombre'));//por ahora es vacio pero este por lo general es Person::model()->findAll() con las condiciones que necesites

                         $prodArray = CHtml::listData($productos, 'prd_id', 'concatened');

                         foreach($modelp as $product): 

                 ?>

                    <tr class="templateContent">

                        <td>

                            

                            <?php echo $form->dropDownList($product,"[$i]prd_id",$prodArray ,array('prompt'=>'','style'=>'width:200px')); ?>

                            

                        </td>

                        <td>

                            <?php echo $form->textField($product,"[$i]descripcion",array('style'=>'width:350px')); ?>

                        </td>

                        <td>

                            <?php echo $form->textField($product,"[$i]cantidad",array('style'=>'width:50px')); ?>

                        </td>

                        <td>

                            <?php echo $form->textField($product,"[$i]valor",array('style'=>'width:50px')); ?>

                        </td>

                        <td>

                            <?php echo $form->textField($product,"[$i]descuento",array('style'=>'width:50px')); ?>

                        </td>

                        <td>

                            <div class="remove"><?php echo Yii::t('ui','Remove');?></div>

                             <?php echo CHtml::hiddenField("rowIndex_$i",$i,array('class'=>'rowIndex'));?>

                        </td>

                    </tr>

                    <?php

                         $i++;

                        endforeach;

                    ?>

                </tbody>

                <tfoot>

                    <tr>

                        <td colspan="4">

                            <div class="add"><?php echo Yii::t('ui','New');//aca puedes colocar un texto un boton o lo que quieras?></div>

                            

                            <textarea class="template" rows="0" cols="0">

                                <tr class="templateContent">

                                    <td>

                                        <?php echo CHtml::dropDownList("FacturasHasProductos[{0}][prd_id]",'', $prodArray,array(''=>'Seleccione','style'=>'width:200px')); ?>

                                        

                                    </td>

                                    <td>

                                        <?php echo CHtml::textField("FacturasHasProductos[{0}][descripcion]",'',array('style'=>'width:350px')); ?>

                                    </td>

                                    <td>

                                        <?php echo CHtml::textField("FacturasHasProductos[{0}][cantidad]",'',array('style'=>'width:50px')); ?>

                                    </td>

                                    <td>

                                        <?php echo CHtml::textField("FacturasHasProductos[{0}][valor]",'',array('style'=>'width:50px')); ?>

                                    </td>

                                    <td>

                                        <?php echo CHtml::textField("FacturasHasProductos[{0}][descuento]",'',array('style'=>'width:50px')); ?>

                                    </td>

                                    <td>

                                        <div class="remove"><?php echo Yii::t('ui','Remove');// tambn puedes colcar texto o cual quier boton ?></div>

                                        <?php echo CHtml::hiddenField('rowIndex_{0}','{0}',array('class'=>'rowIndex'));?>

                                    </td>

                                </tr>

                            </textarea>

                        </td>

                    </tr>

                </tfoot>

            </table> 

    




<script type="text/javascript">

    $(".template").fadeOut(5);

</script>




En el Controlador




public function actionCreate()

	{

		$model=new Facturas;

		$modelp=new FacturasHasProductos;

                

		// Uncomment the following line if AJAX validation is needed

		// $this->performAjaxValidation($model);

                if(isset($_POST['Facturas'])){

                   

                    $model->attributes=$_POST['Facturas'];

                    $valid=true;

                    $valid=$model->validate() && $valid;

                    if(!$valid){

                        echo CHtml::errorSummary($model);

                    }

                    if(isset($_POST['FacturasHasProductos'])){

                        $arrayProductos=$_POST['FacturasHasProductos'];

                        //Primero Validamos los campos

                        foreach($arrayProductos as $i=>$j){

                            $modelp->attributes=$_POST['FacturasHasProductos'][$i];

                            $valid=$modelp->validate() && $valid;

                            if(!$valid)

                                echo CHtml::errorSummary($modelp);

                        }

                        if($valid){

                            if(!Yii::app()->user->isGuest)

                                $model->usu_id = $usu;

                            else

                                throw new CHttpException(411,'Usted no puede crear Facturas');

                            

                            if($model->save()){

                                    $arrayProductos=$_POST['FacturasHasProductos'];

                                    //Guardamos todos los productos

                                    foreach($arrayProductos as $i=>$j){

                                        $modelp=new FacturasHasProductos;

                                        $modelp->attributes=$_POST['FacturasHasProductos'][$i];

                                        $modelp->fac_id = $model->fac_id;

                                        $modelp->save(false);

                                        

                                    }

                                    echo 'Creacion exitosa';

                                    //$this->redirect(array('facturas/update', 'id'=>$model->fac_id));

                                    Yii::app()->end();

                            }

                        }

                    }

                    else{

                        echo 'Favor llene los Productos';

                        Yii::app()->end();

                    }

                    

                        

		}

                else{

                    $this->render('create',array(

                            'model'=>$model,'modelp'=>array(),

                    ));

                }

	}



No es la forma mas cristiana pero me parece buena

Entiendo la vista, Create, El controller pero la "vista productos que es donde se maneja el tabular input" es el formulario?

Entiendo lo que pides, esto fue lo que yo hice, tengo una tabla con los datos generales y otra con los detalles:

En la forma donde se captan los datos:




<?php

/* @var $this DatossalController */

/* @var $model Datossal */

/* @var $form CActiveForm */

?>

<script>


$(function(){

	//Add, Save, Edit and Delete functions code

	$(".btnEdit").on("click", Edit);

	$(".btnDelete").on("click", Delete);

	$("#btnAdd").on("click", Add);		

});




function Add(){

	

	if($('#producto option:selected').text()!=''){

		var cantidad= $('#incantidad').val()==''?'0':$('#incantidad').val();            	

            	

            	$("#tblData tbody").append(

            	"<tr class='even'>"+    	

            	"<td>"+ $('#producto option:selected').text()+"</td>"+

            	"<td>"+ cantidad +"</td>"+       		

            	"<td><img src='images/update.png' class='btnEdit' title='Actualizar' style='cursor:pointer'>	<img src='images/delete.png' class='btnDelete' title='Eliminar' style='cursor:pointer'/></td>"+

            	"</tr>");


            	$(".btnEdit").off("click");

            	$(".btnDelete").off("click");

            	$(".btnEdit").on("click", Edit); 	

            	$(".btnDelete").on("click", Delete);


            	document.getElementById('incantidad').value ='';       		

            	$('#producto option:selected').remove();

	}

	else{

		alert('No quedan productos para adicionar');

	}

		

};


var oldCantidad;

function Edit(){

	var par = $(this).parent().parent(); //tr

	var tdCantidad = par.children("td:nth-child(2)");  

	var tdButtons = par.children("td:nth-child(3)");

	

	oldCantidad=tdCantidad.html();

	

	tdCantidad.html("<input type='text' id='txtCantidad' value='"+tdCantidad.html()+"'/>"); 

	tdButtons.html("<img src='images/save.png' class='btnSave' title='Guardar' style='cursor:pointer'/>	<img src='images/cancel.gif' class='btnCancel' title='Cancelar' style='cursor:pointer'/>");

 

	$(".btnEdit").off("click");

	$(".btnDelete").off("click");

	$(".btnSave").on("click", Save);

	$(".btnCancel").on("click", Cancel);

	$(".btnEdit").on("click", Edit);

	$(".btnDelete").on("click", Delete);

};


function Save(){

	var par = $(this).parent().parent(); //tr

	var tdCantidad = par.children("td:nth-child(2)"); 

	var tdButtons = par.children("td:nth-child(3)");

 	

	tdCantidad.html(tdCantidad.children("input[type=text]").val());  

	tdButtons.html("<img src='images/update.png' class='btnEdit' title='Actualizar' style='cursor:pointer'/>	<img src='images/delete.png' class='btnDelete' title='Eliminar' style='cursor:pointer'/>");

 

	$(".btnEdit").off("click");

	$(".btnDelete").off("click");

	$(".btnEdit").on("click", Edit);

	$(".btnDelete").on("click", Delete);

};


function Cancel(){

	var par = $(this).parent().parent(); //tr

	var tdCantidad = par.children("td:nth-child(2)"); 

	var tdButtons = par.children("td:nth-child(3)");

	

	tdCantidad.html(oldCantidad); 

	tdButtons.html("<img src='images/update.png' class='btnEdit' title='Actualizar' style='cursor:pointer'/>	<img src='images/delete.png' class='btnDelete' title='Eliminar' style='cursor:pointer'/>");

 

	$(".btnEdit").off("click");

	$(".btnDelete").off("click");

	$(".btnEdit").on("click", Edit);

	$(".btnDelete").on("click", Delete);

};


var cont=1;

function Delete(){

	var par = $(this).parent().parent(); //tr

	$('#producto')

 		.append($("<option></option>")

 		.attr("value",cont)

 		.text(par.children("td:nth-child(1)").html())); 

	

	cont++;

	par.remove();

};


$('#datossal-form').live('submit',function(event) {


	var tbl = $('#tblData tbody tr').map(function() {

			var row = $(this);

			return {	producto: row.find(':nth-child(1)').text(),

						cantidad: row.find(':nth-child(2)').text()

					};

			}).get();

	

	$.ajax({

    	url: 'index.php?r=datossal/salvarFactura',

    	type: 'POST',

    	dataType: 'text',

    	data: {"detalle":tbl, 

		"fecha": $('#Datossal_fecha').val(),

            	"codsalida":$('#Datossal_Codsalida').val(),

            	"cliente":$('#Datossal_CodCliente').val(),

		"codueb":$('#Datossal_CodUEB').val(),

            	"codmon":$('#Datossal_CodMon').val(),

		"modelid":$('#modelid').val() 

        		},

    	success: function(data) {

		window.location.href='index.php?r=datossal/admin';		

    	}

	});

	return false;

});

</script>


<style>

.mystyle

{

	background: white;

	border-collapse: collapse;

	width: 100%;

	border: 1px #D0E3EF solid;

	

}

.mystyle th, .mystyle td

{	

	border: 1px white solid;

	padding: 0.3em;

}

.mystyle th

{

	color: white;

	background: #69A8CD;

	text-align: center;

}

.mystyle tr.even

{

	background: #F8F8F8;

}

.mystyle tr.odd

{

	background: #E5F1F4;

}

.mystyle tr.selected

{

	background: #BCE774;

}

.mystyle tbody tr:hover

{

	background: #ECFBD4;

}


</style>



Luego capto los datos generales en la misma forma y con esto capto los detalles de los productos:




 <fieldset>

		<legend>Lista de Productos</legend>

		<table>

			<tr>

				<td>

					<label>Producto</label>

                                    	<?php 

                                        	$conceptos=array();

                                        	$todos=Productos::model()->findAll();


                                        	foreach ($todos as $producto) {

                                            	$esta=false;

                                            	foreach ($model->detalleFacturas as $value) {

                                                	if($producto->NProductos==$value->idProducto->NProductos){

                                                        	$esta=true;

                                                        	break;

                                                	}                                                	

                                         		}

                                         		if(!$esta)

                                             		$conceptos[$producto->Codprod]=$producto->NProductos;

                                        	}


                                        	echo CHtml::dropDownList('producto','', $conceptos);                         		

                                    	?>

				</td>

				<td>

					<label>Cantidad</label>

					<input type="text" id="incantidad"/>

				</td>				

			</tr>

			<tr>

				<td>

					<input type="button" id="btnAdd" value="Adicionar Producto" />

				</td>

			</tr>

		</table>

		

	<table id="tblData" class="mystyle"> 		

    	<thead>

        	<tr>       		

            	<th>Producto</th>

            	<th>Cantidad</th>

            	<th style="width:50px"></th>

        	</tr>

    	</thead>

    	<tbody>

        	<?php 

            	foreach ($model->detalleFacturas as $value) {

                	echo "<tr>";

                	echo "<td>".$value->idProducto->NProductos."</td>";

                	echo "<td>".$value->cantidad."</td>";           		

                	echo "<td><img src='".Yii::app()->request->baseUrl."/images/update.png' class='btnEdit' title='Actualizar' style='cursor:pointer'>

                            	<img src='".Yii::app()->request->baseUrl."/images/delete.png' class='btnDelete' title='Eliminar' style='cursor:pointer'/></td>";

                	echo "</tr>";

         		}

        	?>  

    	</tbody>

	</table>

	

</fieldset>



En el modelo de los datos generales escribo las relaciones:




public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

	     'detalleFacturas' => array(self::HAS_MANY, 'Detallesfact', 'Id_sal'), // esta la necesito para los detalles de la forma

	  	'codsalida' => array(self::BELONGS_TO, 'Tiposalida', 'Codsalida'),

	  	'codCliente' => array(self::BELONGS_TO, 'Clientes', 'CodCliente'),

	  	'codMon' => array(self::BELONGS_TO, 'Monedas', 'CodMon'),

	  	'codUEB' => array(self::BELONGS_TO, 'Entidades', 'CodUEB'),

		);

	}



En el controlador hago lo siguiente, creo una accion salvar facturas, ya que cuando se crea tambien tiene que actualizar otra tabla llamada submayor, o sea, si doy una salida a un producto entonces le resta esa cantidad al saldo del submayor:




public function actionSalvarFactura()

{

	$id=$_POST['modelid'];


	if($id)

    	$model=$this->loadModel($id); 	

	else        	

    	$model = new Datossal;	

	if(isset($_POST['detalle']))

	{

   	$model->fecha=$_POST['fecha'];

        	$model->Codsalida=$_POST['codsalida'];

        	$model->CodCliente=$_POST['cliente'];

        	$model->CodUEB=$_POST['codueb'];

        	$model->CodMon=$_POST['codmon'];


   	if ($model->save()) {


        	Detallesfact::model()->deleteAll('Id_sal=:id', array(':id'=>$model->id));


        	foreach($_POST['detalle'] as $item){


            	//Insertar los detalles de la salida

            	$producto= Productos::model()->find('NProductos=:postID', array(':postID'=>$item['producto']));

            	$detalle=new Detallesfact;  	

            	$detalle->Id_sal=$model->id;

            	$detalle->Codprod=$producto->Codprod;

            	$detalle->cantidad=$item['cantidad'];

            	$detalle->save(); 

            	

            	if(!$id){

                	$criteria=new CDbCriteria;                                    	

                	$criteria->addCondition('CodUEB=:coden'); 

                	$criteria->addCondition('Codprod=:codigo'); 	

                	$criteria->params=array(':coden'=>$model->CodUEB , ':codigo'=>$producto->Codprod);			

                	$objSubmp = Submayorprod::model()->find($criteria);

                	$objSubmp->SaldoAct = $objSubmp->SaldoAct - $item['cantidad'];

                	$objSubmp->save();

            	}

		

        	}

    	}		

	}

 }



No se si sera la mejor pero me funciona bien

y no sera posible hacerlo mas facilmente usando el CGridView para los detalles, lo que propongo es usar la vista (view) de "factura", y mas abajo renderizar el CGridView de "detalles" con el filtro de la clave foranea de "Facturas" y finalmente renderizar un formulario que permita agregar mas productos (con ajax claro esta) yo lo he tratado de hacer de mil manera y no me funciona, aun no me doy por vencido, ustedes se preguntaran ¿pero por que no intentarlo de otra forma? y es que me parece mucho mas elegante tomar las herramientas que ofrece Yii y adaptarlas, Si alguien tiene exito o lo ha visto en alguna parte se lo agradeceria me informara, saludos a todos y feliz año nuevo

amigos estoy haciendo al parecer algo muy parecido les podria pedir de favor sui pueden subir un video de como lo hicieron y cual es el resusltado final.

de antemano muchisims gracias.