B4A=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=12.8
@EndOfDesignText@
Sub Class_Globals
	Dim ruta As String
	Dim almacen As String
	Dim cliente As String
	Dim db As SQL
	Private EventName As String 'ignore
	Private CallBack As Object 'ignore
	Dim tablaProds As String
	Dim muestraLogs As Boolean
	Dim releaseMode As Boolean
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize (vCallback As Object, vEventName As String, db0 As SQL) As Object
	EventName = vEventName
	CallBack = vCallback
	db = db0
'	Log("PROMOS INIT")
	db.ExecNonQuery("CREATE TABLE IF NOT EXISTS PROMOCIONES_INFO (P_ID TEXT, P_ESTATUS TEXT, P_MAXXCLIENTE NUMERIC, P_MAXRECURRENTE NUMERIC, P_MAXPROMOS NUMERIC, HISTORICO TEXT, P_TIPOS TEXT, P_PRODSFIJOS TEXT, P_PRODSFIJOS_CANT NUMERIC, P_PRODSFIJOS_PIEZAS TEXT, P_PRODSFIJOS_PRECIOS TEXT, P_PRODSVARIABLES TEXT, P_PRODSVARIABLES_CANT NUMERIC, P_PRODSVARIABLES_PRECIOS TEXT, P_PRODSVARIABLES_REQUERIDOS NUMERIC)")
	db.ExecNonQuery("DELETE FROM PROMOCIONES_INFO")
	#if RELEASE
		releaseMode = True
	#Else if DEBUG
		releaseMode = False
	#end if
	Return Me
End Sub

Sub procesaPromocion(idPromo As String, cliente0 As String, tipoVenta As String) As Map 'ignore
	'######################################################
			muestraLogs = True ' Si es verdadero, se muestran los logs de la clase.
	'######################################################
	If releaseMode Then muestraLogs = False
	cliente = cliente0
'	Private inicioContador As String = DateTime.Now
	Private maxPromos As Int = 0
	Private maxPromosXFijos As Int = 0
	tablaProds = "cat_gunaprod2"
	If tipoVenta = "ABORDO" Or tipoVenta = "PREVENTA" Then tablaProds = "cat_gunaprod"
	If muestraLogs Then LogColor($"**********************************************************************"$, Colors.Green)
	If muestraLogs Then LogColor($"*********   INICIAMOS REVISION DE PROMO ${idPromo}   *********"$, Colors.Green)
	If muestraLogs Then LogColor($"**********************************************************************"$, Colors.Green)
'	LogColor("****************************   " & traeInfoDePromo("P_PRODSFIJOS", idPromo), Colors.blue)
	traePromo(idPromo, cliente0) ' Generamos la tabla con la información d la promoción.
	maxPromosXFijos = revisaMaxPromosProdsFijosPorInventario(idPromo)
	If maxPromosXFijos < 1 Then
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_ESTATUS = 'ko' where P_ID = '${idPromo}'"$)
	End If
	Private logColor1 As String
	If traeInfoDePromo("P_ESTATUS", idPromo) = "ok" Then logColor1 = Colors.RGB(0,143,0) Else logColor1 = Colors.Red
	If muestraLogs And traeInfoDePromo("P_ESTATUS", idPromo) = "ok" Then LogColor($"### MAX PROMOS X FIJOS = ${maxPromosXFijos}"$, logColor1)
	If traeInfoDePromo("P_ESTATUS", idPromo) = "ok" Then 'Si encontramos la promoción, entonces ...
		maxPromos = traeMaxPromos(idPromo) ' Buscamos el máximo de promociones permitidas.
		If muestraLogs Then LogColor($"Promociones permitidas=${maxPromos}"$, Colors.Blue)
'		If muestraLogs Then Log("Promos vendidas: " & traePromosVendidas(idPromo))
		If maxPromos < 1 Then
			If muestraLogs Then LogColor("Ya se vendieron las promos PERMITIDAS para el cliente", Colors.red)
			Return CreateMap("status":"ko") ' , "mp":pm
		End If
		'👇🏼 Restamos del inventario (mapa) las piezas necesarias para los productos fijos. 👇🏼
		Private inventarioSinFijos As Map = restaFijosDePromo(idPromo)
		If muestraLogs Then LogColor("inventarioSinFijos="&inventarioSinFijos, Colors.Magenta)
		If inventarioSinFijos.Get("resultado") = "ok" Then
			'Revisamos que los productos variables requeridos sean menos que el inventario total (mapa).
			Private pv As Boolean =  alcanzanLosVariablesParaPromo(idPromo, inventarioSinFijos)
			If pv Then logColor1 = Colors.RGB(0,143,0) Else logColor1 = Colors.Red
			If muestraLogs And pv Then
				LogColor("### ALCANZAN LOS VARIABLES? --> " & pv, logColor1)
			End If
'			LogColor("TIEMPO DE PROCESO ESTA PROMO: " & ((DateTime.Now-inicioContador)/1000), Colors.Red)
			If pv Then
				Return CreateMap("status":"ok", "prodsFijosCant":traeInfoDePromo("P_PRODSFIJOS_CANT", idPromo), "prodsVariablesCant":traeInfoDePromo("P_PRODSVARIABLES_CANT", idPromo))
			Else
				If muestraLogs Then LogColor("NO HAY INVENTARIO SUFICIENTE " & idPromo, Colors.red)
				Return CreateMap("status":"ko")
			End If
		Else
			If muestraLogs Then LogColor("NO HAY INVENTARIO SUFICIENTE " & idPromo, Colors.red)
'			LogColor("TIEMPO DE PROCESO ESTA PROMO: " & ((DateTime.Now-inicioContador)/1000), Colors.Red)
			Return CreateMap("status":"ko")
		End If
	Else
		If muestraLogs Then LogColor("NO HAY INVENTARIO SUFICIENTE " & idPromo, Colors.red)
