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
|
`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
|
(dispatch cron no-op), inbound API 401s with no key configured. Synced to
|
||||||
`/opt/odoo/custom-addons` + `-i` via the restart-safe recipe.
|
`/opt/odoo/custom-addons` + `-i` via the restart-safe recipe.
|
||||||
- **NexaCloud: NOT deployed (deliberately).** `deploy.sh` does `rsync --delete` of the
|
- **NexaCloud (prod, `vps.nexasystems.ca` / 192.168.1.250): DEPLOYED — INERT.** Did NOT
|
||||||
**local working tree** then `--no-cache` rebuild — it would ship the concurrent
|
use `./deploy.sh` (it `rsync --delete`s the working tree → would have shipped the
|
||||||
**uncommitted Cursor WIP** (7 files) to prod. My 2b/2c is also feature-flagged OFF and
|
concurrent **uncommitted Cursor WIP** (7 files) AND wiped the gitignored prod `.env`
|
||||||
the app wasn't boot-tested. Deploy only AFTER: Cursor's WIP is committed/finished, the
|
files). Instead deployed **surgically**: rsync of ONLY my 6 committed billing files (no
|
||||||
app boots clean, and `ODOO_BILLING_*` config is set (otherwise it's inert anyway).
|
`--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):**
|
**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
|
1. Grant the read-only DSN (`fusion_billing.nexacloud_dsn`) — see the module README — then
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# License OPL-1
|
# License OPL-1
|
||||||
{
|
{
|
||||||
'name': 'Fusion Helpdesk Central — Client API Keys',
|
'name': 'Fusion Helpdesk Central — Client API Keys',
|
||||||
'version': '19.0.2.4.1',
|
'version': '19.0.2.4.2',
|
||||||
'category': 'Productivity',
|
'category': 'Productivity',
|
||||||
'summary': 'Admin UI on the central Odoo for issuing per-client API '
|
'summary': 'Admin UI on the central Odoo for issuing per-client API '
|
||||||
'keys used by fusion_helpdesk client deployments.',
|
'keys used by fusion_helpdesk client deployments.',
|
||||||
|
|||||||
@@ -289,18 +289,23 @@ class HelpdeskTicket(models.Model):
|
|||||||
def _fc_post_engagement_notice(self, owner_name, findings):
|
def _fc_post_engagement_notice(self, owner_name, findings):
|
||||||
"""Public chatter message posted when an engagement is sent.
|
"""Public chatter message posted when an engagement is sent.
|
||||||
|
|
||||||
Two purposes:
|
Mirrors the engagement email body so the chatter shows the same
|
||||||
- Tells the employee (via the entech My Tickets inbox, which only
|
context the owner is reading: the engineer's reply (findings)
|
||||||
reads public comments) that their request has been escalated
|
and the summary that's being used for the decision. We skip the
|
||||||
for approval and who's deciding.
|
Original Request section that's in the email — the employee
|
||||||
- Captures our reply (findings) on the public thread so the
|
filed the ticket, they already know what they asked for.
|
||||||
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).
|
|
||||||
|
|
||||||
Posted via message_post with subtype_xmlid='mail.mt_comment' so
|
Critically: uses styled <div>s instead of <blockquote>. Odoo
|
||||||
it survives the entech-side _public_messages filter
|
auto-flags <blockquote> content as "quoted" (data-o-mail-quote-
|
||||||
(message_type='comment' + non-internal subtype).
|
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
|
from markupsafe import Markup, escape
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -309,14 +314,27 @@ class HelpdeskTicket(models.Model):
|
|||||||
'<p>⏳ <b>Awaiting owner approval from %s.</b> '
|
'<p>⏳ <b>Awaiting owner approval from %s.</b> '
|
||||||
'Their decision will appear here when they reply.</p>'
|
'Their decision will appear here when they reply.</p>'
|
||||||
) % name_html
|
) % name_html
|
||||||
|
# Section 1: our reply (the wizard's findings)
|
||||||
text = (findings or '').strip()
|
text = (findings or '').strip()
|
||||||
if text:
|
if text:
|
||||||
body += (
|
body += (
|
||||||
'<p><b>Our reply:</b></p>'
|
'<p style="margin:12px 0 4px 0;"><b>Our reply:</b></p>'
|
||||||
'<blockquote style="margin:6px 0 0 0; padding:6px 12px; '
|
'<div style="margin:0; padding:10px 14px; '
|
||||||
'border-left:3px solid #c7dcfa; color:#1e3a5f; '
|
'border-left:3px solid #c7dcfa; color:#1e3a5f; '
|
||||||
'background:#f4f8ff;">%s</blockquote>'
|
'background:#f4f8ff; border-radius:4px; '
|
||||||
) % escape(text).replace('\n', '<br/>')
|
'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(
|
self.message_post(
|
||||||
body=Markup(body),
|
body=Markup(body),
|
||||||
message_type='comment',
|
message_type='comment',
|
||||||
|
|||||||
Reference in New Issue
Block a user