Files
odoo-addons/addons/tk_construction_management/models/budget.py

231 lines
12 KiB
Python

from odoo import api, fields, models, _
class SubProjectBudget(models.Model):
_name = 'sub.project.budget'
_description = "Sub Project Budget"
_inherit = ['mail.thread', 'mail.activity.mixin']
name = fields.Char(string="Title")
progress = fields.Float(string="Budget Utilization(%)", compute="compute_used_budget")
total_budget_amount = fields.Monetary(string="Total Budget Amount", compute="compute_used_budget")
utilization_amount = fields.Monetary(string="Budget Utilization", compute="compute_used_budget")
start_date = fields.Date(string="Start Date")
end_date = fields.Date(string="End Date")
site_id = fields.Many2one('tk.construction.site', string="Project")
sub_project_id = fields.Many2one('tk.construction.project', string="Sub Project")
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
string="Responsible")
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company)
currency_id = fields.Many2one('res.currency', related='company_id.currency_id', string='Currency')
status = fields.Selection([('draft', 'Draft'), ('waiting_approval', 'Waiting Approval'), ('approved', 'Approved'),
('in_progress', 'In Progress'), ('complete', 'Complete'), ('cancel', 'Cancel'),
('reject', 'Reject')], default='draft')
budget_count = fields.Integer(string="Budget Line Count", compute="compute_count")
reject_reason = fields.Text(string="Reject Reason")
budget_line_ids = fields.One2many('project.budget', 'sub_project_budget_id')
def compute_count(self):
for rec in self:
rec.budget_count = self.env['project.budget'].search_count([('sub_project_budget_id', '=', rec.id)])
def action_view_budget_line(self):
return {
'type': 'ir.actions.act_window',
'name': _('Budget'),
'res_model': 'project.budget',
'domain': [('sub_project_budget_id', '=', self.id)],
'context': {'default_sub_project_budget_id': self.id},
'view_mode': 'list,kanban,form',
'target': 'current'
}
def action_department_approval(self):
self.status = 'waiting_approval'
def action_approve_budget(self):
self.status = 'approved'
def action_reject_budget(self):
self.status = 'reject'
def action_complete_budget(self):
self.status = 'complete'
def action_cancel_budget(self):
self.status = 'cancel'
def action_reset_draft_budget(self):
self.status = 'draft'
@api.depends('budget_line_ids', 'budget_line_ids.budget', 'budget_line_ids.remaining_budget')
def compute_used_budget(self):
for rec in self:
utilize_budget = 0.0
total_budget_amount = 0.0
remaining_budget_amount = 0.0
for data in rec.budget_line_ids:
total_budget_amount = total_budget_amount + data.budget
remaining_budget_amount = remaining_budget_amount + data.remaining_budget
utilize_budget = 100 - (
(remaining_budget_amount * 100 / total_budget_amount) if total_budget_amount > 0 else 100)
rec.progress = round(utilize_budget, 1)
rec.total_budget_amount = total_budget_amount
rec.utilization_amount = total_budget_amount - remaining_budget_amount
class ProjectBudget(models.Model):
_name = 'project.budget'
_description = "Project Budget"
_rec_name = 'job_type_id'
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company)
currency_id = fields.Many2one('res.currency', related='company_id.currency_id', string='Currency')
sub_project_budget_id = fields.Many2one('sub.project.budget', string="Sub Project Budget")
project_id = fields.Many2one(related="sub_project_budget_id.sub_project_id", string="Project")
site_id = fields.Many2one(related="project_id.construction_site_id", string="Site")
job_type_id = fields.Many2one('job.type', string="Work Type")
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
boq_qty = fields.Float(string="BOQ Qty")
additional_qty = fields.Float(string="Add. Qty", help="Additional Qty")
total_qty = fields.Float(string="Total Qty.", compute="compute_total_qty")
rate_analysis_id = fields.Many2one('rate.analysis', string="Rate Analysis")
price_per_qty = fields.Monetary(string="Price / Qty", compute="compute_rate_analysis_info", store=True)
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_rate_analysis_info", store=True)
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_rate_analysis_info", store=True)
budget = fields.Monetary(string="Total Budget Amount", compute="compute_rate_analysis_info", store=True)
# Spent
material_spent = fields.Monetary(string="Material Spent", compute="_compute_budget_calculation")
equipment_spent = fields.Monetary(string="Equipment Spent", compute="_compute_budget_calculation")
labour_spent = fields.Monetary(string="Labour Spent", compute="_compute_budget_calculation")
overhead_spent = fields.Monetary(string="Overhead Spent", compute="_compute_budget_calculation")
remaining_budget = fields.Monetary(string="Remaining Budget", compute="_compute_budget_calculation")
# Spent in Percentage
total_spent = fields.Float(string="Utilization(%)", compute="_compute_budget_calculation")
boq_used_qty = fields.Float(string="Used Qty", compute="_compute_budget_calculation")
@api.depends('boq_qty', 'additional_qty')
def compute_total_qty(self):
for rec in self:
rec.total_qty = rec.boq_qty + rec.additional_qty
@api.depends('rate_analysis_id', 'rate_analysis_id.total_amount', 'rate_analysis_id.untaxed_amount',
'rate_analysis_id.tax_amount', 'total_qty')
def compute_rate_analysis_info(self):
for rec in self:
untaxed_amount = 0.0
tax_amount = 0.0
price_per_qty = 0.0
budget = 0.0
if rec.rate_analysis_id:
price_per_qty = rec.rate_analysis_id.total_amount
untaxed_amount = rec.rate_analysis_id.untaxed_amount
tax_amount = rec.rate_analysis_id.tax_amount
budget = rec.total_qty * price_per_qty
rec.untaxed_amount = untaxed_amount
rec.tax_amount = tax_amount
rec.price_per_qty = price_per_qty
rec.budget = budget
@api.depends('project_id', 'sub_category_id', 'job_type_id', 'budget', 'total_qty')
def _compute_budget_calculation(self):
for rec in self:
budget_phase_ids = self.env['job.costing'].search(
[('project_id', '=', rec.project_id.id), ('activity_id', '=', rec.job_type_id.id)]).mapped('id')
domain = [('project_id', '=', rec.project_id.id), ('work_type_id', '=', rec.job_type_id.id),
('sub_category_id', '=', rec.sub_category_id.id), ('state', '=', 'complete'),
('job_sheet_id', 'in', budget_phase_ids)]
material_spent = sum(self.env['order.material.line'].search(domain).mapped('total_price'))
equipment_spent = sum(self.env['order.equipment.line'].search(domain).mapped('total_cost'))
labour_spent = sum(self.env['order.labour.line'].search(domain).mapped('sub_total'))
overhead_spent = sum(self.env['order.overhead.line'].search(domain).mapped('sub_total'))
remaining_budget = rec.budget - (material_spent + equipment_spent + labour_spent + overhead_spent)
total_spent = 100 - ((remaining_budget * 100 / rec.budget) if rec.budget > 0 else 100)
boq_used_qty = rec.total_qty - (
(remaining_budget * rec.total_qty / rec.budget) if rec.budget > 0 else rec.total_qty)
rec.equipment_spent = equipment_spent
rec.labour_spent = labour_spent
rec.overhead_spent = overhead_spent
rec.material_spent = material_spent
rec.remaining_budget = remaining_budget
rec.total_spent = round(total_spent, 1)
rec.boq_used_qty = boq_used_qty
@api.onchange('sub_category_id', 'job_type_id')
def _onchange_sub_category(self):
ids = []
for rec in self:
if not rec.job_type_id:
return
ids = rec.job_type_id.sub_category_ids.ids
return {'domain': {'sub_category_id': [('id', 'in', ids)]}}
def action_view_material_budget(self):
budget_phase_ids = self.env['job.costing'].search(
[('project_id', '=', self.project_id.id), ('activity_id', '=', self.job_type_id.id)]).mapped('id')
domain = [('project_id', '=', self.project_id.id), ('work_type_id', '=', self.job_type_id.id),
('sub_category_id', '=', self.sub_category_id.id), ('state', '=', 'complete'),
('job_sheet_id', 'in', budget_phase_ids)]
ids = self.env['order.material.line'].search(domain).mapped('id')
return {
'type': 'ir.actions.act_window',
'name': _('Material'),
'res_model': 'order.material.line',
'domain': [('id', 'in', ids)],
'context': {'create': False},
'view_mode': 'list',
'target': 'current'
}
def action_view_equipment_budget(self):
budget_phase_ids = self.env['job.costing'].search(
[('project_id', '=', self.project_id.id), ('activity_id', '=', self.job_type_id.id)]).mapped('id')
domain = [('project_id', '=', self.project_id.id), ('work_type_id', '=', self.job_type_id.id),
('sub_category_id', '=', self.sub_category_id.id), ('state', '=', 'complete'),
('job_sheet_id', 'in', budget_phase_ids)]
ids = self.env['order.equipment.line'].search(domain).mapped('id')
return {
'type': 'ir.actions.act_window',
'name': _('Equipment'),
'res_model': 'order.equipment.line',
'domain': [('id', 'in', ids)],
'context': {'create': False},
'view_mode': 'list',
'target': 'current'
}
def action_view_labour_budget(self):
budget_phase_ids = self.env['job.costing'].search(
[('project_id', '=', self.project_id.id), ('activity_id', '=', self.job_type_id.id)]).mapped('id')
domain = [('project_id', '=', self.project_id.id), ('work_type_id', '=', self.job_type_id.id),
('sub_category_id', '=', self.sub_category_id.id), ('state', '=', 'complete'),
('job_sheet_id', 'in', budget_phase_ids)]
ids = self.env['order.labour.line'].search(domain).mapped('id')
return {
'type': 'ir.actions.act_window',
'name': _('Labour'),
'res_model': 'order.labour.line',
'domain': [('id', 'in', ids)],
'context': {'create': False},
'view_mode': 'list',
'target': 'current'
}
def action_view_overhead_budget(self):
budget_phase_ids = self.env['job.costing'].search(
[('project_id', '=', self.project_id.id), ('activity_id', '=', self.job_type_id.id)]).mapped('id')
domain = [('project_id', '=', self.project_id.id), ('work_type_id', '=', self.job_type_id.id),
('sub_category_id', '=', self.sub_category_id.id), ('state', '=', 'complete'),
('job_sheet_id', 'in', budget_phase_ids)]
ids = self.env['order.overhead.line'].search(domain).mapped('id')
return {
'type': 'ir.actions.act_window',
'name': _('Overhead'),
'res_model': 'order.overhead.line',
'domain': [('id', 'in', ids)],
'context': {'create': False},
'view_mode': 'list',
'target': 'current'
}