# Order private notes, follow-up owner, and profit — agreed design

- Agreed: 2026-10-25, through three question rounds (Q1–Q17).
- Status: implemented and applied to the active local project; deployment is pending.
- Branch: `feat/order-staff-notes-profit-2026-10-25`, based on `dev`.
- Scope: admin/staff web orders list and order details, filtered totals, and the existing Excel export.
- Background: [orders and checkout](../01-orders-checkout.md), [domain glossary](../../../CONTEXT.md).

## Goal and record ownership

Staff can keep private notes, nominate a follow-up owner, and see product profit or
loss for a customer's complete checkout.

All three additions belong to the **Combined Order** (the whole checkout), including
when it contains several sellers' **Child Orders**. There is one shared list of
individual private notes and one optional owner for the checkout. Profit appears on its summary row. Opening a child
order's admin details must refer to the same checkout-level values; sibling orders
must not develop separate copies.

The follow-up owner is a staff responsibility, separate from the delivery employee
and accountant. Assignment does not change staff access or the existing fulfillment,
payment, seller-confirmation, or accounting workflow.

## Private notes

- Individual plain-text notes, each at most 5,000 characters; Arabic text is supported.
- Anyone with `view_order_details` may add a note. The writer may edit/delete their
  own notes; CX Manager may edit/delete any note. Super Admin retains its override.
- Each entry displays its writer, creation time and its own Edit/Delete controls.
  Editing never transfers authorship. The old shared textarea cannot overwrite notes.
- The orders list shows an indicator when the checkout has a note.
- Preserve previous text, new text, actor, and time in an internal change history,
  including deletions. Deletes remove the entry from the active list and retain its
  original record and audit. Stale edit/delete revisions return 409.
- `order_staff_notes` stores body, immutable author ID/name, revision, timestamps and
  soft deletion time. The migration imports saved legacy note versions from the
  existing history once, retaining their writers and leaving original audits unchanged.
  Since 2026-11-10 the table also holds the order-rating follow-up's customer notes
  (`notable_type = customer`, `notable_key` = the customer key; order notes are
  `notable_type = order`, ids unchanged). `OrderStaffNote` sees only order rows
  (`App\Utility\StaffNotes`, migration `2026_11_10_000005`).
  Unattributed current notes remain manageable by CX Manager; ownership is never
  guessed from the current follow-up assignee.
- A new note alerts every active CX and CX Manager (bell, database only) except its
  writer; the alert names the writer and the order, never the note's text. Edits and
  deletes alert nobody (added 2026-11-05).
- Notes remain editable after payment, delivery, cancellation, and return.
- Render notes as escaped text, preserving useful line breaks.
- Notes and their history are internal staff data. They must not enter customer or
  seller pages, customer/seller API responses, invoices, or customer/seller messages.
- Private notes are excluded from the Excel export.

## Follow-up owner

The child-order and combined-order headers expose an internal follow-up button.
It opens a responsive modal containing assignment, private notes and change history.
The permission-controlled product margin stays directly on the order details page,
outside the modal. The modal renders outside page forms
through the admin layout's `order-staff-modals` stack. Its header/footer stay visible
while the body scrolls. The footer Save button targets the assignment form;
Add note and each note's Save/Delete controls submit separate requests.
Notification links using `#order-staff-details` and history pagination open it
automatically. Closing preserves unsaved notes; note drafts survive a tab refresh.
Add, Edit, Delete, assignment saves and history/note pagination all update the modal
through AJAX, without reloading the page. An inline confirmation precedes Delete;
buttons show a spinner and are disabled during requests. The modal retains scroll
position and unsaved drafts. During saving, duplicate submits and dismissal are
blocked; errors keep the modal and text available. Success shows a toast immediately
for the saving user. Recipient assignment alerts remain quiet.

The inline product-margin summary shows a signed amount,
status icon/label, net product sales after discounts/coupon, and purchase cost.
Missing costs and void checkouts never display a numeric profit. The orders list
and filtered totals reuse compact status cards with the same calculation rules.

