Inventory split: InventoryItem entity, schema v2 with tested migration, Lager screen
Stock, package size and prescription state move to inventory_item; medication is now a regimen (itemId FK RESTRICT — deleting a stock item can never silently take adherence history with it). Supply warnings aggregate consumption across all regimens sharing an item. New Lager screen with one-tap '+1 pakning' restock; med editor picks an existing item or creates one inline via the shared ItemFormState (one form, no drift). Backup format v2 mirrors the split and still restores v1 files by performing the same conversion the DB migration does. MIGRATION_1_2 statements are copied from the exported 2.json and proven by an instrumented MigrationTestHelper test on the emulator (validates against the schema AND asserts data survival) before it ever touches the phone's medical record. Heads-up: connectedAndroidTest must target the emulator (ANDROID_SERIAL) — the phone's release signature rejects debug test APKs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f8e8dca081
commit
9b5817fcef
27 changed files with 1451 additions and 342 deletions
|
|
@ -65,7 +65,7 @@ class AlarmScheduler(private val context: Context) {
|
|||
val db = MedDatabase.get(context)
|
||||
val doseTime = db.doseTimeDao().getById(doseTimeId) ?: return
|
||||
val med = db.medicationDao().getById(doseTime.medId)
|
||||
if (med == null || !med.active) {
|
||||
if (med == null || !med.med.active) {
|
||||
cancelDoseTime(doseTimeId)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,14 +60,14 @@ class DoseAlarmReceiver : BroadcastReceiver() {
|
|||
|
||||
val doseTime = db.doseTimeDao().getById(doseTimeId) ?: return
|
||||
val med = db.medicationDao().getById(doseTime.medId) ?: return
|
||||
if (!med.active) return
|
||||
if (!med.med.active) return
|
||||
|
||||
val existing = db.doseLogDao().getForOccurrence(doseTimeId, scheduledAt)
|
||||
val log = when {
|
||||
existing == null -> {
|
||||
val id = db.doseLogDao().insert(
|
||||
DoseLog(
|
||||
medId = med.id,
|
||||
medId = med.med.id,
|
||||
doseTimeId = doseTimeId,
|
||||
scheduledAtMillis = scheduledAt,
|
||||
amount = doseTime.amount,
|
||||
|
|
|
|||
|
|
@ -35,16 +35,20 @@ class SupplyCheckReceiver : BroadcastReceiver() {
|
|||
val today = LocalDate.now()
|
||||
val lines = mutableListOf<String>()
|
||||
|
||||
for (med in db.medicationDao().getActive()) {
|
||||
val doseTimes = db.doseTimeDao().getForMed(med.id)
|
||||
val rate = ScheduleEngine.dailyConsumption(doseTimes)
|
||||
if (ScheduleEngine.needsRefill(med.inventoryUnits, rate, med.lowStockLeadDays)) {
|
||||
val days = ScheduleEngine.daysOfSupply(med.inventoryUnits, rate)
|
||||
lines += "${med.name}: lager for ${days?.toInt() ?: 0} dager"
|
||||
// Per inventory item: consumption is the SUM across all active regimens
|
||||
// drawing from it — one shared box drains faster than either alone.
|
||||
val byItem = db.medicationDao().getActive().groupBy { it.item.id }
|
||||
for ((_, meds) in byItem) {
|
||||
val item = meds.first().item
|
||||
var rate = 0.0
|
||||
for (med in meds) rate += ScheduleEngine.dailyConsumption(db.doseTimeDao().getForMed(med.med.id))
|
||||
if (ScheduleEngine.needsRefill(item.stockUnits, rate, item.lowStockLeadDays)) {
|
||||
val days = ScheduleEngine.daysOfSupply(item.stockUnits, rate)
|
||||
lines += "${item.name}: lager for ${days?.toInt() ?: 0} dager"
|
||||
}
|
||||
if (ScheduleEngine.needsRenewal(med.rxExpiryEpochDay, today)) {
|
||||
val expired = med.rxExpiryEpochDay!! < today.toEpochDay()
|
||||
lines += if (expired) "${med.name}: resept utløpt" else "${med.name}: resept må fornyes"
|
||||
if (ScheduleEngine.needsRenewal(item.rxExpiryEpochDay, today)) {
|
||||
val expired = item.rxExpiryEpochDay!! < today.toEpochDay()
|
||||
lines += if (expired) "${item.name}: resept utløpt" else "${item.name}: resept må fornyes"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue