Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] estate: add the real estate module #206

Draft
wants to merge 16 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions budget/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
21 changes: 21 additions & 0 deletions budget/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
'name': 'Budget',
'version': '1.0',
'author': 'DhruvKumar Nagar',
'summary': 'Manage budgets effectively',
'description': """
A custom module for creating and managing budgets in Odoo.
""",
'depends': ['base', 'account'],
'data': [
'security/ir.model.access.csv',
'views/budget_line_view.xml',
'views/budget_menu_view.xml',
'wizard/budget_wizard_view.xml',
'views/budget_views.xml',
],
'installable': True,
'application': True,
'auto_install': False,
"license": "LGPL-3"
}
3 changes: 3 additions & 0 deletions budget/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import budget
from . import budget_line
from . import account_analytic_line
38 changes: 38 additions & 0 deletions budget/models/account_analytic_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError


class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"

budget_line_id = fields.Many2one(comodel_name="budget.management.budget.lines")

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
budget_line = self.env["budget.management.budget.lines"].browse(
vals.get("budget_line_id")
)
budget = budget_line.budget_id
if budget.on_over_budget == "restriction":
if sum(budget_line.analytic_line_ids.mapped("amount"))+vals.get("amount") > budget_line.budget_amount:
raise ValidationError(
"You cannot create a budget line because it exceeds the allowed budget!"
)
return super(AccountAnalyticLine, self).create(vals_list)

def write(self, vals):
if "amount" in vals:
for record in self:
old_amount = record.amount
new_amount = vals.get("amount")
print(old_amount,new_amount)
total_amount = sum(record.budget_line_id.analytic_line_ids.mapped("amount")) + new_amount - old_amount

budget_line = record.budget_line_id
budget = budget_line.budget_id
if budget.on_over_budget == "restriction" and total_amount > budget_line.budget_amount:
raise ValidationError(
"You cannot update this budget line because it exceeds the allowed budget!"
)
return super(AccountAnalyticLine, self).write(vals)
136 changes: 136 additions & 0 deletions budget/models/budget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError, UserError

class Budget(models.Model):
_name = "budget.budget"
_inherit = ["mail.thread", "mail.activity.mixin"]
_description = "Budget Management"

name = fields.Char(compute="_compute_budget_name", store=True, readonly=True)
active = fields.Boolean(default=True)
is_favorite = fields.Boolean(default=False)
color = fields.Integer(string="Color Index")
state = fields.Selection(
selection=[
("draft", "Draft"),
("confirmed", "Confirmed"),
("revised", "Revised"),
("done", "Done"),
],
required=True,
default="draft",
tracking=True,
)
on_over_budget = fields.Selection(
selection=[("warning", "Warning"), ("restriction", "Restriction")],
tracking=True,
)
responsible = fields.Many2one(
comodel_name="res.users",
string="Responsible",
tracking=True,
)
revision_id = fields.Many2one(
comodel_name="budget.budget", # This should point to the same budget model
tracking=True,
readonly=True,
)
date_start = fields.Date(string="Start Date", required=True)
date_end = fields.Date(string="Expiration Date", required=True, index=True)
company_id = fields.Many2one(
"res.company",
string="Company",
default=lambda self: self.env.company,
)
budget_line_ids = fields.One2many(
comodel_name="budget.management.budget.lines", inverse_name="budget_id"
)
warnings = fields.Text(readonly=True)
currency_id = fields.Many2one(
comodel_name="res.currency",
string="Currency",
required=True,
default=lambda self: self.env.company.currency_id,
)

@api.depends("date_start", "date_end")
def _compute_budget_name(self):
for record in self:
if record.date_start and record.date_end:
start_date = record.date_start.strftime("%Y-%m")
end_date = record.date_end.strftime("%Y-%m")
record.name = f"Budget {start_date} to {end_date}"
else:
record.name = "Unknown Budget"

@api.constrains("date_start", "date_end")
def _check_period_overlap(self):
for record in self:
overlapping_budgets = self.search(
[
("id", "!=", record.id),
("date_start", "<=", record.date_start),
("date_end", ">=", record.date_end),
("company_id", "=", record.company_id.id),
]
)
if overlapping_budgets:
raise ValidationError(
"Cannot create overlapping budgets for the same period and company."
)

def onclick_reset_to_draft(self):
for record in self:
if record.state != "draft":
record.state = "draft"

def onclick_confirmed(self):
for record in self:
if record.state == "draft":
record.state = "confirmed"

def onclick_revise(self):
for record in self:
if record.state != "confirmed":
raise UserError("Only confirmed budgets can be revised.")

# Archive the current record and set its state to 'revised'
record.sudo().write({
'state': 'revised',
'active': False, # Archive the original record
'revision_id': record.id, # Set the revision_id to refer to the original budget
})

# Create a duplicate budget (this will be the revised budget)
duplicate = record.copy()

# Manually copy related budget lines to the new budget (duplicate)
for line in record.budget_line_ids:
self.env["budget.management.budget.lines"].create({
'name': line.name,
'budget_id': duplicate.id, # Link to the duplicated budget
'budget_amount': line.budget_amount,
'analytic_account_id': line.analytic_account_id.id,
# Add any other necessary fields here
})

# Update the duplicate record's state and fields
duplicate.sudo().write({
'state': 'draft', # Set the duplicate budget's state to draft
'active': True, # Set the new record as active
'responsible': self.env.user.id, # Set the current user as responsible for the revised budget
'revision_id': record.id, # Set the revision_id to point to the original budget (parent budget)
'name': f"{record.name} (Revised)", # Adjust the name for clarity
})

# Log a message for traceability
record.message_post(
body="The budget has been revised. A new draft budget has been created.",
message_type="notification",
)