- One optional owner per checkout; checkouts start unassigned.
- The picker contains active staff/admin accounts with both `view_order_details`
  and `edit_order`. Eligibility is checked again when saving, rather than trusting
  the submitted account ID. The existing Super Admin permission bypass applies.
- Only staff in `CX` or `CX Manager` roles, with the existing order-view/edit
  permissions, can assign, reassign, or clear the owner. Super Admin retains its
  bypass. This does not narrow the assignee dropdown's existing eligibility rules.
- Display the owner on both the orders list and details page; provide assignment
  controls in the internal section of the details page.
- Assignment remains editable after payment, delivery, cancellation, and return.
- Record the previous owner, new owner, assigning staff member, and time. History
  must remain understandable if an account is later removed or renamed.
- New assignments and reassignments create a personal in-app notification for the new
  owner, with the checkout number and a link to open it. Assigning or reassigning
  to yourself does not send a notification. Notes-only saves, unchanged
  ownership, clearing ownership, and rejected stale saves do not create assignment alerts.
- The assignee's bell refreshes quietly while they use the admin panel; assignment
  notifications never produce a recipient toast. The saving staff member receives a
  translated success toast after Save reloads the page.

Add list filters for a selected staff member, **Assigned to me**, and **Unassigned**.
Apply them through the existing shared list/export query, retain them through
pagination, and combine them with existing order filters. These filters select
checkouts by their shared owner; they do not create a new access restriction.

## Permissions and simultaneous edits

Use a dedicated profit permission alongside the existing order permissions:

| Action | Rule |
| --- | --- |
| View internal fields | Existing access to the relevant order screen |
| Add a private note | Any staff/admin with `view_order_details`; `edit_order` is not required |
| Edit/delete a private note | Its writer or CX Manager, plus `view_order_details` |
| Assign an unassigned order | `CX` or `CX Manager`, plus the existing view/edit permissions |
| Reassign/clear an existing owner | `CX Manager` or the currently assigned employee with the `CX` role, plus the existing view/edit permissions |
| View profit/loss on list/details or export it | `view_order_profit_loss`, plus access to the relevant screen/export |

Enforce permissions on the server as well as in the UI. Preserve the existing
Super Admin permission bypass.

Any staff/admin with `view_order_details` may add notes, regardless of assignment or
`edit_order`. Edit/delete additionally requires authorship or CX Manager. `full access`
has the same writer-only rule unless also CX Manager or Super Admin. The locked save
rechecks permission, note ownership, checkout scope and revision before changing notes.
Note access does not grant assignment access: employees outside the CX roles cannot
change ownership. A CX employee can reassign/clear only their own assigned
orders; other CX employees cannot take over an assigned order. CX Manager may change
any assignment. The UI and locked save both use the current owner, so an outdated
unassigned form cannot bypass this restriction.

`2026_10_26_000003_add_view_order_profit_loss_permission.php` registers the new
permission under Staff Roles > Orders. The follow-up migration
`2026_10_26_000004_grant_order_profit_loss_roles.php` grants it to existing CEO,
Product Manager, Marketing Manager, admin, Accountant, Ecommerce and Operation
Manager roles, matching case/space/underscore/hyphen variants. It does not create
missing roles or grant CX/CX Manager profit access. `2026_10_27_000002_grant_new_permissions_to_full_access.php` adds "full access": Staff Roles > edit lists only the permissions the editing account holds (`@can`), so its staff could not see or grant it. Without
the permission, margin data is not calculated/passed to list or detail views, and
the three checkout-profit columns are omitted from exports. Existing purchase-price
columns retain their established behavior.

Notes and ownership saves require concurrency checks. A stale save must be rejected
when another staff member has changed the relevant data since the form was loaded;
it must not silently overwrite that change. Explain that a refresh is required and
preserve the second person's unsaved note text so it can be reviewed and reapplied.
Use a version/fingerprint or equivalent implementation consistent with the repository.

## Profit definition

