Working
vendor payment and email
This commit is contained in:
+121
-29
@@ -1,7 +1,20 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = "sale.order"
|
||||
# ClaimID
|
||||
warranty_claim_ids = fields.One2many(
|
||||
"bec.warranty.claim",
|
||||
"sale_order_id",
|
||||
string="Warranty Claims"
|
||||
)
|
||||
|
||||
compressor_tag_image = fields.Binary(
|
||||
string="Compressor Tag Photo",
|
||||
related="warranty_claim_ids.compressor_tag_image",
|
||||
readonly=True
|
||||
)
|
||||
|
||||
# Checkbox under customer
|
||||
warranty_order = fields.Boolean(string="Warranty Order")
|
||||
@@ -17,6 +30,7 @@ class SaleOrder(models.Model):
|
||||
[
|
||||
("90_day", "90 Day Warranty"),
|
||||
("cabinet", "Cabinet Warranty"),
|
||||
("compressor", "Compressor Warranty"),
|
||||
("other", "Other"),
|
||||
],
|
||||
string="Warranty Type",
|
||||
@@ -26,7 +40,9 @@ class SaleOrder(models.Model):
|
||||
warranty_true_vendor_id = fields.Many2one(
|
||||
"res.partner",
|
||||
string="True Vendor",
|
||||
domain="[('supplier_rank', '>', 0)]",
|
||||
related="warranty_manufacturer_id.vendor_id",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
# Fields manufacturer requires
|
||||
@@ -49,11 +65,13 @@ class SaleOrder(models.Model):
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends("order_line.product_id")
|
||||
@api.depends("order_line.product_id", "partner_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.
|
||||
"""
|
||||
SaleOrder = self.env["sale.order"]
|
||||
|
||||
for order in self:
|
||||
if not order.partner_id or not order.order_line:
|
||||
order.warranty_original_sale_ids = False
|
||||
@@ -64,17 +82,89 @@ class SaleOrder(models.Model):
|
||||
order.warranty_original_sale_ids = False
|
||||
continue
|
||||
|
||||
candidates = self.search([
|
||||
("id", "!=", order.id),
|
||||
# Build domain safely
|
||||
domain = [
|
||||
("partner_id", "=", order.partner_id.id),
|
||||
("state", "in", ["sale", "done"]),
|
||||
("order_line.product_id", "in", product_ids),
|
||||
], limit=50)
|
||||
]
|
||||
|
||||
# Avoid comparing with NewId_*; use the real DB id if it exists
|
||||
if order._origin and order._origin.id:
|
||||
domain.append(("id", "!=", order._origin.id))
|
||||
|
||||
candidates = SaleOrder.search(domain, limit=50)
|
||||
order.warranty_original_sale_ids = candidates
|
||||
|
||||
# ---------- create draft claim on SO confirm ----------
|
||||
|
||||
def action_confirm(self):
|
||||
res = super().action_confirm()
|
||||
|
||||
for order in self:
|
||||
if not order.warranty_order:
|
||||
continue
|
||||
|
||||
# Avoid duplicate claims if already created (button or earlier run)
|
||||
if order.warranty_claim_ids:
|
||||
continue
|
||||
|
||||
# Build claim lines from all SO lines
|
||||
line_commands = []
|
||||
for so_line in order.order_line:
|
||||
if not so_line.product_id:
|
||||
continue
|
||||
|
||||
line_commands.append((0, 0, {
|
||||
"sale_line_id": so_line.id,
|
||||
"product_id": so_line.product_id.id,
|
||||
"quantity": so_line.product_uom_qty,
|
||||
# You can adjust these if later you have per-line model/serial
|
||||
"model": order.warranty_model,
|
||||
"serial_number": order.warranty_serial,
|
||||
"failure": order.warranty_failure,
|
||||
}))
|
||||
|
||||
claim_vals = {
|
||||
"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,
|
||||
}
|
||||
|
||||
if line_commands:
|
||||
claim_vals["line_ids"] = line_commands
|
||||
|
||||
self.env["bec.warranty.claim"].create(claim_vals)
|
||||
|
||||
return res
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def action_create_warranty_claim(self):
|
||||
"""Button on SO warranty tab: create a warranty.claim pre-filled."""
|
||||
"""Button on SO warranty tab: create a warranty claim pre-filled."""
|
||||
self.ensure_one()
|
||||
|
||||
# Build claim lines from all SO lines on this order
|
||||
line_commands = []
|
||||
for so_line in self.order_line:
|
||||
if not so_line.product_id:
|
||||
continue
|
||||
|
||||
line_commands.append((0, 0, {
|
||||
"sale_line_id": so_line.id,
|
||||
"product_id": so_line.product_id.id,
|
||||
"quantity": so_line.product_uom_qty,
|
||||
"model": self.warranty_model,
|
||||
"serial_number": self.warranty_serial,
|
||||
"failure": self.warranty_failure,
|
||||
}))
|
||||
|
||||
claim_vals = {
|
||||
"sale_order_id": self.id,
|
||||
"partner_id": self.partner_id.id,
|
||||
@@ -86,31 +176,33 @@ class SaleOrder(models.Model):
|
||||
"warranty_type": self.warranty_type,
|
||||
"original_sale_order_id": self.warranty_original_sale_id.id,
|
||||
}
|
||||
claim = self.env["warranty.claim"].create(claim_vals)
|
||||
|
||||
if line_commands:
|
||||
claim_vals["line_ids"] = line_commands
|
||||
|
||||
claim = self.env["bec.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
|
||||
|
||||
|
||||
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["bec.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
|
||||
|
||||
+168
-9
@@ -1,13 +1,22 @@
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
class WarrantyClaim(models.Model):
|
||||
_name = "warranty.claim"
|
||||
_name = "bec.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)
|
||||
|
||||
install_date = fields.Date("Date of Install")
|
||||
failure_date = fields.Date("Date of Failure")
|
||||
resolution_type = fields.Selection(
|
||||
[
|
||||
("credit", "Credit"),
|
||||
("replacement", "Replacement"),
|
||||
],
|
||||
string="Requested Resolution",
|
||||
)
|
||||
# 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")
|
||||
@@ -46,6 +55,19 @@ class WarrantyClaim(models.Model):
|
||||
)
|
||||
|
||||
# Technical / product fields
|
||||
line_ids = fields.One2many(
|
||||
"bec.warranty.claim.line",
|
||||
"claim_id",
|
||||
string="Claim Lines",
|
||||
)
|
||||
|
||||
# customer credit (customer credit note / refund)
|
||||
# customer_credit_id = fields.Many2one(
|
||||
# 'account.move',
|
||||
# string="Customer Credit",
|
||||
# domain=[('move_type', '=', 'out_refund')],
|
||||
# tracking=True,
|
||||
# )
|
||||
product_id = fields.Many2one("product.product", string="Product")
|
||||
model = fields.Char("Model")
|
||||
serial_number = fields.Char("Serial Number")
|
||||
@@ -54,6 +76,7 @@ class WarrantyClaim(models.Model):
|
||||
[
|
||||
("90_day", "90 Day Warranty"),
|
||||
("cabinet", "Cabinet Warranty"),
|
||||
("compressor", "Compressor Warranty"),
|
||||
("other", "Other"),
|
||||
],
|
||||
string="Warranty Type",
|
||||
@@ -63,7 +86,6 @@ class WarrantyClaim(models.Model):
|
||||
[
|
||||
("draft", "Draft"),
|
||||
("sent", "Sent to Vendor"),
|
||||
("in_progress", "In Progress"),
|
||||
("approved", "Approved"),
|
||||
("rejected", "Rejected"),
|
||||
("done", "Done"),
|
||||
@@ -73,6 +95,61 @@ class WarrantyClaim(models.Model):
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
company_id = fields.Many2one(
|
||||
"res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env.company,
|
||||
required=True,
|
||||
)
|
||||
compressor_tag_image = fields.Binary(
|
||||
string="Compressor Tag Photo",
|
||||
attachment=True,
|
||||
help="Photo of the compressor nameplate/tag required for compressor warranty claims.",
|
||||
)
|
||||
@api.constrains("warranty_type", "compressor_tag_image")
|
||||
def _check_compressor_tag_image_required(self):
|
||||
for claim in self:
|
||||
# adjust 'compressor' to your actual selection key
|
||||
if claim.warranty_type == "compressor" and not claim.compressor_tag_image:
|
||||
raise ValidationError(
|
||||
_("A compressor tag photo is required for compressor warranty claims.")
|
||||
)
|
||||
|
||||
currency_id = fields.Many2one(
|
||||
"res.currency",
|
||||
string="Currency",
|
||||
related="company_id.currency_id",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
vendor_move_id = fields.Many2one(
|
||||
"account.move",
|
||||
string="Vendor Credit / Invoice",
|
||||
help="Vendor credit note or invoice used to reimburse this warranty claim.",
|
||||
)
|
||||
|
||||
vendor_move_amount = fields.Monetary(
|
||||
string="Vendor Amount",
|
||||
currency_field="currency_id",
|
||||
compute="_compute_vendor_move_amount",
|
||||
store=False,
|
||||
)
|
||||
|
||||
vendor_move_state = fields.Selection(
|
||||
related="vendor_move_id.state",
|
||||
string="Vendor Doc State",
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@api.depends("vendor_move_id")
|
||||
def _compute_vendor_move_amount(self):
|
||||
for claim in self:
|
||||
claim.vendor_move_amount = claim.vendor_move_id.amount_total if claim.vendor_move_id else 0.0
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if vals.get("name", "New") == "New":
|
||||
@@ -85,10 +162,92 @@ class WarrantyClaim(models.Model):
|
||||
return super().create(vals)
|
||||
|
||||
def action_send_to_vendor(self):
|
||||
"""Uses an email template and moves to 'sent' state."""
|
||||
"""Open email wizard prefilled with the warranty template."""
|
||||
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)
|
||||
|
||||
# get template (adjust the XML ID if your template id is different)
|
||||
template = self.env.ref(
|
||||
"warranty_claim.mail_template_warranty_claim_90day",
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
compose_form = self.env.ref(
|
||||
"mail.email_compose_message_wizard_form",
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
|
||||
# move to Sent state right away (optional – comment this out if you prefer manual)
|
||||
self.state = "sent"
|
||||
return True
|
||||
|
||||
ctx = {
|
||||
"default_model": self._name,
|
||||
"default_res_ids": [self.id],
|
||||
"default_use_template": bool(template),
|
||||
"default_template_id": template.id if template else False,
|
||||
"default_composition_mode": "comment",
|
||||
"force_email": True,
|
||||
}
|
||||
|
||||
return {
|
||||
"name": _("Send Warranty Email"),
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "mail.compose.message",
|
||||
"view_mode": "form",
|
||||
"views": [(compose_form.id, "form")],
|
||||
"target": "new",
|
||||
"context": ctx,
|
||||
}
|
||||
|
||||
def action_create_vendor_credit(self):
|
||||
self.ensure_one()
|
||||
|
||||
move = self.env["account.move"].create({
|
||||
"move_type": "in_refund",
|
||||
"partner_id": self.true_vendor_id.id,
|
||||
"invoice_date": fields.Date.today(),
|
||||
"invoice_line_ids": [
|
||||
(0, 0, {
|
||||
"name": f"Warranty Claim {self.name}",
|
||||
"quantity": 1,
|
||||
"price_unit": self.product_id.standard_price * -1, # or reimburse amount
|
||||
"account_id": self.env.company.warranty_expense_account_id.id if hasattr(self.env.company, 'warranty_expense_account_id') else None,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
self.vendor_credit_id = move.id
|
||||
|
||||
return {
|
||||
"name": "Vendor Credit Note",
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "account.move",
|
||||
"view_mode": "form",
|
||||
"res_id": move.id,
|
||||
"target": "current",
|
||||
}
|
||||
|
||||
class WarrantyClaimLine(models.Model):
|
||||
_name = "bec.warranty.claim.line"
|
||||
_description = "Warranty Claim Line"
|
||||
|
||||
claim_id = fields.Many2one(
|
||||
"bec.warranty.claim",
|
||||
string="Warranty Claim",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
sale_line_id = fields.Many2one(
|
||||
"sale.order.line",
|
||||
string="Sales Order Line",
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
"product.product",
|
||||
string="Product",
|
||||
required=True,
|
||||
)
|
||||
quantity = fields.Float(
|
||||
string="Quantity",
|
||||
default=1.0,
|
||||
)
|
||||
model = fields.Char("Model")
|
||||
serial_number = fields.Char("Serial Number")
|
||||
failure = fields.Text("Failure Description")
|
||||
Reference in New Issue
Block a user