diff --git a/.travis.yml b/.travis.yml index 99cfdd4fa..a1bece4b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,12 @@ virtualenv: install: - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} + - pip install -U pip wheel - pip install unidecode - - pip install https://github.com/aricaldeira/pybrasil/archive/master.zip + - pip install python-dateutil + - pip install pytz + - pip install pyparsing + - pip install git+https://github.com/aricaldeira/pybrasil.git - travis_install_nightly script: diff --git a/l10n_br_hr_holiday/i18n/pt_BR.po b/l10n_br_hr_holiday/i18n/pt_BR.po index ed5c7ff26..830e0cb3b 100644 --- a/l10n_br_hr_holiday/i18n/pt_BR.po +++ b/l10n_br_hr_holiday/i18n/pt_BR.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-02 17:30+0000\n" -"PO-Revision-Date: 2017-01-02 17:30+0000\n" +"POT-Creation-Date: 2017-02-02 21:44+0000\n" +"PO-Revision-Date: 2017-02-02 21:44+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -56,10 +56,10 @@ msgid "Alistamento Militar" msgstr "Alistamento Militar" #. module: l10n_br_hr_holiday -#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:33 +#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:49 #, python-format msgid "Atestado Obrigatório!" -msgstr "Atestado Obrigatório!" +msgstr "Favor anexar documento comprobatório" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_blood_donation @@ -109,7 +109,13 @@ msgstr "Disposicao TRE" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_end_of_year_recess msgid "End of year recess" -msgstr "Recesso de final de ano" +msgstr "Recesso" + +#. module: l10n_br_hr_holiday +#: selection:hr.holidays,tipo:0 +#: selection:hr.holidays.status,tipo:0 +msgid "Férias" +msgstr "Férias" #. module: l10n_br_hr_holiday #: field:hr.holidays,attachment_ids:0 @@ -124,7 +130,7 @@ msgstr "Folga" #. module: l10n_br_hr_holiday #: model:ir.model,name:l10n_br_hr_holiday.model_hr_holidays_status msgid "Leave Type" -msgstr "Tipo de falta" +msgstr "Tipo de evento" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_Legal_adoption @@ -144,7 +150,7 @@ msgstr "Limite de Horas" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_marriage msgid "Marriage" -msgstr "Casamento" +msgstr "Licença Casamento" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_maternity_leave @@ -169,23 +175,52 @@ msgid "Need attachment" msgstr "Comprovante obrigatório" #. module: l10n_br_hr_holiday -#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:42 -#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:46 +#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:58 +#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:62 #, python-format msgid "Number of days exceeded!" msgstr "Quantidade de dias excedido!" #. module: l10n_br_hr_holiday -#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:52 +#: code:addons/l10n_br_hr_holiday/models/hr_holidays.py:68 #, python-format msgid "Number of hours exceeded!" msgstr "Quantidade de horas excedidas!" +#. module: l10n_br_hr_holiday +#: model:ir.ui.menu,name:l10n_br_hr_holiday.menu_ocorrencias +msgid "Ocorrencias" +msgstr "Evento" + +#. module: l10n_br_hr_holiday +#: selection:hr.holidays,tipo:0 +#: selection:hr.holidays.status,tipo:0 +#: model:ir.actions.act_window,name:l10n_br_hr_holiday.l10n_br_hr_holiday_action_ocorrencias +msgid "Ocorrências" +msgstr "Evento" + #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_paternity_leave msgid "Paternity leave" msgstr "Licença paternidade" +#. module: l10n_br_hr_holiday +#: field:hr.holidays,payroll_discount:0 +#: field:hr.holidays.status,payroll_discount:0 +msgid "Payroll Discount" +msgstr "Payroll Discount" + +#. module: l10n_br_hr_holiday +#: model:ir.model,name:l10n_br_hr_holiday.model_resource_calendar +msgid "Resource Calendar" +msgstr "Calendário de Recursos" + +#. module: l10n_br_hr_holiday +#: field:hr.holidays,tipo:0 +#: field:hr.holidays.status,tipo:0 +msgid "Tipo" +msgstr "Tipo" + #. module: l10n_br_hr_holiday #: field:hr.holidays.status,type_day:0 msgid "Tipo de Dia" @@ -194,4 +229,4 @@ msgstr "Tipo de Dia" #. module: l10n_br_hr_holiday #: model:hr.holidays.status,name:l10n_br_hr_holiday.holiday_status_yearly_leave_days msgid "Yearly Leave Days" -msgstr "Abono Pecuniário" +msgstr "Abono" diff --git a/l10n_br_hr_holiday/views/hr_holidays.xml b/l10n_br_hr_holiday/views/hr_holidays.xml index 2eccb3b0b..9fbbf7637 100644 --- a/l10n_br_hr_holiday/views/hr_holidays.xml +++ b/l10n_br_hr_holiday/views/hr_holidays.xml @@ -13,6 +13,9 @@ + + +

