190 lines
11 KiB
Python
190 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Кадровое мероприятие для ЕФС-1 подраздел 1.1 (СЗВ-ТД).
|
||
|
||
Одно мероприятие кадрового учёта (приём, перевод, увольнение и т.п.) по
|
||
сотруднику — источник для элемента «Мероприятие» (тип ЕФС:ТипТД) формы
|
||
ЕФС-1 подраздел 1.1 «Сведения о трудовой (иной) деятельности».
|
||
|
||
ЕФС-1 (СФР) — событийная форма: подаётся не за период, а по факту кадрового
|
||
события. При приёме и увольнении — не позднее следующего рабочего дня, при
|
||
прочих мероприятиях — до 25-го числа следующего месяца. Поэтому мероприятие
|
||
регистрируется отдельной записью и затем выгружается в документ ЕФС-1
|
||
(rsbu.efs1.szvtd).
|
||
|
||
Поля и справочники — строго по XSD ЕФС:ТипТД (ТипыЕФС_2024-01-01):
|
||
UUID, Дата, Вид (1..10), Должность, ЯвляетсяСовместителем, ВидПР,
|
||
СтруктурноеПодразделение, КодВФ, КодВФпоОКЗ, ОснованиеУвольнения
|
||
(КодПричиныУвольнения, НормативныйДокумент, Статья, Часть, Пункт, Подпункт,
|
||
Абзац), Причина, Основание (Наименование, Дата, Серия, Номер).
|
||
"""
|
||
import uuid
|
||
|
||
from odoo import api, fields, models
|
||
|
||
# Виды мероприятий (ЕФС:ТипТД/Вид). Значения 1..10 по XSD.
|
||
EVENT_KIND_SELECTION = [
|
||
('1', 'ПРИЕМ'),
|
||
('2', 'ПЕРЕВОД'),
|
||
('3', 'ПЕРЕИМЕНОВАНИЕ'),
|
||
('4', 'УСТАНОВЛЕНИЕ (ПРИСВОЕНИЕ)'),
|
||
('5', 'УВОЛЬНЕНИЕ'),
|
||
('6', 'ЗАПРЕТ ЗАНИМАТЬ ДОЛЖНОСТЬ'),
|
||
('7', 'ПРИОСТАНОВЛЕНИЕ'),
|
||
('8', 'ВОЗОБНОВЛЕНИЕ'),
|
||
('9', 'НАЧАЛО ДОГОВОРА ГПХ'),
|
||
('10', 'ОКОНЧАНИЕ ДОГОВОРА ГПХ'),
|
||
]
|
||
|
||
# Виды мероприятий, для которых КодВФпоОКЗ обязателен (по описанию XSD).
|
||
KINDS_REQUIRE_OKZ = {'1', '2', '4', '5', '7', '8', '9', '10'}
|
||
|
||
# Признак совместительства / срочности (ЕФС:ТипТД/ЯвляетсяСовместителем).
|
||
EMPLOYMENT_TYPE_SELECTION = [
|
||
('0', 'Бессрочный трудовой договор'),
|
||
('0.1', 'Срочный ТД до 6 месяцев'),
|
||
('0.2', 'Срочный ТД более 6 месяцев'),
|
||
('1', 'Совместительство'),
|
||
('1.1', 'Совместительство, срочный до 6 месяцев'),
|
||
('1.2', 'Совместительство, срочный более 6 месяцев'),
|
||
]
|
||
|
||
|
||
class RsbuHrEvent(models.Model):
|
||
_name = 'rsbu.hr.event'
|
||
_description = 'РСБУ — Кадровое мероприятие (ЕФС-1, СЗВ-ТД)'
|
||
_order = 'event_date desc, id desc'
|
||
_check_company_auto = True
|
||
|
||
name = fields.Char(string='Наименование', compute='_compute_name',
|
||
store=True)
|
||
company_id = fields.Many2one(
|
||
'res.company', string='Компания', required=True, index=True,
|
||
default=lambda self: self.env.company)
|
||
employee_id = fields.Many2one(
|
||
'hr.employee', string='Сотрудник', required=True, index=True,
|
||
check_company=True)
|
||
event_uuid = fields.Char(
|
||
string='UUID мероприятия', required=True, copy=False, index=True,
|
||
default=lambda self: str(uuid.uuid4()),
|
||
help='Уникальный идентификатор мероприятия (элемент UUID). '
|
||
'Используется СФР для сопоставления и отмены мероприятий.')
|
||
event_date = fields.Date(
|
||
string='Дата мероприятия', required=True,
|
||
default=fields.Date.context_today,
|
||
help='Дата проведения кадрового мероприятия (приёма, увольнения '
|
||
'и т.п.).')
|
||
kind = fields.Selection(
|
||
selection=EVENT_KIND_SELECTION, string='Вид мероприятия',
|
||
required=True, default='1')
|
||
# Сведения о занятости.
|
||
job_title = fields.Char(
|
||
string='Должность',
|
||
help='Должность, профессия, специальность, квалификация (элемент '
|
||
'Должность).')
|
||
employment_type = fields.Selection(
|
||
selection=EMPLOYMENT_TYPE_SELECTION, string='Характер работы',
|
||
default='0',
|
||
help='Признак совместительства/срочности трудового договора '
|
||
'(элемент ЯвляетсяСовместителем).')
|
||
work_kind = fields.Char(
|
||
string='Вид поручаемой работы',
|
||
help='Конкретный вид поручаемой работы (элемент ВидПР).')
|
||
department_name = fields.Char(
|
||
string='Структурное подразделение',
|
||
help='Структурное подразделение (элемент СтруктурноеПодразделение).')
|
||
func_code = fields.Char(
|
||
string='Код выполняемой функции',
|
||
help='Код выполняемой функции (элемент КодВФ): особый код трудовой '
|
||
'функции (ДИСТ, ДГПХ и т.п.) либо код по профстандарту.')
|
||
okz_code = fields.Char(
|
||
string='Код по ОКЗ',
|
||
help='Кодовое обозначение занятия по ОКЗ, формат NNNN.N (элемент '
|
||
'КодВФпоОКЗ). По умолчанию берётся из карточки сотрудника.')
|
||
# Документ-основание (приказ).
|
||
basis_name = fields.Char(
|
||
string='Документ-основание', default='Приказ',
|
||
help='Наименование документа-основания (элемент Основание/'
|
||
'Наименование), напр. «Приказ».')
|
||
basis_date = fields.Date(
|
||
string='Дата основания',
|
||
help='Дата документа-основания (элемент Основание/Дата).')
|
||
basis_series = fields.Char(
|
||
string='Серия основания',
|
||
help='Серия документа-основания (элемент Основание/Серия).')
|
||
basis_number = fields.Char(
|
||
string='Номер основания',
|
||
help='Номер документа-основания (элемент Основание/Номер).')
|
||
# Основание увольнения (только для вида «УВОЛЬНЕНИЕ»).
|
||
dismissal_reason_code = fields.Char(
|
||
string='Код причины увольнения',
|
||
help='Код причины увольнения (элемент ОснованиеУвольнения/'
|
||
'КодПричиныУвольнения), напр. «п3ч1с77тк».')
|
||
dismissal_document = fields.Char(
|
||
string='Нормативный документ', default='Трудовой кодекс Российской Федерации',
|
||
help='Наименование нормативного документа (элемент '
|
||
'ОснованиеУвольнения/НормативныйДокумент).')
|
||
dismissal_article = fields.Char(
|
||
string='Статья',
|
||
help='Номер статьи нормативного акта (элемент ОснованиеУвольнения/'
|
||
'Статья).')
|
||
dismissal_part = fields.Char(
|
||
string='Часть',
|
||
help='Часть статьи (элемент ОснованиеУвольнения/Часть).')
|
||
dismissal_point = fields.Char(
|
||
string='Пункт',
|
||
help='Пункт статьи (элемент ОснованиеУвольнения/Пункт).')
|
||
dismissal_subpoint = fields.Char(
|
||
string='Подпункт',
|
||
help='Подпункт статьи (элемент ОснованиеУвольнения/Подпункт).')
|
||
dismissal_paragraph = fields.Char(
|
||
string='Абзац',
|
||
help='Абзац статьи (элемент ОснованиеУвольнения/Абзац).')
|
||
dismissal_reason = fields.Char(
|
||
string='Причина увольнения',
|
||
help='Текстовая формулировка причины увольнения (элемент Причина).')
|
||
|
||
state = fields.Selection(
|
||
selection=[('draft', 'Черновик'), ('reported', 'Подано')],
|
||
default='draft', string='Состояние', copy=False)
|
||
|
||
@api.depends('employee_id', 'kind', 'event_date')
|
||
def _compute_name(self):
|
||
labels = dict(EVENT_KIND_SELECTION)
|
||
for rec in self:
|
||
label = labels.get(rec.kind, '?')
|
||
emp = rec.employee_id.name or '?'
|
||
dt = rec.event_date and rec.event_date.strftime('%d.%m.%Y') or '?'
|
||
rec.name = '%s — %s (%s)' % (label, emp, dt)
|
||
|
||
@api.onchange('employee_id')
|
||
def _onchange_employee_id(self):
|
||
"""Подставить должность/подразделение/ОКЗ из карточки сотрудника."""
|
||
for rec in self:
|
||
emp = rec.employee_id
|
||
if not emp:
|
||
continue
|
||
if not rec.job_title:
|
||
rec.job_title = emp.job_title or (
|
||
emp.job_id.name if emp.job_id else False)
|
||
if not rec.department_name:
|
||
rec.department_name = (
|
||
emp.department_id.name if emp.department_id else False)
|
||
if not rec.okz_code:
|
||
rec.okz_code = emp.rsbu_okz_code or False
|
||
|
||
@api.onchange('kind')
|
||
def _onchange_kind(self):
|
||
"""Подставить дату основания из даты мероприятия, если пусто."""
|
||
for rec in self:
|
||
if not rec.basis_date and rec.event_date:
|
||
rec.basis_date = rec.event_date
|
||
|
||
def _okz_required(self):
|
||
self.ensure_one()
|
||
return self.kind in KINDS_REQUIRE_OKZ
|
||
|
||
def _effective_okz(self):
|
||
"""Код ОКЗ мероприятия (из записи или карточки сотрудника)."""
|
||
self.ensure_one()
|
||
return self.okz_code or self.employee_id.rsbu_okz_code or ''
|