FragmentTicketVenta

  
  
  package com.example.ventas2

import android.graphics.Color
import android.graphics.Typeface
import android.os.Bundle
import android.view.GestureDetector
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.ListView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import kotlin.math.abs

class MenuPruebaActivity : AppCompatActivity() {

    private val ruta = mutableListOf()

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_menu_prueba)

        val sidePanel = findViewById(R.id.sidePanel)
        val rootLayout = findViewById(R.id.main)

        sidePanel.post {
            sidePanel.translationX = -sidePanel.width.toFloat()
        }

        val contentFrame = findViewById(R.id.contentFrame)
        val tvRuta = findViewById(R.id.tvRuta)

        val listView = ListView(this)
        contentFrame.addView(listView)

        /////////////////////////////////////////////////////////

        data class NodoMenu(
            val nombre: String,
            val hijos: List = emptyList(),
            val unidad: String? = null
        )

        val menuPrincipal = listOf(

            NodoMenu("Inicio"),

            NodoMenu(
                "Ventas",
                listOf(
                    NodoMenu(
                        "Cierres",
                        listOf(
                            NodoMenu(
                                "Común",
                                listOf(
                                    NodoMenu("Rojo", unidad = "u"),
                                    NodoMenu("Azul", unidad = "u")
                                )
                            ),
                            NodoMenu("Diente de Perro", unidad = "u"),
                            NodoMenu("Metal", unidad = "u")
                        )
                    ),
                    NodoMenu(
                        "Botones",
                        listOf(
                            NodoMenu("Redondos", unidad = "u"),
                            NodoMenu("Cuadrados", unidad = "u")
                        )
                    ),
                    NodoMenu(
                        "Cintas",
                        listOf(
                            NodoMenu("Negra", unidad = "m"),
                            NodoMenu("Roja", unidad = "m")
                        )
                    )
                )
            ),

            NodoMenu(
                "Configuracion",
                listOf(
                    NodoMenu("Agregar"),
                    NodoMenu("Editar"),
                    NodoMenu("Eliminar")
                )
            ),

            NodoMenu("Salir")
        )

        var nivelActual = menuPrincipal
        val pila = mutableListOf>()

        fun actualizarLista() {
            val nombres = nivelActual.map { it.nombre }
            listView.adapter = ArrayAdapter(
                this,
                android.R.layout.simple_list_item_1,
                nombres
            )
        }

        actualizarLista()


        fun abrirMenu() {
            sidePanel.animate()
                .translationX(0f)
                .setDuration(200)
                .start()
        }

        fun cerrarMenu() {
            sidePanel.animate()
                .translationX(-sidePanel.width.toFloat())
                .setDuration(200)
                .start()
        }

        var startX = 0f

        rootLayout.setOnTouchListener { _, event ->
            when (event.action) {

                MotionEvent.ACTION_DOWN -> {
                    startX = event.rawX
                    true
                }

                MotionEvent.ACTION_UP -> {
                    val delta = event.rawX - startX

                    if (delta > 120) {
                        abrirMenu()
                    } else if (delta < -120) {
                        cerrarMenu()
                    }
                    true
                }

                else -> false
            }
        }

        /*       fun actualizarRuta() {
                   tvRuta.text = if (ruta.isEmpty()) {
                       "Ventas"
                   } else {
                       "Ventas > " + ruta.joinToString(" > ")
                   }
               }
       */
        fun actualizarRuta() {
            tvRuta.text = ruta.joinToString(" > ")
        }

        fun animarLista(direccion: Int) {
            val fromX = if (direccion > 0) 1f else -1f

            listView.translationX = fromX * listView.width

            val width = if (listView.width == 0) 300 else listView.width
            listView.translationX = fromX * width

            listView.animate()
                .translationX(0f)
                .setDuration(180)
                .start()
        }

        fun volverNivel() {
            if (pila.isNotEmpty()) {
                nivelActual = pila[pila.size - 1]
                pila.removeAt(pila.size - 1)


                if (ruta.isNotEmpty()) {
                    ruta.removeAt(ruta.size - 1) // o ruta.removeAt(ruta.lastIndex)
                }
                actualizarRuta()

                actualizarLista()
                animarLista(-1)
            }
        }

        val gestureDetector = GestureDetector(this,
            object : GestureDetector.SimpleOnGestureListener() {

                private val SWIPE_THRESHOLD = 100
                private val SWIPE_VELOCITY_THRESHOLD = 100

                override fun onFling(
                    e1: MotionEvent?,
                    e2: MotionEvent,
                    velocityX: Float,
                    velocityY: Float
                ): Boolean {
                    val diffX = e2.x - (e1?.x ?: 0f)
                    val diffY = e2.y - (e1?.y ?: 0f)

                    if (abs(diffX) > abs(diffY) &&
                        abs(diffX) > SWIPE_THRESHOLD &&
                        abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
                    )
                    // if (kotlin.math.abs(diffX) > SWIPE_THRESHOLD &&
                    //     kotlin.math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
                    // )
                    {
                        if (diffX > 0) {
                            volverNivel()
                        }

                        return true
                    }
                    return false
                }
            })
        listView.setOnTouchListener { _, event ->
            gestureDetector.onTouchEvent(event)
            false
        }

        listView.setOnItemClickListener { _, _, position, _ ->

            val seleccionado = nivelActual[position]

            if (seleccionado.hijos.isNotEmpty()) {
                pila.add(nivelActual)
                nivelActual = seleccionado.hijos

                ruta.add(seleccionado.nombre)
                actualizarRuta()

                actualizarLista()
                animarLista(1)
            } else {
                cerrarMenu()

                Toast.makeText(
                    this,
                    "Producto: ${seleccionado.nombre}",
                    Toast.LENGTH_SHORT
                ).show()

                // Acá después conectamos con tu diálogo real
            }
        }

    }
}
 
  
  