@@ -27,7 +30,7 @@ id="l10n_br_hr_holiday_action_ocorrencias" name="Ocorrências" res_model="hr.holidays" - view_mode="calendar" + view_mode="calendar,form" context="{'default_tipo':'ocorrencias', 'default_type': 'remove', 'search_default_my_leaves':1 }"/> @@ -39,5 +42,9 @@ groups="base.group_hr_manager" sequence="6"/> + + Evento + + diff --git a/l10n_br_hr_payroll/__openerp__.py b/l10n_br_hr_payroll/__openerp__.py index ecb68bbea..8a8ea9293 100644 --- a/l10n_br_hr_payroll/__openerp__.py +++ b/l10n_br_hr_payroll/__openerp__.py @@ -16,19 +16,22 @@ 'l10n_br_hr_contract', 'hr_payroll', ], + 'external_dependencies': { + 'python': ['pybrasil'], + }, 'data': [ 'data/l10n_br_hr_income_tax.xml', 'data/l10n_br_hr_income_tax_deductable_amount_family.xml', - 'views/hr_contract.xml', - 'security/l10n_br_hr_contract.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract_cargo_atividade.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract_filiacao_sindical.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract_jornada.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract_lotacao_local.xml', - 'views/l10n_br_hr_contract/l10n_br_hr_contract_remuneracao.xml', 'data/l10n_br_hr_payroll_data_rubricas.xml', 'data/l10n_br_hr_payroll_data_tabela_INSS.xml', + 'views/hr_contract.xml', + 'security/l10n_br_hr_contract.xml', + 'views/l10n_br_hr_contract.xml', + 'views/l10n_br_hr_contract_cargo_atividade.xml', + 'views/l10n_br_hr_contract_filiacao_sindical.xml', + 'views/l10n_br_hr_contract_jornada.xml', + 'views/l10n_br_hr_contract_lotacao_local.xml', + 'views/l10n_br_hr_contract_remuneracao.xml', 'security/ir.model.access.csv', 'views/l10n_br_hr_child_benefit_view.xml', 'views/l10n_br_hr_income_tax_view.xml', @@ -38,6 +41,11 @@ 'views/l10n_br_hr_social_security_tax_view.xml', 'views/hr_payslip.xml', 'views/hr_salary_rule.xml', + 'views/hr_payroll_structure.xml', + 'views/hr_payslip_run.xml', + ], + 'demo': [ + 'demo/hr_contract.xml', ], 'installable': True, 'auto_install': False, diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml b/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml index e4b11a8ae..b8ee2d05e 100644 --- a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml +++ b/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml @@ -5,6 +5,40 @@ + + 2016 + 0 + 0 + 0 + + + + 2016 + 1903.99 + 7.5 + 142.80 + + + + 2016 + 2826.66 + 15.00 + 354.80 + + + + 2016 + 3751.06 + 22.50 + 636.13 + + + + 2016 + 4664.68 + 27.5 + 869.36 + 2017 diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_rubricas.xml b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_rubricas.xml index b2e804805..679bd0aea 100644 --- a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_rubricas.xml +++ b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_rubricas.xml @@ -38,6 +38,14 @@ FGTS FGTS + + SEFIP + SEFIP + + + FERIAS + FERIAS + @@ -131,29 +139,121 @@ + + + INSS_EMPRESA_BASE + INSS Empresa Base + + code + result=BASE_INSS + if 'BASE_INSS_13' in locals(): + result+=BASE_INSS_13 + + + + + INSS_DEDUCAO_PREVIDENCIARIA + INSS Dedução Previdenciária + + code + result=0 + if 'SALARIO_FAMILIA' in locals(): + result+=SALARIO_FAMILIA + if 'LICENCA_MATERNIDADE' in locals(): + result+=LICENCA_MATERNIDADE + + + + + INSS_EMPRESA + INSS Empresa + + code + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.total_rate + + + + + INSS_OUTRAS_ENTIDADES + INSS Outras Entidades + + code + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.other_entities_rate + + + + + INSS_RAT_FAP + INSS RAT/FAP + + code + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate + + + + + INSS_EMPRESA_TOTAL + INSS Empresa Total + + code + result=INSS_EMPRESA + if 'INSS_RAT_FAP' in locals(): + result+=INSS_RAT_FAP + if 'INSS_OUTRAS_ENTIDADES' in locals(): + result+=INSS_OUTRAS_ENTIDADES + + + + + INSS_EMPRESA_LIQUIDO + INSS Empresa Líquido + + code + result=INSS_EMPRESA_TOTAL-INSS_DEDUCAO_PREVIDENCIARIA + + + + + + BASE_FERIAS + Base FERIAS + + code + none + result = MEDIAS.SALARIO.media/12.0 +result_qty = MEDIAS.SALARIO.meses + + + + + FERIAS - Rubrica de FERIAS - + FERIAS + code - python - result = worked_days.FERIAS.number_of_days > 0 - result = worked_days.FERIAS.number_of_days * contract.wage / 30 - + none + result = BASE_FERIAS / worked_days.SALDO_FERIAS.number_of_days +result_qty = worked_days.FERIAS.number_of_days + + 1/3_FERIAS - Rubrica de 1/3 FERIAS - + 1/3 FERIAS + code - python - result = worked_days.FERIAS.number_of_days > 0 - result = FERIAS / 3 - + none + result = FERIAS / 3.0 + + @@ -191,7 +291,10 @@ code none - result = CALCULAR.get_specific_rubric_value(rubrica.id) + if MEDIAS: + result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) +else: + result = CALCULAR.get_specific_rubric_value(rubrica.id) @@ -201,7 +304,46 @@ code none - result = worked_days.DIAS_BASE.number_of_days * contract.wage/30 + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + + + + + + + + SALARIO + Honorario Presidente + + code + none + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + + + + + + + + SALARIO + Honorario Diretoria + + code + none + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + + + + + + + + SALARIO + Honorario Conselho + + code + none + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 @@ -235,18 +377,21 @@ code none - result = BASE_FGTS + result = BASE_FGTS/2 - REMBOLSO_SAUDE Reembolso Plano de Saude code none - result = CALCULAR.get_specific_rubric_value(rubrica.id) + if MEDIAS: + result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) +else: + result = CALCULAR.get_specific_rubric_value(rubrica.id) + @@ -254,10 +399,10 @@ ADIANTAMENTO_13 Adiantamento 13 Salario - percentage - contract.wage - 1.0 - 50.00 + code + none + result = CALCULAR.BUSCAR_PRIMEIRA_PARCELA() + @@ -266,10 +411,11 @@ PRIMEIRA_PARCELA_13 Primeira parcela 13 Salario - percentage - contract.wage - 1.0 - 50.00 + code + none + result = MEDIAS.SALARIO.media/12.0 +result_qty = MEDIAS.SALARIO.meses + @@ -278,22 +424,25 @@ SEGUNDA_PARCELA_13 Segunda parcela 13 Salário - percentage - contract.wage - 1.0 - 50.00 + code + none + result = MEDIAS.SALARIO.media/12.0 +result_qty = MEDIAS.SALARIO.meses + + + FERIAS - Estrutura de Salario de Férias - + Férias + @@ -338,6 +487,30 @@ + + HONORARIO_PRESIDENTE + Honorario Presidente + + + + + + + HONORARIO_DIRETORIA + Honorario Diretoria + + + + + + + HONORARIO_CONSELHO + Honorario Conselho + + + + + PRIMEIRA_PARCELA_13 Primeira Parcela 13 Salário diff --git a/l10n_br_hr_payroll/demo/hr_contract.xml b/l10n_br_hr_payroll/demo/hr_contract.xml new file mode 100644 index 000000000..9b907a7a2 --- /dev/null +++ b/l10n_br_hr_payroll/demo/hr_contract.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + 2017 + 1.5 + 1.5 + 3.0 + 6.0 + + + + + 2016 + 1.5 + 1.5 + 3.0 + 6.0 + + + + + + + + + Tony Stark + user + +24242424 + + + + + + Contract For Tony Stark + + + + + + This is Tony Stark's contract + + + + + + + + 2017 + + + + + + + + + + + + Bruce Banner + user + +71717171 + + + + + + + + 1.0 + 100 + 400.00 + + + + + Contract For Bruce Banner + + + + + This is Bruce Banner's contract + + + + + + + + + 2017 + Holerite_01 + + + + + + + + Dias Base + 1 + DIAS_BASE + 0.0 + 30 + + + + + + diff --git a/l10n_br_hr_payroll/models/__init__.py b/l10n_br_hr_payroll/models/__init__.py index 481cf73a1..40421af62 100644 --- a/l10n_br_hr_payroll/models/__init__.py +++ b/l10n_br_hr_payroll/models/__init__.py @@ -3,6 +3,7 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import hr_payslip +from . import hr_payslip_line from . import l10n_br_hr_child_benefit from . import l10n_br_hr_income_tax from . import l10n_br_hr_income_tax_deductable_amount_family @@ -15,3 +16,6 @@ from . import hr_contract from . import hr_contract_salary_rule from . import hr_employee +from . import hr_payroll_structure +from . import l10n_br_hr_medias +from . import hr_payslip_run diff --git a/l10n_br_hr_payroll/models/hr_contract.py b/l10n_br_hr_payroll/models/hr_contract.py index a9dc5f68c..31851350e 100644 --- a/l10n_br_hr_payroll/models/hr_contract.py +++ b/l10n_br_hr_payroll/models/hr_contract.py @@ -3,28 +3,87 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from openerp import api, fields, models +from datetime import datetime class HrContract(models.Model): _inherit = 'hr.contract' + _rec_name = 'nome_contrato' + + @api.depends('employee_id', 'date_start') + def _nome_contrato(self): + for contrato in self: + nome = contrato.employee_id.name + inicio_contrato = contrato.date_start + fim_contrato = contrato.date_end + + if inicio_contrato: + inicio_contrato = datetime.strptime(inicio_contrato, + '%Y-%m-%d') + inicio_contrato = inicio_contrato.strftime('%d/%m/%y') + + if fim_contrato: + fim_contrato = datetime.strptime(fim_contrato, '%Y-%m-%d') + fim_contrato = fim_contrato.strftime('%d/%m/%y') + if fim_contrato > fields.Date.today(): + fim_contrato = "- D. %s" % fim_contrato + else: + fim_contrato = "- %s" % fim_contrato + else: + fim_contrato = '' + matricula = contrato.name + nome_contrato = '[%s] %s - %s %s' % (matricula, + nome, inicio_contrato, + fim_contrato) + contrato.nome_contrato = nome_contrato + + nome_contrato = fields.Char(default="[mat] nome - inicio - fim", + compute="_nome_contrato", store=True) + + @api.multi + def _buscar_salario_vigente_periodo(self, data_inicio, data_fim): + contract_change_obj = self.env['l10n_br_hr.contract.change'] + change = contract_change_obj.search( + [ + ('change_date', '>=', data_inicio), + ('change_date', '<=', data_fim), + ('wage', '>', 0), + ], + order="change_date DESC", + limit=1, + ) + return change.wage @api.multi def _salario_dia(self, data_inicio, data_fim): - if data_inicio >= self.date_start and data_fim <= self.date_end: + if data_inicio >= self.date_start and \ + (data_fim <= self.date_end or not self.date_end): return self.wage/30 + else: + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim)/30 @api.multi def _salario_hora(self, data_inicio, data_fim): - if data_inicio >= self.date_start and data_fim <= self.date_end: + if data_inicio >= self.date_start and \ + (data_fim <= self.date_end or not self.date_end): return self.wage/( 220 if not self.monthly_hours else self.monthly_hours ) + else: + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim)/( + 220 if not self.monthly_hours else self.monthly_hours + ) @api.multi def _salario_mes(self, data_inicio, data_fim): - if data_inicio >= self.date_start and data_fim <= self.date_end: + if data_inicio >= self.date_start and \ + (data_fim <= self.date_end or not self.date_end): return self.wage + else: + return self._buscar_salario_vigente_periodo(data_inicio, data_fim) specific_rule_ids = fields.One2many( comodel_name='hr.contract.salary.rule', @@ -71,3 +130,274 @@ def _salario_mes(self, data_inicio, data_fim): ('change_type', '=', 'filiacao-sindical') ], ) + company_id = fields.Many2one( + comodel_name='res.company', + string='Empresa', + required=True, + ) + + # Admissão + tipo_do_contrato = fields.Selection( + selection=[], + string="Tipo do contrato" + ) + + tipo_de_admissao = fields.Selection( + selection=[], + string="Tipo de admissão" + ) + + indicativo_de_admissao = fields.Selection( + selection=[('transferencia', u'Trasferência'), + ('normal', u'Normal')], + string="Indicativo da admissão" + ) + + contrato_transferido = fields.Selection( + selection=[], + string="Contrato transferido" + ) + + data_da_transferencia = fields.Date( + string="Data da transferencia" + ) + + seguro_desemprego = fields.Boolean( + string="Em Seguro Desemprego?" + ) + + primeiro_emprego = fields.Boolean( + string="Primeiro emprego?" + ) + + primeira_experiencia = fields.Integer( + string="Tempo em dias do 1º período de experiência" + ) + + data_primeira_experiencia = fields.Date( + string="Início da primeira experiência" + ) + + segunda_experiencia = fields.Integer( + string="Tempo em dias do 2º período de experiência" + ) + + data_segunda_experiencia = fields.Date( + string="Início da segunda experiência" + ) + + # Lotação + departamento_lotacao = fields.Selection( + selection=[], + string="Departamento/lotação" + ) + + lotacao_cliente_fornecedor = fields.Selection( + selection=[], + string="Lotação/cliente/fornecedor" + ) + + # Jornada + tipo_de_jornada = fields.Selection( + selection=[], + string="Tipo de jornada de trabalho" + ) + + jornada_seg_sex = fields.Selection( + selection=[], + string="Jornada padrão de segunda a sexta-feira" + ) + + jornada_sab = fields.Selection( + selection=[], + string="Jornada no sábado" + ) + + # Aba Vínculos Anteriores e cedentes + # Vínculo anterior + cnpj_empregador_anterior = fields.Char( + string="CNPJ do empregador anterior" + ) + + matricula_anterior = fields.Char( + string="Matrícula anterior" + ) + + data_admissao_anterior = fields.Date( + string="Data de admissão no vínculo anterior" + ) + + observacoes_vinculo_anterior = fields.Text( + string="Observações do vínculo anterior" + ) + + # Vínculo cedente + cnpj_empregador_cedente = fields.Char( + string="CNPJ do empregador cedente" + ) + + matricula_cedente = fields.Char( + string="Matrícula cedente" + ) + + data_admissao_cedente = fields.Date( + string="Data de admissão no vínculo cedente" + ) + + onus_vinculo_cedente = fields.Selection( + selection=[], + string="Ônus para o cedente" + ) + + # Aba Saúde ocupacional + data_atestado_saude = fields.Date( + string="Data do atestado de saúde ocupacional" + ) + + numero_crm = fields.Integer( + string="CRM nº" + ) + + nome_medico_encarregado = fields.Char( + string="Nome do médico encarregado" + ) + + estado_crm = fields.Selection( + selection=[], + string="Estado do CRM" + ) + + # Tree Exames + exame_ids = fields.One2many( + comodel_name='hr.exame.medico', + inverse_name='contract_id', + string="Exames" + ) + + # Aba Processo judicial + numero_processo = fields.Integer( + string="Nº processo judicial" + ) + + nome_advogado_autor = fields.Char( + string="Advogado do autor do processo" + ) + + nome_advogado_empresa = fields.Char( + string="Advogado da empresa" + ) + + observacoes_processo = fields.Text( + string="Observações do processo judicial" + ) + + # Aba Cursos e treinamentos + curso_ids = fields.One2many( + comodel_name='hr.curso', + inverse_name='contract_id', + string="Cursos" + ) + + # Aba Afastamentos + afastamento_ids = fields.One2many( + comodel_name='hr.holidays', + inverse_name='contrato_id', + string="Afastamentos" + ) + + +class Exame(models.Model): + _name = 'hr.exame.medico' + + name = fields.Char( + string="Exame" + ) + + data_do_exame = fields.Date( + string="Data do exame" + ) + + data_de_validade = fields.Date( + string="Data de validade" + ) + + contract_id = fields.Many2one( + comodel_name='hr.contract', + ) + + +class Curso(models.Model): + _name = 'hr.curso' + + name = fields.Char( + string="Curso" + ) + + carga_horaria = fields.Integer( + string="Carga horária" + ) + + inicio_curso = fields.Date( + string="Início" + ) + + fim_curso = fields.Date( + string="Encerramento" + ) + + situacao = fields.Selection( + selection=[], + string="Situação" + ) + + contract_id = fields.Many2one( + comodel_name='hr.contract', + ) + + +class HrHoliday(models.Model): + _inherit = 'hr.holidays' + + rubrica = fields.Char( + string="Rubrica" + ) + + periodo = fields.Char( + string="Data de afastamento" + ) + + valor_inss = fields.Float( + string="Valor INSS" + ) + + contrato_id = fields.Many2one( + comodel_name='hr.contract', + ) + + +class HrContractSalaryUnit(models.Model): + _inherit = 'hr.contract.salary.unit' + + @api.multi + def name_get(self): + result = [] + for record in self: + name = record['name'] + if name == 'Monthly': + name = 'Por mês' + elif name == 'Biweekly': + name = 'Por 15 dias' + elif name == 'Weekly': + name = 'Por semana' + elif name == 'Daily': + name = 'Por dia' + elif name == 'Hourly': + name = 'Por hora' + elif name == 'Task': + name = 'Por tarefa' + elif name == 'Others': + name = 'Outros' + elif record['code']: + name = record['code'] + ' - ' + name + result.append((record['id'], name)) + return result diff --git a/l10n_br_hr_payroll/models/hr_payroll_structure.py b/l10n_br_hr_payroll/models/hr_payroll_structure.py new file mode 100644 index 000000000..07537a78a --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_payroll_structure.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import fields, models + + +class HrPayrollStructure(models.Model): + _inherit = 'hr.payroll.structure' + + ferias = fields.Many2one( + comodel_name='hr.payroll.structure', + string='Férias', + domain=[('tipo_estrutura', '=', 'ferias')], + ) + + tipo_estrutura = fields.Selection( + selection=[ + ('normal', 'Folha Normal'), + ('ferias', 'Fériass'), + ('adiantamento_13', 'Adiantamento do 13º'), + ('segunda_parcela_13', 'Segunda Parcela do 13º'), + ('recisao', 'Recisão'), + ], + string='Tipo de Estrutura de Salários', + ) diff --git a/l10n_br_hr_payroll/models/hr_payslip.py b/l10n_br_hr_payroll/models/hr_payslip.py index 6e0d6f5c9..642a6344d 100644 --- a/l10n_br_hr_payroll/models/hr_payslip.py +++ b/l10n_br_hr_payroll/models/hr_payslip.py @@ -2,49 +2,166 @@ # Copyright (C) 2016 KMEE (http://www.kmee.com.br) # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from openerp import api, fields, models +import logging +from openerp import api, fields, models, exceptions, _ from datetime import datetime +from dateutil.relativedelta import relativedelta +from lxml import etree + +_logger = logging.getLogger(__name__) + +try: + from pybrasil import valor, data +except ImportError: + _logger.info('Cannot import pybrasil') MES_DO_ANO = [ - (1, u'Jan'), - (2, u'Fev'), - (3, u'Mar'), - (4, u'Abr'), - (5, u'Mai'), - (6, u'Jun'), - (7, u'Jul'), - (8, u'Ago'), - (9, u'Set'), - (10, u'Out'), - (11, u'Nov'), - (12, u'Dez'), + (1, u'Janeiro'), + (2, u'Fevereiro'), + (3, u'Marco'), + (4, u'Abril'), + (5, u'Maio'), + (6, u'Junho'), + (7, u'Julho'), + (8, u'Agosto'), + (9, u'Setembro'), + (10, u'Outubro'), + (11, u'Novembro'), + (12, u'Dezembro'), +] + +TIPO_DE_FOLHA = [ + ('normal', u'Folha normal'), + ('rescisao', u'Rescisão'), + ('ferias', u'Férias'), + ('decimo_terceiro', u'Décimo terceiro (13º)'), + ('aviso_previo', u'Aviso Prévio'), + ('licenca_maternidade', u'Licença maternidade'), + ('auxilio_doenca', u'Auxílio doença'), + ('auxílio_acidente_trabalho', u'Auxílio acidente de trabalho'), ] class HrPayslip(models.Model): _inherit = 'hr.payslip' + @api.multi + def _buscar_dias_aviso_previo(self): + for payslip in self: + if payslip.tipo_de_folha == 'aviso_previo': + periodos_aquisitivos = self.env['hr.vacation.control'].search( + [ + ('contract_id', '=', payslip.contract_id.id), + ('fim_aquisitivo', '<', payslip.date_to) + ] + ) + if periodos_aquisitivos: + payslip.dias_aviso_previo = 30 + len( + periodos_aquisitivos) * 3 + else: + payslip.dias_aviso_previo = 30 + @api.multi def _valor_total_folha(self): - total = 0.00 - for line in self.line_ids: - total += line.valor_provento - line.valor_deducao - self.write({'total_folha': total}) + for holerite in self: + total = 0.00 + total_proventos = 0.00 + total_descontos = 0.00 + base_inss = 0.00 + base_irpf = 0.00 + base_fgts = 0.00 + fgts = 0.00 + inss = 0.00 + irpf = 0.00 + codigo = {} + codigo['BASE_FGTS'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_BASE_FGTS').code + codigo['BASE_INSS'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_BASE_INSS').code + codigo['BASE_IRPF'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_BASE_IRPF').code + codigo['FGTS'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_FGTS').code + codigo['INSS'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_INSS').code + codigo['IRPF'] = \ + holerite.env\ + .ref('l10n_br_hr_payroll.hr_salary_rule_IRPF').code + for line in holerite.line_ids: + total += line.valor_provento - line.valor_deducao + total_proventos += line.valor_provento + total_descontos += line.valor_deducao + if codigo['BASE_FGTS']: + base_fgts = line.total + elif codigo['BASE_INSS']: + base_inss = line.total + elif codigo('BASE_IRPF'): + base_irpf = line.total + elif codigo['FGTS']: + fgts = line.total + elif codigo['INSS']: + inss = line.total + elif codigo['IRPF']: + irpf = line.total + holerite.total_folha = total + holerite.total_proventos = total_proventos + holerite.total_descontos = total_descontos + holerite.base_fgts = base_fgts + holerite.base_inss = base_inss + holerite.base_irpf = base_irpf + holerite.fgts = fgts + holerite.inss = inss + holerite.irpf = irpf + # Formato + holerite.data_admissao_fmt =\ + data.formata_data(holerite.contract_id.date_start) + holerite.salario_base_fmt =\ + valor.formata_valor(holerite.contract_id.wage) + holerite.total_folha_fmt =\ + valor.formata_valor(holerite.total_folha) + holerite.total_proventos_fmt =\ + valor.formata_valor(holerite.total_proventos) + holerite.total_descontos_fmt =\ + valor.formata_valor(holerite.total_descontos) + holerite.base_fgts_fmt = valor.formata_valor(holerite.base_fgts) + holerite.base_inss_fmt = valor.formata_valor(holerite.base_inss) + holerite.base_irpf_fmt = valor.formata_valor(holerite.base_irpf) + holerite.fgts_fmt = valor.formata_valor(holerite.fgts) + holerite.inss_fmt = valor.formata_valor(holerite.inss) + holerite.irpf_fmt = valor.formata_valor(holerite.irpf) employee_id_readonly = fields.Many2one( string=u'Funcionário', comodel_name='hr.employee', compute='set_employee_id', ) + is_simulacao = fields.Boolean( + string=u"Simulação", + ) + dias_aviso_previo = fields.Integer( + string="Dias de Aviso Prévio", + ) @api.depends('line_ids') @api.model def _buscar_payslip_line(self): - lines = [] - for line in self.line_ids: - if line.valor_provento or line.valor_deducao: - lines.append(line.id) - self.line_resume_ids = lines + for holerite in self: + lines = [] + for line in holerite.line_ids: + if line.valor_provento or line.valor_deducao: + lines.append(line.id) + holerite.line_resume_ids = lines + + tipo_de_folha = fields.Selection( + selection=TIPO_DE_FOLHA, + string=u'Tipo de folha', + default='normal', + ) struct_id_readonly = fields.Many2one( string=u'Estrutura de Salário', @@ -64,16 +181,158 @@ def _buscar_payslip_line(self): default=datetime.now().year, ) + data_mes_ano = fields.Char( + string=u'Mês/Ano', + compute='computar_mes_ano', + ) + total_folha = fields.Float( - string="Total", - default=0.00 + string=u'Total', + default=0.00, + compute='_valor_total_folha' + ) + + total_folha_fmt = fields.Char( + string=u'Total', + default='0', + compute='_valor_total_folha' + ) + + data_admissao_fmt = fields.Char( + string=u'Data de admissao', + default='0', + compute='_valor_total_folha' + ) + + salario_base_fmt = fields.Char( + string=u'Salario Base', + default='0', + compute='_valor_total_folha' + ) + + total_proventos = fields.Float( + string=u'Total Proventos', + default=0.00, + compute='_valor_total_folha' + ) + + total_proventos_fmt = fields.Char( + string=u'Total Proventos', + default='0', + compute='_valor_total_folha' + ) + + total_descontos = fields.Float( + string=u'Total Descontos', + default=0.00, + compute='_valor_total_folha' + ) + + total_descontos_fmt = fields.Char( + string=u'Total Descontos', + default='0', + compute='_valor_total_folha' + ) + + base_fgts = fields.Float( + string=u'Base do FGTS', + default=0.00, + compute='_valor_total_folha' + ) + + base_fgts_fmt = fields.Char( + string=u'Base do FGTS', + default='0', + compute='_valor_total_folha' + ) + + base_inss = fields.Float( + string=u'Base do INSS', + default=0.00, + compute='_valor_total_folha' + ) + + base_inss_fmt = fields.Char( + string=u'Base do INSS', + default='0', + compute='_valor_total_folha' + ) + + base_irpf = fields.Float( + string=u'Base do IRPF', + default=0.00, + compute='_valor_total_folha' + ) + + base_irpf_fmt = fields.Char( + string=u'Base do IRPF', + default='0', + compute='_valor_total_folha' + ) + + fgts = fields.Float( + string=u'FGTS', + default=0.00, + compute='_valor_total_folha' + ) + + fgts_fmt = fields.Char( + string=u'FGTS', + default='0', + compute='_valor_total_folha' + ) + + inss = fields.Float( + string=u'INSS', + default=0.00, + compute='_valor_total_folha' + ) + + inss_fmt = fields.Char( + string=u'INSS', + default='0', + compute='_valor_total_folha' + ) + + irpf = fields.Float( + string=u'IRPF', + default=0.00, + compute='_valor_total_folha' + ) + + irpf_fmt = fields.Char( + string=u'IRPF', + default='0', + compute='_valor_total_folha' + ) + + medias_proventos = fields.One2many( + string=u'Linhas das medias dos proventos', + comodel_name='l10n_br.hr.medias', + inverse_name='holerite_id', ) line_resume_ids = fields.One2many( comodel_name='hr.payslip.line', inverse_name='slip_id', compute=_buscar_payslip_line, - string="Holerite Resumo", + string=u"Holerite Resumo", + ) + + @api.depends('contract_id') + @api.model + def _get_periodo_aquisitivo(self): + if self.contract_id: + controles_ferias = self.contract_id.vacation_control_ids + if controles_ferias: + return controles_ferias[0] + + periodo_aquisitivo = fields.Many2one( + comodel_name='hr.vacation.control', + default='_get_periodo_aquisitivo', + string="Período Aquisitivo", + domain="[('contract_id','=',contract_id)]", + store=True, ) def get_attendances(self, nome, sequence, code, number_of_days, @@ -89,14 +348,14 @@ def get_attendances(self, nome, sequence, code, number_of_days, return attendance @api.multi - def get_worked_day_lines(self, date_from, date_to): + def get_worked_day_lines(self, contract_id, date_from, date_to): """ @param contract_ids: list of contract id @return: returns a list of dict containing the input that should be applied for the given contract between date_from and date_to """ result = [] - for contract_id in self: + for contract_id in self.env['hr.contract'].browse(contract_id): # get dias Base para cálculo do mês dias_mes = self.env['resource.calendar'].get_dias_base( @@ -160,10 +419,21 @@ def get_worked_day_lines(self, date_from, date_to): 0.0, contract_id ) ] + if hr_contract.vacation_control_ids[0].saldo: + saldo_ferias = hr_contract.vacation_control_ids[0].saldo + else: + saldo_ferias = 0 + result += [ + self.get_attendances( + u'Saldo de dias máximo para Férias', 8, + u'SALDO_FERIAS', saldo_ferias, + 0.0, contract_id + ) + ] # get Dias Trabalhados quantidade_dias_trabalhados = \ - dias_mes - leaves['quantidade_dias_faltas_nao_remuneradas'] - \ + 30 - leaves['quantidade_dias_faltas_nao_remuneradas'] - \ quantity_DSR_discount - quantidade_dias_ferias result += [self.get_attendances(u'Dias Trabalhados', 34, u'DIAS_TRABALHADOS', @@ -223,17 +493,18 @@ def IRRF(self, BASE_IR, BASE_INSS): def get_contract_specific_rubrics(self, contract_id, rule_ids): contract = self.env['hr.contract'].browse(contract_id.id) for rule in contract.specific_rule_ids: - if datetime.strftime( - datetime.now(), '%Y-%m-%d') >= rule.date_start: - if not rule.date_stop or datetime.strftime( - datetime.now(), '%Y-%m-%d') <= rule.date_stop: + if self.date_from >= rule.date_start: + if not rule.date_stop or self.date_to <= rule.date_stop: rule_ids.append((rule.rule_id.id, rule.rule_id.sequence)) return rule_ids @api.model - def get_specific_rubric_value(self, rubrica_id): + def get_specific_rubric_value(self, rubrica_id, medias_obj=False): for rubrica in self.contract_id.specific_rule_ids: if rubrica.rule_id.id == rubrica_id: + if medias_obj: + if rubrica.rule_id.code not in medias_obj.dict.keys(): + return 0 return rubrica.specific_quantity * \ rubrica.specific_percentual/100 * \ rubrica.specific_amount @@ -245,6 +516,75 @@ def _buscar_valor_salario(self, codigo): return tipo_salario.amount return 0.00 + @api.multi + def _get_rat_fap_period_values(self, year): + rat_fap_obj = self.env['l10n_br.hr.rat.fap'] + rat_fap = rat_fap_obj = rat_fap_obj.search( + [('year', '=', year), ('company_id', '=', self.company_id.id)] + ) + if rat_fap: + return rat_fap + else: + raise exceptions.Warning( + _('Can\'t find this year values in Rat Fap Table') + ) + + @api.multi + def buscar_estruturas_salario(self): + if self.tipo_de_folha == "normal" \ + or self.tipo_de_folha == "aviso_previo": + return self.contract_id.struct_id + elif self.tipo_de_folha == "decimo_terceiro": + if self.mes_do_ano < 12: + estrutura_decimo_terceiro = self.env.ref( + 'l10n_br_hr_payroll.' + 'hr_salary_structure_PRIMEIRA_PARCELA_13' + ) + return estrutura_decimo_terceiro + else: + estrutura_decimo_terceiro = self.env.ref( + 'l10n_br_hr_payroll.' + 'hr_salary_structure_SEGUNDA_PARCELA_13' + ) + return estrutura_decimo_terceiro + elif self.tipo_de_folha == "ferias": + estrutura_decimo_terceiro = self.env.ref( + 'l10n_br_hr_payroll.' + 'hr_salary_structure_FERIAS' + ) + return estrutura_decimo_terceiro + + # @api.multi + # def buscar_media_rubrica(self, rubrica_id): + # rubrica = self.env['hr.salary.rule'].browse(rubrica_id) + # for media in self.medias_proventos: + # if rubrica.name == media.nome_rubrica: + # return media.media + + @api.multi + def BUSCAR_PRIMEIRA_PARCELA(self): + primeira_parcela_struct_id = self.env.ref( + 'l10n_br_hr_payroll.hr_salary_structure_PRIMEIRA_PARCELA_13' + ) + primeira_parcela_id = self.env.ref( + 'l10n_br_hr_payroll.hr_salary_rule_PRIMEIRA_PARCELA_13' + ) + payslip_id = self.env['hr.payslip'].search( + [ + ('contract_id', '=', self.contract_id.id), + ('date_from', '>=', str(self.ano) + '-01-01'), + ('date_to', '<=', str(self.ano) + '-11-30'), + ('struct_id', '=', primeira_parcela_struct_id.id) + ] + ) + if len(payslip_id) > 1: + raise exceptions.Warning( + _('Existe mais de um holerite da primeira parcela do 13º!') + ) + for line in payslip_id.line_ids: + if line.salary_rule_id.id == primeira_parcela_id.id: + return line.total + @api.multi def get_payslip_lines(self, payslip_id): """ @@ -341,40 +681,52 @@ def sum(self, code, from_date, to_date=None): payslip = payslip_obj.browse(payslip_id) worked_days = {} for worked_days_line in payslip.worked_days_line_ids: - worked_days[worked_days_line.code] = worked_days_line + if payslip.tipo_de_folha == "aviso_previo" \ + and worked_days_line.code == u'DIAS_TRABALHADOS': + worked_days_line.number_of_days = payslip.dias_aviso_previo + worked_days[worked_days_line.code] = worked_days_line + else: + worked_days[worked_days_line.code] = worked_days_line inputs = {} for input_line in payslip.input_line_ids: inputs[input_line.code] = input_line - + medias = {} + for media in payslip.medias_proventos: + medias[media.rubrica_id.code] = media input_obj = InputLine(payslip.employee_id.id, inputs) worked_days_obj = WorkedDays(payslip.employee_id.id, worked_days) payslip_obj = Payslips(payslip.employee_id.id, payslip) rules_obj = BrowsableObject(payslip.employee_id.id, rules) + medias_obj = BrowsableObject(payslip.employee_id.id, medias) \ + if payslip.tipo_de_folha in ["ferias", "decimo_terceiro"] \ + else False categories_obj = \ BrowsableObject(payslip.employee_id.id, categories_dict) - salario_mes = self._buscar_valor_salario('SALARIO_MES') - salario_dia = self._buscar_valor_salario('SALARIO_DIA') - salario_hora = self._buscar_valor_salario('SALARIO_HORA') - + salario_mes = payslip._buscar_valor_salario('SALARIO_MES') + salario_dia = payslip._buscar_valor_salario('SALARIO_DIA') + salario_hora = payslip._buscar_valor_salario('SALARIO_HORA') + rat_fap = payslip._get_rat_fap_period_values(payslip.ano) baselocaldict = { 'CALCULAR': payslip, 'BASE_INSS': 0.0, 'BASE_FGTS': 0.0, 'BASE_IR': 0.0, 'categories': categories_obj, 'rules': rules_obj, 'payslip': payslip_obj, 'worked_days': worked_days_obj, 'inputs': input_obj, 'rubrica': None, 'SALARIO_MES': salario_mes, 'SALARIO_DIA': salario_dia, 'SALARIO_HORA': salario_hora, + 'RAT_FAP': rat_fap, 'MEDIAS': medias_obj, } for contract_ids in self: # get the ids of the structures on the contracts # and their parent id as well - structure_ids = self.env['hr.contract'].browse( - contract_ids.ids).get_all_structures() + # structure_ids = self.env['hr.contract'].browse( + # contract_ids.ids).get_all_structures() + structure_ids = payslip.struct_id._get_parent_structure() # get the rules of the structure and thier children rule_ids = self.env['hr.payroll.structure'].browse( structure_ids).get_all_rules() - rule_ids = self.get_contract_specific_rubrics( + rule_ids = payslip.get_contract_specific_rubrics( contract_ids, rule_ids) # run the rules by sequence @@ -397,6 +749,12 @@ def sum(self, code, from_date, to_date=None): # compute the amount of the rule amount, qty, rate = \ obj_rule.compute_rule(rule.id, localdict) + # se ja tiver sido calculado a media dessa rubrica, + # utilizar valor da media e multiplicar pela reinciden. + if medias.get(rule.code): + amount = medias.get(rule.code).media/12 + qty = medias.get(rule.code).meses + # check if there is already a rule computed # with that code previous_amount = \ @@ -429,7 +787,8 @@ def sum(self, code, from_date, to_date=None): result_dict[key] = { 'salary_rule_id': rule.id, 'contract_id': contract.id, - 'name': rule.name, + 'name': u'Média de ' + rule.name + if medias_obj else rule.name, 'code': rule.code, 'category_id': rule.category_id.id, 'sequence': rule.sequence, @@ -460,82 +819,181 @@ def sum(self, code, from_date, to_date=None): result = [value for code, value in result_dict.items()] return result - def _computar_ano(self): - ano = datetime.now().year - return ano + @api.multi + def onchange_employee_id(self, date_from, date_to, contract_id): + worked_days_obj = self.env['hr.payslip.worked_days'] + input_obj = self.env['hr.payslip.input'] + + # delete old worked days lines + old_worked_days_ids = worked_days_obj.search( + [('payslip_id', '=', self.id)] + ) + if old_worked_days_ids: + for worked_day_id in old_worked_days_ids: + worked_day_id.unlink() + + # delete old input lines + old_input_ids = input_obj.search([('payslip_id', '=', self.id)]) + if old_input_ids: + for input_id in old_input_ids: + input_id.unlink() + + # defaults + res = { + 'value': { + 'line_ids': [], + 'input_line_ids': [], + 'worked_days_line_ids': [], + 'name': '', + } + } + # computation of the salary input + worked_days_line_ids = self.get_worked_day_lines( + contract_id, date_from, date_to + ) + input_line_ids = self.get_inputs(contract_id, date_from, date_to) + res['value'].update( + { + 'worked_days_line_ids': worked_days_line_ids, + 'input_line_ids': input_line_ids, + } + ) + return res @api.multi @api.onchange('contract_id') def set_employee_id(self): for record in self: - record.employee_id = record.contract_id.employee_id - record.struct_id = record.contract_id.struct_id - record.employee_id_readonly = record.employee_id + record.struct_id = record.buscar_estruturas_salario() record.struct_id_readonly = record.struct_id + record.set_dates() + if record.contract_id: + record.employee_id = record.contract_id.employee_id + record.employee_id_readonly = record.employee_id + record._get_periodo_aquisitivo() - ultimo_dia_do_mes = self.env['resource.calendar'].\ - get_ultimo_dia_mes(self.mes_do_ano, self.ano) + @api.multi + @api.onchange('mes_do_ano', 'ano') + def buscar_datas_periodo(self): + for record in self: + record.set_dates() + if record.contract_id: + record.onchange_employee_id( + record.date_from, record.date_to, record.contract_id.id + ) - primeiro_dia_do_mes = \ - datetime.strptime(str(self.mes_do_ano) + '-' + - str(self.ano), '%m-%Y') + def computar_mes_ano(self): + for record in self: + record.data_mes_ano = MES_DO_ANO[record.mes_do_ano-1][1][:3] + \ + '/' + str(record.ano) - if not record.contract_id.date_start: - continue + def set_dates(self): + for record in self: + ultimo_dia_do_mes = str( + self.env['resource.calendar'].get_ultimo_dia_mes( + record.mes_do_ano, record.ano)) - date_start = record.contract_id.date_start + primeiro_dia_do_mes = str( + datetime.strptime(str(record.mes_do_ano) + '-' + + str(record.ano), '%m-%Y')) - if str(primeiro_dia_do_mes) < date_start: - date_from = record.contract_id.date_start - else: - date_from = str(primeiro_dia_do_mes) + record.date_from = primeiro_dia_do_mes + record.date_to = ultimo_dia_do_mes - record.date_from = date_from + data_de_inicio = record.contract_id.date_start + data_final = record.contract_id.date_end - date_end = record.contract_id.date_end + if data_de_inicio and primeiro_dia_do_mes < data_de_inicio: + record.date_from = record.contract_id.date_start - if not date_end: - record.date_to = str(ultimo_dia_do_mes) - elif str(ultimo_dia_do_mes) > record.contract_id.date_end: + if data_final and ultimo_dia_do_mes > data_final: record.date_to = record.contract_id.date_end - else: - record.date_to = str(ultimo_dia_do_mes) @api.multi def compute_sheet(self): + if self.tipo_de_folha in ["decimo_terceiro", "ferias", "aviso_previo"]: + hr_medias_ids, data_de_inicio, data_final = \ + self.gerar_media_dos_proventos() + + if not hr_medias_ids \ + and self.tipo_de_folha in ["decimo_terceiro", "ferias"]: + raise exceptions.Warning( + _('Nenhum Holerite encontrado para médias nesse período!') + ) + + self.validacao_holerites_anteriores( + data_de_inicio, data_final, self.contract_id) super(HrPayslip, self).compute_sheet() self._valor_total_folha() return True + def validacao_holerites_anteriores(self, data_inicio, data_fim, contrato): + """ + VAlida se existe todos os holerites calculados e confirmados em + determinado período. + :param date_from: + :param date_to: + :return: + """ + folha_obj = self.env['hr.payslip'] + domain = [ + ('date_from', '>=', data_inicio), + ('date_to', '<=', data_fim), + ('contract_id', '=', contrato.id), + ('state', '=', 'done'), + ] + folhas_periodo = folha_obj.search(domain) + + folhas_sorted = folhas_periodo.sorted(key=lambda r: r.date_from) + mes = fields.Date.from_string(data_inicio) + relativedelta(months=-1) + + for folha in folhas_sorted: + mes = mes + relativedelta(months=1) + if folha.mes_do_ano != mes.month: + raise exceptions.ValidationError(_( + "Faltando Holerite confirmado do mês de %s" + ) % MES_DO_ANO[mes.month-1][1]) + + if mes.month != fields.Date.from_string(data_fim).month: + raise exceptions.ValidationError(_( + "Não foi encontrado holerite confirmado do mês de %s" + ) % MES_DO_ANO[mes.month-1][1]) -class HrPayslipeLine(models.Model): - _inherit = "hr.payslip.line" - - @api.model - def _valor_provento(self): - for record in self: - if record.salary_rule_id.category_id.code == "PROVENTO": - record.valor_provento = record.total + @api.multi + def gerar_media_dos_proventos(self): + medias_obj = self.env['l10n_br.hr.medias'] + if self.tipo_de_folha == 'ferias' \ + or self.tipo_de_folha == 'aviso_previo': + periodo_aquisitivo = self.periodo_aquisitivo + data_de_inicio = str(fields.Date.from_string( + periodo_aquisitivo.inicio_aquisitivo)) + data_final = str(fields.Date.from_string( + periodo_aquisitivo.fim_aquisitivo)) + elif self.tipo_de_folha == 'decimo_terceiro': + if self.contract_id.date_start > str(self.ano) + '-01-01': + data_de_inicio = self.contract_id.date_start else: - record.valor_provento = 0.00 + data_de_inicio = str(self.ano) + '-01-01' + data_final = self.date_to + hr_medias_ids = medias_obj.gerar_media_dos_proventos( + data_de_inicio, data_final, self) + return hr_medias_ids, data_de_inicio, data_final @api.model - def _valor_deducao(self): - for record in self: - if record.salary_rule_id.category_id.code in ["DEDUCAO"] \ - or record.salary_rule_id.code == "INSS" \ - or record.salary_rule_id.code == "IRPF": - record.valor_deducao = record.total - else: - record.valor_deducao = 0.00 - - valor_provento = fields.Float( - string="Provento", - compute=_valor_provento, - default=0.00, - ) - valor_deducao = fields.Float( - string="Dedução", - compute=_valor_deducao, - default=0.00, - ) + def fields_view_get(self, view_id=None, view_type='form', + toolbar=False, submenu=False): + res = super(HrPayslip, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, + submenu=submenu + ) + if view_type == 'form': + doc = etree.XML(res['arch']) + for sheet in doc.xpath("//sheet"): + parent = sheet.getparent() + index = parent.index(sheet) + for child in sheet: + parent.insert(index, child) + index += 1 + parent.remove(sheet) + res['arch'] = etree.tostring(doc) + return res diff --git a/l10n_br_hr_payroll/models/hr_payslip_line.py b/l10n_br_hr_payroll/models/hr_payslip_line.py new file mode 100644 index 000000000..3ec3f70c8 --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_payslip_line.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import logging +from openerp import api, fields, models + +_logger = logging.getLogger(__name__) + +try: + from pybrasil import valor +except ImportError: + _logger.info('Cannot import pybrasil') + + +class HrPayslipeLine(models.Model): + _inherit = "hr.payslip.line" + + @api.model + def _valor_provento(self): + for record in self: + record.quantity_fmt = valor.formata_valor(record.quantity) + if record.salary_rule_id.category_id.code == "PROVENTO": + record.valor_provento = record.total + record.valor_provento_fmt = \ + valor.formata_valor(record.valor_provento) + else: + record.valor_provento = 0.00 + record.valor_provento_fmt = '' + + @api.model + def _valor_deducao(self): + for record in self: + if record.salary_rule_id.category_id.code in ["DEDUCAO"] \ + or record.salary_rule_id.code == "INSS" \ + or record.salary_rule_id.code == "IRPF": + record.valor_deducao = record.total + record.valor_deducao_fmt = \ + valor.formata_valor(record.valor_deducao) + else: + record.valor_deducao = 0.00 + record.valor_deducao_fmt = '' + + quantity_fmt = fields.Char( + string=u'Quantidade', + compute=_valor_provento, + default='', + ) + + valor_provento = fields.Float( + string=u'Provento', + compute=_valor_provento, + default=0.00, + ) + + valor_provento_fmt = fields.Char( + string=u'Provento', + compute=_valor_provento, + default='', + ) + valor_deducao = fields.Float( + string=u'Dedução', + compute=_valor_deducao, + default=0.00, + ) + + valor_deducao_fmt = fields.Char( + string=u'Dedução', + compute=_valor_deducao, + default='', + ) diff --git a/l10n_br_hr_payroll/models/hr_payslip_run.py b/l10n_br_hr_payroll/models/hr_payslip_run.py new file mode 100644 index 000000000..a8f0f090b --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_payslip_run.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from datetime import datetime + +from openerp import api, fields, models + +MES_DO_ANO = [ + (1, u'Jan'), + (2, u'Fev'), + (3, u'Mar'), + (4, u'Abr'), + (5, u'Mai'), + (6, u'Jun'), + (7, u'Jul'), + (8, u'Ago'), + (9, u'Set'), + (10, u'Out'), + (11, u'Nov'), + (12, u'Dez'), +] + +TIPO_DE_FOLHA = [ + ('normal', u'Folha normal'), + ('decimo_terceiro', u'Décimo terceiro (13º)'), +] + + +class HrPayslipRun(models.Model): + _inherit = "hr.payslip.run" + + mes_do_ano = fields.Selection( + selection=MES_DO_ANO, + string=u'Mês', + required=True, + default=datetime.now().month, + ) + ano = fields.Integer( + string=u'Ano', + default=datetime.now().year, + ) + tipo_de_folha = fields.Selection( + selection=TIPO_DE_FOLHA, + string=u'Tipo de folha', + default='normal', + ) + contract_id = fields.Many2many( + comodel_name='hr.contract', + string='Contratos', + ) + contract_id_readonly = fields.Many2many( + comodel_name='hr.contract', + string='Contratos', + ) + departamento_id = fields.Many2one( + comodel_name='hr.department', + string='Departamento', + ) + company_id = fields.Many2one( + comodel_name='res.company', + string='Empresa', + ) + + @api.multi + @api.onchange('mes_do_ano') + def buscar_datas_periodo(self): + for record in self: + record.set_dates() + + def set_dates(self): + for record in self: + ultimo_dia_do_mes = str( + self.env['resource.calendar'].get_ultimo_dia_mes( + record.mes_do_ano, record.ano)) + + primeiro_dia_do_mes = str( + datetime.strptime(str(record.mes_do_ano) + '-' + + str(record.ano), '%m-%Y')) + + record.date_start = primeiro_dia_do_mes + record.date_end = ultimo_dia_do_mes + + @api.multi + def verificar_holerites_gerados(self): + contract_id = self.env['hr.contract'].search( + [ + ('company_id', '=', self.company_id.id) + ] + ) + contratos = [contrato.id for contrato in contract_id] + payslip_obj = self.env['hr.payslip'] + payslips = payslip_obj.search( + [ + ('tipo_de_folha', '=', self.tipo_de_folha), + ('date_from', '>=', self.date_start), + ('date_to', '<=', self.date_end), + ('contract_id', 'in', contratos) + ] + ) + contratos_holerites_gerados = [] + for payslip in payslips: + if payslip.contract_id.id not in contratos_holerites_gerados: + contratos_holerites_gerados.append(payslip.contract_id.id) + contratos_sem_holerite = [ + contrato.id for contrato in contract_id + if contrato.id not in contratos_holerites_gerados + ] + if self.id: + self.write( + { + 'contract_id': [(6, 0, contratos_sem_holerite)], + 'contract_id_readonly': [(6, 0, contratos_sem_holerite)], + } + ) + else: + self.contract_id = contratos_sem_holerite + self.contract_id_readonly = contratos_sem_holerite + + @api.multi + def gerar_holerites(self): + for contrato in self.contract_id: + try: + payslip_obj = self.env['hr.payslip'] + payslip = payslip_obj.create( + { + 'contract_id': contrato.id, + 'mes_do_ano': self.mes_do_ano, + 'ano': self.ano, + 'date_from': self.date_start, + 'date_to': self.date_end, + 'employee_id': contrato.employee_id.id, + 'tipo_de_folha': self.tipo_de_folha, + 'payslip_run_id': self.id, + } + ) + payslip.set_employee_id() + payslip.onchange_employee_id( + self.date_start, + self.date_end, + contrato.id + ) + worked_days_line_ids = payslip.get_worked_day_lines( + contrato.id, self.date_start, self.date_end + ) + input_line_ids = payslip.get_inputs( + contrato.id, self.date_start, self.date_end + ) + worked_days_obj = self.env['hr.payslip.worked_days'] + input_obj = self.env['hr.payslip.input'] + for worked_day in worked_days_line_ids: + worked_day.update({'payslip_id': payslip.id}) + worked_days_obj.create(worked_day) + for input_id in input_line_ids: + input_id.update({'payslip_id': payslip.id}) + input_obj.create(input_id) + payslip.compute_sheet() + except: + self._cr.rollback() + pass + self.verificar_holerites_gerados() diff --git a/l10n_br_hr_payroll/models/hr_salary_rule.py b/l10n_br_hr_payroll/models/hr_salary_rule.py index 8c31992a9..9bed00117 100644 --- a/l10n_br_hr_payroll/models/hr_salary_rule.py +++ b/l10n_br_hr_payroll/models/hr_salary_rule.py @@ -2,20 +2,103 @@ # Copyright (C) 2016 KMEE (http://www.kmee.com.br) # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from openerp import fields, models +import logging + +from openerp import fields, models, exceptions, _ +from openerp.tools.safe_eval import safe_eval + +_logger = logging.getLogger(__name__) + +try: + from pybrasil.python_pt_BR import python_pt_BR + # from pybrasil.valor.decimal import Decimal + +except ImportError: + _logger.info('Cannot import pybrasil') + + +CALCULO_FOLHA_PT_BR = { + u'resultado': 'result', + u'valor': 'result', + u'taxa': 'result_rate', + u'aliquota': 'result_rate', + u'alíquota': 'result_rate', + u'quantidade': 'result_qty', + u'quant': 'result_qty', + u'qtd': 'result_qty', + u'qtde': 'result_qty', +} class HrSalaryRule(models.Model): _inherit = 'hr.salary.rule' compoe_base_INSS = fields.Boolean( - string=u'Compõe Base INSS' + string=u'Compõe Base INSS', ) compoe_base_IR = fields.Boolean( - string=u'Compõe Base IR' + string=u'Compõe Base IR', ) compoe_base_FGTS = fields.Boolean( - string=u'Compõe Base FGTS' + string=u'Compõe Base FGTS', + ) + + def compute_rule(self, cr, uid, rule_id, localdict, context=None): + rule = self.browse(cr, uid, rule_id, context=context) + + if rule.amount_select != 'code': + return super(HrSalaryRule, self).compute_rule(cr, uid, rule_id, + localdict, + context=context) + + codigo_python = python_pt_BR(rule.amount_python_compute or '', + CALCULO_FOLHA_PT_BR) + + try: + safe_eval(codigo_python, localdict, mode='exec', nocopy=True) + result = localdict['result'] + + if 'result_qty' in localdict: + result_qty = localdict['result_qty'] + else: + result_qty = 1 + + if 'result_rate' in localdict: + result_rate = localdict['result_rate'] + else: + result_rate = 100 + + return result, result_qty, result_rate + + except: + msg = _('Wrong python code defined for salary rule %s (%s).') + raise exceptions.ValidationError(msg % (rule.name, rule.code)) + + def satisfy_condition(self, cr, uid, rule_id, localdict, context=None): + rule = self.browse(cr, uid, rule_id, context=context) + + if rule.condition_select != 'python': + return super(HrSalaryRule, self).satisfy_condition( + cr, uid, rule_id, localdict, context=context + ) + + codigo_python = python_pt_BR(rule.condition_python or '', + CALCULO_FOLHA_PT_BR) + + try: + safe_eval(codigo_python, localdict, mode='exec', nocopy=True) + return 'result' in localdict and localdict['result'] or False + + except: + msg = _('Wrong python condition defined for salary rule %s (%s).') + raise exceptions.UserError(msg % (rule.name, rule.code)) + + tipo_media = fields.Selection( + selection=[ + ('valor', 'Valor'), + ('quantidade', 'Quantidade'), + ], + string='Tipo de Média da Rubrica', ) diff --git a/l10n_br_hr_payroll/models/l10n_br_hr_contract.py b/l10n_br_hr_payroll/models/l10n_br_hr_contract.py index 80aeddf57..f5a5327f1 100644 --- a/l10n_br_hr_payroll/models/l10n_br_hr_contract.py +++ b/l10n_br_hr_payroll/models/l10n_br_hr_contract.py @@ -3,12 +3,16 @@ # 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 + +STATES = [('draft', 'Rascunho'), + ('applied', 'Aplicada')] class HrContractChangeReason(models.Model): _name = 'l10n_br_hr.contract.change_reason' - _description = u"Motivo de alteração contatual" + _description = u"Motivo de alteração contratual" name = fields.Char(u"Motivo") @@ -16,7 +20,7 @@ class HrContractChangeReason(models.Model): class HrContractChange(models.Model): _name = 'l10n_br_hr.contract.change' - _description = u"Alteração contatual" + _description = u"Alteração contratual" _inherit = 'hr.contract' def _get_default_type(self): @@ -24,7 +28,16 @@ def _get_default_type(self): if change_type: return change_type else: - raise UserWarning(u'Sem tipo de alteração definido!') + raise UserError(u'Sem tipo de alteração definido!') + + @api.depends('contract_id', 'change_history_ids') + def _get_change_history(self): + change_type = self._context.get('change_type', False) + full_history = self.search( + [('contract_id', '=', self.contract_id.id), + ('change_type', '=', change_type), + ('state', '=', 'applied')]) + self.change_history_ids = full_history contract_id = fields.Many2one( 'hr.contract', @@ -37,20 +50,20 @@ def _get_default_type(self): ('cargo-atividade', u'Cargo/Atividade'), ('filiacao-sindical', u'Filiação Sindical'), ('lotacao-local', u'Lotação/Local de trabalho'), - # ('curso-treinamento', u'Curso/Treinamento'), ], string=u"Tipo de alteração contratual", default=_get_default_type ) change_reason_id = fields.Many2one( comodel_name='l10n_br_hr.contract.change_reason', - string=u"Motivo", + string=u"Motivo", required=True, ) change_date = fields.Date(u'Data da alteração') - change_history = fields.One2many( + change_history_ids = fields.Many2many( comodel_name='l10n_br_hr.contract.change', inverse_name='contract_id', string=u"Histórico", + compute=_get_change_history, ) name = fields.Char(string='Contract Reference', required=False) employee_id = fields.Many2one(string='Employee', @@ -59,7 +72,129 @@ def _get_default_type(self): type_id = fields.Many2one(string='Contract Type', comodel_name='hr.contract.type', required=False) + state = fields.Selection(string=u'Alteração aplicada', selection=STATES, + default='draft') + user_id = fields.Many2one( + comodel_name='res.users', + string='Alterado por', + ) @api.onchange('contract_id') def _onchange_contract_id(self): - pass + contract = self.contract_id + self.change_date = fields.datetime.now() + self.notes = contract.notes + if self.change_type == 'remuneracao': + self.wage = contract.wage + self.salary_unit = contract.salary_unit + self.struct_id = contract.struct_id + elif self.change_type == 'jornada': + self.wage = contract.wage + self.working_hours = contract.working_hours + self.schedule_pay = contract.schedule_pay + self.monthly_hours = contract.monthly_hours + self.weekly_hours = contract.weekly_hours + elif self.change_type == 'cargo-atividade': + self.wage = contract.wage + self.job_id = contract.job_id + self.type_id = contract.type_id + self.admission_type_id = contract.admission_type_id + self.labor_bond_type_id = contract.labor_bond_type_id + self.labor_regime_id = contract.labor_regime_id + elif self.change_type == 'filiacao-sindical': + self.wage = contract.wage + self.union = contract.union + self.union_cnpj = contract.union_cnpj + self.union_entity_code = contract.union_entity_code + self.discount_union_contribution = \ + contract.discount_union_contribution + self.month_base_date = contract.month_base_date + + @api.multi + def apply_contract_changes(self): + for change in self: + contract = change.contract_id + if self.change_type == 'remuneracao': + if not self.env['l10n_br_hr.contract.change'].search( + [('wage', '>', 0), + ('change_date', '<', change.change_date)]): + vals = { + 'contract_id': contract.id, + 'change_date': contract.date_start, + 'change_reason_id': change.change_reason_id.id, + 'wage': contract.wage, + 'struct_id': change.struct_id.id, + } + self.env['l10n_br_hr.contract.change'].create(vals) + contract.wage = self.wage + contract.salary_unit = self.salary_unit + contract.struct_id = self.struct_id + elif self.change_type == 'jornada': + if not self.env['l10n_br_hr.contract.change'].search( + [('working_hours', '!=', False), + ('change_date', '<', change.change_date)]): + vals = { + 'contract_id': contract.id, + 'change_date': contract.date_start, + 'change_reason_id': change.change_reason_id.id, + 'wage': contract.wage, + 'working_hours': contract.working_hours.id, + 'struct_id': change.struct_id.id, + } + self.env['l10n_br_hr.contract.change'].create(vals) + contract.working_hours = self.working_hours + contract.schedule_pay = self.schedule_pay + contract.monthly_hours = self.monthly_hours + contract.weekly_hours = self.weekly_hours + elif self.change_type == 'cargo-atividade': + if not self.env['l10n_br_hr.contract.change'].search( + [('job_id', '!=', False), + ('change_date', '<', change.change_date)]): + vals = { + 'contract_id': contract.id, + 'change_date': contract.date_start, + 'change_reason_id': change.change_reason_id.id, + 'wage': contract.wage, + 'job_id': contract.job_id.id, + 'type_id': contract.type_id.id, + 'adminission_type_id': contract.admission_type_id.id, + 'labor_bond_type_id': contract.labor_bond_type_id.id, + 'labor_regime_id': contract.labor_regime_id.id, + 'struct_id': change.struct_id.id, + } + self.env['l10n_br_hr.contract.change'].create(vals) + contract.job_id = self.job_id + contract.type_id = self.type_id + contract.admission_type_id = self.admission_type_id + contract.labor_bond_type_id = self.labor_bond_type_id + contract.labor_regime_id = self.labor_regime_id + elif self.change_type == 'filiacao-sindical': + if not self.env['l10n_br_hr.contract.change'].search( + [('union', '!=', False), + ('change_date', '<', change.change_date)]): + vals = { + 'contract_id': contract.id, + 'change_date': contract.date_start, + 'change_reason_id': change.change_reason_id.id, + 'wage': contract.wage, + 'union': contract.union, + 'union_cnpj': contract.union_cnpj, + 'union_entity_code': contract.union_entity_code, + 'discount_union_contribution': + contract.discount_union_contribution, + 'month_base_date': contract.month_base_date, + 'struct_id': change.struct_id.id, + } + self.env['l10n_br_hr.contract.change'].create(vals) + contract.union = self.union + contract.union_cnpj = self.union_cnpj + contract.union_entity_code = self.union_entity_code + contract.discount_union_contribution = \ + self.discount_union_contribution + contract.month_base_date = self.month_base_date + self.state = 'applied' + + @api.model + def create(self, vals): + vals.update({'user_id': self.env.user.id}) + return super(HrContractChange, self).create(vals) diff --git a/l10n_br_hr_payroll/models/l10n_br_hr_medias.py b/l10n_br_hr_payroll/models/l10n_br_hr_medias.py new file mode 100644 index 000000000..6aecac966 --- /dev/null +++ b/l10n_br_hr_payroll/models/l10n_br_hr_medias.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import api, fields, models + +MES_DO_ANO = [ + (1, u'Janeiro'), + (2, u'Fevereiro'), + (3, u'Marco'), + (4, u'Abril'), + (5, u'Maio'), + (6, u'Junho'), + (7, u'Julho'), + (8, u'Agosto'), + (9, u'Setembro'), + (10, u'Outubro'), + (11, u'Novembro'), + (12, u'Dezembro'), +] + + +class L10nBrHrMedias(models.Model): + _name = 'l10n_br.hr.medias' + _description = 'Brazilian HR - Medias dos Proventos' + # _order = 'year desc' + + holerite_id = fields.Many2one( + string=u'Holerite', + comodel_name='hr.payslip', + ) + rubrica_id = fields.Many2one( + comodel_name="hr.salary.rule", + string="Id da rubrica" + ) + nome_rubrica = fields.Char( + string=u'Nome da Rubrica', + ) + mes_1 = fields.Char( + string=u'1º Mes', + ) + mes_2 = fields.Char( + string=u'2º Mes', + ) + mes_3 = fields.Char( + string=u'3º Mes', + ) + mes_4 = fields.Char( + string=u'4º Mes', + ) + mes_5 = fields.Char( + string=u'5º Mes', + ) + mes_6 = fields.Char( + string=u'6º Mes', + ) + mes_7 = fields.Char( + string=u'7º Mes', + ) + mes_8 = fields.Char( + string=u'8º Mes', + ) + mes_9 = fields.Char( + string=u'9º Mes', + ) + mes_10 = fields.Char( + string=u'10º Mes', + ) + mes_11 = fields.Char( + string=u'11º Mes', + ) + mes_12 = fields.Char( + string=u'12º Mes', + ) + soma = fields.Float( + string=u'Total dos Meses', + compute='calcular_soma' + ) + meses = fields.Float( + string=u'Meses do periodo', + ) + media = fields.Float( + string=u'Média', + compute='calcular_media', + ) + media_texto = fields.Char( + string=u'Média' + ) + linha_de_titulo = fields.Boolean( + string=u'Linha do Titulo', + help='Indica se é a linha construida para compor o título', + default=False, + ) + + def calcular_soma(self): + for linha in self: + if not linha.linha_de_titulo: + linha.soma = \ + float(linha.mes_1) + float(linha.mes_2) + \ + float(linha.mes_3) + float(linha.mes_4) + \ + float(linha.mes_5) + float(linha.mes_6) + \ + float(linha.mes_7) + float(linha.mes_8) + \ + float(linha.mes_9) + float(linha.mes_10) + \ + float(linha.mes_11) + float(linha.mes_12) + + def calcular_media(self): + for linha in self: + if not linha.linha_de_titulo: + if linha.meses == 0: + linha.media = 123 + else: + linha.media = linha.soma/linha.meses + + @api.multi + def gerar_media_dos_proventos(self, data_inicio, data_fim, holerite_id): + """ + Recuperar os proventos do periodo e retornar média + :param data_inicio: + :param data_fim: + :param holerite_id: + :return: + """ + for linha in holerite_id.medias_proventos: + linha.unlink() + + folha_obj = self.env['hr.payslip'] + domain = [ + ('date_from', '>=', data_inicio), + ('date_to', '<=', data_fim), + ('contract_id', '=', holerite_id.contract_id.id), + ('state', '=', 'done'), + ] + folhas_periodo = folha_obj.search(domain) + folhas_periodo = folhas_periodo.sorted(key=lambda r: r.date_from) + medias = {} + for folha in folhas_periodo: + for linha in folha.line_ids: + if linha.salary_rule_id.category_id.code == "PROVENTO" \ + and linha.salary_rule_id.tipo_media: + if not medias.get(linha.salary_rule_id.id): + medias.update({ + linha.salary_rule_id.id: + [{ + 'mes': MES_DO_ANO[folha.mes_do_ano-1][1], + 'valor': linha.total, + 'rubrica_id': linha.salary_rule_id.id, + }] + }) + else: + medias[linha.salary_rule_id.id].append({ + 'mes': MES_DO_ANO[folha.mes_do_ano-1][1], + 'valor': linha.total, + 'rubrica_id': linha.salary_rule_id.id, + }) + + linha_obj = self.env['l10n_br.hr.medias'] + hr_medias_ids = [] + titulo = {} + meses_titulos = [] + + # definindo titulo da visao tree + for rubrica in medias: + mes_cont = 1 + titulo.update({'meses': len(medias[rubrica])}) + titulo.update({'holerite_id': holerite_id.id}) + titulo.update({'linha_de_titulo': True}) + for mes in medias[rubrica]: + titulo.update({'mes_' + str(mes_cont): str(mes['mes']), }) + if str(mes['mes']) in meses_titulos: + meses_titulos.remove(str(mes['mes'])) + meses_titulos.append(str(mes['mes'])) + mes_cont += 1 + linha_obj.create(titulo) + + # definindo a linha + for rubrica in medias: + vals = {} + nome_rubrica = self.env['hr.salary.rule'].\ + browse(rubrica).display_name + vals.update({'nome_rubrica': nome_rubrica}) + vals.update({'meses': len(medias[rubrica])}) + vals.update({'holerite_id': holerite_id.id}) + vals.update({'rubrica_id': rubrica}) + + for mes in medias[rubrica]: + mes_cont = 1 + for mes_titulo in meses_titulos: + # se o mes em questão for igual mes do titulo + if mes_titulo == mes['mes']: + vals.update({ + 'mes_' + str(mes_cont): str(mes['valor']), + }) + break + mes_cont += 1 + hr_medias_ids.append(linha_obj.create(vals)) + + return hr_medias_ids diff --git a/l10n_br_hr_payroll/security/ir.model.access.csv b/l10n_br_hr_payroll/security/ir.model.access.csv index 20e0f93fb..7b71254f2 100644 --- a/l10n_br_hr_payroll/security/ir.model.access.csv +++ b/l10n_br_hr_payroll/security/ir.model.access.csv @@ -8,3 +8,6 @@ "access_l10n_br_hr_contract_change_reason","access_l10n_br_hr_contract_change_reason","model_l10n_br_hr_contract_change_reason","",1,0,0,0 "access_l10n_br_hr_contract_change","access_l10n_br_hr_contract_change","model_l10n_br_hr_contract_change","",1,0,0,0 "access_hr_contract_salary_rule","access_hr_contract_salary_rule","model_hr_contract_salary_rule","",1,0,0,0 +"access_l10n_br_hr_medias","access_l10n_br_hr_medias","model_l10n_br_hr_medias","",1,0,0,0 +"access_hr_exame_medico","access_hr_exame_medico","model_hr_exame_medico","",1,0,0,0 +"access_hr_curso","access_hr_curso","model_hr_curso","",1,0,0,0 \ No newline at end of file diff --git a/l10n_br_hr_payroll/views/hr_contract.xml b/l10n_br_hr_payroll/views/hr_contract.xml index 5ec97482f..97b8da154 100644 --- a/l10n_br_hr_payroll/views/hr_contract.xml +++ b/l10n_br_hr_payroll/views/hr_contract.xml @@ -10,6 +10,9 @@ hr.contract + + + @@ -43,6 +46,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_hr_payroll/views/hr_payroll_structure.xml b/l10n_br_hr_payroll/views/hr_payroll_structure.xml new file mode 100644 index 000000000..e3a02abf9 --- /dev/null +++ b/l10n_br_hr_payroll/views/hr_payroll_structure.xml @@ -0,0 +1,22 @@ + + + + + + + + + hr.payroll.structure.form (in l10n_br_hr_payroll) + hr.payroll.structure + + + + + + + + + + + diff --git a/l10n_br_hr_payroll/views/hr_payslip.xml b/l10n_br_hr_payroll/views/hr_payslip.xml index 6ed6f9f9b..bd975f81d 100644 --- a/l10n_br_hr_payroll/views/hr_payslip.xml +++ b/l10n_br_hr_payroll/views/hr_payslip.xml @@ -5,11 +5,41 @@ + + + + hr.payslip.tree + hr.payslip + + + + + + + + + + + + + + + Holerites do Funcionário + hr.payslip + form + + + {'default_tipo_de_folha': 'normal'} + [('tipo_de_folha','=','normal')] + + hr.payslip.form (in l10n_br_hr_payroll) hr.payslip + @@ -103,13 +133,42 @@ + + +