Tower: upload tk_construction_management 18.0.2.0.8 (was 18.0.2.0.8, via marketplace)

This commit is contained in:
2026-05-08 19:20:12 +00:00
parent c412640ca2
commit 63c62699f5
174 changed files with 44759 additions and 0 deletions

View File

@@ -0,0 +1,748 @@
# -*- coding: utf-8 -*-
# Copyright 2020-Today TechKhedut.
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
from odoo import fields, api, models, _
class JobOrder(models.Model):
_name = 'job.order'
_description = "Work Order"
_inherit = ['mail.thread', 'mail.activity.mixin']
name = fields.Char(string='Sequence', required=True,
readonly=True, default=lambda self: _('New'))
title = fields.Char(string="Title")
stage = fields.Selection([('draft', 'Draft'),
('waiting_approval', 'Waiting for approval'),
('approved', 'Approved'),
('complete', 'Complete'),
('cancel', 'Cancel')], default='draft', tracking=True)
is_user = fields.Boolean(compute="_compute_user_role")
is_material_requisition = fields.Boolean(compute="compute_material_req")
# Project Details
site_id = fields.Many2one('tk.construction.site', string="Project")
project_id = fields.Many2one(
'tk.construction.project', string="Sub Project")
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')
warehouse_id = fields.Many2one(
related="project_id.warehouse_id", string="Warehouse")
# Other Details
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
string="Created By")
start_date = fields.Date(string="Start Date")
end_date = fields.Date(string="End Date")
# Work Type
work_type_id = fields.Many2one('job.type', string="Work Type")
job_sheet_id = fields.Many2one(
'job.costing', string="Project Phase(WBS)", domain="[('project_id','=',project_id)]")
# Department
department_id = fields.Many2one(
'construction.department', string="Department")
manager_ids = fields.Many2many('res.users', string="Manager")
user_id = fields.Many2one('res.users', string="Responsible")
# Material
material_req_id = fields.Many2one(
'material.requisition', string="Material Request")
state = fields.Selection([('draft', 'Draft'),
('material_request', 'Material Request'),
('material_arrive', 'Material Arrived'),
('in_progress', 'In Progress'),
('complete', 'Complete'),
('cancel', 'Cancel')], default='draft')
# Task Details
project_project_id = fields.Many2one(
related="project_id.project_id", string="Project ")
task_name = fields.Char(string="Task Title")
task_desc = fields.Text(string="Description")
assignees_ids = fields.Many2many('res.users', 'construction_assign_dep_rel', 'assign_id', 'dep_id',
string="Assignees")
task_id = fields.Many2one('project.task', string="Task")
# One2 many
material_order_ids = fields.One2many('order.material.line', 'job_order_id')
equipment_order_ids = fields.One2many(
'order.equipment.line', 'job_order_id')
labour_order_ids = fields.One2many('order.labour.line', 'job_order_id')
overhead_order_ids = fields.One2many('order.overhead.line', 'job_order_id')
# Total
equipment_total_cost = fields.Monetary(
string="Equipment Cost", compute="_compute_total")
labour_total_cost = fields.Monetary(
string="Labour Cost", compute="_compute_total")
overhead_total_cost = fields.Monetary(
string="Overhead Cost", compute="_compute_total")
# Consume Order and Subcontract
consume_order_ids = fields.One2many('material.consume', 'job_order_id')
equipment_contract_ids = fields.One2many(
'equipment.subcontract', 'job_order_id')
labour_contract_ids = fields.One2many('labour.subcontract', 'job_order_id')
overhead_contract_ids = fields.One2many(
'overhead.subcontract', 'job_order_id')
# Count
po_count = fields.Integer(string="Purchase Count",
compute="_compute_count")
equip_po_count = fields.Integer(
string="Purchase Equip Count", compute="_compute_count")
labour_po_count = fields.Integer(
string="Purchase Labour Count", compute="_compute_count")
overhead_po_count = fields.Integer(
string="Purchase Overhead Count", compute="_compute_count")
bill_count = fields.Integer(string="Bill Count", compute="_compute_count")
delivery_count = fields.Integer(
string="Delivery Count", compute="_compute_count")
equip_contract_count = fields.Integer(
string="Equipment Contract Count", compute="_compute_count")
labour_contract_count = fields.Integer(
string="Labour Contract Count", compute="_compute_count")
overhead_contract_count = fields.Integer(
string="Overhead Contract Count", compute="_compute_count")
material_consume_count = fields.Integer(
string="Material Consume Count", compute="_compute_count")
# Create, Write, Unlink, Constrain
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('name', _('New')) == _('New') and vals.get('project_id'):
project_id = self.env['tk.construction.project'].browse(
vals.get('project_id'))
vals['name'] = str(project_id.code) + "/" + (
self.env['ir.sequence'].next_by_code('job.order') or _('New'))
res = super(JobOrder, self).create(vals_list)
for rec in res:
data = {
'name': rec.task_name,
'description': rec.task_desc,
'user_ids': rec.assignees_ids.ids,
'project_id': rec.project_project_id.id,
'job_order_id': rec.id,
'con_project_id': rec.project_id.id,
'date_deadline': rec.end_date
}
task_id = self.env['project.task'].create(data)
rec.task_id = task_id.id
return res
def name_get(self):
data = []
for rec in self:
data.append((rec.id, '%s - %s' % (rec.name, rec.title)))
return data
# Compute
def _compute_count(self):
for rec in self:
rec.po_count = self.env['purchase.order'].search_count(
[('job_order_id', '=', rec.id)])
rec.equip_po_count = self.env['purchase.order'].search_count(
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'equipment')])
rec.labour_po_count = self.env['purchase.order'].search_count(
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'labour')])
rec.overhead_po_count = self.env['purchase.order'].search_count(
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'overhead')])
rec.bill_count = self.env['account.move'].search_count(
[('job_order_id', '=', rec.id)])
rec.delivery_count = self.env['stock.picking'].search_count(
[('consume_order_id', '=', rec.id)])
rec.equip_contract_count = self.env['equipment.subcontract'].search_count(
[('job_order_id', '=', rec.id)])
rec.labour_contract_count = self.env['labour.subcontract'].search_count(
[('job_order_id', '=', rec.id)])
rec.overhead_contract_count = self.env['overhead.subcontract'].search_count(
[('job_order_id', '=', rec.id)])
rec.material_consume_count = self.env['material.consume'].search_count(
[('job_order_id', '=', rec.id)])
def _compute_user_role(self):
if self.env.user.has_group('tk_construction_management.advance_construction_user'):
self.is_user = True
else:
self.is_user = False
@api.depends('material_order_ids')
def compute_material_req(self):
for rec in self:
material_req = False
for data in rec.material_order_ids:
if data.qty != 0:
material_req = True
break
rec.is_material_requisition = material_req
@api.depends('material_order_ids', 'equipment_order_ids', 'labour_order_ids', 'overhead_order_ids')
def _compute_total(self):
equipment, labour, overhead = 0, 0, 0
for rec in self:
for data in rec.equipment_order_ids:
equipment = equipment + data.total_cost
for data in rec.labour_order_ids:
labour = labour + data.sub_total
for data in rec.overhead_order_ids:
overhead = overhead + data.sub_total
rec.equipment_total_cost = equipment
rec.labour_total_cost = labour
rec.overhead_total_cost = overhead
# Smart Button
def action_view_purchase_order(self):
return {
'type': 'ir.actions.act_window',
'name': _('Purchase Order'),
'res_model': 'purchase.order',
'domain': [('job_order_id', '=', self.id)],
'context': {
'default_job_order_id': self.id,
'group_by': 'purchase_order'
},
'view_mode': 'list,form,kanban',
'target': 'current'
}
def action_view_bills(self):
return {
'type': 'ir.actions.act_window',
'name': _('Bills'),
'res_model': 'account.move',
'domain': [('job_order_id', '=', self.id)],
'context': {
'default_job_order_id': self.id
},
'view_mode': 'list,form,kanban',
'target': 'current'
}
def action_view_delivery_order(self):
return {
'type': 'ir.actions.act_window',
'name': _('Delivery Order'),
'res_model': 'stock.picking',
'domain': [('consume_order_id', '=', self.id)],
'context': {
'default_': self.id
},
'view_mode': 'list,form,kanban',
'target': 'current'
}
def action_view_purchase_order_equipment(self):
return {
'type': 'ir.actions.act_window',
'name': _('Equipment PO'),
'res_model': 'purchase.order',
'domain': [('job_order_id', '=', self.id), ('purchase_order', '=', 'equipment')],
'context': {
'default_job_order_id': self.id,
'purchase_order': 'equipment'
},
'view_mode': 'list,form,kanban',
'target': 'current'
}
def action_view_contract_equipment(self):
return {
'type': 'ir.actions.act_window',
'name': _('Equipment Contract'),
'res_model': 'equipment.subcontract',
'domain': [('job_order_id', '=', self.id)],
'context': {
'create': False,
},
'view_mode': 'list,form',
'target': 'current'
}
def action_view_contract_labour(self):
return {
'type': 'ir.actions.act_window',
'name': _('Labour Contract'),
'res_model': 'labour.subcontract',
'domain': [('job_order_id', '=', self.id)],
'context': {
'create': False,
},
'view_mode': 'list,form',
'target': 'current'
}
def action_view_contract_overhead(self):
return {
'type': 'ir.actions.act_window',
'name': _('Overhead Contract'),
'res_model': 'overhead.subcontract',
'domain': [('job_order_id', '=', self.id)],
'context': {
'create': False,
},
'view_mode': 'list,form',
'target': 'current'
}
def action_view_material_consume_order(self):
return {
'type': 'ir.actions.act_window',
'name': _('Consume Orders'),
'res_model': 'material.consume',
'domain': [('job_order_id', '=', self.id)],
'context': {
'create': False,
},
'view_mode': 'list,form',
'target': 'current'
}
# Button
def action_request_material(self):
if not self.material_order_ids:
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'title': _('Add Material'),
'message': "Add Material to Create Material Request",
'sticky': False,
}
}
return message
material_request_id = self.env['material.requisition'].create({
'title': self.title,
'company_id': self.company_id.id,
'desc': self.task_desc,
'project_id': self.project_id.id,
'department_id': self.department_id.id,
'manager_ids': self.manager_ids.ids,
'user_id': self.user_id.id,
'work_order_id': self.id,
'work_type_id': self.work_type_id.id,
'site_id': self.site_id.id
})
for data in self.material_order_ids:
if data.qty > 0:
record = {
'material_id': data.material_id.id,
'name': data.name,
'qty': data.qty,
'warehouse_id': self.warehouse_id.id,
'material_req_id': material_request_id.id,
'sub_category_id': data.sub_category_id.id,
'job_sheet_id': self.job_sheet_id.id,
}
req_line_id = self.env['material.requisition.line'].create(
record)
data.material_req_line_id = req_line_id.id
self.material_req_id = material_request_id.id
self.state = 'material_request'
return {
'type': 'ir.actions.act_window',
'name': _('Material Request'),
'res_model': 'material.requisition',
'res_id': material_request_id.id,
'view_mode': 'form',
'target': 'current'
}
def action_create_equipment_subcontract(self):
for rec in self.equipment_order_ids:
if not rec.equip_sub_contract_id:
data = {
'name': rec.desc,
'equipment_id': rec.equipment_id.id,
'cost_type': rec.cost_type,
'qty': rec.qty,
'remain_qty': rec.qty,
'cost': rec.cost,
'total_cost': rec.total_cost,
'vendor_id': rec.vendor_id.id,
'job_type_id': self.work_type_id.id,
'sub_category_id': rec.sub_category_id.id,
'job_order_id': self.id,
'remaining_amount': rec.total_cost,
'manager_ids': self.manager_ids.ids,
'tax_id': rec.tax_id.id
}
equip_line_id = self.env['equipment.subcontract'].create(data)
rec.equip_sub_contract_id = equip_line_id.id
def action_create_labour_subcontract(self):
for rec in self.labour_order_ids:
if not rec.labour_sub_contract_id:
data = {
'name': rec.name,
'product_id': rec.product_id.id,
'hours': rec.hours,
'remain_hours': rec.hours,
'cost': rec.cost,
'total_cost': rec.sub_total,
'vendor_id': rec.vendor_id.id,
'job_type_id': self.work_type_id.id,
'sub_category_id': rec.sub_category_id.id,
'job_order_id': self.id,
'remaining_amount': rec.sub_total,
'manager_ids': self.manager_ids.ids,
'tax_id': rec.tax_id.id
}
labour_line_id = self.env['labour.subcontract'].create(data)
rec.labour_sub_contract_id = labour_line_id.id
def action_create_overhead_subcontract(self):
for rec in self.overhead_order_ids:
if not rec.overhead_sub_contract_id:
data = {
'name': rec.name,
'product_id': rec.product_id.id,
'qty': rec.qty,
'remain_qty': rec.qty,
'cost': rec.cost,
'total_cost': rec.sub_total,
'vendor_id': rec.vendor_id.id,
'job_type_id': self.work_type_id.id,
'sub_category_id': rec.sub_category_id.id,
'job_order_id': self.id,
'remaining_amount': rec.sub_total,
'manager_ids': self.manager_ids.ids,
'tax_id': rec.tax_id.id
}
overhead_line_id = self.env['overhead.subcontract'].create(
data)
rec.overhead_sub_contract_id = overhead_line_id.id
def action_create_material_consume_order(self):
remain_qty = False
for rec in self.material_order_ids:
if not rec.remain_qty == 0:
remain_qty = True
break
if remain_qty:
consume_order_id = self.env['material.consume'].create({
'job_order_id': self.id,
'manager_ids': self.manager_ids.ids,
'warehouse_id': self.warehouse_id.id
})
for rec in self.material_order_ids:
if rec.remain_qty > 0:
self.env['material.consume.line'].create({
'material_id': rec.material_id.id,
'name': rec.name,
'qty': rec.remain_qty,
'material_consume_id': consume_order_id.id,
'material_line_id': rec.id
})
return {
'type': 'ir.actions.act_window',
'name': _('Material Consume Order'),
'res_model': 'material.consume',
'res_id': consume_order_id.id,
'view_mode': 'form',
'target': 'new'
}
if not remain_qty:
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'message': "No Material consumption orders found !",
'sticky': False,
}
}
return message
def action_department_approval(self):
self.stage = 'waiting_approval'
def action_department_approval_approved(self):
self.stage = 'approved'
def action_department_approval_reject(self):
self.stage = 'cancel'
def action_draft_order(self):
self.stage = 'draft'
def action_cancel_order(self):
self.stage = 'cancel'
def action_in_progress(self):
self.state = 'in_progress'
def action_reset_draft(self):
self.state = 'draft'
def action_complete_work_order(self):
material = True
equipment = True
labour = True
overhead = True
for data in self.equipment_contract_ids:
if data.stage != 'done':
equipment = False
break
for data in self.labour_contract_ids:
if data.stage != 'done':
labour = False
break
for data in self.overhead_contract_ids:
if data.stage != 'done':
overhead = False
break
for data in self.consume_order_ids:
if data.qc_status != 'approve':
material = False
break
if not material or not equipment or not labour or not overhead:
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'message': _(
"Please Complete Consume Order / Equipment Subcontract / Labour Subcontract / Overhead Subcontract to complete work order"),
'sticky': False,
}
}
return message
self.state = 'complete'
class OrderMaterialLine(models.Model):
_name = 'order.material.line'
_description = "Job order material line"
material_id = fields.Many2one(
'product.product', string="Material", domain="[('is_material','=',True)]")
name = fields.Char(string="Description")
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')
qty = fields.Integer(string="Qty.", default=1)
remain_qty = fields.Integer(string="Remain Qty.", default=1)
usage_qty = fields.Integer(string="Used Qty.")
uom_id = fields.Many2one(related="material_id.uom_po_id", string="UOM")
job_order_id = fields.Many2one('job.order', string="Work Order")
sub_category_id = fields.Many2one(
'job.sub.category', string="Work Sub Type")
price = fields.Monetary(string="Price")
total_price = fields.Monetary(
string="Total Price", compute="_compute_total_price")
material_req_line_id = fields.Many2one('material.requisition.line')
phase_forcast_qty = fields.Float()
tax_id = fields.Many2one('account.tax', string="Taxes")
# Budget Field
job_sheet_id = fields.Many2one(
related="job_order_id.job_sheet_id", store=True)
project_id = fields.Many2one(related="job_order_id.project_id", store=True)
work_type_id = fields.Many2one(
related="job_order_id.work_type_id", store=True)
state = fields.Selection(related="job_order_id.state", store=True)
@api.onchange('material_id')
def _onchange_product_desc(self):
for rec in self:
rec.name = rec.material_id.name
rec.price = rec.material_id.standard_price
@api.depends('qty', 'price', 'tax_id')
def _compute_total_price(self):
for rec in self:
total = 0.0
if rec.qty and rec.price:
total = rec.qty * rec.price
if rec.tax_id:
tax_amount = rec.tax_id.amount * total / 100
total = tax_amount + total
rec.total_price = total
@api.onchange('qty')
def _onchange_remain_qty(self):
for rec in self:
rec.remain_qty = rec.qty
@api.depends('warehouse_id', 'material_id')
def _compute_forcast_qty(self):
for rec in self:
qty = 0.0
if rec.material_id:
qty = rec.material_id.with_context(
warehouse=rec.warehouse_id.id).virtual_available
rec.forcast_qty = qty
class OrderEquipmentLine(models.Model):
_name = 'order.equipment.line'
_description = 'Construction Work Order Equipment Line'
equipment_id = fields.Many2one(
'product.product', string="Equipment", domain="[('is_equipment','=',True)]")
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')
cost_type = fields.Selection(
[('depreciation_cost', 'Depreciation Cost'), ('investment_cost', 'Investment/Interest Cost'),
('tax', 'Tax'), ('rent', 'Rent'), ('other', 'Other')], string="Type", default='rent')
desc = fields.Text(string='Description')
qty = fields.Integer(string="Qty.", default=1)
cost = fields.Monetary(string="Estimation Cost")
total_cost = fields.Monetary(
string="Total Cost", compute="_compute_total_cost", store=True)
vendor_id = fields.Many2one('res.partner', string="Vendor")
job_order_id = fields.Many2one('job.order', string="Work Order")
purchase_order_id = fields.Many2one(
'purchase.order', string="Purchase Order")
is_po_create = fields.Boolean()
sub_category_id = fields.Many2one(
'job.sub.category', string="Work Sub Type")
equip_sub_contract_id = fields.Many2one(
'equipment.subcontract', string="Equip Subcontract")
phase_forcast_qty = fields.Float()
tax_id = fields.Many2one('account.tax', string="Taxes")
# Budget Field
job_sheet_id = fields.Many2one(
related="job_order_id.job_sheet_id", store=True)
project_id = fields.Many2one(related="job_order_id.project_id", store=True)
work_type_id = fields.Many2one(
related="job_order_id.work_type_id", store=True)
state = fields.Selection(related="job_order_id.state", store=True)
@api.depends('qty', 'cost', 'tax_id')
def _compute_total_cost(self):
for rec in self:
total = 0.0
if rec.cost and rec.qty:
total = rec.cost * rec.qty
if rec.tax_id:
tax_amount = rec.tax_id.amount * total / 100
total = tax_amount + total
rec.total_cost = total
@api.onchange('equipment_id')
def _onchange_product_desc(self):
for rec in self:
rec.desc = rec.equipment_id.name
rec.cost = rec.equipment_id.standard_price
class OrderLabourLine(models.Model):
_name = 'order.labour.line'
_description = "Work Order Labour Line"
job_order_id = fields.Many2one('job.order', string="Work Order")
product_id = fields.Many2one(
'product.product', string="Product", domain="[('is_labour','=',True)]")
name = fields.Char(string="Description")
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')
hours = fields.Float(string="Hours")
remain_hours = fields.Float(
related='labour_sub_contract_id.remain_hours', string="Remain Hours")
cost = fields.Monetary(string="Cost / Hour")
sub_total = fields.Monetary(
string="Sub Total", compute="_compute_sub_total", store=True)
vendor_id = fields.Many2one('res.partner', string="Vendor")
is_bill_created = fields.Boolean()
purchase_order_id = fields.Many2one(
'purchase.order', string="Purchase Order")
sub_category_id = fields.Many2one(
'job.sub.category', string="Work Sub Type")
labour_sub_contract_id = fields.Many2one(
'labour.subcontract', string="Subcontract")
phase_forcast_qty = fields.Float()
tax_id = fields.Many2one('account.tax', string="Taxes")
# Budget Field
job_sheet_id = fields.Many2one(
related="job_order_id.job_sheet_id", store=True)
project_id = fields.Many2one(related="job_order_id.project_id", store=True)
work_type_id = fields.Many2one(
related="job_order_id.work_type_id", store=True)
state = fields.Selection(related="job_order_id.state", store=True)
@api.onchange('product_id')
def _onchange_product_desc(self):
for rec in self:
rec.name = rec.product_id.name
rec.cost = rec.product_id.standard_price
@api.depends('cost', 'hours', 'tax_id')
def _compute_sub_total(self):
for rec in self:
total = 0.0
if rec.cost and rec.hours:
total = rec.cost * rec.hours
if rec.tax_id:
tax_amount = rec.tax_id.amount * total / 100
total = tax_amount + total
rec.sub_total = total
class OrderOverheadLine(models.Model):
_name = 'order.overhead.line'
_description = "Work Order Overhead Line"
job_order_id = fields.Many2one('job.order', string="Work Order")
product_id = fields.Many2one(
'product.product', string="Product", domain="[('is_overhead','=',True)]")
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')
name = fields.Char(string="Description")
qty = fields.Integer(string="Qty.", default=1)
uom_id = fields.Many2one(
related="product_id.uom_po_id", string="Unit of Measure")
cost = fields.Monetary(string="Cost / Unit")
sub_total = fields.Monetary(
string="Sub Total", compute="_compute_sub_total", store=True)
vendor_id = fields.Many2one('res.partner', string="Vendor")
is_bill_created = fields.Boolean()
purchase_order_id = fields.Many2one(
'purchase.order', string="Purchase Order")
sub_category_id = fields.Many2one(
'job.sub.category', string="Work Sub Type")
overhead_sub_contract_id = fields.Many2one(
'overhead.subcontract', string="Subcontract")
phase_forcast_qty = fields.Float()
tax_id = fields.Many2one('account.tax', string="Taxes")
# Budget Field
job_sheet_id = fields.Many2one(
related="job_order_id.job_sheet_id", store=True)
project_id = fields.Many2one(related="job_order_id.project_id", store=True)
work_type_id = fields.Many2one(
related="job_order_id.work_type_id", store=True)
state = fields.Selection(related="job_order_id.state", store=True)
@api.depends('cost', 'qty', 'tax_id')
def _compute_sub_total(self):
for rec in self:
total = 0.0
if rec.cost and rec.qty:
total = rec.cost * rec.qty
if rec.tax_id:
tax_amount = rec.tax_id.amount * total / 100
total = tax_amount + total
rec.sub_total = total
@api.onchange('product_id')
def _onchange_product_desc(self):
for rec in self:
rec.name = rec.product_id.name
rec.cost = rec.product_id.standard_price