MenuPruebaActivity

    
      package com.example.ventas2

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.text.InputType
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import java.io.File
import java.io.FileOutputStream
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
//import java.util.logging.Handler

import android.os.Handler
import android.os.Looper
import android.graphics.BitmapFactory


class FragmentTicketVenta : Fragment() {

    // NO CIERRA NUNCA
//    override fun onDestroy() {
//        super.onDestroy()
//        NotificationManagerCompat.from(requireContext()).cancel(1001)
    //   }

    // CIERRA AL CERRAR LA APP DEBERÍA
    override fun onDestroy() {
        super.onDestroy()
         //NotificationManagerCompat.from(this).cancel(1001)
         NotificationManagerCompat.from(requireContext()).cancel(1001)
    //NotificationManagerCompat.from(requireActivity()).cancel(1001)
    }
    // Se cierra sola al minimizar
//    override fun onStop() {
//        super.onStop()
    //NotificationManagerCompat.from(this).cancel(1001)
//        NotificationManagerCompat.from(requireActivity()).cancel(1001)
//    }
    // NO CIERRA NUNCA
 //   override fun onDestroyView() {
 //       super.onDestroyView()
 //       NotificationManagerCompat.from(requireContext()).cancel(1001)
 //   }

    private var totalGeneral = 0.0
    lateinit var textTotalGeneral: TextView
    lateinit var listView: ListView
    lateinit var tableProductos: TableLayout

    lateinit var tableHeader: TableLayout
    lateinit var buttonGuardar: Button


    //   lateinit var textFecha: TextView

    private val lineas = mutableListOf()

    data class LineaTicket(
        val producto: String,
        val cantidad: Double,
        val unidad: String,
        val precioUnit: Double,
        val total: Double
    )

