# -*- coding: utf-8 -*- """Документ ЕФС-1 подраздел 1.1 (СЗВ-ТД) — сведения о трудовой деятельности. Единая форма сведений ЕФС-1 (СФР), подраздел 1.1 «Сведения о трудовой (иной) деятельности зарегистрированного лица». Формат 2024-01-01 (пространство имён http://пф.рф/ЕФС-1/2024-01-01). Подаётся в Социальный фонд России по факту кадровых мероприятий (приём, увольнение, перевод и т.п.). Документ группирует кадровые мероприятия (rsbu.hr.event) по зарегистрированным лицам: один документ → блок СЗВ с несколькими ЗЛ, у каждого ЗЛ — свои мероприятия. Автозаполнение собирает ещё не поданные мероприятия за отчётный период. XML-выгрузка и XSD-валидация — в rsbu_efs1_szvtd_xml (через xml.etree.ElementTree + xmlschema, т.к. имена элементов и namespace СФР — кириллические). Реквизиты страхователя (рег. номер СФР, наименование, ИНН/КПП, ОКВЭД, ОГРН) и руководителя берутся из настроек компании (rsbu_reporting_base). """ from odoo import api, fields, models class RsbuEfs1Szvtd(models.Model): _name = 'rsbu.efs1.szvtd' _description = 'РСБУ — ЕФС-1 подраздел 1.1 (СЗВ-ТД)' _order = 'date_fill 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) date_from = fields.Date( string='Период с', required=True, default=lambda self: fields.Date.today().replace(day=1), help='Начало периода отбора кадровых мероприятий для автозаполнения.') date_to = fields.Date( string='Период по', required=True, default=fields.Date.context_today, help='Конец периода отбора кадровых мероприятий для автозаполнения.') date_fill = fields.Date( string='Дата заполнения', required=True, default=fields.Date.context_today, help='Дата заполнения сведений (элемент ДатаЗаполнения).') state = fields.Selection( selection=[('draft', 'Черновик'), ('computed', 'Сформировано'), ('reported', 'Подано')], default='draft', string='Состояние', copy=False) event_ids = fields.One2many( 'rsbu.hr.event', 'efs1_id', string='Кадровые мероприятия') event_count = fields.Integer( string='Мероприятий', compute='_compute_counts') employee_count = fields.Integer( string='Сотрудников', compute='_compute_counts') @api.depends('date_from', 'date_to') def _compute_name(self): for rec in self: df = rec.date_from and rec.date_from.strftime('%d.%m.%Y') or '?' dt = rec.date_to and rec.date_to.strftime('%d.%m.%Y') or '?' rec.name = 'ЕФС-1 (СЗВ-ТД): %s — %s' % (df, dt) @api.depends('event_ids', 'event_ids.employee_id') def _compute_counts(self): for rec in self: rec.event_count = len(rec.event_ids) rec.employee_count = len(rec.event_ids.employee_id) # ------------------------------------------------------------------ def action_compute(self): """Автозаполнить документ ещё не поданными мероприятиями за период.""" Event = self.env['rsbu.hr.event'] for rec in self: # Отвязать прежний набор (не удаляя сами мероприятия). rec.event_ids.write({'efs1_id': False}) events = Event.search([ ('company_id', '=', rec.company_id.id), ('event_date', '>=', rec.date_from), ('event_date', '<=', rec.date_to), ('state', '=', 'draft'), ('efs1_id', '=', False), ]) events.write({'efs1_id': rec.id}) rec.state = 'computed' return True def _events_by_employee(self): """Сгруппировать мероприятия документа по сотруднику (ЗЛ). Возвращает список пар (employee, events) в стабильном порядке (по ФИО сотрудника), мероприятия внутри — по дате. """ self.ensure_one() by_emp = {} for ev in self.event_ids: by_emp.setdefault(ev.employee_id, self.env['rsbu.hr.event']) by_emp[ev.employee_id] |= ev pairs = [] for emp in sorted(by_emp, key=lambda e: (e.name or '')): events = by_emp[emp].sorted(lambda e: (e.event_date, e.id)) pairs.append((emp, events)) return pairs