'		LogColor("TIEMPO DE PROCESO ESTA PROMO: " & ((DateTime.Now-inicioContador)/1000), Colors.Red)
		Return CreateMap("status":"ko") ' , "mp":pm
	End If
	' Si tenemos suficiente inventario para los variables mostramos la promocion, si no ...
	' 		break	'NO HAY INVENTARIO SUFICIENTE PARA LA PROMOCION.
End Sub

'Genera una tabla (PROMOCIONES_INFO) con la información de la promo.
'Columnas: P_ID, P_MAXXCLIENTE, P_MAXRECURRENTE, P_MAXPROMOS, HISTORICO,
'	P_TIPOS={idProducto=tipo} 'Mapa con id y tipo del producto, 0 si es fijo y 1 si es variable.
'	P_PRODSFIJOS=[idProducto,idProducto] 'Lista con los ids de los productos fijos.
'	P_PRODSVARIABLES=[idProducto,idProducto] 'Lista con los ids de los productos variables.
'	P_ESTATUS="OK" 'Ok si existe la promocion.
'	P_PRODSVARIABLES_REQUERIDOS=5 'Cantidad de productos variables requeridos para la promoción.
Sub traePromo(promo As String, cliente0 As String) As Map
	'######################################################
			muestraLogs = True ' Si es verdadero, se muestran los logs de la clase.
	'######################################################
	If releaseMode Then muestraLogs = False
	If muestraLogs Then LogColor("> TraePromo: " & promo, Colors.blue)
'	Private inicioContador As String = DateTime.Now
	Private c As Cursor = Starter.skmt.ExecQuery("Select * from promos_comp where cat_pa_id = '"& promo&"'") 'Obtenemos las el maximo de promocioones a otorgar.
	Private siHistorico As String = 0
	Private promoMap As Map
	Private prodsFijos, prodsFijosPrecios, prodsFijosReq, prodsVariables, prodsVariablesPrecios As List
	promoMap.Initialize
	prodsFijos.Initialize
	prodsFijosPrecios.Initialize
	prodsFijosReq.Initialize
	prodsVariables.Initialize
	prodsVariablesPrecios.Initialize
	c.Position = 0
'	promoMap = CreateMap("id":promo, "maxXcliente":0, "maxRecurrente":0, "maxPromos":0) 'Ponemos el DEFAULT en CERO.
	Private yaExistePromo As Cursor = db.ExecQuery($"SELECT P_ID from PROMOCIONES_INFO where P_ID = '${promo}'"$)
	If yaExistePromo.RowCount > 0 Then
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_MAXXCLIENTE = 0, P_MAXRECURRENTE = 0, P_MAXPROMOS = 0 where P_ID = '${promo}'"$)
	Else
		db.ExecNonQuery($"insert into PROMOCIONES_INFO (P_ID, P_MAXXCLIENTE, P_MAXRECURRENTE, P_MAXPROMOS) values ('${promo}', 0, 0, 0)"$)		
	End If
	If c.RowCount > 0 Then
'		promoMap = CreateMap("id":promo, "maxXcliente":c.GetString("CAT_PA_MAXPROMCLIE"), "maxRecurrente":c.GetString("CAT_PA_MAXPROMREC"), "maxPromos":c.GetString("CAT_PA_MAXPROM"))
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_MAXXCLIENTE = ${c.GetInt("CAT_PA_MAXPROMCLIE")}, P_MAXRECURRENTE = ${c.GetInt("CAT_PA_MAXPROMREC")}, P_MAXPROMOS = ${c.GetInt("CAT_PA_MAXPROM")} where P_ID = '${promo}'"$)
'		Log($"update PROMOCIONES_INFO set P_MAXXCLIENTE = ${c.GetInt("CAT_PA_MAXPROMREC")}, P_MAXRECURRENTE = ${c.GetInt("CAT_PA_MAXPROMREC")}, P_MAXPROMOS = ${c.GetInt("CAT_PA_MAXPROM")} where P_ID = '${promo}'"$)
	End If
	'#########   PROMOCIONES SEGMENTADAS   ##########
'	Si la promo esta en la lista, SOLO los clientes que la tengan la pueden ver.
	Private ps As Cursor = Starter.skmt.ExecQuery($"select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${cliente0}' "$)
'	Log($"select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${cliente0}' "$)
	If muestraLogs Then Log($"PROMOS SEGMENTADAS DISPONIBLES: ${ps.RowCount}"$)
	If ps.RowCount > 0 Then
		ps.Position = 0
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_MAXXCLIENTE = 0, P_MAXRECURRENTE = 0, P_MAXPROMOS = 0 where P_ID = '${promo}'"$) 'Si es segmentada la ponemos en cero, porque SOLO le debe de aparecer a ciertos clientes.
'		promoMap = CreateMap("id":promo, "maxXcliente":0, "maxRecurrente":0, "maxPromos":0) 'Si es segmentada la ponemos en cero, porque SOLO le debe de aparecer a ciertos clientes.
		If muestraLogs Then LogColor($">>> PROMO "${promo}" SEGMENTADA PARA EL CLIENTE ${ps.GetString("HCCP_CLIENTE")}"$, Colors.Magenta)
'		Select hccp_cliente As valido from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${traecliente}'
		Private ps2 As Cursor = Starter.skmt.ExecQuery($"Select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${cliente0}' and HCCP_CANT > HCCP_CANT_VENDIDA"$)
'		Log($"Select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${traeCliente}' and HCCP_CANT > HCCP_CANT_VENDIDA"$)
		If muestraLogs Then Log($"registros:${ps2.RowCount}"$)
		If ps2.RowCount > 0 Then
			ps2.Position = 0