    private val productos = mapOf(
        "Botones" to "u",
        "Lentejuelas" to "g",
        "Elásticos" to "m"
    )
    private val nombresProductos = productos.keys.toList()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_ticket_venta, container, false)

        //////////////////////////////////////////////////////////////////
        crearCanalNotificacion()
        pedirPermisoNotificaciones()

        // Solo para probar (cambialo a 10 segundos)
        Handler(Looper.getMainLooper()).postDelayed({

            val horaActual = SimpleDateFormat("HH:mm", Locale("es","AR"))
                .format(Date())

            mostrarNotificacion("Son las $horaActual")

        }, 10_000L) // 10 segundos

        //val intent = Intent(requireContext(), MenuPruebaActivity::class.java)
        //startActivity(intent)

        /*
        val toolbar = view.findViewById(R.id.toolbar)

    toolbar.setOnClickListener {
            requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, FragmentMenuPrueba())
                .addToBackStack(null)
                .commit()
        }

    */
        /////////////////////////////////////////////////////////////////

        textTotalGeneral = view.findViewById(R.id.textTotalGeneral)
        listView = view.findViewById(R.id.listView)
        tableHeader = view.findViewById(R.id.tableHeader)
        tableProductos = view.findViewById(R.id.tableProductos)
        buttonGuardar = view.findViewById(R.id.buttonGuardar)

        buttonGuardar.visibility = View.GONE

        buttonGuardar.setOnClickListener {
            //    val nombreArchivo = "venta_${System.currentTimeMillis()}.txt"
            //    generarComprobantePNG(nombreArchivo)
            /*    val exito = generarComprobantePNG(nombreArchivo)   //guardarVentaEnDescargas(nombreArchivo)
                if (exito) {
                    AlertDialog.Builder(requireContext())
                        .setTitle("¡Venta guardada!")
                        .setMessage("Archivo: $nombreArchivo\nUbicación: Descargas")
                        .setPositiveButton("OK", null)
                        .show()
                } else {
                    Toast.makeText(requireContext(), "Error al guardar la venta", Toast.LENGTH_LONG).show()
                }
                */
            val editText = EditText(requireContext())
            editText.hint = "Nombre del comprador"

            AlertDialog.Builder(requireContext())
                .setTitle("Nombre del cliente")
                .setView(editText)
                .setPositiveButton("Generar") { _, _ ->
                    val nombre = editText.text.toString()
                    generarComprobantePNG(nombre)
                }
                .setNegativeButton("Cancelar", null)
                .show()
        }

        textTotalGeneral.setOnLongClickListener {
            nuevaVenta()
            true
        }

        agregarFilaTitulo()

        val adapter = ArrayAdapter(
            requireContext(),
            android.R.layout.simple_list_item_1,
            nombresProductos
        )
        listView.adapter = adapter

        listView.setOnItemClickListener { _, _, position, _ ->

            val producto = nombresProductos[position]
            val unidad = productos[producto]!!

            val inputCantidad = EditText(requireContext())
            inputCantidad.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL

            AlertDialog.Builder(requireContext())
                .setTitle("Cantidad")
                .setMessage("Ingrese cantidad para $producto ($unidad)")
                .setView(inputCantidad)
                .setPositiveButton("OK") { _, _ ->

                    val cantidadStr = inputCantidad.text.toString()
                    if (cantidadStr.isNotEmpty()) {

                        val cantidad = cantidadStr.toDouble()

                        val inputPrecio = EditText(requireContext())
                        inputPrecio.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL

                        AlertDialog.Builder(requireContext())
                            .setTitle("Precio Unitario")
                            .setMessage("Ingrese precio unitario para $producto")
                            .setView(inputPrecio)
                            .setPositiveButton("OK") { _, _ ->

                                val precioStr = inputPrecio.text.toString()
                                if (precioStr.isNotEmpty()) {

                                    val precioUnit = precioStr.toDouble()
                                    val totalLinea = cantidad * precioUnit

                                    agregarProducto(
                                        producto,
                                        cantidad,
                                        unidad,
                                        precioUnit,
                                        totalLinea
                                    )
                                }
                            }
                            .setNegativeButton("Cancelar", null)
                            .show()
                    }
                }
                .setNegativeButton("Cancelar", null)
                .show()
        }


        return view
    }

    private fun formatearCantidad(cantidad: Double, unidad: String): String {
        return if (unidad == "u") {
            "${cantidad.toInt()}u"
        } else {
            "${"%.2f".format(cantidad)}$unidad"
        }
    }
    private fun generarComprobantePNG(nombreCliente: String): Boolean {

        return try {

            val inflater = LayoutInflater.from(requireContext())
            val view = inflater.inflate(R.layout.layout_comprobante, null)

            val textFecha = view.findViewById(R.id.textFecha)

            val fechaActual = SimpleDateFormat("dd/MM/yyyy - HH:mm", Locale("es", "AR"))
            textFecha.text = fechaActual.format(Date())

            val layoutLineas = view.findViewById(R.id.layoutLineas)
            val txtTotal = view.findViewById(R.id.txtTotal)

            layoutLineas.removeAllViews()

            var totalGeneral = 0.0

            //           val view = inflater.inflate(R.layout.layout_comprobante, null)

            val nombreArchivo = "venta_${System.currentTimeMillis()}.png"

            val txtNombre = view.findViewById(R.id.txtCliente)
            txtNombre.text = nombreCliente

            val headerLayout = LinearLayout(requireContext())
            headerLayout.orientation = LinearLayout.HORIZONTAL
            headerLayout.layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )

            val paramsProducto = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f)
            val paramsOtros = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)

            fun crearHeader(texto: String, params: LinearLayout.LayoutParams, gravity: Int): TextView {
                return TextView(requireContext()).apply {
                    layoutParams = params
                    text = texto
                    textSize = 13f
                    setTypeface(null, Typeface.BOLD)
                    setTextColor(Color.DKGRAY)
                    this.gravity = gravity
                }
            }

            headerLayout.addView(crearHeader("Producto", paramsProducto, Gravity.START))
            headerLayout.addView(crearHeader("Cant.", paramsOtros, Gravity.END))
            headerLayout.addView(crearHeader("P.Unit", paramsOtros, Gravity.END))
            headerLayout.addView(crearHeader("Total", paramsOtros, Gravity.END))

            layoutLineas.addView(headerLayout)



            for (linea in lineas) {

                val filaLayout = LinearLayout(requireContext())
                filaLayout.orientation = LinearLayout.HORIZONTAL
                filaLayout.layoutParams = LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT
                )

                val paramsProducto = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f)
                val paramsOtros = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)

                val txtProducto = TextView(requireContext())
                txtProducto.layoutParams = paramsProducto
                txtProducto.text = linea.producto
                txtProducto.textSize = 14f

                val txtCantidad = TextView(requireContext())
                txtCantidad.layoutParams = paramsOtros
                txtCantidad.text = formatearCantidad(linea.cantidad, linea.unidad)
                txtCantidad.gravity = Gravity.END
                txtCantidad.textSize = 14f

                val txtPrecio = TextView(requireContext())
                txtPrecio.layoutParams = paramsOtros
                //txtPrecio.text = "$${linea.precioUnit}"
                txtPrecio.text = formatearMoneda(linea.precioUnit)
                txtPrecio.gravity = Gravity.END
                txtPrecio.textSize = 14f

                val txtTotalLinea = TextView(requireContext())
                txtTotalLinea.layoutParams = paramsOtros
                //txtTotalLinea.text = "$${linea.total}"
                txtTotalLinea.text = formatearMoneda(linea.total)
                txtTotalLinea.gravity = Gravity.END
                txtTotalLinea.textSize = 14f

                filaLayout.addView(txtProducto)
                filaLayout.addView(txtCantidad)
                filaLayout.addView(txtPrecio)
                filaLayout.addView(txtTotalLinea)

                layoutLineas.addView(filaLayout)

                totalGeneral += linea.total
            }
            //txtTotal.text = "TOTAL: $${"%.2f".format(totalGeneral)}"
            txtTotal.text = "TOTAL: ${formatearMoneda(totalGeneral)}"

            /*
            view.measure(
                View.MeasureSpec.makeMeasureSpec(800, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.UNSPECIFIED
            )
*/
            val density = resources.displayMetrics.density
            val widthPx = (400 * density).toInt()   // 600dp ancho tipo recibo vertical

            view.measure(
                View.MeasureSpec.makeMeasureSpec(widthPx, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.UNSPECIFIED
            )
            view.layout(0, 0, view.measuredWidth, view.measuredHeight)

            val bitmap = Bitmap.createBitmap(
                view.measuredWidth,
                view.measuredHeight,
                Bitmap.Config.ARGB_8888
            )

            val canvas = Canvas(bitmap)
            view.draw(canvas)

            val file = File(requireContext().cacheDir, "$nombreArchivo.png")

            val fos = FileOutputStream(file)
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
            fos.flush()
            fos.close()

            compartirImagen(file)

            true   // 👈 éxito

        } catch (e: Exception) {
            e.printStackTrace()
            false  // 👈 error
        }
    }


    private fun formatearMoneda(valor: Double): String {
        val formato = NumberFormat.getNumberInstance(Locale("es", "AR"))
        formato.maximumFractionDigits = 0
        formato.minimumFractionDigits = 0
        return "$${formato.format(valor)}"
    }

    private fun compartirImagen(file: File) {

        val uri = FileProvider.getUriForFile(
            requireContext(),
            "${requireContext().packageName}.provider",
            file
        )

        val intent = Intent(Intent.ACTION_SEND)
        intent.type = "image/png"
        intent.putExtra(Intent.EXTRA_STREAM, uri)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

        startActivity(Intent.createChooser(intent, "Compartir comprobante"))
    }

    private fun agregarFilaTitulo() {
        val filaTitulo = TableRow(requireContext())
        val col1 = TextView(requireContext())
        col1.text = "Producto"; col1.setPadding(16,16,16,16); col1.setTextColor(Color.YELLOW)
        val col2 = TextView(requireContext()); col2.gravity = Gravity.END; col2.text = "Cant"; col2.setPadding(16,16,16,16); col2.setTextColor(Color.YELLOW)
        val col3 = TextView(requireContext()); col3.gravity = Gravity.END; col3.text = "P.Unit"; col3.setPadding(16,16,16,16); col3.setTextColor(Color.YELLOW)
        val col4 = TextView(requireContext()); col4.gravity = Gravity.END; col4.text = "Total"; col4.setPadding(16,16,16,16); col4.setTextColor(Color.YELLOW)

        col1.setTypeface(null, Typeface.BOLD)
        col2.setTypeface(null, Typeface.BOLD)
        col3.setTypeface(null, Typeface.BOLD)
        col4.setTypeface(null, Typeface.BOLD)

        filaTitulo.setBackgroundColor(Color.DKGRAY)
        filaTitulo.addView(col1)
        filaTitulo.addView(col2)
        filaTitulo.addView(col3)
        filaTitulo.addView(col4)
        //tableProductos.addView(filaTitulo) //tableHeader
        tableHeader.addView(filaTitulo)
    }

    private fun agregarProducto(
        producto: String,
        cantidad: Double,
        unidad: String,
        precioUnit: Double,
        totalLinea: Double
    ) {

        lineas.add(
            LineaTicket(producto, cantidad, unidad, precioUnit, totalLinea)
        )

        val fila = TableRow(requireContext())

        val colProducto = TextView(requireContext())
        colProducto.text = producto; colProducto.setPadding(16,16,16,16)

        val colCantUnidad = TextView(requireContext())
        colCantUnidad.gravity = Gravity.END
        val cantidadFormateada = if (unidad == "u") cantidad.toInt().toString() else if (cantidad % 1.0 == 0.0) cantidad.toInt().toString() else cantidad.toString()
        colCantUnidad.text = "$cantidadFormateada$unidad"; colCantUnidad.setPadding(16,16,16,16)

        val colPrecio = TextView(requireContext())
        colPrecio.gravity = Gravity.END
        val formato = NumberFormat.getNumberInstance(Locale("es","AR"))
        colPrecio.text = "$" + formato.format(precioUnit.toInt()); colPrecio.setPadding(16,16,16,16)

        val colTotal = TextView(requireContext())
        colTotal.gravity = Gravity.END
        colTotal.text = "$" + formato.format(totalLinea.toInt()); colTotal.setPadding(16,16,16,16)

        fila.addView(colProducto)
        fila.addView(colCantUnidad)
        fila.addView(colPrecio)
        fila.addView(colTotal)
        //refrescarColoresFilas()

        val index = tableProductos.childCount

        val colorPar = Color.parseColor("#1C1C1E")
        val colorImpar = Color.parseColor("#1E88E5") //1E88E5 //2A2A2E

        val colorBase = if (index % 2 == 0) colorPar else colorImpar

        fila.setBackgroundColor(colorBase)

        tableProductos.addView(fila)

        totalGeneral += totalLinea
        textTotalGeneral.text = "TOTAL: $" + formato.format(totalGeneral.toInt())
        actualizarBotonGuardar()

        //fila.setBackgroundColor(Color.TRANSPARENT)
        //fila.setBackgroundColor(Color.parseColor("#1E3A5F")
        /*
                fila.setOnClickListener {
                    //fila.setBackgroundColor(Color.parseColor("#1E3A5F"))
                    fila.setBackgroundColor(Color.TRANSPARENT)
                    AlertDialog.Builder(requireContext())
                        .setTitle("Eliminar")
                        .setMessage("¿Seguro quieres eliminar la línea seleccionada?")
                        .setPositiveButton("Sí") { _, _ ->
                            totalGeneral -= totalLinea
                            textTotalGeneral.text = "TOTAL: $" + formato.format(totalGeneral.toInt())
                            tableProductos.removeView(fila)
                        }
                        .setNegativeButton("No") { dialog, _ ->
                            //fila.setBackgroundColor(Color.TRANSPARENT)
                            fila.setBackgroundColor(Color.parseColor("#1E3A5F"))
                            dialog.dismiss()
                        }
                        //.setOnCancelListener { fila.setBackgroundColor(Color.TRANSPARENT) }
                        .setOnCancelListener { fila.setBackgroundColor(Color.parseColor("#1E3A5F")) }
                        .setCancelable(false)
                        .show()
                }
                */
        fila.setOnClickListener {

            fila.setBackgroundColor(Color.parseColor("#3A4F7A"))

            AlertDialog.Builder(requireContext())
                .setTitle("Eliminar")
                .setMessage("¿Seguro quieres eliminar la línea seleccionada?")
                .setPositiveButton("Sí") { _, _ ->
                    totalGeneral -= totalLinea
                    textTotalGeneral.text = "TOTAL: $" + formato.format(totalGeneral.toInt())
                    tableProductos.removeView(fila)
                    refrescarColoresFilas()
                    actualizarBotonGuardar()
                }
                .setNegativeButton("No") { dialog, _ ->
                    fila.setBackgroundColor(colorBase)
                    dialog.dismiss()
                }
                .setOnCancelListener {
                    fila.setBackgroundColor(colorBase)
                }
                .show()
        }
    }

    private fun refrescarColoresFilas() {

        val colorPar = Color.parseColor("#1C1C1E")
        val colorImpar = Color.parseColor("#1E88E5") //1E88E5 //2A2A2E

        for (i in 0 until tableProductos.childCount) {
            val fila = tableProductos.getChildAt(i)
            val colorBase = if (i % 2 == 0) colorPar else colorImpar
            fila.setBackgroundColor(colorBase)
        }
    }

    private fun actualizarBotonGuardar() {
        buttonGuardar.visibility = if (tableProductos.childCount > 0) View.VISIBLE else View.GONE
        //buttonGuardar.visibility =  View.VISIBLE else View.GONE
    }

    private fun nuevaVenta() {
        //while (tableProductos.childCount > 1)
        //tableProductos.removeViewAt()
        tableProductos.removeAllViews()

        //refrescarColoresFilas()
        lineas.clear()   // 🔥 ESTO ES LO QUE FALTABA
        totalGeneral = 0.0
        val formato = NumberFormat.getNumberInstance(Locale("es","AR"))
        textTotalGeneral.text = "TOTAL: $0"
        actualizarBotonGuardar()
    }

    private fun guardarVentaEnDescargas(nombreArchivo: String): Boolean {
        return try {
            val downloadsFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            if (!downloadsFolder.exists()) downloadsFolder.mkdirs()
            val archivo = File(downloadsFolder, nombreArchivo)
            val sb = StringBuilder()
            sb.append("Verdulería Juanita\n--------------------------------------------------\n")
            sb.append(String.format("%-10s %-10s %-10s %-10s\n", "Producto", "Cant/Unidad", "Precio U.", "Total"))
            for (i in 1 until tableProductos.childCount) {
                val fila = tableProductos.getChildAt(i) as TableRow
                val colProducto = (fila.getChildAt(0) as TextView).text.toString()
                val colCantidad = (fila.getChildAt(1) as TextView).text.toString()
                val colPrecioUnit = (fila.getChildAt(2) as TextView).text.toString()
                val colTotal = (fila.getChildAt(3) as TextView).text.toString()
                sb.append(String.format("%-10s %-10s %-10s %-10s\n", colProducto, colCantidad, colPrecioUnit, colTotal))
            }
            sb.append("--------------------------------------------------\nTOTAL: $" + totalGeneral.toInt() + "\n--------------------------------------------------\n¡Gracias por su compra!\nNo válido como factura\n")
            archivo.writeText(sb.toString())
            true
        } catch (e: Exception) { e.printStackTrace(); false }
    }
    private fun crearCanalNotificacion() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val nombre = "CanalVentas"
            val descripcion = "Notificaciones de prueba"
            val importancia = NotificationManager.IMPORTANCE_HIGH
            val canal = NotificationChannel("canal_ventas", nombre, importancia)
            canal.description = descripcion

            val notificationManager = requireContext()
                .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

            notificationManager.createNotificationChannel(canal)
        }
    }

    private fun mostrarNotificacion(mensaje: String) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (requireContext().checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)
                != PackageManager.PERMISSION_GRANTED) {
                return
            }
        }

        val builder = NotificationCompat.Builder(requireContext(), "canal_ventas")
            //.setSmallIcon(R.drawable.ic_launcher_foreground)
            .setSmallIcon(R.drawable.ic_notificacion)
            .setLargeIcon(
                BitmapFactory.decodeResource(resources, R.drawable.bot)
            )
            .setContentTitle("Mercería ButtonTop")
            //.setContentText(mensaje)
            .setContentText("App activa ❤️")
            //.setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .setOngoing(true) // Siempre visible no se puede quitar

        with(NotificationManagerCompat.from(requireContext())) {
            notify(1001, builder.build())
        }
    }

    private fun programarNotificacionEn4Minutos() {

        Handler(Looper.getMainLooper()).postDelayed({

            val horaActual = SimpleDateFormat("HH:mm", Locale("es","AR"))
                .format(Date())

            mostrarNotificacion("Son las $horaActual")

        }, 4 * 60 * 1000) // 4 minutos
    }
    private fun pedirPermisoNotificaciones() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

            if (requireContext().checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)
                != PackageManager.PERMISSION_GRANTED) {

                requestPermissions(
                    arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
                    101
                )
            }
        }
    }
}
    
  




fragment_ticket_venta.xml





layout_comprobante.xml





activity_menu_prueba.xml