Facturas con consecutivos con acceso a varias terminales

Facturas con consecutivos con acceso a varias terminales

Postby Adolfredo Martinez » Thu Aug 26, 2021 10:58 am

Buena como puede controlar uno, cuando varias personas intentan guardar una factura en el mismo instante en mysql o en sql. Intente con un contador en una tabla pero siempre se crea varias con el mismo numero cuando se intenta acceder al mismo tiempo desde 2 pc. Hay alguna forma o algun algoritmo para controlar eso y que se asignen consecutivos validos sin perder consecutivos en el camino?. Osea bloquear el registro, cuando el termine libéralo, para que otro lo tome, sin perder el consecutivo.

Diciendo de otra forma, para que me entienda:
Estoy generando un consecutivo trayendo el último de la tabla y sumandole 1, el problema es que si se realiza la petición mas de una vez al mismo tiempo, el código seria el mismo para las peticiones que se hagan.


Gracias por la atención prestada.


Adolfredo Martinez
Adolfredo Martinez
 
Posts: 121
Joined: Fri Feb 15, 2019 1:37 pm
Location: Aracataca-Colombia

Re: Facturas con consecutivos con acceso a varias terminales

Postby karinha » Thu Aug 26, 2021 11:11 am

João Santos - São Paulo - Brasil - Phone: +55(11)95150-7341
User avatar
karinha
 
Posts: 7872
Joined: Tue Dec 20, 2005 7:36 pm
Location: São Paulo - Brasil

Re: Facturas con consecutivos con acceso a varias terminales

Postby Armando » Thu Aug 26, 2021 4:21 pm

Adolfredo:

No te sirve un campo auto incrementable para el número de folio? y
tal vez apoyándote en una transacción

Saludos
SOI, s.a. de c.v.
estbucarm@gmail.com
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
User avatar
Armando
 
Posts: 3242
Joined: Fri Oct 07, 2005 8:20 pm
Location: Toluca, México

Re: Facturas con consecutivos con acceso a varias terminales

Postby carlos vargas » Thu Aug 26, 2021 5:22 pm

Code: Select all  Expand view  RUN

STATIC FUNCTION Cobr_Grabar( lNuevo )
   LOCAL lGrabado := FALSE
   LOCAL cWhere

   oServer:lThrowError := TRUE

   TRY

      oServer:BeginTransaction()

      IF lNuevo .and. ( nNumero := IncCount( "control", "cont_cobr" ) ) > 0
         oServer:Insert2( "cobradores", { { "num_cobr"  , nNumero    }, ;
                                          { "nombre"    , cNombre    }, ;
                                          { "cedula"    , cCedula    }, ;
                                          { "ciudad"    , cCiudad    }, ;
                                          { "direccion" , cDireccion }, ;
                                          { "telefonos" , cTelefonos }, ;
                                          { "fdi"       , dFechaI    }, ;
                                          { "activo"    , lActivo    }, ;
                                          { "fdr"       , dFechaR    }, ;
                                          { "nota"      , cNota      } } )
      ELSE
         cWhere := "num_cobr=" + Var2Str( nNumero )
         oServer:Update2( "cobradores", { { "nombre"    , cNombre    }, ;
                                          { "cedula"    , cCedula    }, ;
                                          { "ciudad"    , cCiudad    }, ;
                                          { "direccion" , cDireccion }, ;
                                          { "telefonos" , cTelefonos }, ;
                                          { "fdi"       , dFechaI    }, ;
                                          { "activo"    , lActivo    }, ;
                                          { "fdr"       , dFechaR    }, ;
                                          { "nota"      , cNota      } }, cWhere )
      ENDIF

      oServer:Commit()
      oQryCobr:Refresh()
      lGrabado := TRUE

   CATCH oError
      ShowError( oError )
      oServer:Rollback()
   END

   oServer:lThrowError := FALSE

   IF lGrabado
      IIf( lNuevo, ( oBrw:GoBottom(), oBrw:Refresh() ), oBrw:RefreshCurrent() )
   ELSE
      oBrw:GoTop()
   ENDIF

RETURN lGrabado
 



Code: Select all  Expand view  RUN


FUNCTION IncCount( cTable, cField )
   LOCAL oQryTmp
   LOCAL nCount := 0

   oQryTmp := oServer:Query( "SELECT " + cField + " FROM " + cTable + " FOR UPDATE" )

   IF oQryTmp:RecCount() >= 0
      nCount := oQryTmp:FieldGet( 1 ) + 1
      oServer:Execute( "UPDATE " + cTable + " SET " + cField + " = " + Var2Str( nCount ) )
   ENDIF

   oQryTmp:End()

