Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/luisumit/LaPreviaRestobar/llms.txt

Use this file to discover all available pages before exploring further.

La Previa Restobar uses two separate notification pathways to keep staff informed without requiring them to watch the screen constantly. Order-status notifications fire in near real-time as Firebase data changes, while low-stock inventory alerts are delivered on a background schedule that runs even when the app is not in the foreground.

Two Notification Types

Order Status Notifications

Delivered when order status changes — e.g. a new order arrives at the chef panel, or a chef marks an order ready for the waiter. Driven by the Firebase real-time listener.

Low-Stock Admin Alerts

Delivered on a 12-hour schedule by AdminStockWorker. Checks the local Room database for products where stock == 0 (agotado) or stock ≤ minStock (stock bajo) and notifies the admin.

AdminStockScheduler

AdminStockScheduler is a Kotlin object (singleton) that registers and cancels the periodic WorkManager task for inventory alerts.
object AdminStockScheduler {

    private const val WORK_NAME = "admin_stock_worker"

    fun schedulePeriodicCheck(context: Context) {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
            .build()

        val workRequest = PeriodicWorkRequestBuilder<AdminStockWorker>(
            12, TimeUnit.HOURS,   // Primary interval: every 12 hours
            2, TimeUnit.HOURS     // Flex window: may fire up to 2 hours early
        )
            .setConstraints(constraints)
            .setInitialDelay(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            WORK_NAME,
            ExistingPeriodicWorkPolicy.KEEP,
            workRequest
        )
    }

    fun cancelPeriodicCheck(context: Context) {
        WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
    }

    fun triggerImmediateCheck(context: Context) {
        val workRequest = OneTimeWorkRequestBuilder<AdminStockWorker>().build()
        WorkManager.getInstance(context).enqueue(workRequest)
    }
}

Network-independent

NetworkType.NOT_REQUIRED means the inventory check runs even without an internet connection, since it reads directly from the local Room database.

KEEP policy

ExistingPeriodicWorkPolicy.KEEP prevents duplicate workers from being registered if the app restarts or the scheduler is called multiple times.

Immediate trigger

triggerImmediateCheck() enqueues a one-time AdminStockWorker for testing or for manual stock audits initiated from the admin UI.

AdminStockWorker

AdminStockWorker is a HiltWorker — it receives a fully injected AppDatabase instance via Hilt’s @AssistedInject mechanism.
@HiltWorker
class AdminStockWorker @AssistedInject constructor(
    @Assisted private val appContext: Context,
    @Assisted params: WorkerParameters,
    private val db: AppDatabase
) : CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            try {
                val products = db.productDao().getAll()
                val trackedProducts = products.filter { it.trackInventory }

                val outOfStock = trackedProducts.filter { it.stock == 0.0 }
                val lowStock = trackedProducts.filter { it.stock > 0 && it.stock <= it.minStock }

                if (outOfStock.isNotEmpty() || lowStock.isNotEmpty()) {
                    sendNotification(outOfStock, lowStock)
                }
                Result.success()
            } catch (e: Exception) {
                Result.retry()
            }
        }
    }
}
1

Load all products from Room

db.productDao().getAll() reads the full product list from the local Room cache. No network request is made.
2

Filter tracked products

Only products with trackInventory = true are eligible for low-stock alerts. Products that don’t track inventory (e.g. drinks sold by the glass without a counted stock) are ignored.
3

Classify severity

Products are split into two buckets: agotados (stock == 0.0) and stock bajo (stock > 0 && stock <= minStock). Each bucket is displayed separately in the notification body.
4

Send notification

If either bucket is non-empty, sendNotification() builds and posts the Android notification. If both buckets are empty, the worker exits silently — no notification is sent.
5

Retry on failure

If an exception is caught (e.g. database unavailable), Result.retry() tells WorkManager to try again with exponential back-off.

Notification Channel Setup

The low-stock notification is posted to a dedicated channel created at runtime for Android 8.0+ (API 26+):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        "admin_stock_channel",
        "Alertas de Inventario",
        NotificationManager.IMPORTANCE_HIGH
    ).apply {
        description = "Notificaciones de stock bajo y agotado para el administrador"
        enableVibration(true)
    }
    notificationManager.createNotificationChannel(channel)
}
Channel IDNameImportance
admin_stock_channelAlertas de InventarioIMPORTANCE_HIGH (heads-up)
IMPORTANCE_HIGH causes the notification to appear as a heads-up banner even when the phone is in use, ensuring the admin sees critical stock warnings immediately. The notification title and body are dynamically constructed based on the severity breakdown:
ConditionTitle
Both out-of-stock and low-stock items⚠️ {n} agotados + {n} stock bajo
Only out-of-stock items❌ {n} producto(s) AGOTADOS
Only low-stock items⚠️ {n} producto(s) con stock bajo
The body lists up to three items per severity bucket. If more exist, a "y {n} más..." line is appended to keep the notification compact. A PendingIntent deep-links the admin directly to the inventory screen when the notification is tapped:
val intent = Intent(appContext, MainActivity::class.java).apply {
    putExtra("open_inventory", true)
    putExtra("notification_type", "stock_alert")
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

Notification Icon and Colors

The small icon for all app notifications uses the R.drawable.ic_notification drawable. The accent color is set dynamically:
  • Out-of-stock notifications: R.color.notification_out_of_stock (red)
  • Low-stock-only notifications: R.color.notification_low_stock (amber)

Android 13+ Permission

On Android 13 (API 33) and above, the POST_NOTIFICATIONS runtime permission must be granted before any notification can appear. The app requests this permission during onboarding. If the user denies it, no notification channels will fire — but all WorkManager tasks continue to run silently in the background.

UI Notification Panels

In addition to system-level notifications, the app includes two in-screen notification panels:
  • NotificationPanel — shown in the waiter role view; displays incoming order-ready alerts as the chef marks orders LISTO.
  • ChefNotificationPanel — shown in the kitchen view; surfaces new ENVIADO orders that arrive from the waiter in real time.
Both panels are driven by the Firebase ValueEventListener in FirebaseOrderRepositoryImpl and do not depend on WorkManager.

Build docs developers (and LLMs) love