B4A=true Group=Default Group ModulesStructureVersion=1 Type=Class Version=12.8 @EndOfDesignText@ 'Class module: C_Promociones Sub Class_Globals Private sql As SQL ' Referencia a tu clase de Trade Spending (ajusta el tipo si se llama diferente) Private ts As Object ' Lo dejo como Object para que no te marque error si no has copiado esa clase aun ' Variable local para el cliente actual (para no depender de Subs.traeCliente) Private mClienteActual As String End Sub ' Inicializa la clase. ' db: La conexión SQL (Starter.skmt) ' tradeSpending: La instancia de tu clase de TradeSpending (B4XPages.MainPage.promos.ts) Public Sub Initialize(db As SQL, tradeSpendingInstance As Object) sql = db ts = tradeSpendingInstance End Sub ' ============================================================================= ' CONTROLADOR PRINCIPAL: ORQUESTADOR DE VALIDACIONES ' ============================================================================= Public Sub procesaPromocion(idPromo As String, cliente As String) As Map Private thisLog As Boolean = True 'Si es verdadero, muestra los logs de este sub. Private inicioContador As String = DateTime.Now Private mp As Int = 0 If thisLog Then LogColor($"****************************************************************"$, Colors.RGB(142,0,255)) If thisLog Then LogColor($"********* Iniciamos revision de Promo ${idPromo} *********"$, Colors.RGB(142,0,255)) If thisLog Then LogColor($"****************************************************************"$, Colors.RGB(142,0,255)) Try Private pm As TPromoLegacy = TraePromo(idPromo, cliente) If thisLog Then LogColor(pm, Colors.Blue) Private maxPromosXprodsFijos As Int = RevisaMaxPromosProdsFijos(pm) If maxPromosXprodsFijos < 1 Then pm.Resultado = "0" If thisLog Then LogColor($"***********************************************************************"$, Colors.Blue) LogColor($"*** PROMOS DISPONIBLES X PRODS FIJOS (${idPromo}) = ${maxPromosXprodsFijos} ***"$, Colors.Blue) If thisLog Then LogColor($"***********************************************************************"$, Colors.Blue) If pm.Resultado = "ok" Then 'Si encontramos la promoción, entonces ... mp = TraeMaxPromos(pm) If mp < 1 Then If thisLog Then LogColor("Ya se vendieron las promos PERMITIDAS para el cliente", Colors.red) Return CreateMap("status":"ko", "mp":pm) End If Private inventarioSinFijos As Map = restaFijosPromo(pm) If thisLog Then LogColor("inventariosfijos="&inventarioSinFijos, Colors.Magenta) If inventarioSinFijos.Get("resultado") = "ok" Then Private pv As Boolean = alcanzanLosVariablesParaPromo(pm, inventarioSinFijos) If thisLog Then LogColor($"****************************************************"$, Colors.Blue) If thisLog Then LogColor($"***** ¿ALCANZAN LOS VARIABLES? ==> ${IIf(pv, "SI", "NO")} *****"$, Colors.Blue) If thisLog Then LogColor($"****************************************************"$, Colors.Blue) If pv Then Return CreateMap("status":"ok", "mp":pm) Else Return CreateMap("status":"ko", "mp":pm) Else If thisLog Then LogColor("NO HAY INVENTARIO SUFICIENTE " & idPromo, Colors.red) Return CreateMap("status":"ko", "mp":pm) End If Else Return CreateMap("status":"ko", "mp":pm) End If LogColor("TIEMPO DE PROCESO ESTA PROMO: " & ((DateTime.Now-inicioContador)/1000), Colors.Red) Catch Log($"Promo ${idPromo} mal configurada"$) ToastMessageShow($"Promo ${idPromo} mal configurada"$, True) Log(LastException) Return CreateMap("status":"ko", "mp":Null) End Try End Sub ' ============================================================================= ' RUTINA PRINCIPAL: ARMA LA ESTRUCTURA (Clon de Subs.traePromo) ' ============================================================================= Public Sub TraePromo(promo As String, cliente As String) As TPromoLegacy mClienteActual = cliente ' Guardamos el cliente para uso interno Dim t As TPromoLegacy t.Initialize ' Inicialización de Listas y Mapas (CRÍTICO para evitar Nulls) t.ProdsFijos.Initialize t.ProdsFijosPiezas.Initialize t.ProdsFijosPrecios.Initialize t.ProdsVariables.Initialize t.ProdsVariablesPrecios.Initialize t.Productos.Initialize t.Tipos.Initialize t.Id = promo t.MaxXCliente = 0 t.MaxRecurrente = 0 t.MaxPromos = 0 ' --- 1. LECTURA GENERAL --- Dim c As Cursor = sql.ExecQuery("Select * from promos_comp where cat_pa_id = '"& promo&"'") If c.RowCount > 0 Then c.Position = 0 t.MaxXCliente = c.GetString("CAT_PA_MAXPROMCLIE") t.MaxRecurrente = c.GetString("CAT_PA_MAXPROMREC") t.MaxPromos = c.GetString("CAT_PA_MAXPROM") End If c.Close ' --- 2. SEGMENTACIÓN --- Dim ps As Cursor = sql.ExecQuery($"select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}'"$) If ps.RowCount > 0 Then ' Es segmentada -> Bloqueamos por defecto t.MaxXCliente = "0" t.MaxRecurrente = "0" t.MaxPromos = "0" Dim ps2 As Cursor = sql.ExecQuery($"Select * from HIST_CLIENTE_CANT_PROMOS where HCCP_PROMO = '${promo}' and HCCP_CLIENTE = '${cliente}' and HCCP_CANT > HCCP_CANT_VENDIDA"$) If ps2.RowCount > 0 Then ps2.Position = 0 t.MaxXCliente = (ps2.GetInt("HCCP_CANT") - ps2.GetInt("HCCP_CANT_VENDIDA")) t.MaxRecurrente = ps2.GetString("HCCP_CANT") t.MaxPromos = ps2.GetString("HCCP_CANT") End If ps2.Close End If ps.Close ' --- 3. HISTÓRICO --- Dim siHistorico As String = "0" c = sql.ExecQuery("Select count(*) as hist from HIST_PROMOS where HP_CLIENTE = '"& cliente & "' and HP_CODIGO_PROMOCION = '" & promo & "'") c.Position = 0 If c.GetInt("hist") > 0 Then siHistorico = "1" t.Historico = siHistorico c.Close ' --- 4. DETALLES Y PRODUCTOS --- c = sql.ExecQuery("Select * from CAT_DETALLES_PAQ where CAT_DP_ID = '"& promo & "'") If c.RowCount > 0 Then t.Resultado = "ok" For i = 0 To c.RowCount -1 c.Position = i Dim idProd As String = c.GetString("CAT_DP_IDPROD") ' Llenamos el mapa de productos (igual que el original para compatibilidad) t.Productos.Put(idProd, CreateMap("idProducto":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"))) t.Tipos.Put(idProd, c.GetString("CAT_DP_TIPO")) If c.GetString("CAT_DP_TIPO") = "0" Then t.ProdsFijos.Add(idProd) t.ProdsFijosPrecios.Add(c.GetString("CAT_DP_PRECIO")) t.ProdsFijosPiezas.Add(c.GetString("CAT_DP_PZAS")) End If If c.GetString("CAT_DP_TIPO") = "1" Then t.ProdsVariables.Add(idProd) t.ProdsVariablesPrecios.Add(c.GetString("CAT_DP_PRECIO")) End If Next Else t.Resultado = "No hay datos de la promoción." t.MensajeError = "Sin detalles" End If c.Close ' --- 5. VARIABLES REQUERIDAS --- c = sql.ExecQuery("Select CAT_GP_STS, CAT_GP_NOMBRE from CAT_GUNAPROD2 where CAT_GP_ID = '"& promo & "'") If c.RowCount > 0 Then c.Position = 0 t.ProdsVariablesRequeridos = c.GetInt("CAT_GP_STS") t.Descripcion = c.GetString("CAT_GP_NOMBRE") Else t.ProdsVariablesRequeridos = 0 End If c.Close Return t End Sub ' ============================================================================= ' LÓGICA DE CÁLCULO 1: FIJOS (Clon de Subs.revisaMaxPromosProdsFijosPorInventario) ' ============================================================================= Public Sub RevisaMaxPromosProdsFijos(pm As TPromoLegacy) As Int Dim tLista As List tLista.Initialize ' 1. Agregamos el máximo configurado tLista.Add(TraeMaxPromos(pm)) ' 2. Obtenemos inventario real Dim invDispParaPromo As Map = TraemosInventarioDisponibleParaPromo(pm.Id) Dim prodsFijosPiezas As List = pm.ProdsFijosPiezas Dim idProdsFijos As List = pm.ProdsFijos Dim idProdsFijosPrecios As List = pm.ProdsFijosPrecios ' 3. Iteramos sobre los fijos For p = 0 To idProdsFijos.Size -1 Dim thisInvDisp As Int = 0 If invDispParaPromo.Get(idProdsFijos.Get(p)) <> Null Then thisInvDisp = invDispParaPromo.Get(idProdsFijos.Get(p)) End If Dim pzasReq As Int = prodsFijosPiezas.Get(p) If pzasReq > 0 Then Dim division As Double = thisInvDisp / pzasReq Dim x() As String = Regex.Split("\.", $"${division}"$) tLista.Add(x(0).As(Int)) ' --- AQUI ESTA TU CAMBIO (DIRECTO Y SIN DOLOR) --- ' Nota: Cambié pm.Get("id") por pm.Id porque ahora es un Type Dim maxTS As Int = B4XPages.MainPage.promos.ts.traeBonificacionesMaximas("bonificaciones", mClienteActual, idProdsFijos.Get(p), pzasReq, idProdsFijosPrecios.Get(p), pm.Id) tLista.Add(maxTS) ' ------------------------------------------------- Else Log($"Promo ${pm.Id} mal configurada (0 piezas requeridas en producto fijo)"$) ToastMessageShow($"Promo ${pm.Id} mal configurada"$, True) tLista.Add(0) End If Next tLista.Sort(True) Return tLista.Get(0) End Sub ' ============================================================================= ' LÓGICA DE CÁLCULO 2: MÁXIMOS (Clon de Subs.traeMaxPromos) ' ============================================================================= Public Sub TraeMaxPromos(pm As TPromoLegacy) As Int Dim maxPromos As List maxPromos.Initialize ' A. Histórico del Cliente (HCCP) Dim hccp As Cursor = sql.ExecQuery($"select HCCP_CANT from HIST_CLIENTE_CANT_PROMOS where HCCP_CLIENTE = '${mClienteActual}' and HCCP_PROMO = '${pm.Id}'"$) If hccp.RowCount > 0 Then hccp.Position = 0 maxPromos.Add(hccp.GetInt("HCCP_CANT")) End If hccp.Close ' B. Trade Spending Variables If ts <> Null Then Dim maxPromosXDescPV As String = B4XPages.MainPage.promos.ts.maxPromosPorProdsVariables(pm.ProdsVariables, pm.Id) maxPromos.Add(maxPromosXDescPV.As(Int)) End If ' C. Límites Generales If pm.Historico = "1" And pm.MaxRecurrente <> "null" Then maxPromos.Add(pm.MaxRecurrente.As(Int)) If pm.MaxPromos <> "null" Then maxPromos.Add(pm.MaxPromos.As(Int)) If pm.MaxXCliente <> "null" Then maxPromos.Add(pm.MaxXCliente.As(Int)) maxPromos.Sort(True) Dim mp As Int = 0 If maxPromos.Size > 0 Then Dim mp0 As Int = maxPromos.Get(0) ' Restamos lo ya vendido mp = mp0 - TraePromosVendidas(pm.Id, mClienteActual) End If Return mp End Sub ' ============================================================================= ' LÓGICA DE CÁLCULO 3: RESTA INVENTARIO FIJO (Clon de Subs.restaFijosPromo) ' ============================================================================= Public Sub restaFijosPromo(pm As TPromoLegacy) As Map Private thisLog As Boolean = False 'Si es verdadero, muestra los logs de este sub. Private inventariosDisponiblesParaEstaPromo As Map = TraemosInventarioDisponibleParaPromo(pm.Id) 'Obtenemos un mapa con el inventario disponible para cada producto de la promocion desde la base de datos. If thisLog Then LogColor(inventariosDisponiblesParaEstaPromo, Colors.red) If thisLog Then LogColor("Inventario inicial antes de FIJOS: "&inventariosDisponiblesParaEstaPromo, Colors.Gray) 'Inventario inicial. Private i As Int Private prodsmap As Map = pm.productos 'Obtenemos un mapa con todos los productos de la promoción. Private prodsFijos As List = pm.ProdsFijos 'Obtenemos una lista con los productos fijos de la promoción. If thisLog Then LogColor("ProdsFijos -> " & prodsFijos, Colors.red) inventariosDisponiblesParaEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") 'Valor por default If thisLog Then Log($"Prods fijos requeridos ${prodsFijos.Size}"$) If prodsFijos.Size = 0 Then inventariosDisponiblesParaEstaPromo.Put("resultado", "ok") 'Si no lleva prods fijos la promo, entonces ponemos FIJOS OK. For p = 0 To prodsFijos.Size - 1 Private t As String = prodsFijos.Get(p) 'Obtenemos el Id de este producto desde la lista de productos fijos. Private p2 As Map = prodsmap.Get(t) 'Obtenemos un mapa con los datos de este producto (id, precio, almacen, tipo, piezas, etc.) If thisLog Then Log($"T: ${t}, prod ${p2.Get("idProducto")}, piezas: ${p2.Get("piezas")}"$) 'Producto y piezas requeridas If thisLog Then Log("inventariosDisponiblesParaEstaPromo="&inventariosDisponiblesParaEstaPromo) If inventariosDisponiblesParaEstaPromo.ContainsKey(t) Then 'Si el mapa del inventario contiene el id del producto entonces ... i = inventariosDisponiblesParaEstaPromo.get(t) 'Obtenemos del mapa el inventario de este producto. Private nuevoInv As Int = NumberFormat2((i - pm.ProdsFijosPiezas.Get(p)), 1, 0,0,False) If thisLog Then Log($"Nuevo inventario de ${t}: ${i}-${pm.ProdsFijosPiezas.Get(p)} = ${nuevoInv}"$) 'El nuevo inventario. inventariosDisponiblesParaEstaPromo.Put(t, $"${nuevoInv}"$) 'Restamos del inventario las piezas requeridas para la promoción y guardamos el nuevo inventario en el mapa. inventariosDisponiblesParaEstaPromo.Put("resultado", "ok") Else 'Si en el mapa no esta el id del producto, entonces no tenemos inventario. inventariosDisponiblesParaEstaPromo.Put("resultado", "No hay suficiente producto para la promocion.") If thisLog Then LogColor("Sin suficiente inventario fijo: " & t, Colors.Blue) Exit End If If i - p2.Get("piezas") < 0 Then inventariosDisponiblesParaEstaPromo.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 thisLog Then LogColor("Inventario final despues de FIJOS: "&inventariosDisponiblesParaEstaPromo, Colors.blue) 'Inventario final. Return inventariosDisponiblesParaEstaPromo End Sub ' ============================================================================= ' LÓGICA DE CÁLCULO 4: VARIABLES (Clon de Subs.alcanzanLosVariablesParaPromo) ' ============================================================================= Public Sub alcanzanLosVariablesParaPromo(pm As TPromoLegacy, inventarioSinFijos As Map) As Boolean Private thisLog As Boolean = False 'Si es verdadero, muestra los logs de este sub. If thisLog Then LogColor("Inventario inicial: "&inventarioSinFijos, Colors.Gray) 'Inventario inicial. Private totalProdsVariables As Int = 0 Private prodsVariables As List = pm.ProdsVariables 'Obtenemos la 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 thisLog Then Log($"prod ${t}, hay: ${p2}"$) 'Producto y piezas requeridas totalProdsVariables = totalProdsVariables + p2 End If Next If thisLog Then Log("Total prods variables=" & totalProdsVariables & ", requeridos=" & pm.ProdsVariablesRequeridos) Private res As Boolean = False If totalProdsVariables >= pm.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 ' ============================================================================= ' HELPERS PRIVADOS (Para hacer la clase portable sin depender de Subs.bas) ' ============================================================================= Private Sub TraePromosVendidas(promo As String, cliente As String) As Int Dim c As Cursor Dim pv As Int = 0 c = sql.ExecQuery($"select sum(PE_CANT) as cuantas from PEDIDO where PE_PROID = '${promo}' and PE_CLIENTE = '${cliente}'"$) If c.RowCount > 0 Then c.Position = 0 If c.GetString("cuantas") <> Null Then pv = c.GetInt("cuantas") End If c.Close Return pv End Sub Private Sub TraemosInventarioDisponibleParaPromo(promo As String) As Map Dim c As Cursor c = sql.ExecQuery2("SELECT CAT_GP_ID, CAT_GP_ALMACEN FROM CAT_GUNAPROD2 WHERE CAT_GP_ID IN (select CAT_DP_IDPROD FROM CAT_DETALLES_PAQ WHERE CAT_DP_ID = ?)", Array As String(promo)) Dim 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")) Next End If c.Close Return prods End Sub