The Elastic Beanstalk nginx proxy kept its 1 MB default body limit, so every
media upload over ~1 MB got an nginx 413 before reaching the API. Ship a
.platform nginx override (120M) in the bundle and assert it in the bundle
contract.
Apply the client-confirmed contract: photos up to 10 MB (JPEG/PNG/HEIC),
videos up to 100 MB (MP4/MOV), at most 10 photos and 3 videos per work order,
with stable generic rejection messages. The request ceiling (110 MB) sits
between the per-kind caps and the proxy so oversize files get the generic
message. The vendor portal accepts the same photo/video types and caps.
Editing a work order's POC never reached the backend: no update path wrote
PocName/PocPhone/PocNotes, so the optimistic UI edit was lost on refetch and
the completion freeze captured the Site contact instead of the manual value,
and nothing was audited.
- Add tenant-scoped PATCH api/workorders/{id}/poc via new WorkOrderPocService
+ WorkOrderPocDataService: persists the override, stages FieldChanged audit
entries (which also write field locks so sync never overwrites a manual POC),
and enforces row-version concurrency and terminal-status read-only rules.
- Lock semantics (SH-190): a manual POC away from the Site's live contacts is
stored WO-level; an edit equal to a live Site contact (or blanking name+phone)
stores nothing so the WO follows the Site. PocCustomized exposes the state.
- Board projection, completion freeze and create now share one Site-contact
fallback (first non-deleted contact by SiteContactOrder) so a never-overridden
WO keeps following the Site, including at create when the wizard prefills it.
- Route contract baseline gains PATCH {id:int}/poc.
Adds the personal producers behind GET /api/notifications without changing its shape:
new assignments (SH-288), unanswered comments on work the user takes part in (SH-289),
@mentions, and decisions on uplifts the user requested (SH-215).
Adds GET /api/notifications, a per-user read model derived from live
work-order state: Unassigned (grouped, High), No Vendor and Aveta Missing
(per work order, Medium) and Vendor Conflict, account-scoped from claims
and ordered by section severity with a fixed reason tie-break.
#144 branched from the SH-210 decision-actions commit before the SH-207/SH-208
read-contract corrections landed, so it re-implemented workOrderClosed,
attachmentCount, decidedByName, and pendingExposureTotal with stale semantics:
it projected WorkerOrderNumber (the CRM external id) instead of InternalWONumber
(what the board renders) and summed raw RequestedNTE, which double-counts the new
NTE total on vendor-portal rows across sequential approvals.
Merge origin/feat/ab/sh-210-uplift-decisions (#141, what lands) in and resolve
every conflict in #141's favour, so those fields and their granted-amount
exposure math now come from #141 rather than being duplicated here. Keep only the
two deltas #144 actually adds on top of #141:
- attachmentCount counts non-deleted UpliftEvidence documents on the dispatch,
not every completion document, so completion photos no longer inflate the chip;
- the queue read resolves the effective work order via the primary-plus-linked
(DispatchWorkOrders) convention the sibling reads use, so a dispatch linked only
through that table surfaces its WO context, closed flag, and exposure. Covered
by an in-memory test and a SQLite relational test that proves the fallback
translates to SQL.
Drop the superseded attachment-count test that asserted completion documents
count, and align the relational test to seed InternalWONumber.
The approvals queue frontend needs four list-contract additions the read
contract PRs do not carry yet: workOrderClosed so the Approved tab can
disable Revoke on terminal work orders (mirroring the SH-196 revoke
guard), attachmentCount from non-deleted UpliftEvidence documents so the
+N chip renders, decidedByName for the Approved By column, and the
queue-wide pendingExposureTotal for the header total.
Seed an organization-wide Area catalogue (East, Central, West, California)
with stable ids, add a nullable AreaId to VendorCompany, allow only Admins
to change it through the roster endpoints, expose areas facet metadata and
an areas[n] company-directory filter with the __unassigned__ sentinel.