PDA

View Full Version : extjs 4.2 Ext.data.Model.save() don't refresh record data with server returned data.



maneljn
15 Apr 2013, 7:26 AM
REQUIRED INFORMATION Ext version tested:

Ext 4.2 rev 4.2.0.663

Browser versions tested against:

IE9.0.8112.16421
FF 20.0.1 (firebug 1.11.2 installed)

DOCTYPE tested against:

HTML5

Description:

If you save() an Ext.data.Model with Ext.Direct API "update" function, in earlier versions is expected that if server returns the refreshed data of the model, the record was also updated in client side. With new Extjs 4.2 version this does not work anymore. Never refreshes the client side record.

The result that was expected:

Refresh the model data with the server returned values

The result that occurs instead:

Does not refresh (it ignores the returned server data)

Test Case:
The model example:


//@charset UTF-8

// Modelo de datos para los alumnos
Ext.define('esiacademia.model.alumno', {
extend: 'Ext.data.Model',

idProperty: 'alum_id',
fields: [
{ name: 'alum_id', type: 'integer' },
{ name: 'alum_guid', type: 'string' },
{ name: 'alum_contacto_id', type: 'integer' },
{ name: 'alum_tutorlegal_contacto_id', type: 'integer' },
{ name: 'alum_observaciones', type: 'string' },
{ name: 'alum_activo', type: 'boolean' },

// Contacto asociado
{ name: 'cto_id', type: 'integer', persist: false },
{ name: 'cto_guid', type: 'string', persist: false },
{ name: 'cto_tipo', type: 'string', persist: false },
{ name: 'cto_sexo', type: 'string', persist: false },
{ name: 'cto_nombre_propio', type: 'string', persist: false },
{ name: 'cto_apellidos', type: 'string', persist: false },
{ name: 'cto_nombre_fiscal', type: 'string', persist: false },
{ name: 'cto_nombre_com', type: 'string', persist: false },
{ name: 'cto_nif', type: 'string', persist: false },
{ name: 'cto_nacimiento', type: 'date', dateFormat: 'Y-m-d', persist: false },
{ name: 'cto_edad', type: 'integer', persist: false },

// Direccion fiscal asociada
{ name: 'dir_id', type: 'integer', persist: false },
{ name: 'dir_guid', type: 'string', persist: false },
{ name: 'dir_alias', type: 'string', persist: false },
{ name: 'dir_via', type: 'string', persist: false },
{ name: 'dir_nom', type: 'string', persist: false },
{ name: 'dir_num', type: 'integer', persist: false },
{ name: 'dir_esc', type: 'string', persist: false },
{ name: 'dir_pis', type: 'string', persist: false },
{ name: 'dir_pue', type: 'string', persist: false },
{ name: 'dir_direc1', type: 'string', persist: false },
{ name: 'dir_direc2', type: 'string', persist: false },
{ name: 'dir_pos', type: 'string', persist: false },
{ name: 'dir_pob', type: 'string', persist: false },
{ name: 'dir_pro', type: 'string', persist: false },
{ name: 'dir_pais_iso3', type: 'string', persist: false },
{ name: 'dir_tel1', type: 'string', persist: false },
{ name: 'dir_tel2', type: 'string', persist: false },
{ name: 'dir_fax', type: 'string', persist: false },
{ name: 'dir_email', type: 'string', persist: false },
{ name: 'dir_web', type: 'string', persist: false },

// Tutor legal - Contacto asociado
{ name: 'alum_tutorlegal_cto_nombre_fiscal', type: 'string', persist: false },

{ name: 'alum_usralta_id', type: 'integer', persist: false },
{ name: 'alum_fechaalta', type: 'date', dateFormat: 'Y-m-d H:i:s', persist: false },
{ name: 'alum_usrmod_id', type: 'integer', persist: false },
{ name: 'alum_fechamod', type: 'date', dateFormat: 'Y-m-d H:i:s' }, // Este campo se usará para el aviso de sobreescritura en concurrencia (2 usuarios grabando en el mismo registro a la vez)
{ name: 'alum_usralta_nombre', type: 'string', persist: false },
{ name: 'alum_usrmod_nombre', type: 'string', persist: false }
]

});


The store example:
//@charset UTF-8

// Store para los alumnos
Ext.define('esiacademia.store.alumnos', {
extend: 'Ext.data.Store',

requires: [
'esiacademia.model.alumno'
],

constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: false,
autoSync: false,
buffered: false,
model: 'esiacademia.model.alumno',
remoteSort: true,
remoteFilter: true,
pageSize: 50,
proxy: {
type: 'direct',
batchActions: false,
paramAsHash: true,
extraParams: {
buscar: null
},
api: {
read: Ext.esiacademiaDirect.esiacademia_alumnos.getAlumnos,
create: Ext.esiacademiaDirect.esiacademia_alumnos.createAlumno,
update: Ext.esiacademiaDirect.esiacademia_alumnos.updateAlumno,
destroy: Ext.esiacademiaDirect.esiacademia_alumnos.destroyAlumno
},
reader: {
type: 'json',
root: 'data',
idProperty: 'alum_id',
totalProperty: 'total',
successProperty: 'success',
messageProperty : 'message'
}
}
}, cfg)]);
}


});

The controller piece of code:


// Cargar valores del formulario en el registro asociado
registro.set( formulario.getValues() );
// asignar el proxy direct del store actual al registro
registro.setProxy(panelEdicion.storeAsociado.getProxy());

// Mostrar "guardando" en barra de estado
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','Guardando registro ... ') + registro.get('cto_nombre_fiscal'),
iconCls: 'x-status-busy',
clear: false
});
}
// Mostrar mascara
panelEdicion.getEl().mask(gt.dgettext('esiacademia','Guardando...'));

//
// Iniciar proceso multiguardar
// Básicamente gestiona flags para guardar la ficha principal, subficha direcciones, upload foto, etc...
// Asi se pueden ejecutar los procesos de guardar asincronos de forma consecutiva si van bien
// y al final cerrar el formulario de edicion o dejarlo si hay errores en cualquier guardar.
//
me.clearMultiGuardar();

// Guardar registro a traves del proxy direct
registro.save({
// Procesar respuesta
callback: function( records, operation) {
panelEdicion.getEl().unmask();

if (operation.wasSuccessful()) {
// Limpiar barra de estado
if (mainStatusBar) {
mainStatusBar.clearStatus({ useDefaults:true, anim: false });
}
//console.log(operation.response);
//console.log(operation.response.result);
// Guardar correcto
if ( panelEdicion.modo=="nuevo" ) {
// Agregar el registro al store si se ha guardado bien y no existe ya.
if ( panelEdicion.storeAsociado.findExact('alum_id',registro.get('alum_id')) == -1) {
//panelEdicion.storeAsociado.insert(0,registro);
panelEdicion.storeAsociado.add(registro);
}
// Forzar cambios hechos en el store para que no lanze operaciones con el registro agregado
panelEdicion.storeAsociado.commitChanges();
}

// Informar que hemos completado correctamente la tarea de guardar ficha para el multiguardar
me.completarMultiGuardar(registro, panelEdicion, 'ficha', true);

} else {
// Error al guardar
if (operation.hasException()) {
var mensajeError = operation.getError();
var iconoError = Ext.Msg.WARNING
} else {
var mensajeError = gt.dgettext('esiacademia','Error: No se han podido guardar los datos.');
var iconoError = Ext.Msg.ERROR
}

// Mostrar mensaje error
if (mainStatusBar) {
mainStatusBar.setStatus({
text: mensajeError,
iconCls: 'x-status-error',
//clear: true
clear: { useDefaults:true, anim: false }
});
}

Ext.Msg.show({
title: gt.dgettext('esiacademia','Guardar'),
msg: mensajeError,
buttons: Ext.Msg.OK,
icon: iconoError
});

// Informar que hemos completado con errores la tarea de guardar ficha para el multiguardar
me.completarMultiGuardar(registro, panelEdicion, 'ficha', false);

}
}
});


The POST


{"action":"esiacademia_alumnos","method":"updateAlumno","data":[{"alum_id":4,"alum_guid":"5063ff738c7d1","alum_contacto_id":66,"alum_tutorlegal_contacto_id":1,"alum_observaciones":"Observaciones varias","alum_activo":true,"alum_fechamod":"2012-09-27 09:42:40"}],"type":"rpc","tid":6}


The returned server data



{"type":"rpc","tid":6,"action":"esiacademia_alumnos","method":"updateAlumno","result":{"success":true,"data":[{"alum_id":"4","alum_guid":"5063ff738c7d1","alum_contacto_id":"66","alum_tutorlegal_contacto_id":"1","alum_observaciones":"Observaciones varias","alum_activo":"1","alum_usralta_id":"1","alum_fechaalta":"2012-09-27 09:25:39","alum_usrmod_id":"1","alum_fechamod":"2013-04-15 17:24:50","cto_id":"66","cto_guid":"5054debeac1b9","cto_tipo":"P","cto_sexo":"H","cto_nombre_propio":"MARIANO","cto_apellidos":"DE CAL FRAY","cto_nombre_fiscal":"DE CAL FRAY, MARIANO","cto_nombre_com":"","cto_nif":"40442132J","cto_nacimiento":"0000-00-00","cto_idioma":"es_ES","cto_observaciones":"","cto_activo":"1","cto_usralta_id":"1","cto_fechaalta":"2012-09-15 22:02:06","cto_usrmod_id":"1","cto_fechamod":"2012-12-04 18:28:12","dir_id":"53","dir_guid":"5054debeac9c6","dir_contacto_id":"66","dir_fiscal":"1","dir_alias":"","dir_via":"","dir_nom":"NOM DEL CARRER","dir_num":"0","dir_esc":"","dir_pis":"","dir_pue":"","dir_direc1":"NOM DEL CARRER","dir_direc2":"","dir_pos":"","dir_pob":"","dir_pro":"","dir_pais_iso3":"ESP","dir_tel1":"","dir_tel2":"","dir_fax":"","dir_email":"","dir_web":"","dir_activo":"1","dir_usralta_id":"1","dir_fechaalta":"2012-09-15 22:02:06","dir_usrmod_id":"1","dir_fechamod":"2012-12-04 18:28:12","cto_edad":null,"alum_tutorlegal_cto_nombre_fiscal":"JU\u00c0REZ NICOLAU, MANEL","alum_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","alum_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"}],"total":"1"}}



HELPFUL INFORMATION Debugging already done:

none

Possible fix:

not provided

Operating System:

7 Pro

slemmon
15 Apr 2013, 11:25 AM
I was unable to reproduce the issue using the code below. Tested on 4.1.3, 4.2, and 4.2.1 Beta.

CODE


