diff --git a/sale_usability/__manifest__.py b/sale_usability/__manifest__.py
index e03f307..baf32ea 100644
--- a/sale_usability/__manifest__.py
+++ b/sale_usability/__manifest__.py
@@ -23,6 +23,7 @@
'views/account_move.xml',
'views/res_company.xml',
"views/res_partner.xml",
+ "views/sale_template.xml",
'wizards/sale_invoice_discount_all_lines_view.xml',
'security/ir.model.access.csv',
],
diff --git a/sale_usability/models/sale_order.py b/sale_usability/models/sale_order.py
index c218d4f..cf870d1 100644
--- a/sale_usability/models/sale_order.py
+++ b/sale_usability/models/sale_order.py
@@ -135,3 +135,29 @@ class SaleOrderLine(models.Model):
if no_product_code_param and no_product_code_param == 'True':
product = product.with_context(display_default_code=False)
return super().get_sale_order_line_multiline_description_sale(product)
+
+ # In v12, I developped the 3 modules service_line_qty_update_base, service_line_qty_update_purchase
+ # and service_line_qty_update_sale that add a wizard to update service lines and track the changes
+ # in the chatter.
+ # In v14, you can edit the quantity of the service lines directly and the purchase module
+ # tracks changes in the chatter... but the sale module doesn't track the changes of 'qty_delivered'
+ # So I "ported" that native feature of the purchase module to sale.order.line... here it is !
+ # We can remove that code if this feature is added in the sale module (it's NOT the case in
+ # odoo v17)
+ def write(self, vals):
+ if 'qty_delivered' in vals:
+ for line in self:
+ line._track_qty_delivered(vals['qty_delivered'])
+ return super().write(vals)
+
+ def _track_qty_delivered(self, new_qty):
+ self.ensure_one()
+ prec = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+ if (
+ float_compare(new_qty, self.qty_delivered, precision_digits=prec) and
+ self.order_id.state == 'sale'):
+ self.order_id.message_post_with_view(
+ 'sale_usability.track_so_line_qty_delivered_template',
+ values={'line': self, 'qty_delivered': new_qty},
+ subtype_id=self.env.ref('mail.mt_note').id
+ )
diff --git a/sale_usability/views/sale_template.xml b/sale_usability/views/sale_template.xml
new file mode 100644
index 0000000..cd34e17
--- /dev/null
+++ b/sale_usability/views/sale_template.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ The delivered quantity has been updated.
+
+
:
+ Delivered Quantity: ->
+
+
+
+
+
+
diff --git a/service_line_qty_update_base/__init__.py b/service_line_qty_update_base/__init__.py
deleted file mode 100644
index 4027237..0000000
--- a/service_line_qty_update_base/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import wizard
diff --git a/service_line_qty_update_base/__manifest__.py b/service_line_qty_update_base/__manifest__.py
deleted file mode 100644
index 4245897..0000000
--- a/service_line_qty_update_base/__manifest__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2020 Akretion France (http://www.akretion.com)
-# @author Alexis de Lattre
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-{
- 'name': 'Service Line Qty Update Base',
- 'version': '12.0.1.0.0',
- 'category': 'Tools',
- 'license': 'AGPL-3',
- 'summary': 'Update delivery qty on service lines - Base module',
- 'author': 'Akretion',
- 'website': 'http://www.akretion.com',
- 'depends': ['product'],
- 'data': [
- 'wizard/service_qty_update_view.xml',
- ],
- 'installable': False,
-}
diff --git a/service_line_qty_update_base/wizard/__init__.py b/service_line_qty_update_base/wizard/__init__.py
deleted file mode 100644
index c0ac3f4..0000000
--- a/service_line_qty_update_base/wizard/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import service_qty_update
diff --git a/service_line_qty_update_base/wizard/service_qty_update.py b/service_line_qty_update_base/wizard/service_qty_update.py
deleted file mode 100644
index 711587d..0000000
--- a/service_line_qty_update_base/wizard/service_qty_update.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2020 Akretion France (http://www.akretion.com/)
-# @author: Alexis de Lattre
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-
-from odoo import _, api, fields, models
-from odoo.tools import float_compare, float_is_zero
-import odoo.addons.decimal_precision as dp
-from odoo.exceptions import UserError
-
-
-class ServiceQtyUpdate(models.TransientModel):
- _name = 'service.qty.update'
- _description = 'Wizard to update delivery qty on service lines'
-
- line_ids = fields.One2many('service.qty.update.line', 'parent_id', string="Lines")
-
- def run(self):
- self.ensure_one()
- prec = self.env['decimal.precision'].precision_get('Product Unit of Measure')
- for line in self.line_ids:
- if float_compare(line.post_delivered_qty, line.order_qty, precision_digits=prec) > 0:
- raise UserError(_(
- "On line '%s', the total delivered qty (%s) is superior to the ordered qty (%s).") % (line.name, line.post_delivered_qty, line.order_qty))
- fc_added = float_compare(line.added_delivered_qty, 0, precision_digits=prec)
- if fc_added < 0:
- raise UserError(_(
- "On line '%s', the added quantity is negative.") % line.name)
- if fc_added > 0:
- line.process_line()
- return True
-
-
-class ServiceQtyUpdateLine(models.TransientModel):
- _name = 'service.qty.update.line'
- _description = 'Lines of the wizard that updates delivery qty on service lines'
-
- parent_id = fields.Many2one(
- 'service.qty.update', string='Wizard', ondelete='cascade')
- product_id = fields.Many2one('product.product', string='Product', readonly=True)
- name = fields.Char()
- name_readonly = fields.Char(related='name', string='Description')
- order_qty = fields.Float(
- string='Order Qty',
- digits=dp.get_precision('Product Unit of Measure'))
- order_qty_readonly = fields.Float(related='order_qty', string='Product Unit of Measure')
- pre_delivered_qty = fields.Float(
- digits=dp.get_precision('Product Unit of Measure'))
- pre_delivered_qty_readonly = fields.Float(related='pre_delivered_qty', string='Current Delivered Qty')
- added_delivered_qty = fields.Float(
- string='Added Delivered Qty',
- digits=dp.get_precision('Product Unit of Measure'))
- post_delivered_qty = fields.Float(
- compute='_compute_post_delivered_qty',
- string='Total Delivered Qty',
- digits=dp.get_precision('Product Unit of Measure'))
- uom_id = fields.Many2one('uom.uom', string='UoM', readonly=True)
- comment = fields.Char(string='Comment')
-
- @api.depends('pre_delivered_qty', 'added_delivered_qty')
- def _compute_post_delivered_qty(self):
- for line in self:
- line.post_delivered_qty = line.pre_delivered_qty + line.added_delivered_qty
-
- def process_line(self):
- # Write and message_post
- return
-
- # sale : qty_delivered
- # purchase : qty_received
diff --git a/service_line_qty_update_base/wizard/service_qty_update_view.xml b/service_line_qty_update_base/wizard/service_qty_update_view.xml
deleted file mode 100644
index 2593996..0000000
--- a/service_line_qty_update_base/wizard/service_qty_update_view.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
- service.qty.update
-
-
-
-
-
-
- Service Order Lines - Update Delivered Qty
- service.qty.update
- form
- new
-
-
-
-
diff --git a/service_line_qty_update_purchase/__init__.py b/service_line_qty_update_purchase/__init__.py
deleted file mode 100644
index 9b42961..0000000
--- a/service_line_qty_update_purchase/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import models
-from . import wizard
diff --git a/service_line_qty_update_purchase/__manifest__.py b/service_line_qty_update_purchase/__manifest__.py
deleted file mode 100644
index 91f0aae..0000000
--- a/service_line_qty_update_purchase/__manifest__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2020 Akretion France (http://www.akretion.com)
-# @author Alexis de Lattre
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-{
- 'name': 'Service Line Qty Update Purchase',
- 'version': '12.0.1.0.0',
- 'category': 'Tools',
- 'license': 'AGPL-3',
- 'summary': 'Update delivery qty on service lines - Purchase module',
- 'author': 'Akretion',
- 'website': 'http://www.akretion.com',
- 'depends': [
- 'purchase',
- 'service_line_qty_update_base',
- 'purchase_reception_status',
- ],
- 'data': [
- 'views/purchase_order.xml',
- ],
- 'installable': False,
-}
diff --git a/service_line_qty_update_purchase/models/__init__.py b/service_line_qty_update_purchase/models/__init__.py
deleted file mode 100644
index 9f03530..0000000
--- a/service_line_qty_update_purchase/models/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import purchase_order
diff --git a/service_line_qty_update_purchase/models/purchase_order.py b/service_line_qty_update_purchase/models/purchase_order.py
deleted file mode 100644
index 9cb17cd..0000000
--- a/service_line_qty_update_purchase/models/purchase_order.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2020 Akretion France (http://www.akretion.com)
-# @author Alexis de Lattre
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-from odoo import api, fields, models
-
-
-class PurchaseOrder(models.Model):
- _inherit = 'purchase.order'
-
- has_service = fields.Boolean(compute='_compute_has_service')
-
- @api.depends('order_line.product_id.type')
- def _compute_has_service(self):
- for order in self:
- has_service = False
- for l in order.order_line:
- if l.product_id.type == 'service':
- has_service = True
- break
- order.has_service = has_service
diff --git a/service_line_qty_update_purchase/views/purchase_order.xml b/service_line_qty_update_purchase/views/purchase_order.xml
deleted file mode 100644
index d0e0dc1..0000000
--- a/service_line_qty_update_purchase/views/purchase_order.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
- purchase.order.form
- purchase.order
-
-
-
-
- 1
-
-
-
-
-
-
diff --git a/service_line_qty_update_purchase/wizard/__init__.py b/service_line_qty_update_purchase/wizard/__init__.py
deleted file mode 100644
index c0ac3f4..0000000
--- a/service_line_qty_update_purchase/wizard/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import service_qty_update
diff --git a/service_line_qty_update_purchase/wizard/service_qty_update.py b/service_line_qty_update_purchase/wizard/service_qty_update.py
deleted file mode 100644
index de31e0c..0000000
--- a/service_line_qty_update_purchase/wizard/service_qty_update.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright 2020 Akretion France (http://www.akretion.com/)
-# @author: Alexis de Lattre
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-
-from odoo import _, api, fields, models
-from odoo.tools import float_compare
-from odoo.exceptions import UserError
-
-
-class ServiceQtyUpdate(models.TransientModel):
- _inherit = 'service.qty.update'
-
- @api.model
- def default_get(self, fields_list):
- res = super().default_get(fields_list)
- prec = self.env['decimal.precision'].precision_get('Product Unit of Measure')
- if self._context.get('active_model') == 'purchase.order' and self._context.get('active_id'):
- lines = []
- order = self.env['purchase.order'].browse(self._context['active_id'])
- for l in order.order_line.filtered(lambda x: x.product_id.type == 'service'):
- if float_compare(l.product_qty, l.qty_received, precision_digits=prec) > 0:
- lines.append((0, 0, {
- 'purchase_line_id': l.id,
- 'product_id': l.product_id.id,
- 'name': l.name,
- 'name_readonly': l.name,
- 'order_qty': l.product_qty,
- 'order_qty_readonly': l.product_qty,
- 'pre_delivered_qty': l.qty_received,
- 'pre_delivered_qty_readonly': l.qty_received,
- 'uom_id': l.product_uom.id,
- }))
- if lines:
- res['line_ids'] = lines
- else:
- raise UserError(_(
- "All service lines are fully received."))
- return res
-
-
-class ServiceQtyUpdateLine(models.TransientModel):
- _inherit = 'service.qty.update.line'
-
- purchase_line_id = fields.Many2one('purchase.order.line', string='Purchase Line', readonly=True)
-
- def process_line(self):
- po_line = self.purchase_line_id
- if po_line:
- new_qty = po_line.qty_received + self.added_delivered_qty
- po_line.write({'qty_received': new_qty})
- body = """
-
Received qty updated on service line %s:
-
-
Added received qty: %s
-
Total received qty: %s
-
- """ % (self.name, self.added_delivered_qty, new_qty)
- if self.comment:
- body += '