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,10 @@
# -*- coding: utf-8 -*-
# Copyright 2020-Today TechKhedut.
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
from . import site_project
from . import warehouse
from . import requisition_reject
from . import import_material
from . import construction_budget
from . import project_wbs
from . import wbs_entries

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="budget_construction_view_form" model="ir.ui.view">
<field name="name">budget.construction.view.form</field>
<field name="model">budget.construction</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="name" required="1"/>
</group>
<group>
<field name="responsible_id" required="1"/>
</group>
</group>
<footer>
<button string="Create Budget" type="object" class="btn btn-primary"
name="action_create_sub_project_budget"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="budget_construction_action" model="ir.actions.act_window">
<field name="name">Construction Budget</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">budget.construction</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,42 @@
from odoo import api, fields, models, _
class BudgetConstruction(models.TransientModel):
_name = 'budget.construction'
_description = "Budget Construction"
name = fields.Char(string="Title")
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
string="Responsible")
def action_create_sub_project_budget(self):
active_id = self._context.get('active_id')
project_id = self.env['tk.construction.project'].browse(active_id)
if not project_id.boq_budget_ids:
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'message': "Please add BOQ Line to create Budget.",
'sticky': False,
}
}
return message
budget_id = self.env['sub.project.budget'].create({
'name': self.name,
'responsible_id': self.responsible_id.id,
'site_id': project_id.construction_site_id.id,
'sub_project_id': project_id.id,
'start_date': project_id.start_date,
'end_date': project_id.end_date
})
project_id.budget_id = budget_id.id
for data in project_id.boq_budget_ids:
self.env['project.budget'].create({
'sub_project_budget_id': budget_id.id,
'job_type_id': data.activity_id.id,
'sub_category_id': data.sub_activity_id.id,
'boq_qty': data.total_qty,
})

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Copyright 2020-Today TechKhedut.
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
class ImportMaterial(models.TransientModel):
_name = 'import.material'
_description = "Import Material for Material Requisition"
material_req_id = fields.Many2one('material.requisition')
template_id = fields.Many2one('construction.product.template', string="Template")
@api.model
def default_get(self, fields):
res = super(ImportMaterial, self).default_get(fields)
res['material_req_id'] = self._context.get('active_id')
return res
def action_import_material(self):
self.material_req_id.material_line_ids = [(5, 0, 0)]
for data in self.template_id.template_ids:
record = {
'material_id': data.product_id.id,
'name': data.name,
'material_req_id': self.material_req_id.id
}
self.env['material.requisition.line'].create(record)
class ImportMaterialSheet(models.Model):
_name = 'import.material.sheet'
_description = "Import Material from Sheet"
job_cost_id = fields.Many2one('job.costing', string="Jon Cost")
import_from = fields.Selection([('from_material', 'From Material Requisition'), ('from_template', 'From Template')],
string="Import From", default='from_material')
template_id = fields.Many2one('construction.product.template', string="Template")
material_req_id = fields.Many2one('material.requisition', string="Material Requisition")
@api.model
def default_get(self, fields):
res = super(ImportMaterialSheet, self).default_get(fields)
res['job_cost_id'] = self._context.get('active_id')
return res
def action_import_material(self):
self.job_cost_id.cost_material_ids = [(5, 0, 0)]
if self.import_from == 'from_material':
for data in self.material_req_id.material_line_ids:
record = {
'material_id': data.material_id.id,
'name': data.name,
'job_costing_id': self.job_cost_id.id,
'job_type_id': data.job_type_id.id,
'sub_category_id': data.sub_category_id.id,
}
self.env['cost.material.line'].create(record)
if self.import_from == 'from_template':
for data in self.template_id.template_ids:
record = {
'material_id': data.product_id.id,
'name': data.name,
'job_costing_id': self.job_cost_id.id
}
self.env['cost.material.line'].create(record)

View File