Ext.define('Writer.Grid', {
extend: 'Ext.grid.Panel',
alias: 'widget.writergrid',


initComponent: function(){
var me = this;
this.editing = Ext.create('Ext.grid.plugin.CellEditing');


Ext.apply(this, {
plugins: [this.editing],
columns: [{
text: 'ID',
width: 40,
dataIndex: 'id'
}, {
header: 'Email',
flex: 1,
dataIndex: 'email',
field: {
type: 'textfield'
}
}, {
header: 'First',
width: 100,
dataIndex: 'first',
field: {
type: 'textfield'
}
}, {
header: 'Last',
width: 100,
dataIndex: 'last',
field: {
type: 'textfield'
}
}]
, bbar: [{
text: 'Sync'
, handler: function () {
me.getStore().sync();
}
}]
});
this.callParent();
}
});


Ext.define('Writer.Person', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int',
useNull: true
}, 'email', 'first', 'last'],
validations: [{
type: 'length',
field: 'email',
min: 1
}, {
type: 'length',
field: 'first',
min: 1
}, {
type: 'length',
field: 'last',
min: 1
}]
});


var store = Ext.create('Ext.data.Store', {
model: 'Writer.Person',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'data/json.json',
create: 'app.php/users/create',
update: 'data/json2.json',
destroy: 'app.php/users/destroy'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
}
},
});


Ext.widget('writergrid', {
renderTo: document.body
, store: store
});

Example Read Data


{"success":true,"message":"Loaded data","data":[{"id":1,"first":"Fred","last":"Flintstone","email":"fred@flintstone.com"}]}


Example Response Data


{"success":true,"message":"Loaded data","data":[{"id":1,"first":"Changed First Name","last":"Changed Last Name","email":"changed@update.com"}]}

maneljn
15 Apr 2013, 11:08 PM
slemmon, have you tried with save() method explicity ?

In my example, my grid never was refreshed after success save with data returned by server.

Manel

slemmon
16 Apr 2013, 7:26 AM
Ok. I refactored my test case with the proxy on the model itself and instead of sync() on the store I'm doing save() on the model instance and I'm afraid I'm still not able to reproduce the issue. I tested on 4.1.3, 4.2, and 4.2.1 Beta.



CODE:


Ext.define('Writer.Grid', {
extend: 'Ext.grid.Panel',
alias: 'widget.writergrid',




initComponent: function(){
var me = this;
this.editing = Ext.create('Ext.grid.plugin.CellEditing');




Ext.apply(this, {
plugins: [this.editing],
columns: [{
text: 'ID',
width: 40,
dataIndex: 'id'
}, {
header: 'Email',
flex: 1,
dataIndex: 'email',
field: {
type: 'textfield'
}
}, {
header: 'First',
width: 100,
dataIndex: 'first',
field: {
type: 'textfield'
}
}, {
header: 'Last',
width: 100,
dataIndex: 'last',
field: {
type: 'textfield'
}
}]
, bbar: [{
text: 'Save'
, handler: function () {
me.getStore().first().save();
}
}]
});
this.callParent();
}
});




Ext.define('Writer.Person', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int',
useNull: true
}, 'email', 'first', 'last'],
validations: [{
type: 'length',
field: 'email',
min: 1
}, {
type: 'length',
field: 'first',
min: 1
}, {
type: 'length',
field: 'last',
min: 1
}],
proxy: {
type: 'ajax',
api: {
read: 'data/json.json',
create: 'app.php/users/create',
update: 'data/json2.json',
destroy: 'app.php/users/destroy'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
}
}
});




var store = Ext.create('Ext.data.Store', {
model: 'Writer.Person',
autoLoad: true
});




Ext.widget('writergrid', {
renderTo: document.body
, store: store
});


Original Data:


{"success":true,"message":"Loaded data","data":[{"id":1,"first":"Fred","last":"Flintstone","email":"fred@flintstone.com"}]}


Save response:


{"success":true,"message":"Loaded data","data":[{"id":1,"first":"Changed First Name","last":"Changed Last Name","email":"changed@update.com"}]}

maneljn
16 Apr 2013, 11:26 PM
I will try to add some console.log() inside my code and report again with the results.

Manel

maneljn
16 Apr 2013, 11:57 PM
Slemmon, that's a new example of my app, using a table wich has few fields to be more clear.

The model declaration:


//@charset UTF-8

// Modelo de datos para los recursos materiales
Ext.define('esiacademia.model.recursoMaterial', {
extend: 'Ext.data.Model',

idProperty: 'recmat_id',
fields: [
{ name: 'recmat_id', type: 'integer' },
{ name: 'recmat_guid', type: 'string' },
{ name: 'recmat_codigo', type: 'string' },
{ name: 'recmat_nombre', type: 'string' },
{ name: 'recmat_pertenece_contacto_id', type: 'integer' },
{ name: 'recmat_activo', type: 'boolean' },

// Pertenece a - Contacto asociado
{ name: 'recmat_pertenece_cto_nombre_fiscal', type: 'string', persist: false },

{ name: 'recmat_usralta_id', type: 'integer', persist: false },
{ name: 'recmat_fechaalta', type: 'date', dateFormat: 'Y-m-d H:i:s', persist: false },
{ name: 'recmat_usrmod_id', type: 'integer', persist: false },
{ name: 'recmat_fechamod', type: 'date', dateFormat: 'Y-m-d H:i:s' }, // Este campo se usará para el aviso de sobreescritura en concurrencia (2 usuarios grabando en el mismo registro a la vez)
{ name: 'recmat_usralta_nombre', type: 'string', persist: false },
{ name: 'recmat_usrmod_nombre', type: 'string', persist: false }
]

});



The store declaration


//@charset UTF-8

// Store para los recursos materiales
Ext.define('esiacademia.store.recursosMateriales', {
extend: 'Ext.data.Store',

requires: [
'esiacademia.model.recursoMaterial'
],

constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: false,
autoSync: false,
buffered: false,
model: 'esiacademia.model.recursoMaterial',
remoteSort: true,
remoteFilter: true,
pageSize: 50,
proxy: {
type: 'direct',
batchActions: false,
paramAsHash: true,
extraParams: {
buscar: null
},
api: {
read: Ext.esiacademiaDirect.esiacademia_recursosMateriales.getRecursosMateriales,
create: Ext.esiacademiaDirect.esiacademia_recursosMateriales.createRecursoMaterial,
update: Ext.esiacademiaDirect.esiacademia_recursosMateriales.updateRecursoMaterial,
destroy: Ext.esiacademiaDirect.esiacademia_recursosMateriales.destroyRecursoMaterial
},
reader: {
type: 'json',
root: 'data',
idProperty: 'recmat_id',
totalProperty: 'total',
successProperty: 'success',
messageProperty : 'message'
}
}
}, cfg)]);
}


});



The views are this 3 components:



//@charset UTF-8

Ext.define('esiacademia.view.recursosMateriales.mant', {
extend: 'esiacademia.view.tabpanelBase',
alias : 'widget.esiacademia_view_recursosMateriales_mant',

requires: [
],

name: 'tabpanelRecursosMateriales', // El name final se asigna al abrir la opcion de menu (eleentMant)
title: gt.dgettext('esiacademia','Recursos Materiales - Mantenimiento'),
tabPosition: 'top',

initComponent: function() {
var me = this;

// Ejecutar metodo de su parent
me.callParent(arguments);

// Crear el panel del grid
var mantGrid = esiacademiaApp.getView('recursosMateriales.mantGrid');
if (mantGrid) {
var mantGridView = mantGrid.create();
me.add( mantGridView );
me.setActiveTab(mantGridView);
}
}

});




//@charset UTF-8

Ext.define('esiacademia.view.recursosMateriales.mantGrid', {
extend: 'esiacademia.view.gridBase',
alias : 'widget.esiacademia_view_recursosMateriales_mantGrid',

requires: [
'Ext.Action',
'Ext.toolbar.Paging',
'Cesi.ux.PagingToolbarResizer',
'Ext.menu.Menu',
'Ext.grid.feature.GroupingSummary',
'Ext.grid.column.Date',
'Ext.toolbar.Toolbar',
'Ext.toolbar.Separator',
'Ext.toolbar.Fill',
'Ext.data.Store',
'Cesi.ux.form.field.TriggerBuscar'
],

name: 'gridRecursosMateriales',
title: gt.dgettext('esiacademia','Lista'),
closable: false,
viewConfig: {
stripeRows: false
},

initComponent: function() {
var me = this;

// Añadir eventos para hacer un fireEvent desde los botones Action, puesto que los Action no encajan bien en el
// modelo MVC
me.addEvents("nuevo","editar","borrar");

// ---------------------------------------------------------------
// Definir boton accion NUEVO
// ---------------------------------------------------------------
me.nuevoAction = Ext.create('Ext.Action', {
name: 'nuevoAction',
iconCls: 'icono_nuevo',
text: gt.dgettext('esiacademia','Nuevo Recurso'),
disabled: false,
handler: function(){
// Lanzar evento 'nuevo' del grid pasando el action y el propio grid
this.fireEvent("nuevo", this.nuevoAction, this);
},
scope: me
});

// ---------------------------------------------------------------
// Definir boton accion EDITAR
// ---------------------------------------------------------------
me.editarAction = Ext.create('Ext.Action', {
name: 'editarAction',
iconCls: 'icono_editar',
text: gt.dgettext('esiacademia','Editar Recurso'),
disabled: true,
handler: function(){
// Lanzar evento editar del grid pasando el action y el propio grid
this.fireEvent("editar", this.editarAction, this );
},
scope: me
});

// ---------------------------------------------------------------
// Definir boton accion BORRAR
// ---------------------------------------------------------------
me.borrarAction = Ext.create('Ext.Action', {
name: 'borrarAction',
iconCls: 'icono_borrar',
text: gt.dgettext('esiacademia','Borrar Recurso'),
disabled: true,
handler: function(){
// Lanzar evento 'borrar' del grid pasando el action y el propio grid
this.fireEvent("borrar", this.borrarAction, this);
},
scope: me
});

// ---------------------------------------------------------------
// Definir texfield BUSCAR
// ---------------------------------------------------------------
me.campoBuscar = Ext.create('Cesi.ux.form.field.TriggerBuscar', {
name: 'campoBuscar',
width: 200
});

// ---------------------------------------------------------------
// Menu grid boton derecho
// ---------------------------------------------------------------
me.menuContextualGrid = Ext.create('Ext.menu.Menu', {
name: 'menuContextualGrid',
items: [
me.nuevoAction,
'-',
me.editarAction,
me.borrarAction
]
});


// ---------------------------------------------------------------
// Asignar el store (Forzamos la creación de un objeto nuevo)
// ---------------------------------------------------------------
me.store = Ext.create('esiacademia.store.recursosMateriales', {
storeId: Ext.id()
});

// ---------------------------------------------------------------
// Definir ventana para el ESCOGE de Contacto cuando editamos
// Se puede definir aqui en el grid en lugar del mantenimiento
// porque es una ventana modal y no puede estar el escoge abierto
// 2 veces a la vez
// ---------------------------------------------------------------
me.windowEscogeContacto = esiacademiaApp.getView('contactos.escoge').create();

// ---------------------------------------------------------------
// Barra paginacion de la lista
// ---------------------------------------------------------------
me.paginacionGrid = Ext.create('Ext.PagingToolbar', {
name: 'paginacionGrid',
store: me.store,
displayInfo: true,
pageSize: 50,
//plugins : [Ext.create('Cesi.ux.PagingToolbarResizer', {options : [ 50, 100, 500 ] })]
plugins : [Ext.create('Cesi.ux.PagingToolbarResizer')]
});

// ---------------------------------------------------------------
// Agrupar por una columna
// ---------------------------------------------------------------
me.groupingGrid = Ext.create('Ext.grid.feature.GroupingSummary',{
name: 'groupingGrid',
groupByText: gt.dgettext('esiacademia','Agrupar por esta columna'),
showGroupsText: gt.dgettext('esiacademia','Mostrar agrupaciones'),
groupHeaderTpl: '{columnName}: {name} ( {rows.length} ' + gt.dgettext('esiacademia','regs.') + ' )',
hideGroupedHeader: false,
enableGroupingMenu: true,
enableNoGroups: true
});

me.features = [ me.groupingGrid ];


// ---------------------------------------------------------------
// Definir las columnas
// ---------------------------------------------------------------
me.columns = [
{text: gt.dgettext('esiacademia','Id'), width: 50, dataIndex: 'recmat_id', hidden: false, sortable: true, groupable: false },
{text: gt.dgettext('esiacademia','Guid'), width: 75, dataIndex: 'recmat_guid', hidden: true, sortable: true, groupable: false },
{text: gt.dgettext('esiacademia','Activo'), width: 45, dataIndex: 'recmat_activo', sortable: true, align:'center', groupable: false,
renderer: function(val, metaData, record, rowIndex, colIndex, store, view) {
return (val ? gt.dgettext('esiacademia','Si') : gt.dgettext('esiacademia','No'));
}
},
{text: gt.dgettext('esiacademia','Código'), width: 100, dataIndex: 'recmat_codigo', sortable: true, groupable: false },
{text: gt.dgettext('esiacademia','Nombre'), width: 300, dataIndex: 'recmat_nombre', sortable: true, groupable: false },
{text: gt.dgettext('esiacademia','Pertenece empresa / contacto'), width: 200, dataIndex: 'recmat_pertenece_cto_nombre_fiscal', sortable: true, groupable: true, groupable: true },
{text: gt.dgettext('esiacademia','Fec.Alta'), width: 110, dataIndex: 'recmat_fechaalta', sortable: true, xtype: 'datecolumn', format: 'd-m-Y H:i', groupable: false },
{text: gt.dgettext('esiacademia','Fec.Mod.'), width: 110, dataIndex: 'recmat_fechamod', sortable: true, xtype: 'datecolumn', format: 'd-m-Y H:i', groupable: false }
];

// barra botones superior
me.dockedItems = [{
xtype: 'toolbar',
items: [
me.nuevoAction,
me.editarAction,
me.borrarAction,
{ xtype:'tbfill' },
me.campoBuscar
]
}];

// barra paginacion inferior
me.bbar = me.paginacionGrid;

// Ejecutar metodo de su parent
me.callParent(arguments);

// Cargar el store asociado
me.store.load();
}

});