RETURN nCount
 


CREATE TABLE `control` (
`cont_usua` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_ruta` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_cobr` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_clie` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_pres` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_abon` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`cont_docu` BIGINT(10) UNSIGNED NOT NULL DEFAULT '0',
`my_recno` BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`my_recno`) USING BTREE
)
COLLATE='latin1_spanish_ci'
ENGINE=InnoDB
Salu2
Carlos Vargas
Desde Managua, Nicaragua (CA)
User avatar
carlos vargas
 
Posts: 1721
Joined: Tue Oct 11, 2005 5:01 pm
Location: Nicaragua

Re: Facturas con consecutivos con acceso a varias terminales

Postby Adolfredo Martinez » Fri Aug 27, 2021 12:06 am

Gracias por responder.

Carlos Vargas, gracias por esa líneas de código, me pondré a la mano, les avisare como me fue.
Adolfredo Martinez
 
Posts: 121
Joined: Fri Feb 15, 2019 1:37 pm
Location: Aracataca-Colombia

Re: Facturas con consecutivos con acceso a varias terminales

Postby Adolfredo Martinez » Sat Sep 04, 2021 9:47 pm

Con el ejemplo de Carlos, logre solucionar el problema del consecutivo de la factura. Pero tengo otra inquietud:

Como logro si dos terminales la A: esta insertando o borrando Facturas del cliente 100. Pero en la terminal B: selecciono el mismo cliente pero haciendo otra actividad ya sea borrando una factura o modificándola. Como logro bloquear el terminal B, hasta que termine la terminal A. mando le un mensaje que esta ocupada por otro usuario. Para cuando entra la terminal B, ya encuentre la modificacion hecha por la terminal A.
Adolfredo Martinez
 
Posts: 121
Joined: Fri Feb 15, 2019 1:37 pm
Location: Aracataca-Colombia

Re: Facturas con consecutivos con acceso a varias terminales

Postby armando.lagunas » Wed Sep 08, 2021 1:47 pm

Estimado:

lo que yo utilizo en SQL es un campo lógico (0-1) como una bandera, terminal A: toma el registro y le cambio el estado a ese campo inmediatamente a 0 (ocupado) y cuando termino los cambios realizados en el registro le devuelvo el estado 1 (Activo), entonces en la terminal B verifica el estado de este campo antes de hacer cualquier cosa en el registro.

lo otro es realizar procedimiento almacenado en la base de datos estableciendo SET TRANSACTION ISOLATION LEVEL READ COMMITTED que es lo que permite bloquear temporalmente registros en una base de datos SQL

busca en internet como utilizarlo hay varios LEVEL para analizar y utilizar

Espero que te sirva

Saludos
SkyPe: armando.lagunas@hotmail.com
Mail: armando.lagunas@gmail.com
User avatar
armando.lagunas
 
Posts: 346
Joined: Mon Oct 05, 2009 3:35 pm
Location: Curico-Chile

Re: Facturas con consecutivos con acceso a varias terminales

Postby Adolfredo Martinez » Wed Sep 08, 2021 5:42 pm

Gracias Armando.


Me pondré mano a la obra, soy nuevo en esto de Mysql, gracias algunos amigos del fórum, he salido adelante.
Adolfredo Martinez
 
Posts: 121
Joined: Fri Feb 15, 2019 1:37 pm
Location: Aracataca-Colombia

Re: Facturas con consecutivos con acceso a varias terminales

Postby JoseAlvarez » Wed Sep 08, 2021 10:31 pm

Saludos Adolfredo.

Yo lo hago asi y nunca he tenido problemas. Funciona con cualquier numero de estaciones en la red.

Code: Select all  Expand view  RUN

      cTabla:= _cPrefijo+'_docventa'  //nombre de tu tabla


      cQuery:=''
      cQuery:="SELECT * FROM "+ cTabla  + " FOR UPDATE; "  // seleccionamos el UNICO registro de la tabla con la clausula FOR UPDATE
      oQuery:= _oSqlConex:Query( cQuery )                  // este "FOR UPDATE" hace un bloqueo y ninguna otra estacion puede accesar
                                                           // mientras se  realiza la operacion de sumar uno al consecutivo y tomarlo
                                                           // en variable de memoria

      nNumeroFactura:=oQuery:numero_factura+1              // Sumamos 1 al valor del contador guardado en la tabla        

      cQuery:=''
      cQuery:="Update "+cTabla+" Set "                    // aqui actualizamos el valor del campo en la tabla ya incrmentado en 1
      cQuery+="numero_factura:="+alltrim(str(nNumeroFactura))+"; "

      _oSqlConex:Execute( cQuery    )
       
      _oSqlConex:End                                      // al cerrar la conexcion, se libera el registro para los demas usuarios.

 


La clausula FOR UPDATE es la que hace toda la magia. Por supuesto, siempre debes hacer el SELECT primero.
En mi caso uso una tabla con un solo registro por eso el SELECT no lleva un WHERE, puedes adaptarlo a tu situacion.
"Los errores en programación, siempre están entre la silla y el teclado..."

Fwh 19.06 32 bits + Harbour 3.2 + Borland 7.4 + MariaDB + TDolphin

Carora, Estado Lara, Venezuela.
User avatar
JoseAlvarez
 
Posts: 805
Joined: Sun Nov 09, 2014 5:01 pm

Re: Facturas con consecutivos con acceso a varias terminales

Postby TecniSoftware » Thu Sep 09, 2021 12:47 pm

Hola Jose

Queria hacerte una pregunta porque me dejaste con dudas:

    _oSqlConex:End                                      // al cerrar la conexcion, se libera el registro para los demas usuarios.


El registro se libera al cerrar _oSqlConex o al cerrar oQuery ???

Porque si tuvieras que cerrar la _oSqlConex deberias volver a reconectarte a cada rato para poder seguir trabajando.


Muchos saludos
Alejandro
Alejandro Cebolido
Buenos Aires, Argentina
User avatar
TecniSoftware
 
Posts: 235
Joined: Fri Oct 28, 2005 6:29 pm
Location: Quilmes, Buenos Aires, Argentina

Re: Facturas con consecutivos con acceso a varias terminales

Postby JoseAlvarez » Thu Sep 09, 2021 7:02 pm

Saludos alejandro.

Hago mis sistemas para que puedan trabajar con datos hospedados tanto local como Remoto (en la nube).
Por eso mi filosofia de trabajo es:

1.- conectar
2.- consultar/actualizar
3.- desconectar

Localmente puedes tener una conexion con MYSQL largo rato o puedes configurarlo para eso, pero remotamente (en la nube) el servidor te desconecta a los 30seg. de inactividad. Por lo menos en el servidor que yo uso (hostgator).

Por eso conecto y desconecto a cada rato sin importar si estoy trabajando en cualquiera de las formas. Mis sistemas asi quedan listos para trabajar local o remoto y se configura al momento de instalarlos.

Cuando voy a hacer varias actualizaciones las hago con transacciones y alli conecto y desconecto solo una vez, ya que mientras se ejecutan las operaciones el servidor se mantiene activo.

Cuando utilizas FOR UPDATE se libera el registro al cerrar la conexcion, o al realizar un COMMIT en el caso de usar transacciones.

Disculpen por no haber colocado un ejemplo menos personalizado.
Gracias por la observacion.

Un Abrazo.
"Los errores en programación, siempre están entre la silla y el teclado..."

Fwh 19.06 32 bits + Harbour 3.2 + Borland 7.4 + MariaDB + TDolphin

Carora, Estado Lara, Venezuela.
User avatar
JoseAlvarez
 
Posts: 805
Joined: Sun Nov 09, 2014 5:01 pm

Re: Facturas con consecutivos con acceso a varias terminales

Postby carlos vargas » Fri Sep 10, 2021 4:31 pm

Alejandro,
La idea es, si en lugar de cerrar la conexion, utilizas transacciones
al realizar el commit o el rollback, se libera el registro seleccionado con el SELECT ... FOR UPDATE...


salu2
carlos vargas
Salu2
Carlos Vargas
Desde Managua, Nicaragua (CA)
User avatar
carlos vargas
 
Posts: 1721
Joined: Tue Oct 11, 2005 5:01 pm
Location: Nicaragua

Re: Facturas con consecutivos con acceso a varias terminales

Postby VictorCasajuana » Mon Sep 13, 2021 11:56 am

armando.lagunas wrote:Estimado:

lo que yo utilizo en SQL es un campo lógico (0-1) como una bandera, terminal A: toma el registro y le cambio el estado a ese campo inmediatamente a 0 (ocupado) y cuando termino los cambios realizados en el registro le devuelvo el estado 1 (Activo), entonces en la terminal B verifica el estado de este campo antes de hacer cualquier cosa en el registro.

lo otro es realizar procedimiento almacenado en la base de datos estableciendo SET TRANSACTION ISOLATION LEVEL READ COMMITTED que es lo que permite bloquear temporalmente registros en una base de datos SQL

busca en internet como utilizarlo hay varios LEVEL para analizar y utilizar

Espero que te sirva

Saludos

Hola Armando.
Este tema siempre me ha intrigado y a veces me pongo a buscar info pero nunca llego a buen puerto. En tu caso lo tienes bien controlado ya que modificas la tabla. No me gusta mucho tener que modificar la estructura de datos para este fin pero se podría aceptar como solución. Pero me surge una duda, si el terminal A toma el registro, cambias la bandera a 1 y se cierra el programa sin pasar por el proceso de guardar la ficha, que haces? por ejemplo se puede apagar el ordenador, error de ejecución, etc...

aquí hay una buena charla que tuvimos hace meses al respecto: https://forum.modharbour.app/viewtopic.php?f=25&t=259
--------
¿ Y porque no ?
¿ And why not ?
User avatar
VictorCasajuana
 
Posts: 268
Joined: Wed Mar 28, 2018 4:38 pm
Location: Vinaròs

Re: Facturas con consecutivos con acceso a varias terminales

Postby JoseAlvarez » Mon Sep 13, 2021 4:06 pm

VictorCasajuana wrote:
armando.lagunas wrote:Estimado:

lo que yo utilizo en SQL es un campo lógico (0-1) como una bandera, terminal A: toma el registro y le cambio el estado a ese campo inmediatamente a 0 (ocupado) y cuando termino los cambios realizados en el registro le devuelvo el estado 1 (Activo), entonces en la terminal B verifica el estado de este campo antes de hacer cualquier cosa en el registro.

lo otro es realizar procedimiento almacenado en la base de datos estableciendo SET TRANSACTION ISOLATION LEVEL READ COMMITTED que es lo que permite bloquear temporalmente registros en una base de datos SQL

busca en internet como utilizarlo hay varios LEVEL para analizar y utilizar

Espero que te sirva

Saludos

Hola Armando.
Este tema siempre me ha intrigado y a veces me pongo a buscar info pero nunca llego a buen puerto. En tu caso lo tienes bien controlado ya que modificas la tabla. No me gusta mucho tener que modificar la estructura de datos para este fin pero se podría aceptar como solución. Pero me surge una duda, si el terminal A toma el registro, cambias la bandera a 1 y se cierra el programa sin pasar por el proceso de guardar la ficha, que haces? por ejemplo se puede apagar el ordenador, error de ejecución, etc...

aquí hay una buena charla que tuvimos hace meses al respecto: https://forum.modharbour.app/viewtopic.php?f=25&t=259


Hola Victor, saludos.

Veo que el tema es recurrente, por lo que me tome el tiempo para hacer dos ejemplos sencillos que a mi me han funcionado muy bien. Jamas he tenido problema con mis sistemas en este aspecto.

Lo primero es aclarar que toda la magia la hace la instruccion SELECT.. FOR UPDATE... y esto significa 3 cosas:

1.- Siempre hay que hacer un SELECT... FOR UPDATE previo al UPDATE ... SET eso es lo que nos va a bloquear el registro seleccionado.
sin permitir que nadie mas tenga acceso a el.

2.- Para que SELECT... FOR UPDATE funcione debemos tener el AUTOCOMMIT en 0 (OFF o FALSE) segun la version de mysql o mariadb.
si no se desea tener configurado de manera permanente, se puede activar o desactivar mientras hacemos la operacion.

3.- En caso de no querer usar el AUTOCOMMIT, la otra opcion mas comoda y tambien valida, es usar una transaccion. El caso o gusto particular de cada quien indicara
cual sistema usar, si AUTOCOMMIT o TRANSACCION.

Aca dejo los ejemplos:

USANDO AUTOCOMMIT

Code: Select all  Expand view  RUN

# include FIVEWIN.CH

Function Fnct_Prueba()

   Local oQuery, cQuery, cTabla
   Local nNuevoNumero
   
   _oSqlConex:=TDolphinSrv():New( _cHost, _cUser, _cPasswordUser,,, _cDataBase,bSQLCnxErr() )  // mis variables de conexion son publicas.
                                                                                               // cada quien debe manejar esta parte
   if  _oSqlConex:lError                                                                       // segun su forma de trabajar.
      _oSqlConex:End()
      return .f.
   endif
   
   cQuery := "SET AUTOCOMMIT=0 ; "                   // colocamos el AUTOCOMMIT temporalmente en 0 (desactivado)
   oQuery := _oSqlConex:Execute( cQuery    )         // de esta forma AUTOCOMMIT funciona solo con la sesion actual
                                                     // en caso de que no este configurado por defecto
     
   cTabla := _cPrefijo+'_docventatemp'
   
   cQuery := ''
   cQuery := "SELECT numerotemp_documento FROM "+ cTabla +" FOR UPDATE ; "   // hacemos el SELECT... FOR UPDATE para bloquear la fila de
   oQuery := _oSqlConex:Query( cQuery )                                      //  la tabla
   
   nNuevoNumero:=oQuery:numerotemp_documento+1    // aumentamos en 1 el contador consecutivo
   
   cQuery :=''
   cQuery :="Update "+cTabla+" Set "
   cQuery +="numerotemp_documento:="+alltrim(str(nNuevoNumero))+" ; "       // actualizamos la tabla
   _oSqlConex:Execute( cQuery  )
   
   //msginfo('Stop')    // activar si se desea chequear la tabla con phpmyadmin, heidi, navicat, etc. durante el proceso.
   
   cQuery:="commit ; "                               // hacemos commit para actualizar la tabla.
   oQuery:= _oSqlConex:Execute( cQuery     )                    
                                                                                     
   cQuery := "SET AUTOCOMMIT=1 ; "                   // Colocamos el autocommit de nuevo a su estado activo          
   oQuery := _oSqlConex:Execute( cQuery    )                                    
                                                                                                   
    _oSqlConex:End()                                 // cerramos la conexion

return nNuevoNumero     // nNuevoNumero es el numero asignado a la nueva factura que estamos emitiendo ...


 



Usando Transacciones

Code: Select all  Expand view  RUN

# include FIVEWIN.CH

Function Fnct_Prueba()

  Local oQuery, cQuery, cTabla
  Local nNuevoNumero

  _oSqlConex:=TDolphinSrv():New( _cHost, _cUser, _cPasswordUser,,, _cDataBase,bSQLCnxErr() )  // mis variables de conexion son publicas.  
                                                                                              // cada quien debe manejar esta parte  
  if _oSqlConex:lError                                                                        // segun su forma de trabajar.
    _oSqlConex:End()
    return .f.
  endif
   
  _oSqlConex:BeginTransaction()     // comenzamos la transaccion
     
  cTabla := _cPrefijo+'_docventatemp'

  cQuery := ''
  cQuery := "SELECT numerotemp_documento FROM "+ cTabla +" FOR UPDATE ; "   // hacemos el SELECT... FOR UPDATE para bloquear la fila de
  oQuery := _oSqlConex:Query( cQuery )                                      //  la tabla
           
  nNuevoNumero:=oQuery:numerotemp_documento+1     // aumentamos en 1 el contador consecutivo
   
  cQuery :=''
  cQuery :="Update "+cTabla+" Set "
  cQuery +="numerotemp_documento:="+alltrim(str(nNuevoNumero))+" ; "       // actualizamos la tabla      
  _oSqlConex:Execute( cQuery  )
   
  //msginfo('Stop')    // activar si se desea chequear durante el proceso.
   
  _oSqlConex:CommitTransaction()

  _oSqlConex:End()                                 // cerramos la conexion


return nNuevoNumero     // nNuevoNumero es el numero asignado a la nueva factura que estamos emitiendo ...




 


En ambos casos, el registro se libera al ejecutarse el COMMIT.

Esto es solo una idea de como lo hago, cada quien puede adaptarlo a su modo.

Espero haber ayudado. Quizas algun colega le consiga fallas o pueda sugerir mejoras. Todo es bienvenido. Pero como comento, jamas he tenido problemas con esta manera de obtener consecutivos sin que se repitan, no importa el numero de estaciones que accesen al mismo tiempo la tabla.
"Los errores en programación, siempre están entre la silla y el teclado..."

Fwh 19.06 32 bits + Harbour 3.2 + Borland 7.4 + MariaDB + TDolphin

Carora, Estado Lara, Venezuela.
User avatar
JoseAlvarez
 
Posts: 805
Joined: Sun Nov 09, 2014 5:01 pm

Re: Facturas con consecutivos con acceso a varias terminales

Postby VictorCasajuana » Tue Sep 14, 2021 6:28 am

Vale, me había confundido. Pensaba que el bloqueo que comentabas al principio ( 0 en el campo lógico ) lo utilizabas cuando un usuario, por ejemplo, abría una ficha para modificar los datos, para que el resto de usuarios no pudiesen modificar dicha ficha o al menos saber de su edición por otro usuario.
Lo que expones es correcto, pero ya es a nivel de transacciones dentro de un proceso automático el cual no está controlado por el usuario.
--------
¿ Y porque no ?
¿ And why not ?
User avatar
VictorCasajuana
 
Posts: 268
Joined: Wed Mar 28, 2018 4:38 pm
Location: Vinaròs

Next

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 85 guests