This is product margin, not a complete accounting net-profit calculation:

```text
profit = product selling revenue after product discounts and checkout coupon
         - product purchase cost
```

Exclude customer shipping charges, tax, payment fees, and actual delivery expenses.
Example: products sold for 100 SYP, a 10 SYP coupon, and purchase cost of 80 SYP
produce 10 SYP profit.

Use the actual billed product amounts; product discounts already reflected in saved
selling prices must not be deducted twice. Subtract the checkout's effective coupon
once, using the existing live-order coupon rules. Calculate across the included
live lines of the whole checkout, not independently per seller.

### Purchase-cost source and reliability

1. Prefer the saved order-line purchase cost when available. It is a **whole-line
   BASE amount**, so do not multiply it by quantity a second time.
2. Without a saved cost, use the product's current purchase cost under the existing
   RAW-to-BASE conversion rule and multiply by the line quantity. Mark the resulting
   checkout figure as **Estimated profit/loss**. An estimate on any included line
   makes the checkout result estimated.
3. If any included line has no usable purchase cost, show **Cannot calculate —
   purchase cost missing**, with the missing-cost product-line count. Do not show a
   numeric profit/loss or profitability badge based on only the known costs.
4. Reuse the existing `order_details.purchase_price` snapshots. Do not introduce an
   additional reporting-cost column. Sources without a saved cost keep the existing
   current-product fallback and estimated label. This supersedes the initial plan
   to add a separate snapshot, following the user's scope clarification.

Preserve the existing distinction between an explicitly saved zero cost and an
unavailable current product cost: the current utility accepts a non-null saved zero
but treats a missing/nonpositive fallback product price as unknown.

Amounts are calculated in BASE and displayed in SYP under the repository's historical
order exchange-rate and rounding rules. Mixed exchange-type products must be
normalized before summing. Do not convert an already-BASE amount with a product's
exchange type again, or use today's display rate for historical order revenue.

### Lifecycle and labels

| Checkout condition | Presentation |
| --- | --- |
| At least one live child order is still in progress | Expected profit / Expected loss |
| All remaining live child orders are delivered | Profit / Loss |
| Some included costs use current product prices | Add the Estimated qualifier |
| Any included cost is missing | Cannot calculate, plus missing-cost count |
| Valid calculation equals zero | Break-even |
| Entire checkout cancelled or fully returned | Show the closed status; no profit amount |

Exclude cancelled/returned child orders and cancelled/returned lines from both
product revenue and purchase cost. Partial cancellation/return therefore leaves a
calculation for the products that remain included. Payment settlement is not a
condition for showing the delivered-order figure.

Recalculate from current order data after adding/removing products, changing quantity
or variant, editing coupons, cancellation, and return. Correct the existing admin
quantity-edit path that changes selling amounts without scaling the saved whole-line
purchase cost. Check equivalent staff, customer web, mobile, and other applicable
order-edit paths so the new display does not inherit inconsistent cost snapshots.

## Filtered totals and Excel export

- Add profit/loss to the existing filtered totals and Excel export, including
  estimated/missing-cost labels. Include the follow-up owner in the export.
- Count each included checkout once for checkout-level profit. The total covers the
  whole filtered result, across all pages, rather than only the current page.
- Keep the whole-checkout definition consistent between the displayed summary rows,
  totals, and export. Where a seller/child filter selects a checkout, its checkout
  profit is still the whole-checkout figure; do not sum it once per matching child.
- Convert each checkout under its historical rate before aggregating displayed SYP
  figures across dates. Follow the existing rounding convention consistently.
- If any included checkout has missing costs, the filtered profit total reads
  **Cannot calculate**, with the missing-cost count. Do not present a partial known-cost
  sum as the total profit.
- If costs are complete but any included checkout is estimated, qualify the filtered
  total as estimated. A total containing in-progress checkouts must preserve the
  expected nature of those figures rather than imply every amount is realized.
- Owner filters affect list, totals, and export consistently. Notes remain excluded
  from the export.