'			Log($"registros:${ps2.RowCount}, cliente: ${ps2.GetString("HCCP_CLIENTE")} cant:${ps2.GetString("HCCP_CANT")}, Vendidas: ${ps2.GetString("HCCP_CANT_VENDIDA")}"$)
			db.ExecNonQuery($"update PROMOCIONES_INFO set P_MAXXCLIENTE = ${(ps2.getint("HCCP_CANT")-ps2.GetInt("HCCP_CANT_VENDIDA"))}, P_MAXRECURRENTE = ${ps2.GetString("HCCP_CANT")}, P_MAXPROMOS = ${ps2.GetString("HCCP_CANT")} where P_ID = '${promo}'"$) 'Si es segmentada SOLO le aparece a ciertos clientes.
'			Log($"update PROMOCIONES_INFO set P_MAXXCLIENTE = ${(ps2.GetString("HCCP_CANT")-ps2.GetString("HCCP_CANT_VENDIDA"))}, P_MAXRECURRENTE = ${ps2.GetString("HCCP_CANT")}, P_MAXPROMOS = ${ps2.GetString("HCCP_CANT")} where P_ID = '${promo}'"$)
'			promoMap = CreateMap("id":promo, "maxXcliente":(ps2.GetString("HCCP_CANT")-ps2.GetString("HCCP_CANT_VENDIDA")), "maxRecurrente":ps2.GetString("HCCP_CANT"), "maxPromos":ps2.GetString("HCCP_CANT")) 'Si es segmentada SOLO le aparece a ciertos clientes.
		End If
	End If
'	Log(promoMap)
'	Log($"P_MAXXCLIENTE = ${(ps2.GetString("HCCP_CANT")-ps2.GetString("HCCP_CANT_VENDIDA"))}, P_MAXRECURRENTE = ${ps2.GetString("HCCP_CANT")}, P_MAXPROMOS = ${ps2.GetString("HCCP_CANT")}'"$)
'	##########   TERMINA PROMOS SEGMENTADAS   ##########
	c = Starter.skmt.ExecQuery("Select count(*) as hist from HIST_PROMOS where HP_CLIENTE = '"& cliente0 & "' and HP_CODIGO_PROMOCION = '" & promo & "'") 'Revisamos si hay historico de la promoción.
	c.Position = 0
	If c.GetString("hist") > 0 Then siHistorico = 1
	db.ExecNonQuery($"update PROMOCIONES_INFO set HISTORICO = ${siHistorico} where P_ID = '${promo}'"$)
'	promoMap.Put("historico", siHistorico)
	c = Starter.skmt.ExecQuery("Select * from CAT_DETALLES_PAQ where CAT_DP_ID = '"& promo & "'") 'Obtenemos los detalles de la promoción.
	c.Position = 0
	If c.RowCount > 0 Then
		Private prods, tipos As Map
		prods.Initialize
		tipos.Initialize
		For i = 0 To c.RowCount -1
			c.Position=i
			prods.Put(c.GetString("CAT_DP_IDPROD"), CreateMap("idProducto":c.GetString("CAT_DP_IDPROD"), "precioSimptos":c.GetString("CAT_DP_PRECIO_SIMPTOS"), "precio":c.GetString("CAT_DP_PRECIO"), "tipo":c.GetString("CAT_DP_TIPO"), "piezas":c.GetString("CAT_DP_PZAS"), "regalo":c.GetString("CAT_DP_REGALO"), "clasif":c.GetString("CAT_DP_CLASIF")))
			tipos.Put(c.GetString("CAT_DP_IDPROD"), c.GetString("CAT_DP_TIPO"))
			If c.GetString("CAT_DP_TIPO") = "0" Then
				prodsFijos.Add(c.GetString("CAT_DP_IDPROD"))
				prodsFijosPrecios.Add(c.GetString("CAT_DP_PRECIO"))
				prodsFijosReq.Add(c.GetString("CAT_DP_PZAS"))
			End If
			If c.GetString("CAT_DP_TIPO") = "1" Then
				prodsVariables.Add(c.GetString("CAT_DP_IDPROD"))
				prodsVariablesPrecios.Add(c.GetString("CAT_DP_PRECIO"))
			End If
'			If muestraLogs Then Log($"id:${c.GetString("CAT_DP_IDPROD")}, tipo:${c.GetString("CAT_DP_TIPO")}"$)
		Next
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_TIPOS = '${tipos}', P_PRODSFIJOS = '${prodsFijos}', P_PRODSFIJOS_CANT = ${prodsFijos.Size}, P_PRODSFIJOS_PRECIOS = '${prodsFijosPrecios}', P_PRODSFIJOS_PIEZAS = '${prodsFijosReq}', P_PRODSVARIABLES = '${prodsVariables}', P_PRODSVARIABLES_CANT = ${prodsVariables.Size}, P_PRODSVARIABLES_PRECIOS = '${prodsVariablesPrecios}', P_ESTATUS = 'ok' where P_ID = '${promo}'"$)
'		promoMap.Put("productos", prods) 'Mapa con los productos de la promocion (id, precio, almacen, tipo, piezas, etc.)
'		promoMap.Put("tipos", tipos) 'Mapa con los productos de la promoción y su tipo (fijo o variable).
'		promoMap.Put("prodsFijos", prodsFijos) 'Lista de los productos fijos de la promoción.
'		promoMap.Put("prodsFijosCant", prodsFijos.Size)
'		promoMap.Put("prodsFijosPrecios", prodsFijosPrecios)
'		promoMap.Put("prodsFijosReq", prodsFijosReq)
'		promoMap.Put("prodsVariables", prodsVariables) 'Lista de los productos variables de la promoción.
'		promoMap.Put("prodsVariablesCant", prodsVariables.Size)
'		promoMap.Put("prodsVariablesPrecios", prodsVariablesPrecios)
'		promoMap.Put("resultado", "ok")
	Else
