| @ -0,0 +1,48 @@ | |||||
|  | .. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg | ||||
|  |     :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html | ||||
|  |     :alt: License: AGPL-3 | ||||
|  | 
 | ||||
|  | Sale Order Multiple Pricelist | ||||
|  | ============================= | ||||
|  | This module helps to apply multiple price lists to the same sale order. | ||||
|  | 
 | ||||
|  | Configuration | ||||
|  | ============= | ||||
|  | * Enable "Pricelists" and "Multiple Pricelists" from the Sale Order Configuration Settings. | ||||
|  | 
 | ||||
|  | Company | ||||
|  | ======= | ||||
|  | * `Cybrosys Techno Solutions <https://cybrosys.com/>`__ | ||||
|  | 
 | ||||
|  | License | ||||
|  | ======= | ||||
|  | Affero General Public License, v3.0 (AGPL v3). | ||||
|  | (https://www.gnu.org/licenses/agpl-3.0-standalone.html) | ||||
|  | 
 | ||||
|  | Credits | ||||
|  | ------- | ||||
|  | * Developers: (V17) Bhagyadev KP, | ||||
|  |               (V18) Aysha Shalin | ||||
|  |   Contact: odoo@cybrosys.com | ||||
|  | 
 | ||||
|  | Contacts | ||||
|  | -------- | ||||
|  | * Mail Contact : odoo@cybrosys.com | ||||
|  | * Website : https://cybrosys.com | ||||
|  | 
 | ||||
|  | Bug Tracker | ||||
|  | ----------- | ||||
|  | Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. | ||||
|  | 
 | ||||
|  | Maintainer | ||||
|  | ========== | ||||
|  | .. image:: https://cybrosys.com/images/logo.png | ||||
|  |    :target: https://cybrosys.com | ||||
|  | 
 | ||||
|  | This module is maintained by Cybrosys Technologies. | ||||
|  | 
 | ||||
|  | For support and more information, please visit `Our Website <https://cybrosys.com/>`__ | ||||
|  | 
 | ||||
|  | Further information | ||||
|  | =================== | ||||
|  | HTML Description: `<static/description/index.html>`__ | ||||
| @ -0,0 +1,23 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from . import models | ||||
|  | from . import wizard | ||||
| @ -0,0 +1,45 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | { | ||||
|  |     'name': "Sale Order Multiple Pricelist", | ||||
|  |     'version': '18.0.1.0.0', | ||||
|  |     'category': 'Sales', | ||||
|  |     'summary': 'Multiple price lists can be applied  to the same Sale Order.', | ||||
|  |     'description': """Users can apply different price lists for ech order lines  | ||||
|  |     of the same Sale Order.""", | ||||
|  |     'author': 'Cybrosys Techno Solutions', | ||||
|  |     'company': 'Cybrosys Techno Solutions', | ||||
|  |     'maintainer': 'Cybrosys Techno Solutions', | ||||
|  |     'website': 'https://www.cybrosys.com', | ||||
|  |     'depends': ['sale_management'], | ||||
|  |     'data': [ | ||||
|  |         'security/ir.model.access.csv', | ||||
|  |         'views/sale_order_views.xml', | ||||
|  |         'views/res_config_settings_views.xml', | ||||
|  |         'wizard/pricelist_product_views.xml' | ||||
|  |     ], | ||||
|  |     'images': ['static/description/banner.jpg'], | ||||
|  |     'license': 'AGPL-3', | ||||
|  |     'installable': True, | ||||
|  |     'auto_install': False, | ||||
|  |     'application': False, | ||||
|  | } | ||||
| @ -0,0 +1,6 @@ | |||||
|  | ## Module <multi_pricelist> | ||||
|  | 
 | ||||
|  | #### 03.01.2025 | ||||
|  | #### Version 18.0.1.0.0 | ||||
|  | ##### ADD | ||||
|  | - Initial Commit for Sale Order Multiple Pricelist | ||||
| @ -0,0 +1,23 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from . import res_config_settings | ||||
|  | from . import sale_order_line | ||||
| @ -0,0 +1,51 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from odoo import api, fields, models | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class ResConfigSettings(models.TransientModel): | ||||
|  |     """ This class inherits res.config.settings model to add the settings | ||||
|  |     for multiple price lists """ | ||||
|  |     _inherit = 'res.config.settings' | ||||
|  | 
 | ||||
|  |     multi_pricelist = fields.Boolean(string="Multiple Pricelists", | ||||
|  |                                      help="Multiple price lists" | ||||
|  |                                           "for the same sale order") | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def get_values(self): | ||||
|  |         """ Supering the get_values method to get multi_price list field """ | ||||
|  |         res = super(ResConfigSettings, self).get_values() | ||||
|  |         multi_pricelist = self.env[ | ||||
|  |             'ir.config_parameter'].sudo().get_param( | ||||
|  |             'multi_pricelist.multi_pricelist') | ||||
|  |         res.update( | ||||
|  |             multi_pricelist=multi_pricelist, | ||||
|  |         ) | ||||
|  |         return res | ||||
|  | 
 | ||||
|  |     def set_values(self): | ||||
|  |         """ Supering the set_values method to set multi_price list field """ | ||||
|  |         super(ResConfigSettings, self).set_values() | ||||
|  |         param = self.env['ir.config_parameter'].sudo() | ||||
|  |         param.set_param('multi_pricelist.multi_pricelist', | ||||
|  |                         self.multi_pricelist) | ||||
| @ -0,0 +1,186 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from datetime import datetime | ||||
|  | from odoo import fields, models, _ | ||||
|  | from odoo.exceptions import UserError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class SaleOrderLine(models.Model): | ||||
|  |     """ Inherits sale.order.line to add the functions for checking the | ||||
|  |     visibility of the pricelists in order lines and applying the pricelists | ||||
|  |     to order lines. """ | ||||
|  |     _inherit = 'sale.order.line' | ||||
|  | 
 | ||||
|  |     pricelist_visibility = fields.Boolean( | ||||
|  |         compute="_compute_pricelist_visibility", string="Pricelist Visible", | ||||
|  |         help="Multi Pricelist enabled or not") | ||||
|  |     applied_pricelist_id = fields.Many2one( | ||||
|  |         'product.pricelist', | ||||
|  |         string="PriceList", | ||||
|  |         help="Price lists that is applied to the order line.") | ||||
|  | 
 | ||||
|  |     def _get_pricelist_price(self): | ||||
|  |         """ Overriding the _get_pricelist_price method to apply multiple | ||||
|  |         pricelists """ | ||||
|  |         self.ensure_one() | ||||
|  |         self.product_id.ensure_one() | ||||
|  |         if self.applied_pricelist_id: | ||||
|  |             for line in self: | ||||
|  |                 line.pricelist_item_id = line.applied_pricelist_id._get_product_rule( | ||||
|  |                     line.product_id, | ||||
|  |                     quantity=line.product_uom_qty or 1.0, | ||||
|  |                     uom=line.product_uom, | ||||
|  |                     date=line.order_id.date_order, | ||||
|  |                 ) | ||||
|  |             price = self.pricelist_item_id._compute_price( | ||||
|  |                 product=self.product_id.with_context( | ||||
|  |                     **self._get_product_price_context()), | ||||
|  |                 quantity=self.product_uom_qty or 1.0, | ||||
|  |                 uom=self.product_uom, | ||||
|  |                 date=self.order_id.date_order, | ||||
|  |                 currency=self.currency_id, | ||||
|  |             ) | ||||
|  |             return price | ||||
|  |         else: | ||||
|  |             price = self.pricelist_item_id._compute_price( | ||||
|  |                 product=self.product_id.with_context( | ||||
|  |                     **self._get_product_price_context()), | ||||
|  |                 quantity=self.product_uom_qty or 1.0, | ||||
|  |                 uom=self.product_uom, | ||||
|  |                 date=self.order_id.date_order, | ||||
|  |                 currency=self.currency_id, | ||||
|  |             ) | ||||
|  |             return price | ||||
|  | 
 | ||||
|  |     def apply_pricelist(self): | ||||
|  |         """ This function will help to select all the pricelists for a product | ||||
|  |         in order line and apply it""" | ||||
|  |         for rec in self: | ||||
|  |             date_time_today = datetime.today().strftime("%Y-%m-%d %H:%M:%S") | ||||
|  |             # find the matching price list for a product | ||||
|  |             price_ids = self.env['product.pricelist.item'].search( | ||||
|  |                 ['|', '|', ('product_tmpl_id', '=', False), | ||||
|  |                  ('categ_id', '=', rec.product_id.categ_id.id), | ||||
|  |                  ('product_tmpl_id', '=', rec.product_id.product_tmpl_id.id), | ||||
|  |                  ('min_quantity', '<=', rec.product_uom_qty),'|', | ||||
|  |                  ('date_start', '<=', date_time_today), | ||||
|  |                  ('date_start', '=', False), '|', | ||||
|  |                  ('date_end', '>=', date_time_today), | ||||
|  |                  ('date_end', '=', False), | ||||
|  |                  ]) | ||||
|  |             variant_ids = self.env['product.pricelist.item'].search( | ||||
|  |                 [ | ||||
|  |                     ('product_id', '=', rec.product_id.id), | ||||
|  |                     ('min_quantity', '<=', rec.product_uom_qty), | ||||
|  |                     '|', ('date_start', '<=', date_time_today), | ||||
|  |                     ('date_start', '=', False), '|', | ||||
|  |                     ('date_end', '>=', date_time_today), | ||||
|  |                     ('date_end', '=', False), | ||||
|  |                 ]) | ||||
|  |             combined_ids = price_ids + variant_ids | ||||
|  |             if combined_ids: | ||||
|  |                 pricelist_wizard = self.env['pricelist.product'].create({ | ||||
|  |                     'order_line_id': rec.id, | ||||
|  |                     'line_ids': [(0, 0, { | ||||
|  |                         'pricelist_id': price.pricelist_id.id, | ||||
|  |                         'product_id': rec.product_id.id, | ||||
|  |                         'unit_price': self.unit_price(price), | ||||
|  |                         'unit_cost': rec.product_id.standard_price, | ||||
|  |                         'uom_id': rec.product_id.uom_id.id | ||||
|  |                     }) for price in combined_ids], | ||||
|  |                 }) | ||||
|  |             else: | ||||
|  |                 raise UserError(_( | ||||
|  |                     "No price list is configured for this product!")) | ||||
|  |         return { | ||||
|  |             'type': 'ir.actions.act_window', | ||||
|  |             'target': 'new', | ||||
|  |             'name': 'Select Pricelist', | ||||
|  |             'view_mode': 'form', | ||||
|  |             'view_id': self.env.ref( | ||||
|  |                 "multi_pricelist.pricelist_wizard_view_form", False).id, | ||||
|  |             'res_model': 'pricelist.product', | ||||
|  |             'res_id': pricelist_wizard.id, | ||||
|  |         } | ||||
|  | 
 | ||||
|  |     def _compute_pricelist_visibility(self): | ||||
|  |         """ Computes pricelist_visibility by checking the config parameter """ | ||||
|  |         for rec in self: | ||||
|  |             rec.pricelist_visibility = self.env[ | ||||
|  |                 'ir.config_parameter'].sudo().get_param( | ||||
|  |                 'multi_pricelist.multi_pricelist') | ||||
|  |             if rec.order_id.state in ['sale', 'done', 'cancel']: | ||||
|  |                 rec.pricelist_visibility = False | ||||
|  | 
 | ||||
|  |     def unit_price(self, price): | ||||
|  |         """ Compute the unit price of the product according to the | ||||
|  |         price_list_item. """ | ||||
|  |         if price.compute_price == 'fixed': | ||||
|  |             unt_price = price.fixed_price | ||||
|  |         elif price.compute_price == 'percentage' and price.percent_price != 0: | ||||
|  |             unt_price = self.product_id.list_price * ( | ||||
|  |                     1 - price.percent_price / 100) | ||||
|  |         elif price.compute_price == 'formula' and price.base == 'list_price': | ||||
|  |             unt_price = (self.product_id.list_price * ( | ||||
|  |                     1 - price.price_discount / 100) + price.price_surcharge) | ||||
|  |             if price.price_min_margin or price.price_max_margin: | ||||
|  |                 if (unt_price < price.price_min_margin + | ||||
|  |                         self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_min_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 elif (unt_price > price.price_max_margin + | ||||
|  |                       self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_max_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 else: | ||||
|  |                     unt_price = unt_price | ||||
|  |         elif price.compute_price == 'formula' and price.base == 'standard_price': | ||||
|  |             unt_price = (self.product_id.standard_price * ( | ||||
|  |                     1 - price.price_discount / 100) + price.price_surcharge) | ||||
|  |             if price.price_min_margin or price.price_max_margin: | ||||
|  |                 if (unt_price < price.price_min_margin + | ||||
|  |                         self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_min_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 elif (unt_price > price.price_max_margin + | ||||
|  |                       self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_max_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 else: | ||||
|  |                     unt_price = unt_price | ||||
|  |         elif price.compute_price == 'formula' and price.base == 'pricelist': | ||||
|  |             unt_price = (self.unit_price(price.base_pricelist_id.item_ids) * ( | ||||
|  |                     1 - price.price_discount / 100) + price.price_surcharge) | ||||
|  |             if price.price_min_margin or price.price_max_margin: | ||||
|  |                 if (unt_price < price.price_min_margin + | ||||
|  |                         self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_min_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 elif (unt_price > price.price_max_margin + | ||||
|  |                       self.product_id.list_price): | ||||
|  |                     unt_price = (price.price_max_margin + | ||||
|  |                                  self.product_id.list_price) | ||||
|  |                 else: | ||||
|  |                     unt_price = unt_price | ||||
|  |         else: | ||||
|  |             unt_price = self.product_id.list_price | ||||
|  |         return unt_price | ||||
| 
 | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 210 KiB | 
| After Width: | Height: | Size: 209 KiB | 
| After Width: | Height: | Size: 109 KiB | 
| After Width: | Height: | Size: 495 B | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 624 B | 
| After Width: | Height: | Size: 136 KiB | 
| After Width: | Height: | Size: 214 KiB | 
| After Width: | Height: | Size: 36 KiB | 
| After Width: | Height: | Size: 3.6 KiB | 
| After Width: | Height: | Size: 310 B | 
| After Width: | Height: | Size: 929 B | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 3.3 KiB | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 17 KiB | 
| After Width: | Height: | Size: 542 B | 
| After Width: | Height: | Size: 576 B | 
| After Width: | Height: | Size: 733 B | 
| After Width: | Height: | Size: 4.3 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 4.0 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 738 KiB | 
| After Width: | Height: | Size: 2.2 KiB | 
| After Width: | Height: | Size: 911 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 600 B | 
| After Width: | Height: | Size: 673 B | 
| After Width: | Height: | Size: 2.0 KiB | 
| After Width: | Height: | Size: 462 B | 
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 926 B | 
| After Width: | Height: | Size: 9.0 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 7.0 KiB | 
| After Width: | Height: | Size: 878 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 653 B | 
| After Width: | Height: | Size: 800 B | 
| After Width: | Height: | Size: 905 B | 
| After Width: | Height: | Size: 189 KiB | 
| After Width: | Height: | Size: 4.3 KiB | 
| After Width: | Height: | Size: 839 B | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 5.9 KiB | 
| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 34 KiB | 
| After Width: | Height: | Size: 26 KiB | 
| After Width: | Height: | Size: 3.8 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 1.9 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 427 B | 
| After Width: | Height: | Size: 627 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 988 B | 
| After Width: | Height: | Size: 3.7 KiB | 
| After Width: | Height: | Size: 5.0 KiB | 
| After Width: | Height: | Size: 875 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 92 KiB | 
| After Width: | Height: | Size: 131 KiB | 
| After Width: | Height: | Size: 88 KiB | 
| After Width: | Height: | Size: 87 KiB | 
| After Width: | Height: | Size: 92 KiB | 
| After Width: | Height: | Size: 87 KiB | 
| After Width: | Height: | Size: 163 KiB | 
| After Width: | Height: | Size: 86 KiB | 
| After Width: | Height: | Size: 184 KiB | 
| After Width: | Height: | Size: 182 KiB | 
| After Width: | Height: | Size: 185 KiB | 
| After Width: | Height: | Size: 208 KiB | 
| After Width: | Height: | Size: 718 KiB | 
| After Width: | Height: | Size: 38 KiB | 
| @ -0,0 +1,18 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  |     <!-- Inherits Configuration settings form view to add the settings to enable multi pricelists --> | ||||
|  |     <record id="res_config_settings_view_form" | ||||
|  |             model="ir.ui.view"> | ||||
|  |         <field name="name">res.config.settings.view.form.inherit.multi.pricelist</field> | ||||
|  |         <field name="model">res.config.settings</field> | ||||
|  |         <field name="inherit_id" ref="sale.res_config_settings_view_form"/> | ||||
|  |         <field name="arch" type="xml"> | ||||
|  |             <xpath expr="//setting[@id='pricelist_configuration']" | ||||
|  |                    position="after"> | ||||
|  |                 <setting id="line_pricelist_settings" invisible="not group_product_pricelist" title="Multiple pricelist for same sale order" help="Multiple pricelist for same sale order"> | ||||
|  |                     <field name="multi_pricelist"/> | ||||
|  |                 </setting> | ||||
|  |             </xpath> | ||||
|  |         </field> | ||||
|  |     </record> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,21 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  |     <!-- Inherits sale order form view to add the pricelist button to the order lines --> | ||||
|  |     <record id="view_order_form" model="ir.ui.view"> | ||||
|  |         <field name="name">sale.order.view.form.inherit.multi.pricelist</field> | ||||
|  |         <field name="model">sale.order</field> | ||||
|  |         <field name="inherit_id" ref="sale.view_order_form"/> | ||||
|  |         <field name="arch" type="xml"> | ||||
|  |             <xpath expr="/form/sheet/notebook/page/field[@name='order_line']/list/field[@name='price_subtotal']" | ||||
|  |                    position="after"> | ||||
|  |                 <button name="apply_pricelist" type="object" | ||||
|  |                         string="Apply Pricelist" | ||||
|  |                         invisible="not pricelist_visibility"> | ||||
|  |                     <i class="fa fa-fw o_button_icon fa-save"/> | ||||
|  |                 </button> | ||||
|  |                 <field name="applied_pricelist_id" readonly="1"/> | ||||
|  |                 <field name="pricelist_visibility" column_invisible="1"/> | ||||
|  |             </xpath> | ||||
|  |         </field> | ||||
|  |     </record> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,23 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from . import pricelist_line | ||||
|  | from . import pricelist_product | ||||
| @ -0,0 +1,68 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from odoo import api, fields, models | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class PricelistLine(models.TransientModel): | ||||
|  |     """ Model for applying the price lists for sale orderline items """ | ||||
|  |     _name = 'pricelist.line' | ||||
|  |     _rec_name = 'wizard_id' | ||||
|  |     _description = 'Pricelist Lines' | ||||
|  | 
 | ||||
|  |     wizard_id = fields.Many2one('pricelist.product', | ||||
|  |                                 string="Price List Selection", | ||||
|  |                                 help="Price List Selection") | ||||
|  |     pricelist_id = fields.Many2one('product.pricelist', | ||||
|  |                                    string="PriceList", | ||||
|  |                                    help="Price lists that can be applied for " | ||||
|  |                                         "the order line.") | ||||
|  |     product_id = fields.Many2one('product.product', | ||||
|  |                                  string="Product", | ||||
|  |                                  help="Order line Product") | ||||
|  |     unit_price = fields.Float(string="Unit Price", | ||||
|  |                               help="Price of the product per unit") | ||||
|  |     unit_cost = fields.Float(string='Unit Cost', | ||||
|  |                              help="Cost of the product per unit") | ||||
|  |     margin = fields.Float(compute='_compute_margin', string="Margin %", | ||||
|  |                           help="calculated by ((unit price - unit cost) " | ||||
|  |                                "unit cost)* 100") | ||||
|  |     uom_id = fields.Many2one('uom.uom', string="UOM", help="Unit Of Measure") | ||||
|  | 
 | ||||
|  |     @api.depends('unit_price', 'unit_cost') | ||||
|  |     def _compute_margin(self): | ||||
|  |         """ This function will compute the margin for the product when the | ||||
|  |         price list applied. | ||||
|  |         It is calculated by ((unit price - unit cost)/ unit cost)* 100 """ | ||||
|  |         for rec in self: | ||||
|  |             rec.margin = 100 | ||||
|  |             if rec.unit_cost: | ||||
|  |                 rec.margin = ((rec.unit_price-rec.unit_cost)/rec.unit_cost)*100 | ||||
|  | 
 | ||||
|  |     def apply_pricelist(self): | ||||
|  |         """This function will apply the selected pricelist to the order line""" | ||||
|  |         for rec in self: | ||||
|  |             rec.wizard_id.order_line_id.update({ | ||||
|  |                 'applied_pricelist_id': rec.pricelist_id, | ||||
|  |             }) | ||||
|  |             rec.wizard_id.order_line_id.update({ | ||||
|  |                 'price_unit': rec.unit_price, | ||||
|  |             }) | ||||
| @ -0,0 +1,35 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    Author: Aysha Shalin (odoo@cybrosys.com) | ||||
|  | # | ||||
|  | #    You can modify it under the terms of the GNU AFFERO | ||||
|  | #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | ||||
|  | # | ||||
|  | #    This program is distributed in the hope that it will be useful, | ||||
|  | #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  | #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  | #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | ||||
|  | # | ||||
|  | #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC | ||||
|  | #    LICENSE (AGPL v3) along with this program. | ||||
|  | #    If not, see <https://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from odoo import fields, models | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class PricelistProduct(models.TransientModel): | ||||
|  |     """ New transient model for the price list wizard """ | ||||
|  |     _name = 'pricelist.product' | ||||
|  |     _rec_name = 'order_line_id' | ||||
|  |     _description = 'Price list product' | ||||
|  | 
 | ||||
|  |     order_line_id = fields.Many2one('sale.order.line', | ||||
|  |                                     string="Order Line", | ||||
|  |                                     help="Order line of the selected order") | ||||
|  |     line_ids = fields.One2many('pricelist.line', 'wizard_id', | ||||
|  |                                string="Price lists", help="Pricelist lines") | ||||
| @ -0,0 +1,38 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  |     <!-- Pricelist wizard form view --> | ||||
|  |     <record id="pricelist_wizard_view_form" model="ir.ui.view"> | ||||
|  |         <field name="name">pricelist.product.view.form</field> | ||||
|  |         <field name="model">pricelist.product</field> | ||||
|  |         <field name="arch" type="xml"> | ||||
|  |             <form> | ||||
|  |                 <sheet> | ||||
|  |                     <h3> | ||||
|  |                         Select one Pricelist | ||||
|  |                     </h3> | ||||
|  |                     <notebook> | ||||
|  |                         <page string="Pricelists"> | ||||
|  |                             <field name="line_ids"> | ||||
|  |                                 <list> | ||||
|  |                                     <field name="pricelist_id"/> | ||||
|  |                                     <field name="product_id"/> | ||||
|  |                                     <field name="unit_price"/> | ||||
|  |                                     <field name="margin"/> | ||||
|  |                                     <field name="uom_id"/> | ||||
|  |                                     <button name="apply_pricelist" | ||||
|  |                                             string="Apply" type="object" | ||||
|  |                                             class="btn-primary" | ||||
|  |                                             data-hotkey="q"/> | ||||
|  |                                 </list> | ||||
|  |                             </field> | ||||
|  |                         </page> | ||||
|  |                     </notebook> | ||||
|  |                     <footer> | ||||
|  |                         <button string="Cancel" special="cancel" | ||||
|  |                                 data-hotkey="z"/> | ||||
|  |                     </footer> | ||||
|  |                 </sheet> | ||||
|  |             </form> | ||||
|  |         </field> | ||||
|  |     </record> | ||||
|  | </odoo> | ||||