## Existing code and implementation guidance

Read the current code before implementation; line numbers and older codebase-map
snapshots may drift.

| Source | Relevance |
| --- | --- |
| [OrderController](../../../app/Http/Controllers/OrderController.php) | Shared `filteredOrdersQuery`, admin list/details, quantity/product/coupon edits |
| [OrderPurchaseCostUtility](../../../app/Utility/OrderPurchaseCostUtility.php) | Existing purchase-cost fallback, live-line rules, historical-rate display |
| [Order model](../../../app/Models/Order.php) | `listTotals`, child-order relationships, existing auditing/events |
| [CombinedOrder model](../../../app/Models/CombinedOrder.php) | Checkout-level record and relationships |
| [OrderFollowUp model](../../../app/Models/OrderFollowUp.php) | Existing per-child confirmation, delivery and accountant assignments; distinct from this checkout owner |
| [SellerPricing](../../../app/Services/SellerPricing/SellerPricing.php) | Current line-cost snapshot calculation and product-source rules |
| [OrderDetail model](../../../app/Models/OrderDetail.php) | Creation-time cost stamping |
| [OrdersExport](../../../app/Exports/OrdersExport.php) | Existing export and purchase-cost columns |
| [Admin list](../../../resources/views/backend/sales/index.blade.php) | Grouped checkout/seller rows and existing cost warnings |
| [Combined details](../../../resources/views/backend/sales/show_combined.blade.php) | Checkout details with child-order sections |
| [Child details](../../../resources/views/backend/sales/show.blade.php) | Existing per-child admin details entry point |

Current facts: there is no general private-order-notes field or follow-up staff owner.
Existing address/cancel/return notes have different meanings and must not be reused.
Existing cost snapshots cover governed seller products; other sources often fall back
to the current product cost. The separate sales/profit report has different cost
semantics and is not the source of truth for this calculation.

Match the existing controller/helper/utility style. Do not introduce a repository and
service architecture for this ticket. Reuse the existing audit mechanism where it can
preserve private-note text and owner history securely. Storage, migrations, concurrency
tokens, and query strategy are implementation details; they must satisfy these behaviors.

The new internal fields must not trigger order-payment, stock, customer-status, seller,
or Logistic side effects. Cost snapshot consistency may require changes in shared
creation/edit paths, but no new customer/seller UI or public API fields are in scope.

Historical costs may already be affected by past quantity edits. Fixing future edits
does not reconstruct those historical costs; this design does not authorize a bulk
production-data repair or invent historical snapshots from today's catalog prices.

## Acceptance criteria

1. A multi-seller checkout has one shared note and owner, visible consistently from
   the list and all relevant admin detail entry points.
2. Authorized staff can save/clear Arabic notes up to 5,000 characters and
   assign/reassign/unassign eligible owners at any lifecycle stage. History retains
   old/new values, actor, and time.
3. View-only staff cannot mutate either field, including by a direct request.
   Customers and sellers cannot retrieve internal notes/history or ownership fields.
4. Concurrent stale saves fail without overwriting newer changes, and unsaved note
   text remains recoverable.
5. Owner filters compose with existing filters, persist through pagination, and
   produce consistent list, filtered-total, and export results.
6. Profit uses discounted product revenue minus the checkout coupon and purchase cost;
   shipping, tax, and fees are excluded. Both positive and negative results work, and
   an exact zero displays Break-even.
7. Saved costs remain stable after catalog price changes. Current-cost fallbacks are
   labelled estimated, while missing costs block numeric profit and propagate to the
   filtered total with an accurate missing-line count.
8. Pending/mixed-progress, delivered, fully void, and partially cancelled/returned
   checkouts follow the lifecycle rules above. Mixed exchange types and historical
   rates follow the existing price contract.
9. Quantity/variant/product/coupon edits and cancellations/returns update profit
   consistently across applicable web/mobile creation and edit paths. In particular,
   the admin quantity-edit regression is covered.
