Tower: upload tk_construction_management 18.0.2.0.8 (was 18.0.2.0.8, via marketplace)
BIN
addons/tk_construction_management/.DS_Store
vendored
Normal file
5
addons/tk_construction_management/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
from . import models
|
||||
from . import wizard
|
||||
90
addons/tk_construction_management/__manifest__.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': "Construction Management | Job Costing | BOQ | Work Orders | RA Billing | Material Purchase Requisition | Construction Sub Contracting | Job Order | Job Sheet | Construction Budget",
|
||||
'description': """
|
||||
- Construction Management
|
||||
- BOQ
|
||||
- Rate Analysis
|
||||
- RA Billing
|
||||
- Job Costing
|
||||
- Job Cost Sheet
|
||||
- Job Contract
|
||||
- Project Site
|
||||
- Construction Project Management
|
||||
- Job Budget Management
|
||||
- Construction Scrape Management
|
||||
- Construction Waste Management
|
||||
""",
|
||||
'summary': """Advance Construction Management""",
|
||||
'version': "18.0.2.0.8",
|
||||
'author': 'TechKhedut Inc.',
|
||||
'company': 'TechKhedut Inc.',
|
||||
'maintainer': 'TechKhedut Inc.',
|
||||
'website': "https://www.techkhedut.com",
|
||||
'support': "info@techkhedut.com",
|
||||
'category': 'Industry',
|
||||
'depends': ['mail', 'contacts', 'stock', 'hr', 'purchase', 'project', 'hr_timesheet'],
|
||||
'data': [
|
||||
# security
|
||||
'security/groups.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/security.xml',
|
||||
# Data
|
||||
'data/sequence.xml',
|
||||
'data/construction_data.xml',
|
||||
'data/construction_report_paperformat.xml',
|
||||
# wizard
|
||||
'wizard/site_project_view.xml',
|
||||
'wizard/project_warehouse_view.xml',
|
||||
'wizard/requisition_reject_view.xml',
|
||||
'wizard/import_material_view.xml',
|
||||
'wizard/budget_construction_view.xml',
|
||||
'wizard/project_wbs_view.xml',
|
||||
'wizard/wbs_entries_view.xml',
|
||||
# views
|
||||
'views/assets.xml',
|
||||
'views/construction_site_view.xml',
|
||||
'views/res_partner_view.xml',
|
||||
'views/document_view.xml',
|
||||
'views/construction_configuration_view.xml',
|
||||
'views/construction_project_view.xml',
|
||||
'views/construction_department_view.xml',
|
||||
'views/purchase_stock_inherit_view.xml',
|
||||
'views/job_costing_view.xml',
|
||||
'views/job_order_view.xml',
|
||||
'views/material_requisition_view.xml',
|
||||
'views/construction_product_template_view.xml',
|
||||
'views/internal_transfer_view.xml',
|
||||
'views/construction_project_task_view.xml',
|
||||
'views/scrap_order_view.xml',
|
||||
'views/project_budget_view.xml',
|
||||
'views/construction_res_user_view_inherit.xml',
|
||||
'views/res_config_views.xml',
|
||||
'views/sub_contract_view.xml',
|
||||
'views/quality_check_view.xml',
|
||||
'views/rate_analysis_view.xml',
|
||||
# Menus
|
||||
'views/menus.xml',
|
||||
# Reports
|
||||
'report/work_completion_report_template.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'tk_construction_management/static/src/js/construction_dashboard.js',
|
||||
'tk_construction_management/static/src/js/lib/apexcharts.js',
|
||||
'tk_construction_management/static/src/xml/template.xml',
|
||||
'tk_construction_management/static/src/css/lib/dashboard.css',
|
||||
'tk_construction_management/static/src/css/lib/style.css',
|
||||
'tk_construction_management/static/src/css/style.scss',
|
||||
],
|
||||
},
|
||||
'images': ['static/description/cover.gif'],
|
||||
'license': 'OPL-1',
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
'price': 399,
|
||||
'currency': 'USD',
|
||||
}
|
||||
10
addons/tk_construction_management/data/construction_data.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!--Product-->
|
||||
<record id="construction_product_1" model="product.product">
|
||||
<field name="name">Insurance</field>
|
||||
<field name="type">service</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="construction_paper_format" model="report.paperformat">
|
||||
<field name="name">French Bank Check</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="format">A4</field>
|
||||
<field name="page_height">0</field>
|
||||
<field name="page_width">0</field>
|
||||
<field name="orientation">Portrait</field>
|
||||
<field name="margin_top">12</field>
|
||||
<field name="margin_bottom">3</field>
|
||||
<field name="margin_left">10</field>
|
||||
<field name="margin_right">10</field>
|
||||
<field name="header_line" eval="False"/>
|
||||
<field name="header_spacing">3</field>
|
||||
<field name="dpi">80</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
85
addons/tk_construction_management/data/sequence.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="sequence_job_costing" model="ir.sequence">
|
||||
<field name="name">Job Costing</field>
|
||||
<field name="code">job.costing</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_job_order" model="ir.sequence">
|
||||
<field name="name">Job Order</field>
|
||||
<field name="code">job.order</field>
|
||||
<field name="prefix">WO/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_material_req" model="ir.sequence">
|
||||
<field name="name">Material Requisition</field>
|
||||
<field name="code">material.req</field>
|
||||
<field name="prefix">MREQI/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_internal_transfer" model="ir.sequence">
|
||||
<field name="name">Internal Transfer</field>
|
||||
<field name="code">internal.transfer</field>
|
||||
<field name="prefix">IT/%(year)s/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_construction_scrap_order" model="ir.sequence">
|
||||
<field name="name">Scrap Order Sequence</field>
|
||||
<field name="code">scrap.order</field>
|
||||
<field name="prefix">CS/SCRAP/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="number_next">1</field>
|
||||
<field name="number_increment">1</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_equip_sub_contract" model="ir.sequence">
|
||||
<field name="name">Equipment Subcontract</field>
|
||||
<field name="code">equip.sub</field>
|
||||
<field name="prefix">ES/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_labour_sub_contract" model="ir.sequence">
|
||||
<field name="name">Labour Subcontract</field>
|
||||
<field name="code">labour.sub</field>
|
||||
<field name="prefix">LS/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_overhead_sub_contract" model="ir.sequence">
|
||||
<field name="name">Overhead Subcontract</field>
|
||||
<field name="code">overhead.sub</field>
|
||||
<field name="prefix">OS/</field>
|
||||
<field name="padding">5</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="sequence_material_consume_seq" model="ir.sequence">
|
||||
<field name="name">Material Sequence</field>
|
||||
<field name="code">material.consume</field>
|
||||
<field name="prefix">MCO/</field>
|
||||
<field name="padding">6</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
23
addons/tk_construction_management/models/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
from . import construction_site
|
||||
from . import res_partner
|
||||
from . import configuration
|
||||
from . import construction_project
|
||||
from . import department
|
||||
from . import con_purchase_stock
|
||||
from . import job_costing
|
||||
from . import job_order
|
||||
from . import material_requisition
|
||||
from . import con_template
|
||||
from . import internal_transfer
|
||||
from . import project_task
|
||||
from . import construction_scrap
|
||||
from . import construction_user
|
||||
from . import construction_dashboard
|
||||
from . import construction_emplyoee
|
||||
from . import res_config
|
||||
from . import sub_contract
|
||||
from . import rate_analysis
|
||||
from . import budget
|
||||
230
addons/tk_construction_management/models/budget.py
Normal file
@@ -0,0 +1,230 @@
|
||||
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'
|
||||
}
|
||||
131
addons/tk_construction_management/models/con_purchase_stock.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# -*- 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 ConstructionPurchase(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
material_req_id = fields.Many2one('material.requisition', string="Material Requisition")
|
||||
project_id = fields.Many2one(related="material_req_id.project_id", string="Sub Project", store=True)
|
||||
job_order_id = fields.Many2one('job.order', string="Work order")
|
||||
purchase_order = fields.Selection([('equipment', 'Equipment'), ('labour', 'Labour'), ('overhead', 'Overhead')],
|
||||
string="Source Ref.")
|
||||
equipment_subcontract_id = fields.Many2one('equipment.subcontract', string="Subcontract")
|
||||
labour_subcontract_id = fields.Many2one('labour.subcontract', string="Subcontract ")
|
||||
overhead_subcontract_id = fields.Many2one('overhead.subcontract', string="Subcontract ")
|
||||
|
||||
def _prepare_invoice(self):
|
||||
res = super(ConstructionPurchase, self)._prepare_invoice()
|
||||
if self.material_req_id:
|
||||
res['material_req_id'] = self.material_req_id.id
|
||||
if self.job_order_id:
|
||||
res['job_order_id'] = self.job_order_id.id
|
||||
res['purchase_order'] = self.purchase_order
|
||||
res['equipment_subcontract_id'] = self.equipment_subcontract_id.id
|
||||
res['labour_subcontract_id'] = self.labour_subcontract_id.id
|
||||
res['overhead_subcontract_id'] = self.overhead_subcontract_id.id
|
||||
return res
|
||||
|
||||
|
||||
class ConstructionBills(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
material_req_id = fields.Many2one('material.requisition', string="Material Requisition")
|
||||
project_id = fields.Many2one(related="material_req_id.project_id", string="Sub Project", store=True)
|
||||
job_order_id = fields.Many2one('job.order', string="Work order")
|
||||
purchase_order = fields.Selection([('equipment', 'Equipment'), ('labour', 'Labour'), ('overhead', 'Overhead')],
|
||||
string="Source Ref.")
|
||||
equipment_subcontract_id = fields.Many2one('equipment.subcontract', string="Subcontract")
|
||||
labour_subcontract_id = fields.Many2one('labour.subcontract', string="Subcontract ")
|
||||
overhead_subcontract_id = fields.Many2one('overhead.subcontract', string="Subcontract ")
|
||||
|
||||
|
||||
class ConstructionProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
last_po_price = fields.Monetary(string="Last Purchase Price")
|
||||
is_material = fields.Boolean(string="Is Material")
|
||||
is_equipment = fields.Boolean(string="Is Equipment")
|
||||
is_labour = fields.Boolean(string="Is Labour")
|
||||
is_overhead = fields.Boolean(string="Is Overhead")
|
||||
is_expense = fields.Boolean(string="Is Expense")
|
||||
|
||||
|
||||
class ConstructionWarehouse(models.Model):
|
||||
_inherit = 'stock.warehouse'
|
||||
|
||||
project_id = fields.Many2one('tk.construction.project', string="Project")
|
||||
consume_stock_location_id = fields.Many2one('stock.location', string="Consume Location")
|
||||
|
||||
|
||||
class ConstructionDelivery(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
code = fields.Selection(related='picking_type_id.code', store=True, string="Code ")
|
||||
transfer_id = fields.Many2one('internal.transfer', string="Transfer Ref.")
|
||||
consume_order_id = fields.Many2one('job.order', string="Consume Order Ref.")
|
||||
material_consume_id = fields.Many2one('material.consume', string="Material Consume Ref.")
|
||||
|
||||
def button_validate(self):
|
||||
# Clean-up the context key at validation to avoid forcing the creation of immediate
|
||||
# transfers.
|
||||
ctx = dict(self.env.context)
|
||||
ctx.pop('default_immediate_transfer', None)
|
||||
self = self.with_context(ctx)
|
||||
|
||||
# Sanity checks.
|
||||
if not self.env.context.get('skip_sanity_check', False):
|
||||
self._sanity_check()
|
||||
|
||||
self.message_subscribe([self.env.user.partner_id.id])
|
||||
|
||||
# Run the pre-validation wizards. Processing a pre-validation wizard should work on the
|
||||
# moves and/or the context and never call `_action_done`.
|
||||
if not self.env.context.get('button_validate_picking_ids'):
|
||||
self = self.with_context(button_validate_picking_ids=self.ids)
|
||||
res = self._pre_action_done_hook()
|
||||
if res is not True:
|
||||
return res
|
||||
|
||||
# Call `_action_done`.
|
||||
pickings_not_to_backorder = self.filtered(lambda p: p.picking_type_id.create_backorder == 'never')
|
||||
if self.env.context.get('picking_ids_not_to_backorder'):
|
||||
pickings_not_to_backorder |= self.browse(self.env.context['picking_ids_not_to_backorder']).filtered(
|
||||
lambda p: p.picking_type_id.create_backorder != 'always'
|
||||
)
|
||||
pickings_to_backorder = self - pickings_not_to_backorder
|
||||
pickings_not_to_backorder.with_context(cancel_backorder=True)._action_done()
|
||||
pickings_to_backorder.with_context(cancel_backorder=False)._action_done()
|
||||
|
||||
if self.user_has_groups('stock.group_reception_report') \
|
||||
and self.picking_type_id.auto_show_reception_report:
|
||||
lines = self.move_ids.filtered(lambda
|
||||
m: m.product_id.type == 'product' and m.state != 'cancel' and m.quantity_done and not m.move_dest_ids)
|
||||
if lines:
|
||||
# don't show reception report if all already assigned/nothing to assign
|
||||
wh_location_ids = self.env['stock.location']._search(
|
||||
[('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.ids),
|
||||
('usage', '!=', 'supplier')])
|
||||
if self.env['stock.move'].search([
|
||||
('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']),
|
||||
('product_qty', '>', 0),
|
||||
('location_id', 'in', wh_location_ids),
|
||||
('move_orig_ids', '=', False),
|
||||
('picking_id', 'not in', self.ids),
|
||||
('product_id', 'in', lines.product_id.ids)], limit=1):
|
||||
action = self.action_view_reception_report()
|
||||
action['context'] = {'default_picking_ids': self.ids}
|
||||
return action
|
||||
|
||||
if self.transfer_id:
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Internal Transfer'),
|
||||
'res_model': 'internal.transfer',
|
||||
'res_id': self.transfer_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
return True
|
||||
26
addons/tk_construction_management/models/con_template.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- 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 ConstructionTemplate(models.Model):
|
||||
_name = 'construction.product.template'
|
||||
_description = "Product Template"
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
template_ids = fields.One2many('construction.template.line', 'template_id')
|
||||
|
||||
|
||||
class ConstructionTemplateLine(models.Model):
|
||||
_name = 'construction.template.line'
|
||||
_description = "Product Template Line"
|
||||
|
||||
product_id = fields.Many2one('product.product', string="Product", domain="[('is_material','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
template_id = fields.Many2one('construction.product.template')
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.product_id.name
|
||||
33
addons/tk_construction_management/models/configuration.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- 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 DocumentType(models.Model):
|
||||
_name = 'site.document.type'
|
||||
_description = "Project Document Type"
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
|
||||
|
||||
class JobType(models.Model):
|
||||
_name = 'job.type'
|
||||
_description = "Activity"
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
sub_category_ids = fields.Many2many('job.sub.category', string="Work Sub Type")
|
||||
|
||||
|
||||
class JobSubCategory(models.Model):
|
||||
_name = 'job.sub.category'
|
||||
_description = "Sub Activity"
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
|
||||
|
||||
class InsuranceRisk(models.Model):
|
||||
_name = 'insurance.risk'
|
||||
_description = "Insurance Risk"
|
||||
|
||||
name = fields.Char(string="Risk")
|
||||
@@ -0,0 +1,245 @@
|
||||
# -*- 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 ConstructionDashboard(models.Model):
|
||||
_name = 'tk.construction.dashboard'
|
||||
_description = "Construction Dashboard"
|
||||
|
||||
name = fields.Char()
|
||||
|
||||
@api.model
|
||||
def get_construction_state(self, site_id, project_id):
|
||||
site_domain = []
|
||||
project_domain = []
|
||||
int_domain = []
|
||||
if (not site_id and not project_id) or (site_id == 'all_site' and project_id == 'all_project'):
|
||||
pass
|
||||
elif not site_id == 'all_site' and project_id == 'all_project':
|
||||
site_domain = [('construction_site_id', '=', int(site_id))]
|
||||
int_domain = [('site_id', '=', int(site_id))]
|
||||
projects = self.env['tk.construction.project'].search(
|
||||
site_domain).mapped('id')
|
||||
project_domain = [('project_id', 'in', projects)]
|
||||
elif site_id and project_id:
|
||||
site_domain = [('construction_site_id', '=', int(site_id))]
|
||||
int_domain = [('site_id', '=', int(site_id))]
|
||||
project_domain = [('project_id', '=', int(project_id))]
|
||||
total_site = self.env['tk.construction.site'].search_count([])
|
||||
total_project = self.env['tk.construction.project'].search_count(
|
||||
site_domain)
|
||||
total_mrq = self.env['material.requisition'].search_count(
|
||||
project_domain)
|
||||
job_sheet_count = self.env['job.costing'].search_count(project_domain)
|
||||
job_order_count = self.env['job.order'].search_count(project_domain)
|
||||
back_order = self.env['material.requisition'].sudo().search_count(
|
||||
[('is_back_order', '=', True)] + project_domain)
|
||||
forward_transfer = self.env['internal.transfer'].sudo().search_count(
|
||||
[('is_forward_transfer', '=', True)] + int_domain)
|
||||
return {
|
||||
'total_site': total_site,
|
||||
'total_project': total_project,
|
||||
'total_mrq': total_mrq,
|
||||
'job_sheet_count': job_sheet_count,
|
||||
'job_order_count': job_order_count,
|
||||
'mrq_state': self.get_mrq_state(project_domain),
|
||||
'site_state': self.get_site_state(),
|
||||
'internal_state': self.get_it_state(int_domain),
|
||||
'site_timeline': self.construction_time_line(),
|
||||
'project_timeline': self.project_time_line(site_domain),
|
||||
'project_status': self.get_project_status(site_domain),
|
||||
'job_order_po': self.get_purchase_order(project_domain),
|
||||
'back_order': back_order,
|
||||
'forward_transfer': forward_transfer,
|
||||
'purchase_order': self.get_purchase_order_state(project_domain),
|
||||
'con_sites': self.get_site_list(),
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_project_list(self, site_domain):
|
||||
projects = {}
|
||||
if not site_domain == 'all_site':
|
||||
domain = [('construction_site_id', '=', int(site_domain))]
|
||||
project_obj = self.env['tk.construction.project'].sudo()
|
||||
for rec in project_obj.search(domain):
|
||||
projects[rec.id] = rec.name
|
||||
return projects
|
||||
if site_domain == 'all_site':
|
||||
return projects
|
||||
|
||||
def get_site_list(self):
|
||||
site_obj = self.env['tk.construction.site'].sudo()
|
||||
sites = {}
|
||||
for rec in site_obj.search([]):
|
||||
sites[rec.id] = rec.name
|
||||
return sites
|
||||
|
||||
def get_mrq_state(self, project_domain):
|
||||
material_requisition = self.env['material.requisition'].sudo()
|
||||
draft = material_requisition.search_count(
|
||||
[('stage', '=', 'draft')] + project_domain)
|
||||
waiting_approval = material_requisition.search_count(
|
||||
[('stage', '=', 'department_approval')] + project_domain)
|
||||
approved = material_requisition.search_count(
|
||||
[('stage', '=', 'approve')] + project_domain)
|
||||
ready_delivery = material_requisition.search_count(
|
||||
[('stage', '=', 'ready_delivery')] + project_domain)
|
||||
material_arrive = material_requisition.search_count(
|
||||
[('stage', '=', 'material_arrived')] + project_domain)
|
||||
internal_transfer = material_requisition.search_count(
|
||||
[('stage', '=', 'internal_transfer')] + project_domain)
|
||||
reject = material_requisition.search_count(
|
||||
[('stage', '=', 'reject')] + project_domain)
|
||||
cancel = material_requisition.search_count(
|
||||
[('stage', '=', 'cancel')] + project_domain)
|
||||
return [
|
||||
['Draft', 'Waiting Approval', 'In Progress', 'Ready Delivery', 'Material Arrive', 'Internal Transfer',
|
||||
'Reject', 'Cancel'],
|
||||
[draft, waiting_approval, approved, ready_delivery,
|
||||
material_arrive, internal_transfer, reject, cancel]
|
||||
]
|
||||
|
||||
def get_site_state(self):
|
||||
site = self.env['tk.construction.site'].sudo()
|
||||
draft = site.search_count([('status', '=', 'draft')])
|
||||
in_progress = site.search_count([('status', '=', 'in_progress')])
|
||||
done = site.search_count([('status', '=', 'complete')])
|
||||
return [
|
||||
['Draft', 'In Progress', 'Complete'], [draft, in_progress, done]
|
||||
]
|
||||
|
||||
def get_it_state(self, site_domain):
|
||||
internal_transfer = self.env['internal.transfer'].sudo()
|
||||
draft = internal_transfer.search_count(
|
||||
[('stage', '=', 'draft')] + site_domain)
|
||||
in_progress = internal_transfer.search_count(
|
||||
[('stage', '=', 'in_progress')] + site_domain)
|
||||
done = internal_transfer.search_count(
|
||||
[('stage', '=', 'done')] + site_domain)
|
||||
return [
|
||||
['Draft', 'In Progress', 'Done'], [draft, in_progress, done]
|
||||
]
|
||||
|
||||
def construction_time_line(self):
|
||||
site_data = []
|
||||
construction_site_data = self.env['tk.construction.site'].search([])
|
||||
for site in construction_site_data:
|
||||
if site.status == "in_progress":
|
||||
site_data.append({
|
||||
'name': site.name,
|
||||
'start_date': str(site.start_date),
|
||||
'end_date': str(site.end_date),
|
||||
})
|
||||
return site_data
|
||||
|
||||
def project_time_line(self, site_domain):
|
||||
data = []
|
||||
project_data = self.env['tk.construction.project'].search(site_domain)
|
||||
for p in project_data:
|
||||
if p.stage == "Construction":
|
||||
data.append({
|
||||
'name': str(p.name) + " - " + str(p.construction_site_id.name),
|
||||
'start_date': str(p.start_date),
|
||||
'end_date': str(p.end_date),
|
||||
})
|
||||
return data
|
||||
|
||||
def get_project_status(self, site_domain):
|
||||
project_obj = self.env['tk.construction.project'].sudo()
|
||||
planning = project_obj.search_count(
|
||||
[('stage', '=', 'Planning')] + site_domain)
|
||||
procurement = project_obj.search_count(
|
||||
[('stage', '=', 'Procurement')] + site_domain)
|
||||
construction = project_obj.search_count(
|
||||
[('stage', '=', 'Construction')] + site_domain)
|
||||
handover = project_obj.search_count(
|
||||
[('stage', '=', 'Handover')] + site_domain)
|
||||
return [
|
||||
['Planning', 'Procurement', 'Construction', 'Handover'],
|
||||
[planning, procurement, construction, handover]
|
||||
]
|
||||
|
||||
def get_purchase_order(self, project_domain):
|
||||
jo_order = []
|
||||
mrq_po = []
|
||||
equip_po = []
|
||||
labour_po = []
|
||||
overhead_po = []
|
||||
job_order = self.env['job.order'].sudo().search(project_domain)
|
||||
for j in job_order:
|
||||
if j.project_id.stage == "Construction":
|
||||
name = str(j.name) + " - " + str(j.title)
|
||||
jo_order.append(name)
|
||||
mrq_po.append(j.material_req_id.po_count)
|
||||
equip_po.append(j.equip_po_count)
|
||||
labour_po.append(j.labour_po_count)
|
||||
overhead_po.append(j.overhead_po_count)
|
||||
data = [jo_order, mrq_po, equip_po, labour_po, overhead_po]
|
||||
return data
|
||||
|
||||
def get_purchase_order_state(self, project_domain):
|
||||
domain = project_domain
|
||||
po_obj = self.env['purchase.order'].sudo()
|
||||
mr_po = po_obj.search_count(
|
||||
[('material_req_id', '!=', False)] + domain)
|
||||
equip_po = 0
|
||||
labour_po = 0
|
||||
overhead_po = 0
|
||||
job_orders = self.env['job.order'].search(domain)
|
||||
for rec in job_orders:
|
||||
equip_po = equip_po + po_obj.search_count(
|
||||
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'equipment')])
|
||||
labour_po = labour_po + po_obj.search_count(
|
||||
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'labour')])
|
||||
overhead_po = overhead_po + po_obj.search_count(
|
||||
[('job_order_id', '=', rec.id), ('purchase_order', '=', 'overhead')])
|
||||
return {
|
||||
'mr_po': mr_po,
|
||||
'equip_po': equip_po,
|
||||
'labour_po': labour_po,
|
||||
'overhead_po': overhead_po,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_construction_project_domain(self, site_id, project_id):
|
||||
site_domain = []
|
||||
project_domain = []
|
||||
if site_id == 'all_site':
|
||||
pass
|
||||
elif not site_id == 'all_site' and project_id == 'all_project':
|
||||
site_domain = [('construction_site_id', '=', int(site_id))]
|
||||
projects = self.env['tk.construction.project'].search(
|
||||
site_domain).mapped('id')
|
||||
project_domain = [('project_id', 'in', projects)]
|
||||
elif site_id and project_id:
|
||||
site_domain = [('construction_site_id', '=', int(site_id))]
|
||||
project_domain = [('project_id', '=', int(project_id))]
|
||||
return [site_domain, project_domain]
|
||||
|
||||
@api.model
|
||||
def get_job_order_po(self, site_id, project_id, source):
|
||||
project_domain = []
|
||||
if site_id == 'all_site':
|
||||
pass
|
||||
elif not site_id == 'all_site' and project_id == 'all_project':
|
||||
site_domain = [('construction_site_id', '=', int(site_id))]
|
||||
projects = self.env['tk.construction.project'].search(
|
||||
site_domain).mapped('id')
|
||||
project_domain = [('project_id', 'in', projects)]
|
||||
elif site_id and project_id:
|
||||
project_domain = [('project_id', '=', int(project_id))]
|
||||
ids = []
|
||||
po_obj = self.env['purchase.order'].sudo()
|
||||
for data in self.env['job.order'].search(project_domain):
|
||||
if source == 'equip':
|
||||
ids = ids + po_obj.search(
|
||||
[('job_order_id', '=', data.id), ('purchase_order', '=', 'equipment')]).mapped('id')
|
||||
if source == 'labour':
|
||||
ids = ids + po_obj.search(
|
||||
[('job_order_id', '=', data.id), ('purchase_order', '=', 'labour')]).mapped('id')
|
||||
if source == 'overhead':
|
||||
ids = ids + po_obj.search(
|
||||
[('job_order_id', '=', data.id), ('purchase_order', '=', 'overhead')]).mapped('id')
|
||||
return ids
|
||||
@@ -0,0 +1,8 @@
|
||||
# -*- 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 ConstructionEmployee(models.Model):
|
||||
_inherit = "hr.employee"
|
||||
846
addons/tk_construction_management/models/construction_project.py
Normal file
@@ -0,0 +1,846 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
import xlwt
|
||||
import base64
|
||||
from io import BytesIO
|
||||
from odoo import fields, api, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class ConstructionProject(models.Model):
|
||||
_name = 'tk.construction.project'
|
||||
_description = "Construction Project"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string="Title", tracking=True)
|
||||
start_date = fields.Date(string="Schedule Start Date", tracking=True)
|
||||
end_date = fields.Date(string="Schedule End Date", tracking=True)
|
||||
project_progress = fields.Float(
|
||||
related="budget_id.progress", string="Project Progress", tracking=True)
|
||||
construction_site_id = fields.Many2one(
|
||||
'tk.construction.site', string="Project ", tracking=True)
|
||||
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
|
||||
string="Created By")
|
||||
stage = fields.Selection([('draft', 'Draft'),
|
||||
('Planning', 'Planning'),
|
||||
('Procurement', 'Procurement'),
|
||||
('Construction', 'Construction'),
|
||||
('Handover', 'Handover')], string="Stage", default="draft", tracking=True)
|
||||
warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string="Warehouse", tracking=True)
|
||||
code = fields.Char(string="Code", tracking=True)
|
||||
project_id = fields.Many2one(
|
||||
'project.project', string="Project", tracking=True)
|
||||
engineer_ids = fields.Many2many(
|
||||
'hr.employee', string="Engineers", tracking=True)
|
||||
|
||||
# Department
|
||||
department_id = fields.Many2one(
|
||||
'construction.department', string="Department", tracking=True)
|
||||
manager_ids = fields.Many2many('res.users', string="Manager")
|
||||
user_id = fields.Many2one('res.users', string="Responsible")
|
||||
|
||||
# Address
|
||||
zip = fields.Char(string='Pin Code')
|
||||
street = fields.Char(string='Street1')
|
||||
street2 = fields.Char(string='Street2')
|
||||
city = fields.Char(string='City')
|
||||
country_id = fields.Many2one('res.country', 'Country')
|
||||
state_id = fields.Many2one(
|
||||
"res.country.state", string='State', readonly=False, store=True,
|
||||
domain="[('country_id', '=?', country_id)]")
|
||||
longitude = fields.Char(string="Longitude")
|
||||
latitude = fields.Char(string="Latitude")
|
||||
|
||||
# BOQ Details
|
||||
is_use_measure = fields.Boolean(
|
||||
string="Is use of (LENGTH x WIDTH x HEIGHT ) ?")
|
||||
boq_budget_ids = fields.One2many(
|
||||
'boq.budget', 'project_id', string="Budget ")
|
||||
|
||||
# Budget
|
||||
budget_id = fields.Many2one('sub.project.budget', string="Budget")
|
||||
|
||||
# One2Many
|
||||
document_ids = fields.One2many(
|
||||
'project.documents', 'project_id', string="Documents")
|
||||
policy_ids = fields.One2many(
|
||||
'project.insurance', 'project_id', string="Insurance")
|
||||
expense_ids = fields.One2many(
|
||||
'extra.expense', 'project_id', string="Expense")
|
||||
phase_ids = fields.One2many('job.costing', 'project_id')
|
||||
|
||||
# Count & Totals
|
||||
job_sheet_count = fields.Integer(
|
||||
string="Project Phase Count", compute="_compute_count")
|
||||
job_order_count = fields.Integer(
|
||||
string="Work Order Count", compute="_compute_count")
|
||||
mrq_count = fields.Integer(string="MRQ Count", compute="_compute_count")
|
||||
mrq_po_count = fields.Integer(
|
||||
string="MRQ PO Count", compute="_compute_count")
|
||||
jo_po_count = fields.Integer(
|
||||
string="JO PO Count", compute="_compute_count")
|
||||
inspection_task_count = fields.Integer(
|
||||
string="Inspection Task Count", compute="_compute_count")
|
||||
task_count = fields.Integer(string="Task Count", compute="_compute_count")
|
||||
budget_count = fields.Integer(
|
||||
string="Budget Count", compute="_compute_count")
|
||||
|
||||
# Create, Write, Unlink, Constrain
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
res = super(ConstructionProject, self).create(vals)
|
||||
for rec in res:
|
||||
data = {
|
||||
'name': rec.name + "'s Project",
|
||||
'construction_project_id': rec.id,
|
||||
'company_id': self.env.company.id,
|
||||
'privacy_visibility': 'followers'
|
||||
}
|
||||
project_id = self.env['project.project'].create(data)
|
||||
rec.project_id = project_id.id
|
||||
task_stage = self.env['project.task.type'].sudo().search(
|
||||
[('active', '=', True), ('user_id', '=', False)])
|
||||
ids = []
|
||||
for data in task_stage:
|
||||
ids = data.project_ids.ids
|
||||
ids.append(project_id.id)
|
||||
data.project_ids = ids
|
||||
return res
|
||||
|
||||
@api.constrains('boq_budget_ids')
|
||||
def _check_sub_activity(self):
|
||||
for rec in self:
|
||||
activity_counts = {}
|
||||
for activity in rec.boq_budget_ids:
|
||||
key = (activity.activity_id.id, activity.sub_activity_id.id)
|
||||
if key in activity_counts:
|
||||
activity_counts[key] += 1
|
||||
else:
|
||||
activity_counts[key] = 1
|
||||
duplicate = [pair for pair,
|
||||
count in activity_counts.items() if count > 1]
|
||||
if duplicate:
|
||||
raise ValidationError(
|
||||
"Duplicate work type and sub work type pair found.")
|
||||
|
||||
@api.onchange('construction_site_id')
|
||||
def onchange_site_address(self):
|
||||
for rec in self:
|
||||
if rec.construction_site_id:
|
||||
rec.zip = rec.construction_site_id.zip
|
||||
rec.street = rec.construction_site_id.street
|
||||
rec.street2 = rec.construction_site_id.street2
|
||||
rec.city = rec.construction_site_id.city
|
||||
rec.state_id = rec.construction_site_id.state_id.id
|
||||
rec.country_id = rec.construction_site_id.country_id.id
|
||||
rec.start_date = rec.construction_site_id.start_date
|
||||
rec.end_date = rec.construction_site_id.end_date
|
||||
|
||||
# Compute
|
||||
def _compute_count(self):
|
||||
for rec in self:
|
||||
rec.job_sheet_count = self.env['job.costing'].search_count(
|
||||
[('project_id', '=', rec.id)])
|
||||
rec.job_order_count = self.env['job.order'].search_count(
|
||||
[('project_id', '=', rec.id)])
|
||||
rec.mrq_count = self.env['material.requisition'].search_count(
|
||||
[('project_id', '=', rec.id)])
|
||||
rec.mrq_po_count = self.env['purchase.order'].search_count(
|
||||
[('project_id', '=', rec.id)])
|
||||
job_order_ids = self.env['job.order'].search(
|
||||
[('project_id', '=', self.id)]).mapped('id')
|
||||
po_ids = self.env['purchase.order'].search(
|
||||
[('job_order_id', 'in', job_order_ids)]).mapped('id')
|
||||
rec.jo_po_count = len(po_ids)
|
||||
rec.task_count = self.env['project.task'].search_count(
|
||||
[('con_project_id', '=', rec.id), ('is_inspection_task', '=', False)])
|
||||
rec.inspection_task_count = self.env['project.task'].search_count(
|
||||
[('con_project_id', '=', rec.id), ('is_inspection_task', '=', True)])
|
||||
rec.budget_count = self.env['project.budget'].search_count(
|
||||
[('sub_project_budget_id', '=', rec.budget_id.id)])
|
||||
|
||||
# Onchange
|
||||
@api.onchange('department_id', 'manager_ids')
|
||||
def _onchange_department_manager(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.manager_ids.ids
|
||||
return {'domain': {'manager_ids': [('id', 'in', ids)]}}
|
||||
|
||||
@api.onchange('department_id', 'manager_ids', 'user_id')
|
||||
def _onchange_department_responsible(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.user_ids.ids
|
||||
return {'domain': {'user_id': [('id', 'in', ids)]}}
|
||||
|
||||
# Smart Button
|
||||
def action_gmap_location(self):
|
||||
if self.longitude and self.latitude:
|
||||
longitude = self.longitude
|
||||
latitude = self.latitude
|
||||
http_url = 'https://maps.google.com/maps?q=loc:' + latitude + ',' + longitude
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'target': 'new',
|
||||
'url': http_url,
|
||||
}
|
||||
else:
|
||||
raise ValidationError(
|
||||
_("! Enter Proper Longitude and Latitude Values"))
|
||||
|
||||
def action_view_job_sheet(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Project Phase(WBS)'),
|
||||
'res_model': 'job.costing',
|
||||
'domain': [('project_id', '=', self.id)],
|
||||
'context': {'default_project_id': self.id},
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_job_order(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order'),
|
||||
'res_model': 'job.order',
|
||||
'domain': [('project_id', '=', self.id)],
|
||||
'context': {'default_project_id': self.id},
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_material_requisition(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Material Requisition'),
|
||||
'res_model': 'material.requisition',
|
||||
'domain': [('project_id', '=', self.id)],
|
||||
'context': {'default_project_id': self.id},
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_mrq_purchase_orders(self):
|
||||
ids = self.env['purchase.order'].search(
|
||||
[('project_id', '=', self.id)]).mapped('id')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('MRQ Purchase Order'),
|
||||
'res_model': 'purchase.order',
|
||||
'domain': [('id', 'in', ids)],
|
||||
'context': {'create': False},
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_jo_purchase_orders(self):
|
||||
job_order_ids = self.env['job.order'].search(
|
||||
[('project_id', '=', self.id)]).mapped('id')
|
||||
po_ids = self.env['purchase.order'].search(
|
||||
[('job_order_id', 'in', job_order_ids)]).mapped('id')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order PO'),
|
||||
'res_model': 'purchase.order',
|
||||
'domain': [('id', 'in', po_ids)],
|
||||
'context': {'create': False, 'group_by': 'purchase_order'},
|
||||
'view_mode': 'tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_project_task(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Task'),
|
||||
'res_model': 'project.task',
|
||||
'domain': [('con_project_id', '=', self.id), ('is_inspection_task', '=', False)],
|
||||
'context': {'default_con_project_id': self.id, 'default_project_id': self.project_id.id},
|
||||
'view_mode': 'kanban,tree,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_project_task_inspection(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Inspection Task'),
|
||||
'res_model': 'project.task',
|
||||
'domain': [('con_project_id', '=', self.id), ('is_inspection_task', '=', True)],
|
||||
'context': {'default_con_project_id': self.id, 'default_project_id': self.project_id.id,
|
||||
'default_is_inspection_task': True, 'default_priority': '1'},
|
||||
'view_mode': 'tree,form,kanban',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_budget(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Budget'),
|
||||
'res_model': 'project.budget',
|
||||
'domain': [('sub_project_budget_id', '=', self.budget_id.id)],
|
||||
'context': {'default_sub_project_budget_id': self.budget_id.id},
|
||||
'view_mode': 'tree,kanban,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
# Button
|
||||
def action_project_planning(self):
|
||||
self.stage = 'Planning'
|
||||
|
||||
def action_stage_procurement(self):
|
||||
if not self.warehouse_id:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please choose a warehouse to proceed to the next stage of construction process",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
if not self.budget_id:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please create a budget to proceed to the next stage of construction process",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
|
||||
self.stage = 'Procurement'
|
||||
|
||||
def action_stage_construction(self):
|
||||
self.stage = 'Construction'
|
||||
|
||||
def action_stage_handover(self):
|
||||
phase_completed = True
|
||||
phase_record = self.env['job.costing'].search(
|
||||
[('project_id', '=', self.id)])
|
||||
for data in phase_record:
|
||||
if data.status != 'complete':
|
||||
phase_completed = False
|
||||
break
|
||||
if not phase_completed:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please complete all the phases related to this project to handover this project.",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
self.stage = 'Handover'
|
||||
|
||||
def get_float_time(self, t):
|
||||
hour, minute = divmod(t, 1)
|
||||
minute *= 60
|
||||
result = '{}:{}'.format(int(hour), int(minute))
|
||||
return result
|
||||
|
||||
def action_print_budget_excel_report(self):
|
||||
workbook = xlwt.Workbook(encoding='utf-8')
|
||||
sheet1 = workbook.add_sheet('Budget Details', cell_overwrite_ok=True)
|
||||
sheet1.show_grid = False
|
||||
sheet1.col(0).width = 400
|
||||
sheet1.col(3).width = 600
|
||||
sheet1.col(9).width = 600
|
||||
sheet1.row(4).height = 400
|
||||
sheet1.row(5).height = 200
|
||||
sheet1.row(6).height = 400
|
||||
sheet1.row(7).height = 200
|
||||
sheet1.row(8).height = 400
|
||||
sheet1.row(9).height = 200
|
||||
sheet1.row(10).height = 400
|
||||
sheet1.row(11).height = 200
|
||||
sheet1.row(12).height = 400
|
||||
sheet1.row(17).height = 400
|
||||
sheet1.col(12).width = 5000
|
||||
sheet1.col(13).width = 4000
|
||||
sheet1.col(14).width = 5000
|
||||
sheet1.col(15).width = 5000
|
||||
sheet1.col(16).width = 8000
|
||||
sheet1.col(17).width = 7000
|
||||
sheet1.col(18).width = 7000
|
||||
sheet1.col(19).width = 7000
|
||||
sheet1.col(20).width = 7000
|
||||
sheet1.col(21).width = 7000
|
||||
sheet1.col(22).width = 5000
|
||||
sheet1.row(14).height = 600
|
||||
border_squre = xlwt.Borders()
|
||||
border_squre.bottom = xlwt.Borders.HAIR
|
||||
border_squre.bottom_colour = xlwt.Style.colour_map["sea_green"]
|
||||
al = xlwt.Alignment()
|
||||
al.horz = xlwt.Alignment.HORZ_LEFT
|
||||
al.vert = xlwt.Alignment.VERT_CENTER
|
||||
date_format = xlwt.XFStyle()
|
||||
date_format.num_format_str = 'mm/dd/yyyy'
|
||||
date_format.font.name = "Century Gothic"
|
||||
date_format.borders = border_squre
|
||||
date_format.alignment = al
|
||||
date_format.font.colour_index = 0x36
|
||||
date_format.font.bold = 1
|
||||
sheet1.set_panes_frozen(True)
|
||||
sheet1.set_horz_split_pos(18)
|
||||
sheet1.remove_splits = True
|
||||
title = xlwt.easyxf(
|
||||
"font: height 350, name Century Gothic, bold on, color_index blue_gray;"
|
||||
" align: vert center, horz center;"
|
||||
"border: bottom thick, bottom_color sea_green;")
|
||||
values = xlwt.easyxf(
|
||||
"font:name Century Gothic, bold on, color_index blue_gray;"
|
||||
" align: vert center, horz left;"
|
||||
"border: bottom hair, bottom_color sea_green;")
|
||||
line_percentage_value = xlwt.easyxf(
|
||||
"font:name Century Gothic, bold on, color_index blue_gray;"
|
||||
" align: vert center, horz center;"
|
||||
"border: bottom hair, bottom_color blue_gray, right hair,right_color blue_gray;")
|
||||
line_values = xlwt.easyxf(
|
||||
"font:name Century Gothic;"
|
||||
" align: vert center, horz center;"
|
||||
"border: bottom hair, bottom_color blue_gray, right hair,right_color blue_gray,left hair,left_color blue_gray;")
|
||||
line_amount_values = xlwt.easyxf(
|
||||
"font:name Century Gothic;"
|
||||
" align: vert center, horz right;"
|
||||
"border: bottom hair, bottom_color blue_gray, right hair,right_color blue_gray,left hair,left_color blue_gray;")
|
||||
line_amount_sub_title = xlwt.easyxf(
|
||||
"font: height 185, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz right; "
|
||||
"border: top hair, bottom hair, left hair, right hair, "
|
||||
"top_color gray50, bottom_color gray50, left_color gray50, right_color gray50")
|
||||
sub_title = xlwt.easyxf(
|
||||
"font: height 185, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz center; "
|
||||
"border: top hair, bottom hair, left hair, right hair, "
|
||||
"top_color gray50, bottom_color gray50, left_color gray50, right_color gray50")
|
||||
# Budget Details
|
||||
budget_amount = str(self.budget_id.currency_id.symbol) + \
|
||||
" "+str(self.budget_id.total_budget_amount)
|
||||
utilization_amount = str(self.budget_id.currency_id.symbol) + " " + str(
|
||||
self.budget_id.utilization_amount)
|
||||
utilization_percentage = str(self.budget_id.progress) + " %"
|
||||
sheet1.write_merge(0, 2, 1, 11, "Budget Details", title)
|
||||
sheet1.write_merge(4, 4, 1, 2, "Project", sub_title)
|
||||
sheet1.write_merge(4, 4, 4, 5, self.construction_site_id.name, values)
|
||||
sheet1.write_merge(6, 6, 1, 2, "Sub Project", sub_title)
|
||||
sheet1.write_merge(6, 6, 4, 5, self.name, values)
|
||||
sheet1.write_merge(4, 4, 7, 8, "Start Date", sub_title)
|
||||
sheet1.write_merge(
|
||||
4, 4, 10, 11, self.budget_id.start_date, date_format)
|
||||
sheet1.write_merge(6, 6, 7, 8, "End Date", sub_title)
|
||||
sheet1.write_merge(6, 6, 10, 11, self.budget_id.end_date, date_format)
|
||||
sheet1.write_merge(8, 8, 1, 2, "Total Budget Amount", sub_title)
|
||||
sheet1.write_merge(8, 8, 4, 5, budget_amount, values)
|
||||
sheet1.write_merge(10, 10, 1, 2, "Budget Utilization", sub_title)
|
||||
sheet1.write_merge(10, 10, 4, 5, utilization_amount, values)
|
||||
sheet1.write_merge(12, 12, 1, 2, "Utilization(%)", sub_title)
|
||||
sheet1.write_merge(12, 12, 4, 5, utilization_percentage, values)
|
||||
# Budget Lines
|
||||
sheet1.write_merge(14, 15, 1, 22, "Budget Lines", title)
|
||||
sheet1.write_merge(17, 17, 1, 3, "Work Type", sub_title)
|
||||
sheet1.write_merge(17, 17, 4, 6, "Work Sub Type", sub_title)
|
||||
sheet1.write(17, 7, "BOQ Qty", line_amount_sub_title)
|
||||
sheet1.write_merge(17, 17, 8, 10, "Additional Qty",
|
||||
line_amount_sub_title)
|
||||
sheet1.write(17, 11, "Total Qty", line_amount_sub_title)
|
||||
sheet1.write(17, 12, "Rate Analysis", sub_title)
|
||||
sheet1.write(17, 13, "Price / Qty", line_amount_sub_title)
|
||||
sheet1.write(17, 14, "Untaxed", line_amount_sub_title)
|
||||
sheet1.write(17, 15, "Tax Amount", line_amount_sub_title)
|
||||
sheet1.write(17, 16, "Total Budget", line_amount_sub_title)
|
||||
sheet1.write(17, 17, "Material Spent", line_amount_sub_title)
|
||||
sheet1.write(17, 18, "Equipment Spent", line_amount_sub_title)
|
||||
sheet1.write(17, 19, "Labour Spent", line_amount_sub_title)
|
||||
sheet1.write(17, 20, "Overhead Spent", line_amount_sub_title)
|
||||
sheet1.write(17, 21, "Remaining Budget", line_amount_sub_title)
|
||||
sheet1.write(17, 22, "Utilization(%)", sub_title)
|
||||
col = 18
|
||||
for data in self.budget_id.budget_line_ids:
|
||||
sheet1.row(col).height = 400
|
||||
sheet1.write_merge(
|
||||
col, col, 1, 3, data.job_type_id.name, line_values)
|
||||
sheet1.write_merge(
|
||||
col, col, 4, 6, data.sub_category_id.name, line_values)
|
||||
sheet1.write(col, 7, data.boq_qty, line_amount_values)
|
||||
sheet1.write_merge(
|
||||
col, col, 8, 10, data.additional_qty, line_amount_values)
|
||||
sheet1.write(col, 11, (data.boq_qty +
|
||||
data.additional_qty), line_amount_values)
|
||||
sheet1.write(col, 12, ((data.rate_analysis_id.name)
|
||||
if data.rate_analysis_id else ""), line_values)
|
||||
sheet1.write(col, 13, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.price_per_qty)), line_amount_values)
|
||||
sheet1.write(col, 14, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.untaxed_amount)), line_amount_values)
|
||||
sheet1.write(col, 15, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.tax_amount)), line_amount_values)
|
||||
sheet1.write(col, 16, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.budget)), line_amount_values)
|
||||
sheet1.write(col, 17, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.material_spent)), line_amount_values)
|
||||
sheet1.write(col, 18, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.equipment_spent)), line_amount_values)
|
||||
sheet1.write(col, 19, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.labour_spent)), line_amount_values)
|
||||
sheet1.write(col, 20, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.overhead_spent)), line_amount_values)
|
||||
sheet1.write(col, 21, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.remaining_budget)), line_amount_values)
|
||||
sheet1.write(
|
||||
col, 22, (str(data.total_spent) + " " + "%"), line_percentage_value)
|
||||
col = col + 1
|
||||
|
||||
# Budget Spent
|
||||
self.get_budget_spent(workbook=workbook)
|
||||
|
||||
# Print Report
|
||||
stream = BytesIO()
|
||||
workbook.save(stream)
|
||||
out = base64.encodebytes(stream.getvalue())
|
||||
attachment = self.env['ir.attachment'].sudo()
|
||||
filename = 'Budget Report' + ".xls"
|
||||
attachment_id = attachment.create(
|
||||
{'name': filename,
|
||||
'type': 'binary',
|
||||
'public': False,
|
||||
'datas': out})
|
||||
if attachment_id:
|
||||
report = {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/web/content/%s?download=true' % (attachment_id.id),
|
||||
'target': 'self',
|
||||
}
|
||||
return report
|
||||
|
||||
def get_budget_spent(self, workbook):
|
||||
title = xlwt.easyxf(
|
||||
"font: height 300, name Century Gothic, bold on, color_index blue_gray;"
|
||||
" align: vert center, horz center;"
|
||||
"border: bottom thick, bottom_color sea_green;")
|
||||
title2 = xlwt.easyxf(
|
||||
"font: height 250, name Century Gothic, bold on, color_index blue_gray;"
|
||||
" align: vert center, horz left;"
|
||||
"border: bottom thin, bottom_color sea_green;")
|
||||
title3 = xlwt.easyxf(
|
||||
"font: height 250, name Century Gothic, bold on;"
|
||||
" align: vert center, horz right;"
|
||||
"border: bottom thin, bottom_color sea_green;")
|
||||
line_amount_sub_title = xlwt.easyxf(
|
||||
"font: height 185, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz right; "
|
||||
"border: top hair, bottom hair, left hair, right hair, "
|
||||
"top_color gray50, bottom_color gray50, left_color gray50, right_color gray50")
|
||||
line_values = xlwt.easyxf(
|
||||
"font:name Century Gothic;"
|
||||
" align: vert center, horz center;"
|
||||
"border: bottom hair, bottom_color blue_gray, right hair,right_color blue_gray,left hair,left_color blue_gray;")
|
||||
sub_title = xlwt.easyxf(
|
||||
"font: height 185, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz center; "
|
||||
"border: top hair, bottom hair, left hair, right hair, "
|
||||
"top_color gray50, bottom_color gray50, left_color gray50, right_color gray50")
|
||||
sub_title2 = xlwt.easyxf(
|
||||
"font: height 200, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz left; "
|
||||
"border: bottom hair,"
|
||||
"bottom_color sea_green;")
|
||||
sub_title_amount = xlwt.easyxf(
|
||||
"font: height 200, name Century Gothic, bold on, color_index gray80; "
|
||||
"align: vert center, horz right; "
|
||||
"border: bottom hair,"
|
||||
"bottom_color sea_green;")
|
||||
horiz_double_line = xlwt.easyxf("border: top double, top_color gray50")
|
||||
for data in self.budget_id.budget_line_ids:
|
||||
budget_phase_ids = self.env['job.costing'].search(
|
||||
[('project_id', '=', self.id), ('activity_id', '=', data.job_type_id.id)]).mapped('id')
|
||||
domain = [('project_id', '=', self.id), ('work_type_id', '=', data.job_type_id.id),
|
||||
('sub_category_id', '=',
|
||||
data.sub_category_id.id), ('state', '=', 'complete'),
|
||||
('job_sheet_id', 'in', budget_phase_ids)]
|
||||
material_spent_rec = self.env['order.material.line'].search(domain)
|
||||
equip_spent_rec = self.env['order.equipment.line'].search(domain)
|
||||
labour_spent_rec = self.env['order.labour.line'].search(domain)
|
||||
overhead_spent_rec = self.env['order.overhead.line'].search(domain)
|
||||
sheet_name = data.job_type_id.name + \
|
||||
"(" + data.sub_category_id.name + ")"
|
||||
sheet = workbook.add_sheet(sheet_name, cell_overwrite_ok=True)
|
||||
sheet.show_grid = False
|
||||
row = 0
|
||||
sheet.row(4).height = 400
|
||||
sheet.row(6).height = 400
|
||||
sheet.write_merge(0, 2, 0, 6, sheet_name, title)
|
||||
sheet.write(4, 0, "Total Budget", sub_title2)
|
||||
sheet.write(4, 1, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.budget)), sub_title_amount)
|
||||
sheet.write_merge(4, 4, 3, 4, "Used Budget Amount", sub_title2)
|
||||
sheet.write_merge(4, 4, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str((data.budget - data.remaining_budget))), sub_title_amount)
|
||||
sheet.write_merge(6, 6, 3, 4, "Remaining Budget", sub_title2)
|
||||
sheet.write_merge(6, 6, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.remaining_budget)), sub_title_amount)
|
||||
|
||||
row = 5
|
||||
|
||||
# Material Rec
|
||||
sheet.write_merge(row+4, row+5, 0, 4, "Material Spent", title2)
|
||||
sheet.write_merge(row+4, row+5, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.material_spent)), title3)
|
||||
sheet.col(0).width = 8000
|
||||
sheet.col(1).width = 8000
|
||||
sheet.col(2).width = 6500
|
||||
sheet.col(3).width = 5000
|
||||
sheet.col(4).width = 5000
|
||||
sheet.col(5).width = 5000
|
||||
sheet.col(6).width = 5000
|
||||
sheet.row(row+6).height = 600
|
||||
sheet.write(row+6, 0, "Project Phase(WBS)", sub_title)
|
||||
sheet.write(row+6, 1, "Work Order", sub_title)
|
||||
sheet.write(row+6, 2, "Material", sub_title)
|
||||
sheet.write(row+6, 3, "Qty.", line_amount_sub_title)
|
||||
sheet.write(row+6, 4, "Unit", sub_title)
|
||||
sheet.write(row+6, 5, "Price/Qty", line_amount_sub_title)
|
||||
sheet.write(row+6, 6, "Total Price", line_amount_sub_title)
|
||||
row = row + 7
|
||||
for rec in material_spent_rec:
|
||||
sheet.row(row).height = 400
|
||||
sheet.write(row, 0, (rec.job_sheet_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 1, (rec.job_order_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 2, rec.name, line_values)
|
||||
sheet.write(row, 3, rec.qty, line_amount_sub_title)
|
||||
sheet.write(row, 4, rec.uom_id.name, line_values)
|
||||
sheet.write(row, 5, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.price)), line_amount_sub_title)
|
||||
sheet.write(row, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.total_price)), line_amount_sub_title)
|
||||
row = row + 1
|
||||
sheet.write_merge(row, row, 0, 6, " ", horiz_double_line)
|
||||
row = row + 1
|
||||
|
||||
# Equipment Rec
|
||||
sheet.write_merge(row+1, row+2, 0, 4, "Equipment Spent", title2)
|
||||
sheet.write_merge(row+1, row+2, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.equipment_spent)), title3)
|
||||
row = row + 3
|
||||
sheet.row(row).height = 600
|
||||
sheet.write(row, 0, "Project Phase(WBS)", sub_title)
|
||||
sheet.write(row, 1, "Work Order", sub_title)
|
||||
sheet.write(row, 2, "Vendor", sub_title)
|
||||
sheet.write(row, 3, "Equipment.", sub_title)
|
||||
sheet.write(row, 4, "Qty.", line_amount_sub_title)
|
||||
sheet.write(row, 5, "Price/Qty", line_amount_sub_title)
|
||||
sheet.write(row, 6, "Total Price", line_amount_sub_title)
|
||||
row = row+1
|
||||
for rec in equip_spent_rec:
|
||||
sheet.row(row).height = 400
|
||||
sheet.write(row, 0, (rec.job_sheet_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 1, (rec.job_order_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 2, rec.vendor_id.name, line_values)
|
||||
sheet.write(row, 3, rec.desc, line_values)
|
||||
sheet.write(row, 4, rec.qty, line_amount_sub_title)
|
||||
sheet.write(row, 5, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.cost)), line_amount_sub_title)
|
||||
sheet.write(row, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.total_cost)), line_amount_sub_title)
|
||||
row = row + 1
|
||||
sheet.write_merge(row, row, 0, 6, " ", horiz_double_line)
|
||||
row = row + 1
|
||||
|
||||
# Labour Rec
|
||||
sheet.write_merge(row+1, row+2, 0, 4, "Labour Spent", title2)
|
||||
sheet.write_merge(row+1, row+2, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.labour_spent)), title3)
|
||||
row = row + 3
|
||||
sheet.row(row).height = 600
|
||||
sheet.write(row, 0, "Project Phase(WBS)", sub_title)
|
||||
sheet.write(row, 1, "Work Order", sub_title)
|
||||
sheet.write(row, 2, "Vendor", sub_title)
|
||||
sheet.write(row, 3, "Product", sub_title)
|
||||
sheet.write(row, 4, "Hours", line_amount_sub_title)
|
||||
sheet.write(row, 5, "Cost / Hour", line_amount_sub_title)
|
||||
sheet.write(row, 6, "Sub Total", line_amount_sub_title)
|
||||
row = row+1
|
||||
for rec in labour_spent_rec:
|
||||
sheet.row(row).height = 400
|
||||
sheet.write(row, 0, (rec.job_sheet_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 1, (rec.job_order_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 2, rec.vendor_id.name, line_values)
|
||||
sheet.write(row, 3, rec.name, line_values)
|
||||
sheet.write(row, 4, rec.hours, line_amount_sub_title)
|
||||
sheet.write(row, 5, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.cost)), line_amount_sub_title)
|
||||
sheet.write(row, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.sub_total)), line_amount_sub_title)
|
||||
row = row + 1
|
||||
sheet.write_merge(row, row, 0, 6, " ", horiz_double_line)
|
||||
row = row + 1
|
||||
|
||||
# Overhead Rec
|
||||
sheet.write_merge(row+1, row+2, 0, 4, "Overhead Spent", title2)
|
||||
sheet.write_merge(row+1, row+2, 5, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(data.overhead_spent)), title3)
|
||||
row = row + 3
|
||||
sheet.row(row).height = 600
|
||||
sheet.write(row, 0, "Project Phase(WBS)", sub_title)
|
||||
sheet.write(row, 1, "Work Order", sub_title)
|
||||
sheet.write(row, 2, "Vendor", sub_title)
|
||||
sheet.write(row, 3, "Product", sub_title)
|
||||
sheet.write(row, 4, "Qty.", line_amount_sub_title)
|
||||
sheet.write(row, 5, "Cost / Qty", line_amount_sub_title)
|
||||
sheet.write(row, 6, "Sub Total", line_amount_sub_title)
|
||||
row = row+1
|
||||
for rec in overhead_spent_rec:
|
||||
sheet.row(row).height = 400
|
||||
sheet.write(row, 0, (rec.job_sheet_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 1, (rec.job_order_id.name +
|
||||
" - " + rec.job_order_id.name), line_values)
|
||||
sheet.write(row, 2, rec.vendor_id.name, line_values)
|
||||
sheet.write(row, 3, rec.name, line_values)
|
||||
sheet.write(row, 4, rec.qty, line_amount_sub_title)
|
||||
sheet.write(row, 5, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.cost)), line_amount_sub_title)
|
||||
sheet.write(row, 6, (str(self.budget_id.currency_id.symbol) +
|
||||
" " + str(rec.sub_total)), line_amount_sub_title)
|
||||
row = row + 1
|
||||
sheet.write_merge(row, row, 0, 6, " ", horiz_double_line)
|
||||
row = row + 1
|
||||
|
||||
|
||||
class ProjectDocuments(models.Model):
|
||||
_name = 'project.documents'
|
||||
_description = "Project Documents"
|
||||
_rec_name = 'file_name'
|
||||
|
||||
document_type_id = fields.Many2one('site.document.type', string="Document")
|
||||
document = fields.Binary(string='Documents', required=True)
|
||||
file_name = fields.Char(string='File Name')
|
||||
project_id = fields.Many2one('tk.construction.project')
|
||||
|
||||
|
||||
class ProjectInsurance(models.Model):
|
||||
_name = 'project.insurance'
|
||||
_description = "Project Insurance"
|
||||
|
||||
name = fields.Char(string="Insurance")
|
||||
policy_no = fields.Char(string="Insurance No")
|
||||
risk_ids = fields.Many2many('insurance.risk', string="Risk Covered")
|
||||
document = fields.Binary(string='Documents')
|
||||
file_name = fields.Char(string='File Name')
|
||||
project_id = fields.Many2one('tk.construction.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')
|
||||
total_charge = fields.Monetary(string="Total Charge")
|
||||
issue_date = fields.Date(string="Issue Date", default=fields.Date.today())
|
||||
bill_id = fields.Many2one('account.move', string="Bill")
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
|
||||
def action_create_bil(self):
|
||||
record = {
|
||||
'product_id': self.env.ref('tk_construction_management.construction_product_1').id,
|
||||
'name': self.project_id.name + "\n" + "Name : " + self.name + "\n" + "No : " + self.policy_no,
|
||||
'quantity': 1,
|
||||
'tax_ids': False,
|
||||
'price_unit': self.total_charge
|
||||
}
|
||||
line = [(0, 0, record)]
|
||||
main_data = {
|
||||
'partner_id': self.vendor_id.id,
|
||||
'move_type': 'in_invoice',
|
||||
'invoice_date': fields.date.today(),
|
||||
'invoice_line_ids': line,
|
||||
}
|
||||
bill_id = self.env['account.move'].create(main_data)
|
||||
self.bill_id = bill_id.id
|
||||
|
||||
|
||||
class ConstructionExtraExpense(models.Model):
|
||||
_name = 'extra.expense'
|
||||
_description = "Extra Expense"
|
||||
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string="Expense", domain="[('is_expense','=',True)]")
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
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 = fields.Monetary(string="Cost")
|
||||
bill_id = fields.Many2one('account.move', string="Bill")
|
||||
note = fields.Char(string="Note")
|
||||
project_id = fields.Many2one('tk.construction.project', string="Project")
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_expense_cost(self):
|
||||
for rec in self:
|
||||
rec.cost = rec.product_id.standard_price
|
||||
|
||||
def action_create_expense_bill(self):
|
||||
if self.vendor_id and self.product_id:
|
||||
data = {
|
||||
'product_id': self.product_id.id,
|
||||
'name': (self.product_id.name + " " + self.note) if self.note else self.product_id.name,
|
||||
'quantity': self.qty,
|
||||
'tax_ids': False,
|
||||
'price_unit': self.cost
|
||||
}
|
||||
invoice_line = [(0, 0, data)]
|
||||
invoice_id = self.env['account.move'].create({
|
||||
'partner_id': self.vendor_id.id,
|
||||
'invoice_line_ids': invoice_line,
|
||||
'move_type': 'in_invoice',
|
||||
})
|
||||
self.bill_id = invoice_id.id
|
||||
|
||||
|
||||
class BoqBudget(models.Model):
|
||||
_name = 'boq.budget'
|
||||
_description = "Boq Budget"
|
||||
_rec_name = 'activity_id'
|
||||
|
||||
project_id = fields.Many2one(
|
||||
'tk.construction.project', string="Sub Project")
|
||||
site_id = fields.Many2one(
|
||||
related="project_id.construction_site_id", string="Project")
|
||||
activity_id = fields.Many2one('job.type', string="Work Type")
|
||||
sub_activity_ids = fields.Many2many(
|
||||
related="activity_id.sub_category_ids", string="Sub Activities")
|
||||
sub_activity_id = fields.Many2one('job.sub.category', string="Work Sub Type",
|
||||
domain="[('id','in',sub_activity_ids)]")
|
||||
qty = fields.Float(string="Qty.", default=1.0)
|
||||
total_qty = fields.Float(
|
||||
string="Total Qty.", compute="compute_total_qty", store=True)
|
||||
length = fields.Float(string="Length")
|
||||
width = fields.Float(string="Width")
|
||||
height = fields.Float(string="Height")
|
||||
is_use_measure = fields.Boolean(
|
||||
related="project_id.is_use_measure", store=True)
|
||||
|
||||
@api.depends('project_id.is_use_measure', 'length', 'width', 'height', 'qty')
|
||||
def compute_total_qty(self):
|
||||
for rec in self:
|
||||
total_qty = 0.0
|
||||
if rec.project_id.is_use_measure:
|
||||
total_qty = rec.height * rec.width * rec.length * rec.qty
|
||||
else:
|
||||
total_qty = rec.qty
|
||||
rec.total_qty = total_qty
|
||||
119
addons/tk_construction_management/models/construction_scrap.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# -*- 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 ScrapOrder(models.Model):
|
||||
_name = 'scrap.order'
|
||||
_description = "Construction Scrap Order"
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(string='Sequence', copy=False, required=True, readonly=True, default=lambda self: _('New'))
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
note = fields.Text(string="Note")
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
scrap_order_line_ids = fields.One2many('scrap.order.line', 'scrap_order_id', string="Scrap Order Line")
|
||||
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')
|
||||
total = fields.Monetary(string="Total", compute="_compute_net_total", store=True)
|
||||
invoice_id = fields.Many2one('account.move', string="Invoice")
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
|
||||
@api.depends('scrap_order_line_ids')
|
||||
def _compute_net_total(self):
|
||||
for rec in self:
|
||||
amount = 0.0
|
||||
if rec.scrap_order_line_ids:
|
||||
for data in rec.scrap_order_line_ids:
|
||||
amount = amount + data.net_total
|
||||
rec.total = amount
|
||||
else:
|
||||
rec.total = 0.0
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('name', _('New')) == _('New'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('scrap.order') or _('New')
|
||||
res = super(ScrapOrder, self).create(vals_list)
|
||||
return res
|
||||
|
||||
@api.onchange('job_order_id')
|
||||
def _onchange_job_order(self):
|
||||
for rec in self:
|
||||
if not rec.job_order_id:
|
||||
return
|
||||
if rec.job_order_id:
|
||||
lines = []
|
||||
rec.scrap_order_line_ids = [(5, 0, 0)]
|
||||
for data in rec.job_order_id.material_order_ids:
|
||||
lines.append((0, 0, {
|
||||
'product_id': data.material_id.id,
|
||||
'scrap_type': 'material',
|
||||
'qty': 1,
|
||||
}))
|
||||
for data in rec.job_order_id.equipment_order_ids:
|
||||
lines.append((0, 0, {
|
||||
'product_id': data.equipment_id.id,
|
||||
'scrap_type': 'equipment',
|
||||
'qty': 1,
|
||||
}))
|
||||
for data in rec.job_order_id.overhead_order_ids:
|
||||
lines.append((0, 0, {
|
||||
'product_id': data.product_id.id,
|
||||
'scrap_type': 'overhead',
|
||||
'qty': 1,
|
||||
}))
|
||||
rec.scrap_order_line_ids = lines
|
||||
|
||||
def action_create_invoice(self):
|
||||
invoice_line = []
|
||||
for data in self.scrap_order_line_ids:
|
||||
invoice_line.append((0, 0, {
|
||||
'product_id': data.product_id.id,
|
||||
'name': data.product_id.name,
|
||||
'quantity': data.qty,
|
||||
'tax_ids': False,
|
||||
'price_unit': data.dep_cost
|
||||
}))
|
||||
invoice_id = self.env['account.move'].create({
|
||||
'partner_id': self.vendor_id.id,
|
||||
'invoice_line_ids': invoice_line,
|
||||
'move_type': 'out_invoice',
|
||||
})
|
||||
invoice_id.action_post()
|
||||
self.invoice_id = invoice_id.id
|
||||
|
||||
|
||||
class ScrapOrderLine(models.Model):
|
||||
_name = 'scrap.order.line'
|
||||
_description = "Scrap Order Line"
|
||||
|
||||
scrap_type = fields.Selection([('material', 'Material'), ('equipment', 'Equipment'), ('overhead', 'Overhead')],
|
||||
string="Scrap of")
|
||||
product_id = fields.Many2one("product.product", string="Product")
|
||||
qty = fields.Integer(string="Qty.")
|
||||
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')
|
||||
dep_cost = fields.Monetary(string="Value")
|
||||
scrap_order_id = fields.Many2one('scrap.order', string="Scrap Order")
|
||||
net_total = fields.Monetary(string="Total Value", compute="_compute_net_total")
|
||||
|
||||
@api.onchange('scrap_type')
|
||||
def filter_materia_equipment(self):
|
||||
for rec in self:
|
||||
if rec.scrap_type == "material":
|
||||
return {'domain': {'product_id': [('is_material', '=', True)]}}
|
||||
elif rec.scrap_type == "equipment":
|
||||
return {'domain': {'product_id': [('is_equipment', '=', True)]}}
|
||||
elif rec.scrap_type == 'overhead':
|
||||
return {'domain': {'product_id': [('is_overhead', '=', True)]}}
|
||||
|
||||
@api.depends('dep_cost', 'qty')
|
||||
def _compute_net_total(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.net_total = rec.dep_cost * rec.qty
|
||||
else:
|
||||
rec.net_total = 0
|
||||
203
addons/tk_construction_management/models/construction_site.py
Normal file
@@ -0,0 +1,203 @@
|
||||
# -*- 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, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ConstructionSite(models.Model):
|
||||
_name = 'tk.construction.site'
|
||||
_description = "Construction Project"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string="Title", tracking=True)
|
||||
start_date = fields.Date(string="Start Date", tracking=True)
|
||||
end_date = fields.Date(string="End Date", tracking=True)
|
||||
status = fields.Selection([('draft', 'Draft'), ('in_progress', 'In Progress'), ('complete', 'Complete')],
|
||||
default='draft', tracking=True)
|
||||
con_project_id = fields.Many2one('tk.construction.project', string="Project", tracking=True)
|
||||
phone = fields.Char(string="Phone")
|
||||
mobile = fields.Char(string="Mobile")
|
||||
email = fields.Char(string="Email")
|
||||
warehouse_id = fields.Many2one('stock.warehouse', string="Warehouse", tracking=True)
|
||||
|
||||
# Address
|
||||
zip = fields.Char(string='Pin Code')
|
||||
street = fields.Char(string='Street1')
|
||||
street2 = fields.Char(string='Street2')
|
||||
city = fields.Char(string='City')
|
||||
country_id = fields.Many2one('res.country', 'Country')
|
||||
state_id = fields.Many2one("res.country.state", string='State', readonly=False, store=True,
|
||||
domain="[('country_id', '=?', country_id)]")
|
||||
longitude = fields.Char(string="Longitude")
|
||||
latitude = fields.Char(string="Latitude")
|
||||
|
||||
# One2Many
|
||||
stakeholder_ids = fields.One2many('stakeholder.line', 'site_id')
|
||||
site_image_ids = fields.One2many('site.images', 'site_id')
|
||||
site_dimension_ids = fields.One2many('site.dimension', 'site_id')
|
||||
document_permit_ids = fields.One2many('document.permit', 'site_id')
|
||||
construction_project_ids = fields.One2many('tk.construction.project', 'construction_site_id')
|
||||
boq_ids = fields.One2many('tk.construction.project', 'construction_site_id')
|
||||
|
||||
# Count & Totals
|
||||
document_count = fields.Integer(string="Document Count", compute="_compute_count")
|
||||
project_count = fields.Integer(string="Project Count", compute="_compute_count")
|
||||
total_area = fields.Float(string="Total Area ", compute="_compute_total_area")
|
||||
|
||||
# Create, Write, Unlink, Constrain
|
||||
@api.constrains('stakeholder_ids')
|
||||
def _check_stakeholder_stack(self):
|
||||
for record in self.stakeholder_ids:
|
||||
duplicates = self.stakeholder_ids.filtered(
|
||||
lambda r: r.id != record.id and r.stakeholder_id.id == record.stakeholder_id.id)
|
||||
if duplicates:
|
||||
raise ValidationError(_("Stakeholder already added !"))
|
||||
|
||||
@api.constrains('stakeholder_ids')
|
||||
def _check_stakeholder_ids(self):
|
||||
percentage = 0.0
|
||||
for record in self.stakeholder_ids:
|
||||
percentage = percentage + record.percentage
|
||||
if percentage > 100:
|
||||
raise ValidationError(_("Percentage cannot exceed the limit of 100%."))
|
||||
|
||||
# Compute
|
||||
def _compute_count(self):
|
||||
for rec in self:
|
||||
rec.document_count = self.env['site.documents'].search_count([('site_id', '=', rec.id)])
|
||||
rec.project_count = self.env['tk.construction.project'].search_count(
|
||||
[('construction_site_id', '=', rec.id)])
|
||||
|
||||
@api.depends('site_dimension_ids')
|
||||
def _compute_total_area(self):
|
||||
for rec in self:
|
||||
total = 0.0
|
||||
if rec.site_dimension_ids:
|
||||
for data in rec.site_dimension_ids:
|
||||
total = total + data.area
|
||||
rec.total_area = total
|
||||
|
||||
# Smart Bottom
|
||||
def action_gmap_location(self):
|
||||
if self.longitude and self.latitude:
|
||||
longitude = self.longitude
|
||||
latitude = self.latitude
|
||||
http_url = 'https://maps.google.com/maps?q=loc:' + latitude + ',' + longitude
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'target': 'new',
|
||||
'url': http_url,
|
||||
}
|
||||
else:
|
||||
raise ValidationError(_("! Enter Proper Longitude and Latitude Values"))
|
||||
|
||||
def action_site_document(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Documents'),
|
||||
'res_model': 'site.documents',
|
||||
'domain': [('site_id', '=', self.id)],
|
||||
'context': {'default_site_id': self.id},
|
||||
'view_mode': 'list',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_project(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Project'),
|
||||
'res_model': 'tk.construction.project',
|
||||
'domain': [('construction_site_id', '=', self.id)],
|
||||
'context': {'default_construction_site_id': self.id},
|
||||
'view_mode': 'list,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
# Button
|
||||
def action_site_complete(self):
|
||||
self.status = 'complete'
|
||||
|
||||
def action_site_in_progress(self):
|
||||
self.status = 'in_progress'
|
||||
|
||||
|
||||
# Stakeholder
|
||||
class StakeholderLine(models.Model):
|
||||
_name = 'stakeholder.line'
|
||||
_description = "Stack Holder Line"
|
||||
_rec_name = 'stakeholder_id'
|
||||
|
||||
site_id = fields.Many2one('tk.construction.site', string="Construction Project")
|
||||
stakeholder_id = fields.Many2one('res.partner', domain="[('stack_holder','=',True)]")
|
||||
percentage = fields.Float(string="Percentage")
|
||||
image_1920 = fields.Binary(related="stakeholder_id.image_1920")
|
||||
phone = fields.Char(related="stakeholder_id.phone", string="Phone")
|
||||
email = fields.Char(related="stakeholder_id.email", string="Email")
|
||||
|
||||
|
||||
# Project Documents
|
||||
class SiteDocuments(models.Model):
|
||||
_name = 'site.documents'
|
||||
_description = "Project Documents"
|
||||
_rec_name = 'document_type_id'
|
||||
|
||||
document_type_id = fields.Many2one('site.document.type', string="Document Type")
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
site_id = fields.Many2one('tk.construction.site', string="Construction Project", ondelete='cascade')
|
||||
document = fields.Binary(string='Documents', required=True)
|
||||
file_name = fields.Char(string='File Name')
|
||||
|
||||
|
||||
# Project Images
|
||||
class SiteImages(models.Model):
|
||||
_name = 'site.images'
|
||||
_description = "Project Images"
|
||||
|
||||
site_id = fields.Many2one('tk.construction.site', string="Construction Project", ondelete='cascade')
|
||||
name = fields.Char(string='Title')
|
||||
image = fields.Image(string='Images')
|
||||
|
||||
|
||||
# Project Dimension
|
||||
class SiteDimension(models.Model):
|
||||
_name = 'site.dimension'
|
||||
_description = "Project Dimension"
|
||||
_rec_name = "site_id"
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
site_id = fields.Many2one('tk.construction.site', string="Construction Project", ondelete='cascade')
|
||||
length = fields.Float(string="Length(m)")
|
||||
width = fields.Float(string="Width(m)")
|
||||
area = fields.Float(string="Area", compute="_compute_area")
|
||||
|
||||
@api.depends('length', 'width')
|
||||
def _compute_area(self):
|
||||
for rec in self:
|
||||
area = 0.0
|
||||
if rec.length and rec.width:
|
||||
area = rec.length * rec.width
|
||||
rec.area = area
|
||||
|
||||
|
||||
# Document Permit
|
||||
class DocumentPermit(models.Model):
|
||||
_name = 'document.permit'
|
||||
_description = "Document Permit"
|
||||
_rec_name = 'document_type_id'
|
||||
|
||||
document_type_id = fields.Many2one('site.document.type', string="Document Type")
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
site_id = fields.Many2one('tk.construction.site', string="Construction Project", ondelete='cascade')
|
||||
document = fields.Binary(string='Documents', required=True)
|
||||
file_name = fields.Char(string='File Name')
|
||||
status = fields.Selection([('a', 'Approve'), ('r', 'Reject')], string="Status")
|
||||
feedback = fields.Char(string="Feedback")
|
||||
submitted_by = fields.Many2one('res.users', string="Submitted by",
|
||||
default=lambda self: self.env.user and self.env.user.id or False)
|
||||
|
||||
def action_approve(self):
|
||||
self.status = 'a'
|
||||
|
||||
def action_reject(self):
|
||||
self.status = 'r'
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- 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 ConstructionResUser(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
department_ids = fields.Many2many('construction.department', string="Department ")
|
||||
23
addons/tk_construction_management/models/department.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- 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 ConstructionDepartment(models.Model):
|
||||
_name = 'construction.department'
|
||||
_description = "Construction department"
|
||||
|
||||
name = fields.Char(string="Department Name")
|
||||
manager_ids = fields.Many2many('res.users', string="Manager")
|
||||
user_ids = fields.Many2many('res.users', 'construction_user_team_rel', 'user_id', 'team_id_id',
|
||||
string="Responsible", domain="[('id','not in',manager_ids)]")
|
||||
department_id = fields.Many2one('hr.department', string="HR Department")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
res = super(ConstructionDepartment, self).create(vals)
|
||||
for rec in res:
|
||||
for data in rec.manager_ids:
|
||||
data.department_ids = data.department_ids.ids + [rec.id]
|
||||
return res
|
||||
274
addons/tk_construction_management/models/internal_transfer.py
Normal file
@@ -0,0 +1,274 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models, api, _
|
||||
|
||||
|
||||
class InternalTransfer(models.Model):
|
||||
_name = 'internal.transfer'
|
||||
_description = 'Internal transfer material requisition'
|
||||
_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'), ('in_progress', 'In Progress'), ('done', 'Done'),
|
||||
('cancel', 'Cancel')], default='draft', string="Stage", tracking=True)
|
||||
is_order_created = fields.Boolean()
|
||||
delivery_order_check = fields.Boolean(compute="_compute_delivery_order_status")
|
||||
|
||||
# Other Details
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
|
||||
string="Created By")
|
||||
|
||||
# Project Details
|
||||
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company)
|
||||
site_id = fields.Many2one('tk.construction.site', string="Project")
|
||||
project_id = fields.Many2one('tk.construction.project', string="Sub Project")
|
||||
warehouse_id = fields.Many2one(related="site_id.warehouse_id")
|
||||
|
||||
# Work Type & Phase
|
||||
work_type_id = fields.Many2one('job.type', string="Work Type")
|
||||
job_sheet_id = fields.Many2one('job.costing', string="Phase(WBS)")
|
||||
work_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
material_req_id = fields.Many2one('material.requisition', string="Material Requisition")
|
||||
|
||||
# Internal Transfer & Forward Transfer
|
||||
is_forward_transfer = fields.Boolean(tracking=True)
|
||||
forward_transfer_id = fields.Many2one('internal.transfer', string="Forward Transfer")
|
||||
internal_ref = fields.Char(string="Internal Transfer Ref.")
|
||||
|
||||
# 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 User ")
|
||||
|
||||
# Additional Info
|
||||
vehicle_no = fields.Char(string="Vehicle No")
|
||||
vehicle_name = fields.Char(string="Name")
|
||||
model = fields.Char(string="Model")
|
||||
driver_name = fields.Char(string="Driver Name")
|
||||
phone = fields.Char(string="Phone")
|
||||
|
||||
# One 2 many
|
||||
internal_line_ids = fields.One2many('internal.transfer.line', 'internal_transfer_id',
|
||||
string="Internal Transfer Line")
|
||||
|
||||
# Count
|
||||
delivery_count = fields.Integer(string="Delivery 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'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('internal.transfer') or _('New')
|
||||
res = super(InternalTransfer, self).create(vals_list)
|
||||
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.delivery_count = self.env['stock.picking'].search_count([('transfer_id', '=', rec.id)])
|
||||
|
||||
# Compute
|
||||
@api.depends('internal_line_ids')
|
||||
def _compute_delivery_order_status(self):
|
||||
delivery = True
|
||||
delivery_orders = self.env['stock.picking'].search([('transfer_id', '=', self.id)])
|
||||
if delivery_orders:
|
||||
for rec in delivery_orders:
|
||||
if not rec.state == 'done':
|
||||
delivery = False
|
||||
break
|
||||
else:
|
||||
delivery = False
|
||||
if delivery:
|
||||
self.delivery_order_check = True
|
||||
else:
|
||||
self.delivery_order_check = False
|
||||
|
||||
# Onchange
|
||||
@api.onchange('department_id', 'manager_ids')
|
||||
def _onchange_department_manager(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.manager_ids.ids
|
||||
return {'domain': {'manager_ids': [('id', 'in', ids)]}}
|
||||
|
||||
@api.onchange('department_id', 'manager_ids', 'user_id')
|
||||
def _onchange_department_responsible(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.user_ids.ids
|
||||
return {'domain': {'user_id': [('id', 'in', ids)]}}
|
||||
|
||||
# Smart Button
|
||||
def action_view_delivery_order(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Delivery Orders'),
|
||||
'res_model': 'stock.picking',
|
||||
'domain': [('transfer_id', '=', self.id)],
|
||||
'context': {'default_transfer_id': self.id},
|
||||
'view_mode': 'list,form,kanban',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
# Button
|
||||
def action_ready_transfer(self):
|
||||
self.stage = 'in_progress'
|
||||
self.material_req_id.stage = 'internal_transfer'
|
||||
|
||||
def action_complete_transfer(self):
|
||||
self.material_req_id.stage = 'material_arrived'
|
||||
self.work_order_id.state = 'material_arrive'
|
||||
self.stage = 'done'
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order'),
|
||||
'res_model': 'job.order',
|
||||
'res_id': self.work_order_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_cancel_transfer(self):
|
||||
self.material_req_id.stage = 'cancel'
|
||||
self.stage = 'cancel'
|
||||
|
||||
def action_internal_transfer(self):
|
||||
pickup_warehouse_ids = self.internal_line_ids.mapped('pickup_warehouse_id').mapped('id')
|
||||
delivery_warehouse_ids = self.internal_line_ids.mapped('delivery_warehouse_id').mapped('id')
|
||||
for pickup in pickup_warehouse_ids:
|
||||
for delivery in delivery_warehouse_ids:
|
||||
lines = []
|
||||
created_ids = []
|
||||
stock_picking_type_id = False
|
||||
source_id = False
|
||||
destination_id = False
|
||||
for line in self.internal_line_ids:
|
||||
if line.pickup_warehouse_id.id == pickup and line.delivery_warehouse_id.id == delivery:
|
||||
lines.append((0, 0, {
|
||||
'product_id': line.product_id.id,
|
||||
'product_uom_qty': line.qty,
|
||||
'product_uom': line.uom_id.id,
|
||||
'location_id': line.pickup_warehouse_id.lot_stock_id.id,
|
||||
'location_dest_id': line.delivery_warehouse_id.lot_stock_id.id,
|
||||
'name': line.name
|
||||
}))
|
||||
source_id = line.pickup_warehouse_id.lot_stock_id
|
||||
destination_id = line.delivery_warehouse_id.lot_stock_id
|
||||
stock_picking_type_id = self.env['stock.picking.type'].search(
|
||||
[('code', '=', 'internal'), ('warehouse_id', '=', line.pickup_warehouse_id.id)], limit=1)
|
||||
created_ids.append(line.id)
|
||||
if lines and stock_picking_type_id and source_id and destination_id:
|
||||
delivery_record = {
|
||||
'picking_type_id': stock_picking_type_id.id,
|
||||
'location_id': source_id.id,
|
||||
'location_dest_id': destination_id.id,
|
||||
'move_ids_without_package': lines,
|
||||
'transfer_id': self.id,
|
||||
'move_type': 'one'
|
||||
}
|
||||
delivery_id = self.env['stock.picking'].create(delivery_record)
|
||||
delivery_id.action_confirm()
|
||||
for data in created_ids:
|
||||
internal_line_id = self.env['internal.transfer.line'].browse(data)
|
||||
internal_line_id.delivery_order_id = delivery_id.id
|
||||
self.material_req_id.internal_transfer_id = self.id
|
||||
self.is_order_created = True
|
||||
|
||||
def action_forward_transfer(self):
|
||||
is_any_forward = False
|
||||
for data in self.internal_line_ids:
|
||||
if data.delivery_warehouse_id.id == self.warehouse_id.id:
|
||||
is_any_forward = True
|
||||
if not is_any_forward:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "There are no any forward transfer in Internal transfer",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
if is_any_forward:
|
||||
internal_record = {
|
||||
'title': self.title,
|
||||
'site_id': self.site_id.id,
|
||||
'project_id': self.project_id.id,
|
||||
'work_type_id': self.work_type_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id,
|
||||
'work_order_id': self.work_order_id.id,
|
||||
'material_req_id': self.material_req_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
'department_id': self.department_id.id,
|
||||
'manager_ids': self.manager_ids.ids,
|
||||
'user_id': self.user_id.id,
|
||||
'stage': 'in_progress',
|
||||
'internal_ref': self.name
|
||||
}
|
||||
internal_transfer_id = self.env['internal.transfer'].create(internal_record)
|
||||
for data in self.internal_line_ids:
|
||||
if data.delivery_warehouse_id.id == self.warehouse_id.id:
|
||||
line_record = {
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'product_id': data.product_id.id,
|
||||
'name': data.name,
|
||||
'qty': data.qty,
|
||||
'pickup_warehouse_id': data.delivery_warehouse_id.id,
|
||||
'delivery_warehouse_id': data.building_id.warehouse_id.id,
|
||||
'internal_transfer_id': internal_transfer_id.id
|
||||
}
|
||||
self.env['internal.transfer.line'].create(line_record)
|
||||
self.forward_transfer_id = internal_transfer_id.id
|
||||
internal_transfer_id.is_forward_transfer = True
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Forward Transfer'),
|
||||
'res_model': 'internal.transfer',
|
||||
'res_id': internal_transfer_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
|
||||
class InternalTransferLine(models.Model):
|
||||
_name = 'internal.transfer.line'
|
||||
_description = "Internal Transfer Line"
|
||||
|
||||
product_id = fields.Many2one('product.product', string="Product")
|
||||
name = fields.Char(string="Description")
|
||||
qty = fields.Integer('Qty.')
|
||||
forcast_qty = fields.Char(string="Forecast Qty.", compute="_compute_forcast_qty")
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="UOM")
|
||||
pickup_warehouse_id = fields.Many2one('stock.warehouse', string="Pickup Warehouse")
|
||||
delivery_warehouse_id = fields.Many2one('stock.warehouse', string="Delivery Warehouse")
|
||||
internal_transfer_id = fields.Many2one('internal.transfer', string="Internal Transfer")
|
||||
delivery_order_id = fields.Many2one('stock.picking', string="Do")
|
||||
state = fields.Selection(related="delivery_order_id.state", string="Status")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
|
||||
@api.depends('pickup_warehouse_id', 'product_id')
|
||||
def _compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
qty = 0.0
|
||||
if rec.product_id:
|
||||
qty = rec.product_id.with_context(warehouse=rec.pickup_warehouse_id.id).virtual_available
|
||||
rec.forcast_qty = qty
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.product_id.name
|
||||
553
addons/tk_construction_management/models/job_costing.py
Normal file
@@ -0,0 +1,553 @@
|
||||
# -*- 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, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class JobCosting(models.Model):
|
||||
_name = 'job.costing'
|
||||
_description = "Job Costing"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string='Sequence', required=True, readonly=True, default=lambda self: _('New'))
|
||||
title = fields.Char(string="Title")
|
||||
create_date = fields.Date(string="Start Date", default=fields.Date.today())
|
||||
close_date = fields.Date(string="End Date")
|
||||
status = fields.Selection([('draft', 'Draft'), ('waiting_approval', 'Waiting Approval'), ('approved', 'Approved'),
|
||||
('in_progress', 'In Progress'), ('complete', 'Complete'), ('cancel', 'Cancel'),
|
||||
('reject', 'Reject')], default='draft', tracking=True)
|
||||
|
||||
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
|
||||
string="Created By")
|
||||
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')
|
||||
site_id = fields.Many2one('tk.construction.site', string="Project")
|
||||
project_id = fields.Many2one('tk.construction.project', string="Sub Project")
|
||||
activity_id = fields.Many2one('job.type', string="Work Type")
|
||||
|
||||
# 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")
|
||||
|
||||
# One2 many
|
||||
work_order_ids = fields.One2many('job.order', 'job_sheet_id')
|
||||
cost_material_ids = fields.One2many('cost.material.line', 'job_costing_id')
|
||||
cost_equipment_ids = fields.One2many('cost.equipment.line', 'job_costing_id')
|
||||
cost_labour_ids = fields.One2many('cost.labour.line', 'job_costing_id')
|
||||
cost_overhead_ids = fields.One2many('cost.overhead.line', 'job_costing_id')
|
||||
|
||||
# Total
|
||||
material_total_cost = fields.Monetary(string="Total Material Cost", compute="_compute_total")
|
||||
equipment_total_cost = fields.Monetary(string="Total Equipment Cost", compute="_compute_total")
|
||||
labour_total_cost = fields.Monetary(string="Total Labour Cost", compute="_compute_total")
|
||||
overhead_total_cost = fields.Monetary(string="Total Overhead Cost", compute="_compute_total")
|
||||
material_actual_cost = fields.Monetary(string="Actual Material Cost", compute="_compute_total")
|
||||
equipment_actual_cost = fields.Monetary(string="Actual Equipment Cost", compute="_compute_total")
|
||||
labour_actual_cost = fields.Monetary(string="Actual Labour Cost", compute="_compute_total")
|
||||
overhead_actual_cost = fields.Monetary(string="Actual Overhead Cost", compute="_compute_total")
|
||||
|
||||
# Count
|
||||
job_order_count = fields.Integer(string="Jon Order", compute="_compute_count")
|
||||
mrq_count = fields.Integer(string="MRQ Order", compute="_compute_count")
|
||||
|
||||
# Budget Qty.
|
||||
sub_work_type_ids = fields.Many2many('job.sub.category')
|
||||
|
||||
# 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.costing') or _('New'))
|
||||
res = super(JobCosting, self).create(vals_list)
|
||||
return res
|
||||
|
||||
def name_get(self):
|
||||
data = []
|
||||
for rec in self:
|
||||
data.append((rec.id, '%s - %s' % (rec.name, rec.title)))
|
||||
return data
|
||||
|
||||
# Onchange
|
||||
@api.onchange('project_id')
|
||||
def _onchange_project_info(self):
|
||||
for rec in self:
|
||||
rec.project_id = rec.project_id.id
|
||||
rec.department_id = rec.project_id.department_id.id
|
||||
rec.manager_ids = rec.project_id.manager_ids.ids
|
||||
rec.user_id = rec.project_id.user_id.id
|
||||
|
||||
# Compute
|
||||
@api.depends('cost_material_ids', 'cost_equipment_ids', 'cost_labour_ids', 'cost_overhead_ids')
|
||||
def _compute_total(self):
|
||||
material, equipment, labour, overhead = 0.0, 0.0, 0.0, 0.0
|
||||
material_actual_cost = self.env['order.material.line'].search(
|
||||
[('job_sheet_id', '=', self.id), ('state', '=', 'complete')]).mapped(
|
||||
'total_price')
|
||||
equipment_actual_cost = self.env['order.equipment.line'].search(
|
||||
[('job_sheet_id', '=', self.id), ('state', '=', 'complete')]).mapped(
|
||||
'total_cost')
|
||||
labour_actual_cost = self.env['order.labour.line'].search(
|
||||
[('job_sheet_id', '=', self.id), ('state', '=', 'complete')]).mapped(
|
||||
'sub_total')
|
||||
overhead_actual_cost = self.env['order.overhead.line'].search(
|
||||
[('job_sheet_id', '=', self.id), ('state', '=', 'complete')]).mapped(
|
||||
'sub_total')
|
||||
for rec in self:
|
||||
for data in rec.cost_material_ids:
|
||||
material = material + data.total_cost
|
||||
for data in rec.cost_equipment_ids:
|
||||
equipment = equipment + data.total_cost
|
||||
for data in rec.cost_labour_ids:
|
||||
labour = labour + data.sub_total
|
||||
for data in rec.cost_overhead_ids:
|
||||
overhead = overhead + data.sub_total
|
||||
rec.material_total_cost = material
|
||||
rec.equipment_total_cost = equipment
|
||||
rec.labour_total_cost = labour
|
||||
rec.overhead_total_cost = overhead
|
||||
rec.material_actual_cost = sum(material_actual_cost)
|
||||
rec.equipment_actual_cost = sum(equipment_actual_cost)
|
||||
rec.labour_actual_cost = sum(labour_actual_cost)
|
||||
rec.overhead_actual_cost = sum(overhead_actual_cost)
|
||||
|
||||
def _compute_count(self):
|
||||
for rec in self:
|
||||
rec.job_order_count = self.env['job.order'].search_count([('job_sheet_id', '=', rec.id)])
|
||||
rec.mrq_count = self.env['material.requisition'].search_count([('job_sheet_id', '=', rec.id)])
|
||||
|
||||
# Smart Button
|
||||
def action_view_job_order(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order'),
|
||||
'res_model': 'job.order',
|
||||
'domain': [('job_sheet_id', '=', self.id)],
|
||||
'context': {'create': False},
|
||||
'view_mode': 'list,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_mrq(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Material Requisition'),
|
||||
'res_model': 'material.requisition',
|
||||
'domain': [('job_sheet_id', '=', self.id)],
|
||||
'context': {'default_job_sheet_id': self.id},
|
||||
'view_mode': 'list,form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_department_approval(self):
|
||||
self.status = 'waiting_approval'
|
||||
|
||||
def action_approve_phase(self):
|
||||
self.status = 'approved'
|
||||
|
||||
def action_reject_phase(self):
|
||||
self.status = 'reject'
|
||||
|
||||
def action_in_progress(self):
|
||||
self.status = 'in_progress'
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
self.status = 'draft'
|
||||
|
||||
def action_cancel_phase(self):
|
||||
self.status = 'cancel'
|
||||
|
||||
def action_complete_phase(self):
|
||||
is_complete_work_order = True
|
||||
for data in self.work_order_ids:
|
||||
if data.state != 'complete':
|
||||
is_complete_work_order = False
|
||||
break
|
||||
if not is_complete_work_order:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please complete all work orders related to this phase.",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
|
||||
if is_complete_work_order:
|
||||
self.status = 'complete'
|
||||
|
||||
def action_create_work_order(self):
|
||||
material_line = []
|
||||
equipment_line = []
|
||||
labour_line = []
|
||||
overhead_line = []
|
||||
ctx = {
|
||||
'default_site_id': self.site_id.id,
|
||||
'default_project_id': self.project_id.id,
|
||||
'default_start_date': self.create_date,
|
||||
'default_end_date': self.close_date,
|
||||
'default_work_type_id': self.activity_id.id,
|
||||
'default_job_sheet_id': self.id,
|
||||
'default_department_id': self.department_id.id,
|
||||
'default_manager_ids': self.manager_ids.ids,
|
||||
'default_user_id': self.user_id.id
|
||||
}
|
||||
for data in self.cost_material_ids:
|
||||
material_line.append((0, 0, {
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'material_id': data.material_id.id,
|
||||
'name': data.name,
|
||||
'qty': data.forcast_qty,
|
||||
'remain_qty': data.forcast_qty,
|
||||
'phase_forcast_qty': data.forcast_qty,
|
||||
'tax_id': data.tax_id.id,
|
||||
'price': data.cost
|
||||
}))
|
||||
for data in self.cost_equipment_ids:
|
||||
equipment_line.append((0, 0, {
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'equipment_id': data.equipment_id.id,
|
||||
'cost_type': data.cost_type,
|
||||
'desc': data.name,
|
||||
'qty': data.forcast_qty,
|
||||
'cost': data.cost,
|
||||
'phase_forcast_qty': data.forcast_qty,
|
||||
'tax_id': data.tax_id.id,
|
||||
}))
|
||||
for data in self.cost_labour_ids:
|
||||
labour_line.append((0, 0, {
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'product_id': data.product_id.id,
|
||||
'name': data.name,
|
||||
'hours': data.forcast_qty,
|
||||
'cost': data.cost,
|
||||
'phase_forcast_qty': data.forcast_qty,
|
||||
'tax_id': data.tax_id.id,
|
||||
}))
|
||||
for data in self.cost_overhead_ids:
|
||||
overhead_line.append((0, 0, {
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'product_id': data.product_id.id,
|
||||
'name': data.name,
|
||||
'qty': data.forcast_qty,
|
||||
'cost': data.cost,
|
||||
'phase_forcast_qty': data.forcast_qty,
|
||||
'tax_id': data.tax_id.id,
|
||||
}))
|
||||
ctx['default_material_order_ids'] = material_line
|
||||
ctx['default_equipment_order_ids'] = equipment_line
|
||||
ctx['default_labour_order_ids'] = labour_line
|
||||
ctx['default_overhead_order_ids'] = overhead_line
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order'),
|
||||
'res_model': 'job.order',
|
||||
'context': ctx,
|
||||
'view_mode': 'form',
|
||||
'target': 'new'
|
||||
}
|
||||
|
||||
|
||||
class CostMaterialLine(models.Model):
|
||||
_name = 'cost.material.line'
|
||||
_description = 'Construction Job Cost 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)
|
||||
cost = fields.Float(string="Cost")
|
||||
total_cost = fields.Monetary(string="Total Cost", compute="_compute_total_cost", store=True)
|
||||
uom_id = fields.Many2one(related="material_id.uom_po_id", string="Unit of Measure")
|
||||
job_costing_id = fields.Many2one('job.costing', string="Job Costing")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
tax_id = fields.Many2one('account.tax', string="Taxes")
|
||||
budget_qty = fields.Float(string="Budget Qty.", compute="compute_budget_qty", store=True)
|
||||
boq_per_qty = fields.Float(string="Per BOQ RA. Qty.")
|
||||
total_remain_boq_qty = fields.Float(string="Total Remain BOQ Qty")
|
||||
forcast_qty = fields.Float(string="Forcast Qty.", compute="compute_forcast_qty")
|
||||
used_qty = fields.Float(string="Used Qty.")
|
||||
used_budget_qty = fields.Float(string="Used Budget Qty.")
|
||||
remain_qty = fields.Float(string="Remain Qty.", compute="compute_remain_qty")
|
||||
|
||||
@api.onchange('material_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.material_id.name
|
||||
rec.cost = rec.material_id.standard_price
|
||||
|
||||
@api.depends('material_id', 'qty', 'cost', 'tax_id')
|
||||
def _compute_total_cost(self):
|
||||
for rec in self:
|
||||
total_cost = 0.0
|
||||
tax_amount = 0.0
|
||||
if rec.material_id:
|
||||
total_cost = rec.qty * rec.cost
|
||||
if rec.tax_id:
|
||||
tax_amount = rec.tax_id.amount * (rec.qty * rec.cost) / 100
|
||||
total_cost = tax_amount + total_cost
|
||||
rec.total_cost = total_cost
|
||||
|
||||
@api.depends('boq_per_qty', 'qty')
|
||||
def compute_budget_qty(self):
|
||||
for rec in self:
|
||||
budget_qty = 0
|
||||
if rec.boq_per_qty > 0:
|
||||
budget_qty = rec.qty / rec.boq_per_qty
|
||||
rec.budget_qty = budget_qty
|
||||
|
||||
@api.depends('qty',
|
||||
'job_costing_id.activity_id',
|
||||
'material_id',
|
||||
'sub_category_id',
|
||||
'job_costing_id.work_order_ids.material_order_ids.qty')
|
||||
def compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
forcast_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
for data in record.material_order_ids:
|
||||
if data.material_id.id == rec.material_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
forcast_qty = forcast_qty + data.qty
|
||||
rec.forcast_qty = rec.qty - forcast_qty
|
||||
|
||||
@api.depends('job_costing_id.work_order_ids.material_order_ids.qty', 'job_costing_id.work_order_ids.state', 'qty')
|
||||
def compute_remain_qty(self):
|
||||
for rec in self:
|
||||
remain_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
if record.state == 'complete':
|
||||
for data in record.material_order_ids:
|
||||
if data.material_id.id == rec.material_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
remain_qty = remain_qty + data.qty
|
||||
rec.remain_qty = rec.qty - remain_qty
|
||||
|
||||
|
||||
class CostEquipmentLine(models.Model):
|
||||
_name = 'cost.equipment.line'
|
||||
_description = 'Construction Job Cost 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')
|
||||
name = fields.Char(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)
|
||||
job_costing_id = fields.Many2one('job.costing', string="Job Costing")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
tax_id = fields.Many2one('account.tax', string="Taxes")
|
||||
budget_qty = fields.Float(string="Budget Qty.", compute="compute_budget_qty", store=True)
|
||||
boq_per_qty = fields.Float(string="Per BOQ RA. Qty.")
|
||||
total_remain_boq_qty = fields.Float(string="Total Remain BOQ Qty")
|
||||
forcast_qty = fields.Float(string="Forcast Qty.", compute="compute_forcast_qty")
|
||||
used_qty = fields.Float(string="Used Qty.")
|
||||
used_budget_qty = fields.Float(string="Used Budget Qty.")
|
||||
remain_qty = fields.Float(string="Remain Qty.", compute="compute_remain_qty")
|
||||
|
||||
@api.depends('equipment_id', 'qty', 'cost', 'tax_id')
|
||||
def _compute_total_cost(self):
|
||||
for rec in self:
|
||||
total_cost = 0.0
|
||||
if rec.equipment_id:
|
||||
total_cost = rec.qty * rec.cost
|
||||
if rec.tax_id:
|
||||
tax_amount = rec.tax_id.amount * (rec.qty * rec.cost) / 100
|
||||
total_cost = tax_amount + total_cost
|
||||
rec.total_cost = total_cost
|
||||
|
||||
@api.onchange('equipment_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.equipment_id.name
|
||||
rec.cost = rec.equipment_id.standard_price
|
||||
|
||||
@api.depends('boq_per_qty', 'qty')
|
||||
def compute_budget_qty(self):
|
||||
for rec in self:
|
||||
budget_qty = 0
|
||||
if rec.boq_per_qty > 0:
|
||||
budget_qty = rec.qty / rec.boq_per_qty
|
||||
rec.budget_qty = budget_qty
|
||||
|
||||
@api.depends('qty',
|
||||
'job_costing_id.activity_id',
|
||||
'equipment_id',
|
||||
'sub_category_id',
|
||||
'job_costing_id.work_order_ids.equipment_order_ids.qty')
|
||||
def compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
forcast_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
for data in record.equipment_order_ids:
|
||||
if data.equipment_id.id == rec.equipment_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
forcast_qty = forcast_qty + data.qty
|
||||
rec.forcast_qty = rec.qty - forcast_qty
|
||||
|
||||
@api.depends('job_costing_id.work_order_ids.equipment_order_ids.qty', 'job_costing_id.work_order_ids.state', 'qty')
|
||||
def compute_remain_qty(self):
|
||||
for rec in self:
|
||||
remain_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
if record.state == 'complete':
|
||||
for data in record.equipment_order_ids:
|
||||
if data.equipment_id.id == rec.equipment_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
remain_qty = remain_qty + data.qty
|
||||
rec.remain_qty = rec.qty - remain_qty
|
||||
|
||||
|
||||
class CostLabourLine(models.Model):
|
||||
_name = 'cost.labour.line'
|
||||
_description = "Cost Labour Line"
|
||||
|
||||
job_costing_id = fields.Many2one('job.costing', string="Job Costing")
|
||||
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(string="Remaining Hours")
|
||||
cost = fields.Monetary(string="Cost / Hour")
|
||||
sub_total = fields.Monetary(string="Sub Total", compute="_compute_total_cost", store=True)
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
tax_id = fields.Many2one('account.tax', string="Taxes")
|
||||
budget_qty = fields.Float(string="Budget Qty.", compute="compute_budget_qty", store=True)
|
||||
boq_per_qty = fields.Float(string="Per BOQ RA. Qty.")
|
||||
total_remain_boq_qty = fields.Float(string="Total Remain BOQ Qty")
|
||||
forcast_qty = fields.Float(string="Forcast Hours", compute="compute_forcast_qty")
|
||||
used_qty = fields.Float(string="Used Qty.")
|
||||
used_budget_qty = fields.Float(string="Used Budget Qty.")
|
||||
remain_qty = fields.Float(string="Remain Hours", compute="compute_remain_qty")
|
||||
|
||||
@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('product_id', 'hours', 'cost', 'tax_id')
|
||||
def _compute_total_cost(self):
|
||||
for rec in self:
|
||||
total_cost = 0.0
|
||||
if rec.product_id:
|
||||
total_cost = rec.hours * rec.cost
|
||||
if rec.tax_id:
|
||||
tax_amount = rec.tax_id.amount * (rec.hours * rec.cost) / 100
|
||||
total_cost = tax_amount + total_cost
|
||||
rec.sub_total = total_cost
|
||||
|
||||
@api.depends('boq_per_qty', 'hours')
|
||||
def compute_budget_qty(self):
|
||||
for rec in self:
|
||||
budget_qty = 0
|
||||
if rec.boq_per_qty > 0:
|
||||
budget_qty = rec.hours / rec.boq_per_qty
|
||||
rec.budget_qty = budget_qty
|
||||
|
||||
@api.depends('hours',
|
||||
'job_costing_id.activity_id',
|
||||
'product_id',
|
||||
'sub_category_id',
|
||||
'job_costing_id.work_order_ids.labour_order_ids.hours')
|
||||
def compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
forcast_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
for data in record.labour_order_ids:
|
||||
if data.product_id.id == rec.product_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
forcast_qty = forcast_qty + data.hours
|
||||
rec.forcast_qty = rec.hours - forcast_qty
|
||||
|
||||
@api.depends('job_costing_id.work_order_ids.labour_order_ids.hours', 'job_costing_id.work_order_ids.state', 'hours')
|
||||
def compute_remain_qty(self):
|
||||
for rec in self:
|
||||
remain_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
if record.state == 'complete':
|
||||
for data in record.labour_order_ids:
|
||||
if data.product_id.id == rec.product_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
remain_qty = remain_qty + data.hours
|
||||
rec.remain_qty = rec.hours - remain_qty
|
||||
|
||||
|
||||
class CostOverheadLine(models.Model):
|
||||
_name = 'cost.overhead.line'
|
||||
_description = "Cost Overhead Line"
|
||||
|
||||
job_costing_id = fields.Many2one('job.costing', string="Job Costing")
|
||||
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="UOM")
|
||||
cost = fields.Monetary(string="Cost / Unit")
|
||||
sub_total = fields.Monetary(string="Sub Total", compute="_compute_total_cost", store=True)
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
tax_id = fields.Many2one('account.tax', string="Taxes")
|
||||
budget_qty = fields.Float(string="Budget Qty.", compute="compute_budget_qty", store=True)
|
||||
boq_per_qty = fields.Float(string="Per BOQ RA. Qty.")
|
||||
total_remain_boq_qty = fields.Float(string="Total Remain BOQ Qty")
|
||||
forcast_qty = fields.Float(string="Forcast Qty.", compute="compute_forcast_qty")
|
||||
used_qty = fields.Float(string="Used Qty.")
|
||||
used_budget_qty = fields.Float(string="Used Budget Qty.")
|
||||
remain_qty = fields.Float(string="Remain Qty.", compute="compute_remain_qty")
|
||||
|
||||
@api.depends('product_id', 'qty', 'cost', 'tax_id')
|
||||
def _compute_total_cost(self):
|
||||
for rec in self:
|
||||
total_cost = 0.0
|
||||
if rec.product_id:
|
||||
total_cost = rec.qty * rec.cost
|
||||
if rec.tax_id:
|
||||
tax_amount = rec.tax_id.amount * (rec.qty * rec.cost) / 100
|
||||
total_cost = tax_amount + total_cost
|
||||
rec.sub_total = total_cost
|
||||
|
||||
@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('boq_per_qty', 'qty')
|
||||
def compute_budget_qty(self):
|
||||
for rec in self:
|
||||
budget_qty = 0
|
||||
if rec.boq_per_qty > 0:
|
||||
budget_qty = rec.qty / rec.boq_per_qty
|
||||
rec.budget_qty = budget_qty
|
||||
|
||||
@api.depends('qty',
|
||||
'job_costing_id.activity_id',
|
||||
'product_id',
|
||||
'sub_category_id',
|
||||
'job_costing_id.work_order_ids.overhead_order_ids.qty')
|
||||
def compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
forcast_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
for data in record.overhead_order_ids:
|
||||
if data.product_id.id == rec.product_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
forcast_qty = forcast_qty + data.qty
|
||||
rec.forcast_qty = rec.qty - forcast_qty
|
||||
|
||||
@api.depends('job_costing_id.work_order_ids.overhead_order_ids.qty', 'job_costing_id.work_order_ids.state', 'qty')
|
||||
def compute_remain_qty(self):
|
||||
for rec in self:
|
||||
remain_qty = 0.0
|
||||
for record in rec.job_costing_id.work_order_ids:
|
||||
if record.state == 'complete':
|
||||
for data in record.overhead_order_ids:
|
||||
if data.product_id.id == rec.product_id.id and data.sub_category_id.id == rec.sub_category_id.id:
|
||||
remain_qty = remain_qty + data.qty
|
||||
rec.remain_qty = rec.qty - remain_qty
|
||||
748
addons/tk_construction_management/models/job_order.py
Normal 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
|
||||
735
addons/tk_construction_management/models/material_requisition.py
Normal file
@@ -0,0 +1,735 @@
|
||||
# -*- 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, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class MaterialRequisition(models.Model):
|
||||
_name = 'material.requisition'
|
||||
_description = "Material Requisition"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string='Sequence', required=True,
|
||||
readonly=True, default=lambda self: _('New'))
|
||||
title = fields.Char(string="Title", tracking=True)
|
||||
|
||||
reject_reason = fields.Text(string="Reject Reason")
|
||||
allow_resubmit = fields.Boolean(string="Allow Resubmit", tracking=True)
|
||||
po_created = fields.Boolean()
|
||||
delivery_ready = fields.Boolean(compute="_compute_delivery_ready")
|
||||
line_added = fields.Boolean()
|
||||
stage = fields.Selection(
|
||||
[('draft', 'Draft'),
|
||||
('department_approval', 'Waiting Department Approval'),
|
||||
('approve', 'In Progress'),
|
||||
('ready_delivery', 'Ready for Delivery'),
|
||||
('reject', 'Reject'),
|
||||
('internal_transfer', 'Internal Transfer'),
|
||||
('material_arrived', 'Material Arrived'),
|
||||
('cancel', 'Cancel')], default='draft', tracking=True)
|
||||
desc = fields.Text(string="Description")
|
||||
|
||||
# Project Details
|
||||
site_id = fields.Many2one('tk.construction.site', string="Project")
|
||||
project_id = fields.Many2one(
|
||||
'tk.construction.project', string="Sub Project", tracking=True)
|
||||
warehouse_id = fields.Many2one(
|
||||
related="project_id.warehouse_id", string="Warehouse")
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string="Company", default=lambda self: self.env.company)
|
||||
|
||||
# Other Details
|
||||
date = fields.Datetime(string="Date", default=fields.Datetime.now())
|
||||
responsible_id = fields.Many2one('res.users', default=lambda self: self.env.user and self.env.user.id or False,
|
||||
string="Created By")
|
||||
|
||||
# Department Details
|
||||
department_id = fields.Many2one(
|
||||
'construction.department', string="Department", tracking=True)
|
||||
manager_ids = fields.Many2many('res.users', string="Manager")
|
||||
user_id = fields.Many2one('res.users', string="Responsible User ")
|
||||
|
||||
# Work Type & Job Sheet and Phase
|
||||
work_type_id = fields.Many2one('job.type', string="Work Type")
|
||||
work_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
job_sheet_id = fields.Many2one(
|
||||
related="work_order_id.job_sheet_id", store=True)
|
||||
|
||||
internal_transfer_id = fields.Many2one(
|
||||
'internal.transfer', string="Transfer Ref.")
|
||||
|
||||
# Back Order
|
||||
is_back_order = fields.Boolean(tracking=True)
|
||||
is_any_back_order = fields.Boolean(compute="_compute_any_back_order")
|
||||
back_order_id = fields.Many2one(
|
||||
'material.requisition', string="Back Order", tracking=True)
|
||||
material_req_ref = fields.Char(
|
||||
string="Material Requisition Ref.", tracking=True)
|
||||
|
||||
# One 2 Many
|
||||
material_line_ids = fields.One2many(
|
||||
'material.requisition.line', 'material_req_id')
|
||||
material_purchase_ids = fields.One2many(
|
||||
'material.purchase.line', 'material_req_id')
|
||||
material_transfer_ids = fields.One2many(
|
||||
'material.transfer.line', 'material_req_id')
|
||||
|
||||
# compute
|
||||
po_count = fields.Integer(
|
||||
string="Purchase Order Count", compute="_compute_count")
|
||||
bill_count = fields.Integer(string="Bill Count", compute="_compute_count")
|
||||
delivery_count = fields.Integer(
|
||||
string="Delivery 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'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code(
|
||||
'material.req') or _('New')
|
||||
res = super(MaterialRequisition, self).create(vals_list)
|
||||
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(
|
||||
[('material_req_id', '=', rec.id)])
|
||||
rec.bill_count = self.env['account.move'].search_count(
|
||||
[('material_req_id', '=', rec.id)])
|
||||
po = self.material_purchase_ids.mapped(
|
||||
'purchase_order_id').mapped('name')
|
||||
ids = self.env['stock.picking'].search(
|
||||
[('origin', 'in', po), ('code', '=', 'incoming')]).mapped('id')
|
||||
rec.delivery_count = self.env['stock.picking'].search_count(
|
||||
[('id', 'in', ids)])
|
||||
|
||||
def _compute_delivery_ready(self):
|
||||
if self.material_purchase_ids:
|
||||
incomplete = False
|
||||
for rec in self.material_purchase_ids:
|
||||
if not rec.status == 'complete':
|
||||
incomplete = True
|
||||
break
|
||||
if not incomplete:
|
||||
self.delivery_ready = True
|
||||
else:
|
||||
self.delivery_ready = False
|
||||
else:
|
||||
self.delivery_ready = True
|
||||
|
||||
@api.depends('material_line_ids')
|
||||
def _compute_any_back_order(self):
|
||||
back_order = False
|
||||
for rec in self.material_line_ids:
|
||||
if rec.operation_type == 'back_order':
|
||||
back_order = True
|
||||
break
|
||||
if back_order:
|
||||
self.is_any_back_order = True
|
||||
else:
|
||||
self.is_any_back_order = False
|
||||
|
||||
# Onchange
|
||||
@api.onchange('department_id', 'manager_ids')
|
||||
def _onchange_department_manager(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.manager_ids.ids
|
||||
return {'domain': {'manager_ids': [('id', 'in', ids)]}}
|
||||
|
||||
@api.onchange('department_id', 'manager_ids', 'user_id')
|
||||
def _onchange_department_responsible(self):
|
||||
ids = []
|
||||
for rec in self:
|
||||
if rec.department_id:
|
||||
ids = rec.department_id.user_ids.ids
|
||||
return {'domain': {'user_id': [('id', 'in', ids)]}}
|
||||
|
||||
# Smart Button
|
||||
def action_view_purchase_order(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Purchase Order'),
|
||||
'res_model': 'purchase.order',
|
||||
'domain': [('material_req_id', '=', self.id)],
|
||||
'context': {
|
||||
'default_material_req_id': self.id
|
||||
},
|
||||
'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': [('material_req_id', '=', self.id)],
|
||||
'context': {
|
||||
'default_material_req_id': self.id
|
||||
},
|
||||
'view_mode': 'list,form,kanban',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def action_view_delivery_order(self):
|
||||
po = self.material_purchase_ids.mapped(
|
||||
'purchase_order_id').mapped('name')
|
||||
ids = self.env['stock.picking'].search(
|
||||
[('origin', 'in', po)]).mapped('id')
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Delivery Orders'),
|
||||
'res_model': 'stock.picking',
|
||||
'domain': [('id', 'in', ids), ('code', '=', 'incoming')],
|
||||
'view_mode': 'list,form,kanban',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
# Button
|
||||
def action_department_approval(self):
|
||||
user_id = self.env.user.id
|
||||
msg = "<ul><li>Request for Department Approval of <strong>" + str(
|
||||
self.name) + "</strong>" + " On <strong>" + str(
|
||||
fields.Datetime.now()) + "</strong></li></ul>"
|
||||
self.message_post(body=msg, partner_ids=[user_id])
|
||||
self.stage = 'department_approval'
|
||||
|
||||
def action_approve_requisition(self):
|
||||
material_arrives = True
|
||||
for rec in self.material_line_ids:
|
||||
if not (rec.forcast_qty >= rec.qty):
|
||||
material_arrives = False
|
||||
if material_arrives:
|
||||
self.stage = 'material_arrived'
|
||||
self.work_order_id.state = 'material_arrive'
|
||||
else:
|
||||
for rec in self.material_line_ids:
|
||||
if rec.forcast_qty >= rec.qty:
|
||||
rec.is_created = True
|
||||
ready = True
|
||||
for rec in self.material_line_ids:
|
||||
if not rec.is_created:
|
||||
ready = False
|
||||
if not ready:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please Validate Lines to Approve Order",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
self.stage = 'approve'
|
||||
user_id = self.env.user.id
|
||||
msg = "<ul><li>Approval request of <strong>" + str(self.name) + "</strong> has been approved On <strong>" + str(
|
||||
fields.Datetime.now()) + "</strong></li></ul>"
|
||||
self.message_post(body=msg, partner_ids=[user_id])
|
||||
|
||||
def action_draft_requisition(self):
|
||||
self.stage = 'draft'
|
||||
self.allow_resubmit = False
|
||||
|
||||
def action_ready_delivery(self):
|
||||
delivery_warehouse = False
|
||||
for rec in self.material_transfer_ids:
|
||||
if not rec.delivery_warehouse_id:
|
||||
delivery_warehouse = True
|
||||
break
|
||||
if delivery_warehouse:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': _('Warehouse Missing'),
|
||||
'message': "Please Select Warehouse in Internal Transfer",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
if not self.material_purchase_ids:
|
||||
self.line_added = True
|
||||
if not self.line_added:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': _('Add lines'),
|
||||
'message': "Please add purchase order line to internal transfer",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
if not self.material_transfer_ids:
|
||||
self.stage = 'material_arrived'
|
||||
self.work_order_id.state = 'material_arrive'
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Work Order'),
|
||||
'res_model': 'job.order',
|
||||
'res_id': self.work_order_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
else:
|
||||
self.stage = 'ready_delivery'
|
||||
|
||||
def action_create_purchase_order(self):
|
||||
ready = True
|
||||
for rec in self.material_purchase_ids:
|
||||
if not rec.vendor_id:
|
||||
ready = False
|
||||
if not ready:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': ('Vendor or Warehouse Missing !'),
|
||||
'message': "Please Select Warehouse or Vendor in Material Purchase",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
price_check = True
|
||||
for rec in self.material_purchase_ids:
|
||||
if not rec.price > 0:
|
||||
price_check = False
|
||||
if not price_check:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': ('Invalid Product Price !'),
|
||||
'message': "Price must greater than zero to create Purchase Order.",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
vendor_count = self.material_purchase_ids.mapped(
|
||||
'vendor_id').mapped('id')
|
||||
warehouse_count = self.material_purchase_ids.mapped(
|
||||
'purchase_warehouse_id').mapped('id')
|
||||
for warehouse in warehouse_count:
|
||||
for data in vendor_count:
|
||||
lines = []
|
||||
create_po_ids = []
|
||||
for rec in self.material_purchase_ids:
|
||||
if rec.purchase_warehouse_id.id == warehouse and not rec.purchase_order_id and rec.vendor_id.id == data:
|
||||
lines.append((0, 0, {
|
||||
'product_id': rec.product_id.id,
|
||||
'name': rec.name,
|
||||
'product_qty': rec.qty,
|
||||
'product_uom': rec.uom_id.id,
|
||||
'price_unit': rec.price,
|
||||
}))
|
||||
create_po_ids.append(rec.id)
|
||||
rec.product_id.last_po_price = rec.price
|
||||
stock_picking_type_id = self.env['stock.picking.type'].search(
|
||||
[('code', '=', 'incoming'), ('warehouse_id', '=', warehouse)], limit=1)
|
||||
if lines and stock_picking_type_id:
|
||||
record = {
|
||||
'partner_id': data,
|
||||
'order_line': lines,
|
||||
'material_req_id': self.id,
|
||||
'picking_type_id': stock_picking_type_id.id
|
||||
}
|
||||
purchase_order_id = self.env['purchase.order'].create(
|
||||
record)
|
||||
for rec in create_po_ids:
|
||||
materia_po_line = self.env['material.purchase.line'].browse(
|
||||
rec)
|
||||
materia_po_line.purchase_order_id = purchase_order_id.id
|
||||
self.po_created = True
|
||||
|
||||
def action_create_back_order(self):
|
||||
ready = True
|
||||
for rec in self.material_line_ids:
|
||||
if not rec.is_created:
|
||||
ready = False
|
||||
if not ready:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'message': "Please Validate Lines to Create Back Order",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
mrq = {
|
||||
'title': self.title,
|
||||
'project_id': self.project_id.id,
|
||||
'warehouse_id': self.warehouse_id.id,
|
||||
'department_id': self.department_id.id,
|
||||
'manager_ids': self.manager_ids.ids,
|
||||
'user_id': self.user_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id,
|
||||
'stage': 'approve',
|
||||
'is_back_order': True,
|
||||
'work_type_id': self.work_type_id.id,
|
||||
'work_order_id': self.work_order_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id,
|
||||
'site_id': self.site_id.id,
|
||||
'material_req_ref': self.name
|
||||
}
|
||||
mrq_id = self.env['material.requisition'].create(mrq)
|
||||
self.back_order_id = mrq_id.id
|
||||
for data in self.material_line_ids:
|
||||
if data.operation_type == 'back_order':
|
||||
record = {
|
||||
'product_id': data.material_id.id,
|
||||
'name': data.name,
|
||||
'qty': data.back_order_qty,
|
||||
'price': data.material_id.last_po_price if data.material_id.last_po_price != 0 else data.material_id.standard_price,
|
||||
'purchase_warehouse_id': data.warehouse_id.id,
|
||||
'material_req_id': mrq_id.id,
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
'job_sheet_id': data.job_sheet_id.id
|
||||
}
|
||||
self.env['material.purchase.line'].create(record)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Back Order'),
|
||||
'res_model': 'material.requisition',
|
||||
'res_id': mrq_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
def validate_material_line_all(self):
|
||||
validate = True
|
||||
for rec in self.material_line_ids:
|
||||
if not rec.is_created and (not rec.warehouse_id or not rec.operation_type):
|
||||
validate = False
|
||||
if not validate:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': ('Warehouse or operation type Missing !'),
|
||||
'message': "Please Select warehouse or operation Type to validate.",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
for rec in self.material_line_ids:
|
||||
if not rec.is_created:
|
||||
rec.validate_material_line()
|
||||
|
||||
def action_insert_internal_transfer(self):
|
||||
for rec in self.material_purchase_ids:
|
||||
if not rec.purchase_warehouse_id.id == self.project_id.warehouse_id.id:
|
||||
internal_transfer_id = self.env['material.transfer.line'].create({
|
||||
'product_id': rec.product_id.id,
|
||||
'name': rec.name,
|
||||
'pickup_warehouse_id': rec.purchase_warehouse_id.id,
|
||||
'delivery_warehouse_id': self.project_id.warehouse_id.id,
|
||||
'qty': rec.qty,
|
||||
'material_req_id': rec.material_req_id.id,
|
||||
'sub_category_id': rec.sub_category_id.id,
|
||||
'job_sheet_id': rec.job_sheet_id.id,
|
||||
'from_purchase': True
|
||||
})
|
||||
internal_transfer_id._compute_forcast_qty()
|
||||
self.line_added = True
|
||||
|
||||
def action_create_internal_transfer(self):
|
||||
internal_transfer_id = self.env['internal.transfer'].create({
|
||||
'title': 'Internal Transfer of ' + str(self.name),
|
||||
'site_id': self.site_id.id,
|
||||
'project_id': self.project_id.id,
|
||||
'work_type_id': self.work_type_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id,
|
||||
'work_order_id': self.work_order_id.id,
|
||||
'material_req_id': self.id,
|
||||
'department_id': self.department_id.id,
|
||||
'manager_ids': self.manager_ids.ids,
|
||||
'user_id': self.user_id.id,
|
||||
})
|
||||
for data in self.material_transfer_ids:
|
||||
self.env['internal.transfer.line'].create({
|
||||
'product_id': data.product_id.id,
|
||||
'name': data.name,
|
||||
'qty': data.qty,
|
||||
'pickup_warehouse_id': data.pickup_warehouse_id.id,
|
||||
'delivery_warehouse_id': data.delivery_warehouse_id.id,
|
||||
'internal_transfer_id': internal_transfer_id.id,
|
||||
'sub_category_id': data.sub_category_id.id,
|
||||
})
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Internal Transfer'),
|
||||
'res_model': 'internal.transfer',
|
||||
'res_id': internal_transfer_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
|
||||
class MaterialRequisitionLine(models.Model):
|
||||
_name = 'material.requisition.line'
|
||||
_description = "Material Requisition Line"
|
||||
|
||||
material_id = fields.Many2one(
|
||||
'product.product', string="Material", domain="[('is_material','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
uom_id = fields.Many2one(
|
||||
related="material_id.uom_po_id", string="Unit of Measure")
|
||||
operation_type = fields.Selection([('purchase_order', 'Purchase Order'),
|
||||
('internal_transfer', 'Internal Transfer'), ('back_order', 'Back Order')],
|
||||
string="Operation Type")
|
||||
forcast_qty = fields.Float(
|
||||
string="Forcast Qty.", compute="_compute_forcast_qty")
|
||||
warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string="Pickup / Delivery Warehouse")
|
||||
material_req_id = fields.Many2one('material.requisition')
|
||||
is_created = fields.Boolean()
|
||||
forcast_check = fields.Boolean(compute="_compute_forcast_check")
|
||||
sub_category_id = fields.Many2one(
|
||||
'job.sub.category', string="Work Sub Type")
|
||||
stage = fields.Selection(related="material_req_id.stage")
|
||||
job_sheet_id = fields.Many2one('job.costing', string="Job Cost Sheet")
|
||||
remain_qty = fields.Integer(string="Remaining Qty.")
|
||||
back_order_qty = fields.Integer(string="Back Order Qty.")
|
||||
|
||||
@api.depends('receive_qty', 'qty')
|
||||
def _compute_receive_qty(self):
|
||||
for rec in self:
|
||||
if rec.receive_qty and rec.qty:
|
||||
rec.remain_qty = rec.qty - rec.receive_qty
|
||||
else:
|
||||
rec.remain_qty = 0
|
||||
|
||||
@api.onchange('material_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.material_id.name
|
||||
|
||||
@api.depends('forcast_qty', 'qty')
|
||||
def _compute_forcast_check(self):
|
||||
for rec in self:
|
||||
if rec.forcast_qty >= rec.qty:
|
||||
rec.forcast_check = True
|
||||
else:
|
||||
rec.forcast_check = False
|
||||
|
||||
@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
|
||||
|
||||
def validate_material_line(self):
|
||||
if not self.is_created:
|
||||
if not self.warehouse_id or not self.operation_type:
|
||||
message = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'info',
|
||||
'title': ('Warehouse or operation type Missing !'),
|
||||
'message': "Please Select warehouse or operation Type to validate.",
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
return message
|
||||
if self.operation_type == 'purchase_order':
|
||||
purchase_data = {
|
||||
'product_id': self.material_id.id,
|
||||
'name': self.name,
|
||||
'qty': self.qty,
|
||||
'purchase_warehouse_id': self.warehouse_id.id,
|
||||
'material_req_id': self.material_req_id.id,
|
||||
'sub_category_id': self.sub_category_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id,
|
||||
'price': self.material_id.last_po_price if self.material_id.last_po_price != 0 else self.material_id.standard_price
|
||||
}
|
||||
self.env['material.purchase.line'].create(purchase_data)
|
||||
if self.operation_type == 'internal_transfer':
|
||||
internal_data = {
|
||||
'product_id': self.material_id.id,
|
||||
'name': self.name,
|
||||
'qty': self.qty,
|
||||
'pickup_warehouse_id': self.warehouse_id.id,
|
||||
'delivery_warehouse_id': self.material_req_id.project_id.warehouse_id.id,
|
||||
'material_req_id': self.material_req_id.id,
|
||||
'sub_category_id': self.sub_category_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id
|
||||
}
|
||||
if internal_data['pickup_warehouse_id'] == internal_data['delivery_warehouse_id']:
|
||||
pass
|
||||
else:
|
||||
self.env['material.transfer.line'].create(internal_data)
|
||||
if self.operation_type == 'back_order':
|
||||
if self.qty > self.forcast_qty:
|
||||
self.back_order_qty = self.qty - self.forcast_qty
|
||||
internal_data = {
|
||||
'product_id': self.material_id.id,
|
||||
'name': self.name,
|
||||
'qty': self.forcast_qty,
|
||||
'pickup_warehouse_id': self.warehouse_id.id,
|
||||
'delivery_warehouse_id': self.material_req_id.project_id.warehouse_id.id,
|
||||
'material_req_id': self.material_req_id.id,
|
||||
'sub_category_id': self.sub_category_id.id,
|
||||
'job_sheet_id': self.job_sheet_id.id
|
||||
}
|
||||
if internal_data['pickup_warehouse_id'] == internal_data['delivery_warehouse_id']:
|
||||
pass
|
||||
else:
|
||||
self.env['material.transfer.line'].create(
|
||||
internal_data)
|
||||
self.is_created = True
|
||||
|
||||
|
||||
class MaterialPurchaseLine(models.Model):
|
||||
_name = 'material.purchase.line'
|
||||
_description = "Material Purchase Line"
|
||||
|
||||
product_id = fields.Many2one('product.product', string="Product")
|
||||
name = fields.Char(string="Description")
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
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')
|
||||
forcast_qty = fields.Float(
|
||||
string="Forcast Qty.", compute="_compute_forcast_qty")
|
||||
price = fields.Monetary(string="Price")
|
||||
total_price = fields.Monetary(
|
||||
string="Total Price", compute="_compute_total_price")
|
||||
uom_id = fields.Many2one(
|
||||
related="product_id.uom_po_id", string="Unit of Measure")
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
purchase_warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string="Delivery Warehouse")
|
||||
material_req_id = fields.Many2one('material.requisition')
|
||||
purchase_order_id = fields.Many2one(
|
||||
'purchase.order', string="Purchase Order")
|
||||
status = fields.Selection([('incomplete', 'Incomplete'), ('partial_complete', 'Partial Complete'),
|
||||
('complete', 'Complete')], compute="_compute_po_delivery_status")
|
||||
sub_category_id = fields.Many2one(
|
||||
'job.sub.category', string="Work Sub Type")
|
||||
job_sheet_id = fields.Many2one('job.costing', string="Job Cost Sheet")
|
||||
|
||||
def unlink(self):
|
||||
if not self.purchase_order_id:
|
||||
return super(MaterialPurchaseLine, self).unlink()
|
||||
else:
|
||||
raise ValidationError(
|
||||
_('You can not delete material purchase line after creating purchase order'))
|
||||
|
||||
@api.depends('purchase_warehouse_id', 'product_id')
|
||||
def _compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
qty = 0.0
|
||||
if rec.product_id:
|
||||
qty = rec.product_id.with_context(
|
||||
warehouse=rec.purchase_warehouse_id.id).virtual_available
|
||||
rec.forcast_qty = qty
|
||||
|
||||
@api.depends('price', 'qty')
|
||||
def _compute_total_price(self):
|
||||
for rec in self:
|
||||
total = 0.0
|
||||
if rec.qty and rec.price:
|
||||
total = rec.qty * rec.price
|
||||
rec.total_price = total
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.product_id.name
|
||||
|
||||
@api.depends('purchase_order_id')
|
||||
def _compute_po_delivery_status(self):
|
||||
for rec in self:
|
||||
delivery_orders = self.env['stock.picking'].search(
|
||||
[('code', '=', 'incoming'), ('origin', '=', rec.purchase_order_id.name)])
|
||||
if rec.purchase_order_id and delivery_orders:
|
||||
incomplete, complete = False, False
|
||||
for data in delivery_orders:
|
||||
if data.state == 'done':
|
||||
complete = True
|
||||
else:
|
||||
incomplete = True
|
||||
if complete and not incomplete:
|
||||
rec.status = 'complete'
|
||||
elif incomplete and not complete:
|
||||
rec.status = 'incomplete'
|
||||
else:
|
||||
rec.status = "partial_complete"
|
||||
else:
|
||||
rec.status = ""
|
||||
|
||||
@api.onchange('product_id', 'vendor_id')
|
||||
def onchange_product_vendor_price_list(self):
|
||||
for rec in self:
|
||||
if rec.vendor_id and rec.product_id:
|
||||
domain = [('partner_id', '=', rec.vendor_id.id), '|',
|
||||
('product_tmpl_id', '=', rec.product_id.product_tmpl_id.id),
|
||||
('product_id', '=', rec.product_id.id)]
|
||||
vendor_price = self.env['product.supplierinfo'].search(
|
||||
domain, limit=1, order='create_date desc')
|
||||
if vendor_price.price > 0:
|
||||
rec.price = vendor_price.price
|
||||
elif rec.product_id.last_po_price > 0:
|
||||
rec.price = rec.product_id.last_po_price
|
||||
else:
|
||||
rec.price = rec.product_id.standard_price
|
||||
|
||||
|
||||
class MaterialTransferLine(models.Model):
|
||||
_name = 'material.transfer.line'
|
||||
_description = "Material Transfer Line"
|
||||
|
||||
product_id = fields.Many2one('product.product', string="Product")
|
||||
name = fields.Char(string="Description")
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
forcast_qty = fields.Float(
|
||||
string="Forcast Qty.", compute="_compute_forcast_qty")
|
||||
pickup_warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string="Picking Warehouse")
|
||||
delivery_warehouse_id = fields.Many2one(
|
||||
'stock.warehouse', string="Delivery Warehouse")
|
||||
material_req_id = fields.Many2one('material.requisition')
|
||||
sub_category_id = fields.Many2one(
|
||||
'job.sub.category', string="Work Sub Type")
|
||||
job_sheet_id = fields.Many2one('job.costing', string="Job Cost Sheet")
|
||||
from_purchase = fields.Boolean(string="From Purchase")
|
||||
|
||||
@api.depends('pickup_warehouse_id', 'product_id')
|
||||
def _compute_forcast_qty(self):
|
||||
for rec in self:
|
||||
qty = 0.0
|
||||
if rec.product_id:
|
||||
qty = rec.product_id.with_context(
|
||||
warehouse=rec.pickup_warehouse_id.id).virtual_available
|
||||
rec.forcast_qty = qty
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_desc(self):
|
||||
for rec in self:
|
||||
rec.name = rec.product_id.name
|
||||
18
addons/tk_construction_management/models/project_task.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- 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 ConProject(models.Model):
|
||||
_inherit = 'project.project'
|
||||
|
||||
construction_project_id = fields.Many2one('tk.construction.project', string="Construction Building")
|
||||
|
||||
|
||||
class ConstructionTask(models.Model):
|
||||
_inherit = 'project.task'
|
||||
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
is_inspection_task = fields.Boolean(string="Inspection Task")
|
||||
con_project_id = fields.Many2one('tk.construction.project', string="Construction Project")
|
||||
237
addons/tk_construction_management/models/rate_analysis.py
Normal file
@@ -0,0 +1,237 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2022-Today TechKhedut.
|
||||
# Part of TechKhedut. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class RateAnalysis(models.Model):
|
||||
_name = 'rate.analysis'
|
||||
_description = "Rate Analysis"
|
||||
_rec_name = 'name'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string="Title")
|
||||
site_id = fields.Many2one('tk.construction.site', string="Project")
|
||||
project_id = fields.Many2one('tk.construction.project', string="Sub Project",domain="[('construction_site_id','=',site_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')
|
||||
activity_id = fields.Many2one('job.type', string="Work Type")
|
||||
sub_activity_ids = fields.Many2many(related="activity_id.sub_category_ids", string="Sub Activities")
|
||||
sub_activity_id = fields.Many2one('job.sub.category', string="Work Sub Type",domain="[('id','in',sub_activity_ids)]")
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
unit_id = fields.Many2one('uom.uom', string="Unit")
|
||||
|
||||
# Rate Analysis Type
|
||||
material_analysis_ids = fields.One2many('rate.analysis.material', 'rate_analysis_id', string="Rate Analysis Material")
|
||||
equipment_analysis_ids = fields.One2many('rate.analysis.equipment', 'rate_analysis_id',string="Rate Analysis Equipment")
|
||||
labour_analysis_ids = fields.One2many('rate.analysis.labour', 'rate_analysis_id',string="Rate Analysis Labour")
|
||||
overhead_analysis_ids = fields.One2many('rate.analysis.overhead', 'rate_analysis_id',string="Rate Analysis Overhead")
|
||||
|
||||
# Amount
|
||||
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_total_amount")
|
||||
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_total_amount")
|
||||
total_amount = fields.Monetary(string="Total Amount", compute="compute_total_amount")
|
||||
|
||||
@api.depends('material_analysis_ids', 'equipment_analysis_ids', 'labour_analysis_ids', 'overhead_analysis_ids')
|
||||
def compute_total_amount(self):
|
||||
for rec in self:
|
||||
tax_amount = 0.0
|
||||
untaxed_amount = 0.0
|
||||
total_amount = 0.0
|
||||
for data in rec.material_analysis_ids:
|
||||
tax_amount = tax_amount + data.tax_amount
|
||||
untaxed_amount = untaxed_amount + data.untaxed_amount
|
||||
total_amount = total_amount + data.total_amount
|
||||
for data in rec.equipment_analysis_ids:
|
||||
tax_amount = tax_amount + data.tax_amount
|
||||
untaxed_amount = untaxed_amount + data.untaxed_amount
|
||||
total_amount = total_amount + data.total_amount
|
||||
for data in rec.labour_analysis_ids:
|
||||
tax_amount = tax_amount + data.tax_amount
|
||||
untaxed_amount = untaxed_amount + data.untaxed_amount
|
||||
total_amount = total_amount + data.total_amount
|
||||
for data in rec.overhead_analysis_ids:
|
||||
tax_amount = tax_amount + data.tax_amount
|
||||
untaxed_amount = untaxed_amount + data.untaxed_amount
|
||||
total_amount = total_amount + data.total_amount
|
||||
rec.tax_amount = tax_amount
|
||||
rec.untaxed_amount = untaxed_amount
|
||||
rec.total_amount = total_amount
|
||||
|
||||
@api.constrains('material_analysis_ids')
|
||||
def _check_cost_material_uniq_product(self):
|
||||
for record in self.material_analysis_ids:
|
||||
duplicates = self.material_analysis_ids.filtered(
|
||||
lambda r: r.id != record.id and r.product_id.id == record.product_id.id)
|
||||
if duplicates:
|
||||
raise ValidationError(_("Material already added !"))
|
||||
|
||||
@api.constrains('equipment_analysis_ids')
|
||||
def _check_cost_equipment_uniq_product(self):
|
||||
for record in self.equipment_analysis_ids:
|
||||
duplicates = self.equipment_analysis_ids.filtered(
|
||||
lambda r: r.id != record.id and r.product_id.id == record.product_id.id)
|
||||
if duplicates:
|
||||
raise ValidationError(_("Equipment already added !"))
|
||||
|
||||
@api.constrains('labour_analysis_ids')
|
||||
def _check_cost_labour_uniq_product(self):
|
||||
for record in self.labour_analysis_ids:
|
||||
duplicates = self.labour_analysis_ids.filtered(
|
||||
lambda r: r.id != record.id and r.product_id.id == record.product_id.id)
|
||||
if duplicates:
|
||||
raise ValidationError(_("Labour already added !"))
|
||||
|
||||
@api.constrains('overhead_analysis_ids')
|
||||
def _check_cost_overhead_uniq_product(self):
|
||||
for record in self.overhead_analysis_ids:
|
||||
duplicates = self.overhead_analysis_ids.filtered(
|
||||
lambda r: r.id != record.id and r.product_id.id == record.product_id.id)
|
||||
if duplicates:
|
||||
raise ValidationError(_("Overhead already added !"))
|
||||
|
||||
|
||||
|
||||
class RateAnalysisMaterial(models.Model):
|
||||
_name = "rate.analysis.material"
|
||||
_description = "Rate Analysis Material Line"
|
||||
|
||||
rate_analysis_id = fields.Many2one('rate.analysis', string="Rate Analysis")
|
||||
product_id = fields.Many2one('product.product', string="Material", domain="[('is_material','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
code = fields.Char(related="product_id.default_code", string="Code")
|
||||
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)
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="UOM")
|
||||
price = fields.Monetary(string="Price")
|
||||
tax_id = fields.Many2one('account.tax', string="Tax")
|
||||
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_total")
|
||||
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_total")
|
||||
total_amount = fields.Monetary(string="Total Amount", compute="compute_total")
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_product_info(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.name = rec.product_id.name
|
||||
rec.price = rec.product_id.standard_price
|
||||
|
||||
@api.depends('price', 'qty', 'tax_id.amount', 'tax_id')
|
||||
def compute_total(self):
|
||||
for rec in self:
|
||||
untaxed_amount = rec.qty * rec.price
|
||||
tax_amount = (rec.tax_id.amount * untaxed_amount / 100) if rec.tax_id else 0.0
|
||||
total_amount = untaxed_amount + tax_amount
|
||||
rec.untaxed_amount = untaxed_amount
|
||||
rec.tax_amount = tax_amount
|
||||
rec.total_amount = total_amount
|
||||
|
||||
|
||||
class RateAnalysisEquipment(models.Model):
|
||||
_name = "rate.analysis.equipment"
|
||||
_description = "Rate Analysis Equipment Line"
|
||||
|
||||
rate_analysis_id = fields.Many2one('rate.analysis', string="Rate Analysis")
|
||||
product_id = fields.Many2one('product.product', string="Equipment", domain="[('is_equipment','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
code = fields.Char(related="product_id.default_code", string="Code")
|
||||
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)
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="UOM")
|
||||
price = fields.Monetary(string="Price")
|
||||
tax_id = fields.Many2one('account.tax', string="Tax")
|
||||
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_total")
|
||||
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_total")
|
||||
total_amount = fields.Monetary(string="Total Amount", compute="compute_total")
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_product_info(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.name = rec.product_id.name
|
||||
rec.price = rec.product_id.standard_price
|
||||
|
||||
@api.depends('price', 'qty', 'tax_id.amount', 'tax_id')
|
||||
def compute_total(self):
|
||||
for rec in self:
|
||||
untaxed_amount = rec.qty * rec.price
|
||||
tax_amount = (rec.tax_id.amount * untaxed_amount / 100) if rec.tax_id else 0.0
|
||||
total_amount = untaxed_amount + tax_amount
|
||||
rec.untaxed_amount = untaxed_amount
|
||||
rec.tax_amount = tax_amount
|
||||
rec.total_amount = total_amount
|
||||
|
||||
|
||||
class RateAnalysisLabour(models.Model):
|
||||
_name = "rate.analysis.labour"
|
||||
_description = "Rate Analysis Labour Line"
|
||||
|
||||
rate_analysis_id = fields.Many2one('rate.analysis', string="Rate Analysis")
|
||||
product_id = fields.Many2one('product.product', string="Labour", domain="[('is_labour','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
code = fields.Char(related="product_id.default_code", string="Code")
|
||||
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)
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="UOM")
|
||||
price = fields.Monetary(string="Price")
|
||||
tax_id = fields.Many2one('account.tax', string="Tax")
|
||||
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_total")
|
||||
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_total")
|
||||
total_amount = fields.Monetary(string="Total Amount", compute="compute_total")
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_product_info(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.name = rec.product_id.name
|
||||
rec.price = rec.product_id.standard_price
|
||||
|
||||
@api.depends('price', 'qty', 'tax_id.amount', 'tax_id')
|
||||
def compute_total(self):
|
||||
for rec in self:
|
||||
untaxed_amount = rec.qty * rec.price
|
||||
tax_amount = (rec.tax_id.amount * untaxed_amount / 100) if rec.tax_id else 0.0
|
||||
total_amount = untaxed_amount + tax_amount
|
||||
rec.untaxed_amount = untaxed_amount
|
||||
rec.tax_amount = tax_amount
|
||||
rec.total_amount = total_amount
|
||||
|
||||
|
||||
class RateAnalysisOverhead(models.Model):
|
||||
_name = "rate.analysis.overhead"
|
||||
_description = "Rate Analysis Overhead Line"
|
||||
|
||||
rate_analysis_id = fields.Many2one('rate.analysis', string="Rate Analysis")
|
||||
product_id = fields.Many2one('product.product', string="Overhead", domain="[('is_overhead','=',True)]")
|
||||
name = fields.Char(string="Description")
|
||||
code = fields.Char(related="product_id.default_code", string="Code")
|
||||
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)
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="UOM")
|
||||
price = fields.Monetary(string="Price")
|
||||
tax_id = fields.Many2one('account.tax', string="Tax")
|
||||
untaxed_amount = fields.Monetary(string="Untaxed Amount", compute="compute_total")
|
||||
tax_amount = fields.Monetary(string="Tax Amount", compute="compute_total")
|
||||
total_amount = fields.Monetary(string="Total Amount", compute="compute_total")
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_product_info(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.name = rec.product_id.name
|
||||
rec.price = rec.product_id.standard_price
|
||||
|
||||
@api.depends('price', 'qty', 'tax_id.amount', 'tax_id')
|
||||
def compute_total(self):
|
||||
for rec in self:
|
||||
untaxed_amount = rec.qty * rec.price
|
||||
tax_amount = (rec.tax_id.amount * untaxed_amount / 100) if rec.tax_id else 0.0
|
||||
total_amount = untaxed_amount + tax_amount
|
||||
rec.untaxed_amount = untaxed_amount
|
||||
rec.tax_amount = tax_amount
|
||||
rec.total_amount = total_amount
|
||||
11
addons/tk_construction_management/models/res_config.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# -*- 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 ConstructionResConfig(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
phase_prefix = fields.Char(string='Phase Prefix', default="PHASE/",
|
||||
config_parameter='tk_construction_management.phase_prefix')
|
||||
10
addons/tk_construction_management/models/res_partner.py
Normal 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 odoo import fields, api, models
|
||||
|
||||
|
||||
class ConstructionPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
stack_holder = fields.Boolean(string="Stockholder")
|
||||
813
addons/tk_construction_management/models/sub_contract.py
Normal file
@@ -0,0 +1,813 @@
|
||||
# -*- 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, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class EquipmentSubcontract(models.Model):
|
||||
_name = 'equipment.subcontract'
|
||||
_description = "Equipment Subcontract"
|
||||
_rec_name = 'seq'
|
||||
|
||||
seq = fields.Char(string='Sequence', required=True, readonly=True, default=lambda self: _('New'))
|
||||
name = fields.Char(string="Title")
|
||||
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')
|
||||
cost = fields.Monetary(string="Estimation Cost")
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
purchase_order_id = fields.Many2one('purchase.order', string="Purchase Order")
|
||||
job_type_id = fields.Many2one('job.type', string="Work Type")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
stage = fields.Selection([('draft', 'Draft'), ('in_progress', 'In Progress'), ('done', 'Done')], default='draft')
|
||||
po_bill = fields.Selection([('bill', 'Bill'), ('purchase_order', 'Purchase Order')], string="Type", default='bill')
|
||||
ra_bill_ids = fields.One2many('equip.contract.line', 'contract_id', string="Ra Bills")
|
||||
completion_date = fields.Date(string="Completion Date", compute="compute_completion_date")
|
||||
|
||||
# Calculation
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
remain_qty = fields.Integer(string="Remaining Qty")
|
||||
total_cost = fields.Monetary(string="Total Cost")
|
||||
remaining_amount = fields.Monetary(string="Remaining Amount")
|
||||
progress = fields.Float(string="Complete Billing", compute="_compute_payment_progress")
|
||||
tax_id = fields.Many2one('account.tax', string='Tax')
|
||||
|
||||
# Project Details
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
phase_id = fields.Many2one(related="job_order_id.job_sheet_id", string="Project Phase(WBS)", store=True)
|
||||
project_id = fields.Many2one(related='job_order_id.project_id', string="Sub Project", store=True)
|
||||
task_id = fields.Many2one(related="job_order_id.task_id", string="Task", store=True)
|
||||
|
||||
# Department
|
||||
department_id = fields.Many2one(related="job_order_id.department_id", string="Department", store=True)
|
||||
manager_ids = fields.Many2many('res.users', store=True, string="Manager")
|
||||
user_id = fields.Many2one(related="job_order_id.user_id", string="Responsible")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('seq', _('New')) == _('New'):
|
||||
vals['seq'] = self.env['ir.sequence'].next_by_code('equip.sub') or _('New')
|
||||
res = super(EquipmentSubcontract, self).create(vals_list)
|
||||
return res
|
||||
|
||||
def action_in_progress(self):
|
||||
self.stage = 'in_progress'
|
||||
|
||||
def action_state_done(self):
|
||||
self.stage = 'done'
|
||||
|
||||
@api.constrains('ra_bill_ids', 'qty')
|
||||
def _check_ra_bill_qty(self):
|
||||
qty = 0
|
||||
for record in self.ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
qty = qty + record.qty
|
||||
if qty > self.qty:
|
||||
raise ValidationError(_("Quantity should be less than total qty."))
|
||||
|
||||
@api.depends('total_cost', 'remaining_amount')
|
||||
def _compute_payment_progress(self):
|
||||
for rec in self:
|
||||
progress = 0.0
|
||||
if rec.total_cost and rec.remaining_amount:
|
||||
progress = (rec.remaining_amount * 100) / rec.total_cost
|
||||
rec.progress = 100 - progress
|
||||
|
||||
@api.depends('stage', 'ra_bill_ids')
|
||||
def compute_completion_date(self):
|
||||
for rec in self:
|
||||
date = None
|
||||
if rec.stage == "done":
|
||||
dates = rec.ra_bill_ids.mapped('date')
|
||||
if dates:
|
||||
date = dates[-1]
|
||||
rec.completion_date = date
|
||||
|
||||
|
||||
class EquipContractLine(models.Model):
|
||||
_name = 'equip.contract.line'
|
||||
_description = "Equip Contract Line"
|
||||
_rec_name = 'remark'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
contract_id = fields.Many2one('equipment.subcontract', string="Subcontract", ondelete='cascade')
|
||||
percentage = fields.Float(string="Percentage", tracking=True, compute="_compute_percentage")
|
||||
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')
|
||||
amount = fields.Monetary(tracking=True, string="Amount", compute="_compute_percentage_amount")
|
||||
date = fields.Date(string="Date", default=fields.Date.today(), tracking=True)
|
||||
remark = fields.Char(string="Remark", tracking=True)
|
||||
purchase_order_id = fields.Many2one('purchase.order', string="Purchase Order")
|
||||
bill_id = fields.Many2one('account.move', string="Bill")
|
||||
payment_state = fields.Selection(related="bill_id.payment_state", string="Payment State", tracking=True)
|
||||
state = fields.Selection(related="purchase_order_id.state", string="State", tracking=True)
|
||||
po_bill = fields.Selection(related="contract_id.po_bill")
|
||||
qty = fields.Integer(string="Qty")
|
||||
retention_percentage = fields.Float(string="Retention(%)")
|
||||
retention_amount = fields.Monetary(string="Retention Amount", compute="compute_retention_amount")
|
||||
final_amount = fields.Monetary(string="Total Amount", compute="_compute_final_amount")
|
||||
|
||||
# QC Check
|
||||
qc_user_id = fields.Many2one('res.users', string="QC Responsible", tracking=True)
|
||||
qc_status = fields.Selection(
|
||||
[('draft', 'Draft'),
|
||||
('request', 'Department Approval'),
|
||||
('approve', 'Approve'),
|
||||
('reject', 'Reject')], default='draft', string="Quality Check Status", tracking=True)
|
||||
reject_reason = fields.Text(string="Reject Reason", tracking=True)
|
||||
|
||||
def unlink(self):
|
||||
for rec in self:
|
||||
if rec.qc_status != 'draft':
|
||||
raise ValidationError(_("You can't delete until Quality Check status is in Draft"))
|
||||
else:
|
||||
return super(EquipContractLine, self).unlink()
|
||||
|
||||
@api.constrains('qty')
|
||||
def _check_ra_bill_qty(self):
|
||||
ra_bill_ids = self.env['equip.contract.line'].search([('contract_id', '=', self.contract_id.id)])
|
||||
qty = 0
|
||||
for record in ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
qty = qty + record.qty
|
||||
if qty > self.contract_id.qty:
|
||||
raise ValidationError(_("Quantity should be less than total qty."))
|
||||
|
||||
@api.depends('contract_id', 'qty', 'contract_id.tax_id')
|
||||
def _compute_percentage_amount(self):
|
||||
for rec in self:
|
||||
amount = 0.0
|
||||
if rec.qty and rec.contract_id:
|
||||
amount = (rec.contract_id.cost + (rec.contract_id.tax_id.amount * rec.contract_id.cost / 100)) * rec.qty
|
||||
rec.amount = amount
|
||||
|
||||
@api.depends('qty', 'retention_amount')
|
||||
def _compute_final_amount(self):
|
||||
for rec in self:
|
||||
total = 0.0
|
||||
if rec.amount:
|
||||
total = rec.amount - rec.retention_amount
|
||||
rec.final_amount = total
|
||||
|
||||
@api.depends('amount', 'retention_percentage')
|
||||
def compute_retention_amount(self):
|
||||
for rec in self:
|
||||
retention_amount = 0.0
|
||||
if rec.retention_percentage:
|
||||
retention_amount = rec.amount * rec.retention_percentage / 100
|
||||
rec.retention_amount = retention_amount
|
||||
|
||||
@api.depends('contract_id', 'amount')
|
||||
def _compute_percentage(self):
|
||||
for rec in self:
|
||||
percentage = 0.0
|
||||
if rec.contract_id and rec.amount > 0:
|
||||
percentage = (100 * rec.amount) / rec.contract_id.total_cost
|
||||
rec.percentage = percentage
|
||||
|
||||
def action_quality_check(self):
|
||||
self.qc_status = 'request'
|
||||
|
||||
def action_quality_check_approve(self):
|
||||
self.qc_status = 'approve'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_quality_check_reject(self):
|
||||
self.qc_status = 'reject'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
self.qc_status = 'draft'
|
||||
|
||||
def action_create_ra_bill(self):
|
||||
if self.po_bill == 'bill':
|
||||
record = {
|
||||
'product_id': self.contract_id.equipment_id.id,
|
||||
'name': self.contract_id.name,
|
||||
'quantity': 1,
|
||||
'price_unit': self.final_amount,
|
||||
'tax_ids': False
|
||||
}
|
||||
invoice_lines = [(0, 0, record)]
|
||||
data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'invoice_date': self.date,
|
||||
'invoice_line_ids': invoice_lines,
|
||||
'move_type': 'in_invoice',
|
||||
'equipment_subcontract_id': self.contract_id.id,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'purchase_order': 'equipment'
|
||||
}
|
||||
invoice_id = self.env['account.move'].sudo().create(data)
|
||||
self.bill_id = invoice_id.id
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
qty = self.contract_id.remain_qty
|
||||
self.contract_id.remain_qty = qty - self.qty
|
||||
elif self.po_bill == 'purchase_order':
|
||||
purchase_record = {
|
||||
'product_id': self.contract_id.equipment_id.id,
|
||||
'name': self.contract_id.name,
|
||||
'product_qty': 1,
|
||||
'price_unit': self.final_amount,
|
||||
}
|
||||
purchase_lines = [(0, 0, purchase_record)]
|
||||
purchase_data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'order_line': purchase_lines,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'equipment_subcontract_id': self.contract_id.id,
|
||||
'purchase_order': 'equipment'
|
||||
}
|
||||
purchase_order_id = self.env['purchase.order'].create(purchase_data)
|
||||
self.purchase_order_id = purchase_order_id.id
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
qty = self.contract_id.remain_qty
|
||||
self.contract_id.remain_qty = qty - self.qty
|
||||
|
||||
|
||||
class LabourSubcontract(models.Model):
|
||||
_name = 'labour.subcontract'
|
||||
_description = "Labour Sub Contract"
|
||||
_rec_name = 'seq'
|
||||
|
||||
seq = fields.Char(string='Sequence', required=True, readonly=True, default=lambda self: _('New'))
|
||||
name = fields.Char(string="Title")
|
||||
product_id = fields.Many2one('product.product', string="Product", domain="[('is_labour','=',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 = fields.Monetary(string="Cost / Hour")
|
||||
po_bill = fields.Selection([('bill', 'Bill'), ('purchase_order', 'Purchase Order')], string="Type",
|
||||
default='bill')
|
||||
stage = fields.Selection([('draft', 'Draft'), ('in_progress', 'In Progress'), ('done', 'Done')],
|
||||
default='draft')
|
||||
vendor_id = fields.Many2one('res.partner', string="Contractor")
|
||||
job_type_id = fields.Many2one('job.type', string="Work Type")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
ra_bill_ids = fields.One2many('labour.contract.line', 'contract_id', string="Ra Bills")
|
||||
|
||||
# Project Details
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
phase_id = fields.Many2one(related="job_order_id.job_sheet_id", string="Project Phase(WBS)", store=True)
|
||||
project_id = fields.Many2one(related='job_order_id.project_id', string="Sub Project", store=True)
|
||||
task_id = fields.Many2one(related="job_order_id.task_id", string="Task", store=True)
|
||||
|
||||
# Department
|
||||
department_id = fields.Many2one(related="job_order_id.department_id", string="Department", store=True)
|
||||
manager_ids = fields.Many2many('res.users', store=True, string="Manager")
|
||||
user_id = fields.Many2one(related="job_order_id.user_id", string="Responsible")
|
||||
|
||||
# Calculation
|
||||
hours = fields.Float(string="Hours")
|
||||
remain_hours = fields.Float(string="Remaining Hours")
|
||||
total_cost = fields.Monetary(string="Total Cost")
|
||||
remaining_amount = fields.Monetary(string="Remaining Amount")
|
||||
progress = fields.Float(string="Completed Billing", compute="_compute_payment_progress")
|
||||
tax_id = fields.Many2one('account.tax', string='Tax')
|
||||
completion_date = fields.Date(string="Completion Date", compute="compute_completion_date")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('seq', _('New')) == _('New'):
|
||||
vals['seq'] = self.env['ir.sequence'].next_by_code('labour.sub') or _('New')
|
||||
res = super(LabourSubcontract, self).create(vals_list)
|
||||
return res
|
||||
|
||||
def action_in_progress(self):
|
||||
self.stage = 'in_progress'
|
||||
|
||||
def action_state_done(self):
|
||||
self.stage = 'done'
|
||||
|
||||
@api.constrains('ra_bill_ids', 'hours')
|
||||
def _check_ra_bill_hours(self):
|
||||
hours = 0
|
||||
for record in self.ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
hours = hours + record.hours
|
||||
if hours > self.hours:
|
||||
raise ValidationError(_("Hours should be less than total hours"))
|
||||
|
||||
@api.depends('total_cost', 'remaining_amount')
|
||||
def _compute_payment_progress(self):
|
||||
for rec in self:
|
||||
progress = 0.0
|
||||
if rec.total_cost and rec.remaining_amount:
|
||||
progress = (rec.remaining_amount * 100) / rec.total_cost
|
||||
rec.progress = 100 - progress
|
||||
|
||||
@api.depends('stage', 'ra_bill_ids')
|
||||
def compute_completion_date(self):
|
||||
for rec in self:
|
||||
date = None
|
||||
if rec.stage == "done":
|
||||
dates = rec.ra_bill_ids.mapped('date')
|
||||
if dates:
|
||||
date = dates[-1]
|
||||
rec.completion_date = date
|
||||
|
||||
|
||||
class LabourContractLine(models.Model):
|
||||
_name = 'labour.contract.line'
|
||||
_description = "Labour Contract Line"
|
||||
_rec_name = 'remark'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
contract_id = fields.Many2one('labour.subcontract', string="Labour Subcontract", ondelete='cascade')
|
||||
percentage = fields.Float(string="Percentage", tracking=True, compute="_compute_percentage")
|
||||
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')
|
||||
amount = fields.Monetary(tracking=True, string="Amount", compute="_compute_percentage_amount")
|
||||
date = fields.Date(string="Date", default=fields.Date.today(), tracking=True)
|
||||
remark = fields.Char(string="Remark", tracking=True)
|
||||
purchase_order_id = fields.Many2one('purchase.order', string="Purchase Order")
|
||||
bill_id = fields.Many2one('account.move', string="Bill")
|
||||
payment_state = fields.Selection(related="bill_id.payment_state", string="Payment State", tracking=True)
|
||||
state = fields.Selection(related="purchase_order_id.state", string="State", tracking=True)
|
||||
po_bill = fields.Selection(related="contract_id.po_bill")
|
||||
hours = fields.Float(string="Hours")
|
||||
retention_percentage = fields.Float(string="Retention(%)")
|
||||
retention_amount = fields.Monetary(string="Retention Amount", compute="compute_retention_amount")
|
||||
final_amount = fields.Monetary(string="Total Amount", compute="_compute_final_amount")
|
||||
|
||||
# QC Check
|
||||
qc_user_id = fields.Many2one('res.users', string="QC Responsible", tracking=True)
|
||||
qc_status = fields.Selection([('draft', 'Draft'),
|
||||
('request', 'Department Approval'),
|
||||
('approve', 'Approve'),
|
||||
('reject', 'Reject')],
|
||||
default='draft', string="Quality Check Status", tracking=True)
|
||||
reject_reason = fields.Text(string="Reject Reason", tracking=True)
|
||||
|
||||
def unlink(self):
|
||||
for rec in self:
|
||||
if rec.qc_status != 'draft':
|
||||
raise ValidationError(_("You can't delete until Quality Check status is in Draft"))
|
||||
else:
|
||||
return super(LabourContractLine, self).unlink()
|
||||
|
||||
@api.constrains('hours')
|
||||
def _check_ra_bill_hours(self):
|
||||
ra_bill_ids = self.env['labour.contract.line'].search([('contract_id', '=', self.contract_id.id)])
|
||||
hours = 0
|
||||
for record in ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
hours = hours + record.hours
|
||||
if hours > self.contract_id.hours:
|
||||
raise ValidationError(_("Hours should be less than total hours"))
|
||||
|
||||
@api.depends('contract_id', 'hours', 'contract_id.tax_id')
|
||||
def _compute_percentage_amount(self):
|
||||
for rec in self:
|
||||
amount = 0.0
|
||||
if rec.hours and rec.contract_id:
|
||||
amount = (rec.contract_id.cost + (
|
||||
rec.contract_id.tax_id.amount * rec.contract_id.cost / 100)) * rec.hours
|
||||
rec.amount = amount
|
||||
|
||||
@api.depends('hours', 'retention_amount')
|
||||
def _compute_final_amount(self):
|
||||
for rec in self:
|
||||
total = 0.0
|
||||
if rec.amount:
|
||||
total = rec.amount - rec.retention_amount
|
||||
rec.final_amount = total
|
||||
|
||||
@api.depends('amount', 'retention_percentage')
|
||||
def compute_retention_amount(self):
|
||||
for rec in self:
|
||||
retention_amount = 0.0
|
||||
retention_amount = rec.amount * rec.retention_percentage / 100
|
||||
rec.retention_amount = retention_amount
|
||||
|
||||
@api.depends('contract_id', 'amount')
|
||||
def _compute_percentage(self):
|
||||
for rec in self:
|
||||
percentage = 0.0
|
||||
if rec.contract_id and rec.amount > 0:
|
||||
percentage = (100 * rec.amount) / rec.contract_id.total_cost
|
||||
rec.percentage = percentage
|
||||
|
||||
def action_quality_check(self):
|
||||
self.qc_status = 'request'
|
||||
|
||||
def action_quality_check_approve(self):
|
||||
self.qc_status = 'approve'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_quality_check_reject(self):
|
||||
self.qc_status = 'reject'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
self.qc_status = 'draft'
|
||||
|
||||
def action_create_ra_bill(self):
|
||||
if self.po_bill == 'bill':
|
||||
record = {
|
||||
'product_id': self.contract_id.product_id.id,
|
||||
'name': self.contract_id.name,
|
||||
'quantity': 1,
|
||||
'price_unit': self.final_amount,
|
||||
'tax_ids': False
|
||||
}
|
||||
invoice_lines = [(0, 0, record)]
|
||||
data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'invoice_date': self.date,
|
||||
'invoice_line_ids': invoice_lines,
|
||||
'move_type': 'in_invoice',
|
||||
'labour_subcontract_id': self.contract_id.id,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'purchase_order': 'equipment'
|
||||
}
|
||||
invoice_id = self.env['account.move'].sudo().create(data)
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
self.bill_id = invoice_id.id
|
||||
qty = self.contract_id.remain_hours
|
||||
self.contract_id.remain_hours = qty - self.hours
|
||||
elif self.po_bill == 'purchase_order':
|
||||
purchase_record = {
|
||||
'product_id': self.contract_id.product_id.id,
|
||||
'name': self.contract_id.name
|
||||
,
|
||||
'product_qty': 1,
|
||||
'price_unit': self.final_amount,
|
||||
}
|
||||
purchase_lines = [(0, 0, purchase_record)]
|
||||
purchase_data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'order_line': purchase_lines,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'labour_subcontract_id': self.contract_id.id,
|
||||
'purchase_order': 'equipment'
|
||||
}
|
||||
purchase_order_id = self.env['purchase.order'].create(purchase_data)
|
||||
self.purchase_order_id = purchase_order_id.id
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
qty = self.contract_id.remain_hours
|
||||
self.contract_id.remain_hours = qty - self.hours
|
||||
|
||||
|
||||
class OverheadSubcontract(models.Model):
|
||||
_name = 'overhead.subcontract'
|
||||
_description = "Overhead Subcontract"
|
||||
_rec_name = 'seq'
|
||||
|
||||
seq = fields.Char(string='Sequence', required=True, readonly=True, default=lambda self: _('New'))
|
||||
name = fields.Char(string="Title")
|
||||
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')
|
||||
uom_id = fields.Many2one(related="product_id.uom_po_id", string="Unit of Measure")
|
||||
cost = fields.Monetary(string="Cost / Unit")
|
||||
po_bill = fields.Selection([('bill', 'Bill'), ('purchase_order', 'Purchase Order')], string="Type", default='bill')
|
||||
stage = fields.Selection([('draft', 'Draft'), ('in_progress', 'In Progress'), ('done', 'Done')], default='draft')
|
||||
vendor_id = fields.Many2one('res.partner', string="Vendor")
|
||||
job_type_id = fields.Many2one('job.type', string="Work Type")
|
||||
sub_category_id = fields.Many2one('job.sub.category', string="Work Sub Type")
|
||||
ra_bill_ids = fields.One2many('overhead.contract.line', 'contract_id', string="Ra Bills")
|
||||
|
||||
# Project Details
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
phase_id = fields.Many2one(related="job_order_id.job_sheet_id", string="Project Phase(WBS)", store=True)
|
||||
project_id = fields.Many2one(related='job_order_id.project_id', string="Sub Project", store=True)
|
||||
task_id = fields.Many2one(related="job_order_id.task_id", string="Task", store=True)
|
||||
# Department
|
||||
department_id = fields.Many2one(related="job_order_id.department_id", string="Department", store=True)
|
||||
manager_ids = fields.Many2many('res.users', store=True, string="Manager")
|
||||
user_id = fields.Many2one(related="job_order_id.user_id", string="Responsible")
|
||||
|
||||
# Calculation
|
||||
qty = fields.Integer(string="Qty.", default=1)
|
||||
remain_qty = fields.Integer(string="Remaining Qty")
|
||||
total_cost = fields.Monetary(string="Total Cost")
|
||||
remaining_amount = fields.Monetary(string="Remaining Amount")
|
||||
progress = fields.Float(string="Completed Billing", compute="_compute_payment_progress")
|
||||
tax_id = fields.Many2one('account.tax', string='Tax')
|
||||
completion_date = fields.Date(string="Completion Date", compute="compute_completion_date")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('seq', _('New')) == _('New'):
|
||||
vals['seq'] = self.env['ir.sequence'].next_by_code('overhead.sub') or _('New')
|
||||
res = super(OverheadSubcontract, self).create(vals_list)
|
||||
return res
|
||||
|
||||
def action_in_progress(self):
|
||||
self.stage = 'in_progress'
|
||||
|
||||
def action_state_done(self):
|
||||
self.stage = 'done'
|
||||
|
||||
@api.constrains('ra_bill_ids', 'qty')
|
||||
def _check_ra_bill_qty(self):
|
||||
qty = 0
|
||||
for record in self.ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
qty = qty + record.qty
|
||||
if qty > self.qty:
|
||||
raise ValidationError(_("Quantity should be less than total qty."))
|
||||
|
||||
@api.depends('total_cost', 'remaining_amount')
|
||||
def _compute_payment_progress(self):
|
||||
for rec in self:
|
||||
progress = 0.0
|
||||
if rec.total_cost and rec.remaining_amount:
|
||||
progress = (rec.remaining_amount * 100) / rec.total_cost
|
||||
rec.progress = 100 - progress
|
||||
|
||||
@api.depends('stage', 'ra_bill_ids')
|
||||
def compute_completion_date(self):
|
||||
for rec in self:
|
||||
date = None
|
||||
if rec.stage == "done":
|
||||
dates = rec.ra_bill_ids.mapped('date')
|
||||
if dates:
|
||||
date = dates[-1]
|
||||
rec.completion_date = date
|
||||
|
||||
|
||||
class OverheadContractLine(models.Model):
|
||||
_name = 'overhead.contract.line'
|
||||
_description = "Overhead Contract Line"
|
||||
_rec_name = 'remark'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
contract_id = fields.Many2one('overhead.subcontract', string="Overhead Subcontract", ondelete='cascade')
|
||||
percentage = fields.Float(string="Percentage", tracking=True, compute="_compute_percentage")
|
||||
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')
|
||||
amount = fields.Monetary(tracking=True, string="Amount", compute="_compute_percentage_amount")
|
||||
date = fields.Date(string="Date", default=fields.Date.today(), tracking=True)
|
||||
remark = fields.Char(string="Remark", tracking=True)
|
||||
purchase_order_id = fields.Many2one('purchase.order', string="Purchase Order")
|
||||
bill_id = fields.Many2one('account.move', string="Bill")
|
||||
payment_state = fields.Selection(related="bill_id.payment_state", string="Payment State", tracking=True)
|
||||
state = fields.Selection(related="purchase_order_id.state", string="State", tracking=True)
|
||||
po_bill = fields.Selection(related="contract_id.po_bill")
|
||||
qty = fields.Integer(string="Qty")
|
||||
retention_percentage = fields.Float(string="Retention(%)")
|
||||
retention_amount = fields.Monetary(string="Retention Amount", compute="compute_retention_amount")
|
||||
final_amount = fields.Monetary(string="Total Amount", compute="_compute_final_amount")
|
||||
|
||||
# QC Check
|
||||
qc_user_id = fields.Many2one('res.users', string="QC Responsible", tracking=True)
|
||||
qc_status = fields.Selection([('draft', 'Draft'),
|
||||
('request', 'Department Approval'),
|
||||
('approve', 'Approve'),
|
||||
('reject', 'Reject')], default='draft', string="Quality Check Status", tracking=True)
|
||||
reject_reason = fields.Text(string="Reject Reason", tracking=True)
|
||||
|
||||
def unlink(self):
|
||||
for rec in self:
|
||||
if rec.qc_status != 'draft':
|
||||
raise ValidationError(_("You can't delete until Quality Check status is in Draft"))
|
||||
else:
|
||||
return super(OverheadContractLine, self).unlink()
|
||||
|
||||
@api.constrains('qty')
|
||||
def _check_ra_bill_qty(self):
|
||||
ra_bill_ids = self.env['overhead.contract.line'].search([('contract_id', '=', self.contract_id.id)])
|
||||
qty = 0
|
||||
for record in ra_bill_ids:
|
||||
if record.qc_status != 'reject':
|
||||
qty = qty + record.qty
|
||||
if qty > self.contract_id.qty:
|
||||
raise ValidationError(_("Quantity should be less than total qty."))
|
||||
|
||||
@api.depends('contract_id', 'qty', 'contract_id.tax_id')
|
||||
def _compute_percentage_amount(self):
|
||||
for rec in self:
|
||||
amount = 0.0
|
||||
if rec.qty and rec.contract_id:
|
||||
amount = (rec.contract_id.cost + (rec.contract_id.tax_id.amount * rec.contract_id.cost / 100)) * rec.qty
|
||||
rec.amount = amount
|
||||
|
||||
@api.depends('qty', 'retention_amount')
|
||||
def _compute_final_amount(self):
|
||||
for rec in self:
|
||||
total = 0.0
|
||||
if rec.amount:
|
||||
total = rec.amount - rec.retention_amount
|
||||
rec.final_amount = total
|
||||
|
||||
@api.depends('amount', 'retention_percentage')
|
||||
def compute_retention_amount(self):
|
||||
for rec in self:
|
||||
retention_amount = 0.0
|
||||
if rec.retention_percentage:
|
||||
retention_amount = rec.amount * rec.retention_percentage / 100
|
||||
rec.retention_amount = retention_amount
|
||||
|
||||
@api.depends('contract_id', 'amount')
|
||||
def _compute_percentage(self):
|
||||
for rec in self:
|
||||
percentage = 0.0
|
||||
if rec.contract_id and rec.amount > 0:
|
||||
percentage = (100 * rec.amount) / rec.contract_id.total_cost
|
||||
rec.percentage = percentage
|
||||
|
||||
def action_quality_check(self):
|
||||
self.qc_status = 'request'
|
||||
|
||||
def action_quality_check_approve(self):
|
||||
self.qc_status = 'approve'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_quality_check_reject(self):
|
||||
self.qc_status = 'reject'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
self.qc_status = 'draft'
|
||||
|
||||
def action_create_ra_bill(self):
|
||||
if self.po_bill == 'bill':
|
||||
record = {
|
||||
'product_id': self.contract_id.product_id.id,
|
||||
'name': self.contract_id.name,
|
||||
'quantity': 1,
|
||||
'price_unit': self.final_amount,
|
||||
'tax_ids': False
|
||||
}
|
||||
invoice_lines = [(0, 0, record)]
|
||||
data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'invoice_date': self.date,
|
||||
'invoice_line_ids': invoice_lines,
|
||||
'move_type': 'in_invoice',
|
||||
'overhead_subcontract_id': self.contract_id.id,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'purchase_order': 'overhead'
|
||||
}
|
||||
invoice_id = self.env['account.move'].sudo().create(data)
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
self.bill_id = invoice_id.id
|
||||
qty = self.contract_id.remain_qty
|
||||
self.contract_id.remain_qty = qty - self.qty
|
||||
elif self.po_bill == 'purchase_order':
|
||||
purchase_record = {
|
||||
'product_id': self.contract_id.product_id.id,
|
||||
'name': self.contract_id.name
|
||||
,
|
||||
'product_qty': 1,
|
||||
'price_unit': self.final_amount,
|
||||
}
|
||||
purchase_lines = [(0, 0, purchase_record)]
|
||||
purchase_data = {
|
||||
'partner_id': self.contract_id.vendor_id.id,
|
||||
'order_line': purchase_lines,
|
||||
'job_order_id': self.contract_id.job_order_id.id,
|
||||
'overhead_subcontract_id': self.contract_id.id,
|
||||
'purchase_order': 'overhead'
|
||||
}
|
||||
purchase_order_id = self.env['purchase.order'].create(
|
||||
purchase_data)
|
||||
self.purchase_order_id = purchase_order_id.id
|
||||
remaining_amount = self.contract_id.remaining_amount
|
||||
self.contract_id.remaining_amount = remaining_amount - self.amount
|
||||
qty = self.contract_id.remain_qty
|
||||
self.contract_id.remain_qty = qty - self.qty
|
||||
|
||||
|
||||
class MaterialConsume(models.Model):
|
||||
_name = 'material.consume'
|
||||
_description = "Material Consume Order"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_rec_name = 'seq'
|
||||
|
||||
seq = fields.Char(string='Sequence', required=True, readonly=True, default=lambda self: _('New'))
|
||||
date = fields.Date(string="Date", default=fields.Date.today())
|
||||
remark = fields.Char(string="Remark")
|
||||
|
||||
warehouse_id = fields.Many2one('stock.warehouse')
|
||||
consume_order_id = fields.Many2one('stock.picking', string="Consume Order")
|
||||
state = fields.Selection(related="consume_order_id.state", string="Status")
|
||||
consume_order_ids = fields.One2many('material.consume.line', 'material_consume_id', string="Material Consume")
|
||||
|
||||
# Project Details
|
||||
job_order_id = fields.Many2one('job.order', string="Work Order")
|
||||
phase_id = fields.Many2one(related="job_order_id.job_sheet_id", string="Project Phase(WBS)", store=True)
|
||||
project_id = fields.Many2one(related='job_order_id.project_id', string="Sub Project", store=True)
|
||||
task_id = fields.Many2one(related="job_order_id.task_id", string="Task", store=True)
|
||||
# Department
|
||||
department_id = fields.Many2one(related="job_order_id.department_id", string="Department", store=True)
|
||||
manager_ids = fields.Many2many('res.users', store=True, string="Manager")
|
||||
user_id = fields.Many2one(related="job_order_id.user_id", string="Responsible", store=True)
|
||||
|
||||
# Quality Check
|
||||
qc_user_id = fields.Many2one('res.users', string="QC Responsible", tracking=True)
|
||||
qc_status = fields.Selection([('draft', 'Draft'), ('request', 'Department Approval'), ('approve', 'Approve'),
|
||||
('reject', 'Reject'), ('cancel', 'Cancel')], default='draft',
|
||||
string="Quality Check Status", tracking=True)
|
||||
reject_reason = fields.Text(string="Reject Reason", tracking=True)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('seq', _('New')) == _('New'):
|
||||
vals['seq'] = self.env['ir.sequence'].next_by_code('material.consume') or _('New')
|
||||
res = super(MaterialConsume, self).create(vals_list)
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
if self.qc_status != 'draft':
|
||||
raise ValidationError(_("You can't delete until Quality Check status is in Draft"))
|
||||
else:
|
||||
return super(MaterialConsume, self).unlink()
|
||||
|
||||
@api.constrains('consume_order_ids')
|
||||
def _check_material_line_qty(self):
|
||||
for rec in self.consume_order_ids:
|
||||
if rec.qty > rec.material_line_id.remain_qty:
|
||||
raise ValidationError(_("Qty should be less than remain Qty."))
|
||||
|
||||
def action_quality_check(self):
|
||||
self.qc_status = 'request'
|
||||
|
||||
def action_quality_check_approve(self):
|
||||
self.qc_status = 'approve'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_quality_check_reject(self):
|
||||
self.qc_status = 'reject'
|
||||
self.qc_user_id = self.env.user.id
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
self.qc_status = 'draft'
|
||||
|
||||
def action_cancel_consume_order(self):
|
||||
self.qc_status = 'cancel'
|
||||
|
||||
def action_create_consume_order(self):
|
||||
dest_location_id = False
|
||||
if self.warehouse_id.consume_stock_location_id:
|
||||
dest_location_id = self.warehouse_id.consume_stock_location_id
|
||||
else:
|
||||
dest_location_id = self.env['stock.location'].create(
|
||||
{'name': "Consume Location/" + str(self.warehouse_id.name), 'usage': 'production'})
|
||||
self.warehouse_id.consume_stock_location_id = dest_location_id.id
|
||||
lines = []
|
||||
for rec in self.consume_order_ids:
|
||||
lines.append((0, 0, {
|
||||
'product_id': rec.material_id.id,
|
||||
'product_uom_qty': rec.qty,
|
||||
'product_uom': rec.uom_id.id,
|
||||
'location_id': self.warehouse_id.lot_stock_id.id,
|
||||
'location_dest_id': dest_location_id.id,
|
||||
'name': rec.name
|
||||
}))
|
||||
source_id = self.warehouse_id.lot_stock_id
|
||||
stock_picking_type_id = self.env['stock.picking.type'].search(
|
||||
[('code', '=', 'outgoing'), ('warehouse_id', '=', self.warehouse_id.id)], limit=1)
|
||||
delivery_record = {
|
||||
'picking_type_id': stock_picking_type_id.id,
|
||||
'location_id': source_id.id,
|
||||
'location_dest_id': dest_location_id.id,
|
||||
'move_ids_without_package': lines,
|
||||
'consume_order_id': self.job_order_id.id,
|
||||
'material_consume_id': self.id,
|
||||
'move_type': 'one'
|
||||
}
|
||||
delivery_id = self.env['stock.picking'].create(delivery_record)
|
||||
self.consume_order_id = delivery_id.id
|
||||
for rec in self.consume_order_ids:
|
||||
remain_qty = rec.material_line_id.remain_qty
|
||||
usage_qty = rec.material_line_id.usage_qty
|
||||
rec.material_line_id.remain_qty = remain_qty - rec.qty
|
||||
rec.material_line_id.usage_qty = usage_qty + rec.qty
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Consume Order'),
|
||||
'res_model': 'stock.picking',
|
||||
'res_id': delivery_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
|
||||
class MaterialConsumeLine(models.Model):
|
||||
_name = 'material.consume.line'
|
||||
_description = "Material Consume Line"
|
||||
|
||||
material_id = fields.Many2one('product.product', string="Material", domain="[('is_material','=',True)]")
|
||||
uom_id = fields.Many2one(related="material_id.uom_id", string="UOM")
|
||||
name = fields.Char(string="Description")
|
||||
qty = fields.Integer(string="Qty")
|
||||
material_consume_id = fields.Many2one('material.consume', string="Material Consume")
|
||||
qc_status = fields.Selection(related="material_consume_id.qc_status")
|
||||
material_line_id = fields.Many2one('order.material.line')
|
||||
@@ -0,0 +1,422 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="work_completetion_report_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<t t-if="o.stage == 'done'">
|
||||
<h1 style="text-align:center;">
|
||||
Work Completion Certificate
|
||||
</h1>
|
||||
<br />
|
||||
<br />
|
||||
<h1 style="text-align:center;border-bottom:1px solid black;">
|
||||
To Whom it may concern
|
||||
</h1>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Project / Sub Project
|
||||
</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.site_id" /> / <span
|
||||
t-field="o.project_id" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Task Title</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.task_name" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Work Order Number</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.name" /> -<span
|
||||
t-field="o.seq" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;font-size:18px;">
|
||||
<h5>
|
||||
<strong>Work Period</strong>
|
||||
</h5>
|
||||
</td>
|
||||
<td style="font-size:18px;border-bottom:1px solid black;">
|
||||
<strong>
|
||||
<span t-field="o.job_order_id.start_date" /> To <span
|
||||
t-field="o.job_order_id.end_date" />
|
||||
</strong>
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:10%;"></td>
|
||||
<td style="width:80%;text-align:center;">
|
||||
<p> This is to clearify that <strong>
|
||||
<span t-field="o.user_id.name" />
|
||||
</strong>
|
||||
has successfully completed <strong>
|
||||
<span t-field="o.job_type_id.name" /> -<span
|
||||
t-field="o.sub_category_id.name" />
|
||||
</strong>
|
||||
work . <br />
|
||||
<br /> The Work is completed on <strong>
|
||||
<span t-field="o.completion_date" />
|
||||
</strong>
|
||||
succesfully. <br /> Thanking you and assuring you for
|
||||
our best always </p>
|
||||
</td>
|
||||
<td style="width:10%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Customer Comment
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black; "></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;text-align:center;">Authorised Signature</td>
|
||||
<td style="width:40%;"></td>
|
||||
<td style="width:30%;text-align:center;">Signature</td>
|
||||
</tr>
|
||||
<tr style="height:30px;">
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
<td style="width:40%;"></td>
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
Please Complete Subcontract to print work completion certificate.
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
<template id="labour_work_completetion_report_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<t t-if="o.stage == 'done'">
|
||||
<h1 style="text-align:center;">
|
||||
Work Completion Certificate
|
||||
</h1>
|
||||
<br />
|
||||
<br />
|
||||
<h1 style="text-align:center;border-bottom:1px solid black;">
|
||||
To Whom it may concern
|
||||
</h1>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Project / Sub Project
|
||||
</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.site_id" /> / <span
|
||||
t-field="o.project_id" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Task Title</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.task_name" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Work Order Number</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.name" /> -<span
|
||||
t-field="o.seq" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;font-size:18px;">
|
||||
<h5>
|
||||
<strong>Work Period</strong>
|
||||
</h5>
|
||||
</td>
|
||||
<td style="font-size:18px;border-bottom:1px solid black;">
|
||||
<strong>
|
||||
<span t-field="o.job_order_id.start_date" /> To <span
|
||||
t-field="o.job_order_id.end_date" />
|
||||
</strong>
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:10%;"></td>
|
||||
<td style="width:80%;text-align:center;">
|
||||
<p> This is to clearify that <strong>
|
||||
<span t-field="o.user_id.name" />
|
||||
</strong>
|
||||
has successfully completed <strong>
|
||||
<span t-field="o.job_type_id.name" /> -<span
|
||||
t-field="o.sub_category_id.name" />
|
||||
</strong>
|
||||
work . <br />
|
||||
<br /> The Work is completed on <strong>
|
||||
<span t-field="o.completion_date" />
|
||||
</strong>
|
||||
succesfully. <br /> Thanking you and assuring you for
|
||||
our best always </p>
|
||||
</td>
|
||||
<td style="width:10%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Customer Comment
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black; "></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;text-align:center;">Authorised Signature</td>
|
||||
<td style="width:40%;"></td>
|
||||
<td style="width:30%;text-align:center;">Signature</td>
|
||||
</tr>
|
||||
<tr style="height:30px;">
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
<td style="width:40%;"></td>
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
Please Complete Subcontract to print work completion certificate.
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
<template id="overhead_work_completetion_report_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<t t-if="o.stage == 'done'">
|
||||
<h1 style="text-align:center;">
|
||||
Work Completion Certificate
|
||||
</h1>
|
||||
<br />
|
||||
<br />
|
||||
<h1 style="text-align:center;border-bottom:1px solid black;">
|
||||
To Whom it may concern
|
||||
</h1>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Project / Sub Project
|
||||
</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.site_id" /> / <span
|
||||
t-field="o.project_id" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Task Title</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.task_name" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">Work Order Number</td>
|
||||
<td style="border-bottom:1px solid black;">
|
||||
<span t-field="o.job_order_id.name" /> -<span
|
||||
t-field="o.seq" />
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;font-size:18px;">
|
||||
<h5>
|
||||
<strong>Work Period</strong>
|
||||
</h5>
|
||||
</td>
|
||||
<td style="font-size:18px;border-bottom:1px solid black;">
|
||||
<strong>
|
||||
<span t-field="o.job_order_id.start_date" /> To <span
|
||||
t-field="o.job_order_id.end_date" />
|
||||
</strong>
|
||||
</td>
|
||||
<td style="width:20%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:10%;"></td>
|
||||
<td style="width:80%;text-align:center;">
|
||||
<p> This is to clearify that <strong>
|
||||
<span t-field="o.user_id.name" />
|
||||
</strong>
|
||||
has successfully completed <strong>
|
||||
<span t-field="o.job_type_id.name" /> -<span
|
||||
t-field="o.sub_category_id.name" />
|
||||
</strong>
|
||||
work . <br />
|
||||
<br /> The Work is completed on <strong>
|
||||
<span t-field="o.completion_date" />
|
||||
</strong>
|
||||
succesfully. <br /> Thanking you and assuring you for
|
||||
our best always </p>
|
||||
</td>
|
||||
<td style="width:10%;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;">
|
||||
Customer Comment
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black; "></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20%;"></td>
|
||||
<td style="width:20%;height:20px;">
|
||||
</td>
|
||||
<td style="border-bottom: 1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="width:20%;text-align:center;">Authorised Signature</td>
|
||||
<td style="width:40%;"></td>
|
||||
<td style="width:30%;text-align:center;">Signature</td>
|
||||
</tr>
|
||||
<tr style="height:30px;">
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
<td style="width:40%;"></td>
|
||||
<td
|
||||
style="width:30%;text-align:center;border-bottom:1px solid black;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
Please Complete Subcontract to print work completion certificate.
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
<!-- Equipment Subcontract Report -->
|
||||
<record id="equipment_subcontract_work_completion_report" model="ir.actions.report">
|
||||
<field name="name">Work Completion Certificate</field>
|
||||
<field name="model">equipment.subcontract</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">tk_construction_management.work_completetion_report_template</field>
|
||||
<field name="report_file">tk_construction_management.work_completetion_report_template</field>
|
||||
<field name="binding_model_id" ref="model_equipment_subcontract" />
|
||||
<field name="binding_type">report</field>
|
||||
<field name="print_report_name">'Work Completion Certificate'</field>
|
||||
</record>
|
||||
<!-- Labour Subcontract Report -->
|
||||
<record id="labour_subcontract_work_completion_report" model="ir.actions.report">
|
||||
<field name="name">Work Completion Certificate</field>
|
||||
<field name="model">labour.subcontract</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">tk_construction_management.labour_work_completetion_report_template</field>
|
||||
<field name="report_file">tk_construction_management.labour_work_completetion_report_template</field>
|
||||
<field name="binding_model_id" ref="model_labour_subcontract" />
|
||||
<field name="binding_type">report</field>
|
||||
<field name="print_report_name">'Work Completion Certificate'</field>
|
||||
</record>
|
||||
<!-- Overhead Subcontract Report -->
|
||||
<record id="overhead_subcontract_work_completion_report" model="ir.actions.report">
|
||||
<field name="name">Work Completion Certificate</field>
|
||||
<field name="model">overhead.subcontract</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">tk_construction_management.overhead_work_completetion_report_template</field>
|
||||
<field name="report_file">tk_construction_management.overhead_work_completetion_report_template</field>
|
||||
<field name="binding_model_id" ref="model_overhead_subcontract" />
|
||||
<field name="binding_type">report</field>
|
||||
<field name="print_report_name">'Work Completion Certificate'</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
33
addons/tk_construction_management/security/groups.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="module_tk_advance_construction" model="ir.module.category">
|
||||
<field name="name">Advance Construction</field>
|
||||
<field name="description">Access rights for Advance Construction Management</field>
|
||||
</record>
|
||||
<record id="advance_construction_manager" model="res.groups">
|
||||
<field name="name">Construction Manager</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="category_id" ref="module_tk_advance_construction"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
<record id="advance_construction_department" model="res.groups">
|
||||
<field name="name">Construction Department</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="category_id" ref="module_tk_advance_construction"/>
|
||||
</record>
|
||||
<record id="advance_construction_user" model="res.groups">
|
||||
<field name="name">Construction User</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="category_id" ref="module_tk_advance_construction"/>
|
||||
</record>
|
||||
<record id="advance_construction_qc_user" model="res.groups">
|
||||
<field name="name">Construction QC User</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="category_id" ref="module_tk_advance_construction"/>
|
||||
</record>
|
||||
<record id="base.default_user" model="res.users">
|
||||
<field name="groups_id" eval="[(4, ref('advance_construction_manager'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
305
addons/tk_construction_management/security/ir.model.access.csv
Normal file
@@ -0,0 +1,305 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
tk_construction_management.access_tk_construction_site_manager,access_tk_construction_site_manager,tk_construction_management.model_tk_construction_site,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_site_department,access_tk_construction_site_department,tk_construction_management.model_tk_construction_site,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_site_user,access_tk_construction_site_user,tk_construction_management.model_tk_construction_site,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_site_qc_user,access_tk_construction_site_qc_user,tk_construction_management.model_tk_construction_site,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_stakeholder_line_manager,access_stakeholder_line_manager,tk_construction_management.model_stakeholder_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_stakeholder_line_department,access_stakeholder_line_department,tk_construction_management.model_stakeholder_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_stakeholder_line_user,access_stakeholder_line_user,tk_construction_management.model_stakeholder_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_stakeholder_line_qc_user,access_stakeholder_line_qc_user,tk_construction_management.model_stakeholder_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_site_documents_manager,access_site_documents_manager,tk_construction_management.model_site_documents,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_site_documents_department,access_site_documents_department,tk_construction_management.model_site_documents,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_site_documents_user,access_site_documents_user,tk_construction_management.model_site_documents,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_site_documents_qc_user,access_site_documents_qc_user,tk_construction_management.model_site_documents,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_site_document_type_manager,access_site_document_type_manager,tk_construction_management.model_site_document_type,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_site_document_type_department,access_site_document_type_department,tk_construction_management.model_site_document_type,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_site_document_type_user,access_site_document_type_user,tk_construction_management.model_site_document_type,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_site_document_type_qc_user,access_site_document_type_qc_user,tk_construction_management.model_site_document_type,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_site_images_manager,access_site_images_manager,tk_construction_management.model_site_images,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_site_images_department,access_site_images_department,tk_construction_management.model_site_images,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_site_images_user,access_site_images_user,tk_construction_management.model_site_images,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_site_images_qc_user,access_site_images_qc_user,tk_construction_management.model_site_images,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_site_dimension_manager,access_site_dimension_manager,tk_construction_management.model_site_dimension,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_site_dimension_department,access_site_dimension_department,tk_construction_management.model_site_dimension,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_site_dimension_user,access_site_dimension_user,tk_construction_management.model_site_dimension,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_site_dimension_qc_user,access_site_dimension_qc_user,tk_construction_management.model_site_dimension,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_document_permit_manager,access_document_permit_manager,tk_construction_management.model_document_permit,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_document_permit_department,access_document_permit_department,tk_construction_management.model_document_permit,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_document_permit_user,access_document_permit_user,tk_construction_management.model_document_permit,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_document_permit_qc_user,access_document_permit_qc_user,tk_construction_management.model_document_permit,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_tk_construction_project_manager,access_tk_construction_project_manager,tk_construction_management.model_tk_construction_project,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_project_department,access_tk_construction_project_department,tk_construction_management.model_tk_construction_project,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_project_user,access_tk_construction_project_user,tk_construction_management.model_tk_construction_project,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_project_qc_user,access_tk_construction_project_qc_user,tk_construction_management.model_tk_construction_project,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_site_project_manager,access_site_project_manager,tk_construction_management.model_site_project,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_site_project_department,access_site_project_department,tk_construction_management.model_site_project,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_site_project_user,access_site_project_user,tk_construction_management.model_site_project,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_site_project_qc_user,access_site_project_qc_user,tk_construction_management.model_site_project,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_job_type_manager,access_job_type_manager,tk_construction_management.model_job_type,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_job_type_department,access_job_type_department,tk_construction_management.model_job_type,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_job_type_user,access_job_type_user,tk_construction_management.model_job_type,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_job_type_qc_user,access_job_type_qc_user,tk_construction_management.model_job_type,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_job_sub_category_manager,access_job_sub_category_manager,tk_construction_management.model_job_sub_category,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_job_sub_category_department,access_job_sub_category_department,tk_construction_management.model_job_sub_category,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_job_sub_category_user,access_job_sub_category_user,tk_construction_management.model_job_sub_category,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_job_sub_category_qc_user,access_job_sub_category_qc_user,tk_construction_management.model_job_sub_category,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_construction_department_manager,access_construction_department_manager,tk_construction_management.model_construction_department,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_construction_department_department,access_construction_department_department,tk_construction_management.model_construction_department,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_construction_department_user,access_construction_department_user,tk_construction_management.model_construction_department,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_construction_department_qc_user,access_construction_department_qc_user,tk_construction_management.model_construction_department,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_insurance_risk_manager,access_insurance_risk_manager,tk_construction_management.model_insurance_risk,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_insurance_risk_department,access_insurance_risk_department,tk_construction_management.model_insurance_risk,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_insurance_risk_user,access_insurance_risk_user,tk_construction_management.model_insurance_risk,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_insurance_risk_qc_user,access_insurance_risk_qc_user,tk_construction_management.model_insurance_risk,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_project_documents_manager,access_project_documents_manager,tk_construction_management.model_project_documents,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_project_documents_department,access_project_documents_department,tk_construction_management.model_project_documents,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_project_documents_user,access_project_documents_user,tk_construction_management.model_project_documents,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_project_documents_qc_user,access_project_documents_qc_user,tk_construction_management.model_project_documents,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_project_insurance_manager,access_project_insurance_manager,tk_construction_management.model_project_insurance,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_project_insurance_department,access_project_insurance_department,tk_construction_management.model_project_insurance,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_project_insurance_user,access_project_insurance_user,tk_construction_management.model_project_insurance,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_project_insurance_qc_user,access_project_insurance_qc_user,tk_construction_management.model_project_insurance,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_extra_expense_manager,access_extra_expense_manager,tk_construction_management.model_extra_expense,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_extra_expense_department,access_extra_expense_department,tk_construction_management.model_extra_expense,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_extra_expense_user,access_extra_expense_user,tk_construction_management.model_extra_expense,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_extra_expense_qc_user,access_extra_expense_qc_user,tk_construction_management.model_extra_expense,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_job_costing_manager,access_job_costing_manager,tk_construction_management.model_job_costing,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_job_costing_department,access_job_costing_department,tk_construction_management.model_job_costing,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_job_costing_user,access_job_costing_user,tk_construction_management.model_job_costing,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_job_costing_qc_user,access_job_costing_qc_user,tk_construction_management.model_job_costing,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_cost_equipment_line_manager,access_cost_equipment_line_manager,tk_construction_management.model_cost_equipment_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_cost_equipment_line_department,access_cost_equipment_line_department,tk_construction_management.model_cost_equipment_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_cost_equipment_line_user,access_cost_equipment_line_user,tk_construction_management.model_cost_equipment_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_cost_equipment_line_qc_user,access_cost_equipment_line_qc_user,tk_construction_management.model_cost_equipment_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_cost_material_line_manager,access_cost_material_line_manager,tk_construction_management.model_cost_material_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_cost_material_line_department,access_cost_material_line_department,tk_construction_management.model_cost_material_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_cost_material_line_user,access_cost_material_line_user,tk_construction_management.model_cost_material_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_cost_material_line_qc_user,access_cost_material_line_qc_user,tk_construction_management.model_cost_material_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_cost_labour_line_manager,access_cost_labour_line_manager,tk_construction_management.model_cost_labour_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_cost_labour_line_department,access_cost_labour_line_department,tk_construction_management.model_cost_labour_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_cost_labour_line_user,access_cost_labour_line_user,tk_construction_management.model_cost_labour_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_cost_labour_line_qc_user,access_cost_labour_line_qc_user,tk_construction_management.model_cost_labour_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_cost_overhead_line_manager,access_cost_overhead_line_manager,tk_construction_management.model_cost_overhead_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_cost_overhead_line_department,access_cost_overhead_line_department,tk_construction_management.model_cost_overhead_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_cost_overhead_line_user,access_cost_overhead_line_user,tk_construction_management.model_cost_overhead_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_cost_overhead_line_qc_user,access_cost_overhead_line_qc_user,tk_construction_management.model_cost_overhead_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_job_order_manager,access_job_order_manager,tk_construction_management.model_job_order,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_job_order_department,access_job_order_department,tk_construction_management.model_job_order,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_job_order_user,access_job_order_user,tk_construction_management.model_job_order,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_job_order_qc_user,access_job_order_qc_user,tk_construction_management.model_job_order,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_order_material_line_manager,access_order_material_line_manager,tk_construction_management.model_order_material_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_order_material_line_department,access_order_material_line_department,tk_construction_management.model_order_material_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_order_material_line_user,access_order_material_line_user,tk_construction_management.model_order_material_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_order_material_line_qc_user,access_order_material_line_qc_user,tk_construction_management.model_order_material_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_order_equipment_line_manager,access_order_equipment_line_manager,tk_construction_management.model_order_equipment_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_order_equipment_line_department,access_order_equipment_line_department,tk_construction_management.model_order_equipment_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_order_equipment_line_user,access_order_equipment_line_user,tk_construction_management.model_order_equipment_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_order_equipment_line_qc_user,access_order_equipment_line_qc_user,tk_construction_management.model_order_equipment_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_order_labour_line_manager,access_order_labour_line_manager,tk_construction_management.model_order_labour_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_order_labour_line_department,access_order_labour_line_department,tk_construction_management.model_order_labour_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_order_labour_line_user,access_order_labour_line_user,tk_construction_management.model_order_labour_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_order_labour_line_qc_user,access_order_labour_line_qc_user,tk_construction_management.model_order_labour_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_order_overhead_line_manager,access_order_overhead_line_manager,tk_construction_management.model_order_overhead_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_order_overhead_line_department,access_order_overhead_line_department,tk_construction_management.model_order_overhead_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_order_overhead_line_user,access_order_overhead_line_user,tk_construction_management.model_order_overhead_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_order_overhead_line_qc_user,access_order_overhead_line_qc_user,tk_construction_management.model_order_overhead_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_project_warehouse_manager,access_project_warehouse_manager,tk_construction_management.model_project_warehouse,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_project_warehouse_department,access_project_warehouse_department,tk_construction_management.model_project_warehouse,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_project_warehouse_user,access_project_warehouse_user,tk_construction_management.model_project_warehouse,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_project_warehouse_qc_user,access_project_warehouse_qc_user,tk_construction_management.model_project_warehouse,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
|
||||
tk_construction_management.access_material_requisition_manager,access_material_requisition_manager,tk_construction_management.model_material_requisition,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_department,access_material_requisition_department,tk_construction_management.model_material_requisition,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_user,access_material_requisition_user,tk_construction_management.model_material_requisition,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_qc_user,access_material_requisition_qc_user,tk_construction_management.model_material_requisition,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_material_requisition_line_manager,access_material_requisition_line_manager,tk_construction_management.model_material_requisition_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_line_department,access_material_requisition_line_department,tk_construction_management.model_material_requisition_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_line_user,access_material_requisition_line_user,tk_construction_management.model_material_requisition_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_requisition_line_qc_user,access_material_requisition_line_qc_user,tk_construction_management.model_material_requisition_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_material_purchase_line_manager,access_material_purchase_line_manager,tk_construction_management.model_material_purchase_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_purchase_line_department,access_material_purchase_line_department,tk_construction_management.model_material_purchase_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_purchase_line_user,access_material_purchase_line_user,tk_construction_management.model_material_purchase_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_purchase_line_qc_user,access_material_purchase_line_qc_user,tk_construction_management.model_material_purchase_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_material_transfer_line_manager,access_material_transfer_line_manager,tk_construction_management.model_material_transfer_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_transfer_line_department,access_material_transfer_line_department,tk_construction_management.model_material_transfer_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_transfer_line_user,access_material_transfer_line_user,tk_construction_management.model_material_transfer_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_transfer_line_qc_user,access_material_transfer_line_qc_user,tk_construction_management.model_material_transfer_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_requisition_reject_manager,access_requisition_reject_manager,tk_construction_management.model_requisition_reject,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_requisition_reject_department,access_requisition_reject_department,tk_construction_management.model_requisition_reject,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_requisition_reject_user,access_requisition_reject_user,tk_construction_management.model_requisition_reject,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_requisition_reject_qc_user,access_requisition_reject_qc_user,tk_construction_management.model_requisition_reject,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_construction_product_template_manager,access_construction_product_template_manager,tk_construction_management.model_construction_product_template,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_construction_product_template_department,access_construction_product_template_department,tk_construction_management.model_construction_product_template,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_construction_product_template_user,access_construction_product_template_user,tk_construction_management.model_construction_product_template,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_construction_product_template_qc_user,access_construction_product_template_qc_user,tk_construction_management.model_construction_product_template,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_construction_template_line_manager,access_construction_template_line_manager,tk_construction_management.model_construction_template_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_construction_template_line_department,access_construction_template_line_department,tk_construction_management.model_construction_template_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_construction_template_line_user,access_construction_template_line_user,tk_construction_management.model_construction_template_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_construction_template_line_qc_user,access_construction_template_line_qc_user,tk_construction_management.model_construction_template_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_import_material_manager,access_import_material_manager,tk_construction_management.model_import_material,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_import_material_department,access_import_material_department,tk_construction_management.model_import_material,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_import_material_user,access_import_material_user,tk_construction_management.model_import_material,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_import_material_qc_user,access_import_material_qc_user,tk_construction_management.model_import_material,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_import_material_sheet_manager,access_import_material_sheet_manager,tk_construction_management.model_import_material_sheet,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_import_material_sheet_department,access_import_material_sheet_department,tk_construction_management.model_import_material_sheet,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_import_material_sheet_user,access_import_material_sheet_user,tk_construction_management.model_import_material_sheet,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_import_material_sheet_qc_user,access_import_material_sheet_qc_user,tk_construction_management.model_import_material_sheet,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_internal_transfer_manager,access_internal_transfer_manager,tk_construction_management.model_internal_transfer,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_department,access_internal_transfer_department,tk_construction_management.model_internal_transfer,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_user,access_internal_transfer_user,tk_construction_management.model_internal_transfer,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_qc_user,access_internal_transfer_qc_user,tk_construction_management.model_internal_transfer,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_internal_transfer_line_manager,access_internal_transfer_line_manager,tk_construction_management.model_internal_transfer_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_line_department,access_internal_transfer_line_department,tk_construction_management.model_internal_transfer_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_line_user,access_internal_transfer_line_user,tk_construction_management.model_internal_transfer_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_internal_transfer_line_qc_user,access_internal_transfer_line_qc_user,tk_construction_management.model_internal_transfer_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_scrap_order_manager,access_scrap_order_manager,tk_construction_management.model_scrap_order,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_department,access_scrap_order_department,tk_construction_management.model_scrap_order,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_user,access_scrap_order_user,tk_construction_management.model_scrap_order,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_qc_user,access_scrap_order_qc_user,tk_construction_management.model_scrap_order,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_scrap_order_line_manager,access_scrap_order_line_manager,tk_construction_management.model_scrap_order_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_line_department,access_scrap_order_line_department,tk_construction_management.model_scrap_order_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_line_user,access_scrap_order_line_user,tk_construction_management.model_scrap_order_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_scrap_order_line_qc_user,access_scrap_order_line_qc_user,tk_construction_management.model_scrap_order_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_project_budget_manager,access_project_budget_manager,tk_construction_management.model_project_budget,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_project_budget_department,access_project_budget_department,tk_construction_management.model_project_budget,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_project_budget_user,access_project_budget_user,tk_construction_management.model_project_budget,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_project_budget_qc_user,access_project_budget_qc_user,tk_construction_management.model_project_budget,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_tk_construction_dashboard_manager,access_tk_construction_dashboard_manager,tk_construction_management.model_tk_construction_dashboard,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_dashboard_department,access_tk_construction_dashboard_department,tk_construction_management.model_tk_construction_dashboard,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_dashboard_user,access_tk_construction_dashboard_user,tk_construction_management.model_tk_construction_dashboard,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_tk_construction_dashboard_qc_user,access_tk_construction_dashboard_qc_user,tk_construction_management.model_tk_construction_dashboard,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_equipment_subcontract_manager,access_equipment_subcontract_manager,tk_construction_management.model_equipment_subcontract,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_equipment_subcontract_department,access_equipment_subcontract_department,tk_construction_management.model_equipment_subcontract,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_equipment_subcontract_user,access_equipment_subcontract_user,tk_construction_management.model_equipment_subcontract,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_equipment_subcontract_qc_user,access_equipment_subcontract_qc_user,tk_construction_management.model_equipment_subcontract,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_labour_subcontract_manager,access_labour_subcontract_manager,tk_construction_management.model_labour_subcontract,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_labour_subcontract_department,access_labour_subcontract_department,tk_construction_management.model_labour_subcontract,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_labour_subcontract_user,access_labour_subcontract_user,tk_construction_management.model_labour_subcontract,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_labour_subcontract_qc_user,access_labour_subcontract_qc_user,tk_construction_management.model_labour_subcontract,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_overhead_subcontract_manager,access_overhead_subcontract_manager,tk_construction_management.model_overhead_subcontract,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_overhead_subcontract_department,access_overhead_subcontract_department,tk_construction_management.model_overhead_subcontract,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_overhead_subcontract_user,access_overhead_subcontract_user,tk_construction_management.model_overhead_subcontract,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_overhead_subcontract_qc_user,access_overhead_subcontract_qc_user,tk_construction_management.model_overhead_subcontract,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_material_consume_manager,access_material_consume_manager,tk_construction_management.model_material_consume,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_consume_department,access_material_consume_department,tk_construction_management.model_material_consume,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_consume_user,access_material_consume_user,tk_construction_management.model_material_consume,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_consume_qc_user,access_material_consume_qc_user,tk_construction_management.model_material_consume,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_equip_contract_line_manager,access_equip_contract_line_manager,tk_construction_management.model_equip_contract_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_equip_contract_line_department,access_equip_contract_line_department,tk_construction_management.model_equip_contract_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_equip_contract_line_user,access_equip_contract_line_user,tk_construction_management.model_equip_contract_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_equip_contract_line_qc_user,access_equip_contract_line_qc_user,tk_construction_management.model_equip_contract_line,tk_construction_management.advance_construction_qc_user,1,1,0,0
|
||||
|
||||
tk_construction_management.access_labour_contract_line_manager,access_labour_contract_line_manager,tk_construction_management.model_labour_contract_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_labour_contract_line_department,access_labour_contract_line_department,tk_construction_management.model_labour_contract_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_labour_contract_line_user,access_labour_contract_line_user,tk_construction_management.model_labour_contract_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_labour_contract_line_qc_user,access_labour_contract_line_qc_user,tk_construction_management.model_labour_contract_line,tk_construction_management.advance_construction_qc_user,1,1,0,0
|
||||
|
||||
tk_construction_management.access_overhead_contract_line_manager,access_overhead_contract_line_manager,tk_construction_management.model_overhead_contract_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_overhead_contract_line_department,access_overhead_contract_line_department,tk_construction_management.model_overhead_contract_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_overhead_contract_line_user,access_overhead_contract_line_user,tk_construction_management.model_overhead_contract_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_overhead_contract_line_qc_user,access_overhead_contract_line_qc_user,tk_construction_management.model_overhead_contract_line,tk_construction_management.advance_construction_qc_user,1,1,0,0
|
||||
|
||||
tk_construction_management.access_material_consume_line_manager,access_material_consume_line_manager,tk_construction_management.model_material_consume_line,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_material_consume_line_department,access_material_consume_line_department,tk_construction_management.model_material_consume_line,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_material_consume_line_user,access_material_consume_line_user,tk_construction_management.model_material_consume_line,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_material_consume_line_qc_user,access_material_consume_line_qc_user,tk_construction_management.model_material_consume_line,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_rate_analysis_manager,access_rate_analysis_manager,tk_construction_management.model_rate_analysis,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_department,access_rate_analysis_department,tk_construction_management.model_rate_analysis,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_user,access_rate_analysis_user,tk_construction_management.model_rate_analysis,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_qc_user,access_rate_analysis_qc_user,tk_construction_management.model_rate_analysis,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_boq_budget_manager,access_boq_budget_manager,tk_construction_management.model_boq_budget,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_boq_budget_department,access_boq_budget_department,tk_construction_management.model_boq_budget,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_boq_budget_user,access_boq_budget_user,tk_construction_management.model_boq_budget,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_boq_budget_qc_user,access_boq_budget_qc_user,tk_construction_management.model_boq_budget,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_rate_analysis_material_manager,access_rate_analysis_material_manager,tk_construction_management.model_rate_analysis_material,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_material_department,access_rate_analysis_material_department,tk_construction_management.model_rate_analysis_material,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_material_user,access_rate_analysis_material_user,tk_construction_management.model_rate_analysis_material,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_material_qc_user,access_rate_analysis_material_qc_user,tk_construction_management.model_rate_analysis_material,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
|
||||
tk_construction_management.access_rate_analysis_equipment_manager,access_rate_analysis_equipment_manager,tk_construction_management.model_rate_analysis_equipment,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_equipment_department,access_rate_analysis_equipment_department,tk_construction_management.model_rate_analysis_equipment,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_equipment_user,access_rate_analysis_equipment_user,tk_construction_management.model_rate_analysis_equipment,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_equipment_qc_user,access_rate_analysis_equipment_qc_user,tk_construction_management.model_rate_analysis_equipment,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
|
||||
tk_construction_management.access_rate_analysis_labour_manager,access_rate_analysis_labour_manager,tk_construction_management.model_rate_analysis_labour,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_labour_department,access_rate_analysis_labour_department,tk_construction_management.model_rate_analysis_labour,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_labour_user,access_rate_analysis_labour_user,tk_construction_management.model_rate_analysis_labour,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_labour_qc_user,access_rate_analysis_labour_qc_user,tk_construction_management.model_rate_analysis_labour,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
|
||||
tk_construction_management.access_rate_analysis_overhead_manager,access_rate_analysis_overhead_manager,tk_construction_management.model_rate_analysis_overhead,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_overhead_department,access_rate_analysis_overhead_department,tk_construction_management.model_rate_analysis_overhead,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_overhead_user,access_rate_analysis_overhead_user,tk_construction_management.model_rate_analysis_overhead,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_rate_analysis_overhead_qc_user,access_rate_analysis_overhead_qc_user,tk_construction_management.model_rate_analysis_overhead,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_sub_project_budget_manager,access_sub_project_budget_manager,tk_construction_management.model_sub_project_budget,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_sub_project_budget_department,access_sub_project_budget_department,tk_construction_management.model_sub_project_budget,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_sub_project_budget_user,access_sub_project_budget_user,tk_construction_management.model_sub_project_budget,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_sub_project_budget_qc_user,access_sub_project_budget_qc_user,tk_construction_management.model_sub_project_budget,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_budget_construction_manager,access_budget_construction_manager,tk_construction_management.model_budget_construction,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_budget_construction_department,access_budget_construction_department,tk_construction_management.model_budget_construction,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_budget_construction_user,access_budget_construction_user,tk_construction_management.model_budget_construction,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_budget_construction_qc_user,access_budget_construction_qc_user,tk_construction_management.model_budget_construction,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_project_wbs_manager,access_project_wbs_manager,tk_construction_management.model_project_wbs,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_project_wbs_department,access_project_wbs_department,tk_construction_management.model_project_wbs,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_project_wbs_user,access_project_wbs_user,tk_construction_management.model_project_wbs,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_project_wbs_qc_user,access_project_wbs_qc_user,tk_construction_management.model_project_wbs,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
tk_construction_management.access_wbs_entries_manager,access_wbs_entries_manager,tk_construction_management.model_wbs_entries,tk_construction_management.advance_construction_manager,1,1,1,1
|
||||
tk_construction_management.access_wbs_entries_department,access_wbs_entries_department,tk_construction_management.model_wbs_entries,tk_construction_management.advance_construction_department,1,1,1,1
|
||||
tk_construction_management.access_wbs_entries_user,access_wbs_entries_user,tk_construction_management.model_wbs_entries,tk_construction_management.advance_construction_user,1,1,1,1
|
||||
tk_construction_management.access_wbs_entries_qc_user,access_wbs_entries_qc_user,tk_construction_management.model_wbs_entries,tk_construction_management.advance_construction_qc_user,1,0,0,0
|
||||
|
||||
|
104
addons/tk_construction_management/security/security.xml
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!--Project-->
|
||||
<record id="construction_security_rule_project_manager" model="ir.rule">
|
||||
<field name="name">Advance Construction : Administration</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_tk_construction_project"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_manager'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_building_department" model="ir.rule">
|
||||
<field name="name">Construction : Own Department Project</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_tk_construction_project"/>
|
||||
<field name="domain_force">[('department_id','in',user.department_ids.ids)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_department'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_building_user" model="ir.rule">
|
||||
<field name="name">Construction : Own Record Project</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_tk_construction_project"/>
|
||||
<field name="domain_force">[('responsible_id','=',user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_user'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Project Phase-->
|
||||
<record id="construction_security_rule_job_sheet_manager" model="ir.rule">
|
||||
<field name="name">Construction : Administration</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_costing"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_manager'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_job_sheet_department" model="ir.rule">
|
||||
<field name="name">Construction : Own Department Job Sheet</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_costing"/>
|
||||
<field name="domain_force">[('department_id','in',user.department_ids.ids)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_department'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_job_sheet_user" model="ir.rule">
|
||||
<field name="name">Construction : Own Record Job Sheet</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_costing"/>
|
||||
<field name="domain_force">[('responsible_id','=',user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_user'))]"/>
|
||||
</record>
|
||||
|
||||
<!--Job Order-->
|
||||
<record id="construction_security_rule_job_order_manager" model="ir.rule">
|
||||
<field name="name">Construction : Administration</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_order"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_manager'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_job_order_department" model="ir.rule">
|
||||
<field name="name">Construction : Own Department Job Order</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_order"/>
|
||||
<field name="domain_force">[('department_id','in',user.department_ids.ids)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_department'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_job_order_user" model="ir.rule">
|
||||
<field name="name">Construction : Own Record Job Order</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_job_order"/>
|
||||
<field name="domain_force">[('responsible_id','=',user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_user'))]"/>
|
||||
</record>
|
||||
|
||||
<!--Material Requisition-->
|
||||
<record id="construction_security_rule_material_req_manager" model="ir.rule">
|
||||
<field name="name">Construction : Administration</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_material_requisition"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_manager'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_material_req_department" model="ir.rule">
|
||||
<field name="name">Construction : Own Department Material Requisition</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_material_requisition"/>
|
||||
<field name="domain_force">[('department_id','in',user.department_ids.ids)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_department'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_material_req_user" model="ir.rule">
|
||||
<field name="name">Construction : Own Record Material Requisition</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_material_requisition"/>
|
||||
<field name="domain_force">[('responsible_id','=',user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_user'))]"/>
|
||||
</record>
|
||||
|
||||
<!--Internal Transfer-->
|
||||
<record id="construction_security_rule_internal_transfer_manager" model="ir.rule">
|
||||
<field name="name">Construction : Administration</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_internal_transfer"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_manager'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_internal_transfer_department" model="ir.rule">
|
||||
<field name="name">Construction : Own Department Internal Transfer</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_internal_transfer"/>
|
||||
<field name="domain_force">[('department_id','in',user.department_ids.ids)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_department'))]"/>
|
||||
</record>
|
||||
<record id="construction_security_rule_internal_transfer_user" model="ir.rule">
|
||||
<field name="name">Construction : Own Record Internal Transfer</field>
|
||||
<field name="model_id" ref="tk_construction_management.model_internal_transfer"/>
|
||||
<field name="domain_force">[('responsible_id','=',user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('tk_construction_management.advance_construction_user'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
addons/tk_construction_management/static/.DS_Store
vendored
Normal file
BIN
addons/tk_construction_management/static/description/cover.gif
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
addons/tk_construction_management/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
addons/tk_construction_management/static/description/img/ac1.png
Normal file
|
After Width: | Height: | Size: 504 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 629 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 444 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 76 KiB |
BIN
addons/tk_construction_management/static/description/img/ac2.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 313 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 239 KiB |
BIN
addons/tk_construction_management/static/description/img/ac3.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 204 KiB |
BIN
addons/tk_construction_management/static/description/img/ac4.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 167 KiB |