Files
public/ruodoo_rsbu_mil/models/ruodoo_rsbu_mil_report.py

196 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""Отчёты по воинскому учёту: формы 6, 18, 19.
Собирают численности из карточек сотрудников (hr.employee, поля
воинского учёта) и выгружаются в DOCX через docx_report.
* Форма 6 — отчёт о численности работающих и забронированных граждан,
пребывающих в запасе (строки по категориям должности).
* Форма 18 — карточка учёта организации (реквизиты + сводные числа).
* Форма 19 — сведения об обеспеченности трудовыми ресурсами (строки по
категориям должности с потребностью/некомплектом).
Это ОБРАЗЕЦ: методика подсчёта упрощена и уточняется у специалиста ВУ.
"""
from odoo import api, fields, models
from odoo.exceptions import UserError
from .hr_employee import MIL_JOB_CATEGORY
REPORT_KIND = [
('form6', 'Форма 6 — численность работающих и забронированных в запасе'),
('form18', 'Форма 18 — карточка учёта организации'),
('form19', 'Форма 19 — обеспеченность трудовыми ресурсами'),
]
# Шаблоны docx_report (xmlid docx.template → по виду формы).
_REPORT_XMLID = {
'form6': 'ruodoo_rsbu_mil.docx_template_mil_form6',
'form18': 'ruodoo_rsbu_mil.docx_template_mil_form18',
'form19': 'ruodoo_rsbu_mil.docx_template_mil_form19',
}
class RuodooRsbuMilReport(models.Model):
_name = 'ruodoo.rsbu.mil.report'
_description = 'Отчёт по воинскому учёту'
_order = 'report_date desc, id desc'
name = fields.Char(string='Наименование', compute='_compute_name',
store=True)
kind = fields.Selection(
selection=REPORT_KIND, string='Форма', required=True, default='form6')
company_id = fields.Many2one(
'res.company', string='Организация', required=True, index=True,
default=lambda self: self.env.company)
report_date = fields.Date(
string='По состоянию на', required=True,
default=fields.Date.context_today,
help='Дата, по состоянию на которую формируется отчёт.')
year = fields.Integer(
string='Отчётный год', default=lambda self: fields.Date.today().year)
state = fields.Selection(
selection=[('draft', 'Черновик'), ('computed', 'Рассчитано')],
default='draft', string='Состояние', copy=False)
line_ids = fields.One2many(
'ruodoo.rsbu.mil.report.line', 'report_id', string='Строки')
# --- Сводные числа (используются формой 18 и итогами). ---
total_employees = fields.Integer(
string='Всего работающих', compute='_compute_totals', store=True)
total_reserve = fields.Integer(
string='В запасе, всего', compute='_compute_totals', store=True)
total_reserve_officer = fields.Integer(
string='В запасе, офицеры', compute='_compute_totals', store=True)
total_reserve_soldier = fields.Integer(
string='В запасе, прочие', compute='_compute_totals', store=True)
total_booked = fields.Integer(
string='Забронировано, всего', compute='_compute_totals', store=True)
total_booked_officer = fields.Integer(
string='Забронировано, офицеры', compute='_compute_totals',
store=True)
total_booked_soldier = fields.Integer(
string='Забронировано, прочие', compute='_compute_totals', store=True)
total_conscript = fields.Integer(
string='Подлежат призыву', compute='_compute_totals', store=True)
total_limited_fit = fields.Integer(
string='Ограниченно годных', compute='_compute_totals', store=True)
total_no_order = fields.Integer(
string='В запасе без мобпредписаний', compute='_compute_totals',
store=True)
total_with_order = fields.Integer(
string='В запасе с мобпредписаниями', compute='_compute_totals',
store=True)
@api.depends('kind', 'report_date', 'year')
def _compute_name(self):
labels = dict(REPORT_KIND)
for rec in self:
short = (labels.get(rec.kind, '?') or '').split('—')[0].strip()
dt = rec.report_date and rec.report_date.strftime('%d.%m.%Y') or '?'
rec.name = '%s на %s' % (short, dt)
# ------------------------------------------------------------------
def _mil_employees(self):
"""Военнообязанные сотрудники организации."""
self.ensure_one()
return self.env['hr.employee'].search([
('company_id', '=', self.company_id.id),
('rsbu_mil_liable', '=', True),
])
@api.depends('company_id')
def _compute_totals(self):
for rec in self:
emps = rec._mil_employees()
reserve = emps.filtered('rsbu_mil_in_reserve')
booked = reserve.filtered('rsbu_mil_booked')
rec.total_employees = len(emps)
rec.total_reserve = len(reserve)
rec.total_reserve_officer = len(reserve.filtered(
lambda e: e.rsbu_mil_rank_group == 'officer'))
rec.total_reserve_soldier = len(reserve.filtered(
lambda e: e.rsbu_mil_rank_group == 'soldier'))
rec.total_booked = len(booked)
rec.total_booked_officer = len(booked.filtered(
lambda e: e.rsbu_mil_rank_group == 'officer'))
rec.total_booked_soldier = len(booked.filtered(
lambda e: e.rsbu_mil_rank_group == 'soldier'))
rec.total_conscript = len(emps.filtered('rsbu_mil_conscript'))
rec.total_limited_fit = len(reserve.filtered('rsbu_mil_limited_fit'))
not_booked = reserve.filtered(lambda e: not e.rsbu_mil_booked)
rec.total_no_order = len(not_booked.filtered(
lambda e: not e.rsbu_mil_mobilization_order))
rec.total_with_order = len(not_booked.filtered(
'rsbu_mil_mobilization_order'))
# ------------------------------------------------------------------
def action_compute(self):
"""Рассчитать строки отчёта по категориям должности."""
Line = self.env['ruodoo.rsbu.mil.report.line']
for rec in self:
rec.line_ids.unlink()
emps = rec._mil_employees()
vals = []
for code, label in MIL_JOB_CATEGORY:
cat = emps.filtered(lambda e: e.rsbu_mil_job_category == code)
reserve = cat.filtered('rsbu_mil_in_reserve')
booked = reserve.filtered('rsbu_mil_booked')
conscript = cat.filtered('rsbu_mil_conscript')
vals.append({
'report_id': rec.id,
'category': code,
'name': label,
'total': len(cat),
'reserve_total': len(reserve),
'reserve_officer': len(reserve.filtered(
lambda e: e.rsbu_mil_rank_group == 'officer')),
'reserve_soldier': len(reserve.filtered(
lambda e: e.rsbu_mil_rank_group == 'soldier')),
'booked_total': len(booked),
'booked_officer': len(booked.filtered(
lambda e: e.rsbu_mil_rank_group == 'officer')),
'booked_soldier': len(booked.filtered(
lambda e: e.rsbu_mil_rank_group == 'soldier')),
'conscript_total': len(conscript),
})
if vals:
Line.create(vals)
rec.state = 'computed'
return True
def action_print_docx(self):
"""Сформировать DOCX отчёта по шаблону, соответствующему форме."""
self.ensure_one()
if self.state == 'draft':
self.action_compute()
xmlid = _REPORT_XMLID.get(self.kind)
template = self.env.ref(xmlid, raise_if_not_found=False) if xmlid else None
if not template:
raise UserError(
'Не найден шаблон DOCX для выбранной формы воинского учёта.')
return template.report_id.report_action(self)
class RuodooRsbuMilReportLine(models.Model):
_name = 'ruodoo.rsbu.mil.report.line'
_description = 'Строка отчёта по воинскому учёту'
_order = 'report_id, sequence, id'
report_id = fields.Many2one(
'ruodoo.rsbu.mil.report', string='Отчёт', required=True,
ondelete='cascade', index=True)
sequence = fields.Integer(string='Порядок', default=10)
category = fields.Selection(
selection=MIL_JOB_CATEGORY, string='Категория должности')
name = fields.Char(string='Наименование категории')
total = fields.Integer(string='Всего работающих')
reserve_total = fields.Integer(string='В запасе, всего')
reserve_officer = fields.Integer(string='В запасе, офицеры')
reserve_soldier = fields.Integer(string='В запасе, прочие')
booked_total = fields.Integer(string='Забронировано, всего')
booked_officer = fields.Integer(string='Забронировано, офицеры')
booked_soldier = fields.Integer(string='Забронировано, прочие')
conscript_total = fields.Integer(string='Подлежат призыву')
note = fields.Char(string='Примечание')