This commit is contained in:
gsinghpal
2026-02-25 09:40:41 -05:00
parent 0e1aebe60b
commit e71bc503f9
69 changed files with 7537 additions and 82 deletions

View File

@@ -0,0 +1,6 @@
from . import sale_order
from . import sale_order_line
from . import renewal_log
from . import cancellation_request
from . import stock_warehouse
from . import res_config_settings

View File

@@ -0,0 +1,123 @@
import uuid
from odoo import _, api, fields, models
class RentalCancellationRequest(models.Model):
_name = 'rental.cancellation.request'
_description = 'Rental Cancellation Request'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'request_date desc'
_rec_name = 'display_name'
order_id = fields.Many2one(
'sale.order',
string="Rental Order",
required=True,
ondelete='cascade',
index=True,
)
partner_id = fields.Many2one(
related='order_id.partner_id',
store=True,
string="Customer",
)
request_date = fields.Datetime(
string="Request Date",
default=fields.Datetime.now,
)
requested_pickup_date = fields.Datetime(string="Requested Pickup Date")
reason = fields.Text(string="Reason")
state = fields.Selection(
[
('new', 'New'),
('confirmed', 'Confirmed'),
('pickup_scheduled', 'Pickup Scheduled'),
('completed', 'Completed'),
('rejected', 'Rejected'),
],
string="Status",
default='new',
required=True,
tracking=True,
)
assigned_user_id = fields.Many2one(
'res.users',
string="Assigned To",
tracking=True,
)
pickup_activity_id = fields.Many2one(
'mail.activity',
string="Pickup Activity",
ondelete='set null',
)
token = fields.Char(
string="Security Token",
default=lambda self: uuid.uuid4().hex,
copy=False,
index=True,
)
def _compute_display_name(self):
for rec in self:
rec.display_name = (
f"{rec.order_id.name or 'New'} - {rec.partner_id.name or 'Customer'}"
)
def action_confirm(self):
"""Confirm the cancellation and stop auto-renewal."""
self.ensure_one()
self.order_id.write({'rental_auto_renew': False})
self.write({'state': 'confirmed'})
self._schedule_pickup_activity()
self._send_cancellation_confirmation()
def action_schedule_pickup(self):
"""Mark pickup as scheduled."""
self.ensure_one()
self.write({'state': 'pickup_scheduled'})
def action_complete(self):
"""Mark the cancellation and pickup as completed."""
self.ensure_one()
self.write({'state': 'completed'})
if self.pickup_activity_id and not self.pickup_activity_id.date_done:
self.pickup_activity_id.action_done()
def action_reject(self):
"""Reject the cancellation request."""
self.ensure_one()
self.write({'state': 'rejected'})
def _schedule_pickup_activity(self):
"""Create a to-do activity on the sale order for staff to schedule pickup."""
self.ensure_one()
assigned_user = (
self.assigned_user_id
or self.order_id.user_id
or self.env.user
)
activity = self.order_id.activity_schedule(
'mail.mail_activity_data_todo',
date_deadline=fields.Date.today(),
summary=_(
"Schedule rental pickup for %s",
self.partner_id.name or self.order_id.partner_id.name,
),
note=_(
"Customer has requested cancellation and pickup for rental %s. "
"Please contact them to schedule a pickup.",
self.order_id.name,
),
user_id=assigned_user.id,
)
self.pickup_activity_id = activity
def _send_cancellation_confirmation(self):
"""Send confirmation email to the customer."""
template = self.env.ref(
'fusion_rental.mail_template_rental_cancellation_confirmed',
raise_if_not_found=False,
)
if template:
template.send_mail(self.order_id.id, force_send=True)

View File