//@charset UTF-8

Ext.define('esiacademia.view.recursosMateriales.mantEdicion', {
extend: 'esiacademia.view.panelBase',
alias : 'widget.esiacademia_view_recursosMateriales_mantEdicion',

requires: [
'Ext.Action',
'Ext.panel.Panel',
'Ext.form.Panel',
'Ext.container.Container',
'Ext.form.FieldContainer',
'Ext.form.field.ComboBox',
'Ext.form.field.Checkbox',
'Ext.form.field.TextArea',
'Ext.form.field.Display',
'Ext.form.field.Hidden',
'Ext.form.field.Date',
'Ext.form.FieldSet',
'Ext.layout.container.Column',
'Ext.layout.container.Anchor',
'Ext.layout.container.HBox',
'Ext.layout.container.VBox',
'Ext.layout.container.Box',
'Ext.layout.container.Border',
'Ext.toolbar.Toolbar',
'Ext.toolbar.Separator',
'Ext.toolbar.Fill',
'Ext.toolbar.TextItem',
'Ext.button.Button',
'Ext.Img',
'Cesi.ux.form.field.Text',
'Cesi.ux.form.field.TriggerEscoge',
'Cesi.ux.form.field.BoxSelect'
],

name: 'edicionRecursosMateriales',
title: gt.dgettext('esiacademia','Editar'),
closable: false,
layout: 'fit',

modo: null,
registroAsociado: null,
gridAsociado: null,
storeAsociado: null,
windowEscogeContactoAsociado: null,

initComponent: function() {
var me = this;

if (me.registroAsociado) {
if (me.modo=='nuevo') {
me.title = gt.dgettext('esiacademia','Nuevo');
} else {
me.title = gt.dgettext('esiacademia','Id') + ' ' + me.registroAsociado.get('recmat_id') + ' : ' +Ext.String.ellipsis(me.registroAsociado.get('recmat_nombre'),25,true);
}
}
if (me.gridAsociado) {
me.storeAsociado = me.gridAsociado.getStore();
me.windowEscogeContactoAsociado = me.gridAsociado.windowEscogeContacto;
}

// Añadir eventos para hacer un fireEvent desde los botones Action, puesto que los Action no encajan bien en el
// modelo MVC
me.addEvents("guardar","cancelar");

// ---------------------------------------------------------------
// Definir boton accion GUARDAR y VOLVER
// ---------------------------------------------------------------
me.guardarAction = Ext.create('Ext.Action', {
name: 'guardarAction',
iconCls: 'icono_guardar',
text: gt.dgettext('esiacademia','Guardar'),
disabled: false,
handler: function(){
// Lanzar evento 'guardar' del panel edicion pasando el action y el propio panel
this.fireEvent("guardar", this.guardarAction, this);
},
scope: me
});

// ---------------------------------------------------------------
// Definir boton accion CANCELAR
// ---------------------------------------------------------------
me.cancelarAction = Ext.create('Ext.Action', {
name: 'cancelarAction',
iconCls: 'icono_cancelar',
text: gt.dgettext('esiacademia','Cancelar'),
disabled: false,
handler: function(){
// Lanzar evento 'cancelar' del panel edicion pasando el action y el propio panel
this.fireEvent("cancelar", this.cancelarAction, this);
},
scope: me
});

// barra botones superior
me.dockedItems = [{
xtype: 'toolbar',
items: [
me.guardarAction,
me.cancelarAction
]
}];


// ---------------------------------------------------------------
// Formulario para editar los registros
// ---------------------------------------------------------------
me.formularioRecursosMateriales = Ext.create('Ext.form.Panel', {
name: 'formularioRecursosMateriales',
border: false,
bodyPadding: 10,
hidden: false,
autoScroll: true,
fieldDefaults: {
labelAlign: 'left',
labelWidth: 125,
labelStyle: 'font-weight:bold'
},
defaults: {
margins: '0 0 5 0'
},
layout: 'fit',

//
// Campos del formulario
//
items: [{
xtype: 'panel',
title: '',
border: false,
layout: 'border',

items: [{
xtype: 'panel',
border: false,
layout: 'anchor',
region: 'north',
items: [{
xtype: 'fieldset',
title: gt.dgettext('esiacademia','Datos generales'),
collapsible: true,
padding: '5 5 0 5',
items: [{
xtype: 'panel',
border: false,
layout: 'column',

items: [{

// Columna izquierda
xtype: 'container',
columnWidth:.75,
layout: 'anchor',

items: [{
xtype: 'fieldcontainer',
fieldLabel: '',
labelSeparator: '',
labelWidth: 0,
layout: {
type: 'hbox',
pack: 'ratio',
align: 'top'
},
anchor: '100%',
defaultType: 'cesitextfield',
fieldDefaults: {
labelAlign: 'right',
labelStyle: 'font-weight:bold;'
},
defaults: {
margins: '0 5 0 0'
},

items: [{
name: 'recmat_codigo',
xtype: 'cesitextfield',
fieldLabel: gt.dgettext('esicontactos','Código'),
labelAlign: 'left',
labelWidth: 125,
forzarMayusculas: true,
enforceMaxLength: true,
maxLength: 10,
flex: 1,
fieldCls: 'campo_obligatorio',
vtype: 'alphanum',
allowBlank: false
}, {
name: 'recmat_nombre',
xtype: 'cesitextfield',
fieldLabel: gt.dgettext('esicontactos','Nombre'),
labelAlign: 'right',
labelWidth: 75,
forzarMayusculas: true,
enforceMaxLength: true,
maxLength: 100,
flex: 1.5,
fieldCls: 'campo_obligatorio',
allowBlank: false
}, {
name: 'recmat_activo',
xtype: 'checkboxfield',
fieldLabel: gt.dgettext('esiacademia','Activo'),
labelWidth: 50,
boxLabel: '',
margins: '0 0 0 0',
inputValue: true,
uncheckedValue: false, // Si no definimos uncheckedValue solo se incluye en el getValues del form si esta chequedado.
tabIndex: -1 // Para que el cambio de campos con tabulador no se pare aqui
}]

},{
xtype: 'fieldcontainer',
fieldLabel: '',
labelSeparator: '',
labelWidth: 0,
layout: {
type: 'hbox',
pack: 'start',
align: 'top'
},
anchor: '100%',
defaultType: 'cesitextfield',
fieldDefaults: {
labelAlign: 'right',
labelStyle: 'font-weight:bold;'
},
defaults: {
margins: '0 5 0 0'
},

items: [{
name: 'escoge_pertenece_cto_nombre_fiscal',
xtype: 'cesifieldtriggerescoge',
fieldLabel: gt.dgettext('esiacademia','Empresa/Contacto'),
labelAlign: 'left',
labelWidth: 125,
width: 350,
windowEscoge: me.windowEscogeContactoAsociado,
emptyText: gt.dgettext('esiacademia','Introduzca id o nombre'),
allowBlank: true,
forzarMayusculas: false,
submitValue: true
}, {
name: 'recmat_pertenece_contacto_id',
xtype: 'hiddenfield',
submitValue: true
}]
}]


}, {
// Columna derecha
xtype: 'container',
columnWidth:.25,
layout: 'anchor'
}]

}]
}]

// Parte inferior
},{
xtype: 'panel',
border: false,
region: 'center',
layout: 'fit',
items: [{

// Tab panel de subfichas
name: 'recursosMateriales_tabpanel_subfichas',
xtype: 'esiacademia_view_tabpanelBase',
title: '',
border: true,
closable: false,
defaults: {
padding: '5 5 5 5',
layout: 'anchor'
},
items: [{

// Panel informacion de registro
xtype: 'esiacademia_view_panelSubfichaBase',
title: ' ', // Dejar un espacio en blanco en el titulo.
iconCls: 'icono_informacion',
items: [{
name: 'recmat_id',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Id')
}, {
name: 'recmat_guid',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Guid')
}, {
name: 'recmat_fechaalta',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Creada'),
renderer: function(value) {
return Ext.util.Format.date(value,'d-m-Y H:i:s')
}
}, {
name: 'recmat_usralta_nombre',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Creada por')
}, {
name: 'recmat_fechamod',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Modificada'),
renderer: function(value) {
return Ext.util.Format.date(value,'d-m-Y H:i:s')
}
}, {
name: 'recmat_usrmod_nombre',
xtype: 'displayfield',
anchor: '100%',
fieldLabel: gt.dgettext('esiacademia','Modif. por')
}]
}]
}]
}]
}]
});


me.items = [
me.formularioRecursosMateriales
];


//
// Ejecutar metodo de su parent
//
me.callParent(arguments);

// Cargar registro en el formulario si vamos a editar
if (me.registroAsociado) {
// Para cargar un solo valor registro.get('nombrecampo')); getForm.findField('aa').setValue()
me.formularioRecursosMateriales.getForm().reset();
me.formularioRecursosMateriales.getForm().loadRecord(me.registroAsociado);
// Cargar campos escoge
me.formularioRecursosMateriales.getForm().findField('escoge_pertenece_cto_nombre_fiscal').setValue( me.registroAsociado.get('recmat_pertenece_cto_nombre_fiscal') );
// Se le pone el originalValue para que marque bien el dirty al hacer las comprovaciones del valor entrado.
me.formularioRecursosMateriales.getForm().findField('escoge_pertenece_cto_nombre_fiscal').originalValue = me.registroAsociado.get('recmat_pertenece_cto_nombre_fiscal');
}

// Valores iniciales si es "nuevo"
if (me.modo=="nuevo") {
me.formularioRecursosMateriales.getForm().findField('recmat_activo').setValue(true);
// Cargar campos escoge
me.formularioRecursosMateriales.getForm().findField('escoge_pertenece_cto_nombre_fiscal').setValue('');
// Se le pone el originalValue para que marque bien el dirty al hacer las comprovaciones del valor entrado.
me.formularioRecursosMateriales.getForm().findField('escoge_pertenece_cto_nombre_fiscal').originalValue = null;
me.formularioRecursosMateriales.getForm().findField('recmat_pertenece_contacto_id').originalValue = null;
}

}

});