'		promoMap.Put("resultado", "No hay datos de la promoción.")
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_ESTATUS = 'No hay datos de la promoción.' where P_ID = '${promo}'"$)
	End If
	c = Starter.skmt.ExecQuery($"Select CAT_GP_STS, CAT_GP_NOMBRE from ${tablaProds} where CAT_GP_ID = '"$& promo & "'") 'Obtenemos las piezas requeridas de productos variables para la promoción.
	c.Position = 0
	Private pvr As String = 0
	If c.RowCount > 0 Then
		c.Position = 0
		pvr = c.GetString("CAT_GP_STS")
'		promoMap.Put("prodsVariablesRequeridos", pvr) 'Cantidad de productos variables requeridos para la promoción.
		promoMap.put("descripcion", c.GetString("CAT_GP_NOMBRE"))
		db.ExecNonQuery($"update PROMOCIONES_INFO set P_PRODSVARIABLES_REQUERIDOS = ${pvr} where P_ID = '${promo}'"$) 'Cantidad de productos variables requeridos para la promoción.
	End If
	c.Close
'	If muestraLogs Then Log($"Inv variables: ${cuantosVariablesDisponiblesDB(promo)}"$)
'	If muestraLogs Then Log($"Inv dispo: ${traemosInvDispParaPromo(promo)}"$)
'	If muestraLogs Then LogColor($"Promo ${promo}: ${promoMap}"$, Colors.Blue)
'	If muestraLogs Then LogColor("TIEMPO para traePromo -=" & promo & "=- : " & ((DateTime.Now-inicioContador)/1000), Colors.Blue)
	Return promoMap
End Sub

' Regresa cuantas promos alcanzan con los productos FIJOS que hay en inventario.
Sub revisaMaxPromosProdsFijosPorInventario(idPromo As String) As Int
	If muestraLogs Then LogColor("> revisaMaxPromosProdsFijosPorInventario: " & idPromo, Colors.blue)
	Private maxPromosPermitidas As List
	Private promosDispEsteProd, InvDispEsteProd, pzasReqEsteProd As Int
	Private esteColor As Double
	Private esteMensaje As String
	maxPromosPermitidas.Initialize
	maxPromosPermitidas.Add(traeMaxPromos(idPromo)) ' Agregamos a la lista las promos maximas permitidas (por cliente, recurrentes y maxpromos).
	' 👇🏼 Traemos los productos fijos y piezas requeridas de la promoción. 👇🏼
	Private c As ResultSet = db.ExecQuery($"select CAT_DP_IDPROD, sum(CAT_DP_PZAS) as totalPzas from CAT_DETALLES_PAQ where CAT_DP_ID = '${idPromo}' and CAT_DP_TIPO = '0' group by CAT_DP_IDPROD"$)
	Do While c.NextRow
		InvDispEsteProd = traeInventario(c.GetString("CAT_DP_IDPROD"))
		pzasReqEsteProd = c.GetString("totalPzas")
		esteColor = Colors.green
		esteMensaje = "INVENTARIO OK"
		If pzasReqEsteProd > InvDispEsteProd Then
			esteColor = Colors.red
			esteMensaje = "INVENTARIO INSUFICIENTE"
		End If
		If muestraLogs Then LogColor($"PROD FIJO=${c.GetString("CAT_DP_IDPROD")}, INV=${InvDispEsteProd}, REQ=${pzasReqEsteProd} --> ${esteMensaje}"$, esteColor)
		promosDispEsteProd = (InvDispEsteProd/pzasReqEsteProd).As(Int) 'Agregamos el as(int) para que solo nos regrese la parte entera de la division.
		maxPromosPermitidas.Add(promosDispEsteProd) ' Agregamos a la lista las promociones permitidas por inventario de este producto.
	Loop
	maxPromosPermitidas.Sort(True) ' Ordenamos la lista para que el primer elemento sea el MAS pequeño.
	If muestraLogs Then LogColor("Max promos de prodsFijos POR inventario = " & maxPromosPermitidas.Get(0), Colors.Magenta)
	Return maxPromosPermitidas.Get(0) 'Regresamos el MENOR numero de promociones permitidas.
End Sub

'Regresa cuantas promos alcanzan con los productos VARIABLES que hay en inventario.
'La cantidad de promos disponibles se calcula DESPUES de descontar los productos fijos, y si las
'promos por productos fijos llegan al maximo, aunque se puedan mas de producos variables, solo se
'regresa el maximo por productos fijos. Ej. si las promos por variables es 10, pero el maximo por
'fijos es 5, entonces regresamos 5.
Sub revisaMaxPromosProdsVariablesPorInventario(idPromo As String) As Int 'ignore
	If muestraLogs Then LogColor("> revisaMaxPromosProdsVariablesPorInventario: " & idPromo, Colors.blue)
	Private invFijoXpromo As Map
	invFijoXpromo.Initialize
	Private totalProdsVariablesDisponibles As Int = 0
'	If muestraLogs Then LogColor($"pm=${pm}"$, Colors.Blue)
	Private invDispParaPromo As Map = traemosInvDispParaPromo(idPromo)