10. Summary rows, filtered totals, and Excel agree, with checkout figures counted once.
    Excel includes owner and reliability labels and excludes private notes.
11. The Arabic-first interface has translations for new fields, filters, labels,
    validation errors, and concurrency messages.

Use meaningful SQLite tests for the calculation, authorization, shared-checkout
ownership, filtering, audit history, and stale saves. Add targeted opt-in runtime
coverage only where necessary. Never run destructive artisan commands or mutate
production data for verification.

## Decision status

All product questions Q1–Q17 were accepted. The user then authorized implementation.
On 2026-10-26 the user revised the notification decision: alert the new assignee in-app,
including reassignment, with a success toast only for the saving staff member. They also
requested that the filters modal keep its bottom action buttons within the screen.
No deployment, commit, or push has been performed.

## Implementation and rollout

- `OrderStaffDetail` stores one row per checkout in `order_staff_details`. It is
  deliberately separate from the publicly serialized checkout/order models.
- `OrderStaffUtility` supplies eligible staff, filters, revision-checked saves, and
  details-page data. Each save locks the checkout (including its first save), rejects
  stale revisions, and writes the metadata and audit history in the same transaction.
- Private changes use the existing `audits` table, event `order_staff_update`, with
  `OrderStaffDetail` as the auditable type. Customer order-history readers do not read
  that type. Names are retained in the audit payloads for deleted/renamed accounts.
- `OrderProfitUtility` supplies the list, details, filtered totals and export from one
  rule. Filtered totals iterate distinct matching checkouts in bounded batches.
- Existing `order_details.purchase_price` snapshots retain their established priority
  and follow quantity changes, variant switches and partial-return splits. Other
  sources use the existing product-cost fallback, marked estimated. Seller payout
  eligibility is unchanged; no new cost snapshots or historical backfill are added.
- The existing export retains its child-order rows and BASE accounting columns.
  Appended checkout columns hold owner, SYP margin, calculation status and missing-cost
  count. Margin is written on the first exported row encountered for a checkout, then
  left blank on its other rows so spreadsheet sums do not double-count it.
- The details form saves with AJAX. On a conflict/error it keeps note text on screen
  and in tab-local session storage, scoped to the signed-in account and checkout.
  A refresh restores the draft for review; a successful save clears it and shows the
  saving session's success toast through the existing admin-layout flash handler.
- `OrderFollowUpAssignedNotification` uses Laravel's database channel and existing
  `notifications` table. Its row is written in the assignment/audit transaction;
  failure rolls the edit back. Payload contains only checkout ID and assignment
  revision. No notification-type migration, mail, push, or queue worker is required.
- The bell is available to staff with order access, including staff without the
  general `view_notifications` menu permission. Assignment alerts have a separate
  section above other order alerts, and render on the all-notifications page too.
  Opening an alert is restricted to its recipient and existing order permissions:
  details-capable staff open the checkout; list-only staff open their allowed list
  filtered to that checkout. It is then marked read.
- A small feed refreshes the bell every 30 seconds while the tab is visible, and
  when it becomes visible again. It does not mark alerts read or show recipient
  toasts. Background requests retain pending success flashes so they cannot consume
  the saving user's toast before the page reload.
- The filters dialog is bounded by viewport height (dynamic viewport height when
  supported). Its fields scroll inside the modal, while the header/footer remain
  visible. The Apply button stays in the original filters form.

Deploy the migration `2026_10_25_000001_add_order_staff_details_and_cost_snapshots.php`
through the normal reviewed migration process. It now adds only the internal table,
without updating existing order data. Its original filename is retained for installs
that have already recorded it. A follow-up cleanup migration,
`2026_10_26_000001_drop_unused_order_purchase_cost_snapshot.php`, removes the previously
added column only when it has no saved values; it stops for review if any exist.
The existing migration
translation-sync hook imports the new English/Arabic strings; manually managed
deployments can use the existing `translations:sync` command. Before this migration,
margin still uses the existing cost fallback, while staff metadata controls stay hidden.

