diff --git a/fusion_plating/fusion_plating_bridge_mrp/__manifest__.py b/fusion_plating/fusion_plating_bridge_mrp/__manifest__.py
index 52715b5f..50647a3e 100644
--- a/fusion_plating/fusion_plating_bridge_mrp/__manifest__.py
+++ b/fusion_plating/fusion_plating_bridge_mrp/__manifest__.py
@@ -5,7 +5,7 @@
{
"name": "Fusion Plating — MRP Bridge",
- 'version': '19.0.6.1.0',
+ 'version': '19.0.6.2.0',
'category': 'Manufacturing/Plating',
'summary': 'Bridge Fusion Plating facilities, baths and tanks to Odoo MRP work orders.',
'description': """
diff --git a/fusion_plating/fusion_plating_bridge_mrp/models/fp_proficiency.py b/fusion_plating/fusion_plating_bridge_mrp/models/fp_proficiency.py
index e8ca3437..7fb38e86 100644
--- a/fusion_plating/fusion_plating_bridge_mrp/models/fp_proficiency.py
+++ b/fusion_plating/fusion_plating_bridge_mrp/models/fp_proficiency.py
@@ -13,6 +13,8 @@ Shop Roles automatically. The operator never has to fill in a form;
their growing skill set just unlocks itself.
"""
+from markupsafe import Markup
+
from odoo import _, api, fields, models
@@ -160,13 +162,14 @@ class FpOperatorProficiency(models.Model):
'x_fc_work_role_ids': [(4, role.id)],
})
employee.message_post(
- body=_(
+ body=Markup(_(
'🎉 %(name)s promoted — qualified for '
'%(role)s after %(count)s successful '
- 'completions.',
- name=employee.name,
- role=role.name,
- count=rec.completed_count,
- ),
+ 'completions.'
+ )) % {
+ 'name': employee.name,
+ 'role': role.name,
+ 'count': rec.completed_count,
+ },
subtype_xmlid='mail.mt_note',
)
diff --git a/fusion_plating/fusion_plating_bridge_mrp/models/mrp_production.py b/fusion_plating/fusion_plating_bridge_mrp/models/mrp_production.py
index da07c4ca..d2112145 100644
--- a/fusion_plating/fusion_plating_bridge_mrp/models/mrp_production.py
+++ b/fusion_plating/fusion_plating_bridge_mrp/models/mrp_production.py
@@ -5,6 +5,8 @@
import logging
+from markupsafe import Markup
+
from odoo import api, fields, models, _
from odoo.exceptions import UserError
@@ -423,7 +425,7 @@ class MrpProduction(models.Model):
steps_txt = wo_steps.get(wo.sequence)
if steps_txt:
wo.message_post(
- body=_('Recipe steps:
%s') % steps_txt, + body=Markup(_('Recipe steps:
%s')) % steps_txt, subtype_xmlid='mail.mt_note', ) production.message_post( diff --git a/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py b/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py index 0ba0b80b..740d64d0 100644 --- a/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py +++ b/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py @@ -3,6 +3,8 @@ # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Plating product family. +from markupsafe import Markup + from odoo import api, fields, models, _ @@ -86,8 +88,8 @@ class SaleOrder(models.Model): # Don't block SO confirm — log + continue. The manager # can still create the MO manually. so.message_post( - body=_('Auto-MO creation failed:
%s. '
- 'Create the MO manually from MRP.') % exc,
+ body=Markup(_('Auto-MO creation failed: %s. '
+ 'Create the MO manually from MRP.')) % exc,
)
return res
@@ -145,11 +147,11 @@ class SaleOrder(models.Model):
if recipe and 'x_fc_recipe_id' in Production._fields:
mo_vals['x_fc_recipe_id'] = recipe.id
mo = Production.create(mo_vals)
- self.message_post(body=_(
+ self.message_post(body=Markup(_(
'Draft Manufacturing Order %s '
'auto-created. Accept the parts and click Assign to Me to '
'release it to the floor.'
- ) % (mo.id, mo.name))
+ )) % (mo.id, mo.name))
@api.depends(
'state', 'invoice_status',
@@ -182,17 +184,22 @@ class SaleOrder(models.Model):
))
# Paid vs invoiced
- if so.invoice_status == 'invoiced' and so.invoice_ids:
- latest = so.invoice_ids.filtered(lambda i: i.state == 'posted')
- all_paid = latest and all(
- i.payment_state in ('paid', 'in_payment') for i in latest
- )
- if shipped and all_paid:
- so.x_fc_workflow_stage = 'complete'
- continue
- if all_paid and not shipped:
- so.x_fc_workflow_stage = 'paid'
- continue
+ posted_invoices = so.invoice_ids.filtered(lambda i: i.state == 'posted')
+ has_posted_invoice = bool(posted_invoices)
+ all_paid = has_posted_invoice and all(
+ i.payment_state in ('paid', 'in_payment') for i in posted_invoices
+ )
+ if shipped and all_paid:
+ so.x_fc_workflow_stage = 'complete'
+ continue
+ if all_paid and not shipped:
+ so.x_fc_workflow_stage = 'paid'
+ continue
+ # Once an invoice is posted (regardless of payment), the SO has
+ # moved past 'shipped' — the action is on accounting, not us.
+ if shipped and has_posted_invoice:
+ so.x_fc_workflow_stage = 'invoicing'
+ continue
if shipped:
so.x_fc_workflow_stage = 'shipped'
@@ -263,7 +270,7 @@ class SaleOrder(models.Model):
if 'x_fc_assigned_manager_id' in mo._fields and not mo.x_fc_assigned_manager_id:
mo.x_fc_assigned_manager_id = user.id
self.message_post(
- body=_('Job assigned to %s. %d MO(s) released to the floor.')
+ body=Markup(_('Job assigned to %s. %d MO(s) released to the floor.'))
% (user.name, len(mos)),
)
return True
diff --git a/fusion_plating/fusion_plating_bridge_mrp/views/sale_order_views.xml b/fusion_plating/fusion_plating_bridge_mrp/views/sale_order_views.xml
index aab6f25a..a8b59f3d 100644
--- a/fusion_plating/fusion_plating_bridge_mrp/views/sale_order_views.xml
+++ b/fusion_plating/fusion_plating_bridge_mrp/views/sale_order_views.xml
@@ -92,12 +92,15 @@
help="Close the open delivery record(s) and fire auto-invoice per strategy."/>
-
-