@@ -0,0 +1,63 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<data>
<!--For Material Requisition-->
<record id="import_material_view_form" model="ir.ui.view">
<field name="name">import.material.view.form</field>
<field name="model">import.material</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="material_req_id" invisible="1"/>
<field name="template_id"/>
</group>
</group>
<footer>
<button string="Import" type="object" class="btn btn-primary" name="action_import_material"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="import_material_action" model="ir.actions.act_window">
<field name="name">Import Material Line</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">import.material</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--For Project Phase-->
<record id="import_material_sheet_view_form" model="ir.ui.view">
<field name="name">import.material.sheet.view.form</field>
<field name="model">import.material.sheet</field>
<field name="arch" type="xml">
<form>
<h6>
<field name="import_from" widget="radio" options="{'horizontal':True}" required="1"/>
</h6>
<hr/>
<group>
<group>
<field name="job_cost_id" invisible="1"/>
<field name="material_req_id" required="import_from == 'from_material'" invisible="import_from != 'from_material'"/>
<field name="template_id" required="import_from == 'from_template'" invisible="import_from != 'from_template'"/>
</group>
</group>
<footer>
<button string="Import" type="object" class="btn btn-primary" name="action_import_material"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="import_material_sheet_action" model="ir.actions.act_window">
<field name="name">Import Material Line</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">import.material.sheet</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,36 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<data>
<record id="project_warehouse_view_form" model="ir.ui.view">
<field name="name">project.warehouse.view.form</field>
<field name="model">project.warehouse</field>
<field name="arch" type="xml">
<form>
<h3>
<field name="warehouse" required="1" widget="radio" options="{'horizontal':True}"/>
</h3>
<group>
<group>
<field name="warehouse_id" invisible="warehouse != 'link'" required="warehouse == 'link'"/>
<field name="warehouse_name" invisible="warehouse != 'create'" required="warehouse == 'create'"/>
<field name="warehouse_code" invisible="warehouse != 'create'" required="warehouse == 'create'"/>
</group>
</group>
<footer>
<button string="Create" type="object" class="btn btn-primary" name="action_submit_warehouse" invisible="warehouse != 'create'"/>
<button string="Use existing warehouse" type="object" class="btn btn-primary" name="action_submit_warehouse" invisible="warehouse != 'link'"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="project_warehouse_action" model="ir.actions.act_window">
<field name="name">Project Warehouse</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.warehouse</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,66 @@
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class ProjectWBS(models.TransientModel):
_name = 'project.wbs'
_description = "Sub Project WBS"
name = fields.Char(string="Title")
work_type_ids = fields.Many2many('job.type', string="Work Type Jobs", compute="compute_work_type_ids")
activity_id = fields.Many2one('job.type', string="Work Type", domain="[('id','in',work_type_ids)]")
start_date = fields.Date(string="Start Date")
end_date = fields.Date(string="End Date")
sub_project_id = fields.Many2one('tk.construction.project', string="Sub Project")
project_start_date = fields.Date(related="sub_project_id.start_date", store=True)
project_end_date = fields.Date(related="sub_project_id.end_date", store=True)
@api.model
def default_get(self, fields):
res = super(ProjectWBS, self).default_get(fields)
active_id = self._context.get('active_id')
res['sub_project_id'] = active_id
return res
def action_create_project_phase(self):
active_id = self._context.get('active_id')
sub_project_id = self.env['tk.construction.project'].browse(active_id)
phase_id = self.env['job.costing'].create({
'title': self.name,
'activity_id': self.activity_id.id,
'create_date': self.start_date,
'close_date': self.end_date,
'site_id': sub_project_id.construction_site_id.id,
'project_id': sub_project_id.id,
'department_id': sub_project_id.department_id.id,
'manager_ids': sub_project_id.manager_ids.ids,
'user_id': sub_project_id.user_id.id
})
return {
'type': 'ir.actions.act_window',
'name': _('Project Phase(WBS)'),
'res_model': 'job.costing',
'res_id': phase_id.id,
'view_mode': 'form',
'target': 'current'
}
@api.constrains('project_start_date', 'project_end_date', 'start_date')
def _check_wbs_start_date(self):
for record in self:
if record.project_start_date and record.project_end_date:
if not record.project_start_date <= record.start_date <= record.project_end_date:
raise ValidationError("Invalid start date. Must be within sub project start and end dates.")
@api.constrains('start_date', 'project_end_date', 'end_date')
def _check_wbs_end_date(self):
for record in self:
if record.start_date and record.project_end_date:
if not record.project_end_date >= record.end_date > record.start_date:
raise ValidationError(
"Invalid end date. Must be within start date and sub project end date.")
@api.depends('activity_id', 'sub_project_id')
def compute_work_type_ids(self):
ids = self.sub_project_id.boq_budget_ids.mapped('activity_id').mapped('id')
self.work_type_ids = ids

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="project_wbs_view_form" model="ir.ui.view">
<field name="name">project.wbs.view.form</field>
<field name="model">project.wbs</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="name" required="1"/>
<field name="start_date" required="1"/>
<field name="sub_project_id" invisible="1"/>
</group>
<group>
<field name="work_type_ids" invisible="1"/>
<field name="activity_id" required="1"/>
<field name="end_date" required="1"/>
<field name="project_start_date" invisible="1"/>
<field name="project_end_date" invisible="1"/>
</group>
</group>
<footer>
<button string="Create Phase (WBS)" type="object" class="btn btn-primary"
name="action_create_project_phase"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="project_phase_action" model="ir.actions.act_window">
<field name="name">Project Phase(WBS)</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">project.wbs</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,20 @@
# -*- 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 RequisitionReject(models.TransientModel):
_name = 'requisition.reject'
_description = "Requisition Reject Reason"
name = fields.Char(default="Material Requisition Reason")
reject_reason = fields.Text(string="Reject Reason")
allow_resubmit = fields.Boolean(string="Allow Resubmit")
def action_reject_requisition(self):
active_id = self._context.get('active_id')
material_req_id = self.env['material.requisition'].browse(active_id)
material_req_id.reject_reason = self.reject_reason
material_req_id.stage = 'reject'
material_req_id.allow_resubmit = self.allow_resubmit

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="requisition_reject_view_form" model="ir.ui.view">
<field name="name">requisition.reject.view.form</field>
<field name="model">requisition.reject</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="allow_resubmit" widget="boolean_toggle"/>
</group>
</group>
<br/>
<h5>
Reject Reason
</h5>
<hr/>
<field name="reject_reason" required="1"/>
<footer>
<button string="Reject Requisition" type="object" class="btn btn-primary"
name="action_reject_requisition"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="requisition_reject_action" model="ir.actions.act_window">
<field name="name">Requisition Reject Reason</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">requisition.reject</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="requisition_transfer_view_form" model="ir.ui.view">
<field name="name">requisition.transfer.view.form</field>
<field name="model">requisition.transfer</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="site_id" readonly="1" force_save="1"/>
<field name="project_id" required="1"/>
<field name="job_sheet_id" required="1"/>
</group>
</group>
<group>
<field name="material_req_ids" widget="many2many_tags" required="1"/>
</group>
<footer>
<button string="Add Material Requisition" type="object" class="btn btn-primary"
name="action_requisition_transfer"/>
<button string="Cancel" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="requisition_transfer_action" model="ir.actions.act_window">
<field name="name">Requisition Transfer</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">requisition.transfer</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Copyright 2020-Today TechKhedut.
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
class SiteProject(models.TransientModel):
_name = 'site.project'
_description = "Site Project"
name = fields.Char(string="Name")
def action_create_construction_project(self):
active_id = self._context.get('active_id')
site_id = self.env['tk.construction.site'].browse(active_id)
data = {
'name': self.name,
'construction_site_id': site_id.id,
'start_date': site_id.start_date,
'end_date': site_id.end_date,
'zip': site_id.zip,
'street': site_id.street,
'street2': site_id.street2,
'city': site_id.city,
'state_id': site_id.state_id.id,
'country_id': site_id.country_id.id,
'longitude': site_id.longitude,
'latitude': site_id.latitude
}
project_id = self.env['tk.construction.project'].create(data)
return {
'type': 'ir.actions.act_window',
'name': _('Project'),
'res_model': 'tk.construction.project',
'res_id': project_id.id,
'view_mode': 'form',
'target': 'current'
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="site_project_form_view" model="ir.ui.view">
<field name="name">site.project.form.view</field>
<field name="model">site.project</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="name" required="1" placeholer="Title"/>
</group>
</group>
<footer>
<button string="Create Sub Project" type="object" class="btn btn-outline-success"
name="action_create_construction_project"/>
<button string="Cancel" special="cancel" class="btn btn-outline-danger"/>
</footer>
</form>
</field>
</record>
<record id="construction_site_project_action" model="ir.actions.act_window">
<field name="name">Construction Project</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">site.project</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Copyright 2020-Today TechKhedut.
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class ProjectWarehouse(models.TransientModel):
_name = 'project.warehouse'
_description = "Project Warehouse"
_rec_name = 'warehouse'
warehouse = fields.Selection([('create', 'Create new warehouse'), ('link', 'Use existing warehouse')],
string=" ")
warehouse_name = fields.Char(string="Warehouse Name")
warehouse_code = fields.Char(string="Warehouse Code", size=5)
warehouse_id = fields.Many2one('stock.warehouse', string="Warehouse")
def action_submit_warehouse(self):
active_id = self._context.get('active_id')
project_id = self.env['tk.construction.project'].browse(active_id)
if self.warehouse == 'create':
parent_location_id = self.env['stock.location'].create({
'name': self.warehouse_code,
'usage': 'view'
})
location_id = self.env['stock.location'].create({
'location_id': parent_location_id.id,
'name': 'Stock',
'usage': 'internal'
})
data = {
'code': self.warehouse_code,
'name': self.warehouse_name,
'project_id': project_id.id,
'lot_stock_id': location_id.id,
'view_location_id': parent_location_id.id,
'delivery_steps': 'ship_only',
'reception_steps': 'one_step',
'company_id': self.env.company.id,
}
warehouse_id = self.env['stock.warehouse'].create(data)
project_id.warehouse_id = warehouse_id.id
else:
project_id.warehouse_id = self.warehouse_id.id

View File

@@ -0,0 +1,102 @@
from odoo import api, fields, models, _
class ProjectWBSEntries(models.TransientModel):
_name = 'wbs.entries'
_description = "WBS Entries"
boq_qty = fields.Float(string="BOQ Qty", default=1)
activity_id = fields.Many2one('job.type', string="Work Type")
sub_work_type_ids = fields.Many2many('job.sub.category', string="Work Type Jobs",
compute="compute_sub_work_type_ids")
sub_work_type_id = fields.Many2one('job.sub.category',
string="Work Sub Type", domain="[('id','in',sub_work_type_ids)]")
@api.model
def default_get(self, fields):
res = super(ProjectWBSEntries, self).default_get(fields)
active_id = self._context.get('active_id')
phase_id = self.env['job.costing'].browse(active_id)
res['activity_id'] = phase_id.activity_id.id
return res
@api.depends('activity_id')
def compute_sub_work_type_ids(self):
active_id = self._context.get('active_id')
phase_id = self.env['job.costing'].browse(active_id)
ids = self.env['boq.budget'].sudo().search(
[('project_id', '=', phase_id.project_id.id), ('activity_id', '=', self.activity_id.id)]).mapped(
'sub_activity_id').mapped('id')
work_type_ids = [x for x in ids if x not in phase_id.sub_work_type_ids.ids] + [x for x in
phase_id.sub_work_type_ids.ids if
x not in ids]
self.sub_work_type_ids = work_type_ids
def action_create_wbs_entries(self):
active_id = self._context.get('active_id')
phase_id = self.env['job.costing'].browse(active_id)
domain = [('job_type_id', '=', self.activity_id.id), ('sub_category_id', '=', self.sub_work_type_id.id),
('sub_project_budget_id', '=', phase_id.project_id.budget_id.id)]
budget_record_id = self.env['project.budget'].search(domain, limit=1)
if not budget_record_id:
msg = "Budget Entry with work type " + str(self.activity_id.name) + " and sub work type " + str(
self.sub_work_type_id.name) + " not found."
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'title': _('Not Found !'),
'message': msg,
'sticky': False,
}
}
return message
if budget_record_id:
work_sub_type_ids = phase_id.sub_work_type_ids.ids
work_sub_type_ids.append(self.sub_work_type_id.id)
phase_id.write({'sub_work_type_ids': work_sub_type_ids})
for data in budget_record_id.rate_analysis_id.material_analysis_ids:
self.env['cost.material.line'].create({
'sub_category_id': self.sub_work_type_id.id,
'material_id': data.product_id.id,
'name': data.name,
'qty': data.qty * self.boq_qty,
'cost': data.price,
'tax_id': data.tax_id.id,
'boq_per_qty': data.qty,
'job_costing_id': phase_id.id
})
for data in budget_record_id.rate_analysis_id.equipment_analysis_ids:
self.env['cost.equipment.line'].create({
'sub_category_id': self.sub_work_type_id.id,
'equipment_id': data.product_id.id,
'name': data.name,
'qty': data.qty * self.boq_qty,
'cost': data.price,
'tax_id': data.tax_id.id,
'boq_per_qty': data.qty,
'job_costing_id': phase_id.id
})
for data in budget_record_id.rate_analysis_id.labour_analysis_ids:
self.env['cost.labour.line'].create({
'sub_category_id': self.sub_work_type_id.id,
'product_id': data.product_id.id,
'name': data.name,
'hours': data.qty * self.boq_qty,
'cost': data.price,
'tax_id': data.tax_id.id,
'boq_per_qty': data.qty,
'job_costing_id': phase_id.id
})
for data in budget_record_id.rate_analysis_id.overhead_analysis_ids:
self.env['cost.overhead.line'].create({
'sub_category_id': self.sub_work_type_id.id,
'product_id': data.product_id.id,
'name': data.name,
'qty': data.qty * self.boq_qty,
'cost': data.price,
'tax_id': data.tax_id.id,
'boq_per_qty': data.qty,
'job_costing_id': phase_id.id
})

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="wbs_entries_form_view" model="ir.ui.view">
<field name="name">wbs.entries.form.view</field>
<field name="model">wbs.entries</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="boq_qty" required="1"/>
</group>
<group>
<field name="activity_id" required="1" force_save="1" readonly="1"/>
<field name="sub_work_type_ids" widget="many2many_tags" invisible="1"/>
<field name="sub_work_type_id" required="1"/>
</group>
</group>
<footer>
<button string="Create Entries" type="object" class="btn btn-outline-success"
name="action_create_wbs_entries"/>
<button string="Cancel" special="cancel" class="btn btn-outline-danger"/>
</footer>
</form>
</field>
</record>
<record id="wbs_entries_action" model="ir.actions.act_window">
<field name="name">Project Phase(WBS) Entries</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">wbs.entries</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>