Fork de vERP 26

Tras hacer un fork de Velneo vERP 26, doy algunas indicaciones para los que vengan detrás.

Básicamente, esto sirve si se quiere hacer un fork de vERP, conservando su estructura intacta, y queremos que se generen los datos de ejemplo y los datos de configuración por defecto.

  1. Crear la nueva solución: es importante crear un alias de los proyectos de datos y de aplicación, así como indicar la misma versión (26.0) en ambos, para que funcione la importación automática de datos de algunas tablas desde el sitio FTP de Velneo (ya que el proceso de importación de datos utiliza el nº de versión del proyecto de datos y del proyecto de aplicación).

  2. Importar los ficheros de script, primero del proyecto de datos, y luego del proyecto de aplicación. Para ello, realizar primero la exportación por directorios desde vERP, y luego realizar la importación de directorios desde la nueva solución

  3. Desde la “búsqueda en ficheros de script”, buscar los alias de vERP (que son “velneo_verp_2_dat” y “velneo_verp_2_app”) y sustituirlos por los alias de la nueva solución, tanto en el proyecto de datos como en el de aplicación. También hay que buscar los nombres de fichero de los proyectos de vERP (“4ek4uta3.vcd” y “4ekd5b99.vca”), porque aparecen en muchos include.

  4. Realizar el copiado y pegado de objetos desde el proyecto de datos de vERP al de la nueva solución, y lo mismo desde el proyecto de aplicación de vERP al de la nueva solución. Especialmente este último, va a tardar unos cuantos minutos

  5. En las tablas del proyecto de datos de la nueva solución, hay que revisar todos aquellos campos que sean “Singular de plural por índice” o “Singular de plural por posición”, ya que no se traspasan correctamente al pegar. Aunque “parece” que están bien, hay que ajustar manualmente el dato “Plural” del campo (es decir, seleccionar otro y volver a seleccionar el que estaba), para que tome correctamente la tabla de origen. No da ningún error de vDevelop, así que hay que buscarlos manualmente (icono rojo con flecha en el campo)

  6. Mediante la “búsqueda en contenido de objetos”, buscar los nombres de los ficheros de proyecto de vERP (“4ek4uta3.vcd” y “4ekd5b99.vca”), ya que hay procesos enlazados a su correspondiente fichero de script por el nombre de fichero del proyecto. Esto ocurre tanto en el proyecto de datos como en el proyecto de aplicación.

  7. En el proyecto de aplicación, hay que ajustar el proceso DES_DAT_JSO para que descargue los ficheros json desde Velneo en la carpeta de nuestra nueva solución, y no en la de vERP. De manera similar, hay que hacer este ajuste en el proceso IMP_JSO del proyecto de datos.

  8. Finalmente, hay que tener en cuenta que, en los ficheros JSON que se descargan de Velneo con datos de algunas tablas, en la cabecera del JSON se indica la tabla a la que corresponde el fichero como [velneo_verp_2_dat/ID_TABLA], por lo que hay que hacer el ajuste con el alias de nuestra nueva solución.

El principal problema es que si no se hacen algunos de estos pasos, el servidor vServer se cae. Kaputt. Así que hay que tener paciencia.

Saludos!

3 Me gusta

De ahí nació esta idea:
https://soporte.velneo.es/hc/es/community/posts/360018588413-Fork-de-aplicaciones

Saludos.

Buen artículo.
Acabo de hacer un fork siguiendo todas las instrucciones y es bastante dramático, ya que, cuando crees que no puede haber más llamadas a las soluciones, pues todavía salen…
pero creo que faltaría un paso como mínimo.
En la tabla PRS_EM_W (Opciones de menú) hay que modificar el campo proyecto con el alias de nuestro proyecto. El menú se ve, pero no se ejecuta.
No tengo claro si los ficheros adjuntos se copian, me ha dado la sensación que no, pero como tengo que hacer otro fork, lo revisaré
A ver si en alguna versión se puede simplificar esto.

Hola, @carlos.
Tienes razón, las opciones de menú también hay que cambiarlas por el motivo que comentas. Al menos, no rompe vClient ni vServer, como ocurría si no hacías algunos de los pasos anteriores.

Te confirmo que los ficheros adjuntos no se copian. De eso me di cuenta cuando hice el primer vin de la nueva solución (un buen añadido de vInstallBuilder que antes no estaba, comprobar si todos los ficheros adjuntos están en la carpeta de la solución).

