Files
Odoo-Modules/fusion_repairs/data/ir_cron_data.xml
gsinghpal c506b53dec feat(fusion_repairs): Bundle 3 - reminders + upsells (X2 + X4 + M3)
X2 Day-before visit reminder email
- New cron 'Fusion Repairs: Day-before visit reminders' (daily at 08:00)
  walks repair.order records with at least one linked
  fusion.technician.task scheduled for tomorrow and not yet reminded.
- Sends mail.template email_template_visit_day_before to the client.
- New x_fc_day_before_reminder_sent flag (copy=False) so the cron
  never re-sends the same reminder.
- Template uses 4px blue accent, 600px max-width, shows the scheduled
  date + technician name + equipment, with a 'reply to reschedule' note.
- Verified: cron flagged the test repair x_fc_day_before_reminder_sent=True
  after running.

X4 Post-visit NPS / Google review email
- New cron 'Fusion Repairs: Send post-visit NPS emails' (hourly)
  finds repairs in state='done' with write_date >= 24h ago and no NPS
  email sent. Sends mail.template email_template_post_visit_nps.
- New x_fc_nps_email_sent flag so we never re-pester clients.
- Template uses 4px green accent + 'Leave a Google review' CTA button
  linking to res.company.x_fc_google_review_url (or a sensible Google
  search fallback when the company hasn't configured a review URL).

M3 Loaner auto-offer for long-running repairs
- Soft-bridges fusion_loaners_management without a hard dep -
  cron_offer_loaner_for_long_repairs returns immediately if the
  fusion.loaner.checkout model isn't installed.
- Walks repair.order records open longer than
  fusion_repairs.loaner_offer_threshold_days (ICP, default 3 days)
  with no existing loaner-offer activity.
- Posts a 'Repair: Offer Loaner' activity (new mail.activity.type)
  assigned to the repair responsible.
- New x_fc_loaner_offered flag to prevent daily re-posting.
- Manual 'Offer Loaner' button on repair header opens the
  fusion.loaner.checkout wizard pre-filled with partner + SO.
- Daily cron runs at 08:30.

Email + ICP + cron wiring:
- 2 new mail.template records (visit_day_before, post_visit_nps)
- 1 new mail.activity.type (loaner_offer)
- 3 new ir.cron records (day-before, NPS, loaner)
- 1 new ir.config_parameter (loaner_offer_threshold_days)
- 1 new header button (Offer Loaner) on repair.order

Verified end-to-end on local westin-v19:
  X2 setup repair: RO-202605-12 task: TASK-00045
     day-before flag after cron: True (expected True)
  M3 loaner model not installed - cron correctly no-op'd
  (no flag set, no activity posted, no error - the soft-dep guard works)

Bumped to 19.0.1.3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 23:59:40 -04:00

74 lines
4.1 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Daily maintenance reminders at 30/7/1 days before due date. -->
<record id="cron_maintenance_due_reminders" model="ir.cron">
<field name="name">Fusion Repairs: Send maintenance due reminders</field>
<field name="model_id" ref="model_fusion_repair_maintenance_contract"/>
<field name="state">code</field>
<field name="code">model.cron_send_due_reminders()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now().replace(hour=7, minute=0, second=0, microsecond=0) + timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="active" eval="True"/>
</record>
<!-- CL15: Escalate unacknowledged on-call pages every 5 minutes.
Pages older than fusion_repairs.on_call_escalate_minutes
(default 15) get re-paged to the next priority. -->
<record id="cron_on_call_escalate" model="ir.cron">
<field name="name">Fusion Repairs: Escalate unacknowledged on-call pages</field>
<field name="model_id" ref="model_fusion_repair_on_call_service"/>
<field name="state">code</field>
<field name="code">model.cron_escalate_unacknowledged()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">5</field>
<field name="interval_type">minutes</field>
<field name="nextcall" eval="(DateTime.now() + timedelta(minutes=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="active" eval="True"/>
</record>
<!-- X2: Day-before visit reminders. Runs every morning at 08:00. -->
<record id="cron_day_before_reminders" model="ir.cron">
<field name="name">Fusion Repairs: Day-before visit reminders</field>
<field name="model_id" ref="repair.model_repair_order"/>
<field name="state">code</field>
<field name="code">model.cron_send_day_before_reminders()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now().replace(hour=8, minute=0, second=0, microsecond=0) + timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="active" eval="True"/>
</record>
<!-- X4: Post-visit NPS. Runs hourly to send 24h after state=done. -->
<record id="cron_post_visit_nps" model="ir.cron">
<field name="name">Fusion Repairs: Send post-visit NPS emails</field>
<field name="model_id" ref="repair.model_repair_order"/>
<field name="state">code</field>
<field name="code">model.cron_send_post_visit_nps()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="nextcall" eval="(DateTime.now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="active" eval="True"/>
</record>
<!-- M3: Offer loaner activity for long-running repairs. Runs daily. -->
<record id="cron_offer_loaner" model="ir.cron">
<field name="name">Fusion Repairs: Offer loaner for long-running repairs</field>
<field name="model_id" ref="repair.model_repair_order"/>
<field name="state">code</field>
<field name="code">model.cron_offer_loaner_for_long_repairs()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now().replace(hour=8, minute=30, second=0, microsecond=0) + timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="active" eval="True"/>
</record>
</data>
</odoo>