diff --git a/product_pricelist_supplierinfo_chatter/__init__.py b/product_pricelist_supplierinfo_chatter/__init__.py new file mode 100644 index 0000000..cde864b --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/product_pricelist_supplierinfo_chatter/__manifest__.py b/product_pricelist_supplierinfo_chatter/__manifest__.py new file mode 100755 index 0000000..e1f95d3 --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/__manifest__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +{ + "name": "Product Pricelist Supplierinfo Chatter", + "version": "18.0.3.0.0", + "license": "AGPL-3", + "author": "Bemade", + "category": "Tools", + "depends": [ + "base", + "product", + "stock", + "product_pricelist_supplierinfo", + "sale", + "purchase", + "mrp", + ], + "description": """ + This module extends basic inventory and pricelist management in + Odoo to include support for supplierinfo chatter making price + updates visible in the chatter. + """, + "demo": [], + 'data': [ + 'views/product_view.xml', + 'views/supplierinfo_pricelist.xml', + ], + 'test': [], + 'installable': True, + 'active': False +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/product_pricelist_supplierinfo_chatter/models/__init__.py b/product_pricelist_supplierinfo_chatter/models/__init__.py new file mode 100644 index 0000000..937da63 --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/models/__init__.py @@ -0,0 +1 @@ +from . import product_supplier_info \ No newline at end of file diff --git a/product_pricelist_supplierinfo_chatter/models/product_supplier_info.py b/product_pricelist_supplierinfo_chatter/models/product_supplier_info.py new file mode 100755 index 0000000..344ab25 --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/models/product_supplier_info.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api +from markupsafe import Markup + + +class ProductSupplierInfo(models.Model): + _name = "product.supplierinfo" + _inherit = ["product.supplierinfo", "mail.thread", "mail.activity.mixin"] + + # Commented out because breaking basic Odoo tests. See if this is really needed. + + # _sql_constraints = [("supplierinfo_product_tmpl_id_no_null", + # "CHECK((product_tmpl_id IS NOT NULL))", + # "Supplier pricelist need product template"), + # ] + + # This while avoid the database to save those record with lost product_tmpl_id + @api.depends("supplier_list_price", "supplier_discount_percent") + def _compute_price(self): + for rec in self: + rec.price = rec.supplier_list_price - ( + rec.supplier_list_price * rec.supplier_discount_percent / 100 + ) + + @api.depends("supplier_discount_percent") + def _inverse_price(self): + for rec in self: + discount = ( + rec.supplier_discount_percent if rec.supplier_discount_percent else 0 + ) + rec.supplier_list_price = (100 * rec.price) / (100 - discount) + + # We add tracking to the fields that are displayed in the chatter + partner_id = fields.Many2one( + tracking=True, + domain=[("is_company", "=", True)] + ) + + product_name = fields.Char(tracking=True) + product_code = fields.Char(tracking=True) + product_uom = fields.Many2one(tracking=True) + min_qty = fields.Float(tracking=True) + currency_id = fields.Many2one(tracking=True) + date_start = fields.Date(tracking=True) + date_end = fields.Date(tracking=True) + product_id = fields.Many2one(tracking=True) + product_tmpl_id = fields.Many2one(tracking=True) + delay = fields.Integer(tracking=True) + + date_updated = fields.Date( + string="Last updated", + help="Date at which the supplier " + " list price was last updated.", + ) + + purchasing_notes = fields.Text( + tracking=True, + string="Purchasing Notes" + ) + + supplier_list_price = fields.Float( + string="Supplier List Price", + digits="Product Price", + tracking=True, + help="""This the supplier list price to which supplier discounts are applied, if + any, and the net price if no supplier discounts are to be applied""", + ) + + supplier_discount_percent = fields.Float( + string="Supplier discount (%)", + digits="Product Price", + tracking=True, + default=0 + ) + + price = fields.Float( + compute="_compute_price", + inverse="_inverse_price", + string="Supplier Price", + digits="Product Price", + help="This price will be considered as a price for the supplier UoM if any or " + "the default Unit of Measure of the product otherwise", + store=True, + ) + + def _generate_chatter(self, vals, operation): + if len(self) == 1 and self.product_tmpl_id: + msg = "" + headmsg = ( + f"" + f"Price {operation} for {self.product_id.name} :
" + ) + if self.min_qty > 0: + msg += f"
  • Minimum qty : {self.min_qty}
  • " + if self.date_start: + msg += f"
  • Starting : {self.date_start}
  • " + if self.date_end: + msg += f"
  • Ending : {self.date_end}
  • " + if msg: + msg = headmsg + "" + else: + msg = headmsg + for change in vals: + msg += f"{change} --> {vals[change]}
    " + # Should always be there + if self.product_tmpl_id: + self.product_tmpl_id.message_post( + body=Markup(msg), + ) + # only for variant + if self.product_id: + self.product_id.message_post(body=Markup(msg)) + + # Well hard time to set the chatter + # def create(self, vals): + # res = super(ProductSupplierInfo, self).create(vals) + # # # res._generate_chatter(vals, 'create') + + def write(self, vals): + res = super(ProductSupplierInfo, self).write(vals) + self._generate_chatter(vals, "modify") + return res + + def suplierinfo_show_details(self): + """ + Action to open product.supplierinfo (pricelist) in its own windows and not in a javascript + popup to avoid loosing product_tmpl_id. We rely on both context and domain to pass the + information + """ + # views = [(self.env.ref('account.invoice_supplier_tree').id, 'tree'), + # (self.env.ref('account.invoice_supplier_form').id, 'form')] + return { + "name": "Supplier price", + "view_type": "form", + "view_mode": "form", + "res_id": self.id, + "context": self.env.context, + "res_model": "product.supplierinfo", + "domain": [ + ("product_tmpl_id", "=", self.product_tmpl_id), + ("product_id", "=", self.product_id), + ], + "type": "ir.actions.act_window", + # 'view_id': False, + # 'views': views, + } diff --git a/product_pricelist_supplierinfo_chatter/static/description/icon.png b/product_pricelist_supplierinfo_chatter/static/description/icon.png new file mode 100755 index 0000000..0130194 Binary files /dev/null and b/product_pricelist_supplierinfo_chatter/static/description/icon.png differ diff --git a/product_pricelist_supplierinfo_chatter/views/product_view.xml b/product_pricelist_supplierinfo_chatter/views/product_view.xml new file mode 100755 index 0000000..c3fea2e --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/views/product_view.xml @@ -0,0 +1,36 @@ + + + + + product.supplierinfo.tree.no.edit + product.supplierinfo + + + + + + + + + + + + product.template.form.view.landed + + product.template + + + + + {'default_product_tmpl_id': id, + 'product_template_invisible_variant': True, + 'list_view_ref':'product_pricelist_supplierinfo_chatter.product_supplierinfo_tree_view_no_edit'} + + + + {'model': 'product.template', 'active_id': id, + 'list_view_ref':'product_pricelist_supplierinfo_chatter.product_supplierinfo_tree_view_no_edit'} + + + + \ No newline at end of file diff --git a/product_pricelist_supplierinfo_chatter/views/supplierinfo_pricelist.xml b/product_pricelist_supplierinfo_chatter/views/supplierinfo_pricelist.xml new file mode 100644 index 0000000..9d72f74 --- /dev/null +++ b/product_pricelist_supplierinfo_chatter/views/supplierinfo_pricelist.xml @@ -0,0 +1,75 @@ + + + + + supplierinfo.pricelist.chatter + product.supplierinfo + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    +
    +
    +
    +
    \ No newline at end of file