the controller
Look at "onGuardarAction" method, wich is where i save the record.



//@charset UTF-8

Ext.define('esiacademia.controller.recursosMateriales.mant', {
extend: 'Ext.app.Controller',

models:[
'recursoMaterial'
],
stores:[
'recursosMateriales'
],
views: [
'recursosMateriales.mant',
'recursosMateriales.mantGrid',
'recursosMateriales.mantEdicion'
],

init: function() {
var me = this;
me.control({
'esiacademia_view_recursosMateriales_mant' : {
beforedestroy: me.onBeforedestroy
},
'esiacademia_view_recursosMateriales_mant esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]' : {
itemdblclick: me.onItemdblclick,
itemcontextmenu: me.onItemcontextmenu,
selectionchange: me.onSelectionchange,
nuevo: me.onNuevoAction,
editar: me.onEditarAction,
borrar: me.onBorrarAction
},
'esiacademia_view_recursosMateriales_mant esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"] [name="campoBuscar"]' : {
trigger1click: me.onBuscar,
trigger2click: me.onBuscarClear
},
'esiacademia_view_recursosMateriales_mantEdicion' : {
afterrender: me.onRenderFichaEdicion,
guardar: me.onGuardarAction,
cancelar: me.onCancelarAction
},
'esiacademia_view_recursosMateriales_mantEdicion cesifieldtriggerescoge[name="escoge_pertenece_cto_nombre_fiscal"]' : {
blur: me.onBlurCampoEscogeContacto,
premostrarescoge: me.onPremostrarescogeContacto,
escogecompletado: me.onCampoEscogeContactoCompletado
}
});
},

refs: [
],


//
// Se ejecuta cuando se produce el render de la ficha de edicion
// Es decir justo cuando se ha acabado de cargar y dibujar el control (render)
// Inicializamos valores de campos, mostrar/ocultar campos dinamicamente, etc...
//
onRenderFichaEdicion: function( panelEdicion, eOpts) {
var me = this;
// Procesos que se realizan cuando se acaba de crear/dibujar el control de edicion de la ficha de mantenimiento
},

//
// Al seleccionar/desseleccionar un registro(s)
//
onSelectionchange: function( sm, selections, eOpts) {
var editarAction = sm.view.up().editarAction;
var borrarAction = sm.view.up().borrarAction;
if (selections.length==1) {
editarAction.enable();
borrarAction.enable();
} else {
editarAction.disable();
borrarAction.disable();
}
},

//
// Al destruir el grid (es decir al cerrar)
//
onBeforedestroy: function(widget,eOpts) {
var me = this;
var grid = widget.down('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
if (grid) {
// Destruir el store que hemos creado con ID aleatorio para liberar espacio en memoria
var store = Ext.data.StoreManager.lookup(grid.store.storeId);
if (store) {
if (!store.isDestroyed) {
if (store.storeId) {
store.destroyStore();
Ext.data.StoreManager.unregister(store);
}
}
}
// Destruir menu contextual del grid si lo hay
if (grid.menuContextualGrid) {
grid.menuContextualGrid.destroy();
}
}
},

//
// Doble click en un registro para editar.
//
onItemdblclick: function(dv, registro, item, index, e) {
var me = this;
var grid = dv.up();
var editarAction = grid.editarAction;
if (!editarAction.isDisabled()) {
me.refrescarEditarRegistro('editar',registro,grid);
}
},

//
// Menu contextual grid
//
onItemcontextmenu: function(view, rec, node, index, e) {
var me = this;
e.stopEvent();

var grid = view.up('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
grid.menuContextualGrid.showAt(e.getXY());
return false;
},

//
// Nuevo registro
//
onNuevoAction: function(action, grid, event, eOpts) {
var me = this;

var store = grid.getStore();
var registro = Ext.create(esiacademiaApp.getModel('recursoMaterial'));
if (registro) {
me.editarRegistro('nuevo',registro,grid);
} else {
Ext.Msg.show({
title: gt.dgettext('esiacademia','Nuevo'),
msg: gt.dgettext('esiacademia','No se puede crear un nuevo registro.'),
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}
},

//
// Editar registro
//
onEditarAction: function(action, grid, event, eOpts) {
var me = this;
var registro = grid.getSelectionModel().getSelection()[0];
if (registro) {
me.refrescarEditarRegistro('editar',registro,grid);
} else {
Ext.Msg.show({
title: gt.dgettext('esiacademia','Editar'),
msg: gt.dgettext('esiacademia','Debe seleccionar un recurso para editar.'),
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}
},

//
// Borrar registro
//
onBorrarAction: function(action, grid, event, eOpts) {
var me = this;
var registro = grid.getSelectionModel().getSelection()[0];
if (registro) {
me.borrarRegistro(registro,grid);
} else {
Ext.Msg.show({
title: gt.dgettext('esiacademia','Editar'),
msg: gt.dgettext('esiacademia','Debe seleccionar un recurso para borrar.'),
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}
},

//
// Buscar
//
onBuscar: function(triggerBuscar, e) {
var me = this;
var grid = triggerBuscar.up('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
grid.getStore().getProxy().extraParams.buscar = triggerBuscar.getValue();
grid.paginacionGrid.moveFirst();
},

//
// Buscar (Clear)
//
onBuscarClear: function(triggerBuscar, e) {
var me = this;
var grid = triggerBuscar.up('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
triggerBuscar.setValue(null);
grid.getStore().getProxy().extraParams.buscar = null;
grid.paginacionGrid.moveFirst();
},

//
// Refrescar un registro ya existente y después Editar el registro
//
refrescarEditarRegistro: function( modo, registro, grid ) {
var me = this;

if (registro) {
if (grid) {

// Si lo tenemos configurado en esinube para no hacer los refrescos previos vamos directament a editar.
if (!esinubeConfig.cfg_av_refrescarRegEditar) {
// Ahora ya podemos editar el registro
me.editarRegistro( modo, registro, grid );
return true;
}

// Mostrar "cargando" en barra de estado
var mainStatusBar = grid.up('esiacademia_view_main').down('esiacademia_view_mainStatusBar[name="mainStatusBar"]');
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','Cargando registro para editar ... ') + registro.get('recmat_nombre'),
iconCls: 'x-status-busy',
clear: false
});
}

// Ejecutar funcion extdirect para refrescar el registro que corresponde al id solicitado para editar
// Asi cogemos los datos mas recientes para editar
Ext.esiacademiaDirect.esiacademia_recursosMateriales.getRecursosMateriales({
filter: [{
property: 'recmat_id',
value: registro.get('recmat_id')
}]

}, function(provider, response) {

// Cuando llega la respuesta asincrona, entonces editamos el registro si ha ido bien la carga del refresco
if (response.result.success) {
// Comprobar que nos llega el mismo id que hemos pedido
if (response.result.data[0]) {

// Limpiar barra de estado
if (mainStatusBar) {
mainStatusBar.clearStatus({ useDefaults:true, anim: false });
}

if (response.result.data[0].recmat_id==registro.get('recmat_id')) {
// Pasar los datos recibidos al registro que estaba en el store
registro.set( response.result.data[0] );
registro.commit();
// Ahora ya podemos editar el registro
me.editarRegistro( modo, registro, grid );
return true;
}
}
}

// Mostrar mensaje error
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','No se ha podido refrescar el registro antes de editar.'),
iconCls: 'x-status-error',
clear: {anim: false, useDefaults: true }
});
}

// Mostrar mensaje que no se puede editar el registro
Ext.Msg.show({
title: gt.dgettext('esiacademia','Editar'),
msg: gt.dgettext('esiacademia','No se ha podido refrescar el registro antes de editar.<br />Inténtelo dentro de unos segundos o vuelva a refrescar la lista.'),
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING,
fn: function(btn, text, opt) {
// nada
}
})
return false;
}
);
}
}
},

//
// Editar un registro
//
editarRegistro: function( modo, registro, grid ) {

if (registro) {
if (grid) {
var tabpanel = grid.up('esiacademia_view_recursosMateriales_mant');
if (tabpanel) {
// Crear la vista de edicion (formulario de edicion)
var mantEdicion = esiacademiaApp.getView('recursosMateriales.mantEdicion');
if (mantEdicion) {
// Comprobar si ya está abierta esta ficha por su idAsociado y simplemente mostrar
var fichasAbiertas = tabpanel.query('esiacademia_view_recursosMateriales_mantEdicion[name="edicionRecursosMateriales_id_'+registro.get('recmat_id').toString()+'"]');
if (modo!='nuevo' && fichasAbiertas.length > 0) {
// Mostrar la ficha ya abierta anteriormente
tabpanel.setActiveTab(fichasAbiertas[0]);
} else {

// Crear la vista de la ficha para editar
var mantEdicionView = mantEdicion.create({
name: 'edicionRecursosMateriales_id_' + registro.get('recmat_id').toString(),
modo: modo,
registroAsociado: registro,
gridAsociado: grid
});
// Abrir opcion en un nuevo tab
tabpanel.add( mantEdicionView );
tabpanel.setActiveTab( mantEdicionView );
}
}
}

}
}
},

//
// Borrar un registro
//
borrarRegistro: function( registro, grid ) {
if (registro) {
Ext.Msg.show({
title: gt.dgettext('esiacademia','Borrar'),
msg: gt.dgettext('esiacademia','Seguro que desea borrar el recurso') + '<br />' +registro.get('recmat_nombre'),
buttons: Ext.Msg.YESNO,
icon: Ext.MessageBox.QUESTION,
fn: function(btn, text, opt) {
if (btn == 'yes') {

// Mostrar "cargando" en barra de estado
var mainStatusBar = grid.up('esiacademia_view_main').down('esiacademia_view_mainStatusBar[name="mainStatusBar"]');
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','Borrando registro ... ') + registro.get('recmat_nombre'),
iconCls: 'x-status-busy',
clear: false
});
}
// Mostrar mascara
grid.getEl().mask(gt.dgettext('esiacademia','Borrando registro...'));

// asignar el proxy direct del store actual al registro
registro.setProxy(grid.getStore().getProxy());
// Borrar el registro a través del proxy con extdirect
registro.destroy({
// Procesar respuesta
callback: function( records, operation) {
grid.getEl().unmask();

if (operation.wasSuccessful()) {
// Limpiar barra de estado
if (mainStatusBar) {
mainStatusBar.clearStatus({ useDefaults:true, anim: false });
}
//console.log(operation.response);
//console.log(operation.response.result);
// Borrar correcto. Quitar el registro del store
grid.getStore().remove( records );
// Forzar cambios hechos en el store para que no lanze operaciones con el registro borrado
grid.getStore().commitChanges();
} else {
// Error al borrar
if (operation.hasException()) {
var mensajeError = operation.getError();
var iconoError = Ext.Msg.WARNING
} else {
var mensajeError = gt.dgettext('esiacademia','Error: No se ha podido borrar el registro.');
var iconoError = Ext.Msg.ERROR
}

// Mostrar mensaje error
if (mainStatusBar) {
mainStatusBar.setStatus({
text: mensajeError,
iconCls: 'x-status-error',
//clear: true
clear: { useDefaults:true, anim: false }
});
}

Ext.Msg.show({
title: gt.dgettext('esiacademia','Borrar'),
msg: mensajeError,
buttons: Ext.Msg.OK,
icon: iconoError
});
}
}
});
}
}
})
}
},

//
// Cancelar y volver
//
onCancelarAction: function(action, panelEdicion, event, eOpts) {
var formulario = panelEdicion.formularioRecursosMateriales;
var registro = formulario.getRecord()
if (panelEdicion.modo=="nuevo") {
// Eliminar registro creado porque aun no se ha agregado al store
registro = null;
} else if (panelEdicion.modo=="editar") {
// Volver a poner los campos modificados tal como estaban al principio
registro.set( registro.modified );
// Forzar cambios hechos en el store para que no lanze operaciones con el registro tocado
panelEdicion.storeAsociado.commitChanges();
}
// Cerrar el panel de edicion de este registro
Ext.destroy(panelEdicion);
},

//
// Guardar registro
//
onGuardarAction: function(action, panelEdicion, event, eOpts) {
var me = this;
var formulario = panelEdicion.formularioRecursosMateriales;
var registro = formulario.getRecord();
var mainStatusBar = panelEdicion.up('esiacademia_view_main').down('esiacademia_view_mainStatusBar[name="mainStatusBar"]');
var grid = panelEdicion.up('esiacademia_view_recursosMateriales_mant').down('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');

if (formulario && panelEdicion.storeAsociado) {


// Revisar campos del formulario: obligatorios, etc...)
if (!formulario.getForm().isValid()) {
var camposFormulario = formulario.getForm().getFields();
var cadenaLabelsRevisar = '';
camposFormulario.each(function(campo, index, len) {
if (!campo.validate()) {
var etiqueta = '';
etiqueta = etiqueta + (campo.up('esiacademia_view_panelSubfichaBase',2) ? ' (' + gt.dgettext('esiacademia_view_panelSubfichaBase','Pestaña:') + ' ' + campo.up('esiacademia_view_panelSubfichaBase',2).title +') ' : '');
etiqueta = etiqueta + (campo.up('fieldcontainer',1) ? ' ' + campo.up('fieldcontainer',1).fieldLabel : '');
if (campo.fieldLabel) {
etiqueta = etiqueta + (Ext.String.trim(etiqueta).length>0 ? ' - ' : ' ') + (campo.fieldLabel ? campo.fieldLabel : '');
}
cadenaLabelsRevisar = cadenaLabelsRevisar + (cadenaLabelsRevisar.length>0 ? ', ' : '') + etiqueta;
}
});
Ext.Msg.show({
title: gt.dgettext('esiacademia','Guardar'),
msg: gt.dgettext('esiacademia','Revise los siguientes campos: ')+cadenaLabelsRevisar,
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
return false;
}

// Cargar valores del formulario en el registro asociado
registro.set( formulario.getValues() );
// asignar el proxy direct del store actual al registro
registro.setProxy(panelEdicion.storeAsociado.getProxy());

// Mostrar "guardando" en barra de estado
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','Guardando registro ... ') + registro.get('recmat_nombre'),
iconCls: 'x-status-busy',
clear: false
});
}
// Mostrar mascara
panelEdicion.getEl().mask(gt.dgettext('esiacademia','Guardando...'));

//
// Iniciar proceso multiguardar
// Básicamente gestiona flags para guardar la ficha principal, subficha direcciones, upload foto, etc...
// Asi se pueden ejecutar los procesos de guardar asincronos de forma consecutiva si van bien
// y al final cerrar el formulario de edicion o dejarlo si hay errores en cualquier guardar.
//
me.clearMultiGuardar();

// Guardar registro a traves del proxy direct
registro.save({
// Procesar respuesta
callback: function( records, operation) {
panelEdicion.getEl().unmask();

if (operation.wasSuccessful()) {
// Limpiar barra de estado
if (mainStatusBar) {
mainStatusBar.clearStatus({ useDefaults:true, anim: false });
}
//console.log(operation.response);
//console.log(operation.response.result);
// Guardar correcto
if ( panelEdicion.modo=="nuevo" ) {
// Agregar el registro al store si se ha guardado bien y no existe ya.
if ( panelEdicion.storeAsociado.findExact('recmat_id',registro.get('recmat_id')) == -1) {
//panelEdicion.storeAsociado.insert(0,registro);
panelEdicion.storeAsociado.add(registro);
}
// Forzar cambios hechos en el store para que no lanze operaciones con el registro agregado
panelEdicion.storeAsociado.commitChanges();
}

// Informar que hemos completado correctamente la tarea de guardar ficha para el multiguardar
me.completarMultiGuardar(registro, panelEdicion, 'ficha', true);

} else {
// Error al guardar
if (operation.hasException()) {
var mensajeError = operation.getError();
var iconoError = Ext.Msg.WARNING
} else {
var mensajeError = gt.dgettext('esiacademia','Error: No se han podido guardar los datos.');
var iconoError = Ext.Msg.ERROR
}

// Mostrar mensaje error
if (mainStatusBar) {
mainStatusBar.setStatus({
text: mensajeError,
iconCls: 'x-status-error',
//clear: true
clear: { useDefaults:true, anim: false }
});
}

Ext.Msg.show({
title: gt.dgettext('esiacademia','Guardar'),
msg: mensajeError,
buttons: Ext.Msg.OK,
icon: iconoError
});

// Informar que hemos completado con errores la tarea de guardar ficha para el multiguardar
me.completarMultiGuardar(registro, panelEdicion, 'ficha', false);

}
}
});
}
},

//
// Iniciar proceso multiguardar
// Básicamente gestiona flags para guardar la ficha principal, subficha direcciones, upload foto, etc...
// Asi se pueden ejecutar los procesos de guardar asincronos de forma consecutiva si van bien
// y al final cerrar el formulario de edicion o dejarlo si hay errores en cualquier guardar.
//
procesosMultiGuardar: {
ficha: {
completada: false,
success: false
}
},

// Reiniciar proceso de guardar poniendo flags en limpio
clearMultiGuardar: function() {
var me= this;

me.procesosMultiGuardar.ficha = { completada: false, success: false };
},

// Completar cada fase del multiguardar y lanzar la siguiente
completarMultiGuardar: function(registro, panelEdicion, proceso, success) {
var me= this;
var grid = panelEdicion.up('esiacademia_view_recursosMateriales_mant').down('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');

switch (proceso.toLowerCase()) {
case 'ficha':
me.procesosMultiGuardar.ficha = { completada: true, success: success };
if (success) {
// Si se ha guardado bien la ficha, cerramos el panel de edicion
// Cerrar el panel de edicion
Ext.destroy(panelEdicion);
// Dejamos seleccionado en la lista el registro que acabamos de guardar
grid.getSelectionModel().deselectAll( false );
if ( grid.getStore().findRecord('recmat_id',registro.get('recmat_id'),0,false,false,true) ) {
grid.getSelectionModel().select([registro], false, false);
}
}
break;
}

},

//
// Al salir del campo para escoger el contacto id asociado
//
onBlurCampoEscogeContacto: function(field, event, eOpts) {
var me = this;
var panelEdicion = field.up('esiacademia_view_recursosMateriales_mantEdicion');
var mainStatusBar = panelEdicion.up('esiacademia_view_main').down('esiacademia_view_mainStatusBar[name="mainStatusBar"]');
var grid = field.up('esiacademia_view_recursosMateriales_mant').down('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
var windowEscogeContacto = grid.windowEscogeContacto;
var escoge_cto_nombre_fiscal = field.getValue();

if (field.isDirty() && !(windowEscogeContacto.isVisible()) ) {

// Si el campo esta vacio limpiamos campos asociados
if (Ext.String.trim(escoge_cto_nombre_fiscal).length<=0) {
me.aplicarContactoEscogido(field, null);
return;
}

// Comprobar si el usuario ha introducido directamente un numero (esta buscando por id)
// o palabras (le lanzaremos la ventana el escoge)
if ( parseInt(Ext.String.trim(escoge_cto_nombre_fiscal)).toString() != Ext.String.trim(escoge_cto_nombre_fiscal) ) {
// No hay un numero
field.lanzarEscoge();
return;
}

// Mostrar "comprobando" en barra de estado
if (mainStatusBar) {
mainStatusBar.setStatus({
text: gt.dgettext('esiacademia','Comprobando contacto ... '),
iconCls: 'x-status-busy',
clear: false
});
}
// Mostrar mascara
panelEdicion.getEl().mask(gt.dgettext('esiacademia','Comprobando contacto...'));

// Ejecutar funcion del extdirect para comprobar el contacto por id introducido.
Ext.esiacademiaDirect.esiacademia_contactos.getContactos({
filter: [{
property: 'cto_id',
value: parseInt(Ext.String.trim(escoge_cto_nombre_fiscal))
}]

}, function(response, event) {

// Limpiar mask pantalla
panelEdicion.getEl().unmask();

// Si la comprobación ha funcionado y ha devuelto uno y solo un registro.
if (response.success && response.data.length==1) {

// Limpiar barra de estado
if (mainStatusBar) {
mainStatusBar.clearStatus({ useDefaults:true, anim: false });
}

// Comprobar si esta activo o no
if (response.data[0].cto_activo!=true) {
// Mostrar mensaje error
Ext.Msg.show({
title: gt.dgettext('esiacademia','Comprobación contacto asociado'),
msg: gt.dgettext('esiacademia','El contacto seleccionado está inactivo.') + '<br />' + response.data[0].cto_nombre_fiscal,
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
} else {
// Correcto
// Asignar cambios del formulario
me.aplicarContactoEscogido(field, response.data[0]);
return;
}

} else {

var mensajeError = gt.dgettext('esiacademia','No se ha encontrado ningún contacto activo con el id: ') + parseInt(Ext.String.trim(escoge_cto_nombre_fiscal)).toString();

// Mostrar mensaje error
if (mainStatusBar) {
mainStatusBar.setStatus({
text: mensajeError,
iconCls: 'x-status-error',
//clear: true
clear: { useDefaults:true, anim: false }
});
}

// Mostrar mensaje error
Ext.Msg.show({
title: gt.dgettext('esiacademia','Comprobación contacto asociado'),
msg:mensajeError,
buttons: Ext.Msg.OK,
icon: Ext.Msg.WARNING
});
}

// Asignar cambios del formulario (passando null para limpiar)
me.aplicarContactoEscogido(field, null);

});
}
},

//
// Antes de mostrar el escoge para el contacto
//
onPremostrarescogeContacto: function(field, e) {
var me = this;
var grid = field.up('esiacademia_view_recursosMateriales_mant').down('esiacademia_view_recursosMateriales_mantGrid[name="gridRecursosMateriales"]');
var windowEscogeContacto = grid.windowEscogeContacto;

// Aplicar valores de distinta manera en funcion de que campo de contacto tratamos
switch (field.name) {
// Campo contacto pertenece a
case 'escoge_pertenece_cto_nombre_fiscal':
windowEscogeContacto.setFiltrarVinculos('empresas');
break;
// resto sin filtrar
default:
windowEscogeContacto.setFiltrarVinculos('todos');
break;
}
return true;
},

//
// Evento que se dispara desde la ventana de escoge una vez se ha seleccionado un registro.
//
onCampoEscogeContactoCompletado: function(campoLanzador, ventanaEscoge, registro) {
var me = this;
// Aplicar valores
me.aplicarContactoEscogido( campoLanzador, registro );
},

//
// Una vez escogido un contacto por el escoge o por su id, cargar el resto de campos vinculados al contacto del formulario
//
aplicarContactoEscogido: function(campoLanzador, datos) {
var me = this;
var panelEdicionRecursosMateriales = campoLanzador.up('esiacademia_view_recursosMateriales_mantEdicion');

if (panelEdicionRecursosMateriales) {
var formularioRecursosMateriales = panelEdicionRecursosMateriales.formularioRecursosMateriales;

if (formularioRecursosMateriales) {
var formBasic = formularioRecursosMateriales.getForm();

// Aplicar valores de distinta manera en funcion de que campo de contacto tratamos
switch (campoLanzador.name) {

// Campo contacto pertenece a
case 'escoge_pertenece_cto_nombre_fiscal':
if (datos==null) {
// Si el registro es null, limpiar campos
formBasic.findField('recmat_pertenece_contacto_id').setValue(null);
formBasic.findField('escoge_pertenece_cto_nombre_fiscal').setValue(null);
// Se le pone el originalValue al campo lanzador para que marque bien el dirty al hacer las comprovaciones del valor entrado.
formBasic.findField('escoge_pertenece_cto_nombre_fiscal').originalValue = null;

} else {

// Si los datos vienen como un registro (data.model) lo convertimos a objeto puro
if (datos instanceof Ext.data.Model) {
var datosContacto = datos.getData();
} else {
// Si ya viene como un objeto lo dejamos igual
var datosContacto = datos;
}
// Cargar valores en los campos del formulario
formBasic.findField('recmat_pertenece_contacto_id').setValue( datosContacto.cto_id );
formBasic.findField('escoge_pertenece_cto_nombre_fiscal').setValue( datosContacto.cto_nombre_fiscal );
// Se le pone el originalValue al campo lanzador para que marque bien el dirty al hacer las comprovaciones del valor entrado.
formBasic.findField('escoge_pertenece_cto_nombre_fiscal').originalValue = datosContacto.cto_nombre_fiscal;
}
break;
}

}
}
}

});


The server side PHP code


<?php

/**
* @copyright Informàtica i Comunicacions CESI SL
* @version 1
* @author Manel Juarez
*/

class esiacademia_recursosMateriales
{

/**
* Obtener todos los recursosMateriales
* @remotable
*/
public static function getRecursosMateriales($_parametros)
{

// Si se recibe el parametro por ExtDirect con paramAsHash=true viene como un objeto
if (is_object($_parametros)) {
if (isset($_parametros->buscar)) {
$_buscar = trim($_parametros->buscar);
}
if (isset($_parametros->query)) {
// Això ho passa un boxselect si demanes filtratge de la llista (Cadena que escriu l'usuari al combo)
$_query = trim($_parametros->query);
$_buscar = (empty($_buscar) ? "" : " ") . $_query;
}
if (isset($_parametros->group[0])) {
$_agruparPor = $_parametros->group[0]->property;
$_agruparDir = $_parametros->group[0]->direction;
}
if (isset($_parametros->sort[0])) {
$_ordenarPor = $_parametros->sort[0]->property;
$_ordenarDir = $_parametros->sort[0]->direction;
}
if (isset($_parametros->filter)) {
$_filter = $_parametros->filter;
}
$_offset = $_parametros->start;
$_limit = $_parametros->limit;
}

// conexion a la base de datos
$esiacademiaConexionBD = esiacademia_conexion::getInstancia()->conexionBD;
$esiacademiaPrefijoTablas = esiacademia_conexion::esiacademiaPrefijoTablas;
$esinubePrefijoTablas = esinube_conexion::esinubePrefijoTablas;
$esicontactosPrefijoTablas = esicontactos_conexion::esicontactosPrefijoTablas;

if (!$esiacademiaConexionBD) {
$respuesta['success'] = true;
$respuesta['data'] = NULL;
return $respuesta;
}

// Orden por defecto
$campo_ordenar = 'recmat_nombre';
$sentido_ordenar = 'asc';
// Orden pedido por usuario
if (isset($_ordenarPor) && isset($_ordenarDir)) {
switch ($_ordenarPor) {
case 'recmat_usralta_nombre':
$campo_ordenar = 'x.cto_nombre_fiscal';
break;
case 'recmat_usrmod_nombre':
$campo_ordenar = 'x.cto_nombre_fiscal';
break;
case 'recmat_pertenece_contacto_id':
$campo_ordenar='c.cto_nombre_fiscal';
break;
default:
$campo_ordenar = $_ordenarPor;
}
$sentido_ordenar = $_ordenarDir;
}

// Condicion Where
$condWhere = null;

// Campos de filtro del store-proxy
if (isset($_filter)) {
$_filtrar = get_magic_quotes_gpc() ? stripslashes($_filter) : $_filter;
if ( is_array($_filtrar) ) {
$condWhere .= "";
foreach ($_filtrar as $_filtro) {
$condWhere .= " AND ".$_filtro->property."='".$_filtro->value."' ";
}
}
}

// Filtro libre por palabras
if (isset($_buscar)) {
if (!empty($_buscar)) {
$condBuscar = "";
$_buscar = mb_strtolower($_buscar);
$palabras = explode(" ",$_buscar);
foreach ($palabras as $palabra) {
if ( !empty($palabra) ) {
$condBuscarPalabra = " ( LOWER(a.recmat_guid)='".mysql_real_escape_string( $palabra ,$esiacademiaConexionBD)."' )";
$condBuscarPalabra .= " OR ( LOWER(a.recmat_codigo) LIKE '%".mysql_real_escape_string( $palabra ,$esiacademiaConexionBD)."%' )";
$condBuscarPalabra .= " OR ( LOWER(a.recmat_nombre) LIKE '%".mysql_real_escape_string( $palabra ,$esiacademiaConexionBD)."%' )";
$condBuscarPalabra .= " OR ( LOWER(c.cto_nombre_fiscal) LIKE '%".mysql_real_escape_string( $palabra ,$esiacademiaConexionBD)."%' )";
// Campos numericos
if (is_numeric($palabra)) {
$condBuscarPalabra .= " OR ( a.recmat_id='".mysql_real_escape_string( $palabra ,$esiacademiaConexionBD)."' )";
}
$condBuscar .= (!empty($condBuscar) ? " AND " : "" ) . " ( ".$condBuscarPalabra." ) ";
}
}
$condWhere .= "";
$condWhere .= " AND ( ".$condBuscar." )";
}
}

// Contar todos los registros
$sqlContarTotal = "SELECT count(*) FROM ".$esiacademiaPrefijoTablas."RecursosMateriales a ";
$sqlContarTotal .= " LEFT JOIN ".$esicontactosPrefijoTablas."Contactos c ON c.cto_id=a.recmat_pertenece_contacto_id ";
if ($condWhere) {
$sqlContarTotal .= " WHERE true ";
$sqlContarTotal .= $condWhere;
}
$resultadoContarTotal = mysql_query($sqlContarTotal, $esiacademiaConexionBD);
$filaContarTotal= mysql_fetch_row($resultadoContarTotal);

// Seleccionar pagina de registros
$sqlRegistros = "SELECT a.* ";
$sqlRegistros .= " ,c.cto_nombre_fiscal as recmat_pertenece_cto_nombre_fiscal ";
$sqlRegistros .= " ,x.cto_nombre_fiscal as recmat_usralta_nombre ";
$sqlRegistros .= " ,w.cto_nombre_fiscal as recmat_usrmod_nombre ";
$sqlRegistros .= " FROM ".$esiacademiaPrefijoTablas."RecursosMateriales a ";
$sqlRegistros .= " LEFT JOIN ".$esicontactosPrefijoTablas."Contactos c ON c.cto_id=a.recmat_pertenece_contacto_id ";
$sqlRegistros .= " LEFT JOIN (SELECT usr_id, cto_nombre_fiscal FROM ".$esinubePrefijoTablas."Usuarios LEFT JOIN ".$esicontactosPrefijoTablas."Contactos ON usr_contacto_id=cto_id) x ON a.recmat_usralta_id=x.usr_id ";
$sqlRegistros .= " LEFT JOIN (SELECT usr_id, cto_nombre_fiscal FROM ".$esinubePrefijoTablas."Usuarios LEFT JOIN ".$esicontactosPrefijoTablas."Contactos ON usr_contacto_id=cto_id) w ON a.recmat_usrmod_id=w.usr_id ";
if ($condWhere) {
$sqlRegistros .= " WHERE true ";
$sqlRegistros .= $condWhere;
}
$sqlRegistros .= " ORDER BY ".$campo_ordenar." ".$sentido_ordenar." ";
if ($_limit>0) {
$sqlRegistros .= " LIMIT ".$_offset.",".$_limit." ";
}
$resultadoRegistros = mysql_query($sqlRegistros, $esiacademiaConexionBD);
$sqlError=mysql_error($esiacademiaConexionBD); // Captura texto error si hay
$sqlErrorNum=mysql_errno($esiacademiaConexionBD); // Captura num error si hay
if (($resultadoRegistros===false) || $sqlErrorNum>0 )
{
$respuesta['success'] = true;
$respuesta['data'] = NULL;
return $respuesta;
} else {
$registros = array();
while($fila = mysql_fetch_object($resultadoRegistros)){
array_push($registros, $fila);
}
}
$respuesta['success'] = true;
$respuesta['data'] = $registros;
$respuesta['total'] = $filaContarTotal[0];
return $respuesta;
}

/**
* CRUD. Update RecursoMaterial
* @remotable
*/
public static function updateRecursoMaterial($_parametros)
{
$respuesta = self::guardarRecursoMaterial( "actualizar", $_parametros );
return $respuesta;
}

/**
* CRUD. Create RecursoMaterial
* @remotable
*/
public static function createRecursoMaterial($_parametros)
{
$respuesta = self::guardarRecursoMaterial( "insertar", $_parametros );
return $respuesta;
}

/**
* Guardar un RecursoMaterial (distingue con un parametro el insert y el update)
*/
public static function guardarRecursoMaterial( $modo, $_parametros)
{

$recmat_id = intval(get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_id) : $_parametros->recmat_id);
$recmat_guid= get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_guid) : $_parametros->recmat_guid;
$recmat_codigo = get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_codigo) : $_parametros->recmat_codigo;
$recmat_nombre = get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_nombre) : $_parametros->recmat_nombre;
$recmat_pertenece_contacto_id = intval(get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_pertenece_contacto_id) : $_parametros->recmat_pertenece_contacto_id);
// Booleano 1=true o 0=false (realmente es un integer)
$recmat_activo = intval(get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_activo) : $_parametros->recmat_activo);
// Timestamp para el control de concurrencia
$recmat_fechamod = get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_fechamod) : $_parametros->recmat_fechamod;

