mirror of
https://github.com/KeymonSoft/kms_helperSubs.git
synced 2026-04-17 21:06:17 +00:00
26/9/23 - Commit inicial
This commit is contained in:
370
kms_helperSubs.bas
Normal file
370
kms_helperSubs.bas
Normal file
@@ -0,0 +1,370 @@
|
||||
B4A=true
|
||||
Group=Default Group
|
||||
ModulesStructureVersion=1
|
||||
Type=Class
|
||||
Version=12.2
|
||||
@EndOfDesignText@
|
||||
Sub Class_Globals
|
||||
Private Root As B4XView 'ignore
|
||||
Private xui As XUI 'ignore
|
||||
Dim skmt, rkmt As SQL
|
||||
Private EventName As String 'ignore
|
||||
Private CallBack As Object 'ignore
|
||||
' Dim rp As RuntimePermissions
|
||||
Dim safePath As String
|
||||
Dim fechaHoy As String
|
||||
Dim skmtAttached As Boolean = False
|
||||
End Sub
|
||||
|
||||
'You can add more parameters here.
|
||||
Public Sub Initialize (vCallback As Object, vEventName As String) As Object
|
||||
EventName = vEventName
|
||||
CallBack = vCallback
|
||||
' Log("************************** " & safePath)
|
||||
Dim Dirp As String = File.DirRootExternal
|
||||
Dim Dir As String
|
||||
' Dim Dir2 As String
|
||||
Try
|
||||
File.MakeDir(Dirp,"/kmts")
|
||||
Dir = "/kmts"
|
||||
' Log("creado en kmts " & Dirp & Dir)
|
||||
Catch
|
||||
Dir = ""
|
||||
' Log("creado en raiz")
|
||||
End Try
|
||||
safePath = Dirp & Dir
|
||||
rkmt.Initialize(safePath,"md.rkmt.km", True)
|
||||
skmt.Initialize(File.DirInternal,"kmt.db", False)
|
||||
' rkmt.ExecNonQuery($"create table if not exists pedidos (pe_folio NUMERIC, pe_desc NUMERIC, pe_costo_sin TEXT, pe_ruta TEXT, pe_cedis TEXT, pe_costo_tot NUMERIC, pe_costou NUMERIC, pe_cant NUMERIC, pe_pronombre TEXT, pe_proid TEXT, pe_cliente TEXT, pe_fecha TEXT, pe_usuario TEXT, pe_envio_ok TEXT)"$)
|
||||
' rkmt.ExecNonQuery($"create table if not exists inventarios (cat_gp_iniciativa TEXT, cat_gp_tipoprod TEXT, cat_gp_dev TEXT, cat_gp_almacen NUMERIC, cat_gp_id TEXT, cat_gp_nombre TEXT, cat_gp_imp1 TEXT, cat_gp_imp2 TEXT, cat_gp_precio TEXT, cat_gp_clasif TEXT, cat_gp_sts TEXT, cat_gp_tipo TEXT, cat_gp_subtipo TEXT, cat_gp_tipoprod2 TEXT, fecha TEXT)"$)
|
||||
'' rkmt.ExecNonQuery($"create table if not exists inventarios2 (cat_gp_iniciativa TEXT, cat_gp_tipoprod TEXT, cat_gp_dev TEXT, cat_gp_almacen NUMERIC, cat_gp_id TEXT, cat_gp_nombre TEXT, cat_gp_imp1 TEXT, cat_gp_imp2 TEXT, cat_gp_precio TEXT, cat_gp_clasif TEXT, cat_gp_sts TEXT, cat_gp_tipo TEXT, cat_gp_subtipo TEXT, cat_gp_tipoprod2 TEXT, fecha TEXT)"$)
|
||||
' rkmt.ExecNonQuery($"create table if not exists paquetes (cat_dp_precio_simptos TEXT, cat_dp_precio TEXT, cat_dp_almacen TEXT, cat_dp_id TEXT, cat_dp_idprod TEXT, cat_dp_tipo TEXT, cat_dp_pzas TEXT, cat_dp_usuario TEXT, cat_dp_fecha TEXT, cat_dp_regalo TEXT, cat_dp_clasif TEXT, fecha TEXT)"$)
|
||||
If chkIfTableExists(skmt, "PEDIDO") Then rkmt.ExecNonQuery($"create table if not exists PEDIDO (${getTableColumnList(skmt, "PEDIDO")}, FECHA TEXT)"$)
|
||||
If chkIfTableExists(skmt, "CAT_DETALLES_PAQ") Then rkmt.ExecNonQuery($"create table if not exists CAT_DETALLES_PAQ (${getTableColumnList(skmt, "CAT_DETALLES_PAQ")}, FECHA TEXT)"$)
|
||||
If chkIfTableExists(skmt, "CAT_GUNAPROD") Then rkmt.ExecNonQuery($"create table if not exists CAT_GUNAPROD (${getTableColumnList(skmt, "CAT_GUNAPROD")}, FECHA TEXT)"$)
|
||||
If chkIfTableExists(skmt, "CAT_GUNAPROD2") Then rkmt.ExecNonQuery($"create table if not exists CAT_GUNAPROD2 (${getTableColumnList(skmt, "CAT_GUNAPROD2")}, FECHA TEXT)"$)
|
||||
DateTime.DateFormat = "MM/dd/yyyy"
|
||||
fechaHoy = DateTime.Date(DateTime.Now)
|
||||
revisaSkmtAttached
|
||||
Return Me
|
||||
End Sub
|
||||
|
||||
'This event will be called once, before the page becomes visible.
|
||||
'Private Sub B4XPage_Created (Root1 As B4XView)
|
||||
' Root = Root1
|
||||
' 'load the layout to Root
|
||||
'End Sub
|
||||
|
||||
'Revisa si esta montada "kmt.db" como "skmt1" y si no, la monta
|
||||
'Para que "rkmt" vea a "skmt", es necesario montarla (attach).
|
||||
Sub revisaSkmtAttached
|
||||
skmtAttached = False
|
||||
Dim rs As ResultSet = rkmt.ExecQuery("SELECT * FROM pragma_database_list")
|
||||
Do While rs.NextRow 'Revisamos si esta montada "kmt.db" como "skmt1" y si no, la montamos.
|
||||
If rs.GetString("name") = "skmt1" Then skmtAttached = True
|
||||
Loop
|
||||
If Not(skmtAttached) Then
|
||||
rkmt.ExecNonQuery($"attach database '${File.Combine(File.DirInternal, "kmt.db")}' as skmt1;"$)
|
||||
skmtAttached = True
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'Inicializa la BD con "kmt.db" en "File.DirInternal", si el archivo no existe, lo copia desde "File.DirAssets".
|
||||
'Ej: dim skmt as sql = dbInit
|
||||
Sub dbInit As SQL 'ignore
|
||||
Private db As SQL
|
||||
If File.Exists(File.DirInternal, "kmt.db") = False Then File.Copy(File.DirAssets, "kmt.db", File.DirInternal, "kmt.db")
|
||||
db.Initialize(File.DirInternal,"kmt.db", True)
|
||||
' dbOk(True)
|
||||
' Log(db.IsInitialized)
|
||||
Return db
|
||||
End Sub
|
||||
|
||||
'Copia la tabla PEDIDO a rkmt.db en la tarjeta del celular.
|
||||
Sub respalda_pedido
|
||||
Private inicio As String = DateTime.Now
|
||||
' Log("Copiando PEDIDO ...")
|
||||
Private lasCols As String = getTableColumnListName(skmt, "PEDIDO")
|
||||
rkmt.BeginTransaction
|
||||
rkmt.ExecNonQuery($"delete from PEDIDO where FECHA = '${fechaHoy}'"$)
|
||||
rkmt.ExecNonQuery($"insert into PEDIDO (${lasCols}, FECHA) select ${lasCols}, '${fechaHoy}' as FECHA from skmt1.PEDIDO where substr(pe_fecha, 1, 10) = '${fechaHoy}'"$)
|
||||
rkmt.TransactionSuccessful 'Si no se pone TransactionSuccessful no se escribe NADA!!
|
||||
rkmt.EndTransaction
|
||||
Log(((DateTime.Now - inicio)/1000) & " segs")
|
||||
End Sub
|
||||
|
||||
'Copia la tabla CAT_GUNAPRODS a rkmt.db en la tarjeta del celular.
|
||||
Sub respalda_cat_gunaprod
|
||||
Private inicio As String = DateTime.Now
|
||||
' Log("Copiando CAT_GUNAPROD ...")
|
||||
Private lasCols As String = getTableColumnListName(skmt, "CAT_GUNAPROD")
|
||||
lasCols = lasCols.Replace(", CAT_GP_IMG", "") 'Quitamos la imagen del respaldo.
|
||||
rkmt.BeginTransaction
|
||||
rkmt.ExecNonQuery($"delete from CAT_GUNAPROD where FECHA = '${fechaHoy}'"$)
|
||||
rkmt.ExecNonQuery($"insert into CAT_GUNAPROD (${lasCols}, FECHA) select ${lasCols}, '${fechaHoy}' as FECHA from skmt1.CAT_GUNAPROD"$)
|
||||
rkmt.TransactionSuccessful 'Si no se pone TransactionSuccessful no se escribe NADA!!
|
||||
rkmt.EndTransaction
|
||||
Log(((DateTime.Now - inicio)/1000) & " segs")
|
||||
End Sub
|
||||
|
||||
'Copia la tabla CAT_DETALLE_PAQ a rkmt.db en la tarjeta del celular.
|
||||
Sub respalda_cat_detalle_paq
|
||||
Private inicio As String = DateTime.Now
|
||||
' Log("Copiando CAT_DETALLES_PAQ ...")
|
||||
Private lasCols As String = getTableColumnListName(skmt, "CAT_DETALLES_PAQ")
|
||||
rkmt.BeginTransaction
|
||||
rkmt.ExecNonQuery($"delete from CAT_DETALLES_PAQ where fecha = '${fechaHoy}'"$)
|
||||
rkmt.ExecNonQuery($"insert into CAT_DETALLES_PAQ (${lasCols}, FECHA) select ${lasCols}, '${fechaHoy}' as FECHA from skmt1.CAT_DETALLES_PAQ"$)
|
||||
rkmt.TransactionSuccessful 'Si no se pone TransactionSuccessful no se escribe NADA!!
|
||||
rkmt.EndTransaction
|
||||
Log(((DateTime.Now - inicio)/1000) & " segs")
|
||||
End Sub
|
||||
|
||||
'Realiza un "vacuum" de la base de datos "rkmt".
|
||||
Sub vacuum 'ignore
|
||||
Log("Vacuum")
|
||||
rkmt.ExecNonQuery("vacuum;")
|
||||
End Sub
|
||||
|
||||
'Agrega una columna a la tabla especificada.
|
||||
'Hay que indicar el "tipo" de la columna (TEXT, INTEGER, ETC)
|
||||
'Ej. agregaColumna(DB, "TABLA", "COLUMNA", "TIPO")
|
||||
Sub agregaColumna(db As SQL, tabla As String, columna As String, tipo As String) 'ignore
|
||||
Try 'Intentamos usar "pragma_table_info" para revisar si existe la columna en la tabla
|
||||
Private c As Cursor = db.ExecQuery($"SELECT COUNT(*) AS fCol FROM pragma_table_info('${tabla}') WHERE name='${columna}'"$)
|
||||
c.Position = 0
|
||||
If c.GetString("fCol") = 0 Then 'Si no esta la columna la agregamos
|
||||
db.ExecNonQuery($"ALTER TABLE ${tabla} ADD COLUMN ${columna} ${tipo}"$)
|
||||
Log($"Columna "${columna} ${tipo}", agregada a "${tabla}"."$)
|
||||
End If
|
||||
Catch 'Si no funciona "pragma_table_info" lo hacemos con try/catch
|
||||
Try
|
||||
db.ExecNonQuery($"ALTER TABLE ${tabla} ADD COLUMN ${columna} ${tipo}"$)
|
||||
Log($"Columna "${columna} ${tipo}", agregada a "${tabla}".."$)
|
||||
Catch
|
||||
Log(LastException)
|
||||
End Try
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'Regresa los nombres y tipos de las columnas de una tabla dada.
|
||||
Sub getTableColumnList(db As SQL, table As String) As String 'ignore
|
||||
Private l As String = ""
|
||||
If chkIfTableExists(db, table) Then
|
||||
Private c As Cursor = db.ExecQuery($"pragma table_info(${table})"$)
|
||||
If c.RowCount > 0 Then
|
||||
For i = 0 To c.RowCount - 1
|
||||
c.Position = i
|
||||
If l.Length = 0 Then
|
||||
l = $"${c.GetString("name")} ${c.GetString("type")}"$
|
||||
Else
|
||||
l = $"${l}, ${c.GetString("name")} ${c.GetString("type")}"$
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Else
|
||||
Return "No existe la tabla."
|
||||
End If
|
||||
c.Close
|
||||
Return l
|
||||
End Sub
|
||||
|
||||
'Regresa solo los nombres de las columnas de una tabla dada.
|
||||
Sub getTableColumnListName(db As SQL, table As String) As String 'ignore
|
||||
Private l As String = ""
|
||||
If chkIfTableExists(db, table) Then
|
||||
Private c As Cursor = db.ExecQuery($"pragma table_info(${table})"$)
|
||||
If c.RowCount > 0 Then
|
||||
For i = 0 To c.RowCount - 1
|
||||
c.Position = i
|
||||
If l.Length = 0 Then
|
||||
l = $"${c.GetString("name")}"$
|
||||
Else
|
||||
l = $"${l}, ${c.GetString("name")}"$
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Else
|
||||
Return "No existe la tabla."
|
||||
End If
|
||||
c.Close
|
||||
Return l
|
||||
End Sub
|
||||
|
||||
'Revisa si una tabla dada existe en una base de datos dada, regresa true o false.
|
||||
Sub chkIfTableExists(db As SQL, table As String) As Boolean 'ignore
|
||||
Private t As Boolean = False 'ignore
|
||||
Private c As Cursor = db.ExecQuery($"SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'"$)
|
||||
If c.RowCount > 0 Then t = True
|
||||
c.Close
|
||||
Return t
|
||||
End Sub
|
||||
|
||||
'Hace visible y trae al frente el panel con los parametros "Top" y "Left" dados
|
||||
Sub panelVisible(panel As Panel, top As Int, left As Int) 'ignore
|
||||
panel.BringToFront
|
||||
panel.Visible = True
|
||||
panel.Top = top
|
||||
panel.Left = left
|
||||
End Sub
|
||||
|
||||
'Centra una etiqueta dentro de un elemento superior
|
||||
Sub centraEtiqueta(elemento As Label, anchoElementoSuperior As Int) 'ignore
|
||||
elemento.Left = Round(anchoElementoSuperior/2)-(elemento.Width/2)
|
||||
etiquetaCentrada(True)
|
||||
End Sub
|
||||
|
||||
Sub etiquetaCentrada(Success As Boolean)
|
||||
If SubExists(CallBack, EventName & "_etiquetaCentrada") Then
|
||||
CallSub2(CallBack, EventName & "_etiquetaCentrada", Success)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'Centra un panel horizontalmente dentro de un elemento superior
|
||||
Sub centraPanel(elemento As Panel, anchoElementoSuperior As Int) 'ignore
|
||||
elemento.Left = Round(anchoElementoSuperior/2)-(elemento.Width/2)
|
||||
End Sub
|
||||
|
||||
'Centra un panel verticalmente dentro de un elemento superior
|
||||
Sub centraPanelV(elemento As Panel, altoElementoSuperior As Int) 'ignore
|
||||
elemento.Top = Round(altoElementoSuperior/2)-(elemento.Height/2)
|
||||
End Sub
|
||||
|
||||
'Centra una barra de progreso dentro de un elemento superior
|
||||
Sub centraProgressBar(elemento As ProgressBar, anchoElementoSuperior As Int) 'ignore
|
||||
elemento.Left = Round(anchoElementoSuperior/2)-(elemento.Width/2)
|
||||
End Sub
|
||||
|
||||
'Regresa el usuario de la tabla USUARIOA, si no lo encuentra regresa "SinUsuario"
|
||||
Sub traeUsuarioDeDB(db As SQL) As String 'ignore
|
||||
Private c As Cursor
|
||||
Private u As String = "SinUsuario"
|
||||
c = db.ExecQuery("select USUARIO from usuarioa")
|
||||
c.Position=0
|
||||
If c.RowCount > 0 Then u = c.GetString("USUARIO")
|
||||
c.Close
|
||||
Return u
|
||||
End Sub
|
||||
|
||||
'Regresa el almacen actual de la base de datos.
|
||||
Sub traeAlmacen(db As SQL) As String 'ignore
|
||||
Private c As Cursor
|
||||
Private a As String
|
||||
c = db.ExecQuery("select ID_ALMACEN from CAT_ALMACEN")
|
||||
c.Position = 0
|
||||
a = c.GetString("ID_ALMACEN")
|
||||
c.Close
|
||||
Return a
|
||||
End Sub
|
||||
|
||||
'Regresa el nombre del producto desde CAT_GUNAPROD.
|
||||
Sub traeProdNombre(db As SQL, id As String) As String 'ignore
|
||||
Private h As Cursor
|
||||
Private n As String
|
||||
h = db.ExecQuery2("select CAT_GP_NOMBRE from CAT_GUNAPROD where CAT_GP_ID = ? ", Array As String(id.Trim))
|
||||
If h.RowCount > 0 Then
|
||||
h.Position = 0
|
||||
n = h.GetString("CAT_GP_NOMBRE")
|
||||
' Log(h.RowCount&"|"&id&"|"&n&"|")
|
||||
End If
|
||||
h.Close
|
||||
If n = Null Or n="" Then n = "N/A"
|
||||
' Log(h.RowCount&"|"&id&"|"&n&"|")
|
||||
Return n
|
||||
End Sub
|
||||
|
||||
'Regresa la ruta actual de la base de datos.
|
||||
Sub traeRuta(db As SQL) As String 'ignore
|
||||
Private c As Cursor
|
||||
Private r As String
|
||||
c = db.ExecQuery("select CAT_CL_RUTA from kmt_info where CAT_CL_CODIGO In (Select cuenta from cuentaa)")
|
||||
r = "0"
|
||||
If c.RowCount > 0 Then
|
||||
c.Position=0
|
||||
r = c.GetString("CAT_CL_RUTA")
|
||||
End If
|
||||
c.Close
|
||||
Return r
|
||||
End Sub
|
||||
|
||||
'Regresa el cliente actual de la base de dtos (CUENTAA).
|
||||
Sub traeCliente(db As SQL) As String 'ignore
|
||||
Private c As Cursor
|
||||
Private cl As String
|
||||
c = db.ExecQuery("Select CUENTA from cuentaa")
|
||||
c.Position=0
|
||||
cl = c.GetString("CUENTA")
|
||||
c.Close
|
||||
Return cl
|
||||
End Sub
|
||||
|
||||
'Regresa en un mapa el total de productos y monto del pedido del cliente actual
|
||||
'resultado = {"productos":"antidad_de_productos", "monto":"costo_total"}
|
||||
Sub traeTotalesClienteActual(db As SQL) As Map 'ignore
|
||||
Private m As Map
|
||||
m.Initialize
|
||||
Private c_prodsX As Cursor = db.ExecQuery("select ifnull(sum(PE_CANT), 0) as cantProds, ifnull(sum(PE_COSTO_TOT), 0) as costoTotal FROM PEDIDO WHERE PE_CLIENTE IN (Select CUENTA from cuentaa) order by PE_PRONOMBRE asc")
|
||||
c_prodsX.Position = 0
|
||||
' LogColor($"Productos de la orden: ${c_prodsX.GetString("cantProds")}, Total: ${c_prodsX.GetString("costoTotal")}"$, Colors.red)
|
||||
m = CreateMap("productos": c_prodsX.GetString("cantProds"), "monto" : c_prodsX.GetString("costoTotal"))
|
||||
Return m
|
||||
End Sub
|
||||
|
||||
'Borra el pedido del cliente actual.
|
||||
'Borra los registros de la tabla "PEDIDO" y "PEDIDO_CLIENTE"
|
||||
'Actualiza las tablas "cat_gunaprod" y "kmt_info".
|
||||
Sub borraPedidoClienteActual(db As SQL) As String 'ignore
|
||||
Private thisC As Cursor
|
||||
thisC = db.ExecQuery("select PE_PROID,PE_CANT FROM PEDIDO where pe_cliente in (Select CUENTA from cuentaa) ")
|
||||
If thisC.RowCount>0 Then
|
||||
For i=0 To thisC.RowCount -1
|
||||
thisC.Position=i
|
||||
db.ExecNonQuery2("update cat_gunaprod set cat_gp_almacen = cat_gp_almacen + ? where cat_gp_id = ?", Array As Object(thisC.GetString("PE_CANT"),thisC.GetString("PE_PROID")))
|
||||
db.ExecNonQuery2("INSERT INTO INVENT_X_ENVIAR (ALMACEN , PROID , CANTIDAD ) VALUES(?,?,?) ", Array As Object (traeAlmacen(db), thisC.GetString("PE_PROID"),thisC.GetString("PE_CANT")* -1))
|
||||
Next
|
||||
End If
|
||||
db.ExecNonQuery("delete from pedido_cliente where pc_cliente in (Select CUENTA from cuentaa)")
|
||||
db.ExecNonQuery("delete from pedido where pe_cliente in (Select CUENTA from cuentaa)")
|
||||
db.ExecNonQuery("UPDATE kmt_info set gestion = 0 where CAT_CL_CODIGO In (select cuenta from cuentaa)")
|
||||
Return 1
|
||||
End Sub
|
||||
|
||||
'Regresa verdadero si la columna gestion en la tabla "kmt_info" tene valor 2.
|
||||
'si no, entonces regresa falso.
|
||||
Sub pedidoGuardado(db As SQL) As Boolean 'ignore
|
||||
Private guardado As Boolean = False
|
||||
Private g As Cursor = db.ExecQuery("select gestion from kmt_info where CAT_CL_CODIGO in (Select CUENTA from cuentaa)")
|
||||
If g.RowCount > 0 Then
|
||||
g.Position=0
|
||||
If g.GetString("gestion") = "2" Then guardado = True
|
||||
End If
|
||||
Log($"Guardado=${guardado}"$)
|
||||
Return guardado
|
||||
End Sub
|
||||
|
||||
'Regresa verdadero si hay pedido en la tabla "PEDIDO" del cliente actual.
|
||||
Sub hayPedido(db As SQL) As Boolean 'ignore
|
||||
Private thisC As Cursor = db.ExecQuery($"select count(PE_CLIENTE) as hayPedido from PEDIDO where PE_CLIENTE = '${traeCliente(db)}'"$)
|
||||
thisC.Position = 0
|
||||
Private hay As Boolean = False
|
||||
If thisC.GetInt("hayPedido") > 0 Then hay = True
|
||||
' Log($"Cliente actual=${traeCliente}, hayPedido=${hay}"$)
|
||||
Return hay
|
||||
End Sub
|
||||
|
||||
'Guarda el nombre y version de la app en CAT_VARIABLES.
|
||||
Sub guardaAppInfo(db As SQL) 'ignore
|
||||
db.ExecNonQuery("delete from CAT_VARIABLES where CAT_VA_DESCRIPCION = 'EMPRESA' or CAT_VA_DESCRIPCION = 'APP_NAME' or CAT_VA_DESCRIPCION = 'APP_VERSION'")
|
||||
db.ExecNonQuery($"insert into CAT_VARIABLES (CAT_VA_DESCRIPCION, CAT_VA_VALOR) values ('APP_NAME', '${Application.LabelName}')"$)
|
||||
db.ExecNonQuery($"insert into CAT_VARIABLES (CAT_VA_DESCRIPCION, CAT_VA_VALOR) values ('APP_VERSION', '${Application.VersionName}')"$)
|
||||
End Sub
|
||||
|
||||
'Muestra en el Log los campos y valores que regresan en el JobDone.
|
||||
'Sub logJobDoneResultados(resultado As DBResult) 'ignore
|
||||
' For Each records() As Object In resultado.Rows
|
||||
' LogColor($"====== ${resultado.Tag} - REGISTROS = ${resultado.Rows.Size}"$, Colors.RGB(215,37,0))
|
||||
' For Each k As String In resultado.Columns.Keys
|
||||
' LogColor(k & " = " & records(resultado.Columns.Get(k)), Colors.RGB(215,37,0))
|
||||
' Next
|
||||
' Next
|
||||
'End Sub
|
||||
Reference in New Issue
Block a user