Voy a editar el post para reflejar esos cambios. Gracias!

(Edito: no me deja modificar el post original, así que aprovecho para hacer algún comentario adicional)

Los viejos del lugar nos acordamos de cómo se instalaban las aplicaciones de V7 antes de vInstallBuilder y antes de la importación de soluciones compartidas desde vDevelop… cuando había que crear la carpeta de la solución a mano y copiar los ficheros de proyectos (vca y vcd) en el servidor al que ibas a instalarla. Lo que quiero decir es que supongo que, en algún momento, y dado el peso que está adquiriendo vERP, tenga cada vez más sentido contar con una herramienta nativa para realizar forks. Y hay que tener en cuenta que en el roadmap para las siguientes versiones está un reenfoque de la herencia de soluciones. Así que, más tarde o más temprano, espero que tengamos algo que nos haga olvidar estos menesteres.

¡Enhorabuena por el post!
Me ha venido perfecto para comprobar que las horas que me tiré para hacerlo por mi cuenta no fueron en vano: punto por punto, me tocó descubrirlo por prueba-error.
Como se colige, esto es solo un primer paso, para que no se caiga el vServer. Luego vienen menús dinámicos, etc.
Espero que Velneo siga mejorando las funcionalidades que tienen que ver con esto.
Aunque creo que montando un fork estamos “pecando”, ¿no? Se supone que con una buena gestión de herencias, tablas de extensión, puntos de inserción,etc no debería ser necesario el fork ¿me equivoco? Yo lo he montado porque no tengo tiempo de empaparme de todo eso.

Un tema un poco viejo, he hecho todo los puntos que dice ahí y mi fork no carga el menú…
Depurando, en la parte donde hace un query para la tabla PRS_MEN_W:

// Cargar todas las opciones del menú|
|—|—|
var listaMenu = new VRegisterList(theRoot);|
listaMenu.setTable(comp_ind_dat/PRS_MEN_W);|
var busMenu = new VQuery(theRoot);|
busMenu.setQuery(comp_ind_app/PRS_MEN_W_MEN);|
busMenu.setVar(OPC_MEN_DES, prefijoMenu);|
busMenu.setVar(OPC_MEN_HAS, prefijoMenu + zzzzzzzzzzzz);|

a la hora de ejecutar el busMenu.exec() me regresa la consulta vacía a pesar de que al parecer todo está bien…
¿Alguna ayuda o dirección?
Saludos!!

Hola marco_rangel.

Después de hacer un fork de una Solución de Velneo lo habitual es que surjan infinidad de problemas, simplemente porque Velneo no está pensado para hacer fork.

En cualquier caso, el código JavaScript que muestras no tendría problemas si no fuera porque le faltan las comillas en los literales y alguna línea que no es correcta.

Yo, ejecutaría primero ese código con comandos nativos para confirmar que el objeto Búsqueda devuelve los registros correctos.

Saludos
Paco Satué

ok, gracias Paco!!
Lo intentaré así.
Saludos!