// Comprobar campos obligatorios
if (mb_strlen($recmat_nombre)<=0) {
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","No se puede dejar el nombre vacío."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = $respuesta['errorMsg'];
return $respuesta;
}

// conexion a la base de datos
$esiacademiaConexionBD = esiacademia_conexion::getInstancia()->conexionBD;
$esiacademiaPrefijoTablas = esiacademia_conexion::esiacademiaPrefijoTablas;
$esinubePrefijoTablas = esinube_conexion::esinubePrefijoTablas;
$esicontactosPrefijoTablas = esicontactos_conexion::esicontactosPrefijoTablas;

if (!$esiacademiaConexionBD) {
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","No se puede conectar a la base de datos."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = 'Error: '.$respuesta['errorMsg'];
return $respuesta;
}

// Comprovar contacto asociado al "pertenece a" del recurso material
if ($recmat_pertenece_contacto_id>0) {
$parametrosComprovar = new stdClass;
$parametrosComprovar->filter = array();
$parametrosComprovar->filter[0] = new stdClass;
$parametrosComprovar->filter[0]->property = 'cto_id';
$parametrosComprovar->filter[0]->value = $recmat_pertenece_contacto_id;
$respuestaComprovar = esicontactos_contactos::getContactos( $parametrosComprovar );
if ( !$respuestaComprovar['success'] ) {
$respuesta = $respuestaComprovar;
$respuesta['success'] = false;
return $respuesta;
}
if ( count($respuestaComprovar['data'])<=0 ) {
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","No se encuentra el contacto asociado al campo empresa."));
$respuesta['message'] = 'Error: '.$respuesta['errorMsg'];
return $respuesta;
}
}