The individual-note controls require `2026_10_28_000010_create_order_staff_notes_table.php`.
It adds `order_staff_notes` and preserves the existing visible note versions and their
recorded writers without changing the original audits. It was applied to the guarded
local database; remote deployment must run it through the normal migration process.

### Internal save contract

Individual notes use the following CSRF-protected admin endpoints, each requiring
staff/admin authentication and `view_order_details`:

| Method and path | Payload | Additional rule |
| --- | --- | --- |
| POST `/admin/orders/combined/{id}/staff-notes` | `body`: required plain text, 1–5,000 characters | Anyone with order-view access may add |
| PATCH `/admin/orders/combined/{id}/staff-notes/{noteId}` | `body` and current positive `revision` | Writer or CX Manager |
| DELETE `/admin/orders/combined/{id}/staff-notes/{noteId}` | Current positive `revision` | Writer or CX Manager |

An unknown, deleted or differently scoped note returns 404; stale revisions return
409, validation errors 422 and insufficient access 403. The modal sends
`X-Order-Staff-Modal: 1`: success returns `{success, message, html}` containing the
fresh modal content and uses `Cache-Control: private, no-store`. The client replaces
only the modal content and shows the translated success toast; no flash is queued
for the next navigation. Legacy requests without that header retain a session flash.
Stale AJAX responses also include the latest modal so users can review current data
without reloading, preserving their draft and its original revision. These note
actions never notify the follow-up assignee. Assignment and note revisions are
independent, so editing a note does not invalidate an assignment form.
Changes and audits commit atomically; author ID/name cannot be supplied or changed
by the client. `order_staff_details.private_notes` is retained as a compatibility
summary; list/header indicators count active individual notes after migration.

`POST /admin/orders/combined/{id}/staff-details`, route `orders.staff_details.update`.
Requires staff/admin authentication, normal admin middleware, CSRF and `view_order_details`.
After migration this endpoint handles assignments only: `private_notes` is rejected
with 422 so an old form cannot bypass note ownership. Before migration it retains the
legacy shared-field behavior. CX may assign an unassigned order, or reassign/clear
their own assigned order. CX Manager can change any assignment. Assignment changes
additionally require `edit_order`.
The controller and locked save enforce these rules; authorized writers see the add
form and owner-specific actions, while assignment controls follow the current-owner rule.
Staff missing order view permission have read-only notes. Omitted `private_notes`
preserves the current notes under the row lock; an explicit null clears them only
when the actor can edit notes. Unauthorized note changes receive `403`.
The note-access `403` includes an Arabic/English explanation, also shown in the
read-only modal and used as the client's fallback for an empty forbidden response.
Note editors, including `full access`, do not bypass assignment/CX restrictions.

| Form field | Meaning |
| --- | --- |
| `revision` | Required nonnegative integer from the displayed metadata; 0 before the first save |
| `private_notes` | Nullable plain text, at most 5,000 characters |
| `follow_up_user_id` | Optional eligible staff/admin ID; omitted retains ownership, empty clears it; changes require a CX role |

Returns `200 {"success":true,"message":"…"}` on success; AJAX modal requests additionally
receive `html`, and legacy requests retain the translated session flash. Returns `409 {"success":false,"message":"…"}`
for a stale revision, Laravel `422` validation errors for invalid input/ineligible
owners, `403` for insufficient access, `404` for an unknown checkout, and `503` before
the metadata migration is available. Non-CX editors receive `403` for assignment
changes, including clearing; notes-only saves retain ownership. A notes-only edit may retain an existing owner
who has since become unavailable; assigning that account to another checkout is refused.
AJAX modal HTML contains internal notes/history and is returned only to the authorized
staff/admin session with private/no-store caching. No customer or seller API is extended.

`GET /admin/orders/combined/{id}/staff-details` returns `{html}` for the same authorized
staff/admin with `view_order_details`. The modal uses it for AJAX pagination. It
performs no mutations and also uses private/no-store caching.

