Previously, maintenance requests created on HTTP failures were never
automatically resolved. Operators had to close them manually, with no
traceability of when or why the request was closed.
This commit adds automatic resolution when a service returns HTTP 200
while an open maintenance request exists for it.
**Detection logic** (in ``cron_check_http_services``):
Before pass 1, the cron takes a snapshot of all services that currently
have an open (non-done) ``maintenance.request`` via
``http_maintenance_request``. After pass 1, services in that snapshot
that are now OK (``http_status_ok = True``) are identified as recovered
and passed to the new ``_close_http_maintenance_request()`` method.
**Closure logic** (new ``_close_http_maintenance_request`` method):
1. Finds the first ``maintenance.stage`` with ``done = True``.
If none exists (misconfigured instance), the method is a no-op.
2. Moves the ``maintenance.request`` to that done stage via ``sudo()``
to bypass ACL restrictions from the cron user context.
3. Posts a chatter note on the request as OdooBot (``base.partner_root``)
using ``subtype_xmlid="mail.mt_note"`` (internal note, not a follower
notification) indicating the service URL and that the closure was
performed automatically by the monitoring cron.
4. Clears ``http_maintenance_request`` on the ``service.instance``,
allowing a fresh request to be created if the service fails again.
**Tests** (2 new, 16 total):
- ``test_service_recovery_closes_request``: full end-to-end scenario —
first cron run produces a KO request, second cron run with HTTP 200
asserts the request is in a done stage, the chatter note mentioning
the service URL exists, and ``http_maintenance_request`` is cleared.
- ``test_no_close_when_no_open_request``: calling
``_close_http_maintenance_request`` on a service with no open request
is a no-op and does not raise.
**README**: "Automatic Maintenance Requests" section extended with the
recovery behaviour (done stage, OdooBot note, field cleared).
Previously, a single ``maintenance.request`` was created per equipment,
regardless of how many services were down on that equipment. The name
was ``[HTTP KO] {equipment.name}`` and deduplication relied on a
name+date+equipment search that was fragile (manual clear of the field
would lose the reference to an existing open request).
This commit reworks the whole creation logic:
- **1 request per KO service** instead of 1 per equipment. Each failing
``service.instance`` gets its own ``maintenance.request``, allowing
fine-grained tracking and independent resolution.
- **Request name** is now ``[HTTP KO] {service_url}``, making it
immediately identifiable without opening the record.
- **Description** includes the error detail: ``HTTP {status_code}`` for
HTTP errors, or a human-readable network error label when
``last_http_status_code == -1`` (timeout / DNS / SSL failures).
- **Deduplication** is now based solely on whether an open (non-done)
``maintenance.request`` already exists on the ``service.instance``
via the new ``http_maintenance_request`` field. No date boundary —
as long as the request is open, no new one is created.
- **``http_maintenance_request``** field moved from
``maintenance.equipment`` to ``service.instance``, where it belongs
given the 1-request-per-service model. It is exposed as an optional
hidden column in the service instance list view.
- ``_build_ko_services_description()`` is removed (no longer needed).
- ``create_http_maintenance_request()`` now receives a single
``service.instance`` recordset instead of a list.
Tests updated accordingly (14 tests total):
- Tests 2, 4, 9, 10, 12, 13 now assert on
``service_instance.http_maintenance_request``.
- Test 2 also verifies the request name contains the service URL and
the description contains the HTTP status code.
- New test 14 asserts that two KO services on the same equipment
produce two distinct requests with the correct names.