//
// Comprobar concurrencia para evitar que se guarde un registro si otro usuario lo ha tocado mientras editabamos el nuestro.
//
if ($modo=='actualizar' and $_SESSION[_APP_INDEX_SESSION]['esinube']['configuracion']->cfg_av_concurrenciaGuardar) {
$paramConcurrencia = new stdClass();
$paramConcurrencia->tabla = $esiacademiaPrefijoTablas."RecursosMateriales";
$paramConcurrencia->campoId = "recmat_id";
$paramConcurrencia->id = $recmat_id;
$paramConcurrencia->campoFecmod = "recmat_fechamod";
$paramConcurrencia->campoUsrmod_id = "recmat_usrmod_id";
$paramConcurrencia->timestamp = $recmat_fechamod;
$respuestaConcurrencia = esinube_concurrencia::esTimestampOk( $paramConcurrencia );
// Si no es correcto, significa que otro usuario ha guardado este registro mientras el primer usuario editaba
if (!$respuestaConcurrencia['success']) {
$respuesta['success'] = false;
$respuesta['errorNum'] = $respuestaConcurrencia['errorNum'];
$respuesta['errorMsg'] = $respuestaConcurrencia['errorMsg'];
$respuesta['message'] = $respuestaConcurrencia['message'];
return $respuesta;
}
}


