diff --git a/add_product_warranty/__init__.py b/add_product_warranty/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/add_product_warranty/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/add_product_warranty/__manifest__.py b/add_product_warranty/__manifest__.py new file mode 100644 index 00000000000..7027e537bb5 --- /dev/null +++ b/add_product_warranty/__manifest__.py @@ -0,0 +1,19 @@ +{ + "name": "Add Warranty Product", + "description": """ + The Module helps to add warranty to our product + """, + "version": "1.0", + "depends": ["product", "sale_management"], + "data": [ + "security/ir.model.access.csv", + "views/product_warranty.xml", + "views/warranty_menus.xml", + "views/product_template.xml", + "views/sale_order_button.xml", + "wizard/add_warranty_wizard.xml", + ], + "auto-install": True, + "application": False, + "license": "LGPL-3", +} diff --git a/add_product_warranty/models/__init__.py b/add_product_warranty/models/__init__.py new file mode 100644 index 00000000000..26348eaacdf --- /dev/null +++ b/add_product_warranty/models/__init__.py @@ -0,0 +1,3 @@ +from . import product_warranty +from . import product_template +from . import sale_order_unlink diff --git a/add_product_warranty/models/product_template.py b/add_product_warranty/models/product_template.py new file mode 100644 index 00000000000..b47f510d0f1 --- /dev/null +++ b/add_product_warranty/models/product_template.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class productTemplate(models.Model): + _inherit = "product.template" + + is_warranty_available = fields.Boolean(string="Is Warranty Available") diff --git a/add_product_warranty/models/product_warranty.py b/add_product_warranty/models/product_warranty.py new file mode 100644 index 00000000000..5b5da5b3d77 --- /dev/null +++ b/add_product_warranty/models/product_warranty.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class productWarranty(models.Model): + _name = "product.warranty" + _description = "Product Warranty" + + name = fields.Char(string="Name", required=True) + percentage = fields.Float(string="Percentage", required=True) + validity_year = fields.Integer(string="Validity Year") diff --git a/add_product_warranty/models/sale_order_unlink.py b/add_product_warranty/models/sale_order_unlink.py new file mode 100644 index 00000000000..170550981c4 --- /dev/null +++ b/add_product_warranty/models/sale_order_unlink.py @@ -0,0 +1,19 @@ +from odoo import fields, models + + +class salesOrderLine(models.Model): + _inherit = "sale.order.line" + + linked_sale_order_line_id = fields.Many2one( + "sale.order.line", string="Linked Product Line" + ) + + def unlink(self): + for line in self: + linked_warranty_lines = self.search( + [("linked_sale_order_line_id", "=", line.id)] + ) + + linked_warranty_lines.unlink() + + return super().unlink() diff --git a/add_product_warranty/security/ir.model.access.csv b/add_product_warranty/security/ir.model.access.csv new file mode 100644 index 00000000000..4073da91b52 --- /dev/null +++ b/add_product_warranty/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +add_product_warranty.access_product_warranty,access_product_warranty,add_product_warranty.model_product_warranty,base.group_user,1,1,1,1 +add_product_warranty.access_warranty_wizard,access_warranty_wizard,add_product_warranty.model_warranty_wizard,base.group_user,1,1,1,1 +add_product_warranty.access_warranty_wizard_line,access_warranty_wizard_line,add_product_warranty.model_warranty_wizard_line,base.group_user,1,1,1,1 diff --git a/add_product_warranty/views/product_template.xml b/add_product_warranty/views/product_template.xml new file mode 100644 index 00000000000..554f5ffff76 --- /dev/null +++ b/add_product_warranty/views/product_template.xml @@ -0,0 +1,15 @@ + + + + product.template.inherit.form + product.template + + + + + + + + + + diff --git a/add_product_warranty/views/product_warranty.xml b/add_product_warranty/views/product_warranty.xml new file mode 100644 index 00000000000..51a372de0bd --- /dev/null +++ b/add_product_warranty/views/product_warranty.xml @@ -0,0 +1,20 @@ + + + + Warranty Configuration + product.warranty + list,form + + + + product.warranty.list + product.warranty + + + + + + + + + diff --git a/add_product_warranty/views/sale_order_button.xml b/add_product_warranty/views/sale_order_button.xml new file mode 100644 index 00000000000..d0b854643c8 --- /dev/null +++ b/add_product_warranty/views/sale_order_button.xml @@ -0,0 +1,14 @@ + + + + sale.order.button.wizard + sale.order + + + + + + + + diff --git a/add_product_warranty/views/warranty_menus.xml b/add_product_warranty/views/warranty_menus.xml new file mode 100644 index 00000000000..1d4dde6bd7e --- /dev/null +++ b/add_product_warranty/views/warranty_menus.xml @@ -0,0 +1,8 @@ + + + + diff --git a/add_product_warranty/wizard/__init__.py b/add_product_warranty/wizard/__init__.py new file mode 100644 index 00000000000..d02e940f513 --- /dev/null +++ b/add_product_warranty/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import add_warranty_wizard +from . import warranty_wizard_line diff --git a/add_product_warranty/wizard/add_warranty_wizard.py b/add_product_warranty/wizard/add_warranty_wizard.py new file mode 100644 index 00000000000..b717ba2dd34 --- /dev/null +++ b/add_product_warranty/wizard/add_warranty_wizard.py @@ -0,0 +1,66 @@ +from odoo import api, fields, models + + +class addWarrantyWizard(models.TransientModel): + _name = "warranty.wizard" + _description = "Wizard to add Warranty for product" + + sale_order_id = fields.Many2one("sale.order", string="Quotation", required=True) + line_ids = fields.One2many("warranty.wizard.line", "wizard_id", string=" ") + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + sale_order = self.env["sale.order"].browse(self.env.context.get("active_id")) + lines = [] + + for line in sale_order.order_line: + if line.product_id.is_warranty_available: + lines.append( + ( + 0, + 0, + { + "product_id": line.product_id.id, + "sale_order_line_id": line.id, + }, + ) + ) + res["sale_order_id"] = sale_order.id + res["line_ids"] = lines + return res + + def action_add_warranty_product(self): + total_price = 0 + description_lines = [] + linked_lines = None + warranty_product = None + + for line in self.line_ids: + if line.warranty_id: + base_price = line.sale_order_line_id.price_unit + percentage = line.warranty_id.percentage + extended_price = (base_price * percentage) / 100 + total_price += extended_price + + description_lines.append( + f"{line.product_id.name} - Valid till {line.warranty_end_date}" + ) + + if not warranty_product: + warranty_product = line.product_id + + linked_lines = line.sale_order_line_id.id + + if total_price > 0: + self.env["sale.order.line"].create( + { + "order_id": self.sale_order_id.id, + "product_id": warranty_product.id, + "name": "Extended Warranty:\n" + "\n".join(description_lines), + "price_unit": total_price, + "product_uom_qty": 1, + "product_uom": warranty_product.uom_id.id, + "linked_sale_order_line_id": linked_lines, + } + ) diff --git a/add_product_warranty/wizard/add_warranty_wizard.xml b/add_product_warranty/wizard/add_warranty_wizard.xml new file mode 100644 index 00000000000..8d9da4cb041 --- /dev/null +++ b/add_product_warranty/wizard/add_warranty_wizard.xml @@ -0,0 +1,30 @@ + + + + warranty.wizard.form + warranty.wizard + + + + + + + + + + + + + + + + + Add Warranty + warranty.wizard + form + new + + diff --git a/add_product_warranty/wizard/warranty_wizard_line.py b/add_product_warranty/wizard/warranty_wizard_line.py new file mode 100644 index 00000000000..9dde2d30018 --- /dev/null +++ b/add_product_warranty/wizard/warranty_wizard_line.py @@ -0,0 +1,29 @@ +from odoo import api, fields, models + + +class warrantyWizardLine(models.TransientModel): + _name = "warranty.wizard.line" + _description = "warranty Line to Select Warranty" + + wizard_id = fields.Many2one("warranty.wizard", string="Wizard") + product_id = fields.Many2one("product.product", string="Product") + sale_order_line_id = fields.Many2one("sale.order.line", string="Sale order Line") + warranty_id = fields.Many2one("product.warranty", string="Warranty") + + validity_year = fields.Integer( + string="Validity Year", related="warranty_id.validity_year", readonly=True + ) + + warranty_end_date = fields.Date( + string="End Date", compute="_compute_warranty_end_date" + ) + + @api.depends("validity_year") + def _compute_warranty_end_date(self): + for record in self: + if record.validity_year: + record.warranty_end_date = record.warranty_end_date or fields.Date.add( + fields.Date.today(), days=365 * record.validity_year + ) + else: + record.warranty_end_date = False diff --git a/bom_modular_extension/__init__.py b/bom_modular_extension/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/bom_modular_extension/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/bom_modular_extension/__manifest__.py b/bom_modular_extension/__manifest__.py new file mode 100644 index 00000000000..1ce07fdbadf --- /dev/null +++ b/bom_modular_extension/__manifest__.py @@ -0,0 +1,18 @@ +{ + "name": "BOM Modular Extension", + "description": "Add modular types and multiplies MO line quantity by SO values", + "version": "1.0", + "depends": ["sale_management", "mrp", "product"], + "category": "Manufacturing", + "data": [ + "security/ir.model.access.csv", + "views/product_template.xml", + "views/mrp_bom_line.xml", + "wizard/sale_order_line_wizard.xml", + "views/sale_order_button.xml", + "views/mrp_production.xml", + ], + "application": False, + "auto_install": False, + "license": "LGPL-3", +} diff --git a/bom_modular_extension/models/__init__.py b/bom_modular_extension/models/__init__.py new file mode 100644 index 00000000000..5924bbe27bd --- /dev/null +++ b/bom_modular_extension/models/__init__.py @@ -0,0 +1,7 @@ +from . import modular_type +from . import product_template +from . import mrp_bom_line +from . import sale_order_line +from . import sale_order_line_modular_type +from . import sale_order +from . import stock_move diff --git a/bom_modular_extension/models/modular_type.py b/bom_modular_extension/models/modular_type.py new file mode 100644 index 00000000000..a1547ee016a --- /dev/null +++ b/bom_modular_extension/models/modular_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class modularType(models.Model): + _name = "modular.type" + _description = "Help to give modular type to product" + + name = fields.Char(string="Name", required=True) diff --git a/bom_modular_extension/models/mrp_bom_line.py b/bom_modular_extension/models/mrp_bom_line.py new file mode 100644 index 00000000000..ec4c9ad7122 --- /dev/null +++ b/bom_modular_extension/models/mrp_bom_line.py @@ -0,0 +1,26 @@ +from odoo import fields, models, api + + +class MrpBomLine(models.Model): + _inherit = "mrp.bom.line" + + modular_type_id = fields.Many2one( + "modular.type", + string="Modular Type", + domain="[('id', 'in', filter_modular_type_ids)]", + help="Specify which modular type this BOM line belongs to", + ) + + filter_modular_type_ids = fields.Many2many( + "modular.type", + compute="_compute_available_modular_types", + string="Available Modular Types", + ) + + @api.depends("bom_id.product_tmpl_id") + def _compute_available_modular_types(self): + for line in self: + product_tmpl = line.bom_id.product_tmpl_id + line.filter_modular_type_ids = ( + product_tmpl.modular_type_ids if product_tmpl else False + ) diff --git a/bom_modular_extension/models/product_template.py b/bom_modular_extension/models/product_template.py new file mode 100644 index 00000000000..9c39f369c02 --- /dev/null +++ b/bom_modular_extension/models/product_template.py @@ -0,0 +1,9 @@ +from odoo import fields, models + + +class productTemplate(models.Model): + _inherit = "product.template" + + modular_type_ids = fields.Many2many( + "modular.type", help="Help to give modular type to product" + ) diff --git a/bom_modular_extension/models/sale_order.py b/bom_modular_extension/models/sale_order.py new file mode 100644 index 00000000000..3c103fceecf --- /dev/null +++ b/bom_modular_extension/models/sale_order.py @@ -0,0 +1,27 @@ +from odoo import models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def action_confirm(self): + res = super().action_confirm() + + for production in self.mrp_production_ids: + selected_order_line = self.order_line.filtered( + lambda line: line.product_id == production.product_id + and line.product_uom_qty == production.product_qty + ) + + for stock_line in production.move_raw_ids.filtered( + lambda move: move.modular_type_id + ): + modular_value = ( + selected_order_line.mapped("modular_type_value_ids") + .filtered(lambda m: m.modular_type_id == stock_line.modular_type_id) + .value + ) + + stock_line.product_uom_qty *= modular_value or 0.0 + + return res diff --git a/bom_modular_extension/models/sale_order_line.py b/bom_modular_extension/models/sale_order_line.py new file mode 100644 index 00000000000..7655d2105bb --- /dev/null +++ b/bom_modular_extension/models/sale_order_line.py @@ -0,0 +1,18 @@ +from odoo import api, fields, models + + +class saleOrderLine(models.Model): + _inherit = "sale.order.line" + + modular_type_value_ids = fields.One2many( + "sale.order.line.modular.type", "order_line_id", string="Modular Type Value" + ) + + is_modular_types = fields.Boolean( + string="Has modular type", compute="_compute_has_modular_types" + ) + + @api.depends("product_template_id") + def _compute_has_modular_types(self): + for record in self: + record.is_modular_types = bool(record.product_template_id.modular_type_ids) diff --git a/bom_modular_extension/models/sale_order_line_modular_type.py b/bom_modular_extension/models/sale_order_line_modular_type.py new file mode 100644 index 00000000000..533bb21c32d --- /dev/null +++ b/bom_modular_extension/models/sale_order_line_modular_type.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class saleOrderLineModularType(models.Model): + _name = "sale.order.line.modular.type" + _description = "Modular Type Value for Sale Order Line" + + order_line_id = fields.Many2one("sale.order.line", string="Order Line") + modular_type_id = fields.Many2one("modular.type", string="Modular Type") + value = fields.Float(string="Value", required=True) diff --git a/bom_modular_extension/models/stock_move.py b/bom_modular_extension/models/stock_move.py new file mode 100644 index 00000000000..36280227788 --- /dev/null +++ b/bom_modular_extension/models/stock_move.py @@ -0,0 +1,20 @@ +from odoo import api, fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + modular_type_id = fields.Many2one( + comodel_name="modular.type", + compute="_compute_modular_type_id", + store=True, + string="Modular Type", + ) + + @api.depends("raw_material_production_id.bom_id.bom_line_ids") + def _compute_modular_type_id(self): + for move in self: + bom_line = move.raw_material_production_id.bom_id.bom_line_ids.filtered( + lambda bl: bl.product_id == move.product_id + ) + move.modular_type_id = bom_line.modular_type_id if bom_line else False diff --git a/bom_modular_extension/security/ir.model.access.csv b/bom_modular_extension/security/ir.model.access.csv new file mode 100644 index 00000000000..091f94bcf7a --- /dev/null +++ b/bom_modular_extension/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +bom_modular_extension.access_modular_type,access_modular_type,bom_modular_extension.model_modular_type,base.group_user,1,1,1,1 +bom_modular_extension.access_sale_order_line_modular_type,access_sale_order_line_modular_type,bom_modular_extension.model_sale_order_line_modular_type,base.group_user,1,1,1,1 +bom_modular_extension.access_sale_order_line_wizard,access_sale_order_line_wizard,bom_modular_extension.model_sale_order_line_wizard,base.group_user,1,1,1,1 +bom_modular_extension.access_modular_type_value_wizard,access_modular_type_value_wizard,bom_modular_extension.model_modular_type_value_wizard,base.group_user,1,1,1,1 diff --git a/bom_modular_extension/views/mrp_bom_line.xml b/bom_modular_extension/views/mrp_bom_line.xml new file mode 100644 index 00000000000..d47ca39ba65 --- /dev/null +++ b/bom_modular_extension/views/mrp_bom_line.xml @@ -0,0 +1,14 @@ + + + + mrp.bom.form.inherit.modular.type + mrp.bom + + + + + + + + diff --git a/bom_modular_extension/views/mrp_production.xml b/bom_modular_extension/views/mrp_production.xml new file mode 100644 index 00000000000..1a519870d5c --- /dev/null +++ b/bom_modular_extension/views/mrp_production.xml @@ -0,0 +1,14 @@ + + + + pmrp.production.form.inherit + mrp.production + + + + + + + + diff --git a/bom_modular_extension/views/product_template.xml b/bom_modular_extension/views/product_template.xml new file mode 100644 index 00000000000..a436e6da0da --- /dev/null +++ b/bom_modular_extension/views/product_template.xml @@ -0,0 +1,15 @@ + + + + modular.type.product.form + product.template + + + + + + + + + + diff --git a/bom_modular_extension/views/sale_order_button.xml b/bom_modular_extension/views/sale_order_button.xml new file mode 100644 index 00000000000..bc1456db3b5 --- /dev/null +++ b/bom_modular_extension/views/sale_order_button.xml @@ -0,0 +1,14 @@ + + + + sale.order.line.button.add + sale.order + + + + + + + + diff --git a/bom_modular_extension/wizard/__init__.py b/bom_modular_extension/wizard/__init__.py new file mode 100644 index 00000000000..1c4c4acf5c4 --- /dev/null +++ b/bom_modular_extension/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import modular_type_value_wizard +from . import sale_order_line_wizard diff --git a/bom_modular_extension/wizard/modular_type_value_wizard.py b/bom_modular_extension/wizard/modular_type_value_wizard.py new file mode 100644 index 00000000000..06ea8afa1b9 --- /dev/null +++ b/bom_modular_extension/wizard/modular_type_value_wizard.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class modularTypeValueWizard(models.TransientModel): + _name = "modular.type.value.wizard" + _description = "Set Modular Type Value Wizard" + + wizard_id = fields.Many2one("sale.order.line.wizard", string="Wizard") + modular_type_id = fields.Many2one( + "modular.type", string="Modular Type", required=True + ) + value = fields.Float(string="Value", required=True) diff --git a/bom_modular_extension/wizard/sale_order_line_wizard.py b/bom_modular_extension/wizard/sale_order_line_wizard.py new file mode 100644 index 00000000000..da7110e981a --- /dev/null +++ b/bom_modular_extension/wizard/sale_order_line_wizard.py @@ -0,0 +1,43 @@ +from odoo import api, fields, models + + +class saleOrderLineWizard(models.TransientModel): + _name = "sale.order.line.wizard" + _description = "Set Modular Type Values Wizard" + + order_line_id = fields.Many2one( + "sale.order.line", string="Order Line", required=True + ) + modular_type_value_ids = fields.One2many( + "modular.type.value.wizard", "wizard_id", string=" ", required=True + ) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + + order_line = self.env["sale.order.line"].browse( + self.env.context.get("active_id") + ) + values = [] + + for modular_val in order_line.product_template_id.modular_type_ids: + values.append((0, 0, {"modular_type_id": modular_val.id, "value": 0})) + + res.update({"order_line_id": order_line.id, "modular_type_value_ids": values}) + + return res + + def apply_values(self): + self.order_line_id.modular_type_value_ids.unlink() + + for line in self.modular_type_value_ids: + self.env["sale.order.line.modular.type"].create( + { + "order_line_id": self.order_line_id.id, + "modular_type_id": line.modular_type_id.id, + "value": line.value, + } + ) + + return {"type": "ir.actions.act_window_close"} diff --git a/bom_modular_extension/wizard/sale_order_line_wizard.xml b/bom_modular_extension/wizard/sale_order_line_wizard.xml new file mode 100644 index 00000000000..d1fb6ddf771 --- /dev/null +++ b/bom_modular_extension/wizard/sale_order_line_wizard.xml @@ -0,0 +1,30 @@ + + + + sale.order.line.module.wizard + sale.order.line.wizard + + + + + + + + + + + + + + + + + Set Modular Type + sale.order.line.wizard + form + new + + diff --git a/new_product_type/__init__.py b/new_product_type/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/new_product_type/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/new_product_type/__manifest__.py b/new_product_type/__manifest__.py new file mode 100644 index 00000000000..d6373419096 --- /dev/null +++ b/new_product_type/__manifest__.py @@ -0,0 +1,19 @@ +{ + "name": "New Product Type", + "description": """ + " + """, + "version": "0.1", + "depends": ['sale_management'], + "data": [ + "security/ir.model.access.csv", + "views/product_template.xml", + "views/sale_order.xml", + "views/sale_order_report.xml", + "wizard/sale_order_wizard.xml" + ], + "assets": {}, + "license": "AGPL-3", + "installable": True, + "auto-install": True, +} diff --git a/new_product_type/models/__init__.py b/new_product_type/models/__init__.py new file mode 100644 index 00000000000..53fa79af356 --- /dev/null +++ b/new_product_type/models/__init__.py @@ -0,0 +1,3 @@ +from . import product_template +from . import sale_order +from . import sale_order_line diff --git a/new_product_type/models/product_template.py b/new_product_type/models/product_template.py new file mode 100644 index 00000000000..8e983c0a07b --- /dev/null +++ b/new_product_type/models/product_template.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_kit = fields.Boolean(string="Is Kit?") + sub_products = fields.Many2many("product.product", string="Sub Products") diff --git a/new_product_type/models/sale_order.py b/new_product_type/models/sale_order.py new file mode 100644 index 00000000000..99b4542f74e --- /dev/null +++ b/new_product_type/models/sale_order.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + print_in_report = fields.Boolean(string="Print in Report") diff --git a/new_product_type/models/sale_order_line.py b/new_product_type/models/sale_order_line.py new file mode 100644 index 00000000000..bc0ef20f535 --- /dev/null +++ b/new_product_type/models/sale_order_line.py @@ -0,0 +1,57 @@ +from odoo import api, fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + is_kit = fields.Boolean(related="product_template_id.is_kit") + is_sub_product_ol = fields.Boolean() + main_order_line_id = fields.Many2one( + "sale.order.line", + string="Parent Line", + ondelete="cascade", + ) + + child_line_ids = fields.One2many( + "sale.order.line", + "main_order_line_id", + string="Child Lines", + ) + display_price = fields.Float( + compute="_compute_display_price", inverse="_compute_unit_price" + ) + display_sub_total = fields.Float(compute="_compute_amount_price") + + @api.depends("price_unit", "is_sub_product_ol") + def _compute_display_price(self): + for line in self: + line.display_price = 0.0 if line.is_sub_product_ol else line.price_unit + + @api.depends("display_price", "price_subtotal", "is_sub_product_ol") + def _compute_amount_price(self): + for line in self: + line.display_sub_total = ( + 0.0 if line.is_sub_product_ol else line.price_subtotal + ) + + def _compute_unit_price(self): + for line in self: + line.price_unit = line.display_price + + def unlink(self): + for line in self: + line.main_order_line_id.price_subtotal -= ( + line.product_uom_qty * line.price_unit + ) + + return super().unlink() + + def open_sub_product_wizard(self): + return { + "name": "Sale order line wizard action", + "type": "ir.actions.act_window", + "res_model": "sale.order.wizard", + "view_mode": "form", + "target": "new", + "context": {"active_id": self.id}, + } diff --git a/new_product_type/security/ir.model.access.csv b/new_product_type/security/ir.model.access.csv new file mode 100644 index 00000000000..f8280f904b8 --- /dev/null +++ b/new_product_type/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +new_product_type.access_sale_order_wizard,access_sale_order_wizard,new_product_type.model_sale_order_wizard,base.group_user,1,1,1,1 +new_product_type.access_sale_order_line_wizard,access_sale_order_line_wizard,new_product_type.model_sale_order_line_wizard,base.group_user,1,1,1,1 diff --git a/new_product_type/views/product_template.xml b/new_product_type/views/product_template.xml new file mode 100644 index 00000000000..cfe82425c61 --- /dev/null +++ b/new_product_type/views/product_template.xml @@ -0,0 +1,16 @@ + + + + product.template.form.view + product.template + + + + + + + + + + + diff --git a/new_product_type/views/sale_order.xml b/new_product_type/views/sale_order.xml new file mode 100644 index 00000000000..21f6afd9862 --- /dev/null +++ b/new_product_type/views/sale_order.xml @@ -0,0 +1,47 @@ + + + + Sale.Order.View.Form.Inherit.kit + sale.order + + + + + + + + + + is_sub_product_ol + + + is_sub_product_ol + + + is_sub_product_ol + True + + + + + + is_sub_product_ol + + + + + + True + + + + diff --git a/new_product_type/views/sale_order_report.xml b/new_product_type/views/sale_order_report.xml new file mode 100644 index 00000000000..29143fce9b1 --- /dev/null +++ b/new_product_type/views/sale_order_report.xml @@ -0,0 +1,16 @@ + + + + + (not line.is_sub_product_ol or (doc.print_in_report and + line.is_sub_product_ol)) + + + + + + (not line.is_sub_product_ol or (doc.print_in_report and + line.is_sub_product_ol)) + + + diff --git a/new_product_type/wizard/__init__.py b/new_product_type/wizard/__init__.py new file mode 100644 index 00000000000..4d653011e27 --- /dev/null +++ b/new_product_type/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import sale_order_line_wizard +from . import sale_order_wizard diff --git a/new_product_type/wizard/sale_order_line_wizard.py b/new_product_type/wizard/sale_order_line_wizard.py new file mode 100644 index 00000000000..10b17911a02 --- /dev/null +++ b/new_product_type/wizard/sale_order_line_wizard.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class SaleOrderLines(models.TransientModel): + _name = "sale.order.line.wizard" + _description = "sale.order.lines.for.sub.products" + + wizard_id = fields.Many2one("sale.order.wizard") + product_id = fields.Many2one("product.product") + quantity = fields.Float() + wizard_price = fields.Float() + price = fields.Float() diff --git a/new_product_type/wizard/sale_order_wizard.py b/new_product_type/wizard/sale_order_wizard.py new file mode 100644 index 00000000000..2f0e452bdcf --- /dev/null +++ b/new_product_type/wizard/sale_order_wizard.py @@ -0,0 +1,94 @@ +from odoo import api, fields, models + + +class SubProducts(models.TransientModel): + _name = "sale.order.wizard" + _description = "change.quantity.of.subproducts.in.kit" + + main_product_id = fields.Many2one( + "sale.order.line", string="Main Order Line", readonly=True + ) + + line_ids = fields.One2many( + "sale.order.line.wizard", "wizard_id", string="Sub Products" + ) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + active_id = self.env.context.get("active_id") + main_order_line = self.env["sale.order.line"].browse(active_id) + sale_order = main_order_line.order_id + order_line = self.env["sale.order.line"].browse(active_id) + res["main_product_id"] = main_order_line.id + + if order_line: + lines = [] + for product in order_line.product_template_id.sub_products: + existing_line = self.env["sale.order.line"].search( + [ + ("order_id", "=", sale_order.id), + ("main_order_line_id", "=", main_order_line.id), + ("product_id", "=", product.id), + ], + limit=1, + ) + lines.append( + ( + 0, + 0, + { + "product_id": product.id, + "quantity": existing_line.product_uom_qty + if existing_line + else 1, + "price": existing_line.price_unit + if existing_line + else product.list_price, + }, + ) + ) + res["line_ids"] = lines + return res + + def confirm_sub_products(self): + active_id = self.env.context.get("active_id") + main_order_line = self.env["sale.order.line"].browse(active_id) + sale_order = main_order_line.order_id + main_product_subtotal = ( + main_order_line.price_unit * main_order_line.product_uom_qty + ) + for wizard in self: + for line in wizard.line_ids: + if line.quantity > 0: + main_product_subtotal += line.quantity * line.price + + existing_line = self.env["sale.order.line"].search( + [ + ("order_id", "=", sale_order.id), + ("main_order_line_id", "=", main_order_line.id), + ("product_id", "=", line.product_id.id), + ], + limit=1, + ) + + if existing_line: + existing_line.write( + { + "product_uom_qty": line.quantity, + "price_unit": line.price, + } + ) + else: + self.env["sale.order.line"].create( + { + "order_id": sale_order.id, + "product_id": line.product_id.id, + "product_uom_qty": line.quantity, + "name": line.product_id.name, + "is_sub_product_ol": True, + "price_unit": line.price, + "main_order_line_id": main_order_line.id, + } + ) + main_order_line.write({"price_subtotal": main_product_subtotal}) diff --git a/new_product_type/wizard/sale_order_wizard.xml b/new_product_type/wizard/sale_order_wizard.xml new file mode 100644 index 00000000000..e3885653f92 --- /dev/null +++ b/new_product_type/wizard/sale_order_wizard.xml @@ -0,0 +1,31 @@ + + + + Sale order line wizard view form + sale.order.wizard + + + + + + + + Sub Products + + + + + + + + + + + + + + diff --git a/supplier_portal/__init__.py b/supplier_portal/__init__.py new file mode 100644 index 00000000000..e046e49fbe2 --- /dev/null +++ b/supplier_portal/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/supplier_portal/__manifest__.py b/supplier_portal/__manifest__.py new file mode 100644 index 00000000000..c1d8173cdc0 --- /dev/null +++ b/supplier_portal/__manifest__.py @@ -0,0 +1,17 @@ +{ + "name": "Supplier Portal", + "description": """ + Help supplier to provide their Invoice + """, + "category": "Vendor", + "version": "1.0", + "depends": ["account", "website"], + "data": [ + "security/supplier_group.xml", + "views/supplier_template.xml", + "views/website_menu.xml", + ], + "auto-install": True, + "application": False, + "license": "LGPL-3", +} diff --git a/supplier_portal/controllers/__init__.py b/supplier_portal/controllers/__init__.py new file mode 100644 index 00000000000..12a7e529b67 --- /dev/null +++ b/supplier_portal/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/supplier_portal/controllers/main.py b/supplier_portal/controllers/main.py new file mode 100644 index 00000000000..1a8aa1ee158 --- /dev/null +++ b/supplier_portal/controllers/main.py @@ -0,0 +1,86 @@ +from odoo import http +from odoo.http import request +import base64 +from odoo.exceptions import UserError + + +class SupplierPortal(http.Controller): + @http.route(["/upload-invoice"], type="http", auth="user", website=True) + def supplier_upload_page(self, **kwargs): + user = request.env.user + if not user.has_group("supplier_portal.group_supplier_portal"): + raise UserError("Only Supplier Group Users allowed!") + + companies = ( + request.env["res.company"].sudo().search([("user_ids", "in", [user.id])]) + ) + + return request.render( + "supplier_portal.upload_invoice_template", + { + "companies": companies, + }, + ) + + @http.route( + ["/submit-invoice"], + type="http", + auth="user", + methods=["POST"], + csrf=True, + website=True, + ) + def submit_invoice(self, **post): + pdf_file = post.get("pdf_file") + xml_file = post.get("xml_file") + company_id = int(post.get("company_id")) + + pdf_content = pdf_file.read() if pdf_file else b"" + xml_content = xml_file.read() if xml_file else b"" + + journal = ( + request.env["account.journal"] + .sudo() + .search( + [("type", "=", "purchase"), ("company_id", "=", company_id)], limit=1 + ) + ) + + if not journal: + company = request.env["res.company"].sudo().browse(company_id) + raise UserError(f"No journal found for company: {company.name}") + + move_vals = { + "move_type": "in_invoice", + "partner_id": request.env.user.id, + "state": "draft", + "company_id": company_id, + "journal_id": journal.id, + } + + move = request.env["account.move"].sudo().create(move_vals) + + if pdf_content: + request.env["ir.attachment"].sudo().create( + { + "name": pdf_file.filename, + "type": "binary", + "datas": base64.b64encode(pdf_content), + "res_model": "account.move", + "res_id": move.id, + "mimetype": "application/pdf", + } + ) + if xml_content: + request.env["ir.attachment"].sudo().create( + { + "name": xml_file.filename, + "type": "binary", + "datas": base64.b64encode(xml_content), + "res_model": "account.move", + "res_id": move.id, + "mimetype": "text/xml", + } + ) + + return request.redirect("/upload-invoice?success=1") diff --git a/supplier_portal/security/supplier_group.xml b/supplier_portal/security/supplier_group.xml new file mode 100644 index 00000000000..67cf22201bd --- /dev/null +++ b/supplier_portal/security/supplier_group.xml @@ -0,0 +1,9 @@ + + + + + Supplier User + + + + diff --git a/supplier_portal/views/supplier_template.xml b/supplier_portal/views/supplier_template.xml new file mode 100644 index 00000000000..3a0ea2b8693 --- /dev/null +++ b/supplier_portal/views/supplier_template.xml @@ -0,0 +1,39 @@ + + + + + + Upload Invoice + + Invoice submitted successfully! + + + + + + Select Company + + + + + + + + + + + Upload PDF + + + + Upload XML + + + + Submit Invoice + + + + + diff --git a/supplier_portal/views/website_menu.xml b/supplier_portal/views/website_menu.xml new file mode 100644 index 00000000000..6c42738b29c --- /dev/null +++ b/supplier_portal/views/website_menu.xml @@ -0,0 +1,12 @@ + + + + + Upload Invoice + /upload-invoice + + 50 + + + + diff --git a/website_appointment_filter/__init__.py b/website_appointment_filter/__init__.py new file mode 100644 index 00000000000..e046e49fbe2 --- /dev/null +++ b/website_appointment_filter/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/website_appointment_filter/__manifest__.py b/website_appointment_filter/__manifest__.py new file mode 100644 index 00000000000..acc083dfeef --- /dev/null +++ b/website_appointment_filter/__manifest__.py @@ -0,0 +1,15 @@ +{ + "name": "Website Appointment Filter", + "summary": "Adds filters to website appointment", + "version": "1.0", + "depends": [ + "website_appointment", + "appointment_account_payment", + ], + "data": [ + "views/website_appointment_filter.xml", + "views/website_appointment_list.xml", + ], + "auto-install": True, + "license": "LGPL-3", +} diff --git a/website_appointment_filter/controllers/__init__.py b/website_appointment_filter/controllers/__init__.py new file mode 100644 index 00000000000..5bf3181cc81 --- /dev/null +++ b/website_appointment_filter/controllers/__init__.py @@ -0,0 +1 @@ +from . import website_appointment_filter diff --git a/website_appointment_filter/controllers/website_appointment_filter.py b/website_appointment_filter/controllers/website_appointment_filter.py new file mode 100644 index 00000000000..05d9a00d741 --- /dev/null +++ b/website_appointment_filter/controllers/website_appointment_filter.py @@ -0,0 +1,65 @@ +from odoo import http +from odoo.addons.website_appointment.controllers.appointment import WebsiteAppointment # type: ignore +from odoo.http import request + + +class WebsiteAppointmentFilter(WebsiteAppointment): + @http.route() + def appointment_type_index(self, page=1, **params): + base_domain = self._appointment_website_domain() + domain_type = self._build_filter_domain(params) + total_domain = base_domain + domain_type + + available_appointments = ( + request.env["appointment.type"].sudo().search(total_domain) + ) + appointment_data = self._prepare_appointments_cards_data( + page, available_appointments, **params + ) + + cards_layout = request.website.viewref( + "website_appointment.opt_appointments_list_cards" + ).active + + if cards_layout: + return request.render( + "website_appointment.appointments_cards_layout", + self._prepare_appointments_cards_data( + page, available_appointments, **params + ), + ) + else: + return request.render( + "appointment.appointments_list_layout", + self._prepare_appointments_list_data(available_appointments, **params), + ) + + return request.render( + "website_appointment.appointments_cards_layout", appointment_data + ) + + def _build_filter_domain(self, params): + domain_type = [] + type_dict = { + "appointment_type": { + "online": ("location_id", "=", False), + "offline": ("location_id", "!=", False), + }, + "payment_step": { + "required": ("has_payment_step", "=", True), + "not_required": ("has_payment_step", "=", False), + }, + "schedule_based_on": { + "users": ("schedule_based_on", "=", "users"), + "resources": ("schedule_based_on", "=", "resources"), + }, + } + + for key, values in type_dict.items(): + value = params.get(key) + if value and value in values: + domain_type.append(values[value]) + + if params.get("search"): + domain_type.append(("name", "ilike", params["search"])) + return domain_type diff --git a/website_appointment_filter/views/website_appointment_filter.xml b/website_appointment_filter/views/website_appointment_filter.xml new file mode 100644 index 00000000000..4100dbcb0bd --- /dev/null +++ b/website_appointment_filter/views/website_appointment_filter.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + diff --git a/website_appointment_filter/views/website_appointment_list.xml b/website_appointment_filter/views/website_appointment_list.xml new file mode 100644 index 00000000000..7e3de152a47 --- /dev/null +++ b/website_appointment_filter/views/website_appointment_list.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + diff --git a/zero_stock_blockage/__init__.py b/zero_stock_blockage/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/zero_stock_blockage/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/zero_stock_blockage/__manifest__.py b/zero_stock_blockage/__manifest__.py new file mode 100644 index 00000000000..5ab7e730334 --- /dev/null +++ b/zero_stock_blockage/__manifest__.py @@ -0,0 +1,11 @@ +{ + "name": "Zero Stock Blockage", + "description": "Apply approval to confirm sale order", + "version": "1.0", + "depends": ["sale_management"], + "data": [ + "views/sale_order.xml", + ], + "auto-install": True, + "license": "LGPL-3", +} diff --git a/zero_stock_blockage/models/__init__.py b/zero_stock_blockage/models/__init__.py new file mode 100644 index 00000000000..6aacb753131 --- /dev/null +++ b/zero_stock_blockage/models/__init__.py @@ -0,0 +1 @@ +from . import sale_order diff --git a/zero_stock_blockage/models/sale_order.py b/zero_stock_blockage/models/sale_order.py new file mode 100644 index 00000000000..aab8105f0ee --- /dev/null +++ b/zero_stock_blockage/models/sale_order.py @@ -0,0 +1,24 @@ +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + is_approval = fields.Boolean(string="Approval") + is_approval_read = fields.Boolean(compute="_compute_is_approval_read") + + @api.depends_context("uid") + def _compute_is_approval_read(self): + is_user_manager = self.env.user.has_group("sales_team.group_sale_manager") + for record in self: + if is_user_manager: + record.is_approval_read = True + else: + record.is_approval_read = False + + def action_confirm(self): + if not self.is_approval: + raise UserError("You are not authenticated to Confirm Sale Order") + + return super().action_confirm() diff --git a/zero_stock_blockage/views/sale_order.xml b/zero_stock_blockage/views/sale_order.xml new file mode 100644 index 00000000000..9cf6c3e5232 --- /dev/null +++ b/zero_stock_blockage/views/sale_order.xml @@ -0,0 +1,13 @@ + + + + sale.approval.checkbox + sale.order + + + + + + + +