`GET /admin/order-follow-up-notifications`, route `orders.staff_notifications`, is
an authenticated staff/admin feed requiring existing order access. It returns
`{html, has_assignments, has_unread}` for the current user's newest 20 unread assignment
alerts. It is private/no-store, contains no private notes, does not mark alerts read,
and uses an indexed existence query for the badge. Alert clicks use the existing
`admin.notification.read-and-redirect` route, which verifies recipient ownership and
current order access before opening the checkout/list.

## Verification recorded on 2026-10-25

- Targeted SQLite run: **185 tests, 1,244 assertions, no failures/errors**. Covers
  `OrderStaffAndProfitTest`, `OrdersListSellerFilterTotalsTest`,
  `OrdersAccountantExportTest`, `CombinedOrderCouponTest`, `CustomerOrderEditTest`,
  `CustomerReturnRequestTest`, `SellerPricingTest`, and `ExchangeTypePricingTest`.
  Includes the actual admin route with its normal middleware, stale revisions,
  ownership/history, Arabic input, missing/estimated costs, historical rates,
  confirmation-sort filtering, totals past the first 200-checkout batch, partial
  returns, and the admin quantity-cost regression.
- An initial full-suite run exercised 2,944 tests. The isolated worktree lacked the
  local application key/default-language settings, causing environment-dependent
  errors and failures. All 30 affected classes were rerun with a synthetic test-only
  application key and `DEFAULT_LANGUAGE=ar`: **439 tests, 2,567 assertions, no
  failures/errors, one skipped**. The full suite was not rerun as a single invocation
  after setting those defaults. PHPUnit reports the existing configuration deprecation.
- Headless Chrome, using a synthetic SQLite-rendered panel: Arabic RTL desktop/mobile
  rendering, escaped note text, client length limit, request serialization, conflict
  handling, draft retention/restoration, button recovery, and mobile overflow passed;
  no JavaScript exceptions. This validates the isolated panel rather than a live
  production order page.
- PHP syntax checks passed for all 15 changed/new PHP files. All five affected Blade
  templates compiled and passed PHP syntax checks. The diff passes whitespace checks
  with the repository's existing CRLF view file treated as CRLF.

Tests used in-memory SQLite with runtime MySQL tests disabled. No production migration
or application-data mutation was performed.

The user subsequently requested that the files be visible in the current project.
The implementation was transferred to `C:/Users/lenovo/kapitano_web`, on
`feat/order-staff-notes-profit-2026-10-25`, retaining the active project's existing
committed fixes and uncommitted `.gitignore` deletion. Existing local translations
were preserved and the new labels merged into them. The original isolated worktree
is retained as a detached development copy. All feature changes in the active local
project remain uncommitted; nothing has been pushed. This task has not executed the
new database migration.

After transfer, the active project's stale local Laravel route and compiled-view
caches were cleared. The same targeted run passed in the active project:
185 tests and 1,244 assertions, with no failures/errors (the existing PHPUnit
configuration deprecation remains).

### Follow-up verification on 2026-10-26

- The expanded local SQLite run passed **201 tests and 1,324 assertions**, including
  notification delivery/reassignment/deduplication, recipient privacy, actual admin
  routes, order-permission-aware links, transaction rollback on notification failure,
  a saver-only success flash, preserving that flash during background refreshes, and
  notification query counts. The existing PHPUnit configuration deprecation remains.
- Headless Chrome verified that the employee's badge and order link refresh correctly,
  read-state changes clear the badge, and **no recipient toast is shown**.
- The original filter-modal footer clipping was reproduced (footer bottom at 658 px
  in a 600 px viewport). The fixed layout passed at 1366×600, 1280×720, 390×844,
  844×390, and 640×320. Fields scroll; footer buttons remain visible, clickable, and
  attached to the filters form. No JavaScript exceptions were observed.
- PHP syntax checks passed for 19 changed/new PHP files; all seven affected Blade
  templates compiled and passed syntax checks. Changes remain local and uncommitted.
