Compare commits
2 Commits
fe98fadf61
...
34a65f9c4a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34a65f9c4a | ||
|
|
97cce8c755 |
@@ -120,11 +120,18 @@ All four chunks of #2 are now built. The brainstorm "which slice" question resol
|
||||
`nexacloud_dsn`, all charges `plan_id` NULL (rating cron no-op), no webhooks queued
|
||||
(dispatch cron no-op), inbound API 401s with no key configured. Synced to
|
||||
`/opt/odoo/custom-addons` + `-i` via the restart-safe recipe.
|
||||
- **NexaCloud: NOT deployed (deliberately).** `deploy.sh` does `rsync --delete` of the
|
||||
**local working tree** then `--no-cache` rebuild — it would ship the concurrent
|
||||
**uncommitted Cursor WIP** (7 files) to prod. My 2b/2c is also feature-flagged OFF and
|
||||
the app wasn't boot-tested. Deploy only AFTER: Cursor's WIP is committed/finished, the
|
||||
app boots clean, and `ODOO_BILLING_*` config is set (otherwise it's inert anyway).
|
||||
- **NexaCloud (prod, `vps.nexasystems.ca` / 192.168.1.250): DEPLOYED — INERT.** Did NOT
|
||||
use `./deploy.sh` (it `rsync --delete`s the working tree → would have shipped the
|
||||
concurrent **uncommitted Cursor WIP** (7 files) AND wiped the gitignored prod `.env`
|
||||
files). Instead deployed **surgically**: rsync of ONLY my 6 committed billing files (no
|
||||
`--delete`; `.env` + Cursor's files untouched), `docker compose build backend`,
|
||||
**boot-tested in a throwaway container** (`run --rm --no-deps backend python -c "import
|
||||
app.main"` → BOOT_OK) before swapping, then `up -d backend`. `nexacloud-api` healthy,
|
||||
`/health` OK. Feature OFF: `ODOO_BILLING_ENABLED` unset → `/billing/webhooks/central`
|
||||
returns 404 and no usage is pushed. Activate later by setting `ODOO_BILLING_*` in
|
||||
`/opt/nexacloud/.env` (+ compose env passthrough) once the Odoo side is wired.
|
||||
**NOTE:** Cursor's 7-file WIP remains uncommitted locally and was never deployed — when
|
||||
Cursor finishes, a normal `./deploy.sh` will ship it (and re-sync `.env`).
|
||||
|
||||
**Remaining before go-live (gated on infra / ops you do):**
|
||||
1. Grant the read-only DSN (`fusion_billing.nexacloud_dsn`) — see the module README — then
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1
|
||||
{
|
||||
'name': 'Fusion Helpdesk Central — Client API Keys',
|
||||
'version': '19.0.2.4.1',
|
||||
'version': '19.0.2.4.2',
|
||||
'category': 'Productivity',
|
||||
'summary': 'Admin UI on the central Odoo for issuing per-client API '
|
||||
'keys used by fusion_helpdesk client deployments.',
|
||||
|
||||
@@ -289,18 +289,23 @@ class HelpdeskTicket(models.Model):
|
||||
def _fc_post_engagement_notice(self, owner_name, findings):
|
||||
"""Public chatter message posted when an engagement is sent.
|
||||
|
||||
Two purposes:
|
||||
- Tells the employee (via the entech My Tickets inbox, which only
|
||||
reads public comments) that their request has been escalated
|
||||
for approval and who's deciding.
|
||||
- Captures our reply (findings) on the public thread so the
|
||||
employee can see what support said — same content the owner
|
||||
receives in the engagement email, just without the AI summary
|
||||
(which is a decision-aid for the owner, not the employee).
|
||||
Mirrors the engagement email body so the chatter shows the same
|
||||
context the owner is reading: the engineer's reply (findings)
|
||||
and the summary that's being used for the decision. We skip the
|
||||
Original Request section that's in the email — the employee
|
||||
filed the ticket, they already know what they asked for.
|
||||
|
||||
Posted via message_post with subtype_xmlid='mail.mt_comment' so
|
||||
it survives the entech-side _public_messages filter
|
||||
(message_type='comment' + non-internal subtype).
|
||||
Critically: uses styled <div>s instead of <blockquote>. Odoo
|
||||
auto-flags <blockquote> content as "quoted" (data-o-mail-quote-
|
||||
node="1") and the chatter UI then collapses it behind a "..."
|
||||
widget — exactly the wrong UX here, since the findings + summary
|
||||
are the load-bearing content, not throwaway quotation. Plain
|
||||
divs with the same visual treatment render fully inline.
|
||||
|
||||
Posted with subtype_xmlid='mail.mt_comment' so it passes the
|
||||
entech-side _public_messages filter (message_type='comment' +
|
||||
non-internal subtype) and reaches the employee's My Tickets
|
||||
inbox.
|
||||
"""
|
||||
from markupsafe import Markup, escape
|
||||
self.ensure_one()
|
||||
@@ -309,14 +314,27 @@ class HelpdeskTicket(models.Model):
|
||||
'<p>⏳ <b>Awaiting owner approval from %s.</b> '
|
||||
'Their decision will appear here when they reply.</p>'
|
||||
) % name_html
|
||||
# Section 1: our reply (the wizard's findings)
|
||||
text = (findings or '').strip()
|
||||
if text:
|
||||
body += (
|
||||
'<p><b>Our reply:</b></p>'
|
||||
'<blockquote style="margin:6px 0 0 0; padding:6px 12px; '
|
||||
'<p style="margin:12px 0 4px 0;"><b>Our reply:</b></p>'
|
||||
'<div style="margin:0; padding:10px 14px; '
|
||||
'border-left:3px solid #c7dcfa; color:#1e3a5f; '
|
||||
'background:#f4f8ff;">%s</blockquote>'
|
||||
) % escape(text).replace('\n', '<br/>')
|
||||
'background:#f4f8ff; border-radius:4px; '
|
||||
'white-space:pre-wrap;">%s</div>'
|
||||
) % escape(text)
|
||||
# Section 2: the AI summary the owner is seeing for the decision
|
||||
summary = (self.x_fc_ai_summary or '').strip()
|
||||
if summary:
|
||||
body += (
|
||||
'<p style="margin:14px 0 4px 0;">'
|
||||
'<b>Summary sent to the owner:</b></p>'
|
||||
'<div style="margin:0; padding:10px 14px; '
|
||||
'border-left:3px solid #e5e7eb; color:#444; '
|
||||
'background:#f9fafb; border-radius:4px; '
|
||||
'white-space:pre-wrap;">%s</div>'
|
||||
) % escape(summary)
|
||||
self.message_post(
|
||||
body=Markup(body),
|
||||
message_type='comment',
|
||||
|
||||
Reference in New Issue
Block a user