def onclick_done(self):
for record in self:
if record.state in ["confirmed", "revised"]:
record.state = "done"
104 changes: 104 additions & 0 deletions budget/models/budget_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError, UserError

class BudgetLine(models.Model):
_name = "budget.management.budget.lines"
_description = "Budget Management Budget Lines"

name = fields.Char(string="Name")
budget_id = fields.Many2one(
comodel_name="budget.budget", string="Budget", required=True
)
state = fields.Selection(related="budget_id.state")
budget_amount = fields.Monetary(
string="Budget Amount",
default=0.0,
currency_field="currency_id",
help="The total allocated budget for this budget line.",
)
achieved_amount = fields.Monetary(
string="Achieved Amount",
default=0.0,
compute="_compute_achieved_amount",
store=True,
currency_field="currency_id",
)
achieved_percentage = fields.Float(
string="Achieved (%)",
compute="_compute_achieved_amount",
store=True,
readonly=True,
help="Percentage of the budget achieved based on analytic lines.",
)
analytic_account_id = fields.Many2one(
"account.analytic.account", string="Analytic Account", required=True
)
analytic_line_ids = fields.One2many(
comodel_name="account.analytic.line",
inverse_name="budget_line_id",
string="Analytic Lines",
)
over_budget = fields.Monetary(
string="Over Budget",
compute="_compute_achieved_amount",
store=True,
help="The amount by which the budget line exceeds its allocated budget.",
currency_field="currency_id",
)
currency_id = fields.Many2one(
comodel_name="res.currency",
related="budget_id.currency_id",
string="Currency",
readonly=True,
)

# Add these fields for the Gantt view
date_start = fields.Date(string="Start Date", required=True)
date_end = fields.Date(string="End Date", required=True)

@api.depends("analytic_line_ids.amount")
def _compute_achieved_amount(self):
for record in self:
record.achieved_amount = sum(record.analytic_line_ids.mapped("amount"))
record.achieved_percentage = (
(record.achieved_amount / record.budget_amount) * 100
if record.budget_amount > 0
else 0.0
)
record.over_budget = max(0.0, record.achieved_amount - record.budget_amount)

if (
record.budget_id.on_over_budget == "warning"
and record.achieved_amount > record.budget_amount
):
record.budget_id.warnings = "Achieved amount is more than your budget!"
else:
record.budget_id.warnings = False

@api.constrains("budget_amount")
def _check_budget_amount(self):
for record in self:
if record.budget_amount < 0:
raise ValidationError("Budget amount cannot be negative.")

@api.model_create_multi
def create(self, vals_list):
active_budget = None
if self.env.context.get("active_id"):
active_budget = self.env["budget.budget"].browse(self.env.context.get("active_id"))
if active_budget.state != "draft":
raise UserError("Budget lines can only be created when the state is 'draft'.")
else:
for vals in vals_list:
budget_id = vals.get("budget_id")
if budget_id:
active_budget = self.env["budget.budget"].browse(budget_id)
break

if not active_budget:
raise UserError("No budget found in context or record.")

if active_budget.state != "draft":
raise UserError("Budget lines can only be created when the state is 'draft'.")

return super(BudgetLine, self).create(vals_list)
5 changes: 5 additions & 0 deletions budget/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

access_budget_line,budget.line,model_budget_management_budget_lines,,1,1,1,1
access_budget,budget,model_budget_budget,,1,1,1,1
access_budget_wizard,budget.wizard,model_add_budget_wizard,,1,1,1,1
64 changes: 64 additions & 0 deletions budget/views/budget_line_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<odoo>
<record id="view_budget_management_budget_line_tree" model="ir.ui.view">
<field name="name">budget.line.tree</field>
<field name="model">budget.management.budget.lines</field>
<field name="arch" type="xml">
<list string="Budget Lines"
default_order="id desc"
sample="1"
editable="bottom"
>
<field name="currency_id" column_invisible="True" />
<field string="Budget" name="name" />
<field name="budget_id" column_invisible="True" />
<field string="Analytic Account" name="analytic_account_id" />
<field string="Budget Amount" name="budget_amount" />
<field string="Achived Amount" name="achieved_amount" />
</list>
</field>
</record>

<record id="view_budget_lines_graph" model="ir.ui.view">
<field name="name">budget.lines.graph</field>
<field name="model">budget.management.budget.lines</field>
<field name="arch" type="xml">
<graph string="Budget Line Analysis" type="bar">
<field name="budget_id" type="row" />
<field name="achieved_percentage" type="measure" />
<field name="analytic_account_id" type="col" />
</graph>
</field>
</record>

<record id="view_budget_lines_pivot" model="ir.ui.view">
<field name="name">budget.lines.pivot</field>
<field name="model">budget.management.budget.lines</field>
<field name="arch" type="xml">
<pivot string="Budget Line Pivot">
<field name="analytic_account_id" type="row" />
</pivot>
</field>
</record>

<record id="view_budget_lines_gantt" model="ir.ui.view">
<field name="name">budget.lines.gantt</field>
<field name="model">budget.management.budget.lines</field>
<field name="arch" type="xml">
<gantt string="Budget Lines Timeline" date_start="date_start" date_stop="date_end">
<field name="name" />
<field name="budget_amount" sum="Total Budget" />
</gantt>
</field>
</record>

<record id="action_budget_lines" model="ir.actions.act_window">
<field name="name">Budget Lines</field>
<field name="res_model">budget.management.budget.lines</field>
<field name="view_mode">graph,pivot,gantt</field>
<field name="context">{}</field>
</record>

<menuitem id="menu_budget_lines" name="Budget Lines"
parent="budget_management_menu" action="action_budget_lines" />

</odoo>
Loading