Hola, Marco.
Me sale algo así para tu fork:
// ----------------------------
// Cargar el menú dinámico
// ----------------------------
var cargarMenu = function(prefijoMenu)
{
importClass(“VImage”);
importClass(“VQuery”);

// Si no hay menú especificado se asume el general
if (prefijoMenu == null) {
	prefijoMenu = "GEN";
};

// Preparar el control de menú
var formulario = theRoot.dataView();
var menu = formulario.control("MEN_APP");

// Configuración general del menú
menu.setHeaderLabel(0, "Menú General");
menu.setHeaderLabel(1, "Tipo");
menu.setHeaderLabel(2, "idRef");
menu.hideColumn(1);
menu.hideColumn(2);
menu.clear();

// Preparar variables de trabajo
var opcionPadre009 = "";
var opcionPadre012 = "";
var opcionPadre015 = "";
var usuarioGruposUsuarios = theApp.globalVarToString("comp_ind_dat/CUR_USR_GRP").split(",");
usuarioGruposUsuarios.sort();
var usuarioGruposUsuariosNum = usuarioGruposUsuarios.length;
var grupo = "";

// Cargar todas las opciones del menú
var listaMenu = new VRegisterList(theRoot);
listaMenu.setTable("comp_ind_dat/PRS_MEN_W");
var busMenu = new VQuery(theRoot);
busMenu.setQuery("comp_ind_app/PRS_MEN_W_MEN");
busMenu.setVar("OPC_MEN_DES", prefijoMenu);
busMenu.setVar("OPC_MEN_HAS", prefijoMenu + "zzzzzzzzzzzz");
if (busMenu.exec()) {
	listaMenu.append(busMenu.result());
};

// Leer las opciones del menú y cargar el control del árbol
for (var numRegistro = 0; numRegistro < listaMenu.size(); numRegistro++ ) {
	
	// Lectura de los datos de la opción de menú
	var registro = listaMenu.readAt(numRegistro);
	
	// Control de permisos de los grupos de usuario para añadir la opción de menú
	var opcionGruposTodos = registro.fieldToBool("USR_GRP_ALL");
	var opcionGruposUsuarios = registro.fieldToString("USR_GRP").split(",");
	opcionGruposUsuarios.sort();
	var opcionGruposUsuariosNum = opcionGruposUsuarios.length;
	var opcionGrupoEncontrado = false;
	
	// Se busca si el usuario está en alguno de los grupos marcados
	if (usuarioGruposUsuariosNum < opcionGruposUsuariosNum)	{
		for (grupo in usuarioGruposUsuarios) {
			var pos = opcionGruposUsuarios.indexOf(usuarioGruposUsuarios[grupo]);
			if (pos != -1) {
				opcionGrupoEncontrado = true;
				break;
			};
		};
	}
	else {
		for (grupo in opcionGruposUsuarios) {
			var pos = usuarioGruposUsuarios.indexOf(opcionGruposUsuarios[grupo]);
			if (pos != -1) {
				opcionGrupoEncontrado = true;
				break;
			};
		};
	};
	
	// Si la opción está permitida se añade al menú
	if (((opcionGruposTodos == true) && (opcionGrupoEncontrado == false)) || 
		((opcionGruposTodos == false) && (opcionGrupoEncontrado == true)))
	{

		// Preparar las variables con los datos del registro
		var opcionId = registro.fieldToString("ID");
		var nivel = opcionId.length;
		var opcionTexto = registro.fieldToString("NAME");
		var opcionToolTip = registro.fieldToString("TOO_TIP");
		var opcionStatusTip = registro.fieldToString("STA_TIP");
		var opcionWhatsThis = registro.fieldToString("WHA_THI");
		var opcionTipo = registro.fieldToString("MEN_OBJ");
		if (opcionTipo == "C")
			var opcionAccion = registro.fieldToString("PRF_MEN");
		else
			var opcionAccion = registro.fieldToString("OBJ_ID_REF");
		if (registro.fieldToString("ICO_ID_REF") != "" ) {
			var icono = new VImage();
			icono.loadResource(registro.fieldToString("ICO_ID_REF"));
		} else {
			var icono = registro.fieldToImage("ICO");
		};

		switch (nivel)
		{
			case 6:	// Opción de nivel principal de arbolado
				// Se configura la opción
				item = menu.addTopLevelItem();
				item.setText(0, opcionTexto);
				item.setText(1, opcionTipo);
				item.setText(2, opcionAccion);
				item.setToolTip(0, opcionToolTip);
				item.setStatusTip(0, opcionStatusTip);
				item.setWhatsThis(0, opcionWhatsThis);
				item.setIcon(0, icono);
				break;
			
			case 9: // Opción de 2º nivel de arbolado
				opcion = item.addChild();
				var opcionPadre009 = opcion;
				break;

			case 12: // Opción de 3º nivel de arbolado
				opcion = opcionPadre009.addChild();
				var opcionPadre012 = opcion;
				break;
			
			case 15: // Opción de 4º nivel de arbolado
				opcion = opcionPadre012.addChild();
				var opcionPadre015 = opcion;
				break;
			
			case 18: // Opción de 5º nivel de arbolado
				opcion = opcionPadre015.addChild();
				break;
		};
					
		// Si nivel es mayor de 6 es una opción de 2º, 3º, 4º ó 5º nivel
		if (nivel > 6)
		{
			// Se configura la opción
			opcion.setText(0, opcionTexto);
			opcion.setText(1, opcionTipo);
			opcion.setText(2, opcionAccion);
			opcion.setToolTip(0, opcionToolTip);
			opcion.setStatusTip(0, opcionStatusTip);
			opcion.setWhatsThis(0, opcionWhatsThis);
			opcion.setIcon(0, icono);
		};
	};
};

// Preparar visualización del menú por defecto
menu.headerHidden = true;
menu.rootIsDecorated = true;

};