'	If muestraLogs Then 
		Log($"invDispParaPromo=${invDispParaPromo}"$)
	Private maxPromos As String = traeMaxPromos(idPromo)
	Private maxPromosXFijos As Int = revisaMaxPromosProdsFijosPorInventario(idPromo)
	Private idProdsVariables As List = traeListaDePromo("P_PRODSVARIABLES", idPromo)
	Private prodsVariablesRequeridos As Int = traeInfoDePromo("P_PRODSVARIABLES_REQUERIDOS", idPromo)
	Private prodsFijosReq As List = traeListaDePromo("P_PRODSFIJOS_PIEZAS", idPromo)
	Private idProdsFijos As List = traeListaDePromo("P_PRODSFIJOS", idPromo)
	For p = 0 To idProdsFijos.Size -1 'Generamos mapa con los productos fijos y piezas requeridas por promo.
		invFijoXpromo.Put(idProdsFijos.Get(p), prodsFijosReq.Get(p))
		Log(idProdsFijos)
		Private idEsteProd As String = idProdsFijos.Get(p)
		Log(idEsteProd)
		Private invEsteProd As Int = invDispParaPromo.Get(idEsteProd)
		Private pzasReqEsteProd As Int = prodsFijosReq.Get(p)
		If muestraLogs Then Log($"id=${idEsteProd}, inv=${invEsteProd}, pzas=${pzasReqEsteProd}"$)
'		invDispParaPromo.Put(   idEsteProd, (invEsteProd - (1))   )
	Next
	If muestraLogs Then LogColor($"MaxPromos=${maxPromos}, promosXFijos=${maxPromosXFijos}"$, Colors.Blue)
	If muestraLogs Then LogColor($"prodsFijos=${idProdsFijos}"$, Colors.Blue)
	If muestraLogs Then LogColor($"prodsFijosReq=${prodsFijosReq}"$, Colors.Blue)
	If muestraLogs Then LogColor($"prodsVariables=${idProdsVariables}${CRLF}Variables Req=${prodsVariablesRequeridos} "$, Colors.Blue)
	If muestraLogs Then LogColor($"invFijoXpromo=${invFijoXpromo}"$, Colors.Blue)
	If muestraLogs Then Log($"Prods variables disponibles = ${totalProdsVariablesDisponibles}"$)
	Private maxPromosXVariables As Int = 0
	If Not(releaseMode) Then muestraLogs = True
	For x = 1 To maxPromosXFijos
		If muestraLogs Then Log("=====================================================")
		For i = 0 To idProdsFijos.Size - 1
'			Log(prodsFijosReq.Get(i))
			Private prodsFijosRequeridos As Int = prodsFijosReq.Get(i)
			Private esteProdFijo As String = idProdsFijos.Get(i)
'			Log(invDispParaPromo)
			If muestraLogs Then Log($"FIJO - ${idProdsFijos.Get(i)}, ${invDispParaPromo.Get(esteProdFijo)} - ${prodsFijosRequeridos*(i+1)}"$)
			invDispParaPromo.Put(esteProdFijo, invDispParaPromo.Get(esteProdFijo) - prodsFijosRequeridos*(i+1)) 'Restamos las piezas de los productos fijos del inventario disponible.
		Next
		If muestraLogs Then LogColor("Inv disponible despues de restar fijos = " & invDispParaPromo, Colors.Blue)
		totalProdsVariablesDisponibles = 0
		For i = 0 To idProdsVariables.Size - 1 'Obtenemos total de productos variables disponibes.
			Private thisProdVariable As String = idProdsVariables.Get(i)
			If invDispParaPromo.ContainsKey(thisProdVariable) Then
				totalProdsVariablesDisponibles = totalProdsVariablesDisponibles + invDispParaPromo.Get(thisProdVariable)
			End If
		Next
		'Revisamos variables.
		If muestraLogs Then Log($"Var disponibles -  var requeridos : ${totalProdsVariablesDisponibles} - ${prodsVariablesRequeridos*x}"$)
		totalProdsVariablesDisponibles = totalProdsVariablesDisponibles - (prodsVariablesRequeridos*x)
		If muestraLogs Then Log("prodsVariables disponibles despues de promo = " & totalProdsVariablesDisponibles)
		If totalProdsVariablesDisponibles < 0 Then Exit 'Ya no hay inventario disponible.
		maxPromosXVariables = x
	Next
	'Restamos fijos.
'	LogColor("Max promos de prodsVariables POR inventario = " & maxPromosXVariables, Colors.red)
	Return maxPromosXVariables
End Sub

'Regresa un mapa con el inventario disponible por producto para la promoción (desde la base de datos).
Sub traemosInvDispParaPromo(idPromo As String) As Map 'ignore
	If muestraLogs Then LogColor("> traemosInvDispParaPromo: " & idPromo, Colors.blue)
	Private c As Cursor
	c = Starter.skmt.ExecQuery2($"SELECT CAT_GP_ID, CAT_GP_ALMACEN FROM ${tablaProds} WHERE CAT_GP_ID IN (select CAT_DP_IDPROD FROM CAT_DETALLES_PAQ WHERE CAT_DP_ID = ?)"$, Array As String(idPromo))
'	Private prodInv As Map
'	prodInv.Initialize
	Private prods As Map
	prods.Initialize
	If c.RowCount > 0 Then
		For i = 0 To c.RowCount -1
			c.Position = i
			prods.Put(c.GetString("CAT_GP_ID"), c.GetString("CAT_GP_ALMACEN"))
'			Log($"prod:${c.GetString("CAT_GP_ID")}, inventario:${c.GetString("CAT_GP_ALMACEN")}"$)
		Next
'		prodInv.Put("inventarios", prods)
	End If
	Return prods
End Sub

