From 44be2f5cf238de665f0e5cf484ef86a8b54d75fb Mon Sep 17 00:00:00 2001 From: greg Date: Sun, 30 Nov 2025 19:31:24 -0600 Subject: [PATCH] Working no payment --- .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ .idea/warranty_claim.iml | 19 +++++ __init__.py | 1 + __manifest__.py | 25 ++++++ data/mail_template_warranty_claim.xml | 6 ++ models/__init__.py | 3 + models/sale_order_warranty.py | 116 ++++++++++++++++++++++++++ models/warranty_claim.py | 94 +++++++++++++++++++++ models/warranty_vendor.py | 18 ++++ report/report_warranty_claim.xml | 53 ++++++++++++ security/ir.model.access.csv | 3 + views/sale_order_warranty_views.xml | 49 +++++++++++ views/warranty_claim_views.xml | 99 ++++++++++++++++++++++ views/warranty_menus.xml | 25 ++++++ views/warranty_vendor_views.xml | 64 ++++++++++++++ 16 files changed, 589 insertions(+) create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/warranty_claim.iml create mode 100644 __init__.py create mode 100644 __manifest__.py create mode 100644 data/mail_template_warranty_claim.xml create mode 100644 models/__init__.py create mode 100644 models/sale_order_warranty.py create mode 100644 models/warranty_claim.py create mode 100644 models/warranty_vendor.py create mode 100644 report/report_warranty_claim.xml create mode 100644 security/ir.model.access.csv create mode 100644 views/sale_order_warranty_views.xml create mode 100644 views/warranty_claim_views.xml create mode 100644 views/warranty_menus.xml create mode 100644 views/warranty_vendor_views.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7eed931 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/warranty_claim.iml b/.idea/warranty_claim.iml new file mode 100644 index 0000000..82b9934 --- /dev/null +++ b/.idea/warranty_claim.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 0000000..1fadc95 --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,25 @@ +{ + "name": "Warranty Claims", + "version": "17.0.1.0.0", + "summary": "Manage product warranty claims and vendor RMA", + "category": "Sales", + "author": "Wisc-Host", + "depends": [ + "base", + "mail", + "sale_management", + "account", + "purchase", + ], + "data": [ + "security/ir.model.access.csv", + "views/sale_order_warranty_views.xml", + "views/warranty_claim_views.xml", + "views/warranty_menus.xml", + "views/warranty_vendor_views.xml", + "report/report_warranty_claim.xml", + # "data/mail_template_warranty_claim.xml", + ], + "application": True, + "installable": True, +} diff --git a/data/mail_template_warranty_claim.xml b/data/mail_template_warranty_claim.xml new file mode 100644 index 0000000..e3bd6d6 --- /dev/null +++ b/data/mail_template_warranty_claim.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..238f351 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,3 @@ +from . import sale_order_warranty +from . import warranty_claim +from . import warranty_vendor diff --git a/models/sale_order_warranty.py b/models/sale_order_warranty.py new file mode 100644 index 0000000..7546e85 --- /dev/null +++ b/models/sale_order_warranty.py @@ -0,0 +1,116 @@ +from odoo import api, fields, models + +class SaleOrder(models.Model): + _inherit = "sale.order" + + # Checkbox under customer + warranty_order = fields.Boolean(string="Warranty Order") + + # Manufacturer (your logical “warranty manufacturer” entity) + warranty_manufacturer_id = fields.Many2one( + "warranty.manufacturer", + string="Manufacturer", + ) + + # Warranty type + warranty_type = fields.Selection( + [ + ("90_day", "90 Day Warranty"), + ("cabinet", "Cabinet Warranty"), + ("other", "Other"), + ], + string="Warranty Type", + ) + + # “True Vendor” (actual vendor that handles warranty, may differ from manufacturer) + warranty_true_vendor_id = fields.Many2one( + "res.partner", + string="True Vendor", + domain="[('supplier_rank', '>', 0)]", + ) + + # Fields manufacturer requires + warranty_model = fields.Char("Model") + warranty_serial = fields.Char("Serial Number") + warranty_failure = fields.Text("Failure Description") + + # Original sales order (dropdown filtered to orders with matching part numbers) + # We'll add a helper many2many for domain support. + warranty_original_sale_id = fields.Many2one( + "sale.order", + string="Original Sales Order", + domain="[('id', 'in', warranty_original_sale_ids)]", + ) + + warranty_original_sale_ids = fields.Many2many( + "sale.order", + compute="_compute_warranty_original_sale_ids", + string="Candidate Original Sales Orders", + store=False, + ) + + @api.depends("order_line.product_id") + def _compute_warranty_original_sale_ids(self): + """Find previous sales orders for the same customer containing any + of the products on this warranty order. + """ + for order in self: + if not order.partner_id or not order.order_line: + order.warranty_original_sale_ids = False + continue + + product_ids = order.order_line.product_id.ids + if not product_ids: + order.warranty_original_sale_ids = False + continue + + candidates = self.search([ + ("id", "!=", order.id), + ("partner_id", "=", order.partner_id.id), + ("state", "in", ["sale", "done"]), + ("order_line.product_id", "in", product_ids), + ], limit=50) + order.warranty_original_sale_ids = candidates + + def action_create_warranty_claim(self): + """Button on SO warranty tab: create a warranty.claim pre-filled.""" + self.ensure_one() + claim_vals = { + "sale_order_id": self.id, + "partner_id": self.partner_id.id, + "manufacturer_id": self.warranty_manufacturer_id.id, + "true_vendor_id": self.warranty_true_vendor_id.id, + "model": self.warranty_model, + "serial_number": self.warranty_serial, + "failure": self.warranty_failure, + "warranty_type": self.warranty_type, + "original_sale_order_id": self.warranty_original_sale_id.id, + } + claim = self.env["warranty.claim"].create(claim_vals) + action = self.env.ref("odoo_warranty_claims.action_warranty_claims").read()[0] + action["res_id"] = claim.id + action["views"] = [(self.env.ref("odoo_warranty_claims.view_warranty_claim_form").id, "form")] + return action + + + def _create_invoices(self, grouped=False, final=False, date=None): + invoices = super()._create_invoices(grouped=grouped, final=final, date=date) + for order in self: + if order.warranty_order: + # Link customer invoice(s) to the new claim + claim = self.env["warranty.claim"].create({ + "sale_order_id": order.id, + "partner_id": order.partner_id.id, + "manufacturer_id": order.warranty_manufacturer_id.id, + "true_vendor_id": order.warranty_true_vendor_id.id, + "model": order.warranty_model, + "serial_number": order.warranty_serial, + "failure": order.warranty_failure, + "warranty_type": order.warranty_type, + "original_sale_order_id": order.warranty_original_sale_id.id, + "invoice_customer_ids": [(6, 0, invoices.ids)], + }) + # you could also auto-open or email here, but usually this is backend logic + return invoices + + diff --git a/models/warranty_claim.py b/models/warranty_claim.py new file mode 100644 index 0000000..5a2610f --- /dev/null +++ b/models/warranty_claim.py @@ -0,0 +1,94 @@ +from odoo import api, fields, models + +class WarrantyClaim(models.Model): + _name = "warranty.claim" + _description = "Warranty Claim" + _inherit = ["mail.thread", "mail.activity.mixin"] + _order = "create_date desc" + + name = fields.Char("Claim Reference", required=True, copy=False, default="New", tracking=True) + + # Link to the customer and various parties + partner_id = fields.Many2one("res.partner", string="Customer", tracking=True) + manufacturer_id = fields.Many2one("warranty.manufacturer", string="Manufacturer") + true_vendor_id = fields.Many2one( + "res.partner", + string="True Vendor", + domain="[('supplier_rank', '>', 0)]", + tracking=True, + ) + extra_recipient_ids = fields.Many2many( + "res.partner", + string="Additional Email Recipients", + help="Separate recipients you want CC'd on the warranty email.", + ) + + # Link to sales and invoices + sale_order_id = fields.Many2one("sale.order", string="Sales Order", tracking=True) + original_sale_order_id = fields.Many2one("sale.order", string="Original Sales Order") + + # Customer invoices to attach + invoice_customer_ids = fields.Many2many( + "account.move", + "warranty_claim_customer_invoice_rel", + "claim_id", "move_id", + string="Customer Invoices", + domain="[('move_type', 'in', ('out_invoice', 'out_refund'))]", + ) + + # Vendor invoices to attach + invoice_vendor_ids = fields.Many2many( + "account.move", + "warranty_claim_vendor_invoice_rel", + "claim_id", "move_id", + string="Vendor Invoices", + domain="[('move_type', 'in', ('in_invoice', 'in_refund'))]", + ) + + # Technical / product fields + product_id = fields.Many2one("product.product", string="Product") + model = fields.Char("Model") + serial_number = fields.Char("Serial Number") + failure = fields.Text("Failure Description") + warranty_type = fields.Selection( + [ + ("90_day", "90 Day Warranty"), + ("cabinet", "Cabinet Warranty"), + ("other", "Other"), + ], + string="Warranty Type", + ) + claim_date = fields.Date("Claim Date", default=fields.Date.context_today) + state = fields.Selection( + [ + ("draft", "Draft"), + ("sent", "Sent to Vendor"), + ("in_progress", "In Progress"), + ("approved", "Approved"), + ("rejected", "Rejected"), + ("done", "Done"), + ], + string="Status", + default="draft", + tracking=True, + ) + + @api.model + def create(self, vals): + if vals.get("name", "New") == "New": + seq = self.env.ref( + "odoo_warranty_claims.seq_warranty_claim", + raise_if_not_found=False + ) + if seq: + vals["name"] = seq.next_by_id() + return super().create(vals) + + def action_send_to_vendor(self): + """Uses an email template and moves to 'sent' state.""" + self.ensure_one() + template = self.env.ref("odoo_warranty_claims.mail_template_warranty_claim", raise_if_not_found=False) + if template: + template.send_mail(self.id, force_send=True) + self.state = "sent" + return True diff --git a/models/warranty_vendor.py b/models/warranty_vendor.py new file mode 100644 index 0000000..5573c58 --- /dev/null +++ b/models/warranty_vendor.py @@ -0,0 +1,18 @@ +from odoo import fields, models + +class WarrantyManufacturer(models.Model): + _name = "warranty.manufacturer" + _description = "Warranty Manufacturer" + + name = fields.Char(required=True) + vendor_id = fields.Many2one( + "res.partner", + string="True Vendor", + domain="[('supplier_rank', '>', 0)]", + ) + # Flags for what this manufacturer requires + requires_model = fields.Boolean(default=True) + requires_serial = fields.Boolean(default=True) + requires_failure = fields.Boolean(default=True) + requires_original_so = fields.Boolean(default=True) + notes = fields.Text("Vendor Warranty Instructions") diff --git a/report/report_warranty_claim.xml b/report/report_warranty_claim.xml new file mode 100644 index 0000000..de242f6 --- /dev/null +++ b/report/report_warranty_claim.xml @@ -0,0 +1,53 @@ + + + + + + + diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000..fa1d809 --- /dev/null +++ b/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 +access_warranty_claim_user,warranty.claim.user,model_warranty_claim,base.group_user,1,1,1,1 +access_warranty_manufacturer_admin,warranty.manufacturer.admin,model_warranty_manufacturer,base.group_system,1,1,1,1 diff --git a/views/sale_order_warranty_views.xml b/views/sale_order_warranty_views.xml new file mode 100644 index 0000000..fe35cc6 --- /dev/null +++ b/views/sale_order_warranty_views.xml @@ -0,0 +1,49 @@ + + + + sale.order.form.warranty + sale.order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +
diff --git a/views/warranty_claim_views.xml b/views/warranty_claim_views.xml new file mode 100644 index 0000000..2bf4f40 --- /dev/null +++ b/views/warranty_claim_views.xml @@ -0,0 +1,99 @@ + + + + + + warranty.claim.tree + warranty.claim + + + + + + + + + + + + + + + + warranty.claim.form + warranty.claim + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+
+ + + + Warranty Claims + ir.actions.act_window + warranty.claim + tree,form + + + +
+
diff --git a/views/warranty_menus.xml b/views/warranty_menus.xml new file mode 100644 index 0000000..cf2ae12 --- /dev/null +++ b/views/warranty_menus.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/views/warranty_vendor_views.xml b/views/warranty_vendor_views.xml new file mode 100644 index 0000000..e901cb9 --- /dev/null +++ b/views/warranty_vendor_views.xml @@ -0,0 +1,64 @@ + + + + warranty.manufacturer.tree + warranty.manufacturer + + + + + + + + + + + + + + warranty.manufacturer.form + warranty.manufacturer + +
+ + + + + + + + + + + + + + + +
+
+
+ + + Manufacturers + ir.actions.act_window + warranty.manufacturer + tree,form + + + + + + +