open-synergy/opnsynid-hr

View on GitHub
hr_career_transition/models/hr_career_transition.py

Summary

Maintainability
F
1 wk
Test Coverage
# -*- coding: utf-8 -*-
# Copyright 2018 OpenSynergy Indonesia
# Copyright 2020 PT. Simetri Sinergi Indonesia
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from openerp import _, api, fields, models
from openerp.exceptions import Warning as UserError


class HrCareerTransition(models.Model):
    _name = "hr.career_transition"
    _description = "Career Transition"
    _inherit = [
        "mail.thread",
        "base.sequence_document",
        "base.workflow_policy_object",
        "tier.validation",
    ]
    _state_from = [
        "draft",
        "confirm",
    ]
    _state_to = [
        "open",
    ]
    _order = "effective_date desc, id"

    @api.multi
    @api.depends(
        "type_id",
    )
    def _compute_policy(self):
        _super = super(HrCareerTransition, self)
        _super._compute_policy()

    @api.model
    def _default_company_id(self):
        return self.env.user.company_id

    @api.model
    def _default_type_id(self):
        return False

    @api.model
    def _default_employee_id(self):
        employees = self.env.user.employee_ids
        if len(employees) > 0:
            employee = employees[0]
        return employee.id

    name = fields.Char(
        string="# Document",
        required=True,
        default="/",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    ref = fields.Char(
        string="# Reference",
        required=True,
        default="/",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    company_id = fields.Many2one(
        string="Company",
        comodel_name="res.company",
        default=lambda self: self._default_company_id(),
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    employee_id = fields.Many2one(
        string="Employee",
        comodel_name="hr.employee",
        default=lambda self: self._default_employee_id(),
        ondelete="restrict",
        required=True,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    type_id = fields.Many2one(
        string="Type",
        comodel_name="hr.career_transition_type",
        default=lambda self: self._default_type_id(),
        ondelete="restrict",
        required=True,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    effective_date = fields.Date(
        string="Effective Date",
        required=True,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    archieve = fields.Boolean(
        string="Archieve",
        default=False,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    require_reason = fields.Boolean(
        string="Require Reason",
        related="type_id.require_reason",
        store=False,
        readonly=True,
    )
    reason_id = fields.Many2one(
        string="Reason",
        comodel_name="hr.career_transition_type_reason",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    # Contract
    previous_contract_id = fields.Many2one(
        string="Previous Contract",
        comodel_name="hr.contract",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_contract_id = fields.Many2one(
        string="New Contract",
        comodel_name="hr.contract",
        ondelete="restrict",
        readonly=True,
    )
    create_new_contract = fields.Boolean(
        string="Create New Contract",
        related="type_id.create_new_contract",
        store=False,
        readonly=True,
    )
    need_previous_contract = fields.Boolean(
        string="Need Previous Contract",
        related="type_id.need_previous_contract",
        store=False,
        readonly=True,
    )
    contract_start_date = fields.Date(
        string="Contract Date Start",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    contract_end_date = fields.Date(
        string="Contract Date End",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    contract_type_id = fields.Many2one(
        string="Contract Type",
        comodel_name="hr.contract.type",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    # New Data
    new_company_id = fields.Many2one(
        string="New Company",
        comodel_name="res.company",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_department_id = fields.Many2one(
        string="New Department",
        comodel_name="hr.department",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_manager_id = fields.Many2one(
        string="New Manager",
        comodel_name="hr.employee",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_job_id = fields.Many2one(
        string="New Job Title",
        comodel_name="hr.job",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_working_hour_id = fields.Many2one(
        string="New Working Schedule",
        comodel_name="resource.calendar",
        ondelete="restrict",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    new_wage = fields.Float(
        string="New Wage",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    # Previous Data
    previous_company_id = fields.Many2one(
        string="Previous Company",
        comodel_name="res.company",
    )
    previous_department_id = fields.Many2one(
        string="Previous Department",
        comodel_name="hr.department",
    )
    previous_manager_id = fields.Many2one(
        string="Previous Department",
        comodel_name="hr.employee",
    )
    previous_job_id = fields.Many2one(
        string="Previous Job Title",
        comodel_name="hr.job",
    )
    previous_working_hour_id = fields.Many2one(
        string="Previous Working Schedule",
        comodel_name="resource.calendar",
    )
    previous_wage = fields.Float(
        string="Previous Wage",
    )
    # Data Change Policy
    change_company = fields.Boolean(
        related="type_id.change_company",
        string="Change Company?",
        readonly=True,
    )
    change_job_title = fields.Boolean(
        related="type_id.change_job_title",
        string="Change Job Title",
        readonly=True,
    )
    change_department = fields.Boolean(
        related="type_id.change_department",
        string="Change Department",
        readonly=True,
    )
    change_manager = fields.Boolean(
        related="type_id.change_manager",
        string="Change Manager",
        readonly=True,
    )
    change_working_schedule = fields.Boolean(
        related="type_id.change_working_schedule",
        string="Change Working Schedule",
        readonly=True,
    )
    change_wage = fields.Boolean(
        related="type_id.change_wage",
        string="Change Wage",
        readonly=True,
    )

    note = fields.Text(
        string="Note",
    )
    state = fields.Selection(
        string="State",
        required=True,
        readonly=True,
        track_visibility="onchange",
        selection=[
            ("draft", "Draft"),
            ("confirm", "Waiting for Approval"),
            ("open", "On Progress"),
            ("valid", "Valid"),
            ("cancel", "Cancel"),
        ],
        default="draft",
        copy=False,
    )
    # Log Fields
    confirmed_date = fields.Datetime(
        string="Confirmation Date",
        readonly=True,
        copy=False,
    )
    confirmed_user_id = fields.Many2one(
        string="Confirmation By",
        comodel_name="res.users",
        readonly=True,
        copy=False,
    )
    opened_date = fields.Datetime(
        string="Opened Date",
        readonly=True,
        copy=False,
    )
    opened_user_id = fields.Many2one(
        string="Open By",
        comodel_name="res.users",
        readonly=True,
        copy=False,
    )
    validated_date = fields.Datetime(
        string="Validation Date",
        readonly=True,
        copy=False,
    )
    validated_user_id = fields.Many2one(
        string="Validation By",
        comodel_name="res.users",
        readonly=True,
        copy=False,
    )
    cancelled_date = fields.Datetime(
        string="Cancellation Date",
        readonly=True,
        copy=False,
    )
    cancelled_user_id = fields.Many2one(
        string="Cancellation By",
        comodel_name="res.users",
        readonly=True,
        copy=False,
    )
    # Policy Fields
    confirm_ok = fields.Boolean(
        string="Can Confirm",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    valid_ok = fields.Boolean(
        string="Can Validate",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    cancel_ok = fields.Boolean(
        string="Can Cancel",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    restart_ok = fields.Boolean(
        string="Can Restart",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    restart_approval_ok = fields.Boolean(
        string="Can Restart Approval",
        compute="_compute_policy",
    )

    @api.multi
    def action_confirm(self):
        for transition in self:
            transition.write(transition._prepare_confirm_data())
            transition.request_validation()

    @api.multi
    def action_approve(self):
        for transition in self:
            transition.write(transition._prepare_approve_data())

    @api.multi
    def action_valid(self):
        for transition in self:
            transition.write(transition._prepare_valid_data())

    @api.multi
    def action_cancel(self):
        for transition in self:
            if not transition._check_cant_cancel_latest_transition():
                msg = _("You can only cancel valid latest transition")
                raise UserError(msg)
            new_contract = False
            if transition.new_contract_id:
                new_contract = transition.new_contract_id
            transition.write(transition._prepare_cancel_data())
            if new_contract:
                new_contract.unlink()
            else:
                transition._revert_contract()
            transition.restart_validation()

    @api.multi
    def action_restart(self):
        for transition in self:
            transition.write(transition._prepare_restart_data())

    @api.multi
    def validate_tier(self):
        _super = super(HrCareerTransition, self)
        _super.validate_tier()
        for document in self:
            if document.validated:
                document.action_approve()

    @api.multi
    def restart_validation(self):
        _super = super(HrCareerTransition, self)
        _super.restart_validation()
        for document in self:
            document.request_validation()

    @api.multi
    def _prepare_confirm_data(self):
        self.ensure_one()
        result = {
            "state": "confirm",
            "confirmed_user_id": self.env.user.id,
            "confirmed_date": fields.Datetime.now(),
        }
        return result

    @api.multi
    def _prepare_approve_data(self):
        self.ensure_one()
        result = {
            "state": "open",
            "opened_user_id": self.env.user.id,
            "opened_date": fields.Datetime.now(),
        }
        return result

    @api.multi
    def _prepare_valid_data(self):
        self.ensure_one()
        contract = self._create_update_contract()
        result = {
            "state": "valid",
            "validated_user_id": self.env.user.id,
            "validated_date": fields.Datetime.now(),
            "new_contract_id": contract and contract.id or False,
        }
        return result

    @api.multi
    def _prepare_cancel_data(self):
        self.ensure_one()
        result = {
            "state": "cancel",
            "cancelled_user_id": self.env.user.id,
            "cancelled_date": fields.Datetime.now(),
            "new_contract_id": False,
        }
        return result

    @api.multi
    def _prepare_restart_data(self):
        self.ensure_one()
        result = {
            "state": "draft",
            "confirmed_user_id": False,
            "confirmed_date": False,
            "validated_user_id": False,
            "validated_date": False,
            "cancelled_user_id": False,
            "cancelled_date": False,
        }
        return result

    @api.model
    def create(self, values):
        _super = super(HrCareerTransition, self)
        result = _super.create(values)
        result.write(
            {
                "name": result._create_sequence(),
            }
        )
        return result

    @api.onchange("previous_company_id")
    def onchange_new_company_id(self):
        self.new_company_id = self.previous_company_id

    @api.onchange("previous_department_id")
    def onchange_new_department_id(self):
        self.new_department_id = self.previous_department_id

    @api.onchange("previous_manager_id")
    def onchange_new_manager_id(self):
        self.new_manager_id = self.previous_manager_id

    @api.onchange("previous_job_id")
    def onchange_new_job_id(self):
        self.new_job_id = self.previous_job_id

    @api.onchange("previous_working_hour_id")
    def onchange_new_working_hour_id(self):
        self.new_working_hour_id = self.previous_working_hour_id

    @api.onchange("previous_wage")
    def onchange_new_wage(self):
        self.new_wage = self.previous_wage

    @api.multi
    def onchange_previous_contract(self, previous_contract_id):
        value = self._get_value_before_onchange_previous_contract()
        domain = self._get_domain_before_onchange_previous_contract()

        if previous_contract_id:
            obj_contract = self.env["hr.contract"]
            previous_contract = obj_contract.browse([previous_contract_id])[0]
            value = self._get_value_after_onchange_previous_contract(previous_contract)
            domain = self._get_domain_after_onchange_previous_contract(
                previous_contract
            )
        return {"value": value, "domain": domain}

    @api.multi
    def _get_value_before_onchange_previous_contract(self):
        return {
            "previous_working_hour_id": False,
            "previous_job_id": False,
            "previous_department_id": False,
            "previous_manager_id": False,
            "previous_company_id": False,
            "previous_wage": 0.0,
        }

    @api.multi
    def _get_domain_before_onchange_previous_contract(self):
        return {}

    @api.multi
    def _get_value_after_onchange_previous_contract(self, previous_contract):
        return {
            "previous_working_hour_id": previous_contract.working_hours,
            "previous_job_id": previous_contract.job_id,
            "previous_department_id": previous_contract.contract_department_id,
            "previous_manager_id": previous_contract.manager_id,
            "previous_company_id": previous_contract.company_id,
            "previous_wage": previous_contract.wage,
        }

    @api.multi
    def _get_domain_after_onchange_previous_contract(self, previous_contract):
        return {}

    @api.onchange("employee_id", "archieve")
    def onchange_previous_contract_id(self):
        self.previous_contract_id = False
        if self.employee_id and self.need_previous_contract and not self.archieve:
            self.previous_contract_id = self.employee_id.contract_id

    @api.onchange("type_id")
    def onchange_reason_id(self):
        self.reason_id = False

    @api.multi
    def _prepare_new_contract(self):
        self.ensure_one()
        return {
            "employee_id": self.employee_id.id,
            "job_id": self.new_job_id and self.new_job_id.id or False,
            "type_id": self.contract_type_id.id,
            "date_start": self.contract_start_date,
            "date_end": self.contract_end_date,
            "working_hours": (
                self.new_working_hour_id and self.new_working_hour_id.id or False
            ),
            "wage": self.new_wage,
            "contract_department_id": (
                self.new_department_id and self.new_department_id.id or False
            ),
            "manager_id": (self.new_manager_id and self.new_manager_id.id or False),
            "company_id": (self.new_company_id and self.new_company_id.id or False),
        }

    @api.multi
    def _prepare_contract_update(self):
        self.ensure_one()
        return {
            "job_id": self.new_job_id and self.new_job_id.id or False,
            "working_hours": self.new_working_hour_id
            and self.new_working_hour_id.id
            or False,
            "contract_department_id": self.new_department_id
            and self.new_department_id.id
            or False,
            "company_id": self.new_company_id and self.new_company_id.id or False,
            "manager_id": self.new_manager_id and self.new_manager_id.id or False,
            "wage": self.new_wage,
        }

    @api.multi
    def _prepare_contract_revert(self):
        self.ensure_one()
        return {
            "job_id": self.previous_job_id and self.previous_job_id.id or False,
            "working_hours": self.previous_working_hour_id
            and self.previous_working_hour_id.id
            or False,
            "contract_department_id": self.previous_department_id
            and self.previous_department_id.id
            or False,
            "company_id": self.previous_company_id
            and self.previous_company_id.id
            or False,
            "manager_id": self.previous_manager_id
            and self.previous_manager_id.id
            or False,
            "wage": self.previous_wage,
        }

    @api.multi
    def _revert_contract(self):
        self.ensure_one()
        if self._check_revert_contract():
            self.previous_contract_id.write(self._prepare_contract_revert())

    @api.multi
    def _check_revert_contract(self):
        self.ensure_one()
        result = False
        if (
            self.need_previous_contract
            and self.previous_contract_id
            and not self.create_new_contract
            and not self.archieve
        ):
            result = True
        return result

    @api.multi
    def _create_update_contract(self):
        self.ensure_one()
        obj_contract = self.env["hr.contract"]
        result = False
        if self.create_new_contract and not self.archieve:
            result = obj_contract.create(self._prepare_new_contract())
        elif not self.create_new_contract and not self.archieve:
            self.previous_contract_id.write(self._prepare_contract_update())
        return result

    @api.multi
    def _update_contract(self):
        self.ensure_one()
        self.previous_contract_id.write(self._prepare_contract_update())

    @api.multi
    def unlink(self):
        strWarning = _("You can only delete data on draft state")
        for transition in self:
            if transition.state != "draft":
                if not self.env.context.get("force_unlink", False):
                    raise UserError(strWarning)
        _super = super(HrCareerTransition, self)
        _super.unlink()

    @api.constrains(
        "effective_date",
        "state",
    )
    def _constrain_check_effective_date(self):
        if self.state == "valid" and not self.archieve:
            criteria = [
                ("id", "!=", self.id),
                ("state", "=", "valid"),
                ("employee_id", "=", self.employee_id.id),
            ]
            obj_transition = self.env["hr.career_transition"]
            transitions = obj_transition.search(criteria)
            if len(transitions) > 0:
                last_transition = transitions[0]
                if self.effective_date < last_transition.effective_date:
                    msg = _(
                        "Effective date have to be greater than "
                        "lastest career transition"
                    )
                    raise UserError(msg)

    @api.constrains(
        "state",
    )
    def _check_contract_start_date_no_equal_effective_date(self):
        if self.state == "valid" and self.create_new_contract and not self.archieve:
            if self.contract_start_date != self.effective_date:
                msg = _("Contract start date have to be " "equal to effective date")
                raise UserError(msg)

    @api.constrains(
        "state",
    )
    def _check_contract_end_date_equal_less_start_date(self):
        if (
            self.state == "valid"
            and self.create_new_contract
            and self.contract_end_date
        ):
            if self.contract_end_date <= self.contract_start_date:
                msg = _("Contract date end have to be greater than start date")
                raise UserError(msg)

    @api.constrains(
        "state",
    )
    def _check_transition_limit(self):
        transition_count = self.employee_id._get_transition_count(
            self.type_id,
            self.reason_id,
        )
        transition_limit = self.type_id._get_transition_limit(
            self.reason_id,
        )
        if (
            self.state == "valid"
            and transition_limit != 0
            and transition_count > transition_limit
        ):
            msg = _("Career transition limit exceed")
            raise UserError(msg)

    @api.multi
    def _check_cant_cancel_latest_transition(self):
        self.ensure_one()
        result = True
        if (
            self.id != self.employee_id.latest_career_transition_id.id
            and not self.archieve
            and self.state == "valid"
        ):
            result = False
        return result