'Resta los productos fijos del inventario de la promoción y regresa un mapa con el nuevo inventario.
'Hay que darle como parametro el Id de la promocion.
'Regresa en el mapa la llave "resultado" que nos da "ok" o "No hay suficiente producto para la promocion".
Sub restaFijosDePromo(idPromo As String) As Map 'ignore
	If muestraLogs Then LogColor("> restaFijosDePromo: " & idPromo, Colors.blue)
	Private invDispEstaPromo As Map = traemosInvDispParaPromo(idPromo) 'Obtenemos un mapa con el inventario disponible para cada producto de la promocion desde la base de datos.
	If muestraLogs Then LogColor("Inventario inicial antes de FIJOS: "&invDispEstaPromo, Colors.Magenta) 'Inventario inicial.
	Private InvDispEsteProd, pzasReqEsteProd As Int
	Private esteProd As String
	invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") 'Valor por DEFAULT.
	Private nuevoInv As Int
	' 👇🏼 Traemos los productos fijos y piezas requeridas de la promoción. 👇🏼
	Private c As ResultSet = db.ExecQuery($"select CAT_DP_IDPROD, sum(CAT_DP_PZAS) as totalPzas from CAT_DETALLES_PAQ where CAT_DP_ID = '${idPromo}' and CAT_DP_TIPO = '0' group by CAT_DP_IDPROD"$)
	Do While c.NextRow
		esteProd = c.GetString("CAT_DP_IDPROD")
		pzasReqEsteProd = c.GetString("totalPzas")
		If muestraLogs Then Log($"esteProd ${esteProd}, piezasReq: ${pzasReqEsteProd}"$) 'Producto y piezas requeridas
		If muestraLogs Then Log("invDispEstaPromo="&invDispEstaPromo)
		If invDispEstaPromo.ContainsKey(esteProd) Then 'Si el mapa del inventario contiene el id del producto entonces ...
			InvDispEsteProd = invDispEstaPromo.get(esteProd) 'Obtenemos del mapa el inventario de este producto.
			nuevoInv = NumberFormat2((InvDispEsteProd - pzasReqEsteProd), 1, 0,0,False)
			If muestraLogs Then Log($"Nuevo inventario de ${esteProd}: ${InvDispEsteProd}-${pzasReqEsteProd} = ${nuevoInv}"$) 'El nuevo inventario.
			invDispEstaPromo.Put(esteProd.Trim, nuevoInv) 'Restamos del inventario las piezas requeridas para la promoción y guardamos el nuevo inventario en el mapa.
			invDispEstaPromo.Put("resultado", "ok")
		Else 'Si en el mapa no esta el id del producto, entonces no tenemos inventario.
			invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.")
			If muestraLogs Then LogColor("Sin suficiente inventario fijo: " & esteProd, Colors.Blue)
			Exit
		End If
		If nuevoInv < 0 Then
			invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") 'Si el inventario de este producto sale negativo, quiere decir que no tenemos suficiente inventario para la promoción.
			Exit
		End If
	Loop
	If muestraLogs Then LogColor("Inventario final despues de FIJOS: "&invDispEstaPromo, Colors.blue) 'Inventario final.
	Return invDispEstaPromo
End Sub

'Revisa si tenemos los productos variables requeridos para la promoción (mapa).
'Hay que darle como parametro un mapa (traePromo(promo)) con toda la informacion de la promocion.
Sub alcanzanLosVariablesParaPromo(idPromo As String, inventarioSinFijos As Map) As Boolean 'ignore
	If muestraLogs Then LogColor("> alcanzanLosVariablesParaPromo: " & idPromo, Colors.blue)
	If muestraLogs Then LogColor("Inventario inicial: "&inventarioSinFijos, Colors.Gray) 'Inventario inicial.
	Private totalProdsVariables As Int = 0
'	Private prodsmap As Map = promoMap.Get("productos") 'Obtenemos un mapa con todos los productos de la promoción.
	Private prodsVariables As List = traeListaDePromo("P_PRODSVARIABLES", idPromo) ' promoMap.get("prodsVariables") 'Obtenemos un a lista con los productos variables de la promoción.
	For p = 0 To prodsVariables.Size - 1
		Private t As String = prodsVariables.Get(p) 'Obtenemos el Id de este producto desde la lista de productos fijos.
		If inventarioSinFijos.ContainsKey(t) Then 'Si existe el producto en la lista del inventario, entonces ...
			Private p2 As Int = inventarioSinFijos.Get(t) 'Obtenemos el inventario disponible este producto.
			If muestraLogs Then Log($"prod ${t}, hay: ${p2}"$) 'Producto y piezas requeridas
			totalProdsVariables = totalProdsVariables + p2
		End If
	Next
	Private prodsVariablesRequeridos As Int = traeInfoDePromo("P_PRODSVARIABLES_REQUERIDOS", idPromo)
	If muestraLogs Then Log("Total prods variables=" & totalProdsVariables & ", requeridos=" & prodsVariablesRequeridos)
	Private res As Boolean = False
	If totalProdsVariables >= prodsVariablesRequeridos Then res = True 'Si el total de inventario de productos variables (totalProdsVariables) es mayor o igual a los productos requeridos entonces regresamos TRUE
	Return res
End Sub

'Regresa el numero máximo de promociones permitidas, tomando en cuenta recurrentes, clientes y maxPromos.
Sub traeMaxPromos(idPromo As String) As Int
	If muestraLogs Then LogColor("> traeMaxPromos: " & idPromo, Colors.blue)
	Private maxPromos As List
	Private mp0, mp As String = "0"
	maxPromos.Initialize
	Private hccp As Cursor = db.ExecQuery($"select HCCP_CANT from HIST_CLIENTE_CANT_PROMOS where HCCP_CLIENTE = '${cliente}' and HCCP_PROMO = '${idPromo}'"$)
	If muestraLogs Then LogColor("Historico: "&traeInfoDePromo("HISTORICO", idPromo), Colors.Blue)