@@ -0,0 +1,74 @@
from odoo import fields, models
class RentalRenewalLog(models.Model):
_name = 'rental.renewal.log'
_description = 'Rental Renewal Log'
_order = 'create_date desc'
_rec_name = 'display_name'
order_id = fields.Many2one(
'sale.order',
string="Rental Order",
required=True,
ondelete='cascade',
index=True,
)
partner_id = fields.Many2one(
related='order_id.partner_id',
store=True,
string="Customer",
)
renewal_number = fields.Integer(
string="Renewal #",
required=True,
)
previous_start_date = fields.Datetime(string="Previous Start")
previous_return_date = fields.Datetime(string="Previous Return")
new_start_date = fields.Datetime(string="New Start")
new_return_date = fields.Datetime(string="New Return")
invoice_id = fields.Many2one(
'account.move',
string="Invoice",
ondelete='set null',
)
payment_status = fields.Selection(
[
('pending', 'Pending'),
('paid', 'Paid'),
('failed', 'Failed'),
],
string="Payment Status",
default='pending',
)
payment_transaction_id = fields.Many2one(
'payment.transaction',
string="Payment Transaction",
ondelete='set null',
)
renewal_type = fields.Selection(
[
('automatic', 'Automatic'),
('manual', 'Manual'),
],
string="Renewal Type",
required=True,
)
state = fields.Selection(
[
('draft', 'Draft'),
('done', 'Done'),
('failed', 'Failed'),
('cancelled', 'Cancelled'),
],
string="Status",
default='draft',
required=True,
)
notes = fields.Text(string="Notes")
def _compute_display_name(self):
for rec in self:
rec.display_name = (
f"{rec.order_id.name or 'New'} - Renewal #{rec.renewal_number}"
)

View File

@@ -0,0 +1,19 @@
from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
rental_google_review_url = fields.Char(
string="Google Review URL",
config_parameter='fusion_rental.google_review_url',
help="Google Review link shown in thank-you emails after rental close. "
"For multi-location, set per warehouse in Inventory > Configuration > Warehouses.",
)
rental_deposit_hold_days = fields.Integer(
string="Deposit Hold Period (Days)",
config_parameter='fusion_rental.deposit_hold_days',
default=3,
help="Number of days to hold the security deposit after pickup before "
"processing the refund. Default is 3 days.",
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
from odoo import api, fields, models
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
is_security_deposit = fields.Boolean(
string="Is Security Deposit",
default=False,
help="Marks this line as a security deposit for a rental product.",
)
rental_deposit_source_line_id = fields.Many2one(
'sale.order.line',
string="Deposit For",
ondelete='cascade',
help="The rental product line this deposit is associated with.",
)
@api.model_create_multi
def create(self, vals_list):
lines = super().create(vals_list)
deposit_vals = []
for line in lines:
if not line.is_rental or line.is_security_deposit:
continue
if not line.order_id.is_rental_order:
continue
deposit_amount = line.order_id._compute_deposit_amount_for_line(line)
if deposit_amount <= 0:
continue
existing = line.order_id.order_line.filtered(
lambda l: (
l.is_security_deposit
and l.rental_deposit_source_line_id == line
)
)
if existing:
continue
deposit_product = line.order_id._get_deposit_product()
deposit_vals.append({
'order_id': line.order_id.id,
'product_id': deposit_product.id,
'product_uom_id': deposit_product.uom_id.id,
'name': f"SECURITY DEPOSIT - REFUNDABLE - {line.product_id.display_name}",
'product_uom_qty': 1,
'price_unit': deposit_amount,
'is_security_deposit': True,
'rental_deposit_source_line_id': line.id,
})
if deposit_vals:
super().create(deposit_vals)
return lines
def unlink(self):
deposit_lines = self.env['sale.order.line']
for line in self:
if line.is_rental and not line.is_security_deposit:
deposit_lines |= line.order_id.order_line.filtered(
lambda l: l.rental_deposit_source_line_id == line
)
if deposit_lines:
deposit_lines.unlink()
return super().unlink()

View File

@@ -0,0 +1,10 @@
from odoo import fields, models
class StockWarehouse(models.Model):
_inherit = 'stock.warehouse'
google_review_url = fields.Char(
string="Google Review URL",
help="Paste the Google Review link for this location.",
)