// Crear comando SQL
if ($modo=='insertar') {
$sqlInsertUpdate = "INSERT INTO ";
} else {
$sqlInsertUpdate = "UPDATE ";
}
$sqlInsertUpdate.= $esiacademiaPrefijoTablas."RecursosMateriales SET ";
$sqlInsertUpdate.= " recmat_codigo='".mysql_real_escape_string( $recmat_codigo ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_nombre='".mysql_real_escape_string( $recmat_nombre ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_pertenece_contacto_id='".mysql_real_escape_string( $recmat_pertenece_contacto_id ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_activo='".mysql_real_escape_string( $recmat_activo ,$esiacademiaConexionBD)."' ";
if ($modo=='insertar') {
// Crear campo identificador unico al crear el registro nuevo
// Sin prefijo con el more entropy desactivado devuelve 13 caracteres
$recmat_guid = uniqid();
$sqlInsertUpdate.= " ,recmat_guid='".mysql_real_escape_string( $recmat_guid ,$esiacademiaConexionBD)."' ";
}
if ($modo=='actualizar') {
$sqlInsertUpdate.= " ,recmat_usrmod_id='".mysql_real_escape_string( $_SESSION[_APP_INDEX_SESSION]['usuario']['usr_id'] ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_fechamod=now() ";
$sqlInsertUpdate.= " where recmat_id='".mysql_real_escape_string( $recmat_id ,$esiacademiaConexionBD)."' ";
} else {
$sqlInsertUpdate.= " ,recmat_usralta_id='".mysql_real_escape_string( $_SESSION[_APP_INDEX_SESSION]['usuario']['usr_id'] ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_fechaalta=now() ";
$sqlInsertUpdate.= " ,recmat_usrmod_id='".mysql_real_escape_string( $_SESSION[_APP_INDEX_SESSION]['usuario']['usr_id'] ,$esiacademiaConexionBD)."' ";
$sqlInsertUpdate.= " ,recmat_fechamod=now() ";
}

// Ejecutar comando SQL
$resultadoInsertUpdate = mysql_query($sqlInsertUpdate, $esiacademiaConexionBD);
$sqlError=mysql_error($esiacademiaConexionBD); // Captura texto error si hay
$sqlErrorNum=mysql_errno($esiacademiaConexionBD); // Captura num error si hay
if (($resultadoInsertUpdate===false) || $sqlErrorNum>0 )
{
// Error clave de indice repetida
if ($sqlErrorNum==1062) {

// Crear comando SQL para obtener lista de indices y poder mostrar error de valor duplicado con detalle
// Metemos el resultado en un array para acceso por numero de indice
$sqlIndices = "SHOW INDEXES FROM ".$esiacademiaPrefijoTablas."RecursosMateriales ";
$resultadoIndices = mysql_query($sqlIndices, $esiacademiaConexionBD);
$indices = array();
while ($filaIndice = mysql_fetch_object($resultadoIndices)) {
if ($filaIndice->Seq_in_index == 1) {
array_push($indices, $filaIndice);
}
}

$indiceKey = intval(mb_substr($sqlError,mb_strpos(mb_strtolower($sqlError),"for key ")+8)) -1;
if (isset($indices[$indiceKey])) {
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = $sqlErrorNum;
switch (mb_strtolower($indices[$indiceKey]->Key_name)) {
case 'primary':
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Clave primaria (recmat_id) repetida."));
break;
case 'recmat_guid':
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Clave (recmat_guid) repetida."));
break;
case 'recmat_codigo':
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Código repetido. Ya existe un recurso con este código."));
break;
//case 'recmat_nombre':
// $respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Nombre repetido. Ya existe un recurso con este nombre."));
// break;
default:
$respuesta['errorMsg'] = $sqlError;
}
$respuesta['message'] = $respuesta['errorMsg'];
return $respuesta;
}
} else {
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = $sqlErrorNum;
$respuesta['errorMsg'] = $sqlError;
$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
return $respuesta;
}
} else {

// Si actualizamos registro comprobar que se ha tocado uno y solo uno
if ($modo=='actualizar') {
if (mysql_affected_rows($esiacademiaConexionBD)!=1) {
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","No se ha encontrado el registro a actualizar."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = 'Error: '.$respuesta['errorMsg'];
return $respuesta;
}
}

// Obtener ultimo id generado si era insercion nueva
if ($modo=='insertar') {
$recmat_id = mysql_insert_id($esiacademiaConexionBD);
}

//
// Luego insertamos/actualizamos la tabla de vinculos de contactos
// con el contacto asociado (pertenece a)
//
$paramVinculo = new stdClass();
$paramVinculo->ctovinc_contacto_id = $aulrecmat_pertenece_contacto_id;
$paramVinculo->ctovinc_tabla = 'ESIACADEMIA_RecursosMateriales';
$paramVinculo->ctovinc_tabla_campo = 'recmat_pertenece_contacto_id';
$paramVinculo->ctovinc_tabla_campoId = 'recmat_id';
$paramVinculo->ctovinc_tabla_id = $recmat_id;
// Ejecutar metodo de la app esicontactos
$respuestaVinculo = esicontactos_contactosVinculos::guardarContactoVinculo( $paramVinculo );
if (!$respuestaVinculo['success']) {
// error (si falla esto no paramos el proceso)
}

// Devolver el registro refrescado con los datos actualizados
// Pedimos solo el id actual
$parametros = new stdClass;
$parametros->filter = array();
$parametros->filter[0] = new stdClass;
$parametros->filter[0]->property = 'recmat_id';
$parametros->filter[0]->value = $recmat_id;
$respuesta = self::getRecursosMateriales( $parametros );

// correcto
$respuesta['success'] = true;

// // Registrar evento en el logger
// $log = new sigestadmin_modelo_log();
// $log->tipo = 'tabla';
// $log->tabla = 'Distribuidores';
// if ($modo=='insertar') {
// $log->registroId = $ultimo_id;
// $log->accion = 'nuevo';
// } else {
// $log->registroId = $dis_id;
// $log->accion = 'act';
// }
// $log->descripcion = $dis_codigo.' - '.$dis_nombre;
// sigestadmin_logger::getInstancia()->registrarEvento( $log );
}

return $respuesta;
}

/**
* CRUD. Destroy RecursoMaterial
* @remotable
*/
public static function destroyRecursoMaterial($_parametros)
{

// Variable con el id recibido para borrar el registro.
$recmat_id = intval(get_magic_quotes_gpc() ? stripslashes($_parametros->recmat_id) : $_parametros->recmat_id);

// conexion a la base de datos de la parte administrativa
$esiacademiaConexionBD = esiacademia_conexion::getInstancia()->conexionBD;
$esiacademiaPrefijoTablas = esiacademia_conexion::esiacademiaPrefijoTablas;
$esicontactosPrefijoTablas = esicontactos_conexion::esicontactosPrefijoTablas;

if (!$esiacademiaConexionBD) {
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","No se puede conectar a la base de datos."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = 'Error: '.$respuesta['errorMsg'];
return $respuesta;
}

// Comprovar que no tiene vinculos con otras tablas
// Con mysql MyISAM no se pueden hacer foreign keys hay que comprobarlo a mano
$sqlComprovarVinculos = "(SELECT count(*) as vinculos ";
$sqlComprovarVinculos .= " FROM ".$esiacademiaPrefijoTablas."PlanesFormativosRecursosMaterialesLnk a ";
$sqlComprovarVinculos .= " WHERE a.plaforrecmatlnk_recursoMaterial_id='$recmat_id' ";
$sqlComprovarVinculos .= " )";
$resultadoRegistrosComprovarVinculos = mysql_query($sqlComprovarVinculos, $esiacademiaConexionBD);
$sqlError=mysql_error($esiacademiaConexionBD); // Captura texto error si hay
$sqlErrorNum=mysql_errno($esiacademiaConexionBD); // Captura num error si hay
if (($resultadoRegistrosComprovarVinculos===false) || $sqlErrorNum>0 )
{
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = $sqlErrorNum;
$respuesta['errorMsg'] = $sqlError;
$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
return $respuesta;
} else {
$tieneCualquierVinculo = false;
while ( $filaComprovarVinculos = mysql_fetch_object($resultadoRegistrosComprovarVinculos) ) {
// Comprovar si hay vinculos
if ($filaComprovarVinculos->vinculos > 0) {
$tieneCualquierVinculo = true;
break;
}
}
if ($tieneCualquierVinculo) {
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Este recurso está vinculado a otras tablas. No se puede borrar."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = $respuesta['errorMsg'];
return $respuesta;
}
}

// Obtener registro antes de borrar, para usar los valores antiguos (log eventos, avisos, etc...)
// Obtener registro antes de borrar, para usar los valores antiguos (log eventos, avisos, etc...)
$parametros = new stdClass;
$parametros->filter = array();
$parametros->filter[0] = new stdClass;
$parametros->filter[0]->property = 'recmat_id';
$parametros->filter[0]->value = $recmat_id;
$respuestaValoresAntiguos = self::getRecursosMateriales( $parametros );
$filaValoresAntiguos = $respuestaValoresAntiguos['data'][0];

//
// Borrar registro del sql
//
$sqlBorrar = "DELETE FROM ".$esiacademiaPrefijoTablas."RecursosMateriales ";
$sqlBorrar.= " WHERE recmat_id='".mysql_real_escape_string( $recmat_id ,$esiacademiaConexionBD)."' ";

$resultadoBorrar = mysql_query($sqlBorrar,$esiacademiaConexionBD);
$sqlError=mysql_error($esiacademiaConexionBD); // Captura texto error si hay
$sqlErrorNum=mysql_errno($esiacademiaConexionBD); // Captura num error si hay
if (($resultadoBorrar===false) || $sqlErrorNum>0 )
{
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = $sqlErrorNum;
$respuesta['errorMsg'] = $sqlError;
$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
} elseif (mysql_affected_rows($esiacademiaConexionBD)!=1) {
// error
$respuesta['success'] = false;
$respuesta['errorNum'] = 0;
$respuesta['errorMsg'] = addslashes(dgettext("esiacademia","Este recurso no se ha encontrado. No se puede borrar."));
//$respuesta['message'] = 'Error: '.$respuesta['errorNum'].' '.$respuesta['errorMsg'];
$respuesta['message'] = $respuesta['errorMsg'];
} else {

//
// Luego borramos de la tabla de vinculos de contactos
// con el contacto asociado
//
$paramVinculo = new stdClass();
$paramVinculo->ctovinc_tabla = 'ESIACADEMIA_RecursosMateriales';
$paramVinculo->ctovinc_tabla_id = $recmat_id;
// Ejecutar metodo de la app esicontactos
$respuestaVinculo = esicontactos_contactosVinculos::destroyContactoVinculo( $paramVinculo );
if (!$respuestaVinculo['success']) {
// error (si falla esto no paramos el proceso)
}


// Ya damos el correcto, luego hay que borrar registros vinculados (si falla quedaran huerfanos)
// correcto
$respuesta['success'] = true;

}

return $respuesta;
}

}

?>


Reproducing steps:

1st step, show and loading grid
The POST send


{"action":"esiacademia_recursosMateriales","method":"getRecursosMateriales","data":[{"buscar":null,"page":1,"start":0,"limit":50}],"type":"rpc","tid":4}


The response



{"type":"rpc","tid":4,"action":"esiacademia_recursosMateriales","method":"getRecursosMateriales","result":{"success":true,"data":[{"recmat_id":"6","recmat_guid":"5086b0ae13fd2","recmat_codigo":"CAF2","recmat_nombre":"CAFETERA CAPSULES","recmat_pertenece_contacto_id":"75","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-10-23 16:58:54","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 16:58:54","recmat_pertenece_cto_nombre_fiscal":"INFORMATICA I COMUNICACIONS CESI SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"5","recmat_guid":"5065e51624b66","recmat_codigo":"CAF1","recmat_nombre":"CAFETERA CAPSULES","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-09-28 19:57:42","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 17:38:23","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"1","recmat_guid":"5065de92cd08b","recmat_codigo":"PR1","recmat_nombre":"PISSARRA RETOLADOR","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-09-28 19:29:54","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 17:01:30","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"7","recmat_guid":"5086b12d0b3fa","recmat_codigo":"PROJ2","recmat_nombre":"PROJECTOR ACER","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-10-23 17:01:01","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 17:01:01","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"2","recmat_guid":"5065de98d84df","recmat_codigo":"PROJ1","recmat_nombre":"PROJECTOR EPSON","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-09-28 19:30:00","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 17:01:11","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"3","recmat_guid":"5065deadd98ee","recmat_codigo":"PROT1","recmat_nombre":"PROJECTOR TRANSPARENCIES","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-09-28 19:30:21","recmat_usrmod_id":"1","recmat_fechamod":"2013-04-17 09:36:31","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"},{"recmat_id":"8","recmat_guid":"5086b160e0e58","recmat_codigo":"RADIO1","recmat_nombre":"RADIO-CD","recmat_pertenece_contacto_id":"70","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-10-23 17:01:52","recmat_usrmod_id":"1","recmat_fechamod":"2012-10-23 18:53:39","recmat_pertenece_cto_nombre_fiscal":"LYC\u00c9E ANGLO-FRAN\u00c7AIS SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"}],"total":"7"}}


See the attached recursos_screen1 image

2nd step

Now i edit the record with id=3 and name="PROJECTOR TRANSPARENCIES"

See the attached recursos_screen2 image

3rd step

Now i edit and change the name="PRUEBA CAMBIO DE NOMBRE"

See the attached recursos_screen3 image

4rd step

Now i save changes

This is the POST


{"action":"esiacademia_recursosMateriales","method":"updateRecursoMaterial","data":[{"recmat_id":3,"recmat_guid":"5065deadd98ee","recmat_codigo":"PROT1","recmat_nombre":"PRUEBA CAMBIO DE NOMBRE","recmat_pertenece_contacto_id":67,"recmat_activo":true,"recmat_fechamod":"2013-04-17 09:36:31"}],"type":"rpc","tid":5}


This is the returned data from php server



{"type":"rpc","tid":5,"action":"esiacademia_recursosMateriales","method":"updateRecursoMaterial","result":{"success":true,"data":[{"recmat_id":"3","recmat_guid":"5065deadd98ee","recmat_codigo":"PROT1","recmat_nombre":"PRUEBA CAMBIO DE NOMBRE","recmat_pertenece_contacto_id":"67","recmat_activo":"1","recmat_usralta_id":"1","recmat_fechaalta":"2012-09-28 19:30:21","recmat_usrmod_id":"1","recmat_fechamod":"2013-04-17 09:51:24","recmat_pertenece_cto_nombre_fiscal":"CESI ALTA FORMACIO SL","recmat_usralta_nombre":"JU\u00c0REZ NICOLAU, MANEL","recmat_usrmod_nombre":"JU\u00c0REZ NICOLAU, MANEL"}],"total":"1"}}


And now if you observe the GRID still remains with old value of the record.
The only way to see the changes if forcing a refresh of all grid with pagingtoolbar refresh button.
This example , without changing any line of code, with extjs 4.1.1.a works fine and automatically refreshes the row grid.
See the attached recursos_screen4 image

Manel
43175431764317743178

slemmon
17 Apr 2013, 2:23 PM
I believe you when you say you're seeing a bug, but I haven't been able to reproduce it.

Is there any chance you can post a small, contained test case to reproduce the issue to avoid any idiosyncrasies with any application code?

maneljn
17 Apr 2013, 11:28 PM
slemmon, i will try to make some complete code example, and report again.

maneljn
18 Apr 2013, 8:14 AM
Slemmon, new information about this problem:

I have been researching in depth the code, and i think i've concluded that the record is updated correctly with the values ??returned by the server, but the grid does not refresh this values.

The only way to refresh the grid, is forcing this command after the success of model.save callbak ()



grid.getView().refresh();


Do you think the problem may be that the version 4.2 does not throw some event that the grid would need to know that should refresh the record ?
Because in v4.1.1.a i don't need to put the view.refresh() and the grid knows itself that has to do the refresh process.

?¿

Manel

slemmon
18 Apr 2013, 3:06 PM
Not sure. The code examples I tried with above were tested on 4.1.3, 4.2, and 4.2.1 and I didn't see the issue you are seeing.

maneljn
18 Apr 2013, 11:15 PM
slemmon, first of all, thanks for your support.

I have tested again to be sure, and i can confirm, that in my above source code, adding the grid.getView().refresh() after model.save() in extjs 4.2 the problem solves. And with 4.1.1.a it's not necessary to add refresh().

I don't know why, or if i'm making some mistake in my code, but is strange this different behavior between 4.1.1a and 4.2

slemmon
19 Apr 2013, 10:18 AM
You bet. I really appreciate the community reporting bugs to us for sure. The difficulty is I need to be able to create a reproducible test case to send up to the engineers and thus far can't seem to get the save() to fail. :(

maneljn
21 Apr 2013, 12:54 AM
slemmon, next week i will try to create a full simple example to reproduce this behavior, and report again.

Manel