'	If muestraLogs Then Log(pm)
	If traeInfoDePromo("HISTORICO", idPromo) = "1" Then maxPromos.Add(traeInfoDePromo("P_MAXRECURRENTE", idPromo).As(Int)) 'Si hay historico, agregamos maxRecurrente
	If traeInfoDePromo("P_MAXPROMOS", idPromo) <> "null" Then maxPromos.Add(traeInfoDePromo("P_MAXPROMOS", idPromo).As(Int)) 'Agregamos maxPromos
	If traeInfoDePromo("P_MAXXCLIENTE", idPromo) <> "null" Then maxPromos.Add(traeInfoDePromo("P_MAXXCLIENTE", idPromo).As(Int)) 'Agregamos maxXcliente
	If hccp.RowCount > 0 Then 'Agregamos promos HCCP
		hccp.Position = 0
		maxPromos.Add(hccp.GetInt("HCCP_CANT"))
	End If
'	Log($"|${traeInfoDePromo("HISTORICO", idPromo)}|${traeInfoDePromo("P_MAXPROMOS", idPromo)}|${traeInfoDePromo("P_MAXXCLIENTE", idPromo)}|"$)
'	Log(maxPromos)
	maxPromos.Sort(True)
'	If muestraLogs Then Log($"Max promos : ${maxPromos}"$)
	mp = 0
	If maxPromos.Size > 0 Then
		mp0 = maxPromos.Get(0)
'		Log(mp0)
		Private promosVendidas As Int = traePromosVendidas(idPromo)
		mp = mp0 - promosVendidas
		If muestraLogs Then Log($"Max Promos (${mp0}) - Promos Vendidas (${(promosVendidas)}) = Promos Disponibles (${mp})"$)
	End If
	Return mp 'Regresamos el numero mas pequeño de las opciones.
End Sub

'Regresa la cantidad de promos que se le han vendido al cliente.
Sub traePromosVendidas(idPromo As String) As Int
	If muestraLogs Then LogColor("> traePromosVendidas: " & idPromo, Colors.blue)
	Private c As Cursor
	Private pv As Int = 0
	c=db.ExecQuery($"select sum(PE_CANT) as cuantas from PEDIDO where PE_PROID = '${idPromo}' and PE_CLIENTE = '${cliente}'"$)
	If c.RowCount > 0 Then
		c.Position = 0
		If c.GetString("cuantas") <> Null Then pv = c.GetString("cuantas")
	End If
	Return pv
End Sub

'Trae el valor de la columna especificada de la promo dada.
Sub traeInfoDePromo(col As String, idPromo As String) As String
	Private res As String = ""
	Private c As ResultSet = db.ExecQuery($"select ${col} from PROMOCIONES_INFO where P_ID = '${idPromo}'"$)
'	Log($"select ${col} from PROMOCIONES_INFO where P_ID = '${idPromo}'"$)
	Do While c.NextRow
		res = c.GetString(col)
	Loop
	Return res
End Sub

'Regresa una lista de la BD, en la BD se guarda como string y esta funcion la convierte en lista nuevamente. 
Sub traeListaDePromo(col As String, promo As String) As List
'	Log($"${col}, ${promo}"$)
	Private res As List
	Private s As String
	Private c As ResultSet = db.ExecQuery($"select ${col} from PROMOCIONES_INFO where P_ID = '${promo}'"$)
	Do While c.NextRow
		s = c.GetString(col)
	Loop
	If s.StartsWith("[") And s.EndsWith("]") Then 
'		Private j As JSONParser
'		j.Initialize(s)
'		res.Initialize2(j.NextArray)
		res.Initialize
		s = s.Replace("[", "")
		s = s.Replace("]", "")
		Private s2() As String = Regex.Split(",", s)
		For i = 0 To s2.Length - 1
			res.Add(s2(i).as(String).trim)
		Next
	Else
		res.Initialize
	End If
	Return res
End Sub

'Regresa una lista de la BD, en la BD se guarda como string y esta funcion la convierte en lista nuevamente. 
Sub traeMapaDePromo(col As String, promo As String) As List
	Private res As List
	Private s As String
	Private c As ResultSet = db.ExecQuery($"select ${col} from PROMOCIONES_INFO where P_ID = '${promo}'"$)
	Do While c.NextRow
		s = c.GetString(col)
	Loop
	If s.StartsWith("{") And s.EndsWith("}") Then 
		Private j As JSONParser
		j.Initialize(s)
		res.Initialize2(j.NextArray)
	Else
		res.Initialize
	End If
	Return res
End Sub

'Regresa el inventario de un producto dado o CERO si no lo encuentra.
Sub traeInventario(id As String) As Int
	Private inv As Int = 0
	Private c As ResultSet = db.ExecQuery($"select CAT_GP_ALMACEN from ${tablaProds} where CAT_GP_ID = ${id}"$)
	Do While c.NextRow
		inv = c.GetInt("CAT_GP_ALMACEN")
	Loop
	Return inv
End Sub






'Regresa cuantas promos alcanzan con los productos FIJOS que hay en inventario.
Sub revisaMaxPromosProdsFijosPorInventarioXXX(id As String) As Int
	If muestraLogs Then LogColor("> revisaMaxPromosProdsFijosPorInventario: " & id, Colors.blue)
'	Private invFijoXpromo As Map
	Private t As List
	t.Initialize
	t.Add(traeMaxPromos(id)) ' Agregamos a la lista las promos maximas permitidas (recurrente, cliente y promo).
