Added
-
A backup from another installation can be restored without touching a shell. A backup
carries the encryption of the installation that wrote it, so moving to a new server ended at
"could not be decrypted", and every way around it meant swapping files and keys by hand - on
Umbrel, where the key is fixed, there was none at all. When a backup does not open with this
installation's key, the restore dialog now asks for the old installation'sDB_ENCRYPTION_KEY.
The backup is decrypted with it and re-encrypted with this installation's own key before it
replaces the database; the entered key is used only for that restore and is not stored. A wrong
key leaves everything as it was. An installation without a key of its own refuses, instead of
storing the backup decrypted, and over plain HTTP the dialog warns that the key crosses the
network unencrypted. API clients send the key as base64 in theX-Backup-Keyheader of
POST /api/v1/backup/restore(never in the URL), andscripts/restore-backup.jsreads it from
stdin with--backup-key-stdin. See "Moving to a new server" in the installation guide. (#1267) -
You can sign out on your other devices. Since a session now lasts 90 days without use, a lost
phone or a borrowed laptop could stay signed in for months, and signing out only ended the
session on the device you were using. Settings, Account, now has "Other devices" with a button
that ends every other session of your account after a confirmation, including ones started with
single sign-on; this device stays signed in, and the page says how many sessions were ended,
counting only ones that were still valid. Clicking it too often asks you to wait a moment; the
limit applies to each member separately, so one member cannot lock out another. API
tokens and paired wall displays are not sessions and keep working; they are revoked under API
tokens and Displays as before. For API clients:POST /api/v1/auth/logout-othersneeds a
browser session and a CSRF token, answers{ ok: true, ended }, and refuses an API token with
403. (#1354) -
API clients can fill only the empty slots of the meal plan.
POST /api/v1/meals/apply-plan
takes a new optionskip_occupied: true. An assignment whose date and meal type already hold a
meal is then left out instead of being added next to it, and the answer lists it inskippedas
{ index, date, meal_type, reason: "occupied" }, whereindexis its position inassignments,
so an importer such as a Mealie meal-plan sync knows what did not land. A weekly recurring meal
occupies its slot even in a week nobody has opened yet, up to and including its last day; an
occurrence that was deleted or moved away does not. "Occupied" means before the call, so several
assignments for the same empty slot are all created. The option has to be a JSON boolean, and
combining it withreplace_existingis refused with400. Without the option the endpoint
behaves and answers exactly as before. (Discussion #1380) -
Every done task on the board can be archived in one action. The "Done" column of the board
now has an archive button next to its count. After a confirmation it moves the done tasks the
column currently shows into the archive - the ones it shows, so a task someone else completes
while the board is open stays where it is until you have seen it, and a search narrows what goes.
Their status stays done, and each one can still be brought back on its own. Archiving several
tasks from the list's selection mode now takes a single request as well: before, it sent one per
task, stopped at the first locked task or rate limit and then did not refresh, although the rest
had already been archived. Locked tasks you may not change are left out and the message says how
many. For API clients the newPOST /api/v1/tasks/archivetakes{ ids }(at most 500), applies
the same rules as archiving a single task in one transaction, skips tasks the caller cannot see,
and returns{ archived, skipped }. (#1250) -
The overview now says when a task has been started. In "Today" a task used to read "Task"
whether anyone had begun it or not, so starting it from there left no trace on screen. A task in
progress now reads "Task · started", as text, so a screen reader announces it too, and the row
gets no wider on a phone. Where the tasks tile is switched on, a started task carries the same
amber ring as in the task list, and the status is read out with it. Open tasks look as before.
(#1251) -
The activity of a shared-expenses group now loads further with "Load more", down to its first
entry. Until now the activity showed the latest 12 entries and nothing before them, so an older
payment could not be reversed from the interface. "Load more" appends the next entries below the
ones already shown, as often as there are more. Reversing a payment that was loaded this way keeps
the list as deep as it was, so the payment stays in view, now marked as reversed, instead of the
list jumping back to its first page. Loading more is reading, so the button is there at every
access level and in an archived group. The API pages the same way:GET /api/v1/split-expenses/groups/{id}/activityreturnspagination.next_cursor, and passing its
before_atandbefore_idreturns the next page. Entries added while paging appear at the top
and shift nothing, unlikeoffset. Without a cursor the endpoint answers as before, with the same
entries in the same order;has_moreis now exact instead oftruewhenever a page happened to be
full. (#1309) -
A payment recorded in shared expenses can be reversed. Until now a settle-up, once saved,
stayed for good - a transposed figure or the wrong person could not be taken back, while the
expense next to it could be edited and deleted. Each payment in a group's activity now names who
paid whom and how much, and offers "Reverse". After a confirmation, a counter-entry cancels the
payment and the balances go back to where they were before it. Nothing is deleted: the payment
stays in the activity, marked as reversed, together with its payment proof, so the history still
shows what happened. To correct a payment, reverse it and record the right one. Group owners and
admins can reverse any payment, everyone else the ones they recorded - the same rule as for
editing an expense. Without write access to Budget, or in an archived group, the button is not
there, but the "reversed" mark is. Deleting the account of whoever reversed a payment leaves it
reversed and the balances as they were. The API has the same step as
POST /api/v1/split-expenses/groups/{id}/settlements/{settlementId}/reverse; reversing twice
answers 409. (#1309) -
In the week and day views, an appointment of a day or more now shows its times on the all-day
bar. An appointment with a start and an end time that lasts 24 hours or longer - a trip from
Friday 14:00 to Sunday 11:00, a workshop over three days - stays in the all-day row above the time
grid, and there it looked like three whole days. The bar now says "from 14:00" on the first day and
"until 11:00" on the last; the days in between carry no time, because the appointment covers them
completely. The wording and the 12- or 24-hour format are the ones the agenda has always used for
those days, so both views say the same thing about the same day, and the tooltip carries the time
as well. An appointment that ends at exactly 00:00 ends on the day before, as everywhere else in
the calendar, so that is where its "until" appears. Appointments marked as all-day have no times
and look as before. Where a bar is too narrow for both - a phone showing three days, or a busy
week on a smaller screen - the title keeps its room and the time is left off that bar rather than
cut short; the tooltip and the appointment's details still give it. The tooltip also shows a
calendar name with quotation marks in full now; it used to break off at the first one. (#1350) -
Health has a ninth tab: a daily nutrition target per person and a log of what was eaten. You
type the numbers off the packet in front of you - energy in kcal, fat, of which saturates,
carbohydrate, of which sugars, protein, salt and fibre - and the tab shows today's total against
your target. Nothing is derived from ingredients: Yuvomi stores the figure you state about your
own meal and does not compute one from what a product supposedly is. Every one of the eight is
optional, on the entry as well as on the target; a value you leave out stays unstated rather than
becoming a zero, so a meal you only knew the calories for does not claim to contain no fat. A
target of 0 is a target and reads as one - "no sugar today" is a different statement from "no
target set", and the tab and the tile keep them apart. An entry carries a free title, a time, an
optional meal type, a note, and says who may see it: only you, or the whole household, with only
you as the shipped default. A carer who may record for somebody (Settings, Health) can log for
them, and the entry then follows that person's own visibility choice rather than the carer's. The
same progress is available as a dashboard tile, hidden until you add it from the dashboard editor,
and everything is reachable through/api/v1/health/nutrition/*under the existinghealthAPI
scope, so a bridge can write an intake and nothing else. Today's total follows your household time
zone, not UTC, so a late dinner counts towards the day you ate it. There is a CSV export per
person and date range. (#1326) -
A fasting timer can now be added to the dashboard. It shows only your own fast and lets you
start or finish it there, using the same safety confirmation and timer controls as the journal.
Existing dashboards keep it hidden until you add it from the dashboard editor. (#1180) -
Inventory tracked dates can now recur, keep a service history, and vehicles have an odometer.
Give a tracked date (TÜV, boiler service, chimney sweep, extinguisher check, ...) a recurring
interval in months, and pressing "Done" rolls it forward instead of just clearing it - the
reminder moves with it. Every completion is kept in a new service-history view on the item,
alongside its linked maintenance bookings and documents with a running cost total, and a
vehicle's history now plots its odometer readings as a small trend chart. Vehicles can also
carry a manual odometer reading (km or mi) - a tracked date can add a distance interval as a
hint ("1,400 km to go") next to its date, though only the date itself ever produces a reminder. -
A reward can now say how many of it there are. The catalog is shared by the whole household and
had no idea of quantity, so a cinema evening and a wooden train were the same thing to it: both
stayed open to every child at once, again and again, as long as the points lasted. A reward now
carries a number of units for the household; leaving the field empty keeps it unlimited, which is
what every reward you already have stays. A card says how many are left and gets a "Sold out" tag
once they are gone - it stays in the catalog, so everyone can see it exists, but it is no longer
offered. Asking for something that is gone is refused before any points are taken. A request that
was already waiting when the last unit went is not left hanging: approving it turns into a refusal
with the reason on it, and the reserved points go straight back. Two children asking for the last
unit at the same moment cannot both get it. (#1310) -
A recipe ingredient can now be told which row of your pantry it means. Open a recipe, unfold
it, and each ingredient offers to be matched to one row of your stock - "400 g tin of tomatoes"
in the recipe, the tin that actually stands in your cupboard. Yuvomi never guesses this, not even
when the two are spelled identically: a match exists only where somebody confirmed it, and no
import, no recipe save and no name similarity ever writes one. An ingredient without a match reads
"not matched", never "missing" - it is unknown, and saying otherwise would turn half an answer
into a confident one. The match survives editing the recipe, because it hangs on the ingredient
and not on its row in the database; rename that ingredient and the match is gone for good rather
than lying in wait for the old spelling to come back, and the same holds for recipes mirrored from
Mealie or Tandoor when the provider does the renaming. Deleting the stock row removes the match and
leaves the recipe untouched. Changing a match needs permission to write the pantry, not just the
recipes; members who may only look at the pantry see the match and cannot change it, and members
who may not see the pantry at all are not told which row it is - not in the page and not in the
API's answer either. (#1314)
Changed
- You stay signed in as long as you open Yuvomi at least once every 90 days. Until now every
browser was signed out seven days after signing in, even when it was used every day. The sign-in
now slides: each visit keeps it alive, and only a device that has not been used for 90 days has
to sign in again. The cookie is renewed at most every 12 hours, never on images, styles or other
static files, so the session id does not travel in responses a proxy might cache. There is no
setting and no "keep me signed in" checkbox; signing out still ends the session at once. Existing
sign-ins move to the 90 days on their next visit. (#1356)
Fixed
-
A restore can no longer leave a half-written database behind, and a damaged backup is refused.
A restore copied the backup straight over the database file, so if Yuvomi was stopped or the
disk filled up in the middle of it, the next start found a broken database, and nothing said
that the copy kept under.pre-restore-*was the way back. The backup is now written next to
the database first and swapped in with a single rename, so the database is always either the
old one or the restored one; a copy left over by an interrupted restore is removed on the next
start. A backup of this installation with a damaged page further in was also restored without
complaint, because only its first page was read. Every page is now checked before anything is
changed, and a damaged backup is refused with a translated message that says to fetch the file
again or use an older backup. The copy kept under.pre-restore-*is written the same way, so
an interrupted restore never leaves a cut-off copy under that name. A second restore started
while one is still running, from another tab or another admin, is now refused with a message
instead of racing the first one. Changes made while a restore is running are refused with a
translated note to try again in a minute, instead of seeming saved and then disappearing. Pages
keep loading while the backup is copied; only in the short moment the database file itself is
swapped does the app answer with the same note. Calendar and contact syncs, push notifications
and scheduled backups do not start during a restore, and one already running is finished
first. A restore that would leave the database unwritable for Yuvomi stops before replacing
anything and says to run it as the user Yuvomi runs as.
(#1422) -
Synced appointments lose a calendar colour that was never theirs. Up to v2.48.0 the CalDAV
import wrote the calendar's colour into each appointment as if it had been chosen for it. For an
appointment that had been edited in Yuvomi before v2.50.0, that copy was kept as a deliberate
choice, so it went on beating the colour of the assigned person, and after a move to another
calendar it even showed the colour of the old one. For 30 days after its first sync following this
update, each CalDAV account now removes such a colour when it meets the appointment: only on
appointments that were created before v2.49.0 reached this installation and have been edited in
Yuvomi, when the appointment carries no colour of its own on the server and the stored one is
exactly the colour of a calendar of that account, including one deleted on the server since. The
appointment then shows the colour of its person or its calendar again. Appointments that Yuvomi
uploaded itself count only with the colour of the calendar they were uploaded to, and a colour
chosen in Yuvomi after these 30 days began stays. A colour picked in Yuvomi that is not one of
that account's calendar colours stays, and so does any colour the server sets on the appointment
itself. An account added later does not do this, unless it takes over old appointments of a
deleted account; the same applies when an account is pointed at a different server address or user
name. (#1270) -
Appointments moved to another calendar before 2.68.0 can now take that calendar's person, one
by one. Since 2.68.0 an appointment moved between two calendars of one account takes the new
calendar's default assignee along, and with it the colour. Appointments moved before that kept
the person of the calendar they came from, and nothing ever changed that, because the move itself
was long over. "Apply to existing appointments" under Settings > Sync now lists them in its
confirmation: every appointment whose only assignee is still the default assignee of another
calendar of the same account, with its title, date, calendar and "from X to Y". All are selected;
untick what should stay, and only the selected ones change. The list shows at most 5000 at a
time, oldest first; "Show next" moves on to the following ones without applying the current
page. Only appointments you are allowed to see are listed, so another member's private
appointments stay private here too. They are listed one by one because
the stored data cannot tell such an appointment apart from one in a calendar whose default
assignee was changed later, when the previous person is another calendar's default assignee - an
automatic repair would have changed those too. Unassigned appointments are filled as before. An
appointment edited in Yuvomi, assigned to more than one person or to anyone else, created in
Yuvomi and sent to the calendar, or in a calendar without a default assignee is not listed. For a
recurring appointment the whole series changes, including occurrences edited on their own that do
not have their own assignment. For API clients:GETon the backfill route returns the list as
movedin pages of at most 5000 (moved_total,moved_next,moved_after), andPOSTtakes the picked entries asmoves;
without them no existing assignment changes. (#1307) -
Shared expenses that lost their bookings to a deleted account count in the balances again.
Until edits stopped tying an expense to its editor, deleting the account of someone who had
edited another member's shared expense also removed that expense's bookings: the expense stayed
in the list but no longer counted in any balance, and that fix could not bring them back. The
update now rebuilds them from the expense and its shares, exactly as they are booked when an
expense is saved, including the converted amount of an expense in another currency. Only active
expenses without any booking are touched; deleted expenses and complete ones stay as they are.
The group's activity shows "Booking restored" once for each expense that was repaired, so a
changed balance has a visible reason, and each entry names the expense and its amount. (#1382) -
Single sign-on finds your account even when its stored address has a stray space. Signing
in through the identity provider links to an existing account by email address. The address
from the provider was already trimmed, but one stored on the member's contact with a leading or
trailing space, tab or non-breaking space, for example from the contact form or an import, did
not match, and the household got a second member with the same address. Both sides are now
compared by the same rule, for the primary and every further address of the contact. Creating a
member without a password uses that rule too, so it no longer allows an address that sign-in
would then find twice. (Follow-up to #1357) -
"Forgot password" finds your account by email regardless of spaces and capitals. Asking for
a reset link with your email address only worked when it matched the stored contact address
exactly, so a stored address with a stray space or different capitalisation sent no link. Both
sides are now compared the same way as for single sign-on. When two accounts carry the same
address, no link is sent to either, instead of to whichever came first: if several members of
your household share one address, reset by username instead. A guest of shared expenses with
the same address does not count. The page now answers right away and sends the mail
afterwards, so neither its answer nor how long it takes reveals whether an address belongs to
an account. -
A dose logged as pending or skipped no longer keeps an intake time. When an API client
created a medication log with the status pending or skipped and sent ataken_atalong, the
time was stored and appeared in the export as if the dose had been taken. Only a taken dose
carries an intake time now, as when you change the status afterwards. Entries saved before stay
as they are. (Follow-up to #701) -
Screen readers and keyboards get a few rough edges less. Toasts no longer interrupt what a
screen reader is reading or get announced twice: only errors and warnings interrupt, everything
else waits its turn. "Edit member" now starts in the username field instead of on the hidden
picture upload, which showed up as a stray strip over the dialog title and had no name for
screen readers; the birthday and housekeeping staff dialogs had the same problem. The hidden
photo and file pickers in these and in the recipe, inventory and attachment dialogs now have a
name and no longer take an extra, invisible Tab stop next to their button. The member and
invite lists in Settings are valid lists again when empty or after a load error, the two-factor
card keeps a gap between its hint and its buttons, and with read-only access, birthday and
subscription rows no longer show a swipe arrow on touchscreens, since there is nothing to swipe.
A toast now also stays clear of a focused person chip in a dialog, not only of the chip's first
pixel. -
Moving items between the pantry and the shopping list now needs read access to where they
come from. Moving checked items from the shopping list into the pantry, and putting pantry items
on the shopping list, were judged only by the module they write into. The import now also needs
read access to the module it copies from: the shopping list for "Into pantry", the pantry for the
shopping cart on a pantry row. Without it the server answers 403 and copies nothing. Both buttons
stand on the page of that source module, so nobody who can see them loses them. For API clients:
POST /api/v1/pantry/import-shoppingalso needsshopping:read, and
POST /api/v1/shopping/{listId}/import-pantryalso needspantry:read. (#1433) -
Choosing members for a shared-expense group shows contact details only with access to
them. The member picker now shows phone and email only with read access to contacts, and a
member's birthday only with read access to the calendar, where birthdays live. A contact without
an account is linked to the new guest when it is added, so the picker offers such contacts, and
adding one is accepted, only with permission to edit contacts; without any access to contacts
the answer is as if the contact did not exist. Adding the same contact twice at the same moment
now creates one guest instead of failing, creating two guests with the same username at once
gives one guest and a clear "already taken", and a group removed while a guest is being added
leaves no guest account behind. An unknown contact answers 404 instead of a server error. (#1433) -
An inventory item shows its budget bookings only with access to the budget. Without read
access to the budget, an item no longer shows its linked bookings, their total or the bookings
in its history, and the edit form leaves out the bookings section and its buttons instead of
claiming there are none. Linking, unlinking or pre-filling the purchase price from a booking
answers as if the booking did not exist. (#1433) -
A failed restore explains itself in your language and keeps your place. When a backup did
not open, the restore dialog showed the server's English explanation, up to several paragraphs
long, also in a German interface. Each known cause now has a short translated message with the
next step, and anything else gets a general one that points to the server log. A screen reader
now reads the message together with the backup key field, and a wrong key marks the field as
invalid until you type again. After the answer the focus lands in the key field when it
appears, otherwise on the restore button, also on phones, where the closing confirmation used
to take it away again; before, it could end up at the top of the page. If you moved on to
something else while a slow restore was running, the focus stays there. A second click while a
restore is running still starts nothing. (#1267) -
A housekeeping visit no longer gives away a receipt you may not see. The housekeeping API
sent the file name and document number of a visit's receipt to everyone who could open the
housekeeping module, also to members without access to documents and when the receipt was a
private document of someone else. The page already hid the name without document access, but the
API still returned it. Name and number now come only when you may read that document, by the same
rule the documents module uses. Without access to documents the visit does not even say that it
has a receipt; with access but without sight of that document, the report and the edit dialog say
"Attachment present (private)" instead of an upload field. Saving such a visit keeps the receipt:
before, saving it could silently remove someone else's private receipt, and it can no longer be
replaced or removed by someone who cannot see it. Linking a receipt now needs access to
documents. For API clients every visit and work session carrieshas_receipt, which isnull
without access to the documents module;receipt_document_idandreceipt_document_nameare
nullunless you may read the document, API tokens need adocuments:readscope for them, and
PUT /api/v1/housekeeping/visits/{id}answers 403 when it would replace a receipt you cannot see
or link one without access to documents. (#1358) -
Receipts on budget entries, shared expenses and inventory items no longer name or count
documents you may not read. Their API sent the file name and document number of every linked
receipt to anyone who could open the budget or the inventory, also to members without access to
documents and to API tokens without a documents scope. Without access to documents an entry now
says nothing about its receipts - not which, not how many, and the lists show no paperclip -
the same as a task with linked documents. The inventory no longer shows a link that leads nowhere
or lists the document in an item's history. Linking a receipt or a payment proof needs access to
documents, and existing receipts stay when such a member saves the entry. For API clients
attachmentsisnullwithout access to the documents module (for API tokens a
documents:readscope), a settlement'sproof_document_idisnullunless you may read that
document, and a non-emptyattachment_document_idsor aproof_document_idis answered with the
same 403 for every id. (#1358) -
Documents: a calendar event no longer shows its attachment to members who cannot see the
document. An event's attachment is stored in the documents module, but the calendar sent its
name and a link to it to everyone who could see the event, also to members without access to
documents, to API tokens without a documents scope and when the document itself had been made
private. Such members now see the event without an attachment, in the calendar and on the
dashboard. Adding an attachment now needs permission to add documents, and the event dialog only
offers the upload area then; an attachment you cannot see can no longer be replaced or removed by
saving the event, and the event view and the dialog say "Attachment present (private)" instead.
Saving an event no longer makes a private attachment visible again: its visibility is carried
over to the document only by the person who owns it (the event's creator) or an admin, and only
with permission to edit documents; anyone else can only narrow it. The sync of connected
calendars, which reassigns an event when it moves to a calendar with another default person,
never changes who may see its attachment, with one exception: when the event is shown to its
assignees and the attachment is already shared with selected members, that person is added to
them. Otherwise the owner's sharing stays exactly as it is - nothing becomes visible to the whole
family, nothing private is opened, nothing is made private and no share is removed. A copy made
when a series is split keeps the original's sharing and owner. Splitting a series or detaching an
occurrence no longer copies an attachment for someone who cannot see it or may not edit
documents; the new part then has no attachment and the original stays on the series. For API
clientsattachment_document_id,attachment_preview_url,attachment_download_url,
attachment_name,attachment_mimeandattachment_sizearenullunless you may read that
document,attachment_lockedsays whether there is one you cannot see (nullwithout access to
documents), a non-emptyattachment_datawithout adocuments:writeright is answered with 403,
and so is replacing or removing an attachment whose document you cannot read. (#1358) -
Documents: the folder delete preview no longer hints at documents you cannot see. Before
deleting a folder the app asks what the deletion would affect. That answer already counted only
the documents you can see, but whether it offered to delete the documents as well still depended
on documents hidden from you, so an administrator could tell that a folder held one. The offer
now depends only on the documents you can see. Deleting a folder together with its documents
still refuses as long as it holds a document you may not delete. (#1358) -
A task no longer names or counts documents you may not read. The tasks API sent the linked
documents of a task with their names, and the number of them, to everyone who could see the task,
also to members without access to documents and to API tokens without a documents scope. Without
access to documents a task now says nothing about its documents: no paperclip on the card, no
documents row in the detail view, and saving the task keeps its documents. For API clients
document_countanddocumentsarenullwithout access to the documents module (for API
tokens adocuments:readscope) -nullmeans "not told", not "none" -
GET /api/v1/tasks/{id}/documentsanswers 403, and
PUT /api/v1/tasks/{id}/documentsanswers the same 403 for every id in a non-empty
document_ids. (#1358) -
An edited shared expense keeps counting after the editor's account is deleted. When a group
owner or admin edited someone else's expense and that editor's account was later deleted, the
expense stayed in the list but silently dropped out of every balance. Edits now leave the expense
tied to whoever created it, so it counts until it is deleted itself; the activity still shows who
edited it. Expenses edited this way before are corrected when the update starts. An expense whose
editor was already deleted before the update is not repaired: it still shows in the list without
counting in the balances. (#1309) -
An account created at the first single sign-on now gets its contact entry. Every other way of
adding a household member - an invitation, the first setup, an admin creating the account - also
creates the member's contact, which holds the e-mail address the household uses, for example to
send a shopping list. An account created by the first OIDC sign-in had none. It now gets one with
its name and, if the identity provider marks the address as verified (email_verified), that
e-mail address. No picture, no birth date and no phone number are taken over, and later sign-ins
change nothing, so edits made in Yuvomi stay. (#1357) -
"To shopping list" only appears where it can work. Since 2.68.0, sending ingredients from
the kitchen to the shopping list needs permission to change the shopping list, and taking over
the meal plan from inside the shopping list needs permission to change the meal plan. The buttons
did not know that yet: a family member who may only view the shopping list still saw the cart on a
meal, the "To shopping list" button in a recipe and the transfer section in the meal dialog, and
one who may only view the meal plan still saw "Import meal plan" in the list menu - each of them
ended in an error. They are now left out for anyone who lacks the permissions the action needs. -
A family member who may only view the meal plan can put a recipe on the shopping list.
Sending a recipe's ingredients to the shopping list only reads the recipe, so it now needs
permission to view the meal plan and to change the shopping list, for members and for API tokens
alike. Sending a planned meal still needs permission to change the meal plan, because it marks
the meal's ingredients as transferred. -
A reminder no longer sits on top of a dialog's buttons and takes the click meant for them.
When a reminder came due while a dialog was open, its message could lie exactly over "Save": on a
desktop screen the calendar dialog's Save, Cancel and Delete were all under it, and on a phone the
first row of the sheet's buttons or its close button. Clicking there dismissed the reminder and
saved nothing, without any message. While a dialog is open, messages at the bottom of the screen
now move out of the way of its title bar and buttons: above the dialog when there is room (on a
phone, the strip above the sheet), otherwise just above the dialog's buttons. This holds for the
welcome tour and for pickers inside a form, such as choosing a document, too. They also keep
clear of the field you are typing in and of expandable sections such as "More settings", so a
field reached with the Tab key is never hidden behind a message. They always stay on screen and
can still be dismissed. (#1160) -
Deleting a folder no longer reveals activity on documents you cannot see. Before deleting a
folder the app asks the server what the deletion would affect and sends that answer back with the
deletion, so nothing changes unnoticed in between. That answer was built over every document in
the folder, including private ones of other family members, so asking twice showed whether
something had happened to documents you are not allowed to see. It now covers only the documents
you can see. Deleting a folder together with its documents still refuses as long as it holds a
document you may not delete, and that refusal now comes before the "contents changed" message,
which only appears for changes you can see. If such a document arrives while the delete dialog is
open, the app now says in your language that nothing was deleted because the folder holds
documents you may not delete, instead of showing an English server message. Keeping the documents
and deleting only the folder no longer fails while someone else is deleting a single document in
it; that document just loses its folder and is then deleted as intended. For API clients the 403
ofDELETE /api/v1/documents/folders/{id}?documents=deletecarries the reason
FOLDER_DOCUMENTS_NOT_MANAGEABLE. (#1355) -
The API reference now describes
POST /api/v1/meals/apply-planas it behaves. Since 2.52.0
it said that withoutreplace_existinga slot that already has a meal is skipped. The endpoint
has never done that: it adds the new meals next to the ones already planned for the same date and
meal type. The description now says so, and thatreplace_existing: truefirst removes the meals
of exactly the date and meal type pairs named in the request, leaving every other slot alone. It
also lists the actual answers,201on success and400when an assignment is invalid, instead of
200. The endpoint itself is unchanged; a client that relied on the skip has to leave occupied
slots out ofassignmentsitself, as the app does. -
Housekeeping tasks turn "due today" and "overdue" on the household's day, not the server's.
The due day of a recurring task was counted in the time zone of the server. On a server running in
UTC with a household in Berlin, a task done shortly after midnight counted as done the day before,
so it showed "due today" a day early and "overdue" on the day it was actually due; west of UTC the
evening hours were off the same way. The due day is now counted from the day the task was done in
the household time zone (Settings, Region). (#1387) -
Housekeeping counts a visit in the month it happened in the household. Visits, their totals,
the "visits this month" and "paid this month" figures, the monthly payment chart and the tasks
finished this month were grouped by UTC month, and the current month itself was the UTC one. A
visit on the 1st at 00:30 in Berlin was booked to the month before, and in the first hours of a
new month the overview still showed the old one. All of them now use the household's month. (#1387) -
A dose marked as taken through the API without a time is stored in household time.
POST /api/v1/health/logs/{id}/takewithouttaken_at, and aPATCHtotakenwithout one, stored the
current moment as a UTC timestamp, while every time that is sent along is stored as household
wall-clock time. The CSV export then showed the UTC time for these doses. The current minute is
now stored in the same form as every other dose. The app itself always sends the time and was not
affected. (#1387) -
A dose recorded through the API as taken without a time now gets the current time.
POST /api/v1/health/medications/{id}/logswithstatus: "taken"and notaken_atstored the dose
without any time, so the CSV export left the time column empty for it. It now stores the current
minute in household time, the same as marking a dose as taken or correcting it to taken already
do. Doses stored this way before keep their empty time. The app itself always sends the time and
was not affected. (#1399) -
Marking an inventory deadline as done no longer stores a broken date when the next one would
fall after 9999-12-31. The next due date then has a five-digit year, which the date format cannot
hold: the deadline got a date like "99990-06-01", its reminder a date that is not a date, and the
item could not be saved again afterwards. The request is now refused with a message that names
the limit, and neither the deadline nor its history change. (#1387) -
The photo crop dialog is now cached for offline use like the rest of the app. Avatars,
birthday and inventory photos, recipe pictures and quick-link images all go through one crop
dialog, which the app loads only when you pick a picture. It was the one module of that kind the
service worker did not cache ahead of time: without a connection the dialog did not open, and
right after an update a page that was already open could load a newer dialog than the one it was
built for. The check that keeps the offline list complete read only imports written at the top of
a file and never saw the ones loaded on demand; it reads both now. (#1383) -
A group's shared-expenses activity shows each entry under the household's day. The date came
from the stored UTC timestamp, so an entry made late in the evening in a zone east of UTC, or early
in the morning west of it, showed the neighbouring day. It now follows the household time zone,
like the rest of the app. (#1309) -
The birthday API checks the reminder fields before it stores them.
POSTand
PUT /api/v1/birthdayswrotereminder_offset,reminder_custom_amountand
reminder_custom_unitexactly as sent - a negative number, a decimal or any text ended up in the
database, and the server then reminded at a time nobody had chosen or fell back to a default
without saying so. They now answer 400 with a message naming the field. A lead time is empty (no
reminder), "custom", or whole minutes from 0 up to 999 weeks, the most the custom amount can
express; the custom amount is a whole number from 1 to 999, as in the editor, and the unit is
minutes, hours, days or weeks. Records written before this keep working: older versions of the
editor offered 15 minutes, 1 hour and 2 weeks, and those remain valid, and a value that is already
stored - whatever it is - is accepted unchanged when it is sent back, so changing the name or the
date of such a birthday never fails over a reminder nobody touched. In the editor, a custom amount
that is empty, not a whole number, 0 or above 999 is now refused instead of being saved: the editor
says so in the interface language and puts the cursor in the field. It checks only a reminder that
is being changed, so a stored amount nobody touched still never blocks saving, and an amount typed
and then left behind by picking a preset instead is not sent at all. (#1384) -
On a phone, the task filter panel can be closed again after picking filters. The Filter
button sat at the end of the chip row, which scrolls sideways on a phone, and every filter you
picked put another chip in front of it and pushed it further out of view - with the panel open
and several filters chosen there was nothing left on screen to close it with. The button now has
its own place at the start of the row and no longer scrolls with the chips. The panel also ends
with a Done button, for when it is taller than the screen, and Escape closes it while the focus is
inside it. Tapping next to the panel still leaves it open: the panel sits in the page and pushes
the list down, so closing it on a tap would move the list under your finger and the tap would land
on a different task. (#1373) -
A reminder on a synced appointment now moves with it when the appointment is moved in Google,
iCloud, a CalDAV calendar or a subscribed ICS feed. The sync wrote the new start time and left
the reminder at its old one, so "1 hour before" still went off an hour before the old time - and
for an appointment brought forward, only after it had already begun. The reminder now keeps its
lead time and follows the appointment by the same amount, with the same arithmetic Yuvomi uses
when you move an appointment yourself: all-day appointments keep the time of day, a series moves
with its first occurrence, and the reminders every assignee inherited move too. A reminder that
was already delivered or dismissed and now lies in the future goes off again at its new time,
just as it does after you move an appointment in its dialog; one that has moved into the past
keeps its state rather than arriving a second time, and so does one whose appointment the
provider merely wrote down differently at the same time. Outlook is a one-way push and was not
affected. Reminders that already missed a move before this update are not corrected
retroactively. (#1377) -
On a phone, a meal can be dragged to another day again, by a grip next to its buttons. The
drag started, and ended the moment the finger moved: on a phone the week plan is a vertical list,
another day lies exactly on the scrolling axis, and the browser took the gesture as a scroll and
cancelled the drag. Making the whole row draggable would have fixed that and broken scrolling,
because on a phone the week consists almost entirely of meal rows. So each row now carries a grip,
the same one the shopping list, the category manager and the quick links already use: dragging
from the grip moves the meal, and a finger anywhere else on the row scrolls the week as before and
still opens the meal with a tap. On a wider screen nothing changes - there is no grip, and the
mouse still picks up the whole card. Without a pointer, the date in the meal's dialog remains the
way to move it. (#1317) -
In the shopping list's add row, the quantity field is as tall as its neighbours, and
"Quantity" and "Miscellaneous" are no longer cut off. The quantity field had a fixed height
and stood 6 pixels lower than the item and category fields next to it. On a computer it also had a
fixed width of 80 pixels, too narrow for its own placeholder in half of the 24 languages - English
showed "Quantit". On a phone, quantity and category each got half of the row, and the preselected
category "Miscellaneous" was cut off. The fields now take the row's
height, and the category gets more of the width than the quantity, whose entry is only a few
characters: at the usual phone widths every language's placeholder and preselected category fit
in full. The words stay as they are rather than becoming "Qty" and "Misc" - the space was the
problem, not the words, and the same fix covers the longer words in other languages. A custom
category name that is still too long, or a very narrow screen, now ends with "..." instead of
being cut off mid-word. (#1372) -
On browsers older than Chrome 97, fasting durations and a birthday's own reminder lead time
work again. Both are written by one helper that usedfindLastIndex, which Chrome knows from
version 97; below that the helper stopped with an error wherever one of these values was shown -
in Fasting, and on a birthday whose reminder is set to a custom lead time, in its view and in the
editor. It now counts backwards by hand, so it works down to the documented minimum of Chrome 87
and reads exactly as before everywhere else. -
On browsers older than Chrome 108, the main area scrolls and dialogs stay on screen.
Yuvomi sized the page, the app frame and the height limit of its dialogs withdvh, a unit for
the visible window height that Chrome understands from version 108 and Safari from 15.4. An older
browser drops every line that uses it, and with it went the height of the app frame: the main area
grew to the full length of its content, so on a Chromebook with Chrome 91 there was nothing left to
scroll, while the sidebar next to it scrolled normally. Dialogs lost their height limit the same
way and ran off the top and bottom of the screen, taking their buttons with them. Wheredvhis
unknown, these heights now fall back to the plain window height; browsers that know it behave
exactly as before. Older browsers are supported on the paths nobody can do without - starting the
app, scrolling it and using a dialog - rather than everywhere. (#1276) -
A date and time sent to the API with
Zor a numeric offset is now converted instead of cut
off./api/v1accepted values such as2026-09-21T16:00:00Zor2026-09-21T18:00:00+02:00,
but kept only their digits and dropped the offset: in a household on Europe/Berlin, an event
created with16:00:00Z- meant as 18:00 local time - landed at 16:00, without any error. Offsets
are now converted instead of dropped, into the form the field is stored in. Calendar start and end
(also on the occurrence routes and the MCP toolcreate_event) and the health timestamps
(measured_at,performed_at,consumed_at,scheduled_at,taken_at) become household
wall-clock time; for an all-day event only the date counts, so a start at midnight UTC stays on
its day west of UTC.remind_atbecomes UTC without a zone suffix in one notation, the form
reminders are compared in (a bare date is midnight UTC, when it fired before), and
last_completedof a housekeeping task becomes a UTC instant, the form/completewrites. Values
without an offset mean household wall-clock time as before, so a client that sends local digits
sees no change. This changes what is stored for input/api/v1already accepted; as a
fix to values stored wrong without an error it is named here rather than deprecated first (see
"How long that line holds" in MODULES.md).PUT /api/v1/calendar/:idnow stores the validated
value likePOSTdoes instead of the raw request value, which had moved a weekly series by an
hour at the October clock change, and an empty start is now rejected with 400. Reminders already
stored with an offset are not rewritten but compared as the instant they name: one at
18:00:00+02:00used to come two hours late. Digits that are no real point in time, such as 30
February with an offset, are rejected with 400. OpenAPI now says "converted" instead of
"normalized" and describes the request form apart from the stored one, since synced events keep
their offsets. Alast_completedwithout an offset is read in the household time zone instead of
the server's. (#1364) -
Durations are written in your interface language, not in the language of the household
region. A fasting duration and a birthday's own reminder took their words from the region: with
English as your language and "Deutsch (Deutschland)" as the household region, the fasting journal
read "1 Tg. 1 Std. 7 Min." and a birthday reminder "3 Wochen", and an English interface with a
Saudi region showed the Arabic word. The region was only ever meant to decide how numbers look,
and it still does: the words, their plural form and their order now come from your language, and
the digits and separators of the number from the region, the same as every amount next to it -
"1,5 hours" in German number style, "٣ weeks" with Arabic digits. Where language and region
match, nothing changes. (#1365) -
A birthday without a reminder of its own now shows the reminder it really gets, and saving it
no longer moves that reminder. A birthday can come in without a reminder setting: taken over
from Contacts, created for a household member or for a guest of a shared expense, or created
through the API without the field. Such a birthday is reminded on the day itself, and always has
been. Its form said "1 day before" all the same, and saving the form - even without touching
anything - wrote that down, so the reminder quietly moved to the day before. The form now shows
"On the day" for these birthdays, the reading view for read-only members says the same, and
saving writes a reminder only when you pick one. No existing reminder moves. "On the day" is also
in the menu now, for anybody who wants to pick it. (#1363) -
A birthday reminder the form no longer offers now shows as what it is, not as "None".
Until v1.6.5 the birthday form also offered 15 minutes, 1 hour and 2 weeks before, and the API
accepts any number of minutes. Birthdays saved that way kept their value, and they are still
reminded that far ahead - but the form, finding no matching entry, showed "None". Picking "None"
there to switch the reminder off therefore changed nothing, and the reminder kept coming. Such a
birthday now gets an extra entry named after what it does ("2 weeks before", "15 minutes
before"), selected and kept for that birthday alone; saving without touching it leaves the
reminder as it is, and choosing "None" switches it off. The reading view for read-only members
names it the same way. Nothing stored is changed. (#1367) -
A supply request from Housekeeping now needs shopping rights as well. The request puts the
item on the shopping list, and creates a list first when the household has none. Since 2.68.0,
sending a meal or a recipe to the shopping list asks for write access to the shopping list, but
this request was left out and asked only for housekeeping: a member who may only look at the
shopping list, or an API token scoped to housekeeping alone, could still add items to it. It now
asks the same question before it creates anything, and answers a missing right the same way. No
page in Yuvomi sends this request today, so only API clients notice the change, and anybody with
both rights notices no difference. (#1351) -
With read-only access to Tasks, a started task is announced as started. Members who may only
read Tasks see a status sign instead of the check button, and a screen reader reads out what it
shows. For a task in progress it said "Open", while the sign itself showed the amber ring of a
started task. It now says "In Progress", in the task list, for a subtask in the list and for a
subtask in the reading view. Open and done tasks are announced as before. -
With read-only access to Birthdays, tapping a birthday opens it again, so its note can be read
on a phone. Since 2.68.0 a member who may only read the Calendar - the module birthdays belong
to - no longer gets the edit form, and rightly so. But nothing took its place, and on a phone the
list leaves the note out for lack of room: the form had been the only way to it. Tapping a
birthday now opens a reading view with everything the form shows - picture, date of birth, name
day, note and reminder - and not a single control. (#1348) -
After a contact import, "Go to Birthdays" is only offered where you can use it. When imported
contacts carried a birthday, the result offered to take them over into Birthdays - also to
members who may only read the Calendar, where the page opened and the import did not, or who may
not see it at all, where the app sent them back to the start page. The offer now appears only
with permission to edit the Calendar and while Birthdays is switched on for the household; the
count of contacts with a birthday stays in the message either way. (#1348) -
Empty Notes, Contacts and Birthdays pages no longer point read-only members to a + button they
do not have. The button itself was already gone for them, but the text underneath still said
"Add new contacts with the + button" and the like. With read-only access the empty page now only
says that there is nothing here yet. (#1348) -
Controls and hints that are meant to be hidden now really disappear. Yuvomi hides an element
by settinghiddenon it, and the browser honours that only until a stylesheet gives the element
a layout of its own: anydisplayrule wins over it. Several places were caught this way and
stayed on screen. A task comment you deleted stayed in the list while the undo notice was up. The
housekeeping form showed both the daily and the hourly rate, whatever billing type was chosen. The
activity form showed its free-text type field and the prevention form its name field when neither
applied. The first phone or email row of a contact offered a remove button. An attachments field
said "Nothing attached yet" underneath the attachments it listed. A document thumbnail that had not
loaded yet covered the category icon with an empty box. The folder breadcrumb and the attachment
preview in the event dialog left empty space behind. A new test now reads every element the code
hides and every stylesheet rule that setsdisplay, and fails when a rule keeps a hidden element
on screen. (#1340) -
The recipes page keeps its "+ Recipe" button on a wide screen when recipes from Mealie or Tandoor
are in the list. From 1024 pixels wide, the round add button moves up into the page header, into
the same spot as the "Filter by source" menu. As soon as a mirrored recipe was in the list, building
that menu cleared the spot and took the button with it, and the n shortcut stopped working along
with it. The filter now has a place of its own next to the button. (#1340) -
With several people assigned, the event dialog now says why no calendar was picked. Since
#1060 a new event goes to the calendar that names its one assigned person as the default
assignee. With two or more people assigned, the assignment deliberately picks nothing and the
event goes to your own default target - and until now it did so without a word, although the
notes for 2.66.0 said the dialog tells you instead of guessing. It now does, right under the
calendar choice, and names the calendar used instead, or "Store locally only" when your default
target is not available. The hint appears only when a calendar names at least one of the assigned
people, because only then would the assignment have picked something; a household without
default assignees sees nothing new. Choosing a calendar yourself removes the hint, and two
calendars naming the same person keep their own hint as before. (#1332) -
Two first starts on the same new database no longer knock each other out. A fresh installation
that is started twice at once - a container restarted while it is still coming up, two replicas on
one volume - has both processes decide which migrations are pending before either of them has
written anything, and both then apply the same one. The straggler died on it, withUNIQUE constraint failed: schema_migrations.versionor, where the migration's DDL is not idempotent,
table ... already exists. A migration that is already recorded is now the one case the runner
takes as done and carries on from: the other process wrote that row in the same transaction as the
change itself, so the change is there. Nothing else is taken as done. The decision is made on what
stands in the database after the failed attempt has rolled back, never on the wording of the
error - a syntax error, a table that is missing, a constraint that comes out of the migration's own
SQL and a mistake in a JavaScript hook all still stop the start, exactly as before. The start that
came first no longer dies either: it could be locked out of the write by the other one
(SQLITE_BUSY,SQLITE_BUSY_SNAPSHOT), and SQLite reports a snapshot conflict at once, without
waiting. Such a start now rolls back and reads again - if the other process has recorded the
migration in the meantime, it is done; if not, the runner tries the same migration again, at most
five times with a short pause, and only then fails with the original SQLite error. (#1331) -
The language Yuvomi starts in now follows your browser's whole language tag. A browser reports
something likezh-TWorde-AT, and Yuvomi read only the part in front of the hyphen. For the
24 languages it ships that is the right answer every time, but it means a language written in two
scripts could never be reached on its own: whoever sets their system to Taiwan would be given the
simplified Chinese we have and would have to find the other one in the language menu, if it
existed. Yuvomi now answers a tag with the most specific language it actually carries - the exact
tag first, then the script a region implies (Taiwan, Hong Kong and Macau write traditional
characters, mainland China and Singapore do not), and otherwise the plain language, which is what
every language in the app resolves to today, unchanged. The same read happens twice, because the
page sets its language once in the<head>before anything is rendered and once when the app
loads, and the early one had fallen behind: it knew 23 of the 24 languages, Filipino missing since
the day it arrived, so a Filipino system was told the page was English while the page came up in
Filipino. Both now know the same languages and resolve a tag the same way, and a test holds them
together rather than a comment asking the next change to remember. (#1324) -
Moving an appointment across a daylight-saving boundary no longer drags its reminder off the
lead time you set. A reminder is a lead - an hour before, a day before - but when the
appointment moved, the reminder was carried along by the distance between the two dates on the
wall clock rather than by the real distance between the two moments. The two differ by exactly
the hour a summer-time change adds or takes away, so an appointment moved from March to July kept
"one hour before" in the dialog while the alert landed on the appointment's own start time, and
one moved the other way went off two hours early. Nothing looked broken, because "at the start
time" is a setting somebody could have chosen on purpose. All four ways an appointment's time
changes now run through the same calculation: moving a whole series, moving a single occurrence,
an occurrence taking its reminders over from its series, and the "this and all following" split.
All-day entries are included, where the reminder hangs on 09:00 local time. The hour the clocks
change is covered too, including the one that happens twice in autumn: a reminder moved into it
lands on the later of the two readings, the one after the change. The one hour a year that a
spring-forward skips has no honest answer, because that time of day never happens - an
appointment moved onto it counts as starting when the clocks reach the other side. Reminder
times already stored are left as they are. (#1300) -
Health no longer offers buttons that a read-only member is not allowed to press. Where your
access to the module is "read", the largest module in the app still carried every writing control
across all of its tabs, and each of them ended in an error message once the form was filled in:
booking or skipping a dose, the as-needed dose, correcting an entry in the medication log,
deleting a single measurement, editing a medication, a lab report, an activity or a vaccination,
and on the cycle tab starting or ending a period, tapping a day in the calendar, editing an entry
in the history, the import and the settings. The rule is the one Tasks, Calendar, Notes and
Contacts already follow: something that shows a state stays, as a sign that names the state rather
than as a greyed-out button that promises a touch doing nothing, and something that only acts
disappears. A due dose therefore keeps saying that it is still pending, a card keeps its stock
level, the cycle calendar keeps its colours and your own marks, and the line that says your period
is due today stays - without the button beside it. What you can read stays complete: measurements
and charts, the medication log, lab results and their reference ranges, the activity log,
vaccination records, the cycle history, and the CSV exports, which only read. The fasting tab
already asked about this right and is unchanged. Recording for someone you care for is unaffected
as long as your access to Health is "write" - the module right is now asked first, so a
caregiving permission no longer offers a button the server would refuse. (#1265) -
The budget no longer offers buttons that a read-only member is not allowed to press. Where
your access to Budget is "read", every tab except the reports still carried its writing controls,
and each of them ended in an error message: deleting or confirming an entry, opening an entry to
edit it, managing categories, adding or editing an account, editing or deleting a loan, booking
an instalment or correcting one, setting a category budget or the savings goal; on the
subscriptions tab adding, editing, renewing and deleting a subscription - by button and by swipe -
and managing its categories, payment methods, monthly budget and currency; on the shared-expenses
tab adding an expense or a group, editing, archiving, restoring or deleting a group, settling up
and inviting someone. The rule is the one Tasks, Notes, Contacts and Health already follow:
something that shows a state stays, something that only acts disappears. An expected entry keeps
its "expected" tag, a recurring entry and one with a receipt keep their marks, a savings goal that
is set stays as a card, and a plan row keeps its target, what was spent and what is left. An
entry, a subscription or a shared expense still opens when you tap it, but into a read-only view
instead of the editor: it shows everything the editor would - an entry's subcategory, account,
visibility, who is responsible, how it repeats and its receipts; a subscription's description,
category, payment method, the account it runs under and its notes; an expense's payer, how it
is split and each person's share, its notes and receipts - and offers nothing to change. Receipts
are listed only if you may read documents. The same view now opens for an expense in an archived
group. A loan's report also shows the account, the first due month, the interest terms and the
notes from the loan form. Where a value only lived in a form, it now stands where a tap already
leads, without a new button: a credit card's statement shows its credit limit at the top (for
everyone - the card itself only shows what is still available), and at "read" a shared-expense
group shows its default currency, its default split and its members under the description,
with the names cut off after five and the rest counted. What you can read stays complete: the monthly summary and chart, the
account balances and each account's statement, the subscription analytics, a group's balances,
expenses and activity, the reports tab and the CSV exports. A tab with nothing in it yet says so,
instead of telling you to use a + button that is not there. Shared expenses are part of Budget in
the permission settings, so the same right decides there. (#1265) -
Housekeeping no longer offers buttons that a read-only member is not allowed to press. Where
your access to the module is "read", the page still carried most of its writing controls, and each
of them ended in an error message: checking a housekeeper in or out, adding a chore from a
template or from the form, marking a chore done, undoing that, editing or deleting a chore,
editing a housekeeper's profile and setting up the first one from the empty page. The visit log
also showed a greyed-out "Mark as paid" button that did nothing. The rule is the one Tasks, Notes
and Health already follow: something that shows a state stays, as a sign that names the state
rather than as a greyed-out button, and something that only acts disappears. A housekeeper who is
checked in right now therefore still shows as "Currently here", in the words of the dashboard
tile, and every chore keeps its name, area, rhythm and how urgent it is. Visits, the monthly
reports and the month switch stay as they were. What only an edit form used to show is now
readable too: a housekeeper's profile opens as a read-only view with everything the form shows -
contact details, birthday, billing, schedule, colours and notes - and the visit report now also
lists the minutes worked on an hourly visit and the payment receipt, where your access to
Documents lets you read it. The receipt upload in the visit dialog now also asks about Documents,
where the receipt is stored: somebody allowed to edit Housekeeping but only to read Documents saw
the upload, and saving ended in an error before the visit itself was saved. The upload is gone
for them, a receipt that is already linked stays listed, and saving the visit keeps it. (#1265) -
The shopping list no longer offers buttons that a read-only member is not allowed to press.
Where your access to Shopping is "read", every row could still be ticked off (by its box, by
tapping the row and by swiping), deleted (by button and by swipe), edited and dragged into a new
order, and the page kept the quick-add field, the + button, "Create new list" and the whole list menu -
rename, duplicate, import from the meal plan, send, categories, stores and delete. Each of them
ended in an error message, and a ticked item sprang back. The rule is the one Tasks, Notes and
Housekeeping already follow: something that shows a state stays, something that only acts
disappears. The box stays as a sign that says whether the item is ticked off or still open, and
it keeps following what others in the household tick off. What only the edit form used to show
is readable too: an item with a price, a store, a link or a note gets a button that opens a
read-only view with everything the form shows, and the link can be opened from there. An empty
list or a household without lists now only says so, instead of inviting you to add something.
Two ways between the kitchen tabs also ask the right of the tab they write into: "Into pantry"
on the shopping list needs write access to the Pantry, and the cart on a pantry row and "Add all
to shopping list" need write access to Shopping. Before, both ended in an error message for a
member who could only read the other tab. (#1265) -
A nightly recurring appointment that crosses midnight no longer covers the entry it should be
sharing its column with. On a day that carries two occurrences of the same series - last
night's tail and tonight's start - the second took the first one's place in the layout, so the
tail was drawn at full width over anything it overlapped. Only reachable since short overnight
entries started appearing in the time grid at all. -
An appointment that crosses midnight but is shorter than a day is back in the time grid, on both
days. Until now the calendar asked one question about such an entry - does it touch two calendar
days? - and every entry that did was drawn as a chip without times in the all-day row above the
grid. So an evening from 22:00 to 01:30 looked like two whole days, and the ninety minutes after
midnight were nowhere to be found at the hour somebody would look for them. The week and day views
now also ask how long it lasts: under 24 hours it is a block in the grid of both days, cut at the
day boundary - the first day from its start time to midnight, the second from midnight to its end
time - which is the same arithmetic a night shift in the duty roster has had for a while. Because
the block stops at midnight, it costs the next morning nothing: an appointment at 09:00 keeps the
full width of the column. Entries of 24 hours or more stay in the all-day row on purpose; a bar
across the days says more than a block that runs from top to bottom in every column. Each block
also says what it is doing on the day it stands on - "from 22:00" on the first day, "until 01:30"
on the second - instead of repeating the whole appointment in both columns, where the second one
named an evening that day does not have. That is the wording the agenda has used for those days
all along, so both views now answer the same question about the same day the same way. An
appointment that ends at exactly 00:00 still counts as a single day. (#1313) -
Filipino was offered as the language for stored entries and then refused when you saved it.
The setting listed all 24 languages, but the server built its own list from the locale file names
with a pattern that required exactly two letters - and Filipino's file isfil.json, with three.
Picking it came back as "invalid language". Every other language was unaffected, which is why this
went unnoticed.