'	invFijoXpromo.Initialize
'	If muestraLogs Then LogColor($"pm=${pm}"$, Colors.Blue)
	Private prodsFijosReq As List = traeListaDePromo("P_PRODSFIJOS_PIEZAS", id)
	Private idProdsFijos As List = traeListaDePromo("P_PRODSFIJOS", id)
	If muestraLogs Then Log($"ProdsFijos: ${idProdsFijos}, PiezasReq: ${prodsFijosReq}"$)
	Private invDispParaPromo As Map = traemosInvDispParaPromo(id)
	If muestraLogs Then Log(">>> " & $"invDispParaPromo=${invDispParaPromo}"$)
	For p = 0 To idProdsFijos.Size -1 'Generamos una lista con las promos disponibles por producto (dividimos el inventario total entre las piezas requeridas).
		Private thisInvDisp As Int = 0
		If invDispParaPromo.Get(idProdsFijos.Get(p).As(String)) <> Null Then
			thisInvDisp = invDispParaPromo.Get(idProdsFijos.Get(p).As(String))
		End If
		Private esteColor As Double = Colors.green
		Private esteMensaje As String = "INVENTARIO OK"
		If prodsFijosReq.Get(p) > thisInvDisp Then
			esteColor = Colors.red
			esteMensaje = "INVENTARIO INSUFICIENTE"
		End If
		If muestraLogs Then LogColor($"PROD=${idProdsFijos.Get(p)}, INV=${thisInvDisp}, REQ=${prodsFijosReq.Get(p)} - ${esteMensaje}"$, esteColor)
'		If muestraLogs Then Log($"${(thisInvDisp / prodsFijosReq.Get(p))}"$)
		Private x() As String = Regex.Split("\.", $"${(thisInvDisp / prodsFijosReq.Get(p))}"$) 'Separamos el resultado de la division por el punto decimal.
'		If muestraLogs Then Log($"PROD: ${idProdsFijos.Get(p)},  INV: ${x(0)}, REQ: ${prodsFijosReq.Get(p)}"$)
		t.Add(x(0).As(Int)) 'Solo guardamos la parte del entero de la division.
	Next
	t.Sort(True) 'Ordenamos la lista para que en el lugar 0 este el resultado mas pequeño.
'	If muestraLogs Then LogColor($"prodsFijos=${idProdsFijos}"$, Colors.Blue)
'	If muestraLogs Then LogColor($"prodsFijosReq=${prodsFijosReq}"$, Colors.Blue)
'	If muestraLogs Then LogColor($"promosProdsFijosDisp=${t}"$, Colors.Blue)
'	If muestraLogs Then LogColor($"invFijoXpromo=${invFijoXpromo}"$, Colors.Blue)
	If muestraLogs Then LogColor("Max promos de prodsFijos POR inventario = " & t.Get(0), Colors.Magenta)
	Return t.Get(0) 'Regresamos el resultado mas pequeño.
End Sub

'Resta los productos fijos del inventario de la promoción y regresa un mapa con el nuevo inventario.
'Hay que darle como parametro el Id de la promocion.
'Regresa en el mapa la llave "resultado" que nos da "ok" o "No hay suficiente producto para la promocion".
Sub restaFijosDePromoXXX(idPromo As String) As Map 'ignore
	If muestraLogs Then LogColor("> restaFijosDePromo: " & idPromo, Colors.blue)
	Private invDispEstaPromo As Map = traemosInvDispParaPromo(idPromo) 'Obtenemos un mapa con el inventario disponible para cada producto de la promocion desde la base de datos.
	If muestraLogs Then LogColor("Inventario inicial antes de FIJOS: "&invDispEstaPromo, Colors.Magenta) 'Inventario inicial.
	Private inv As Int
	Private prodsFijos As List = traeListaDePromo("P_PRODSFIJOS", idPromo) 'Obtenemos una lista con los productos fijos de la promoción.
	Private prodsFijosReq As List = traeListaDePromo("P_PRODSFIJOS_PIEZAS", idPromo) 'Obtenemos un a lista con los productos fijos de la promoción.
	If muestraLogs Then LogColor("ProdsFijos -> " & prodsFijos, Colors.red)
	invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") 'Valor por DEFAULT.
	If muestraLogs Then Log($"Prods fijos requeridos ${prodsFijos.Size}"$)
	If prodsFijos.Size = 0 Then invDispEstaPromo.Put("resultado", "ok") 'Si no lleva prods fijos la promo, entonces ponemos FIJOS OK.
	Private nuevoInv As Int
	For p = 0 To prodsFijos.Size - 1
		Private esteProd As String = prodsFijos.Get(p) 'Obtenemos el Id de este producto desde la lista de productos fijos.
		If muestraLogs Then Log($"esteProd ${esteProd}, piezasReq: ${prodsFijosReq.Get(p)}"$) 'Producto y piezas requeridas
		If muestraLogs Then Log("invDispEstaPromo="&invDispEstaPromo)
		If invDispEstaPromo.ContainsKey(esteProd) Then 'Si el mapa del inventario contiene el id del producto entonces ...
			inv = invDispEstaPromo.get(esteProd) 'Obtenemos del mapa el inventario de este producto.
			nuevoInv = NumberFormat2((inv - prodsFijosReq.get(p)), 1, 0,0,False)
			If muestraLogs Then Log($"Nuevo inventario de ${esteProd}: ${inv}-${prodsFijosReq.get(p)} = ${nuevoInv}"$) 'El nuevo inventario.
			invDispEstaPromo.Put(esteProd, $"${nuevoInv}"$) 'Restamos del inventario las piezas requeridas para la promoción y guardamos el nuevo inventario en el mapa.
			invDispEstaPromo.Put("resultado", "ok")
		Else 'Si en el mapa no esta el id del producto, entonces no tenemos inventario.
			invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.")
			If muestraLogs Then LogColor("Sin suficiente inventario fijo: " & esteProd, Colors.Blue)
			Exit
		End If
		If nuevoInv < 0 Then
			invDispEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") 'Si el inventario de este producto sale negativo, quiere decir que no tenemos suficiente inventario para la promoción.
			Exit
		End If
	Next
	If muestraLogs Then LogColor("Inventario final despues de FIJOS: "&invDispEstaPromo, Colors.blue) 'Inventario final.
	Return invDispEstaPromo
End Sub