diff --git a/.travis.yml b/.travis.yml index a1bece4b8..db829f14b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,9 @@ install: - pip install python-dateutil - pip install pytz - pip install pyparsing - - pip install git+https://github.com/aricaldeira/pybrasil.git + - pip install git+https://github.com/aricaldeira/pybrasil.git@bb8d47a415a973bb2a823e1f7a480d77f3e22ef8 + - pip install git+https://github.com/kmee/cnab240.git@feature/pagamento + - pip install pyboleto - travis_install_nightly script: diff --git a/l10n_br_hr_allowance/models/hr_holidays.py b/l10n_br_hr_allowance/models/hr_holidays.py new file mode 100644 index 000000000..8b7b6610a --- /dev/null +++ b/l10n_br_hr_allowance/models/hr_holidays.py @@ -0,0 +1,19 @@ +from openerp import api, fields, models + + +class HrHolidays(models.Model): + _inherit = 'hr.holidays' + + ano = fields.Integer( + string=u'Ano referencia', + ) + + contrato_id = fields.Many2one( + comodel_name='hr.contract', + string=u'Contrato associado', + ) + + @api.onchange('contrato_id') + def onchange_contrato(self): + for holiday in self: + holiday.employee_id = holiday.contrato_id.employee_id diff --git a/l10n_br_hr_allowance/views/hr_holidays.xml b/l10n_br_hr_allowance/views/hr_holidays.xml new file mode 100644 index 000000000..35f46eff9 --- /dev/null +++ b/l10n_br_hr_allowance/views/hr_holidays.xml @@ -0,0 +1,15 @@ + + + + hr.holidays.form (in abono) + hr.holidays + + + + + + + + + + \ No newline at end of file diff --git a/l10n_br_hr_arquivos_governo/README.rst b/l10n_br_hr_arquivos_governo/README.rst new file mode 100644 index 000000000..34f9a1f52 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/README.rst @@ -0,0 +1,89 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=============================================================== +Modulo gerador de arquivos TXT da Folha de Pagamento Brasileira +=============================================================== + +This module extends the functionality of l10n_br_hr_payroll to support a geração +de arquivos txt de dados dos recursos humanos em geral +and to allow you to envie às autoridades regulamentadoras brasileiras. + +Installation +============ + +To install this module, you need to: + +#. Do this ... + +Configuration +============= + +To configure this module, you need to: + +#. Go to ... + +.. figure:: path/to/local/image.png + :alt: alternative description + :width: 600 px + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Luis Felipe Miléo +* Matheus Felix +* Rafael da Silva Lima +* Aristides Caldeira + +Funders +------- + +The development of this module has been financially supported by: + +* Company 1 name +* Company 2 name + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/l10n_br_hr_arquivos_governo/__init__.py b/l10n_br_hr_arquivos_governo/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_br_hr_arquivos_governo/__openerp__.py b/l10n_br_hr_arquivos_governo/__openerp__.py new file mode 100644 index 000000000..167aa81cc --- /dev/null +++ b/l10n_br_hr_arquivos_governo/__openerp__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'l10n br hr Arquivos Governo', + 'version': '8.0.0.0.1', + 'license': 'AGPL-3', + 'author': 'KMEE, Odoo Community Association (OCA)', + 'maintainer': 'KMEE', + 'website': 'http://www.kmee.com.br', + 'depends': [ + 'document', + 'l10n_br_hr_payroll', + 'l10n_br_account', + 'l10n_br_financial_payment_order', + ], + 'external_dependencies': { + 'python': ['pybrasil'], + }, + 'data': [ + 'data/l10n_br_hr_contract_type.xml', + 'security/hr_payslip.xml', + 'security/ir.model.access.csv', + 'views/hr_payslip.xml', + 'views/hr_contract_type.xml', + 'views/l10n_br_hr_caged.xml', + 'views/l10n_br_hr_contract.xml', + 'views/l10n_br_hr_employee.xml', + 'views/l10n_br_hr_sefip.xml', + 'views/res_company.xml', + 'views/inherited_hr_salary_rule_view.xml' + ], +} diff --git a/l10n_br_hr_arquivos_governo/constantes_rh.py b/l10n_br_hr_arquivos_governo/constantes_rh.py new file mode 100644 index 000000000..4f2c6f8ba --- /dev/null +++ b/l10n_br_hr_arquivos_governo/constantes_rh.py @@ -0,0 +1,202 @@ +# -*- encoding: utf-8 -*- + +CATEGORIA_TRABALHADOR = ( + ('101', u'101 - Empregado – Geral'), + ('102', u'102 - Empregado – Trabalhador Rural por Pequeno Prazo da Lei ' + u'11.718/2008'), + ('103', u'103 - Empregado – Aprendiz'), + ('104', u'104 - Empregado – Doméstico'), + ('105', u'105 - Empregado – contrato a termo firmado nos termos da' + u' Lei 9601/98'), + ('106', u'106 - Empregado – contrato por prazo determinado nos termos da ' + u'Lei 6019/74'), + ('107', u'107 - Trabalhador não vinculado ao RGPS com direito ao FGTS'), + ('201', u'201 - Trabalhador Avulso – Portuário'), + ('202', u'202 - Trabalhador Avulso – Não Portuário ' + u'(Informação do Sindicato)'), + ('203', u'203 - Trabalhador Avulso – Não Portuário ' + u'(Informação do Contratante)'), + ('301', u'301 - Servidor Público – Titular de Cargo Efetivo'), + ('302', u'302 - Servidor Público – ' + u'Ocupante de Cargo exclusivo em comissão'), + ('303', u'303 - Servidor Público – Exercente de Mandato Eletivo'), + ('304', u'304 - Servidor Público – Agente Público'), + ('305', u'305 - Servidor Público vinculado a RPPS indicado para conselho ' + u'ou órgão representativo, na condição de representante do govern' + u'o, órgão ou entidade da administração pública'), + ('401', u'401 - Dirigente Sindical – Em relação a Remuneração Recebida no' + u' Sindicato'), + ('701', u'701 - Contrib. Individual – Autônomo contratado por Empresas em' + u' geral'), + ('702', u'702 - Contrib. Individual – Autônomo contratado por Contrib. ' + u'Individual, por pessoa física em geral, ou por missão ' + u'diplomática e repartição consular de carreira estrangeiras'), + ('703', u'703 - Contrib. Individual – Autônomo contratado por Entidade ' + u'Beneficente de Assistência Social isenta da cota patronal'), + ('704', u'704 - Excluído.'), + ('711', u'711 - Contrib. Individual – Transportador autônomo contratado' + u' por Empresas em geral'), + ('712', u'712 - Contrib. Individual – Transportador autônomo contratado' + u' por Contrib. Individual, por pessoa física em geral, ou por mis' + u'são diplomática e repartição consular de carreira estrangeiras'), + ('713', u'713 - Contrib. Individual – Transportador autônomo contratado' + u' por Entidade Beneficente de Assistência Social isenta da cota ' + u'patronal'), + ('721', u'721 - Contrib. Individual – Diretor não empregado com FGTS'), + ('722', u'722 - Contrib. Individual – Diretor não empregado sem FGTS'), + ('731', u'731 - Contrib. Individual – Cooperado que presta serviços a ' + u'empresa por intermédio de cooperativa de trabalho'), + ('732', u'732 - Contrib. Individual – Cooperado que presta serviços a ' + u'Entidade Beneficente de Assistência Social isenta da cota ' + u'patronal ou para pessoa física'), + ('733', u'733 - Contrib. Individual – Cooperado eleito para direção da ' + u'Cooperativa'), + ('734', u'734 - Contrib. Individual – Transportador Cooperado que presta' + u' serviços a empresa por intermédio de cooperativa de trabalho'), + ('735', u'735 - Contrib. Individual – Transportador Cooperado que presta' + u' serviços a Entidade Beneficente de Assistência Social isenta ' + u'da cota patronal ou para pessoa física'), + ('736', u'736 - Contrib. Individual – Transportador Cooperado eleito ' + u'para direção da Cooperativa'), + ('741', u'741 - Contrib. Individual – Cooperado filiado a cooperativa ' + u'de produção'), + ('751', u'751 - Contrib. Individual – Micro Empreendedor Individual, ' + u'quando contratado por PJ'), + ('901', u'901 - Estagiário'), +) +CATEGORIA_TRABALHADOR_DIC = dict(CATEGORIA_TRABALHADOR) + +SEFIP_CATEGORIA_TRABALHADOR = { + '701': '13', + '702': '13', + '703': '13', + '721': '11', + '722': '11', + '103': '07' +} + +MESES = [ + ('01', u'Janeiro'), + ('02', u'Fevereiro'), + ('03', u'Março'), + ('04', u'Abril'), + ('05', u'Maio'), + ('06', u'Junho'), + ('07', u'Julho'), + ('08', u'Agosto'), + ('09', u'Setembro'), + ('10', u'Outubro'), + ('11', u'Novembro'), + ('12', u'Dezembro'), + ('13', u'13º Salário'), +] + +MODALIDADE_ARQUIVO = [ + (' ', u'Recolhimento ao FGTS e Declaração à Previdência'), + ('1', u'Declaração ao FGTS e à Previdência'), + ('9', u'Confirmação Informações anteriores – Rec/Decl ao FGTS e' + u' Decl à Previdência'), +] + +CODIGO_RECOLHIMENTO = [ + ('115', u'115 - Recolhimento ao FGTS e informações à Previdência Social'), + ('130', u'130 - Recolhimento ao FGTS e informações à Previdência Social ' + u'relativas ao trabalhador avulso portuário'), + ('135', u'135 - Recolhimento e/ou declaração ao FGTS e informações à ' + u'Previdência Social relativas ao trabalhador avulso não ' + u'portuário'), + ('145', u'145 - Recolhimento ao FGTS de diferenças apuradas pela CAIXA'), + ('150', u'150 - Recolhimento ao FGTS e informações à Previdência Social ' + u'de empresa prestadora de serviços com cessão de mâo-de-obra e ' + u'empresa de trabalho temporário Lei nº 6.019/74, em relação aos ' + u'empregados cedidos, ou de obra de construção civil ' + u'- empreitada parcial'), + ('155', u'155 - Recolhimento ao FGTS e informações à Previdência Social ' + u'de obra de construção civil - empreitada total ou obra própria'), + ('211', u'211 - Declaração para a Previdência Social de Cooperativa de ' + u'Trabalho relativa aos contribuintes individuais cooperados que ' + u'prestam serviçõs a tomadores'), + ('307', u'307 - Recolhimento de Parcelamento de débito com o FGTS'), + ('317', u'317 - Recolhimento de Parcelamento de débito com o FGTS de ' + u'empresa com tomador de serviços'), + ('327', u'327 - Recolhimento de Parcelamento de débito com o FGTS ' + u'priorizando os valores devidos aos trabalhores'), + ('337', u'337 - Recolhimento de Parcelamento de débito com o FGTS de ' + u'empresas com tomador de serviços, priorizando os valores devidos' + u' aos trabalhadores'), + ('345', u'345 - Recolhimento de parcelamento de débito com o FGTS relativo' + u' a diferença de recolhimento, priorizando os valores devidos ' + u'aos trabalhadores'), + ('418', u'418 - Recolhimento recursal para o FGTS'), + ('604', u'604 - Recolhimento ao FGTS de entidades com fins filantrópicos ' + u'- Decreto-Lei nº194, de 24/02/1967 (competências anteriores ' + u'a 10/1989'), + ('608', u'608 - Recolhimento ao FGTS e informações à Previdência Social ' + u'relativo a dirigente sindical'), + ('640', u'640 - Recolhimento ao FGTS para empregado não optante ' + u'(competência anterior a 10/1988)'), + ('650', u'650 - Recolhimento ao FGTS e Informações à Previdência Social' + u' relativo a Anistiados, Reclamatória Trabalhista, Reclamatória ' + u'Trabalhista com reconhecimento de vínculo, Acordo ou Dissídio ' + u'ou Convenção Coletiva, Comissão Conciliação Prévia ou Núcleo' + u' Intersindical Conciliação Trabalhista'), + ('660', u'660 - Recolhimento exclusivo ao FGTS relativo a Anistiados,' + u' Conversão Licença Saúde em Acidente Trabalho, Reclamatória ' + u'Trabalhista, Acordo ou Dissídio ou Convenção Coletiva, ' + u'Comissão Conciliação Prévia ou Núcleo Intersindical ' + u'Conciliação Trabalhista'), +] +RECOLHIMENTO_FGTS = [ + ('1', u'1-GRF no prazo'), + ('2', u'2-GRF em atraso'), + ('3', u'3-GRF em atraso - Ação Fiscal'), + ('5', u'5-Individualização'), + ('6', u'6-Individualização - Ação Fiscal'), + (' ', u'Em branco'), +] +RECOLHIMENTO_GPS = [ + ('1', u'1-GPF no prazo'), + ('2', u'2-GPF em atraso'), + ('3', u'3-Não gera GPS'), +] +CENTRALIZADORA = [ + ('0', u'0 - Não centraliza'), + ('1', u'1 - Centralizadora'), + ('2', u'2 - Centralizada'), +] + + +OCORRENCIA_SEFIP = [ + ('01', u'01 - Não exposição a agente nocivo'), + ('02', u'Exposição a agente nocivo (aposentadoria especial aos ' + u'15 anos de trabalho)'), + ('03', u'Exposição a agente nocivo (aposentadoria especial aos ' + u'20 anos de trabalho)'), + ('04', u'Exposição a agente nocivo (aposentadoria especial aos ' + u'25 anos de trabalho)'), + ('05', u'Mais de um vínculo empregatício (ou fonte pagadora) - ' + u'Não exposição a agente nocivo'), + ('06', u'Mais de um vínculo empregatício (ou fonte pagadora) - ' + u'Exposição a agente nocivo (aposentadoria especial aos ' + u'15 anos de trabalho)'), + ('07', u'Mais de um vínculo empregatício (ou fonte pagadora) - ' + u'Exposição a agente nocivo (aposentadoria especial aos ' + u'20 anos de trabalho)'), + ('08', u'Mais de um vínculo empregatício (ou fonte pagadora) - ' + u'Exposição a agente nocivo (aposentadoria especial aos ' + u'25 anos de trabalho)'), +] + + +CATEGORIA_TRABALHADOR_SEFIP = [ + ('01', u'01 - Empregado'), + ('05', u'05 - Contribuinte individual - Diretor não empregado com FGTS – ' + u'Lei nº 8.036/90, art. 16'), + ('07', u'07 - Menor aprendiz - Lei n°10.097/2000.'), + ('11', u'11 - Contribuinte Individual - Diretor não empregado e demais ' + u'empresários sem FGTS.'), + ('13', u'13 - Contribuinte individual – Trabalhador autônomo ou a este ' + u'equiparado, inclusive o operador de máquina, com contribuição ' + u'sobre remuneração; trabalhador associado à cooperativa de ' + u'produção.'), +] diff --git a/l10n_br_hr_arquivos_governo/data/l10n_br_hr_contract_type.xml b/l10n_br_hr_arquivos_governo/data/l10n_br_hr_contract_type.xml new file mode 100644 index 000000000..306b029a1 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/data/l10n_br_hr_contract_type.xml @@ -0,0 +1,17 @@ + + + + + + + Prazo Indeterminado + 1 + + + + Prazo Determinado + 2 + + + + diff --git a/l10n_br_hr_arquivos_governo/models/__init__.py b/l10n_br_hr_arquivos_governo/models/__init__.py new file mode 100644 index 000000000..6cce1a483 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/__init__.py @@ -0,0 +1,14 @@ +from . import abstract_workflow +from . import abstract_arquivos_governo +from . import arquivo_caged +from . import arquivo_grrf +from . import arquivo_seguro_desemprego +from . import hr_contract_type +from . import hr_payslip +from . import financial_move +from . import l10n_br_hr_caged +from . import l10n_br_hr_contract +from . import l10n_br_hr_payslip +from . import res_company +from . import l10n_br_hr_sefip +from . import inherited_hr_salary_rule diff --git a/l10n_br_hr_arquivos_governo/models/abstract_arquivos_governo.py b/l10n_br_hr_arquivos_governo/models/abstract_arquivos_governo.py new file mode 100644 index 000000000..654c19811 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/abstract_arquivos_governo.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE- Hendrix Costa +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import logging +import re + +_logger = logging.getLogger(__name__) + +try: + from pybrasil.base import tira_acentos + from pybrasil import data +except ImportError: + _logger.info('Cannot import pybrasil') + + +class AbstractArquivosGoverno(object): + + def _gerar_arquivo_temp(self, text, tipo): + """ + Dado um texto, criar um arquivo temporario, escrever nesse arquivo + fechar o arquivo e retornar o path do arquivo criado + :param text: + :param tipo: + :return: + """ + arq = open('/tmp/' + tipo, 'w') + arq.write(text.encode('utf-8')) + arq.close() + return '/tmp/' + tipo + + def _validar(self, word, tam, tipo='AN'): + """ + Função Genérica utilizada para validação de campos que são gerados + nos arquivos TXT's + :param tipo: str - Tipo de palavras validadas: + - A -> Alfabéticas -> Apenas letras do alfabeto + - D -> Data -> Apenas numeral + - V -> Valor -> Valores decimais, retirando a virgula + - N -> Numerico -> Apenas numeros preechidos com zero a esq. + - AN -> Alfanumericos -> Aceita nuemros e caracateres sem acentos + :param word: str - palavra a ser validada + :param tam: int - Tamanho que a palavra deve ser + :return: str - Palavra formatada de acordo com tipo e tamanho + """ + if not word: + word = u'' + + if tipo == 'A': # Alfabetico + word = tira_acentos(word) + # tirar tudo que nao for letra do alfabeto + word = re.sub('[^a-zA-Z]', ' ', word) + # Retirar 2 espaços seguidos + word = re.sub('[ ]+', ' ', word) + return unicode.ljust(unicode(word), tam)[:tam] + + elif tipo == 'D': # Data + # Retira tudo que nao for numeral + word = data.formata_data(word) + word = re.sub(u'[^0-9]', '', str(word)) + return unicode.ljust(unicode(word), tam)[:tam] + + elif tipo == 'V': # Valor + # Pega a parte decimal como inteiro e nas duas ultimas casas + word = int(word * 100) if word else 0 + # Preenche com zeros a esquerda + word = str(word).zfill(tam) + return word[:tam] + + elif tipo == 'N': # Numerico + # Preenche com zeros a esquerda + word = re.sub('[^0-9]', '', str(word)) + word = str(word).zfill(tam) + return word[:tam] + + elif tipo == 'AN': # Alfanumerico + # Tira acentos da palavras + word = tira_acentos(word) + # Preenche com espaço vazio a esquerda + return unicode.ljust(unicode(word), tam)[:tam] diff --git a/l10n_br_hr_arquivos_governo/models/abstract_workflow.py b/l10n_br_hr_arquivos_governo/models/abstract_workflow.py new file mode 100644 index 000000000..0e34023f5 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/abstract_workflow.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE INFORMATICA LTDA - Daniel Sadamo +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from __future__ import ( + division, print_function, unicode_literals, absolute_import +) + +from openerp import api, fields, models, _ +from openerp.exceptions import Warning as UserError + +STATE = [ + ('draft', u'Rascunho'), + ('open', u'Confirmada'), + ('sent', u'Enviado'), +] + + +class AbstractArquivosGovernoWorkflow(models.AbstractModel): + _name = b'abstract.arquivos.governo.workflow' + + state = fields.Selection( + selection=STATE, index=True, + readonly=True, default='draft', + track_visibility='onchange', copy=False + ) + + @api.model + def _avaliable_transition(self, old_state, new_state): + allowed = [ + ('draft', 'open'), + ('open', 'draft'), + ('open', 'sent'), + ] + return (old_state, new_state) in allowed + + @api.multi + def change_state(self, new_state): + for record in self: + if record._avaliable_transition(record.state, new_state): + record.state = new_state + else: + raise UserError(_("This state transition is not allowed")) + + @api.multi + def action_draft(self): + for record in self: + record.change_state('draft') + + @api.multi + def action_open(self): + for record in self: + record.change_state('open') + + @api.multi + def action_sent(self): + for record in self: + record.change_state('sent') + + @api.multi + def unlink(self): + for record in self: + if record.state not in ['draft']: + raise UserError( + _('You cannot delete a record which is not ' + 'draft or cancelled state!') + ) + return super(AbstractArquivosGovernoWorkflow, self).unlink() diff --git a/l10n_br_hr_arquivos_governo/models/arquivo_caged.py b/l10n_br_hr_arquivos_governo/models/arquivo_caged.py new file mode 100644 index 000000000..7ac90d2f7 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/arquivo_caged.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE- Hendrix Costa +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from datetime import datetime + +from .abstract_arquivos_governo import AbstractArquivosGoverno + + +class Caged(AbstractArquivosGoverno): + # Registro do estabelecimento responsável pela informação no + # meio magnético (autorizado). + + def _registro_A(self): + registro_A = self.A_tipo_de_registro + registro_A += self.A_tipo_layout + registro_A += str.ljust('', 3) + registro_A += self._validar(self.A_competencia, 6, 'N') + registro_A += self._validar(self.A_alteracao, 1, 'N') + registro_A += self._validar(self.A_sequencia, 5, 'N') + registro_A += self._validar(self.A_tipo_identificador, 1, 'N') + registro_A += self._validar(self.A_identificador_autorizado, 14, 'N') + registro_A += self._validar(self.A_razao_social, 35, 'AN') + registro_A += self._validar(self.A_endereco, 40, 'AN') + registro_A += self._validar(self.A_cep, 8, 'N') + registro_A += self._validar(self.A_uf, 2, 'A') + registro_A += self._validar(self.A_ddd, 4, 'N') + registro_A += self._validar(self.A_telefone, 8, 'N') + registro_A += self._validar(self.A_ramal, 5, 'N') + registro_A += \ + self._validar(self.A_total_estabelecimento_informados, 5, 'N') + registro_A += \ + self._validar(self.A_total_movimentacoes_informados, 5, 'N') + registro_A += str.ljust('', 92) + registro_A += '\n' + return registro_A + + # Informações da Empresa + def _registro_B(self): + registro_B = self.B_tipo_de_registro + registro_B += self._validar(self.B_tipo_identificador, 1, 'N') + registro_B += \ + self._validar(self.B_identificador_estabelecimento, 14, 'N') + registro_B += self._validar(self.B_sequencia, 5, 'N') + registro_B += self._validar(self.B_primeira_declaracao, 1, 'N') + registro_B += self._validar(self.B_alteracao, 1, 'N') + registro_B += self._validar(self.B_cep, 8, 'N') + registro_B += str.ljust('', 5) + registro_B += self._validar(self.B_razao_social, 40, 'A') + registro_B += self._validar(self.B_endereco, 40, 'AN') + registro_B += self._validar(self.B_bairro, 20, 'A') + registro_B += self._validar(self.B_uf, 2, 'A') + registro_B += self._validar(self.B_total_empregados_existentes, 5, 'N') + registro_B += self._validar(self.B_porte_estabelecimento, 1, 'N') + registro_B += self._validar(self.B_CNAE, 7, 'N') + registro_B += self._validar(self.B_ddd, 4, 'N') + registro_B += self._validar(self.B_telefone, 8, 'N') + registro_B += self._validar(self.B_email, 50, 'AN') + registro_B += str.ljust('', 27) + registro_B += '\n' + return registro_B + + # Informações do trabalhador + def _registro_C(self): + registro_C = self.C_tipo_de_registro + registro_C += self._validar(self.C_tipo_identificador, 1, 'N') + registro_C += \ + self._validar(self.C_identificador_estabelecimento, 14, 'N') + registro_C += self._validar(self.C_sequencia, 5, 'N') + registro_C += self._validar(self.C_PIS_PASEP, 11, 'N') + registro_C += self._validar(self.C_sexo, 1, 'N') + registro_C += self._validar(self.C_nascimento, 8, 'D') + registro_C += self._validar(self.C_grau_instrucao, 2, 'N') + registro_C += str.ljust('', 4) + registro_C += self._validar(self.C_salario_mensal, 8, 'V') + registro_C += self._validar(self.C_horas_trabalhadas, 2, 'N') + registro_C += self._validar(self.C_admissao, 8, 'D') + registro_C += self._validar(self.C_tipo_de_movimento, 2, 'N') + registro_C += self._validar(self.C_dia_desligamento, 2, 'N') + registro_C += self._validar(self.C_nome_empregado, 40, 'A') + registro_C += self._validar(self.C_numero_ctps, 8, 'N') + registro_C += self._validar(self.C_serie_ctps, 4, 'N') + registro_C += str.ljust('', 7) + registro_C += self._validar(self.C_raca_cor, 1, 'N') + registro_C += self._validar(self.C_pessoas_com_deficiencia, 1, 'N') + registro_C += self._validar(self.C_cbo2000, 6, 'N') + registro_C += self._validar(self.C_aprendiz, 1, 'N') + registro_C += self._validar(self.C_uf_ctps, 2, 'A') + registro_C += self._validar(self.C_tipo_deficiencia, 1, 'AN') + registro_C += self._validar(self.C_CPF, 11, 'N') + registro_C += self._validar(self.C_cep_residencia, 8, 'N') + registro_C += str.ljust('', 81) + registro_C += '\n' + return registro_C + + def _registro_X(self): + registro_X = self.X_tipo_de_registro + registro_X += self._validar(self.X_tipo_identificador, 1, 'N') + registro_X += self.\ + _validar(self.X_identificador_estabelecimento, 14, 'N') + registro_X += self._validar(self.X_sequencia, 5, 'N') + registro_X += self._validar(self.X_PIS_PASEP, 11, 'N') + registro_X += self._validar(self.X_sexo, 1, 'N') + registro_X += self._validar(self.X_nascimento, 8, 'N') + registro_X += self._validar(self.X_grau_instrucao, 2, 'N') + registro_X += str.ljust('', 4) + registro_X += self._validar(self.X_salario_mensal, 8, 'V') + registro_X += self._validar(self.X_horas_trabalhadas, 2, 'N') + registro_X += self._validar(self.X_admissao, 8, 'D') + registro_X += self._validar(self.X_tipo_de_movimento, 2, 'N') + registro_X += self._validar(self.X_dia_desligamento, 2, 'N') + registro_X += self._validar(self.X_nome_empregado, 40, 'A') + registro_X += self._validar(self.X_numero_ctps, 8, 'N') + registro_X += self._validar(self.X_serie_ctps, 4, 'N') + registro_X += self._validar(self.X_uf_ctps, 2, 'A') + registro_X += self._validar(self.X_atualizacao, 1, 'N') + registro_X += self._validar(self.X_competencia, 6, 'N') + registro_X += self._validar(self.X_raca_cor, 1, 'N') + registro_X += self._validar(self.X_pessoas_com_deficiencia, 1, 'N') + registro_X += self._validar(self.X_cbo2000, 6, 'N') + registro_X += self._validar(self.X_aprendiz, 1, 'N') + registro_X += self._validar(self.X_tipo_deficiencia, 1, 'A') + registro_X += self._validar(self.X_CPF, 11, 'N') + registro_X += self._validar(self.X_cep_residencia, 8, 'AN') + registro_X += str.ljust('', 81) + registro_X += '\n' + return registro_X + + def _registro_Z(self): + registro_Z = self.Z_tipo_de_registro + registro_Z += self._validar(self.Z_responsavel, 40, 'AN') + registro_Z += self._validar(self.Z_email_responsavel, 50, 'AN') + registro_Z += ''.rjust(7, '0') + registro_Z += self._validar(self.Z_cpf_responsavel, 11, 'N') + registro_Z += str.ljust('', 122) + registro_Z += ''.rjust(9, '0') + registro_Z += '\n' + return registro_Z + + def _gerar_grrf(self): + return \ + self._registro_A() + \ + self._registro_B() + \ + self._registro_C() + \ + self._registro_X() + \ + self._registro_Z() + + def __init__(self, *args, **kwargs): + + # campos do Registro A (AUTORIZADO) ----------------------------------- + # Registro do estabelecimento responsável pela informação (Autorizado) + self.A_tipo_de_registro = 'A' + self.A_tipo_layout = 'L2009' + self.A_competencia = '' + self.A_alteracao = '' + self.A_sequencia = '' + self.A_tipo_identificador = '' + self.A_identificador_autorizado = '' + self.A_razao_social = '' + self.A_endereco = '' + self.A_cep = '' + self.A_uf = '' + self.A_ddd = '' + self.A_telefone = '' + self.A_ramal = '' + self.A_total_estabelecimento_informados = '' + self.A_total_movimentacoes_informados = '' + # --------------------------------------------------------------------- + + # campos do REGISTRO B (ESTABELECIMENTO) ------------------------------ + # dados cadastrais do estabelecimento que teve movimentação + self.B_tipo_de_registro = 'B' + self.B_tipo_identificador = '' + self.B_identificador_estabelecimento = '' + self.B_sequencia = '' + self.B_primeira_declaracao = '' + self.B_alteracao = '' + self.B_cep = '' + self.B_razao_social = '' + self.B_endereco = '' + self.B_bairro = '' + self.B_uf = '' + self.B_total_empregados_existentes = '' + self.B_porte_estabelecimento = '' + self.B_CNAE = '' + self.B_ddd = '' + self.B_telefone = '' + self.B_email = '' + # --------------------------------------------------------------------- + + # campos do REGISTRO C (MOVIMENTAÇÃO) --------------------------------- + self.C_tipo_de_registro = 'C' + self.C_tipo_identificador = '' + self.C_identificador_estabelecimento = '' + self.C_sequencia = '' + self.C_PIS_PASEP = '' + self.C_sexo = '' + self.C_nascimento = '' + self.C_grau_instrucao = '' + self.C_salario_mensal = '' + self.C_horas_trabalhadas = '' + self.C_admissao = '' + self.C_tipo_de_movimento = '' + self.C_dia_desligamento = '' + self.C_nome_empregado = '' + self.C_numero_ctps = '' + self.C_serie_ctps = '' + self.C_uf_ctps = '' + self.C_raca_cor = '' + self.C_pessoas_com_deficiencia = '' + self.C_cbo2000 = '' + self.C_aprendiz = '' + self.C_tipo_deficiencia = '' + self.C_CPF = '' + self.C_cep_residencia = '' + # --------------------------------------------------------------------- + + # campos do REGISTRO X (ACERTO) --------------------------------------- + self.X_tipo_de_registro = 'X' + self.X_tipo_identificador = '' + self.X_identificador_estabelecimento = '' + self.X_sequencia = '' + self.X_PIS_PASEP = '' + self.X_sexo = '' + self.X_nascimento = '' + self.X_grau_instrucao = '' + self.X_salario_mensal = '' + self.X_horas_trabalhadas = '' + self.X_admissao = '' + self.X_tipo_de_movimento = '' + self.X_dia_desligamento = '' + self.X_nome_empregado = '' + self.X_numero_ctps = '' + self.X_serie_ctps = '' + self.X_uf_ctps = '' + self.X_atualizacao = '' + self.X_competencia = '' + self.X_raca_cor = '' + self.X_pessoas_com_deficiencia = '' + self.X_cbo2000 = '' + self.X_aprendiz = '' + self.X_tipo_deficiencia = '' + self.X_CPF = '' + self.X_cep_residencia = '' + # --------------------------------------------------------------------- + + # campos do REGISTRO Z (Responsavel preenchimento) -------------------- + self.Z_tipo_de_registro = 'Z' + self.Z_responsavel = '' + self.Z_email_responsavel = '' + self.Z_cpf_responsavel = '' + + def _validar(self, word, tam, tipo='AN'): + """ + Sobrescrever a função de validacao de palavras da classe abstrata para + obter todas as palavras do caged em maiusculas. + """ + if tipo in ['AN', 'A'] and word: + word = word.upper() + if tipo in ['D'] and not word: + word = datetime.now().strftime("%Y-%m-%d") + return super(Caged, self)._validar(word, tam, tipo) diff --git a/l10n_br_hr_arquivos_governo/models/arquivo_grrf.py b/l10n_br_hr_arquivos_governo/models/arquivo_grrf.py new file mode 100644 index 000000000..54ba0f909 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/arquivo_grrf.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE- Hendrix Costa +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from .abstract_arquivos_governo import AbstractArquivosGoverno + + +class Grrf(AbstractArquivosGoverno): + + # Informações do Responsavel + def _registro_00(self): + registro_00 = self.tipo_de_registro_00 + registro_00 += str.ljust('', 51) + registro_00 += self._validar(self.tipo_de_remessa, 1, 'N') + registro_00 += \ + self._validar(self.tipo_inscricao_responsavel, 1, 'N') \ + if self.tipo_inscricao_responsavel \ + else self._validar(self.self.tipo_de_inscricao_empresa, 1, 'N') + registro_00 += self._validar(self.inscricao_do_responsavel, 14, 'N') \ + if self.inscricao_do_responsavel \ + else self._validar(self.inscricao_da_empresa, 14, 'N') + registro_00 += self._validar(self.razao_social_responsavel, 30, 'AN') \ + if self.razao_social_responsavel \ + else self._validar(self.razao_social_empresa, 30, 'AN') + registro_00 += self._validar(self.nome_do_contato_responsavel, 20, 'A') + registro_00 += self._validar(self.endereco_responsavel, 50) \ + if self.endereco_responsavel \ + else self._validar(self.endereco_empresa, 50) + registro_00 += self._validar(self.bairro_responsavel, 20) \ + if self.bairro_responsavel \ + else self._validar(self.bairro_empresa, 20) + registro_00 += self._validar(self.cep_responsavel, 8, 'N') \ + if self.cep_responsavel \ + else self._validar(self.cep_empresa, 8, 'N') + registro_00 += self._validar(self.cidade_responsavel, 20, 'AN') \ + if self.cidade_responsavel \ + else self._validar(self.cidade_empresa, 20, 'AN') + registro_00 += \ + self._validar(self.unidade_federacao_responsavel, 2, 'A') \ + if self.unidade_federacao_responsavel \ + else self._validar(self.unidade_federacao_empresa, 2, 'A') + registro_00 += \ + self._validar(self.telefone_contato_responsavel, 12, 'N') + registro_00 += self._validar(self.endereco_internet_responsavel, 60) + registro_00 += self._validar(self.data_recolhimento_grrf, 8, 'D') + registro_00 += str.ljust('', 60) + registro_00 += self.final_de_linha + return registro_00 + + # Informações da Empresa + def _registro_10(self): + registro_10 = self.tipo_de_registro_10 + registro_10 += self._validar(self.tipo_de_inscricao_empresa, 1, 'N') + registro_10 += self._validar(self.inscricao_da_empresa, 14, 'N') + registro_10 += ''.rjust(36, '0') + registro_10 += self._validar(self.razao_social_empresa, 40, 'AN') + registro_10 += self._validar(self.endereco_empresa, 50, 'AN') + registro_10 += self._validar(self.bairro_empresa, 20, 'AN') + registro_10 += self._validar(self.cep_empresa, 8, 'N') + registro_10 += self._validar(self.cidade_empresa, 20, 'AN') + registro_10 += self._validar(self.unidade_federacao_empresa, 2, 'A') + registro_10 += self._validar(self.telefone_empresa, 12, 'N') + registro_10 += self._validar(self.CNAE_fiscal, 7, 'N') + registro_10 += self._validar(self.simples, 1, 'N') + registro_10 += self._validar(self.fpas, 3, 'N') + registro_10 += str.ljust('', 143) + registro_10 += self.final_de_linha + return registro_10 + + # Informações do trabalhador + def _registro_40(self): + registro_40 = self.tipo_de_registro_40 + registro_40 += \ + self._validar(self.tipo_de_inscricao_trabalhador, 1, 'N') \ + if self.tipo_de_inscricao_trabalhador \ + else self._validar(self.tipo_de_inscricao_empresa, 1, 'N') + registro_40 += self._validar(self.inscricao_do_trabalhador, 14, 'N') \ + if self.inscricao_do_trabalhador \ + else self._validar(self.inscricao_da_empresa, 14, 'N') + registro_40 += self._validar(self.tipo_inscricao_tomador, 1, 'N') + registro_40 += self._validar(self.inscricao_tomador, 14, 'N') + registro_40 += self._validar(self.PIS_PASEP, 11, 'N') + registro_40 += self._validar(self.data_admissao, 8, 'D') + registro_40 += self._validar(self.categoria_trabalhador, 2, 'N') + registro_40 += self._validar(self.nome_do_trabalhador, 70, 'A') + registro_40 += self._validar(self.numero_ctps, 7, 'N') + registro_40 += self._validar(self.serie_ctps, 5, 'N') + registro_40 += self._validar(self.sexo, 1, 'N') + registro_40 += self._validar(self.grau_de_instrucao, 2, 'N') + registro_40 += self._validar(self.data_nascimento, 8, 'D') + registro_40 += self._validar(self.qtd_horas_trabalhadas_semana, 2, 'N') + registro_40 += self._validar(self.CBO, 6, 'AN') + registro_40 += self._validar(self.data_opcao, 8, 'D') + registro_40 += self._validar(self.codigo_da_movimentacao, 2, 'AN') + registro_40 += self._validar(self.data_movimentacao, 8, 'D') + registro_40 += self._validar(self.codigo_de_saque, 3, 'AN') + registro_40 += self._validar(self.aviso_previo, 1, 'N') + registro_40 += self._validar(self.data_inicio_aviso_previo, 8, 'D') + registro_40 += self._validar(self.reposicao_de_vaga, 1, 'A') + registro_40 += self._validar(self.data_homologacao_dissidio, 8, 'D') + registro_40 += self._validar(self.valor_dissidio, 15, 'V') + registro_40 += self._validar(self.remuneracao_mes_aterior, 15, 'V') + registro_40 += self._validar(self.remuneracao_mes_rescisao, 15, 'V') + registro_40 += self._validar(self.aviso_previo_indenizado, 15, 'V') + registro_40 += \ + self._validar(self.indicativo_pensao_alimenticia, 1, 'A') + registro_40 += \ + self._validar(self.percentual_pensao_alimenticia, 5, 'V') + registro_40 += self._validar(self.valor_pensao_alimenticia, 15, 'V') + registro_40 += self._validar(self.CPF, 11, 'N') + registro_40 += self._validar(self.banco_conta_trabalhador, 3, 'N') + registro_40 += self._validar(self.agencia_trabalhador, 4, 'N') + registro_40 += self._validar(self.conta_trabalhador, 13, 'N') + registro_40 += self._validar(self.saldo_para_fins_rescisorios, 15, 'N') + registro_40 += str.ljust('', 39) + registro_40 += self.final_de_linha + return registro_40 + + def _registro_90(self): + registro_90 = self.tipo_de_registro_90 + registro_90 += self.marca_final_de_registro + registro_90 += str.ljust('', 306) + registro_90 += self.final_de_linha + return registro_90 + + def _gerar_grrf(self): + return \ + self._registro_00() + \ + self._registro_10() + \ + self._registro_40() + \ + self._registro_90() + + # campos do registro 00 --------------------------------------------------- + tipo_de_registro_00 = u'00' # sempre '00' + tipo_de_remessa = u'2' # 2 - GRRF | 4 - Comunicar movimentação + tipo_inscricao_responsavel = u'1' # 1 - CNPJ | 2 - CEI + inscricao_do_responsavel = '' # CNPJ | CEI + razao_social_responsavel = '' + nome_do_contato_responsavel = '' + endereco_responsavel = '' + bairro_responsavel = '' + cep_responsavel = '' + cidade_responsavel = '' + unidade_federacao_responsavel = '' + telefone_contato_responsavel = '' + endereco_internet_responsavel = '' + data_recolhimento_grrf = '' + final_de_linha = u'*' + # ------------------------------------------------------------------------- + + # campos do registro 10 --------------------------------------------------- + tipo_de_registro_10 = u'10' # sempre '10' + tipo_de_inscricao_empresa = u'1' + inscricao_da_empresa = '' # CNPJ | CEI + razao_social_empresa = '' + endereco_empresa = '' + bairro_empresa = '' + cep_empresa = '' + cidade_empresa = '' + unidade_federacao_empresa = '' + telefone_empresa = '' + CNAE_fiscal = '' + simples = '' + fpas = '' + # ------------------------------------------------------------------------ + + # campos do registro 40 --------------------------------------------------- + tipo_de_registro_40 = u'40' # sempre '40' + tipo_de_inscricao_trabalhador = u'1' + inscricao_do_trabalhador = '' + tipo_inscricao_tomador = '' + inscricao_tomador = '' + PIS_PASEP = '' + data_admissao = '' # DDMMAAAA + categoria_trabalhador = u'01' + nome_do_trabalhador = '' + numero_ctps = '' + serie_ctps = '' + sexo = '' + grau_de_instrucao = '' + data_nascimento = '' + qtd_horas_trabalhadas_semana = '' + CBO = '' + data_opcao = '' + codigo_da_movimentacao = '' + data_movimentacao = '' + codigo_de_saque = '' + aviso_previo = '' + data_inicio_aviso_previo = '' + reposicao_de_vaga = '' + data_homologacao_dissidio = '' + valor_dissidio = '' + remuneracao_mes_aterior = '' + remuneracao_mes_rescisao = '' + aviso_previo_indenizado = '' + indicativo_pensao_alimenticia = '' + percentual_pensao_alimenticia = '' + valor_pensao_alimenticia = '' + CPF = '' + banco_conta_trabalhador = '' + agencia_trabalhador = '' + conta_trabalhador = '' + saldo_para_fins_rescisorios = '' + # ------------------------------------------------------------------------ + + # campos do registro 90 --------------------------------------------------- + tipo_de_registro_90 = u'90' # sempre '90' + marca_final_de_registro = ''.rjust(51, '9') + # ------------------------------------------------------------------------ diff --git a/l10n_br_hr_arquivos_governo/models/arquivo_sefip.py b/l10n_br_hr_arquivos_governo/models/arquivo_sefip.py new file mode 100644 index 000000000..0341f2436 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/arquivo_sefip.py @@ -0,0 +1,498 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE INFORMATICA LTDA - Daniel Sadamo +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import logging +from .abstract_arquivos_governo import AbstractArquivosGoverno +import re + +_logger = logging.getLogger(__name__) + +try: + from pybrasil.base import tira_acentos +except ImportError: + _logger.info('Cannot import pybrasil') + + +class SEFIP(AbstractArquivosGoverno): + + def _registro_00_informacoes_responsavel(self): + registro_00 = self.tipo_de_registro_00 + registro_00 += self._validar(self.preenche_brancos, 51, 'AN') + registro_00 += self._validar(self.tipo_remessa, 1, 'N') + registro_00 += self._validar(self.tipo_inscr_resp, 1, 'N') + registro_00 += self._validar(self.inscr_resp, 14, 'N') + registro_00 += self._validar(self.nome_resp, 30, 'AN') + registro_00 += self._validar(self.nome_contato, 20, 'A') + registro_00 += self._validar(self.arq_logradouro, 50, 'AN') + registro_00 += self._validar(self.arq_bairro, 20, 'AN') + registro_00 += self._validar(self.arq_cep, 8, 'N') + registro_00 += self._validar(self.arq_cidade, 20, 'AN') + registro_00 += self._validar(self.arq_uf, 2, 'A') + registro_00 += self._validar(self.tel_contato, 12, 'N') + registro_00 += self._validar(self.internet_contato, 60, 'AN') + registro_00 += self._validar(self.competencia, 6, 'D') + registro_00 += self._validar(self.cod_recolhimento, 3, 'N') + registro_00 += self._validar(self.indic_recolhimento_fgts, 1, 'N') + registro_00 += self._validar(self.modalidade_arq, 1, 'N') + if self.indic_recolhimento_fgts in ['2', '3', '5', '6']: + registro_00 += self._validar(self.data_recolhimento_fgts, 8, 'D') + else: + registro_00 += self._validar(False, 8, 'D') + registro_00 += self._validar(self.indic_recolh_ps, 1, 'N') + if self.indic_recolh_ps == '2': + registro_00 += self._validar(self.data_recolh_ps, 8, 'D') + else: + registro_00 += self._validar(False, 8, 'D') + registro_00 += self._validar(self.indice_recolh_atraso_ps, 7, 'N') + registro_00 += self._validar(self.tipo_inscr_fornec, 1, 'N') + registro_00 += self._validar(self.inscr_fornec, 14, 'N') + registro_00 += self._validar(self.preenche_brancos, 18, 'AN') + registro_00 += self._validar(self.fim_linha, 1, 'AN') + return registro_00 + + def _registro_10_informacoes_empresa(self): + registro_10 = self.tipo_de_registro_10 + registro_10 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_10 += self._validar(self.inscr_empresa, 14, 'N') + registro_10 += self._validar(self.preenche_zeros, 36, 'V') + registro_10 += self._validar(self.emp_nome_razao_social, 40, 'AN') + registro_10 += self._validar(self.emp_logradouro, 50, 'AN') + registro_10 += self._validar(self.emp_bairro, 20, 'AN') + registro_10 += self._validar(self.emp_cep, 8, 'N') + registro_10 += self._validar(self.emp_cidade, 20, 'AN') + registro_10 += self._validar(self.emp_uf, 2, 'A') + registro_10 += self._validar(self.emp_tel, 12, 'N') + registro_10 += self._validar(self.emp_indic_alteracao_endereco, 1, 'A') + registro_10 += self._validar(self.emp_cnae, 7, 'N') + registro_10 += self._validar(self.emp_indic_alteracao_cnae, 1, 'A') + registro_10 += self._validar(self.emp_aliquota_RAT, 2, 'N') + registro_10 += self._validar(self.emp_cod_centralizacao, 1, 'N') + registro_10 += self._validar(self.emp_simples, 1, 'N') + registro_10 += self._validar(self.emp_FPAS, 3, 'N') + registro_10 += self._validar(self.emp_cod_outras_entidades, 4, 'N') + registro_10 += self._validar(self.emp_cod_pagamento_GPS, 4, 'N') + registro_10 += self._validar( + self.emp_percent_isencao_filantropia, 5, 'N') + registro_10 += self._validar(self.emp_salario_familia, 15, 'V') + registro_10 += self._validar(self.emp_salario_maternidade, 15, 'V') + registro_10 += self._validar( + self.emp_contrib_descont_empregados, 15, 'V') + registro_10 += self._validar(self.emp_indic_valor_pos_neg, 1, 'V') + registro_10 += self._validar( + self.emp_valor_devido_ps_referente, 14, 'V') + registro_10 += self._validar(self.emp_banco, 3, 'N') + registro_10 += self._validar(self.emp_ag, 4, 'N') + registro_10 += self._validar(self.emp_cc, 9, 'AN') + registro_10 += self._validar(self.preenche_zeros, 45, 'V') + registro_10 += self._validar(self.preenche_brancos, 4, 'AN') + registro_10 += self._validar(self.fim_linha, 1, 'AN') + return registro_10 + + def _registro_12_inf_adic_recolhimento_empresa(self): + registro_12 = self.tipo_de_registro_12 + registro_12 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_12 += self._validar(self.inscr_empresa, 14, 'N') + registro_12 += self._validar(self.preenche_zeros, 36, 'V') + registro_12 += self._validar(self.ded_13_lic_maternidade, 15, 'V') + registro_12 += self._validar(self.receita_evento_desp_patroc, 15, 'V') + registro_12 += self._validar(self.indic_orig_receita, 1, 'AN') + registro_12 += self._validar(self.comercializacao_producao_pf, 15, 'V') + registro_12 += self._validar(self.comercializacao_producao_pj, 15, 'V') + registro_12 += self._validar(self.rec_outras_info_processo, 11, 'N') + registro_12 += self._validar(self.rec_outras_info_processo_ano, 4, 'N') + registro_12 += self._validar(self.rec_outras_info_vara_JCJ, 5, 'N') + registro_12 += self._validar( + self.rec_outras_info_periodo_inicio, 6, 'D') + registro_12 += self._validar(self.rec_outras_info_periodo_fim, 6, 'D') + registro_12 += self._validar(self.compensacao_valor_corrigido, 15, 'V') + registro_12 += self._validar(self.compensacao_periodo_inicio, 6, 'D') + registro_12 += self._validar(self.compensacao_periodo_fim, 6, 'D') + registro_12 += self._validar( + self.recolh_competencias_ant_folha_inss, 15, 'V') + registro_12 += self._validar( + self.recolh_competencias_ant_folha_outras_ent, 15, 'V') + registro_12 += self._validar( + self.recolh_competencias_ant_comerc_prod_inss, 15, 'V') + registro_12 += self._validar( + self.recolh_competencias_ant_comerc_prod_outras_ent, 15, 'V') + registro_12 += self._validar( + self.recolh_competencias_ant_eventos_desport_inss, 15, 'V') + registro_12 += self._validar( + self.inf_adic_tomador_parc_fgts_cat_01_02_03_05_06, 15, 'V') + registro_12 += self._validar( + self.inf_adic_tomador_parc_fgts_cat_04_07, 15, 'V') + registro_12 += self._validar(self.parc_fgts_valor_recolhido, 15, 'V') + registro_12 += self._validar( + self.vlr_pago_cooperativas_trabalho, 15, 'V') + registro_12 += self._validar(self.preenche_zeros, 45, 'V') + registro_12 += self._validar(self.preenche_brancos, 6, 'AN') + registro_12 += self._validar(self.fim_linha, 1, 'AN') + return registro_12 + + def _registro_13_alteracao_cadastral_trabalhador(self): + registro_13 = self.tipo_de_registro_13 + registro_13 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_13 += self._validar(self.inscr_empresa, 14, 'N') + registro_13 += self._validar(self.preenche_zeros, 36, 'V') + registro_13 += self._validar(self.pis_pasep_ci, 11, 'N') + registro_13 += self._validar(self.data_admissao, 8, 'D') + registro_13 += self._validar(self.categoria_trabalhador, 2, 'N') + registro_13 += self._validar(self.matricula_trabalhador, 11, 'N') + registro_13 += self._validar(self.num_ctps, 7, 'E') + registro_13 += self._validar(self.serie_ctps, 5, 'E') + registro_13 += self._validar(self.nome_trabalhador, 70, 'A') + registro_13 += self._validar(self.codigo_empresa_caixa, 14, 'N') + registro_13 += self._validar(self.codigo_trabalhador_caixa, 11, 'N') + registro_13 += self._validar(self.codigo_alteracao_cadastral, 3, 'N') + registro_13 += self._validar(self.novo_conteudo_campo, 70, 'AN') + registro_13 += self._validar(self.preenche_brancos, 94, 'AN') + registro_13 += self._validar(self.fim_linha, 1, 'AN') + return registro_13 + + def _registro_14_inclusao_alteracao_endereco_trabalhador(self): + registro_14 = self.tipo_de_registro_14 + registro_14 += self._validar(self.tipo_inscr_empresa, 2, 'N') + registro_14 += self._validar(self.inscr_empresa, 14, 'N') + registro_14 += self._validar(self.preenche_zeros, 36, 'V') + registro_14 += self._validar(self.pis_pasep_ci, 11, 'N') + registro_14 += self._validar(self.data_admissao, 8, 'D') + registro_14 += self._validar(self.categoria_trabalhador, 2, 'N') + registro_14 += self._validar(self.nome_trabalhador, 70, 'A') + registro_14 += self._validar(self.num_ctps, 7, 'E') + registro_14 += self._validar(self.serie_ctps, 5, 'E') + registro_14 += self._validar(self.trabalhador_logradouro, 50, 'AN') + registro_14 += self._validar(self.trabalhador_bairro, 20, 'AN') + registro_14 += self._validar(self.trabalhador_cep, 8, 'N') + registro_14 += self._validar(self.trabalhador_cidade, 20, 'AN') + registro_14 += self._validar(self.trabalhador_uf, 2, 'A') + registro_14 += self._validar(self.preenche_brancos, 103, 'AN') + registro_14 += self._validar(self.fim_linha, 1, 'AN') + return registro_14 + + def _registro_20_tomador_de_servico_ou_obra_contrucao_civil(self): + registro_20 = self.tipo_de_registro_20 + registro_20 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_20 += self._validar(self.inscr_empresa, 14, 'N') + registro_20 += self._validar(self.tipo_inscr_tomador, 1, 'N') + registro_20 += self._validar(self.inscr_tomador, 14, 'N') + registro_20 += self._validar(self.preenche_zeros, 21, 'V') + registro_20 += self._validar(self.nome_tomador, 40, 'AN') + registro_20 += self._validar(self.tomador_logradouro, 50, 'AN') + registro_20 += self._validar(self.tomador_bairro, 20, 'AN') + registro_20 += self._validar(self.tomador_cep, 8, 'N') + registro_20 += self._validar(self.tomador_cidade, 20, 'AN') + registro_20 += self._validar(self.tomador_uf, 2, 'A') + registro_20 += self._validar(self.tomador_cod_gps, 4, 'N') + registro_20 += self._validar(self.tomador_salario_familia, 15, 'V') + registro_20 += self._validar( + self.preenche_zeros, 15, 'V') + registro_20 += self._validar(self.preenche_zeros, 1, 'V') + registro_20 += self._validar(self.preenche_zeros, 14, 'V') + registro_20 += self._validar(self.tomador_valor_retencao, 15, 'V') + registro_20 += self._validar(self.tomador_faturas_emitidas, 15, 'V') + registro_20 += self._validar(self.preenche_zeros, 45, 'V') + registro_20 += self._validar(self.preenche_brancos, 42, 'AN') + registro_20 += self._validar(self.fim_linha, 1, 'AN') + return registro_20 + + def _registro_21_informacoes_adicionais_tomador_de_servico(self): + registro_21 = self.tipo_de_registro_21 + registro_21 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_21 += self._validar(self.inscr_empresa, 14, 'N') + registro_21 += self._validar(self.tipo_inscr_tomador, 1, 'N') + registro_21 += self._validar(self.inscr_tomador, 14, 'N') + registro_21 += self._validar(self.preenche_zeros, 21, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_compensacao_corrigido, 15, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_compensacao_periodo_inicio, 6, 'D') + registro_21 += self._validar( + self.inf_adic_tomador_compensacao_periodo_fim, 6, 'D') + registro_21 += self._validar( + self.inf_adic_tomador_recolh_compet_ant_inss, 15, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_recolh_compet_ant_outras_ent, 15, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_parc_fgts_cat_01_02_03_05_06, 15, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_parc_fgts_cat_04_07, 15, 'V') + registro_21 += self._validar( + self.inf_adic_tomador_parc_fgts_vlr_recolhido, 15, 'V') + registro_21 += self._validar(self.preenche_brancos, 204, 'AN') + registro_21 += self._validar(self.fim_linha, 1, 'AN') + return registro_21 + + def _registro_30_registro_do_trabalhador(self): + registro_30 = self.tipo_de_registro_30 + registro_30 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_30 += self._validar(self.inscr_empresa, 14, 'N') + registro_30 += self._validar(self.tipo_inscr_tomador, 1, 'N') + registro_30 += self._validar(self.inscr_tomador, 14, 'N') + registro_30 += self._validar(self.pis_pasep_ci, 11, 'N') + registro_30 += self._validar(self.data_admissao, 8, 'D') + registro_30 += self._validar(self.categoria_trabalhador, 2, 'N') + registro_30 += self._validar(self.nome_trabalhador, 70, 'A') + registro_30 += self._validar(self.matricula_trabalhador, 11, 'N') + registro_30 += self._validar(self.num_ctps, 7, 'E') + registro_30 += self._validar(self.serie_ctps, 5, 'E') + registro_30 += self._validar(self.data_de_opcao, 8, 'D') + registro_30 += self._validar(self.data_de_nascimento, 8, 'D') + registro_30 += self._validar(self.trabalhador_cbo, 5, 'AN') + registro_30 += self._validar(self.trabalhador_remun_sem_13, 15, 'V') + registro_30 += self._validar(self.trabalhador_remun_13, 15, 'V') + registro_30 += self._validar(self.trabalhador_classe_contrib, 2, 'N') + registro_30 += self._validar(self.trabalhador_ocorrencia, 2, 'N') + registro_30 += self._validar( + self.trabalhador_valor_desc_segurado, 15, 'V') + registro_30 += self._validar( + self.trabalhador_remun_base_calc_contribuicao_previdenciaria, + 15, 'V') + registro_30 += self._validar( + self.trabalhador_base_calc_13_previdencia_competencia, 15, 'V') + registro_30 += self._validar( + self.trabalhador_base_calc_13_previdencia_GPS, 15, 'V') + registro_30 += self._validar(self.preenche_brancos, 98, 'AN') + registro_30 += self._validar(self.fim_linha, 1, 'AN') + return registro_30 + + def _registro_32_movimentacao_do_trabalhador(self): + registro_32 = self.tipo_de_registro_32 + registro_32 += self._validar(self.tipo_inscr_empresa, 1, 'N') + registro_32 += self._validar(self.inscr_empresa, 14, 'N') + registro_32 += self._validar(self.tipo_inscr_tomador, 1, 'N') + registro_32 += self._validar(self.inscr_tomador, 14, 'N') + registro_32 += self._validar(self.pis_pasep_ci, 11, 'N') + registro_32 += self._validar(self.data_admissao, 8, 'D') + registro_32 += self._validar(self.categoria_trabalhador, 2, 'N') + registro_32 += self._validar(self.nome_trabalhador, 70, 'A') + registro_32 += self._validar( + self.trabalhador_codigo_movimentacao, 2, 'AN') + registro_32 += self._validar( + self.trabalhador_data_movimentacao, 8, 'D') + registro_32 += self._validar( + self.trabalhador_indic_recolhimento_fgts, 1, 'AN') + registro_32 += self._validar(self.preenche_brancos, 225, 'AN') + registro_32 += self._validar(self.fim_linha, 1, 'AN') + return registro_32 + + # PARA IMPLEMENTAÇAO FUTURA + # def _registro_50_(self): + # pass + # + # def _registro_51_(self): + # pass + + def _registro_90_totalizador_do_arquivo(self): + registro_90 = self.tipo_de_registro_90 + registro_90 += self._validar(self.marca_de_final_registro, 51, 'AN') + registro_90 += self._validar(self.preenche_brancos, 306, 'AN') + registro_90 += self._validar(self.fim_linha, 1, 'AN') + return registro_90 + + def __init__(self, *args, **kwargs): + # campos gerais-------------------------------------------------------- + self.preenche_zeros = '0' # DEVE SER SEMPRE PASSADO COM TIPO 'V' + self.preenche_brancos = '' + self.fim_linha = '*' + self.tipo_inscr_empresa = '1' + self.inscr_empresa = '' + # campos do HEADER ARQUIVO-------------------------------------------- + self.tipo_de_registro_00 = '00' + self.tipo_remessa = '1' + self.tipo_inscr_resp = '1' + self.inscr_resp = '' + self.nome_resp = '' + self.nome_contato = '' + self.arq_logradouro = '' + self.arq_bairro = '' + self.arq_cep = '' + self.arq_cidade = '' + self.arq_uf = '' + self.tel_contato = '' + self.internet_contato = '' + self.competencia = '' + self.cod_recolhimento = '' + self.indic_recolhimento_fgts = '' + self.modalidade_arq = '' + self.data_recolhimento_fgts = ' ' + self.indic_recolh_ps = '' + self.data_recolh_ps = ' ' + self.indice_recolh_atraso_ps = '' + self.tipo_inscr_fornec = '' + self.inscr_fornec = '' + # campos HEADER EMPRESA------------------------------------------------ + self.tipo_de_registro_10 = '10' + self.emp_nome_razao_social = '' + self.emp_logradouro = '' + self.emp_bairro = '' + self.emp_cep = '' + self.emp_cidade = '' + self.emp_uf = '' + self.emp_tel = '' + self.emp_indic_alteracao_endereco = 'N' + self.emp_cnae = '' + self.emp_indic_alteracao_cnae = 'N' + self.emp_aliquota_RAT = '' + self.emp_cod_centralizacao = '' + self.emp_simples = '1' + self.emp_FPAS = '' + self.emp_cod_outras_entidades = '' + self.emp_cod_pagamento_GPS = '' + self.emp_percent_isencao_filantropia = '' + self.emp_salario_familia = '' + self.emp_salario_maternidade = '' + self.emp_contrib_descont_empregados = '' + self.emp_indic_valor_pos_neg = '' + self.emp_valor_devido_ps_referente = '' + self.emp_banco = '' + self.emp_ag = '' + self.emp_cc = '' + # campos do RECOLHIMENTO DA EMPRESA ----------------------------------- + self.tipo_de_registro_12 = '12' + self.ded_13_lic_maternidade = '' + self.receita_evento_desp_patroc = '' + self.indic_orig_receita = '' + self.comercializacao_producao_pf = '' + self.comercializacao_producao_pj = '' + self.rec_outras_info_processo = '' + self.rec_outras_info_processo_ano = '' + self.rec_outras_info_vara_JCJ = '' + self.rec_outras_info_periodo_inicio = '' + self.rec_outras_info_periodo_fim = '' + self.compensacao_valor_corrigido = '' + self.compensacao_periodo_inicio = '' + self.compensacao_periodo_fim = '' + self.recolh_competencias_ant_folha_inss = '' + self.recolh_competencias_ant_folha_outras_ent = '' + self.recolh_competencias_ant_comerc_prod_inss = '' + self.recolh_competencias_ant_comerc_prod_outras_ent = '' + self.recolh_competencias_ant_eventos_desport_inss = '' + self.parc_fgts_soma_remuneracao = '' + self.parc_fgts_valor_recolhido = '' + self.vlr_pago_cooperativas_trabalho = '' + # campos gerais do TRABALHADOR ---------------------------------------- + self.pis_pasep_ci = '' + self.data_admissao = ' ' + self.categoria_trabalhador = '' + self.num_ctps = '' * 7 + self.serie_ctps = '' * 5 + self.nome_trabalhador = '' + self.matricula_trabalhador = '' + # campos ALTERACAO CADASTRAL TRABALHADOR ------------------------------ + self.tipo_de_registro_13 = '13' + self.codigo_empresa_caixa = '' + self.codigo_trabalhador_caixa = '' + self.codigo_alteracao_cadastral = '' + self.novo_conteudo_campo = '' + # campos INCLUSAO/ALTERACAO ENDERECO TRABALHADOR ---------------------- + self.tipo_de_registro_14 = '14' + self.trabalhador_logradouro = '' + self.trabalhador_bairro = '' + self.trabalhador_cep = '' + self.trabalhador_cidade = '' + self.trabalhador_uf = '' + # campos gerais TOMADOR DE SERVICO/OBRA DE CONSTRUCAO CIVIL------------ + self.tipo_inscr_tomador = '' + self.inscr_tomador = '' + # campos TOMADOR DE SERVICO/OBRA DE CONSTRUCAO CIVIL ------------------ + self.tipo_de_registro_20 = '20' + self.nome_tomador = '' + self.tomador_logradouro = '' + self.tomador_bairro = '' + self.tomador_cep = '' + self.tomador_cidade = '' + self.tomador_uf = '' + self.tomador_cod_gps = '' + self.tomador_salario_familia = '' + self.tomador_contrib_desc_empregado_13 = '' + self.tomador_valor_retencao = '' + self.tomador_faturas_emitidas = '' + # campos INFORMACOES ADICIONAIS TOMADOR DE SERVICO/OBRA DE CONST------- + self.tipo_de_registro_21 = '21' + self.inf_adic_tomador_compensacao_corrigido = '' + self.inf_adic_tomador_compensacao_periodo_inicio = '' + self.inf_adic_tomador_compensacao_periodo_fim = '' + self.inf_adic_tomador_recolh_compet_ant_inss = '' + self.inf_adic_tomador_recolh_compet_ant_outras_ent = '' + self.inf_adic_tomador_parc_fgts_cat_01_02_03_05_06 = '' + self.inf_adic_tomador_parc_fgts_cat_04_07 = '' + self.inf_adic_tomador_parc_fgts_vlr_recolhido = '' + # campos REGISTRO DO TRABALHADOR -------------------------------------- + self.tipo_de_registro_30 = '30' + self.data_de_opcao = ' ' + self.data_de_nascimento = ' ' + self.trabalhador_cbo = '' + self.trabalhador_remun_sem_13 = '' + self.trabalhador_remun_13 = '' + self.trabalhador_classe_contrib = '' + self.trabalhador_ocorrencia = '' + self.trabalhador_valor_desc_segurado = '' + self.trabalhador_remun_base_calc_contribuicao_previdenciaria = '' + self.trabalhador_base_calc_13_previdencia_competencia = '' + self.trabalhador_base_calc_13_previdencia_GPS = '' + # campos MOVIMENTACAO DO TRABALHADOR + self.tipo_de_registro_32 = '32' + self.trabalhador_codigo_movimentacao = '' + self.trabalhador_data_movimentacao = ' ' + self.trabalhador_indic_recolhimento_fgts = '' + # campos registro 50 - IMPLEMENTACAO FUTURA + # campos registro 51 - IMPLEMENTACAO FUTURA + + # campos REGISTRO TOTALIZADOR DO ARQUIVO + self.tipo_de_registro_90 = '90' + self.marca_de_final_registro = '9' * 51 + + def _validar(self, word, tam, tipo='AN'): + """ + Função Genérica utilizada para validação de campos que são gerados + nos arquivos TXT's + :param word: + :param tam: + :param tipo: + :return: + """ + if not word: + word = u'' + + if tipo == 'A': # Alfabetico + word = tira_acentos(unicode(word)) + # tirar tudo que nao for letra do alfabeto + word = re.sub('[^a-zA-Z]', ' ', word) + # Retirar 2 espaços seguidos + word = re.sub('[ ]+', ' ', word) + return unicode.ljust(unicode(word), tam)[:tam] + + elif tipo == 'D': # Data + # Retira tudo que nao for numeral + data_calculo = re.sub(u'[^0-9]', '', str(word)) + return unicode.ljust(unicode(data_calculo), tam)[:tam] + + elif tipo == 'V': # Valor + # Pega a parte decimal como inteiro e nas duas ultimas casas + word = int(word * 100) if word else 0 + # Preenche com zeros a esquerda + word = str(word).zfill(tam) + return word[:tam] + + elif tipo == 'N': # Numerico + # Preenche com brancos a esquerda + word = re.sub('[^0-9]', '', str(word)) + # word = str(word).zfill(tam) + # return word[:tam] + return unicode.rjust(unicode(word), tam)[:tam] + + elif tipo == 'AN': # Alfanumerico + # Tira acentos da palavras + word = tira_acentos(unicode(word)) + # Preenche com espaço vazio a direita + return unicode.ljust(unicode(word), tam)[:tam] + + elif tipo == 'E': # Valor + if ' ' in word: + return word + # Pega a parte decimal como inteiro e nas duas ultimas casas + word = int(word * 100) if word else 0 + # Preenche com zeros a esquerda + word = str(word).zfill(tam) + return word[:tam] diff --git a/l10n_br_hr_arquivos_governo/models/arquivo_seguro_desemprego.py b/l10n_br_hr_arquivos_governo/models/arquivo_seguro_desemprego.py new file mode 100644 index 000000000..a3a6e6bea --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/arquivo_seguro_desemprego.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE- Hendrix Costa +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import logging +from .abstract_arquivos_governo import AbstractArquivosGoverno + +_logger = logging.getLogger(__name__) + +try: + from pybrasil.data import hoje +except ImportError: + _logger.info('Cannot import pybrasil') + + +class SeguroDesemprego(AbstractArquivosGoverno): + + # HEADER + def _registro_header(self): + registro_header = self.tipo_de_registro_00 + registro_header += self._validar(self.tipo_identificador, 1, 'N') + registro_header += self._validar(self.cnpj_empresa, 14, 'N') + registro_header += self._validar(self.versao_layout, 3, 'N') + registro_header += ''.ljust(280) + registro_header += '\r\n' + return registro_header + + # REQUERIMENTO + def _registro_requerimento(self): + registro01 = self.tipo_de_registro_01 + registro01 += self._validar(self.cpf, 11, 'N') + registro01 += self._validar(self.nome, 40, 'AN') + registro01 += self._validar(self.endereco, 40, 'AN') + registro01 += self._validar(self.complemento, 16, 'AN') + registro01 += self._validar(self.cep, 8, 'N') + registro01 += self._validar(self.uf, 2, 'A') + registro01 += self._validar(self.ddd, 2, 'N') + registro01 += self._validar(self.telefone, 8, 'N') + registro01 += self._validar(self.nome_mae, 40, 'A') + registro01 += self._validar(self.pis, 11, 'N') + registro01 += self._validar(self.carteira_trabalho_numero, 8, 'N') + registro01 += self._validar(self.carteira_trabalho_serie, 5, 'AN') + registro01 += self._validar(self.carteira_trabalho_estado, 2, 'A') + registro01 += self._validar(self.cbo, 6, 'N') + registro01 += self._validar(self.data_admissao, 8, 'D') + registro01 += self._validar(self.data_demissao, 8, 'D') + registro01 += self._validar(self.sexo, 1, 'N') + registro01 += self._validar(self.grau_instrucao, 2, 'N') + registro01 += self._validar(self.data_nascimento, 8, 'D') + registro01 += self._validar(self.horas_trabalhadas_semana, 2, 'N') + registro01 += \ + self._validar(self.remuneracao_antepenultimo_salario, 10, 'N') + registro01 += \ + self._validar(self.remuneracao_penultimo_salario, 10, 'N') + registro01 += self._validar(self.ultimo_salario, 10, 'N') + registro01 += self._validar(self.numero_meses_trabalhados, 2, 'N') + registro01 += self._validar(self.recebeu_6_salario, 1, 'N') + registro01 += self._validar(self.aviso_previo_indenizado, 1, 'N') + registro01 += self._validar(self.codigo_banco, 3, 'N') + registro01 += self._validar(self.codigo_agencia, 4, 'N') + registro01 += self._validar(self.codigo_agencia_digito, 1, 'N') + registro01 += ''.ljust(28) + registro01 += '\r\n' + return registro01 + + def _registro_trailler(self): + registro_trailler = self.tipo_de_registro_99 + registro_trailler += \ + self._validar(self.total_requerimentos_informados, 5, 'N') + registro_trailler += str.ljust('', 293) + registro_trailler += '\r\n' + return registro_trailler + + def _gerar_arquivo_seguro_desemprego(self): + return \ + self._registro_header() + \ + self._registro_requerimento() + \ + self._registro_trailler() + + def __init__(self, *args, **kwargs): + + # campos do HEADER --------------------------------------------------- + self.tipo_de_registro_00 = '00' + self.tipo_identificador = '1' + self.cnpj_empresa = '' + self.versao_layout = '001' + self.empregados = [] + # --------------------------------------------------------------------- + + # campos do REQUERIMENTO ---------------------------------------------- + self.tipo_de_registro_01 = '01' + self.cpf = '' + self.nome = '' + self.endereco = '' + self.complemento = '' + self.cep = '' + self.uf = '' + self.ddd = '' + self.telefone = '' + self.nome_mae = '' + self.pis = '' + self.carteira_trabalho_numero = '' + self.carteira_trabalho_serie = '' + self.carteira_trabalho_estado = '' + self.cbo = '' + self.data_admissao = hoje() + self.data_demissao = hoje() + self.sexo = 'M' + self.grau_instrucao = '01' + self.data_nascimento = hoje() + self.horas_trabalhadas_semana = 44 + self.remuneracao_antepenultimo_salario = 0 + self.remuneracao_penultimo_salario = 0 + self.ultimo_salario = 0 + self.numero_meses_trabalhados = '00' + self.recebeu_6_salario = '0' + self.aviso_previo_indenizado = '1' + self.codigo_banco = '' + self.codigo_agencia = '' + self.codigo_agencia_digito = '' + # --------------------------------------------------------------------- + + # campos do TRAILLER ------------------------------------------------- + self.tipo_de_registro_99 = '99' + self.total_requerimentos_informados = u'' + # --------------------------------------------------------------------- diff --git a/l10n_br_hr_arquivos_governo/models/financial_move.py b/l10n_br_hr_arquivos_governo/models/financial_move.py new file mode 100644 index 000000000..7edab3aa0 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/financial_move.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import division, print_function, unicode_literals + +import logging + +from openerp import fields, models + +_logger = logging.getLogger(__name__) + + +class FinancialMove(models.Model): + _inherit = b'financial.move' + + sefip_id = fields.Many2one( + comodel_name='l10n_br.hr.sefip', + string='Sefip', + ) diff --git a/l10n_br_hr_arquivos_governo/models/hr_contract_type.py b/l10n_br_hr_arquivos_governo/models/hr_contract_type.py new file mode 100644 index 000000000..8563f86d5 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/hr_contract_type.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class HrContractType(models.Model): + _inherit = 'hr.contract.type' + + code = fields.Char( + string=u'Código do tipo de contrato', + ) diff --git a/l10n_br_hr_arquivos_governo/models/hr_payslip.py b/l10n_br_hr_arquivos_governo/models/hr_payslip.py new file mode 100644 index 000000000..6345e5793 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/hr_payslip.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class HrPayslip(models.Model): + _inherit = 'hr.payslip' + + sefip_id = fields.Many2one( + comodel_name='l10n_br.hr.sefip', + string='Sefip', + ) diff --git a/l10n_br_hr_arquivos_governo/models/inherited_hr_salary_rule.py b/l10n_br_hr_arquivos_governo/models/inherited_hr_salary_rule.py new file mode 100644 index 000000000..465112b23 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/inherited_hr_salary_rule.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import division, print_function, unicode_literals + +from openerp import models, fields + + +class HrSalaryRule(models.Model): + _inherit = b"hr.salary.rule" + + codigo_darf = fields.Char( + string=b"Código da DARF", + ) diff --git a/l10n_br_hr_arquivos_governo/models/l10n_br_hr_caged.py b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_caged.py new file mode 100644 index 000000000..c62564eb3 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_caged.py @@ -0,0 +1,449 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 +import logging +from datetime import datetime + +from dateutil.relativedelta import relativedelta +from openerp import api, fields, models, _ +from openerp.addons.l10n_br_hr_arquivos_governo.models.arquivo_caged \ + import Caged +from openerp.addons.l10n_br_hr_payroll.models.hr_payslip import MES_DO_ANO +from openerp.exceptions import ValidationError, Warning + +_logger = logging.getLogger(__name__) + +try: + from pybrasil import telefone +except ImportError: + _logger.info('Cannot import pybrasil') + + +class CagedAttachment(models.Model): + _name = 'l10n_br.hr.caged.attachments' + _order = 'create_date' + + name = fields.Char(string='Observações') + caged_id = fields.Many2one( + string='Arquivo do governo relacionado', + comodel_name=b'hr.caged' + ) + attachment_ids = fields.Many2many( + string='Arquivo anexo', + comodel_name='ir.attachment', + relation='ir_attachment_caged_rel', + column1='caged_attachment_id', + column2='attachment_id', + ) + + +class HrCaged(models.Model): + + _name = 'hr.caged' + _inherit = ['abstract.arquivos.governo.workflow', 'mail.thread'] + + related_attachment_ids = fields.One2many( + string='Anexos Relacionados', + comodel_name='l10n_br.hr.caged.attachments', + inverse_name='caged_id', + readonly=True, track_visibility='onchange', + states={'draft': [('readonly', False)], 'open': [('readonly', False)]} + ) + mes_do_ano = fields.Selection( + selection=MES_DO_ANO, + string=u'Mês', + default=fields.Date.from_string(fields.Date.today()).month, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + ano = fields.Integer( + string=u'Ano', + default=fields.Date.from_string(fields.Date.today()).year, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + company_id = fields.Many2one( + comodel_name='res.company', + string=u'Empresa', + default=lambda self: self.env.user.company_id or '', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + caged_txt = fields.Text( + string='CAGED TXT', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + primeira_declaracao = fields.Boolean( + string='Primeira declaração?', + help='Define se é ou não a primeira declaração do estabelecimento ao ' + 'Cadastro Geral de Empregados e Desempregados - ' + 'CAGED Lei nº 4.923/65.', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + responsavel = fields.Many2one( + comodel_name='res.users', + string=u'Responsável', + default=lambda self: self.env.user, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + + @api.depends('responsavel') + def _set_info_responsavel(self): + if self.responsavel and self.responsavel.employee_ids: + employee = self.responsavel.employee_ids[0] + if employee: + self.email_responsavel = employee.work_email + self.cpf_responsavel = employee.cpf + self.telefone_responsavel = employee.work_phone + + email_responsavel = fields.Char( + string='Email do Responsável', + compute=_set_info_responsavel, + ) + cpf_responsavel = fields.Char( + string='CPF do Responsável', + compute=_set_info_responsavel, + ) + telefone_responsavel = fields.Char( + string='Telefone do Responsável', + compute=_set_info_responsavel, + ) + + @api.constrains('mes_do_ano', 'ano', 'company_id') + def caged_restriction(self): + caged = self.search_count([ + ('mes_do_ano', '=', self.mes_do_ano), + ('ano', '=', self.ano), + ('company_id', '=', self.company_id.id), + ]) + if caged > 1: + raise ValidationError(u'Ja existe Caged neste período.') + +# 5.1. Especificação Técnica do Arquivo +# · Campos alfabéticos: +# Todos os dados alfabéticos devem ser informados com caracteres maiúsculos. +# Os caracteres de edição ou máscara (pontos, vírgulas, traços, barras, etc.) +# devem ser omitidos. +# · Campos numéricos: +# Todos os dados numéricos devem ser completados com zeros à esquerda. +# · Campo filler: +# Campo sem informação, deixar em branco. +# · Layout: +# arquivo padrão ASCII +# tamanho do registro: 240 posições +# nome do arquivo de movimento mensal: CGEDaaaa.Mmm +# Onde: aaaa = ano de referência +# mm = mês de referência +# Nome do arquivo Acerto: Addaaaa.Mmm +# Onde: aaaa = ano de referência +# mm = mês de referência +# dd = dia de referência + + def _preencher_registro_A(self, caged, total_movimentacoes, sequencia): + """ + Registro A + Dado um contrato, computar a linha de preenchimento do caged relativa + ao preenchimento das informações da empresa responsavel pelas infos. + :param contrato - hr.contract - + :param caged - arquivo_caged + :return: str - Linha de preenchimento do funcioario + """ + company_id = self.company_id + # Data da competencia + caged.A_competencia = str(self.mes_do_ano) + str(self.ano) + # 1 - Nada a alterar + # 2 - Alterar dados cadastrais + caged.A_alteracao = 1 + caged.A_sequencia = sequencia + # Tipo de 1 - CNPJ | 2 - CEI + caged.A_tipo_identificador = 1 + caged.A_identificador_autorizado = company_id.cnpj_cpf + caged.A_razao_social = company_id.legal_name + caged.A_endereco = (company_id.street or '') + \ + (company_id.street2 or '') + \ + (company_id.number or '') + caged.A_cep = company_id.zip + caged.A_uf = company_id.state_id.code + # Telefone e DDD a partir do phone do res_company + if company_id.phone: + ddd, numero = telefone.telefone.separa_fone(company_id.phone) + else: + ddd = ' ' + numero = ' ' + + caged.A_ddd = ddd + caged.A_telefone = numero + caged.A_ramal = '' + # Quantidade de registros tipo B (Estabelecimento) no arquivo. + caged.A_total_estabelecimento_informados = 1 + # Quantidade de registros tipo C e/ou X (Empregado) no arquivo. + caged.A_total_movimentacoes_informados = total_movimentacoes + + return caged._registro_A() + + def _preencher_registro_B(self, caged, sequencia): + """ + Dado um contrato, computar a linha de preenchimento do caged relativa + ao preenchimento das informações da empresa que esta sendo informada. + :param contrato - hr.contract - + :return: str - Linha de preenchimento do funcioario + """ + company_id = self.company_id + qtd_funcionarios = self.env['hr.contract'].search_count([ + ('company_id', '=', self.company_id.id), + ('date_end', '=', False), + ('categoria', '=', 101), + ]) + + caged.B_sequencia = sequencia + # Tipo de 1 - CNPJ | 2 - CEI + caged.B_tipo_identificador = 1 + caged.B_identificador_estabelecimento = company_id.cnpj_cpf + + # TO DO : Definir como sera preenchido esse campo + # 1. primeira declaração + # 2. já informou ao CAGED anteriormente + caged.B_primeira_declaracao = 1 if self.primeira_declaracao else 2 + + # 1 - Nada a alterar + # 2 - Alterar dados cadastrais + caged.B_alteracao = 1 + + caged.B_cep = company_id.zip + caged.B_razao_social = company_id.legal_name + caged.B_endereco = (company_id.street or '') + \ + (company_id.street2 or '') + \ + (company_id.number or '') + caged.B_bairro = company_id.district + caged.B_uf = company_id.state_id.code + # Como vai calcular esse campo? + # criar categoria no contrato + caged.B_total_empregados_existentes = qtd_funcionarios + caged.B_total_empregados_existentes = 33 + + # 1 – Microempresa – para a pessoa jurídica, ou a ela equiparada, + # que auferir, em cada ano-calendário, receita bruta igual ou inferior + # a R$ 240.000,00 (duzentos e quarenta mil reais). + # 2 - Empresa de Pequeno Porte – para a pessoa jurídica, ou a ela + # equiparada, que auferir, em cada ano-calendário, receita bruta + # superior a R$ 240.000,00 (duzentos e quarenta mil reais) e igual ou + # inferior a R$ 2.400.000,00 (dois milhões e quatrocentos mil reais). + # 3 – Empresa/Órgão não classificados – este campo só deve ser + # selecionado se o estabelecimento não se enquadrar como + # MEI, microempresa ou empresa de pequeno porte. + # 4 – Microempreendedor Individual – para o empresário individual + # que tenha auferido receita bruta, no ano-calendário anterior, + # de até R$36.000,00 (trinta e seis mil reais). + caged.B_porte_estabelecimento = 3 + + caged.B_CNAE = company_id.cnae_main_id.code or '' + caged.B_ddd = '0061' + caged.B_telefone = company_id.phone + caged.B_telefone = company_id.phone + caged.B_email = company_id.email + return caged._registro_B() + + def _preencher_registro_C(self, contrato, caged, seq): + """ + Dado um contrato, computar a linha de preenchimento do caged relativa + ao preenchimento das informações do funcionario. + :param contrato - hr.contract - + :return: str - Linha de preenchimento do funcionario + """ + company_id = self.company_id + employee_id = contrato.employee_id + + caged.C_tipo_identificador = 1 + caged.C_identificador_estabelecimento = company_id.cnpj_cpf + caged.C_sequencia = seq + caged.C_PIS_PASEP = employee_id.pis_pasep + caged.C_sexo = 1 if employee_id.gender == 'male' else 2 + caged.C_nascimento = employee_id.birthday + caged.C_grau_instrucao = employee_id.educational_attainment.code + caged.C_salario_mensal = contrato.wage + caged.C_horas_trabalhadas = contrato.weekly_hours + caged.C_tipo_de_movimento = contrato.labor_bond_type_id.code + caged.C_admissao = contrato.date_start + caged.C_dia_desligamento = contrato.date_end + caged.C_nome_empregado = employee_id.name + caged.C_numero_ctps = employee_id.ctps + caged.C_serie_ctps = employee_id.ctps_series + caged.C_raca_cor = employee_id.ethnicity + # 1 - Para indicar SIM + # 2 - Para indicar NÃO + caged.C_pessoas_com_deficiencia = 2 + caged.C_cbo2000 = contrato.job_id.cbo_id.code + # Informar se o empregado é Aprendiz ou não. pela categoria + # 1 - SIM + # 2 – NÃO + caged.C_aprendiz = 2 if contrato.categoria != '103' else 1 + caged.C_uf_ctps = employee_id.ctps_uf_id.code + caged.C_tipo_deficiencia = employee_id.deficiency_id.code + caged.C_CPF = employee_id.cpf + caged.C_cep_residencia = employee_id.address_home_id.zip + return caged._registro_C() + + def _preencher_registro_X(self, contrato, caged, seq): + """ + Dado um contrato, computar a linha de preenchimento do caged relativa + ao preenchimento das informações do funcionario. + :param contrato - hr.contract - + :return: str - Linha de preenchimento do funcionario + """ + caged.X_tipo_de_registro = '' + caged.X_tipo_identificador = '' + caged.X_identificador_estabelecimento = '' + caged.X_sequencia = seq + caged.X_PIS_PASEP = '' + caged.X_sexo = '' + caged.X_nascimento = '' + caged.X_grau_instrucao = '' + caged.X_salario_mensal = '' + caged.X_horas_trabalhadas = '' + caged.X_admissao = '' + caged.X_tipo_de_movimento = '' + caged.X_dia_desligamento = '' + caged.X_nome_empregado = '' + caged.X_numero_ctps = '' + caged.X_serie_ctps = '' + caged.X_uf_ctps = '' + caged.X_atualizacao = '' + caged.X_competencia = '' + caged.X_raca_cor = '' + caged.X_pessoas_com_deficiencia = '' + caged.X_cbo2000 = '' + caged.X_aprendiz = '' + caged.X_tipo_deficiencia = '' + caged.X_CPF = '' + caged.X_cep_residencia = '' + return caged._registro_X() + + def _preencher_registro_Z(self, caged): + """ + Preencher informações do contato referente ao registro Z + """ + caged.Z_responsavel = self.responsavel.name + caged.Z_email_responsavel = self.email_responsavel + caged.Z_cpf_responsavel = self.cpf_responsavel + return caged._registro_Z() + + @api.multi + def action_open(self): + for record in self: + record.criar_anexo_caged() + super(HrCaged, record).action_open() + + @api.multi + def criar_anexo_caged(self): + caged = Caged() + # Cria um arquivo temporario txt do CAGED e escreve o que foi gerado + path_arquivo = caged._gerar_arquivo_temp(self.caged_txt, 'CAGED') + # Gera o anexo apartir do txt do grrf no temp do sistema + mes = str(self.mes_do_ano) \ + if self.mes_do_ano > 9 else '0' + str(self.mes_do_ano) + nome_arquivo = 'CGED' + str(self.ano) + '.M' + mes + self._gerar_anexo(nome_arquivo, path_arquivo) + + @api.multi + def doit(self): + + contrato_model = self.env['hr.contract'] + + # Recuperar data inicial e final para CAGED + primeiro_dia_do_mes = datetime.strptime(str(self.mes_do_ano) + '-' + + str(self.ano), '%m-%Y') + ultimo_dia_do_mes = primeiro_dia_do_mes + relativedelta(months=1) - \ + relativedelta(days=1) + + # Contratacoes do mes + domain = [ + ('company_id', '=', self.company_id.id), + ('date_start', '<=', ultimo_dia_do_mes), + ('date_start', '>=', primeiro_dia_do_mes), + ('categoria', 'not in', ['721', '722']) + ] + contratacoes = contrato_model.search(domain) + + # Demissoes do mes + domain = [ + ('company_id', '=', self.company_id.id), + ('date_end', '<=', ultimo_dia_do_mes), + ('date_end', '>=', primeiro_dia_do_mes), + ('categoria', 'not in', ['721', '722']) + ] + demissoes = contrato_model.search(domain) + + # Total de movimentações (Registro C) + total_mov = len(contratacoes) + len(demissoes) + + # Instancia um objeto do Caged + caged = Caged() + + # Variavel para guardar as informacoes do caged e da sequencia + caged_txt = '' + + sequencia = 1 + + # Preencher o objeto com informações da empresa + # Registro A + caged_txt += self._preencher_registro_A(caged, total_mov, sequencia) + sequencia += 1 + # Registro B + caged_txt += self._preencher_registro_B(caged, sequencia) + sequencia += 1 + + # Registros C + for contrato in contratacoes: + caged_txt += \ + self._preencher_registro_C(contrato, caged, sequencia) + sequencia += 1 + + for contrato in demissoes: + caged_txt += self._preencher_registro_C(contrato, caged, sequencia) + sequencia += 1 + + # Preencher informações do Registro Z (contato) + caged_txt += self._preencher_registro_Z(caged) + + # Guardar campo no modelo com informações do CAGED + self.caged_txt = caged_txt + + return True + + def _gerar_anexo(self, nome_do_arquivo, path_arquivo_temp): + """ + Função para gerar anexo dentro do holerite, apartir de um arquivo + temporário. Deve ser passado o path do arquivo temporário que se + tornará anexo da payslip + :param nome_do_arquivo: + :param path_arquivo_temp: + :return: + """ + caged_attach_obj = self.env['l10n_br.hr.caged.attachments'] + try: + file_attc = open(path_arquivo_temp, 'r') + attc = file_attc.read() + + attachment_data = { + 'name': nome_do_arquivo, + 'datas_fname': nome_do_arquivo, + 'datas': base64.b64encode(attc), + 'res_model': 'l10n_br.hr.caged.attachments', + } + + caged_attachment_data = { + 'name': 'Arquivo Caged', + 'attachment_ids': [(0, 0, attachment_data)], + 'caged_id': self.id, + } + caged_attach_obj.create(caged_attachment_data) + + file_attc.close() + except: + raise Warning( + _('Impossível gerar Anexo do %s' % nome_do_arquivo)) diff --git a/l10n_br_hr_arquivos_governo/models/l10n_br_hr_contract.py b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_contract.py new file mode 100644 index 000000000..983fa25ad --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_contract.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, exceptions, fields, models, _ + +from ..constantes_rh import CATEGORIA_TRABALHADOR, CATEGORIA_TRABALHADOR_SEFIP + + +class HrContract(models.Model): + _inherit = 'hr.contract' + + weekly_hours = fields.Float( + default=40, + ) + + @api.constrains('weekly_hours') + def _check_weekly_hours(self): + if self.weekly_hours < 1: + raise exceptions.Warning( + _('A quantidade de horas semanais deve estar entre 1 e 44.')) + + @api.multi + @api.depends('categoria') + def _compute_categoria_sefip(self): + + for record in self: + if record.categoria in ('701', '702', '703'): + # + # Autônomo + # + record.categoria_sefip = '13' + elif record.categoria == '721': + # + # Pró-labore + # + record.categoria_sefip = '05' + elif record.categoria == '722': + # + # Pró-labore 2 + # + record.categoria_sefip = '11' + elif record.categoria == '103': + # + # Aprendiz + # + record.categoria_sefip = '07' + else: + record.categoria_sefip = '01' + + categoria_sefip = fields.Selection( + selection=CATEGORIA_TRABALHADOR_SEFIP, + compute='_compute_categoria_sefip', + store=True, + ) + + categoria = fields.Selection( + selection=CATEGORIA_TRABALHADOR, + string="Categoria do Contrato", + required=True, + default='101', + ) diff --git a/l10n_br_hr_arquivos_governo/models/l10n_br_hr_payslip.py b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_payslip.py new file mode 100644 index 000000000..76532438f --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_payslip.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 + +from openerp import api, fields, exceptions, models, _ + +from .arquivo_grrf import Grrf +from .arquivo_seguro_desemprego import SeguroDesemprego + + +class HrPayslip(models.Model): + + _inherit = 'hr.payslip' + + attachment_ids = fields.Many2many( + comodel_name='ir.attachment', + relation='hr_payslip_attachment_rel', + column1='hr_payslip', + column2='attachment_id', + string=u'Attachments' + ) + + grrf_txt = fields.Text( + string='GRRF', + ) + + seguro_desemprego_txt = fields.Text( + string='Seguro Desemprego', + ) + + @api.multi + def compute_seguro_desemprego(self): + """ + Método que inicia o processo para gerar novo GRRF. Disparado na view. + :return: + """ + for holerite in self: + # Instancia a classe do seguro desemprego + seguro_desemprego = SeguroDesemprego() + # preenche o objeto com informações do holerite de rescisao + self._preencher_seguro_desemprego(holerite, seguro_desemprego) + # Gera o campo texto e atribui ao campo da payslip + holerite.seguro_desemprego_txt = \ + seguro_desemprego._gerar_arquivo_seguro_desemprego() + # Gera um arquivo temporário com o texto do seguro Desemprego + path_arquivo = seguro_desemprego._gerar_arquivo_temp( + holerite.seguro_desemprego_txt, 'SEGURO_DESEMPREGO') + # Gera anexo da payslip + self._gerar_anexo('DESLIGAMENTOS.SD', path_arquivo) + + @api.multi + def compute_grrf(self): + """ + Método que inicia o processo para gerar novo TXt do Seguro Desemprego. + Disparado na view. + :return: + """ + for holerite in self: + grrf = Grrf() + # Preencher o objeto com informações do holerite + self._preencher_grrf(holerite, grrf) + # Depois de preencher o objeto, chama a função para computar o txt + holerite.grrf_txt = grrf._gerar_grrf() + # Cria um arquivo temporario txt do grrf e escreve o que foi gerado + path_arquivo = grrf._gerar_arquivo_temp(holerite.grrf_txt, 'GRRF') + # Gera o anexo apartir do txt do grrf no temp do sistema + self._gerar_anexo('grrf.re', path_arquivo) + + def _preencher_seguro_desemprego(self, holerite, seguro_desemprego): + """ + Dado um holerite de rescisao preencher os campos do objeto de GRRF + :param holerite: hr.payslip + :param seguro_desemprego: obj arquivo_seguro_desemprego + :return: + """ + # HEADER + seguro_desemprego.tipo_de_inscricao_empresa = 1 # 1 (CNPJ) ou 2 (CEI) + seguro_desemprego.cnpj_empresa = self.company_id.cnpj_cpf + + # REQUERIMENTO + funcionario = holerite.contract_id.employee_id + seguro_desemprego.cpf = funcionario.cpf + seguro_desemprego.nome = funcionario.name + seguro_desemprego.endereco = funcionario.address_home_id.street + seguro_desemprego.complemento = funcionario.address_home_id.street2 + seguro_desemprego.cep = funcionario.address_home_id.zip + seguro_desemprego.uf = funcionario.address_home_id.state_id.code + seguro_desemprego.telefone = funcionario.address_home_id.phone + seguro_desemprego.nome_mae = funcionario.mother_name + seguro_desemprego.pis = funcionario.pis_pasep + seguro_desemprego.carteira_trabalho_numero = funcionario.ctps + seguro_desemprego.carteira_trabalho_estado = \ + funcionario.ctps_uf_id.code + seguro_desemprego.CBO = holerite.contract_id.job_id.cbo_id.code + seguro_desemprego.data_admissao = holerite.contract_id.date_start + seguro_desemprego.data_demissao = holerite.contract_id.date_end + seguro_desemprego.sexo = funcionario.gender + seguro_desemprego.grau_instrucao = \ + funcionario.educational_attainment.code + seguro_desemprego.data_nascimento = funcionario.birthday + seguro_desemprego.horas_trabalhadas_semana = \ + holerite.contract_id.weekly_hours + remuneracao_mes_rescisao = 0 + for line in holerite.line_ids: + if line.code == 'BASE_FGTS': + remuneracao_mes_rescisao = line.total + # Get Rubrica do BASE_FGTS + seguro_desemprego.ultimo_salario = remuneracao_mes_rescisao + # self.numero_meses_trabalhados = '00' + # self.recebeu_6_salario = '0' + # self.aviso_previo_indenizado = '1' + self.codigo_banco = '' + self.codigo_agencia = '' + self.codigo_agencia_digito = '' + + # TRAILLER + seguro_desemprego.sequencia = 1 + + def _preencher_grrf(self, holerite, grrf): + """ + Dado um holerite de rescisao preencher os campos do objeto de GRRF + :param holerite: + :param grrf: + :return: + """ + # Informações do Responsavel + + # Data de pagamento do holerite de rescisao + grrf.data_recolhimento_grrf = '' + # Pessoa que esta logada + grrf.nome_do_contato_responsavel = self.env.user.name + # Telefone pessoa que esta logada + grrf.telefone_contato_responsavel = self.env.user.phone + # Email do usuario logado + grrf.email_contato = self.env.user.email + + # informações da Empresa + grrf.tipo_de_inscricao_empresa = 1 # 1 (CNPJ) ou 2 (CEI) + grrf.inscricao_da_empresa = self.company_id.cnpj_cpf + grrf.razao_social_empresa = self.company_id.legal_name + grrf.endereco_empresa = \ + (self.company_id.street or '') + (self.company_id.number or '') + grrf.bairro_empresa = self.company_id.street2 + grrf.cep_empresa = self.company_id.zip + grrf.cidade_empresa = self.company_id.l10n_br_city_id.name + grrf.unidade_federacao_empresa = self.company_id.state_id.code + grrf.telefone_empresa = self.company_id.phone + grrf.CNAE_fiscal = self.company_id.cnae_main_id.code or '' + grrf.fpas = u'515' # Fixo + + if self.company_id.fiscal_type == 1: # Simples nacional Odoo + simples = 2 # Simples do Layout + else: # Se nao for do simples + simples = 1 # 1 == Não Optante + grrf.simples = simples + + # Informação do trabalhador + funcionario = holerite.contract_id.employee_id + grrf.PIS_PASEP = funcionario.pis_pasep + grrf.data_admissao = holerite.contract_id.date_start + # 01 Empregado; | 02 Trabalhador Avulso | 03 Trabalhador não vinculadO + grrf.categoria_trabalhador = u'01' + grrf.nome_do_trabalhador = funcionario.name + grrf.numero_ctps = funcionario.ctps + grrf.serie_ctps = funcionario.ctps_series + grrf.sexo = funcionario.gender + grrf.grau_de_instrucao = funcionario.educational_attainment.code + grrf.data_nascimento = funcionario.birthday + grrf.CBO = holerite.contract_id.job_id.cbo_id.code + # Data admissao + grrf.data_opcao = holerite.contract_id.date_start + # Código de movimentação da sefip + grrf.codigo_da_movimentacao = holerite.struct_id.tipo_afastamento_sefip + # Data cara sai da empresa + grrf.data_movimentacao = holerite.date_to + grrf.codigo_de_saque = holerite.struct_id.tipo_saque + grrf.aviso_previo = u'2' + grrf.data_inicio_aviso_previo = holerite.date_from + grrf.reposicao_de_vaga = u'N' + + remuneracao_mes_rescisao = 0 + aviso_previo_indenizado = 0 + for line in holerite.line_ids: + if line.code == 'BASE_FGTS': + remuneracao_mes_rescisao = line.total + if line.code == 'BRUTO_AVISO_PREVIO': + aviso_previo_indenizado = line.total + + # Get Rubrica do BASE_FGTS + grrf.remuneracao_mes_rescisao = remuneracao_mes_rescisao + # Rubrica Bruto Aviso previo para somar todos dados de aviso previo + grrf.aviso_previo_indenizado = aviso_previo_indenizado + + grrf.CPF = funcionario.cpf + grrf.banco_conta_trabalhador = '' + grrf.agencia_trabalhador = '' + grrf.conta_trabalhador = '' + # saldo do FGTS consulta manual na caixa + grrf.saldo_para_fins_rescisorios = \ + holerite.saldo_para_fins_rescisorios \ + if holerite.saldo_para_fins_rescisorios else '' + + def _gerar_anexo(self, nome_do_arquivo, path_arquivo_temp): + """ + Função para gerar anexo dentro do holerite, apartir de um arquivo + temporário. Deve ser passado o path do arquivo temporário que se + tornará anexo da payslip + :param nome_do_arquivo: + :param path_arquivo_temp: + :return: + """ + try: + file_attc = open(path_arquivo_temp, 'r') + attc = file_attc.read() + attachment_obj = self.env['ir.attachment'] + attachment_data = { + 'name': nome_do_arquivo, + 'datas_fname': nome_do_arquivo, + 'datas': base64.b64encode(attc), + 'res_model': 'hr.payslip', + 'res_id': self.id, + } + attachment_obj.create(attachment_data) + file_attc.close() + + except: + raise exceptions.Warning( + _('Impossível gerar Anexo do %s' % nome_do_arquivo)) diff --git a/l10n_br_hr_arquivos_governo/models/l10n_br_hr_sefip.py b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_sefip.py new file mode 100644 index 000000000..6ac63f12b --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/l10n_br_hr_sefip.py @@ -0,0 +1,1631 @@ +# -*- coding: utf-8 -*- +# (c) 2017 KMEE INFORMATICA LTDA - Daniel Sadamo +# (c) 2017 KMEE INFORMATICA LTDA - Luis Felipe Mileo +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from __future__ import ( + division, print_function, unicode_literals, absolute_import +) + +import os +import tempfile +import logging +import base64 +import pybrasil +from py3o.template import Template +from datetime import timedelta +from openerp import api, fields, models, _ +from openerp.exceptions import ValidationError +from pybrasil.valor import formata_valor +from pybrasil.data import formata_data +import subprocess +import calendar + +from openerp.addons.l10n_br_base.tools.misc import punctuation_rm + +from .arquivo_sefip import SEFIP +from ..constantes_rh import ( + MESES, + MODALIDADE_ARQUIVO, + CODIGO_RECOLHIMENTO, + RECOLHIMENTO_GPS, + RECOLHIMENTO_FGTS, +) + +_logger = logging.getLogger(__name__) +CURDIR = os.path.dirname(os.path.abspath(__file__)) + + +class SefipAttachments(models.Model): + _name = b'l10n_br.hr.sefip.attachments' + _order = b'create_date' + + name = fields.Char(string='Observações') + type = fields.Selection(string='Tipo', selection=[ + ('sent', 'Enviado'), + ('relatorio', 'relatorio'), + ], default='relatorio') + sefip_id = fields.Many2one( + string='Arquivo do governo relacionado', + comodel_name=b'l10n_br.hr.sefip' + ) + attachment_ids = fields.Many2many( + string='Arquivo anexo', + comodel_name='ir.attachment', + relation='ir_attachment_sefip_rel', + column1='sefip_attachment_id', + column2='attachment_id', + ) + + +class L10nBrSefip(models.Model): + _name = b'l10n_br.hr.sefip' + _inherit = [b'abstract.arquivos.governo.workflow', b'mail.thread'] + + @api.multi + @api.onchange('company_id') + def onchange_company_id(self): + for sefip in self: + if sefip.responsible_user_id: + if sefip.responsible_user_id.parent_id != \ + sefip.company_id.partner_id: + sefip.responsible_user_id = False + return { + 'domain': { + 'responsible_user_id': + [('parent_id', '=', sefip.company_id.partner_id.id)] + } + } + + @api.multi + def name_get(self): + result = [] + meses = dict(MESES) + for record in self: + name = (self.company_id.name + ' ' + meses.get(self.mes) + + '/' + self.ano + ' - Recolhimento: ' + + self.codigo_recolhimento) + result.append((record.id, name)) + return result + + @api.one + @api.depends('codigo_recolhimento') + def _compute_eh_obrigatorio_informacoes_processo(self): + if self.codigo_recolhimento in ('650', '660'): + self.eh_obrigatorio_informacoes_processo = True + else: + self.eh_obrigatorio_informacoes_processo = False + + @api.one + @api.depends('codigo_recolhimento', 'codigo_fpas') + def _compute_eh_obrigatorio_codigo_outras_entidades(self): + if self.codigo_recolhimento in ( + '115', '130', '135', '150', '155', '211', '608', '650'): + self.eh_obrigatorio_codigo_outras_entidades = True + else: + self.eh_obrigatorio_codigo_outras_entidades = False + self.codigo_outras_entidades = False + if self.codigo_fpas == '582': + self.codigo_outras_entidades = '0' + + @api.multi + def _valor_rubrica(self, rubricas, codigo_rubrica): + for rubrica in rubricas: + if rubrica.code == codigo_rubrica: + return rubrica.total + return 0.00 + + @api.multi + def _buscar_codigo_outras_entidades(self): + + # Se adaptar com o 13 salario onde mes == '13'. A variavel sera set 12 + mes = self.mes if self.mes <= '12' else '12' + + if (fields.Date.from_string(self.ano + "-" + mes + "-01") < + fields.Date.from_string("1998-10-01")): + return ' ' + if self.codigo_recolhimento in \ + ['115', '130', '135', '150', '155', '211', '608', '650']: + return self.company_id.codigo_outras_entidades + if self.codigo_recolhimento in \ + ['145', '307', '317', '327', '337', '345', '640', '660']: + return self.company_id.codigo_outras_entidades + if self.codigo_fpas == "582" and fields.Date.\ + from_string(mes + "-" + self.ano + "-01") >= fields.\ + Date.from_string("1999-04-01"): + return '0000' + if self.codigo_fpas == "639" and fields.Date.\ + from_string(mes + "-" + self.ano + "-01") < fields.Date.\ + from_string("1998-10-01"): + return ' ' + if self.codigo_fpas == "868": + return '0000' + if self._simples(self.company_id) in ['1', '4', '5']: + return self.company_id.codigo_outras_entidades + if self._simples(self.company_id) in ['2', '3', '6']: + return ' ' + + @api.multi + def _buscar_codigo_pagamento_gps(self): + + # Se adaptar com o 13 salario onde mes == '13'. A variavel sera set 12 + mes = self.mes if self.mes <= '12' else '12' + + if fields.Date.from_string(self.ano + "-" + mes + "-01") <\ + fields.Date.from_string("1998-10-01"): + return ' ' + if self.codigo_recolhimento in ['115', '150', '211', '650']: + return self.company_id.codigo_recolhimento_GPS + if self.codigo_fpas == "868": + if self.company_id.codigo_recolhimento_GPS in ['1600', '1651']: + return self.company_id.codigo_recolhimento_GPS + return ' ' + + @api.multi + def _buscar_isencao_filantropia(self): + if self.codigo_fpas == "639": + return str("%05d" % self.porcentagem_filantropia * 100) + return ' ' + + related_attachment_ids = fields.One2many( + string='Anexos Relacionados', + comodel_name='l10n_br.hr.sefip.attachments', + inverse_name='sefip_id', + readonly=True, track_visibility='onchange', + states={'draft': [('readonly', False)], 'open': [('readonly', False)]} + ) + responsible_user_id = fields.Many2one( + comodel_name='res.partner', string=u'Usuário Responsável', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + company_id = fields.Many2one( + comodel_name='res.company', string=u'Empresa', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + mes = fields.Selection( + selection=MESES, string=u'Mês', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + ano = fields.Char( + string=u'Ano', size=4, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + modalidade_arquivo = fields.Selection( + selection=MODALIDADE_ARQUIVO, string=u'Modalidade do arquivo', + readonly=True, + states={'draft': [('readonly', False)]}, + help= '• Pode ser:' + '\nBranco – Recolhimento ao FGTS e Declaração à Previdência' + '\n1 - Declaração ao FGTS e à Previdência' + '\n9 - Confirmação Informações anteriores – Rec/Decl ao FGTS e Decl à Previdência' + '\n • Para competência anterior a 10/1998 deve ser igual a branco ou 1.' + '\n • A modalidade 9 não pode ser informada para competências anteriores a 10/1998.' + '\n • Para os códigos 145, 307, 317, 327, 337, 345 e 640 deve ser igual a branco.' + '\n • Para o código 211 deve ser igual a 1 ou 9.' + '\n • Para o FPAS 868 deve igual a branco ou 9.' + '\n • Para a competência 13, deve ser igual a 1 ou 9.' + '\n • Serão acatadas até três cargas consecutivas de SEFIP.RE.' + '\n • Deverá existir apenas um arquivo SEFIP.RE para cada modalidade.', + ) + codigo_recolhimento = fields.Selection( + string=u'Código de recolhimento', selection=CODIGO_RECOLHIMENTO, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + recolhimento_fgts = fields.Selection( + string=u'Recolhimento do FGTS', selection=RECOLHIMENTO_FGTS, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_recolhimento_fgts = fields.Date( + string=u'Data de recolhimento do FGTS', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + codigo_recolhimento_gps = fields.Integer( + string=u'Código de recolhimento do GPS', + related='company_id.codigo_recolhimento_GPS', + store=True, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + recolhimento_gps = fields.Selection( + string=u'Recolhimento do GPS', selection=RECOLHIMENTO_GPS, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_recolhimento_gps = fields.Date( + string=u'Data de recolhimento do GPS', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + codigo_fpas = fields.Char( + string=u'Código FPAS', + default='736', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + help="""Campo obrigatório:\n + • Deve ser um FPAS válido.\n + • Deve ser diferente de 744 e 779, pois as GPS desses códigos serão + geradas automaticamente, sempre que forem informados os respectivos + fatos geradores dessas contribuições.\n + • Deve ser diferente de 620, pois a informação das categorias 15, 16, + 18, 23 e 25 indica os respectivos fatos geradores dessas + contribuições.\n + • Deve ser diferente de 663 e 671 a partir da competência 04/2004.\n + • Deve ser igual a 868 para empregador doméstico.""" + ) + eh_obrigatorio_codigo_outras_entidades = fields.Boolean( + compute='_compute_eh_obrigatorio_codigo_outras_entidades', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + codigo_outras_entidades = fields.Selection( + string=u'Código de outras entidades', + related='company_id.codigo_outras_entidades', + store=True, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + eh_obrigatorio_informacoes_processo = fields.Boolean( + compute='_compute_eh_obrigatorio_informacoes_processo', + default=False, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_geracao = fields.Date( + string=u'Data do arquivo', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + # Processo ou convenção coletiva + num_processo = fields.Char( + string=u'Número do processo', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + ano_processo = fields.Char( + string=u'Ano do processo', size=4, + readonly=True, + states={'draft': [('readonly', False)]}, + ) + vara_jcj = fields.Char( + string=u'Vara/JCJ', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_inicio = fields.Date( + string=u'Data de Início', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_termino = fields.Date( + string=u'Data de término', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + sefip = fields.Text( + string=u'Prévia do SEFIP', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + data_vencimento_grcsu = fields.Date( + string=u'Data de Vencimento da GRCSU', + ) + + folha_ids = fields.One2many( + string='Holerites', + comodel_name='hr.payslip', + inverse_name='sefip_id', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + + boletos_ids = fields.One2many( + string='Guias/Boletos', + comodel_name='financial.move', + inverse_name='sefip_id', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + + @api.onchange('company_id', 'ano', 'mes') + def _onchange_data_vencimento_grcsu(self): + self.ensure_one() + if not (self.ano and self.mes and self.company_id): + return + + mes = self.mes if self.mes < '13' else '12' + ultimo_dia_mes = str(self.ano) + '-' + mes + '-01' + ultimo_dia_mes = pybrasil.data.mes_que_vem(ultimo_dia_mes) + ultimo_dia_mes = pybrasil.data.ultimo_dia_mes(ultimo_dia_mes) + estado = self.company_id.state_id.code + municipio = self.company_id.l10n_br_city_id.name + self.data_vencimento_grcsu = pybrasil.data.dia_util_pagamento( + ultimo_dia_mes, + estado=estado, + municipio=municipio, + antecipa=True + ) + + # segundo o leiaute do SEFIP para competencia 13 esse campo deverá ser + # igual a 1 ou 9. + if self.mes == '13': + self.modalidade_arquivo = '1' + self.recolhimento_fgts = ' ' + + def _valida_tamanho_linha(self, linha): + """Valida tamanho da linha (sempre igual a 360 posições) e + adiciona quebra caso esteja correto""" + if len(linha) == 360: + return linha + '\r\n' + else: + raise ValidationError( + 'Tamanho da linha diferente de 360 posicoes.' + ' tam = %s, linha = %s' % (len(linha), linha) + ) + + def _logadouro_bairro_cep_cidade_uf_telefone(self, type, partner_id): + erro = '' + + if not partner_id.street: + erro += _("Rua {0} não preenchida\n".format(type)) + if not partner_id.district: + erro += _("Bairro {0} não preenchido\n".format(type)) + if not partner_id.zip: + erro += _("Cep {0} não preenchido\n".format(type)) + if not partner_id.city: + erro += _("Cidade {0} não preenchida\n".format(type)) + if not partner_id.state_id and partner_id.state_id.code: + erro += _("UF {0} não preenchido\n".format(type)) + if not partner_id.phone: + # TODO: Pode ser que este campo precise ser revisto por conta da + # formatação + erro += _("Telefone {0} não preenchido\n".format(type)) + # if not partner_id.number: + # erro += _("Número {0} não preenchido\n".format(type)) + if erro: + raise ValidationError(erro) + + logadouro = '' + logadouro += partner_id.street or '' + logadouro += ' ' + logadouro += partner_id.number or '' + logadouro += ' ' + logadouro += partner_id.street2 or '' + + return (logadouro, partner_id.district, partner_id.zip, + partner_id.city, partner_id.state_id.code, + partner_id.phone) + + def _rat(self, company): + """ + - Campo obrigatório. + - Campo com uma posição inteira e uma decimal. + - Campo obrigatório para competência maior ou igual a 10/1998. + - Não pode ser informado para competências anteriores a 10/1998. + - Não pode ser informado para competências anteriores a 04/99 + quando o FPAS for 639. + - Não pode ser informado para os códigos de recolhimento + 145, 307, 317, 327, 337, 345, 640 e 660. + - Será zeros para FPAS 604, 647, 825, 833 e 868 (empregador doméstico) + e para a empresa optante pelo SIMPLES. + - Não pode ser informado para FPAS 604 com recolhimento de código 150 + em competências posteriores a 10/2001. + + Sempre que não informado o campo deve ficar em branco. + + :return: + """ + + if self.codigo_recolhimento in ( + '145', '307', '317', '327', '337', '345', '640', '660'): + return '' + elif (self.codigo_recolhimento in ( + '604', '647', '825', '833', '868') or + self.company_id.fiscal_type in ('1', '2')): + return 0.00 + elif self.codigo_fpas == '604' and self.codigo_recolhimento == '150': + return '' + return self.env['l10n_br.hr.rat.fap'].search( + [('year', '=', self.ano), + ('company_id', '=', company.id) + ], limit=1).rat_rate or '0' + + def _simples(self, company_id): + """ + Campo obrigatório. + Só pode ser: + 1 - Não Optante; + 2 – Optante; + 3 – Optante - faturamento anual superior a R$1.200.000,00 ; + 4 – Não Optante - Produtor Rural Pessoa Física (CEI e FPAS 604 ) + com faturamento anual superior a R$1.200.000,00. + 5 – Não Optante – Empresa com Liminar para não recolhimento da + Contribuição Social – Lei Complementar 110/01, de 26/06/2001. + 6 – Optante - faturamento anual superior a R$1.200.000,00 - + Empresa com Liminar para não recolhimento da Contribuição + Social – Lei Complementar 110/01, de 26/06/2001. + Deve sempre ser igual a 1 ou 5 para + FPAS 582, 639, 663, 671, 680 e 736. + Deve sempre ser igual a 1 para o FPAS 868 (empregador doméstico). + + Não pode ser informado para o código de recolhimento 640. + Não pode ser informado para competência anterior a 12/1996. + + Os códigos 3, 4, 5 e 6 não podem ser informados a partir da + competência 01/2007. + + Sempre que não informado o campo deve ficar em branco. + """ + # TODO: Melhorar esta função para os outros casos de uso + if company_id.fiscal_type == '3': + return '1' + else: + return '2' + + def _tipo_inscricao_cnae(self, company_id): + if company_id.partner_id.is_company: + tipo_inscr_empresa = '1' + inscr_empresa = company_id.cnpj_cpf + cnae = company_id.cnae_main_id.code + else: + tipo_inscr_empresa = '0' + # TODO: Campo não implementado + inscr_empresa = company_id.cei + cnae = company_id.cnae + if '9700500' not in cnae: + raise ValidationError(_( + 'Para empregador doméstico utilizar o número 9700500.' + )) + return tipo_inscr_empresa, inscr_empresa, cnae + + def _ded_13_lic_maternidade(self): + """ Registro 12 item 5: + + Dedução 13o Salário Licença + Maternidade + (Informar o valor da parcela de 13o salário referente ao período + em que a trabalhadora esteve em licença maternidade, nos casos + em que o empregador/contribuinte for responsável pelo pagamento do + salário-maternidade. + A informação deve ser prestada nas seguintes situações: + - na competência 13, referente ao valor pago durante o ano. + - na competência da rescisão do contrato de trabalho (exceto + rescisão por justa causa), aposentadoria sem continuidade + de vínculo ou falecimento ) + + Opcional para a competência 13. + Opcional para o código de recolhimento 115. + Opcional para os códigos de recolhimento 150, 155 e 608, quando o + CNPJ da empresa for igual ao CNPJ do tomador. + Deve ser informado quando houver movimentação por rescisão de + contrato de trabalho (exceto rescisão com justa causa), + aposentadoria sem continuidade de vínculo, aposentadoria por + invalidez ou falecimento, para empregada que possuir afastamento + por motivo de licença maternidade no ano. + Não pode ser informado para os códigos de recolhimento 130, 135, + 145, 211, 307, 317, 327, 337, 345, 640, 650, 660 e para empregador + doméstico (FPAS 868). + Não pode ser informado para licença maternidade iniciada a partir + de 01/12/1999 e com benefícios requeridos até 31/08/2003. + Não pode ser informado para competências anteriores a 10/1998. + Não pode ser informado para as competências 01/2001 a 08/2003. + Sempre que não informado preencher com zeros. + + :return: + """ + if self.mes == '13': + total = 0.00 + for ocorrencia in self.env['hr.holidays'].search([ + ('holiday_status_id.name', '=', 'Licença Maternidade'), + ('data_inicio', '>=', self.ano + '-01-01'), + ('data_fim', '<=', self.ano + '-12-31'), + ]): + total += ( + ocorrencia.contrato_id.wage * + ocorrencia.number_of_days_temp / 30) + return total + else: + rescisoes = self.env['hr.payslip'].search([ + ('mes_do_ano', '=', self.mes), + ('ano', '=', self.ano), + ('tipo_de_folha', '=', 'rescisao') + ]) + total = 0.00 + for rescisao in rescisoes: + ultimo_dia_mes = calendar.monthrange(int(self.ano), int(self.mes))[1] + for ocorrencia in self.env['hr.holidays'].search([ + ('contrato_id.id', '=', rescisao.contract_id.id), + ('holiday_status_id.name', '=', 'Licença Maternidade'), + ('data_inicio', '>=', self.ano + '-01-01'), + ('data_fim', '<=', self.ano + '-' + self.mes + '-' + + str(ultimo_dia_mes)), + ]): + total += (ocorrencia.contrato_id.wage * + ocorrencia.number_of_days_temp / 30) + return total + + def _get_folha_ids(self): + """ + Buscar os holerites que irao compor o SEFIP do mes + :return: + """ + mes_do_ano = self.mes + tipo_de_folha = ['normal', 'rescisao'] + + # No caso do demonstrativo de decimo terceiro + if mes_do_ano == '13': + # Monta data para algumas vericacoes, logo deve setar + tipo_de_folha = ['decimo_terceiro'] + + raiz = self.company_id.cnpj_cpf.split('/')[0] + folha_ids = self.env['hr.payslip'].search([ + ('mes_do_ano', '=', mes_do_ano), + ('ano', '=', self.ano), + ('tipo_de_folha', 'in', tipo_de_folha), + ('state', 'in', ['done','verify']), + ('company_id.partner_id.cnpj_cpf', 'like', raiz), + ]).filtered('total_folha') + # Filtered eh para pegar apenas os holerites que nao estao zerados + + return folha_ids + + def _valida_centralizadora(self, companies): + options = [] + for company in companies: + options.append(company.centralizadora) + if '1' in options: + if '2' not in options: + raise ValidationError( + _(u'Existe uma empresa centralizadora porém não ' + u'existe nenhuma centralizada') + ) + if '2' in options: + if '1' not in options: + raise ValidationError( + _(u'Existe uma empresa centralizada porém não ' + u'existe nenhuma centralizadora') + ) + + @api.multi + def criar_anexo_sefip(self): + sefip = SEFIP() + for record in self: + # Cria um arquivo temporario txt e escreve o que foi gerado + path_arquivo = sefip._gerar_arquivo_temp(record.sefip, 'SEFIP') + # Gera o anexo apartir do txt do grrf no temp do sistema + nome_arquivo = 'SEFIP.re' + self._gerar_anexo(nome_arquivo, path_arquivo) + + def valida_anexos(self): + tipos_anexo = [x.type for x in self.related_attachment_ids] + if 'sent'in tipos_anexo and 'relatorio' in tipos_anexo: + return True + else: + raise ValidationError( + _('É necessário adicionar o relatório gerado pelo ' + 'aplicativo na aba "Arquivos Anexos" para confirmar o envio') + ) + + def prepara_financial_move(self, partner_id, sindicato_info): + ''' + Tratar dados do sefip e criar um dict para criar financial.move de + contribuição sindical. + :param partner_id: id do partner do sindicato + :param valor: float com valor total de contribuição + :return: dict com valores para criar financial.move + ''' + + sequence_id = \ + self.company_id.payment_mode_sindicato_id.sequence_arquivo_id.id + doc_number = str(self.env['ir.sequence'].next_by_id(sequence_id)) + + # Total de contratos ativos na empresa + total_contratos = self.env['hr.contract'].search([ + '&', + ('company_id', '=', self.company_id.id), + '|', + ('date_end', '>', fields.Date.today()), + ('date_end', '=', False), + ]) + # Filtrar contratos por usuarios + total_funcionarios = len(total_contratos.mapped('employee_id')) + + return { + 'date_document': fields.Date.today(), + 'partner_id': partner_id, + 'doc_source_id': 'l10n_br.hr.sefip,' + str(self.id), + 'company_id': self.company_id.id, + 'amount_document': sindicato_info.get('contribuicao_sindicato'), + 'document_number': doc_number, + 'account_id': self.company_id.financial_account_sindicato_id.id, + 'document_type_id': self.company_id.document_type_sindicato_id.id, + 'payment_mode_id': self.company_id.payment_mode_sindicato_id.id, + 'type': '2pay', + 'sindicato_total_remuneracao_contribuintes': + sindicato_info.get('total_remuneracao'), + 'sindicato_qtd_contribuintes': + sindicato_info.get('qtd_contribuintes'), + 'sindicato_total_empregados': total_funcionarios, + 'date_maturity': self.data_vencimento_grcsu, + 'sefip_id': self.id, + } + + def gerar_financial_move_darf( + self, codigo_receita, valor, partner_id=False): + ''' + Tratar dados do sefip e criar um dict para criar financial.move de + guia DARF. + :param DARF: float com valor total do recolhimento + :return: dict com valores para criar financial.move + ''' + + # Número do documento da DARF + sequence_id = self.company_id.darf_sequence_id.id + doc_number = str(self.env['ir.sequence'].next_by_id(sequence_id)) + + # Definir quem sera o contribuinte da DARF, se nao passar nenhum + # nos parametros assume que é a empresa + if not partner_id: + partner_id = self.company_id.partner_id + + # Preencher campo para indicar tipo de financial.move e tambem + # preencher a data de vencimento + descricao = 'DARF' + + if codigo_receita == '0588': + descricao += ' - Diretores' + + if codigo_receita == '0561': + descricao += ' - Funcionários' + + if codigo_receita == '1661': + descricao += ' - PSS Plano de Seguridade Social' + + if codigo_receita == '1769': + descricao += ' - PSS Patronal' + + # Calcular data de vencimento da DARF + # data de vencimento setada na conf da empresa + dia = str(self.company_id.darf_dia_vencimento) + mes = self.mes + + # ou se forem darfs especificas, cair no dia 07 de cada mes + if codigo_receita in ['1769', '1661']: + dia = '07' + mes = str(int(self.mes) + 1) + + data = self.ano + '-' + mes + '-' + dia + data_vencimento = fields.Date.from_string(data) + timedelta(days=31) + + # Gerar FINANCEIRO da DARF + vals_darf = { + 'date_document': fields.Date.today(), + 'partner_id': partner_id.id, + 'doc_source_id': 'l10n_br.hr.sefip,' + str(self.id), + 'company_id': self.company_id.id, + 'amount_document': valor, + 'document_number': 'DARF-' + str(doc_number), + 'account_id': self.company_id.darf_account_id.id, + 'document_type_id': self.company_id.darf_document_type.id, + 'type': '2pay', + 'date_maturity': data_vencimento, + 'payment_mode_id': self.company_id.darf_carteira_cobranca.id, + 'sefip_id': self.id, + 'cod_receita': codigo_receita, + 'descricao': descricao, + } + financial_move_darf = self.env['financial.move'].create(vals_darf) + + # Gerar PDF da DARF + financial_move_darf.button_boleto() + + return financial_move_darf + + def gerar_financial_move_gps(self, empresa_id, dados_empresa): + ''' + Criar financial.move de guia GPS, imprimir GPS e anexá-la ao move. + :param GPS: float com valor total do recolhimento + :return: financial.move + ''' + + empresa = self.env['res.company'].browse(empresa_id) + + sequence_id = empresa.darf_sequence_id.id + doc_number = str(self.env['ir.sequence'].next_by_id(sequence_id)) + + GPS = dados_empresa['INSS_funcionarios'] + \ + dados_empresa['INSS_empresa'] + \ + dados_empresa['INSS_outras_entidades'] + \ + dados_empresa['INSS_rat_fap'] + + INSS = dados_empresa['INSS_funcionarios'] + \ + dados_empresa['INSS_empresa'] + \ + dados_empresa['INSS_rat_fap'] + + TERCEIROS = dados_empresa['INSS_outras_entidades'] + + descricao = 'GPS - '+ empresa.name + + # Gerar movimentação financeira do GPS + vals_gps = { + 'date_document': fields.Date.today(), + 'partner_id': self.env.ref('base.user_root').id, + 'doc_source_id': 'l10n_br.hr.sefip,' + str(self.id), + 'company_id': empresa_id, + 'document_number': 'GPS-' + str(doc_number), + 'account_id': empresa.gps_account_id.id, + 'document_type_id': empresa.gps_document_type.id, + 'type': '2pay', + 'date_maturity': self.data_recolhimento_gps, + 'payment_mode_id': empresa.gps_carteira_cobranca.id, + 'sefip_id': self.id, + 'valor_inss_terceiros': str(TERCEIROS), + 'valor_inss': str(INSS), + 'amount_document': GPS, + 'descricao': descricao, + } + financial_move_gps = self.env['financial.move'].create(vals_gps) + + # Gerar PDF DA GPS + financial_move_gps.button_boleto() + + return financial_move_gps + + def buscar_IRPF_holerite_13(self, contrato): + """ + Buscar o valor do IRPF do holerite de 13º + :param contrato: + :return: + """ + holerite = self.env['hr.payslip'].search([ + ('contract_id', '=', contrato.id), + ('mes_do_ano', '=', '13'), + ('ano', '=', self.ano), + ('tipo_de_folha', 'in', ['decimo_terceiro']), + ('state', 'in', ['done','verify']), + ]) + for line_id in holerite.line_ids: + if line_id.code == 'IRPF': + return line_id.total + + return 0.0 + + @api.multi + def gerar_boletos(self): + ''' + Criar ordem de pagamento para boleto de sindicato + 1. Configurar os dados para criação das financial.moves + 2. Criar os financial.moves + ''' + + # + # Excluir registros financeiros anteriores + # + for boleto_id in self.boletos_ids: + boleto_id.unlink() + + valor_totas_13 = 0 + contribuicao_sindical = {} + + for record in self: + created_ids = [] + empresas = {} + darfs = {} + + for holerite in self.folha_ids: + + if not empresas.get(holerite.company_id.id): + empresas.update({ + holerite.company_id.id: { + 'INSS_funcionarios': 0.00, + 'INSS_empresa': 0.00, + 'INSS_outras_entidades': 0.00, + 'INSS_rat_fap': 0.00, + } + }) + + for line in holerite.line_ids: + remuneracao = line.slip_id.line_ids.filtered( + lambda x: x.code == 'LIQUIDO') + if line.code == 'CONTRIBUICAO_SINDICAL': + id_sindicato = \ + line.slip_id.contract_id.partner_union.id or 0 + if id_sindicato in contribuicao_sindical: + contribuicao_sindical[id_sindicato][ + 'contribuicao_sindicato'] += line.total + contribuicao_sindical[id_sindicato][ + 'qtd_contribuintes'] += 1 + contribuicao_sindical[id_sindicato][ + 'total_remuneracao'] += remuneracao.total + else: + contribuicao_sindical[id_sindicato] = {} + contribuicao_sindical[id_sindicato][ + 'contribuicao_sindicato'] = line.total + contribuicao_sindical[id_sindicato][ + 'qtd_contribuintes'] = 1 + contribuicao_sindical[id_sindicato][ + 'total_remuneracao'] = remuneracao.total + elif line.code in ['INSS', 'INSS_13']: + empresas[line.slip_id.company_id.id][ + 'INSS_funcionarios'] += line.total + elif line.code == 'INSS_EMPRESA': + empresas[line.slip_id.company_id.id][ + 'INSS_empresa'] += line.total + elif line.code == 'INSS_OUTRAS_ENTIDADES': + empresas[line.slip_id.company_id.id][ + 'INSS_outras_entidades'] += line.total + elif line.code == 'INSS_RAT_FAP': + empresas[line.slip_id.company_id.id][ + 'INSS_rat_fap'] += line.total + + # para gerar a DARF, identificar a categoria de contrato pois + # cada categoria tem um código de emissao diferente + elif line.code in ['IRPF', 'IRPF_13', 'IRPF_FERIAS'] and \ + not line.slip_id.struct_id.code in ['FUNCIONARIO_CEDIDO_PSS']: + + if line.slip_id.contract_id.categoria in \ + ['721', '722']: + codigo_darf = '0588' + else: + codigo_darf = '0561' + + if darfs.get(codigo_darf): + darfs[codigo_darf] += line.total + else: + darfs.update({ + codigo_darf: line.total + }) + + # Para rubricas de PSS patronal gerar para cpf do + # funcionario + elif line.code in ['PSS_PATRONAL']: + partner_id = line.employee_id.address_home_id + financial_move_darf = self.gerar_financial_move_darf( + '1769', line.total, partner_id) + created_ids.append(financial_move_darf.id) + + # Para rubricas de PSS do funcionario + elif line.code in ['PSS']: + partner_id = line.employee_id.address_home_id + financial_move_darf = self.gerar_financial_move_darf( + '1661', line.total, partner_id) + created_ids.append(financial_move_darf.id) + + # buscar o valor do IRPF do holerite de 13º + valor_13 = record.buscar_IRPF_holerite_13(holerite.contract_id) + valor_totas_13 += valor_13 + darfs[codigo_darf] += valor_13 + + for sindicato in contribuicao_sindical: + vals = self.prepara_financial_move( + sindicato, contribuicao_sindical[sindicato]) + + financial_move = self.env['financial.move'].create(vals) + created_ids.append(financial_move.id) + + for company in empresas: + dados_empresa = empresas[company] + financial_move_gps = self.gerar_financial_move_gps( + company, dados_empresa) + created_ids.append(financial_move_gps.id) + + for cod_darf in darfs: + financial_move_darf = \ + self.gerar_financial_move_darf(cod_darf, darfs[cod_darf]) + + created_ids.append(financial_move_darf.id) + + return { + 'domain': "[('id', 'in', %s)]" % created_ids, + 'name': _("Guias geradas pelo SEFIP"), + 'res_ids': created_ids, + 'view_type': 'form', + 'view_mode': 'tree,form', + 'auto_search': True, + 'res_model': 'financial.move', + 'view_id': False, + 'search_view_id': False, + 'type': 'ir.actions.act_window' + } + + @api.multi + def action_sent(self): + """ + Confirmar o Envio do Sefip: + 1. Validar se o sefip contem o relatorio em anexo + 2. Chamar a função que muda o status do holerite, liberando para pagamt + """ + for record in self: + record.valida_anexos() + # Liberar holerites para pagamento + for holerite in record.folha_ids: + holerite.hr_verify_sheet() + super(L10nBrSefip, record).action_sent() + + @api.multi + def action_open(self): + """ + Confirmar a geração do Sefip + """ + for record in self: + record.criar_anexo_sefip() + super(L10nBrSefip, record).action_open() + + @api.multi + def gerar_sefip(self): + for record in self: + record.folha_ids = False + sefip = SEFIP() + + if self.mes == '13': + if self.modalidade_arquivo != '1': + raise ValidationError( + 'Na competência 13 a Modalidade do Arquivo deverá ser igual a 1 ou 9.') + + if self.recolhimento_fgts != ' ': + raise ValidationError('Na competência 13 o Indicador de Recolhimento FGTS não deverá ser informado.') + + record.sefip = self._valida_tamanho_linha( + record._preencher_registro_00(sefip)) + folha_ids = record._get_folha_ids() + self._valida_centralizadora(folha_ids.mapped('company_id')) + + for company_id in folha_ids.mapped('company_id'): + folhas_da_empresa = folha_ids.filtered( + lambda r: r.company_id == company_id) + + record.sefip += self._valida_tamanho_linha( + self._preencher_registro_10(company_id, sefip)) + + record.sefip += self._valida_tamanho_linha( + self._preencher_registro_12(company_id, sefip)) + + for folha in folhas_da_empresa.sorted( + key=lambda folha: + ( + punctuation_rm(folha.employee_id.pis_pasep), + folha.contract_id.categoria_sefip + )): + record.sefip += self._valida_tamanho_linha( + record._preencher_registro_30(sefip, folha)) + + if folha.tipo_de_folha == 'rescisao': + record.sefip += self._valida_tamanho_linha( + record._preencher_registro_32(sefip, folha)) + + record.sefip += sefip._registro_90_totalizador_do_arquivo() + + # Setar a relação entre Holerite e o SEFIP + for holerite in folha_ids: + holerite.sefip_id = record.id + + def _preencher_registro_00(self, sefip): + sefip.tipo_inscr_resp = '1' if \ + self.responsible_user_id.parent_id.is_company else '3' + sefip.inscr_resp = self.responsible_user_id.parent_id.cnpj_cpf + sefip.nome_resp = self.responsible_user_id.parent_id.legal_name + sefip.nome_contato = (self.responsible_user_id.legal_name or + self.responsible_user_id.name) + logadouro, bairro, cep, cidade, uf, telefone = \ + self._logadouro_bairro_cep_cidade_uf_telefone( + 'do responsável', self.responsible_user_id + ) + sefip.arq_logradouro = logadouro + sefip.arq_bairro = bairro + sefip.arq_cep = cep + sefip.arq_cidade = cidade + sefip.arq_uf = uf + sefip.tel_contato = telefone + sefip.internet_contato = self.responsible_user_id.email + sefip.competencia = self.ano + self.mes + sefip.cod_recolhimento = self.codigo_recolhimento + sefip.indic_recolhimento_fgts = self.recolhimento_fgts + sefip.modalidade_arq = self.modalidade_arquivo + sefip.data_recolhimento_fgts = fields.Datetime.from_string( + self.data_recolhimento_fgts).strftime('%d%m%Y') \ + if self.data_recolhimento_fgts else '' + sefip.indic_recolh_ps = self.recolhimento_gps + sefip.data_recolh_ps = fields.Datetime.from_string( + self.data_recolhimento_gps).strftime('%d%m%Y') \ + if self.data_recolhimento_gps else '' + if not self.company_id.supplier_partner_id: + raise ValidationError( + 'Campo "Fornecedor do Sistema" não está preenchido nesta ' + 'Empresa ! - Favor preenchê-lo antes de gerar a SEFIP' + ) + elif not self.company_id.supplier_partner_id.cnpj_cpf: + raise ValidationError( + 'Campo "CNPJ/CPF" do Fornecedor do Sistema ' + 'não está preenchido! - Favor preenchê-lo ' + 'antes de gerar a SEFIP' + ) + sefip.tipo_inscr_fornec = ( + '1' if self.company_id.supplier_partner_id.is_company else '3') + sefip.inscr_fornec = self.company_id.supplier_partner_id.cnpj_cpf + + return sefip._registro_00_informacoes_responsavel() + + def _preencher_registro_10(self, company_id, sefip): + + tipo_inscr_empresa, inscr_empresa, cnae = self._tipo_inscricao_cnae( + company_id + ) + + sefip.tipo_inscr_empresa = tipo_inscr_empresa + sefip.inscr_empresa = inscr_empresa + sefip.emp_nome_razao_social = ( + company_id.legal_name or company_id.name or '' + ) + logadouro, bairro, cep, cidade, uf, telefone = \ + self._logadouro_bairro_cep_cidade_uf_telefone( + 'da empresa', company_id.partner_id + ) + sefip.emp_logradouro = logadouro + sefip.emp_bairro = bairro + sefip.emp_cep = cep + sefip.emp_cidade = cidade + sefip.emp_uf = uf + sefip.emp_tel = telefone + # + # A responsabilidade de alteração do enderço da empresa deve ser + # sempre feita pela receita federal, não ousamos usar esta campo. + # + sefip.emp_indic_alteracao_endereco = 'N' + + sefip.emp_cnae = cnae + # sefip.emp_indic_alteracao_cnae = 'n' + sefip.emp_aliquota_RAT = self._rat(company_id) + sefip.emp_cod_centralizacao = company_id.centralizadora + sefip.emp_simples = self._simples(company_id) + sefip.emp_FPAS = self.codigo_fpas + sefip.emp_cod_outras_entidades = self._buscar_codigo_outras_entidades() + sefip.emp_cod_pagamento_GPS = self._buscar_codigo_pagamento_gps() + sefip.emp_percent_isencao_filantropia = \ + self._buscar_isencao_filantropia() + # TODO: + sefip.emp_salario_familia = '' # rubrica salario familia + sefip.emp_salario_maternidade = '' # soma das li mat pagas no mês + sefip.emp_contrib_descont_empregados = '' # total inss retido + sefip.emp_indic_valor_pos_neg = 0 # Sempre positivo + sefip.emp_valor_devido_ps_referente = '' + # valor devido 13 salario, INSS décimo terceiro igual ao + # "emp_contrib_descont_empregados" #24 + + # TODO: implementação futura / não precisa preencher + # sefip.emp_banco = self.company_id.bank_id[0].bank + # sefip.emp_ag = self.company_id.bank_id[0].agency + # sefip.emp_cc = self.company_id.bank_id[0].account + return sefip._registro_10_informacoes_empresa() + + def _preencher_registro_12(self, company_id, sefip): + + tipo_inscr_empresa, inscr_empresa, cnae = self._tipo_inscricao_cnae( + company_id + ) + + sefip.tipo_inscr_empresa = tipo_inscr_empresa + sefip.inscr_empresa = inscr_empresa + + # Item 5 + # TODO: Implementar função + sefip.ded_13_lic_maternidade = self._ded_13_lic_maternidade() + + # Campos da tela + if self.codigo_recolhimento in ('650', '660'): + sefip.rec_outras_info_processo = self.num_processo + sefip.rec_outras_info_processo_ano = self.ano_processo + sefip.rec_outras_info_vara_JCJ = self.vara_jcj + sefip.rec_outras_info_periodo_inicio = self.data_inicio + sefip.rec_outras_info_periodo_fim = self.data_termino + + # Os outros campos são em branco + return sefip._registro_12_inf_adic_recolhimento_empresa() + + def _trabalhador_remun_sem_13(self, folha): + """ Registro 30. Item 16 + + Rubrica Base do INSS + + """ + result = 0.00 + # + # Não pode ser informado para a competência 13 + # + if folha.tipo_de_folha == 'decimo_terceiro': + return result + # + # As remunerações pagas após rescisão do contrato de trabalho e + # conforme determinação do Art. 466 da CLT, + # não devem vir acompanhadas das respectivas movimentações. + # + #elif False: + # # TODO: + # result = 0.00 + + # + # Obrigatório + # + # elif folha.contract_id.categoria_sefip in ( + # '05', '11', '13', '14', '15', '16', '17', '18', '22', '23', + # '24', '25'): + # result = folha.base_inss + # + # Opcional + # + # elif folha.contract_id.categoria_sefip in ( + # '01', '02', '03', '04', '06', '07', '12', '19', '20', '21', + # '26'): + # result = folha.base_inss + + # if not folha.base_inss: + # return self._valor_rubrica(folha.line_ids, "SALARIO") + + result += self._valor_rubrica(folha.line_ids, "BASE_FGTS") + result += self._valor_rubrica(folha.line_ids, "BASE_FGTS_FERIAS") + + # base_inss_ferias = self._valor_rubrica( + # folha.line_ids, "BASE_INSS_FERIAS" + # ) + # result += base_inss_ferias + # + # if not base_inss_ferias: + # result += self._valor_rubrica(folha.line_ids, "1/3_FERIAS") + return result + + def _trabalhador_remun_13(self, folha): + """ Registro 30. Item 17 + + Rúbrica 13 Base do INSS (Somente na rescisão temos o 16 e o 17!) + + """ + result = 0.00 + + # Na competencia 13 nao deve ser informado esses campos + if folha.tipo_de_folha == 'decimo_terceiro': + return result + + # Se o funcionario adiantou ferias no holerite do mes + result += self._valor_rubrica(folha.line_ids, 'ADIANTAMENTO_13_FERIAS') + + # Holerites de 13 gerados em dezembro tem a identificação do mes = 13 + # quando sao gerados como adiantamento, sao setados com o mes corrente + mes_do_ano = [self.mes] if self.mes != '12' else [self.mes, '13'] + + # Buscar folha de 13 salario + folha_ids = self.env['hr.payslip'].search([ + ('ano', '=', self.ano), + ('mes_do_ano', 'in', mes_do_ano), + ('tipo_de_folha', 'in', ['decimo_terceiro']), + ('is_simulacao', '=', False), + ('state', 'in', ['done','verify']), + ('contract_id', '=', folha.contract_id.id), + # ('company_id.partner_id.cnpj_cpf', 'like', raiz) + ], limit=1) + + if folha_ids: + + # Holerites de adiantamento de 13 gerados em maio para funcionarios + # que nao adiantaram o 13 salario nas ferias, vem com o codigo + # de rubrica de PRIMEIRA_PARCELA_13 + result += \ + self._valor_rubrica(folha_ids.line_ids, "PRIMEIRA_PARCELA_13") + + # Holerites de decimo terceiro em dez, a rubrica é SALARIO_13 + base_13 = \ + self._valor_rubrica(folha_ids.line_ids, "SALARIO_13") - \ + self._valor_rubrica( + folha_ids.line_ids, "DESCONTO_ADIANTAMENTO_13") + + result += base_13 + + return result + + # # + # # As remunerações pagas após rescisão do contrato de trabalho e + # # conforme determinação do Art. 466 da CLT, + # # não devem vir acompanhadas das respectivas movimentações . + # # + # elif False: + # # TODO: + # result = 0.00 + # # + # # Campo obrigatório para categoria 02. + # # + # elif folha.contract_id.categoria_sefip == '02': + # # TODO: Implementar campo para calcular a rúbrica do 13 do INSS + # # BASE_INSS_13 + # # ou pesquisar manualemnte + # result = 0.00 + # elif folha.contract_id.categoria_sefip in ( + # '01', '03', '04', '06', '07', '12', '19', '20', '21', '26'): + # # TODO: + # result = 0.00 + # return result + + # return self._valor_rubrica(folha.line_ids, 'ADIANTAMENTO_13_FERIAS') + + def _trabalhador_classe_contrib(self, folha): + """ Registro 30. Item 18 + Classe de Contribuição (Indicar a classe de contribuição do autônomo, + quando a empresa opta por contribuir sobre seu salário-base e os + classifica como categoria 14 ou 16. A classe deve estar compreendida + em tabela fornecida pelo INSS). """ + + result = ' ' + # # + # # Não pode ser informado para a competência 13 + # # + # if folha.tipo_de_folha == 'decimo_terceiro': + # return result + # # + # # Campo obrigatório para as categorias 14 e 16 + # # (apenas em recolhimentos de competências anteriores a 03/2000). + # # + # elif (fields.Date.from_string(folha.date_from) < + # fields.Date.from_string('2000-03-01') and + # folha.contract_id.categoria_sefip in ('14', '16')): + # # TODO: + # return 0.00 + return result + + def _buscar_multiplos_vinculos(self, folha): + """ Esta informação deve ser lançada manualmente no payslip + + :param folha: + :return: + """ + # TODO: Implementar no payslip + return [] + + def _buscar_ocorrencias(self, folha): + """ Estas informações devem ser lançadas na folha + + :param folha: + :return: + """ + # FIXME: Deste jeito não deu certo, pois existem afastamentos s/ data + # folha_date_from = fields.Date.from_string(folha.date_from) + # folha_date_to = fields.Date.from_string(folha.date_to) + # + # ocorrencia_aprovada_ids = folha.contract_id.afastamento_ids.filtered( + # lambda r: r.state == 'validate') + # ocorrencias_no_periodo_ids = ocorrencia_aprovada_ids.filtered( + # lambda r: (folha_date_from >= + # fields.Date.from_string(r.data_inicio) and + # fields.Date.from_string(r.data_fim) <= folha_date_to)) + # + # return ocorrencias_no_periodo_ids + return [] + + def _trabalhador_ocorrencia(self, folha): + """ Registro 30. Item 19 + Ocorrencia: Acidente de trabalho, rescisão, afastamento por + doença lic maternidade, ( situaçeõs que o funcionario deixa de + trablalhar e o inss deverá assumir o pagamento do funcionário) + + Nao implementado: + + - Para empregado doméstico (Cat 06) e diretor não empregado + (Cat 05) permitido apenas branco ou 05. + - + + """ + if folha.contract_id.cnpj_empregador_cedente: + return '05' + + folha_ids = self._get_folha_ids() + count = 0 + for folha_id in folha_ids: + if folha.employee_id.id == folha_id.employee_id.id: + count += 1 + if count == 2: + return '05' + ocorrencia = ' ' + + # TODO: + if folha.tipo_de_folha == 'rescisao': + # FIXME: + # return hr.payroll.structure.tipo_afastamento_sefip + #print ("tipo_afastamento_sefip - Registro 30. Item 19") + pass + + ocorrencias_no_periodo_ids = self._buscar_ocorrencias(folha) + + if not ocorrencias_no_periodo_ids: + return ocorrencia + + # Vai o código do afastamento mais longo e é sempre informado + # A lógica abaixo pode ser removida + + # # + # # Obrigatório para categoria 26, + # # devendo ser informado 05, 06, 07 ou 08. + # # + # if folha.contract_id.categoria_sefip == '26': + # permitido = ['05', '06', '07', '08'] + # # TODO: + # ocorrencia = '05' + # elif folha.contract_id.categoria_sefip in ( + # '02', '22', '23' + # ): + # permitido = [' ', '01', '02', '03', '04'] + # + # if permitido and ocorrencia in permitido: + # return ocorrencia + # elif permitido and ocorrencia not in permitido: + # raise ValidationError( + # _("A ocorrência {0} não é permitida para folha de pagamento" + # " de \n {1}, referente a {2}." + # .format( + # OCORRENCIA_SEFIP[ocorrencia], + # folha.contract_id.name, + # folha.data_extenso + # ))) + # return ocorrencia + + def _trabalhador_valor_desc_segurado(self, folha): + """ Registro 30. Item 20. + + Valor Descontado do Segurado (Destinado à informação do valor da + contribuição do trabalhador com mais de um vínculo empregatício; + ou quando tratar-se de recolhimento de trabalhador avulso, + dissídio coletivo ou reclamatória trabalhista, ou, ainda nos meses + de afastamento e retorno de licença maternidade) + O valor informado será considerado como contribuição do segurado. + + -- + Verificar se no cliente, por exemplo, + o funcionário esta contratado em 2 lugares pois o INSS recolhido + em outro lugar pode ter que ser informado aqui. + + """ + result = 0 + + if (fields.Date.from_string(folha.date_from) < + fields.Date.from_string('1998-10-01')): + return result + # + # Campo opcional para os códigos de recolhimento 130, 135 e 650. + # + if self.codigo_recolhimento in ('130', '135', '650'): + return 0 + + multiplos_vinculos_ids = self._buscar_multiplos_vinculos(folha) + + if not multiplos_vinculos_ids: + return 0.00 + + # TODO: + # Campo opcional para as ocorrências 05, 06, 07 e 08. + # Campo opcional para as categorias de trabalhadores igual a + # 01, 02, 04, 06, 07, 12, 19, 20, 21 e 26. + # Campo opcional para as categorias de trabalhadores igual a + # 05, 11, 13, 15, 17, 18, 24 e 25 a partir da competência 04/2003. + + return 0.00 + + def _trabalhador_remun_base_calc_contribuicao_previdenciaria(self, folha): + """ Registro 30. Item 21 + + Remuneração base de cálculo da contribuição previdenciária + (Destinado à informação da parcela de remuneração sobre a qual incide + contribuição previdenciária, quando o trabalhador estiver afastado + por motivo de acidente de trabalho e/ou prestação de serviço + militar obrigatório ou na informação de Recolhimento + Complementar de FGTS) + + + Preenchido somente quando o funcionário esta afastado. + + Geralmente Zerado + + """ + return 0.00 + + def _trabalhador_base_calc_13_previdencia_competencia(self, folha): + """ Registro 30. Item 22 + + Base de Cálculo 13 Salário Previdência Social – + Referente à competência do movimento + + (Na competência em que ocorreu o afastamento definitivo – informar o + valor total do 13o pago no ano ao trabalhador. + Na competência 12 – Indicar eventuais diferenças de gratificação + natalina de empregados que recebem remuneração variável – Art. 216, + Parágrafo 25, Decreto 3.265 de 29.11.1999) + + Na competência 13, para a geração da GPS, indicar o valor total do + 13o salário pago no ano ao trabalhador) + + """ + + if folha.tipo_de_folha == 'rescisao': + return self._valor_rubrica(folha.line_ids, "BASE_INSS_13") + + if folha.tipo_de_folha == 'decimo_terceiro': + if folha.base_inss: + return folha.base_inss + # No caso dos contratos que nao tem INSS + # pegar a Rubrica do salario de decimo terceiro + else: + return self._valor_rubrica(folha.line_ids, "SALARIO_13") + + return 0.00 + + def _trabalhador_base_calc_13_previdencia_GPS(self, folha): + """ Registro 30. Item 23 + + Base de Cálculo 13 Salário Previdência – Referente + à GPS da competência 13. + + Deve ser utilizado apenas na competência 12, informando o valor + da base de cálculo do 13o dos empregados que recebem remuneração + variável, em relação a remuneração apurada até 20/12 sobre + a qual já houve recolhimento em GPS ). + + """ + # if folha.tipo_de_folha == 'decimo_terceiro': + # return folha.base_inss + return 0.00 + + def _preencher_registro_30(self, sefip, folha): + """ + + Recomendações gerais!: + + Uma linha para cada folha do periodo, sendo rescisão, normal. + Férias não entra. + Na competência 13 considerar somente o 13. + + """ + # if folha.tipo_de_folha == 'ferias': + + codigo_categoria = folha.contract_id.categoria_sefip + + tipo_inscr_empresa, inscr_empresa, cnae = self._tipo_inscricao_cnae( + folha.company_id + ) + sefip.tipo_inscr_empresa = tipo_inscr_empresa + sefip.inscr_empresa = inscr_empresa + + if self.codigo_recolhimento in ( + '130', '135', '211', '150', '155', '317', '337', '608'): + sefip.tipo_inscr_tomador = ' ' + sefip.inscr_tomador = ' ' * 14 + + sefip.pis_pasep_ci = folha.employee_id.pis_pasep + + if codigo_categoria in ('01', '03', '04', '05', '06', '07', '11', + '12', '19', '20', '21', '26'): + sefip.data_admissao = fields.Datetime.from_string( + folha.contract_id.date_start).strftime('%d%m%Y') or '' + + if codigo_categoria in ('01', '03', '04', '05', '06', '07', '11', + '12', '19', '20', '21'): + sefip.categoria_trabalhador = codigo_categoria or '' + + sefip.nome_trabalhador = folha.employee_id.name + + if codigo_categoria not in ( + '06', '13', '14', '15', '16', '17', '18', '22', '23', + '24', '25'): + sefip.matricula_trabalhador = folha.employee_id.registration or '' + + if codigo_categoria in ('01', '03', '04', '06', '07', '26'): + sefip.num_ctps = folha.employee_id.ctps or '' + sefip.serie_ctps = folha.employee_id.ctps_series or '' + else: + sefip.num_ctps = ' ' * 7 or '' + sefip.serie_ctps = ' ' * 5 or '' + + if codigo_categoria in ('01', '03', '04', '05', '06', '07'): + # Item 13: Data de opção do FGtS, é sempre a data de contratação! + sefip.data_de_opcao = fields.Datetime.from_string( + folha.contract_id.date_start).strftime('%d%m%Y') or '' + else: + sefip.data_de_opcao = ' ' + + if codigo_categoria in ('01', '02', '03', '04', '05', '06', '07', + '12', '19', '20', '21', '26') and \ + folha.employee_id.birthday: + sefip.data_de_nascimento = fields.Datetime.from_string( + folha.employee_id.birthday).strftime('%d%m%Y') + else: + sefip.data_de_nascimento = ' ' + + if not folha.contract_id.job_id: + raise ValidationError("Contrato " + folha.contract_id.name + " faltando campo função !") + + if codigo_categoria in '06': + sefip.trabalhador_cbo = '05121' + else: + sefip.trabalhador_cbo = '0' + \ + folha.contract_id.job_id.cbo_id.code[:4] + # Revisar daqui para a frente + + sefip.trabalhador_remun_13 = \ + self._trabalhador_remun_13(folha) or '' + sefip.trabalhador_remun_sem_13 = \ + self._trabalhador_remun_sem_13(folha) or '' + + sefip.trabalhador_classe_contrib = \ + self._trabalhador_classe_contrib(folha) or '' + + sefip.trabalhador_ocorrencia = self._trabalhador_ocorrencia(folha) or '' + sefip.trabalhador_valor_desc_segurado = \ + self._trabalhador_valor_desc_segurado(folha) or '' + sefip.trabalhador_remun_base_calc_contribuicao_previdenciaria = \ + self._trabalhador_remun_base_calc_contribuicao_previdenciaria( + folha) or '' + sefip.trabalhador_base_calc_13_previdencia_competencia = \ + self._trabalhador_base_calc_13_previdencia_competencia(folha) or '' + sefip.trabalhador_base_calc_13_previdencia_GPS = \ + self._trabalhador_base_calc_13_previdencia_GPS(folha) or '' + + return sefip._registro_30_registro_do_trabalhador() + + def _preencher_registro_32(self, sefip, folha): + """ + + Registro de movimentação de Trabalhador + + """ + tipo_afastamento = folha.struct_id.tipo_afastamento_sefip + sefip.trabalhador_codigo_movimentacao = tipo_afastamento or ' ' + sefip.trabalhador_data_movimentacao = \ + formata_data(folha.data_afastamento) or '' + # Gerado GRRF + sefip.trabalhador_indic_recolhimento_fgts = 'S' + + return sefip._registro_32_movimentacao_do_trabalhador() + + def _gerar_anexo(self, nome_do_arquivo, path_arquivo_temp): + """ + Função gerar anexo dentro do SEFIP, sobrescrevendo anexo existente. + Apartir de um arquivo temporário. Deve ser passado o path do + arquivo temporário que se tornará anexo da sefip + :param nome_do_arquivo: + :param path_arquivo_temp: + :return: + """ + attachment_obj = self.env['ir.attachment'] + sefip_attachment_obj = self.env['l10n_br.hr.sefip.attachments'] + + try: + file_attc = open(path_arquivo_temp, 'r') + attc = file_attc.read() + + attachment_data = { + 'name': nome_do_arquivo, + 'datas_fname': nome_do_arquivo, + 'datas': base64.b64encode(attc), + 'res_model': 'l10n_br.hr.sefip.attachments', + } + + if 'sent' in [line.type for line in self.related_attachment_ids]: + for line in self.related_attachment_ids: + if line.type == 'sent': + attach_id = attachment_obj.create(attachment_data) + line.attachment_ids = False + line.attachment_ids = [attach_id.id] + else: + sefip_attachment_data = { + 'name': 'Arquivo SEFIP', + 'sefip_id': self.id, + 'attachment_ids': [(0, 0, attachment_data)], + 'type': 'sent' + } + sefip_attachment_obj.create(sefip_attachment_data) + + file_attc.close() + + except: + raise Warning( + _('Impossível gerar Anexo do %s' % nome_do_arquivo)) diff --git a/l10n_br_hr_arquivos_governo/models/res_company.py b/l10n_br_hr_arquivos_governo/models/res_company.py new file mode 100644 index 000000000..c25648375 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/models/res_company.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +import logging + +from openerp import api, exceptions, fields, models, _ +from ..constantes_rh import CENTRALIZADORA + +_logger = logging.getLogger(__name__) + +try: + from pybrasil import telefone +except ImportError: + _logger.info('Cannot import pybrasil') + + +OUTRAS_ENTIDADES = [ + ('0000', "Em branco"), + ('0001', u"Salário educação"), + ('0002', u"INCRA"), + ('0003', u"Salário educação + INCRA"), + ('4096', u"SESCOOP"), + ('4097', u"SESCOOP + Salário educação"), + ('4098', u"SESCOOP + INCRA"), + ('4099', u"SESCOOP + INCRA + Salário educação"), +] + + +class ResCompany(models.Model): + """Override company to activate validate phones""" + + _inherit = "res.company" + + @api.constrains('phone') + def _check_phone_br(self): + if self.phone: + if not telefone.valida_fone(self.phone): + raise exceptions.Warning( + _('Número do Telefone Inválido')) + + supplier_partner_id = fields.Many2one( + string='Fornecedor do sistema', comodel_name='res.partner' + ) + + codigo_outras_entidades = fields.Selection( + string="Código de Outras Entidades", + selection=OUTRAS_ENTIDADES, + default='0000' + ) + + codigo_recolhimento_GPS = fields.Integer( + string=u"Código de recolhimento do GPS", + ) + + porcentagem_filantropia = fields.Float( + string=u"Porcentagem de Isenção de Filantropia", + default=000.00, + ) + + centralizadora = fields.Selection( + selection=CENTRALIZADORA, + string=u'Centralizadora do FGTS' + ) + + document_type_sindicato_id = fields.Many2one( + comodel_name=u'financial.document.type', + string=u'Documento Sindicato', + help=u'Tipo de documento para boleto do sindicato.', + ) + + financial_account_sindicato_id = fields.Many2one( + comodel_name=u'financial.account', + string=u'Conta Sindicato', + help=u'Conta para lançamentos do sindicato.', + ) + + payment_mode_sindicato_id = fields.Many2one( + string=u'Carteira para Sindicato', + comodel_name=u'payment.mode', + help=u'Carteira padrão para geração de boletos do sindicato.', + ) + + darf_account_id = fields.Many2one( + string=u'Conta', + comodel_name=u'financial.account', + help=u'Conta para lançamentos da DARF' + ) + darf_document_type = fields.Many2one( + string=u'Tipo do documento', + comodel_name=u'financial.document.type', + help=u'Tipo do documento que irá aparecer no lançamento financeiro' + ) + darf_carteira_cobranca = fields.Many2one( + string=u'Carteira de cobrança', + comodel_name=u'payment.mode', + help=u'Nome da carteira de cobrança caso exista' + ) + darf_sequence_id = fields.Many2one( + string=u'Sequencia dos documentos', + comodel_name=u'ir.sequence' + ) + darf_dia_vencimento = fields.Integer( + string=u'Dia de vencimento', + help=u'Dia de vencimento da guia do DARF de todo mês' + ) + + gps_account_id = fields.Many2one( + string=u'Conta', + comodel_name=u'financial.account', + help=u'Conta para lançamentos da DARF' + ) + gps_document_type = fields.Many2one( + string=u'Tipo do documento', + comodel_name=u'financial.document.type', + help=u'Tipo do documento que irá aparecer no lançamento financeiro' + ) + gps_carteira_cobranca = fields.Many2one( + string=u'Carteira de cobrança', + comodel_name=u'payment.mode', + help=u'Nome da carteira de cobrança caso exista' + ) + gps_sequence_id = fields.Many2one( + string=u'Sequencia dos documentos', + comodel_name=u'ir.sequence' + ) diff --git a/l10n_br_hr_arquivos_governo/security/hr_payslip.xml b/l10n_br_hr_arquivos_governo/security/hr_payslip.xml new file mode 100644 index 000000000..8b7238801 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/security/hr_payslip.xml @@ -0,0 +1,20 @@ + + + + + + + + hr.payslip access name + + + + + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/security/ir.model.access.csv b/l10n_br_hr_arquivos_governo/security/ir.model.access.csv new file mode 100644 index 000000000..06bbad3fc --- /dev/null +++ b/l10n_br_hr_arquivos_governo/security/ir.model.access.csv @@ -0,0 +1,10 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_hr_caged,access_hr_caged,model_hr_caged,,1,0,0,0 +"access_hr_caged_manager","create_hr_caged","model_hr_caged","base.group_hr_manager",1,1,1,1 +"l10n_br_account_cnae","l10n_br_account.cnae","l10n_br_account.model_l10n_br_account_cnae","base.group_hr_user",1,0,0,0 +access_hr_sefip,access_hr_sefip,model_l10n_br_hr_sefip,,1,0,0,0 +"access_hr_sefip_manager","create_hr_sefip","model_l10n_br_hr_sefip","base.group_hr_manager",1,1,1,1 +access_hr_sefip_attachment,access_hr_sefip_attachment,model_l10n_br_hr_sefip_attachments,,1,0,0,0 +"access_hr_sefip_attachment_manager","create_hr_sefip_attachment","model_l10n_br_hr_sefip_attachments","base.group_hr_manager",1,1,1,1 +access_hr_caged_attachment,access_hr_caged_attachment,model_l10n_br_hr_caged_attachments,,1,0,0,0 +"access_hr_caged_attachment_manager","create_hr_caged_attachment","model_l10n_br_hr_caged_attachments","base.group_hr_manager",1,1,1,1 diff --git a/l10n_br_hr_arquivos_governo/views/hr_contract_type.xml b/l10n_br_hr_arquivos_governo/views/hr_contract_type.xml new file mode 100644 index 000000000..99d13ca96 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/hr_contract_type.xml @@ -0,0 +1,21 @@ + + + + + + + + + hr.contract.type.view.form (in l10n_br_hr_arquivos_governo) + hr.contract.type + + + + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/views/hr_payslip.xml b/l10n_br_hr_arquivos_governo/views/hr_payslip.xml new file mode 100644 index 000000000..e8f4e3e3a --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/hr_payslip.xml @@ -0,0 +1,79 @@ + + + + + + + + hr.payslip.form (in l10n_br_hr_arquivos_governo) + hr.payslip + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/views/inherited_hr_salary_rule_view.xml b/l10n_br_hr_arquivos_governo/views/inherited_hr_salary_rule_view.xml new file mode 100644 index 000000000..241c3a197 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/inherited_hr_salary_rule_view.xml @@ -0,0 +1,17 @@ + + + + + + hr.salary.rule (Financial Payment Order) + hr.salary.rule + + + + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/views/l10n_br_hr_caged.xml b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_caged.xml new file mode 100644 index 000000000..1ae068811 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_caged.xml @@ -0,0 +1,112 @@ + + + + + + + + + + hr.caged.form (in l10n_br_hr_arquivos_governo) + hr.caged + +
+
+
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+
+ + + hr.caged.tree (in l10n_br_hr_arquivos_governo) + hr.caged + + + + + + + + + + + + + CAGED's + hr.caged + tree,form + + + + CAGED + + + + + + +
+
diff --git a/l10n_br_hr_arquivos_governo/views/l10n_br_hr_contract.xml b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_contract.xml new file mode 100644 index 000000000..15871780e --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_contract.xml @@ -0,0 +1,36 @@ + + + + + + + + + hr.contract.view.form (in l10n_br_hr_arquivos_governo) + hr.contract + + + + 1 + + + + + + l10n_br.hr.contract.view.form(in l10n_br_hr_arquivos_governo) + hr.contract + + + + 1 + + + + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/views/l10n_br_hr_employee.xml b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_employee.xml new file mode 100644 index 000000000..2b1994063 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_employee.xml @@ -0,0 +1,79 @@ + + + + + + + + hr.employee.view.form (in l10n_br_hr_arquivos_governo) + hr.employee + + + + 1 + + + 1 + + + 1 + + + + + + l10n_br.hr.employee.view.form(in l10n_br_hr_arquivos_governo) + hr.employee + + + + + 1 + + + + 1 + + + + 1 + + + + 1 + + + + 1 + + + + 1 + + + + 1 + + + + + + + diff --git a/l10n_br_hr_arquivos_governo/views/l10n_br_hr_sefip.xml b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_sefip.xml new file mode 100644 index 000000000..db3223f9f --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/l10n_br_hr_sefip.xml @@ -0,0 +1,152 @@ + + + + + + + + SEFIP + l10n_br.hr.sefip + tree,form + [] + {} + + + + SEFIP + + + + + + SEFIP + + + + + + + hr.sefip.form + l10n_br.hr.sefip + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+
+ + + hr.sefip.tree (in l10n_br_hr_arquivos_governo) + l10n_br.hr.sefip + + + + + + + + + + + +
+
diff --git a/l10n_br_hr_arquivos_governo/views/res_company.xml b/l10n_br_hr_arquivos_governo/views/res_company.xml new file mode 100644 index 000000000..9a2487774 --- /dev/null +++ b/l10n_br_hr_arquivos_governo/views/res_company.xml @@ -0,0 +1,62 @@ + + + + + + + l10n_br.hr.arquivos.governo.res.company.form.view + res.company + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/l10n_br_hr_backup/README.rst b/l10n_br_hr_backup/README.rst new file mode 100644 index 000000000..c4f33a181 --- /dev/null +++ b/l10n_br_hr_backup/README.rst @@ -0,0 +1,75 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================= +l10n_br Hr Backup +================= + +This module allow backup salary rules and payroll structures + + +Installation +============ + +This module depends on : + +l10n_br_hr_payroll + + +Configuration +============= + + +Usage +===== + +Este módulo faz backup de tres modelos: + +- hr.salary.rule +- hr.payroll.structure +- hr.salary.rule.category + +Após instalado o modulo, navegar até o menu de backup em: + + Recursos Humanos > Configurações > HR Backup + +Em seguida Confirmar o backup. + + +A rotina de backup consiste em varrer o sistema, identificar todas as regras de salarios, + estruturas de salario e categorias de salarios que foram criadas ou editadas via interface + e escrever em arquivos XML que estão pre-estabelecidos na pasta data do próprio modelo. + + +Estrutura de arquivos: + +.. image:: /l10n_br_hr_backup/static/description/tree_files.png + + + +For further information, please visit: + + * https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + + * no known issues + + +Credits +======= + +Contributors +------------ + +* Hendrix Costa + + +Maintainer +---------- + +.. image:: http://www.abgf.gov.br/wp-content/themes/abgf/images/header-logo.png + :alt: ABGF + :target: http://www.abgf.gov.br diff --git a/l10n_br_hr_backup/__init__.py b/l10n_br_hr_backup/__init__.py new file mode 100644 index 000000000..f0750d8eb --- /dev/null +++ b/l10n_br_hr_backup/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_br_hr_backup/__openerp__.py b/l10n_br_hr_backup/__openerp__.py new file mode 100644 index 000000000..c60510284 --- /dev/null +++ b/l10n_br_hr_backup/__openerp__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Backup HR', + 'summary': 'Backup Human Resource', + 'version': '8.0.0.0.1', + 'category': 'Generic Modules', + 'website': 'http://www.kmee.com.br', + 'author': "KMEE, Odoo Community Association (OCA)", + "license": "AGPL-3", + 'depends': [ + 'l10n_br_hr_payroll', + ], + 'data': [ + # 'data/hr_salary_rule_category.xml', + # 'data/hr_salary_rule.xml', + # 'data/hr_payroll_structure.xml', + 'views/hr_backup.xml', + 'views/hr_payroll_structure.xml', + 'views/hr_salary_rule.xml', + # 'views/hr_salary_rule_category.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, +} diff --git a/l10n_br_hr_backup/data/hr_payroll_structure.xml b/l10n_br_hr_backup/data/hr_payroll_structure.xml new file mode 100644 index 000000000..4f640ca50 --- /dev/null +++ b/l10n_br_hr_backup/data/hr_payroll_structure.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/l10n_br_hr_backup/data/hr_salary_rule.xml b/l10n_br_hr_backup/data/hr_salary_rule.xml new file mode 100644 index 000000000..4f640ca50 --- /dev/null +++ b/l10n_br_hr_backup/data/hr_salary_rule.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/l10n_br_hr_backup/data/hr_salary_rule_category.xml b/l10n_br_hr_backup/data/hr_salary_rule_category.xml new file mode 100644 index 000000000..4f640ca50 --- /dev/null +++ b/l10n_br_hr_backup/data/hr_salary_rule_category.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/l10n_br_hr_backup/data/template_model.xml b/l10n_br_hr_backup/data/template_model.xml new file mode 100644 index 000000000..4f640ca50 --- /dev/null +++ b/l10n_br_hr_backup/data/template_model.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/l10n_br_hr_backup/models/__init__.py b/l10n_br_hr_backup/models/__init__.py new file mode 100644 index 000000000..26441c79e --- /dev/null +++ b/l10n_br_hr_backup/models/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import hr_backup +from . import hr_payroll_structure +from . import hr_salary_rule +from . import hr_salary_rule_category diff --git a/l10n_br_hr_backup/models/hr_backup.py b/l10n_br_hr_backup/models/hr_backup.py new file mode 100644 index 000000000..7e77d9aa4 --- /dev/null +++ b/l10n_br_hr_backup/models/hr_backup.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import logging +import os +import re + +from openerp import api, fields, models + +_logger = logging.getLogger(__name__) + + +class HrBackup(models.Model): + _name = 'hr.backup' + + @api.multi + def get_many_to_one_field(self, field_val, field_name, + criar_model_data=False): + """ + Função para retornar campo e valor no formato XML. + """ + # if isinstance(field_val, ) + + if field_val: + if field_val._get_external_ids().get(field_val.id): + field_val = field_val._get_external_ids().get(field_val.id)[0] + + elif criar_model_data: + # self.registrar_modelo_ir_model_data(field_val) + # _logger.info('Criado ir_model_data do Objeto relacional %s ' + # 'do modelo %s - %s' % + # (field_name, field_val._name, field_val.name)) + field_val = 'l10n_br_hr_payroll.' + \ + str(field_val._model).replace('.', '_') +\ + '_' + field_val.code + + else: + _logger.info( + "Informacao do campo %s no modelo %s nao foi salva." + "Campo relacional %s nao possui XML." % + (field_name, field_val._name, field_val.name)) + + linha = "\t\t\t\n" % \ + (field_name, field_val or '') + return linha + + @api.multi + def get_text_field(self, field_val, field_name): + """ + Função para retornar campo e valor no formato XML. + """ + linha = "\t\t\t%s\n" % \ + (field_name, field_val or '') + return linha + + @api.multi + def get_many_to_many(self, field_vals, field_name): + """ + Função para retornar campo e valor no formato XML. + """ + refs = '' + for field in field_vals: + if field and field._get_external_ids().get(field.id): + refs += "ref('%s'), " % \ + field._get_external_ids().get(field.id)[0] + line = \ + "\t\t\t\n" %\ + (field_name, refs[:-2]) + + return line + + @api.multi + def formata_caracteres_xml(self, field): + """ + Formata o XML que nao pode conter caracateres como '<=' ou '>=' + """ + # Validar a entrada de dados + if not isinstance(field, (str, unicode)): + return field + + # Menor igual + field = re.sub('<=', ' <= ', field) + # Menor + field = re.sub('<', ' < ', field) + # Maior Igual + field = re.sub('>=', ' >= ', field) + # Maior + field = re.sub('>', ' > ', field) + + return field + + @api.multi + def get_external_id(self, object, name): + """ + Retorna a referencia externa se existir senao retorna o nome + passado como parametro + :param object: instancia do objeto + :param name: str - nome do record ex.: hr_salary_rule_RUBRICA01 + """ + if object._get_external_ids().get(object.id): + return object._get_external_ids().get(object.id)[0] + return name + + @api.multi + def registrar_modelo_ir_model_data(self, objeto, name=False): + """ + Função que registra na tabela ir_model_data a existencia do objeto que + foi criado via interface + :param objeto: Objeto a ser registrado + :param name: nome do objeto a ser registrado na tabela, + :return: instancia do ir_model_data + """ + ir_model_data_obj = self.env['ir.model.data'] + + # Se nao passar nenhum nome no parametro, utilizar o nome do objeto + if not name: + name = objeto._name + + vals = { + 'name': name, + 'module': 'l10n_br_hr_payroll', + 'model': objeto._model, + 'res_id': objeto.id, + } + + return ir_model_data_obj.create(vals) + + @api.multi + def escrever_arquivo_backup(self, name, texto): + + template_path = '{}/../data/' + name + '.xml' + template_path = template_path.format(os.path.dirname(__file__)) + + xml_antigo = open(template_path, 'r') + xml_antigo = xml_antigo.read() + + if xml_antigo: + # Remover as TAGS de fechamento do arquivo + xml_antigo = re.sub(r'\<\/data>', '', xml_antigo) + xml_antigo = re.sub(r'\<\/openerp>', '', xml_antigo) + else: + xml_antigo = '\t\n\t\t\n' + + xml_final = xml_antigo + '\t\t\n' + xml_final += texto + xml_final += '\t\t\n\t\n' + + backup_xml = open(template_path, 'w') + backup_xml.write(xml_final) + backup_xml.close() + + @api.multi + def gerar_backup_objetos_criados(self, modelo): + """ + Função que busca as regras que foram criadas somente pela interface, + dispara uma função para registro na tabela ir_model_data e retorna um + texto em xml para gerar o backup das regras + """ + model_obj = self.env[modelo] + ir_model_data_obj = self.env['ir.model.data'] + + # Buscar todas as regras que foram geradas apartir do XML + res_id = ir_model_data_obj.search([ + ('model', '=', modelo), + ]).mapped('res_id') + + # Identificar regras geradas pela interface, que nao tem XML + regras_sem_xml = model_obj.search([ + ('id', 'not in', res_id), + ]) + + # variavel que contem as regras em xml + models_xml = u'' + + for model_sem_xml in regras_sem_xml: + # Se nao achar um id externo cria na tabela ir_model_data + if not self.get_external_id(model_sem_xml, False): + model_label = model_sem_xml.code \ + if model_sem_xml.code else str(model_sem_xml.id) + name = modelo.replace('.', '_') + '_' + model_label + self.registrar_modelo_ir_model_data(model_sem_xml, name) + models_xml += model_sem_xml.generate_xml + + return models_xml.encode('utf-8'), len(regras_sem_xml) + + @api.multi + def gerar_backup_objetos_editados(self, modelo): + """ + Função que gera um texto no formato xml para realizar o backup + """ + model_obj = self.env[modelo] + models = model_obj.search([]) + + models_xml = u'' + models_qty = 0 + for model in models: + if model.create_date != model.write_date: + if (not model.last_backup) or \ + (model.write_date > model.last_backup): + models_xml += model.generate_xml + models_qty += 1 + model.last_backup = fields.datetime.now() + return models_xml.encode('utf-8'), models_qty + + @api.multi + def atualizar_modelos_ja_exportados(self, model): + """ + Os modelos exportadas anteriormente, ja possuem registro no + ir_model_data, mas o registro aponta para um modulo inválido (export) + A função readequara esses registros apontando para o modulo de backup + :return: + """ + model_obj = self.env[model] + ir_model_data_obj = self.env['ir.model.data'] + + modelos_ja_exportados = ir_model_data_obj.search([ + ('model', '=', model), + ('module', '=', '__export__'), + ]) + + for modelo_ja_exportado in modelos_ja_exportados: + modelo_id = model_obj.browse(modelo_ja_exportado.res_id) + name = model.replace('.', '_') + '_' + modelo_id.code \ + if modelo_id.code else str(modelo_id.id) + modelo_ja_exportado.module = 'l10n_br_hr_payroll' + modelo_ja_exportado.name = name + modelo_id.write_date = fields.datetime.now() + + @api.multi + def gerar_backup(self): + """ + Rotina chamada pela interface para fazer backup dos objetos de RH + editados e criados via interface + """ + models = [ + 'hr.salary.rule.category', + 'hr.salary.rule', + 'hr.payroll.structure', + ] + + for model in models: + # Verificar Modelos ja exportados anteriormente + self.atualizar_modelos_ja_exportados(model) + + # Gerar xml com todas os objetos criados via interface + xml_objetos_criadas, qty_objetos_criadas = \ + self.gerar_backup_objetos_criados(model) + + # Gerar xml com todos os objetos editados via interface + xml_objetos_editadas, qty_objetos_editados = \ + self.gerar_backup_objetos_editados(model) + + # Ajustar estrutura do xml + xml_objetos = '\n\t\t \n\n' % \ + qty_objetos_criadas + + xml_objetos += xml_objetos_criadas + + xml_objetos += '\n\t\t \n\n' % \ + qty_objetos_editados + + xml_objetos += xml_objetos_editadas + + # Escreve no arquivo XML do modulo + self.escrever_arquivo_backup(model.replace('.', '_'), xml_objetos) diff --git a/l10n_br_hr_backup/models/hr_payroll_structure.py b/l10n_br_hr_backup/models/hr_payroll_structure.py new file mode 100644 index 000000000..caf64d3a4 --- /dev/null +++ b/l10n_br_hr_backup/models/hr_payroll_structure.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 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 + + +class HrPayrollStructure(models.Model): + _inherit = 'hr.payroll.structure' + + @api.multi + def _compute_xml_rubrica(self): + """ + Função que gera XML da estrutura atual + """ + + for regra in self: + + # Se a regra ja tiver identificação no ir_model_data exibe a + # external_id, senão exibe a external id que sera criada pelo + # modulo de backup + backup = self.env['hr.backup'] + + id = backup.get_external_id( + regra, 'hr_payroll_structure_%s' % regra.code) + + record = \ + "\t\t \n" % id + + record += backup.get_text_field(regra.code, 'code') + record += backup.get_text_field(regra.name, 'name') + record += backup.get_text_field(regra.note, 'note') + record += backup.get_many_to_many(regra.rule_ids, 'rule_ids') + record += backup.get_text_field(regra.tipo_saque, 'tipo_saque') + record += backup.get_many_to_one_field( + regra.parent_id, 'parent_id', criar_model_data=True) + record += backup.get_many_to_one_field( + regra.estrutura_ferias_id, 'estrutura_ferias_id', criar_model_data=True) + record += backup.get_many_to_one_field( + regra.estrutura_adiantamento_13_id, 'estrutura_adiantamento_13_id', criar_model_data=True) + record += backup.get_many_to_one_field( + regra.estrutura_13_id, 'estrutura_13_id', criar_model_data=True) + # record += backup.get_many_to_many( + # regra.children_ids, 'children_ids') + record += backup.get_many_to_one_field( + regra.company_id, 'company_id') + record += backup.get_text_field( + regra.tipo_afastamento_cef, 'tipo_afastamento_cef') + record += backup.get_text_field( + regra.tipo_afastamento_sefip, 'tipo_afastamento_sefip') + record += backup.get_text_field( + regra.tipo_estrutura, 'tipo_estrutura') + record += backup.get_text_field( + regra.tipo_desligamento_rais, 'tipo_desligamento_rais') + + record += "\t\t\n\n" + + regra.generate_xml = record + + generate_xml = fields.Text( + string='XML da rubrica gerado automaticamente', + compute='_compute_xml_rubrica', + help='Cole esse xml em seu modulo', + ) + + last_backup = fields.Datetime( + string='Data do ultimo backup', + ) diff --git a/l10n_br_hr_backup/models/hr_salary_rule.py b/l10n_br_hr_backup/models/hr_salary_rule.py new file mode 100644 index 000000000..42fa46912 --- /dev/null +++ b/l10n_br_hr_backup/models/hr_salary_rule.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 KMEE (http://www.kmee.com.br) +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from __future__ import unicode_literals, division, print_function + +from openerp import api, fields, models + + +class HrSalaryRule(models.Model): + _inherit = b'hr.salary.rule' + + @api.multi + def _compute_xml_rubrica(self): + """ + Função que gera XML da rubrica atual + """ + + for regra in self: + + # Se a regra ja tiver identificação no ir_model_data exibe a + # external_id, senão exibe a external id que sera criada pelo + # modulo de backup + backup = self.env['hr.backup'] + + id = backup.get_external_id( + regra, 'hr_salary_rule_%s' % regra.code) + + record = \ + "\t\t \n" % id + + record += backup.get_text_field(regra.code, 'code') + record += backup.get_text_field(regra.name, 'name') + record += backup.get_text_field(regra.active, 'active') + record += backup.get_text_field(regra.sequence, 'sequence') + + record += backup.get_many_to_one_field( + regra.category_id, 'category_id') + record += backup.get_text_field( + regra.condition_select, 'condition_select') + + condition = backup.formata_caracteres_xml(regra.condition_python) + record += backup.get_text_field(condition, 'condition_python') + amount = backup.formata_caracteres_xml(regra.amount_python_compute) + record += backup.get_text_field(amount, 'amount_python_compute') + + custom_amount = backup.formata_caracteres_xml( + regra.custom_amount_python_compute) + record += backup.get_text_field( + custom_amount, 'custom_amount_python_compute') + + record += backup.get_text_field( + regra.amount_select, 'amount_select') + record += backup.get_text_field( + regra.custom_amount_select, 'custom_amount_select') + record += backup.get_text_field( + regra.compoe_base_INSS, 'compoe_base_INSS') + record += backup.get_text_field( + regra.compoe_base_IR, 'compoe_base_IR') + record += backup.get_text_field( + regra.compoe_base_FGTS, 'compoe_base_FGTS') + record += backup.get_text_field( + regra.amount_fix, 'amount_fix') + record += backup.get_text_field( + regra.custom_amount_fix, 'custom_amount_fix') + record += backup.get_text_field( + regra.amount_percentage, 'amount_percentage') + record += backup.get_text_field( + regra.amount_percentage_base, 'amount_percentage_base') + record += backup.get_text_field( + regra.custom_amount_percentage_base, + 'custom_amount_percentage_base') + record += backup.get_text_field( + regra.custom_amount_percentage, 'custom_amount_percentage') + record += backup.get_text_field( + regra.appears_on_payslip, 'appears_on_payslip') + record += backup.get_text_field( + regra.condition_range, 'condition_range') + record += backup.get_text_field( + regra.condition_range_min, 'condition_range_min') + record += backup.get_text_field( + regra.condition_range_max, 'condition_range_max') + record += backup.get_text_field(regra.note, 'note') + record += backup.get_text_field( + regra.calculo_nao_padrao, 'calculo_nao_padrao') + record += backup.get_text_field(regra.quantity, 'quantity') + record += backup.get_text_field( + regra.custom_quantity, 'custom_quantity') + record += backup.get_text_field(regra.tipo_media, 'tipo_media') + record += backup.get_many_to_one_field( + regra.parent_rule_id, 'parent_rule_id', criar_model_data=True) + record += backup.get_many_to_one_field( + regra.company_id, 'company_id') + record += backup.get_many_to_one_field( + regra.register_id, 'register_id') + + record += "\t\t\n\n" + + regra.generate_xml = record + + generate_xml = fields.Text( + string='XML da rubrica gerado automaticamente', + compute='_compute_xml_rubrica', + help='Cole esse xml em seu modulo', + ) + + last_backup = fields.Datetime( + string='Data do ultimo backup', + ) diff --git a/l10n_br_hr_backup/models/hr_salary_rule_category.py b/l10n_br_hr_backup/models/hr_salary_rule_category.py new file mode 100644 index 000000000..b8318609c --- /dev/null +++ b/l10n_br_hr_backup/models/hr_salary_rule_category.py @@ -0,0 +1,50 @@ +# -*- 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 __future__ import unicode_literals, division, print_function + +from openerp import api, fields, models + + +class HrSalaryRuleCategory(models.Model): + _inherit = b'hr.salary.rule.category' + + @api.multi + def _compute_xml_rubrica(self): + """ + Função que gera XML da rubrica atual + """ + + for categoria in self: + + # Se a regra ja tiver identificação no ir_model_data exibe a + # external_id, senão exibe a external id que sera criada pelo + # modulo de backup + backup = self.env['hr.backup'] + + id = backup.get_external_id( + categoria, 'hr_salary_rule_category_%s' % categoria.code) + + record = \ + "\t\t \n" % id + record += backup.get_text_field(categoria.name, 'name') + record += backup.get_text_field(categoria.code, 'code') + record += backup.get_many_to_one_field( + categoria.company_id, 'company_id') + record += backup.get_text_field(categoria.note, 'note') + record += backup.get_many_to_one_field( + categoria.parent_id, 'parent_id', criar_model_data=True) + record += "\t\t\n\n" + categoria.generate_xml = record + + generate_xml = fields.Text( + string='XML da rubrica gerado automaticamente', + compute='_compute_xml_rubrica', + help='Cole esse xml em seu módulo', + ) + + last_backup = fields.Datetime( + string='Data do ultimo backup', + ) diff --git a/l10n_br_hr_backup/security/ir.model.access.csv b/l10n_br_hr_backup/security/ir.model.access.csv new file mode 100644 index 000000000..ec9a83d94 --- /dev/null +++ b/l10n_br_hr_backup/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_hr_backup,access_hr_backup,model_hr_backup,,1,0,0,0 diff --git a/l10n_br_hr_backup/static/description/tree_files.png b/l10n_br_hr_backup/static/description/tree_files.png new file mode 100644 index 000000000..1f47f0a64 Binary files /dev/null and b/l10n_br_hr_backup/static/description/tree_files.png differ diff --git a/l10n_br_hr_backup/views/hr_backup.xml b/l10n_br_hr_backup/views/hr_backup.xml new file mode 100644 index 000000000..ca8bfa9b5 --- /dev/null +++ b/l10n_br_hr_backup/views/hr_backup.xml @@ -0,0 +1,42 @@ + + + + + + + + hr.backup + hr.backup + +
+ +

Backup das Regras de Salario

+
+
+
+
+
+
+ + + HR Backup + hr.backup + form + form + + new + + + + +
+
diff --git a/l10n_br_hr_backup/views/hr_payroll_structure.xml b/l10n_br_hr_backup/views/hr_payroll_structure.xml new file mode 100644 index 000000000..8b6061312 --- /dev/null +++ b/l10n_br_hr_backup/views/hr_payroll_structure.xml @@ -0,0 +1,26 @@ + + + + + + + + + hr.payroll.structure.form (in l10n_br_hr_backup) + hr.payroll.structure + + + + + + + + + + + + + + diff --git a/l10n_br_hr_backup/views/hr_salary_rule.xml b/l10n_br_hr_backup/views/hr_salary_rule.xml new file mode 100644 index 000000000..537c42263 --- /dev/null +++ b/l10n_br_hr_backup/views/hr_salary_rule.xml @@ -0,0 +1,27 @@ + + + + + + + + + hr.salaray.rule.form (in l10n_br_hr_backup) + + hr.salary.rule + + + + + + + + + + + + + + diff --git a/l10n_br_hr_backup/views/hr_salary_rule_category.xml b/l10n_br_hr_backup/views/hr_salary_rule_category.xml new file mode 100644 index 000000000..03504e3b2 --- /dev/null +++ b/l10n_br_hr_backup/views/hr_salary_rule_category.xml @@ -0,0 +1,27 @@ + + + + + + + + + hr.salaray.rule.category.form (in l10n_br_hr_backup) + + hr.salary.rule.category + + + + + + + + + + + + + + diff --git a/l10n_br_hr_gerador_holerite/README.rst b/l10n_br_hr_gerador_holerite/README.rst new file mode 100644 index 000000000..10fa0f66b --- /dev/null +++ b/l10n_br_hr_gerador_holerite/README.rst @@ -0,0 +1,84 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================================== +Gerador de Folha de Pagamento Brasileira +======================================== + +Gerador de Holerites para simulações em férias ou 13º salario. + +Installation +============ + +To install this module, you need to: + +#. Do this ... + +Configuration +============= + +To configure this module, you need to: + +#. Go to ... + +.. figure:: path/to/local/image.png + :alt: alternative description + :width: 600 px + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Hendrix Costa + +Funders +------- + +The development of this module has been financially supported by: + +* Company 1 name +* Company 2 name + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/l10n_br_hr_gerador_holerite/__init__.py b/l10n_br_hr_gerador_holerite/__init__.py new file mode 100644 index 000000000..b1e7ef842 --- /dev/null +++ b/l10n_br_hr_gerador_holerite/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_br_hr_gerador_holerite/__openerp__.py b/l10n_br_hr_gerador_holerite/__openerp__.py new file mode 100644 index 000000000..3b93fe50b --- /dev/null +++ b/l10n_br_hr_gerador_holerite/__openerp__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Brazilian Localization HR Payroll Generator', + 'category': 'Localization', + 'license': 'AGPL-3', + 'author': 'KMEE, Odoo Community Association (OCA)', + 'maintainer': 'KMEE', + 'website': 'http://www.kmee.com.br', + 'version': '8.0.1.0.0', + 'depends': [ + 'l10n_br_hr_payroll', + ], + 'data': [ + 'views/hr_payslip_gerador.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/l10n_br_hr_gerador_holerite/models/__init__.py b/l10n_br_hr_gerador_holerite/models/__init__.py new file mode 100644 index 000000000..7d7528c97 --- /dev/null +++ b/l10n_br_hr_gerador_holerite/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import hr_payslip_gerador diff --git a/l10n_br_hr_gerador_holerite/models/hr_payslip_gerador.py b/l10n_br_hr_gerador_holerite/models/hr_payslip_gerador.py new file mode 100644 index 000000000..bcfb43032 --- /dev/null +++ b/l10n_br_hr_gerador_holerite/models/hr_payslip_gerador.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from dateutil.relativedelta import relativedelta +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 HrPayslipGenerator(models.Model): + _name = 'hr.payslip.generator' + + contract_id = fields.Many2one( + comodel_name='hr.contract', + string="Contrato", + ) + + 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, + ) + + def processar_holerites(self, holerite): + + holerite._compute_set_employee_id() + holerite.compute_sheet() + holerite.process_sheet() + + return holerite + + @api.multi + def gerar_holerites(self): + data_inicio_gerador = \ + str(self.ano) + '-' + str(self.mes_do_ano) + '-01' + cont = 0 + + data_inicio = fields.Date.from_string(data_inicio_gerador) + data_fim = data_inicio + relativedelta(months=1, days=-1) + + while cont <= 12: + + payslip_dict = { + 'contract_id': self.contract_id.id, + 'date_from': data_inicio, + 'date_to': data_fim, + 'employee_id': self.contract_id.employee_id.id, + 'mes_do_ano': data_inicio.month, + 'ano': data_inicio.year, + } + + payslip = self.env['hr.payslip'].create(payslip_dict) + self.processar_holerites(payslip) + + data_inicio += relativedelta(months=1) + data_fim = data_inicio + relativedelta(months=1, days=-1) + cont += 1 diff --git a/l10n_br_hr_gerador_holerite/security/ir.model.access.csv b/l10n_br_hr_gerador_holerite/security/ir.model.access.csv new file mode 100644 index 000000000..6c61453e0 --- /dev/null +++ b/l10n_br_hr_gerador_holerite/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_hr_payslip_generator","access_hr_payslip_generator","model_hr_payslip_generator",,1,0,0,0 diff --git a/l10n_br_hr_gerador_holerite/views/hr_payslip_gerador.xml b/l10n_br_hr_gerador_holerite/views/hr_payslip_gerador.xml new file mode 100644 index 000000000..42cafa0e9 --- /dev/null +++ b/l10n_br_hr_gerador_holerite/views/hr_payslip_gerador.xml @@ -0,0 +1,43 @@ + + + + + + l10n_br.hr.payslip.gerador.form + hr.payslip.generator + form + +
+ + + + + + + + + + + + + Período + + + + + @@ -23,6 +39,11 @@ + + + {'invisible':[('holiday_type','=','category')]} + +
@@ -39,12 +60,38 @@ name="Ocorrencias" action="l10n_br_hr_holiday_action_ocorrencias" parent="hr_holidays.menu_open_ask_holidays" - groups="base.group_hr_manager" + groups="base.group_user" sequence="6"/> - - Evento + + + + + Pedido de Férias + hr.holidays + + form + + { 'default_tipo': 'ferias', + 'default_type': 'remove', + 'search_default_my_leaves':1} + [('type', '=', 'remove'), + ('tipo', '=', 'ferias')] + + +

+ Clique para criar um novo pedido de férias. +

+ Uma vez criado o pedido de férias, ele será enviado ao + gerente para a aprovação. Verifique se foi selecionado + corretamente o número de dias e o período aquisitivo. +

+
+
+ +
diff --git a/l10n_br_hr_payment_order/README.rst b/l10n_br_hr_payment_order/README.rst new file mode 100644 index 000000000..4148462d6 --- /dev/null +++ b/l10n_br_hr_payment_order/README.rst @@ -0,0 +1,79 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== +L10n Br Hr Payment Order +======================== + +Integração da folha de pagamento com as ordens de pagamento + +Installation +============ + +To install this module, you need to: + +#. Do this ... + +Configuration +============= + +To configure this module, you need to: + +#. Go to ... + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +* ... + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Firstname Lastname +* Second Person + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/l10n_br_hr_payment_order/__init__.py b/l10n_br_hr_payment_order/__init__.py new file mode 100644 index 000000000..ec485ab97 --- /dev/null +++ b/l10n_br_hr_payment_order/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import wizard diff --git a/l10n_br_hr_payment_order/__openerp__.py b/l10n_br_hr_payment_order/__openerp__.py new file mode 100644 index 000000000..40391ffa3 --- /dev/null +++ b/l10n_br_hr_payment_order/__openerp__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'L10n Br Hr Payment Order', + 'summary': """ + Integração da folha de pagamento com as ordens de pagamento""", + 'version': '8.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'KMEE,Odoo Community Association (OCA)', + 'website': 'www.kmee.com.br', + 'depends': [ + 'l10n_br_hr_payroll', + 'l10n_br_financial_payment_order', + 'l10n_br_account_banking_payment_cnab', + 'account_payment_purchase', + ], + 'data': [ + 'security/hr_payslip.xml', + 'security/payment_order.xml', + 'security/ir.model.access.csv', + + 'wizard/payslip_payment_create_order_view.xml', + + 'views/hr_payslip.xml', + 'views/hr_contract.xml', + 'views/hr_salary_rule_view.xml', + 'views/payment_mode.xml', + 'views/payment_order.xml', + 'views/res_config_view.xml', + + 'hr_payroll_workflow.xml', + 'payment_order_workflow.xml', + ], + 'demo': [ + 'demo/hr_salary_rule.xml', + 'demo/hr_payslip.xml', + 'demo/payment_order.xml', + ], +} diff --git a/l10n_br_hr_payment_order/demo/hr_payslip.xml b/l10n_br_hr_payment_order/demo/hr_payslip.xml new file mode 100644 index 000000000..f33605d09 --- /dev/null +++ b/l10n_br_hr_payment_order/demo/hr_payslip.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/demo/hr_salary_rule.xml b/l10n_br_hr_payment_order/demo/hr_salary_rule.xml new file mode 100644 index 000000000..15eca9911 --- /dev/null +++ b/l10n_br_hr_payment_order/demo/hr_salary_rule.xml @@ -0,0 +1,16 @@ + + + + + + + True + + + + True + + + + diff --git a/l10n_br_hr_payment_order/demo/payment_order.xml b/l10n_br_hr_payment_order/demo/payment_order.xml new file mode 100644 index 000000000..b1f6faf12 --- /dev/null +++ b/l10n_br_hr_payment_order/demo/payment_order.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/hr_payroll_workflow.xml b/l10n_br_hr_payment_order/hr_payroll_workflow.xml new file mode 100644 index 000000000..6e6ccd3d0 --- /dev/null +++ b/l10n_br_hr_payment_order/hr_payroll_workflow.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + paid_order + + + diff --git a/l10n_br_hr_payment_order/models/__init__.py b/l10n_br_hr_payment_order/models/__init__.py new file mode 100644 index 000000000..424de0b3f --- /dev/null +++ b/l10n_br_hr_payment_order/models/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import payment_order +from . import payment_line +from . import hr_payslip +from . import hr_contract +# from . import hr_payslip_run +from . import res_config +from . import hr_salary_rule +from . import bank_payment_line diff --git a/l10n_br_hr_payment_order/models/bank_payment_line.py b/l10n_br_hr_payment_order/models/bank_payment_line.py new file mode 100644 index 000000000..bdd50691e --- /dev/null +++ b/l10n_br_hr_payment_order/models/bank_payment_line.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields + + +class BankPaymentLine(models.Model): + _inherit = 'bank.payment.line' + + payslip_id = fields.Many2one( + string="Holerite", + comodel_name="hr.payslip" + ) diff --git a/l10n_br_hr_payment_order/models/hr_contract.py b/l10n_br_hr_payment_order/models/hr_contract.py new file mode 100644 index 000000000..a69c8a286 --- /dev/null +++ b/l10n_br_hr_payment_order/models/hr_contract.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class HrContract(models.Model): + _inherit = 'hr.contract' + + payment_mode_id = fields.Many2one( + string="Forma de Pagamento padrão do holerite", + comodel_name='payment.mode', + domain="[('tipo_pagamento', '=', 'folha')]" + ) diff --git a/l10n_br_hr_payment_order/models/hr_payslip.py b/l10n_br_hr_payment_order/models/hr_payslip.py new file mode 100644 index 000000000..bf5a7d776 --- /dev/null +++ b/l10n_br_hr_payment_order/models/hr_payslip.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# 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 HrPayslip(models.Model): + + _inherit = 'hr.payslip' + + payment_mode_id = fields.Many2one( + string="Modo de Pagamento", + comodel_name='payment.mode', + domain="[('tipo_pagamento', '=', 'folha')]" + ) + + payment_order_id = fields.Many2one( + string="Ordem de pagamento", + comodel_name='payment.order', + readonly=True, + # domain="[('type', '=', type)]", + ) + + payment_line_ids = fields.One2many( + string="Pagamentos", + comodel_name="payment.line", + inverse_name="payslip_id", + readonly=True, + ) + + paid_order = fields.Boolean( + string='Pago', + compute='_compute_paid', + readonly=True, + store=True, + ) + + @api.multi + def test_paid(self): + if not self.payment_line_ids: + return False + for line in self.payment_line_ids: + if not line.state2: + return False + if line.state2 != 'paid': + return False + return True + + @api.one + @api.depends('payment_line_ids.bank_line_id.state2') + def _compute_paid(self): + self.paid_order = self.test_paid() + + def _compute_set_employee_id(self): + """ + Setar a forma de pagamento no compute do holerite, buscando do contrato + """ + super(HrPayslip, self)._compute_set_employee_id() + for record in self: + if record.contract_id: + record.payment_mode_id = record.contract_id.payment_mode_id + # partner_id = \ + # record.contract_id.employee_id.parent_id.user_id.partner_id + # record.payment_mode_id = partner_id.supplier_payment_mode + + @api.multi + def action_done(self): + self.write({'state': 'done'}) + return True + + def create_payorder(self, mode_payment): + ''' + Cria um payment order com base no metodo de pagamento + :param mode_payment: Modo de pagamento + :return: objeto do payment.order + ''' + payment_order_model = self.env['payment.order'] + vals = {'mode': mode_payment.id, } + return payment_order_model.create(vals) + + @api.multi + def create_payment_order_line( + self, payment_order, total, communication, partner_id): + """ + Cria a linha da ordem de pagamento + """ + payment_line_model = self.env['payment.line'] + vals = { + 'order_id': payment_order.id, + 'bank_id': self.contract_id.conta_bancaria_id.id, + 'partner_id': partner_id.id, + # 'move_line_id': self.id, + 'communication': communication, + # 'communication_type': communication_type, + # 'currency_id': currency_id, + 'amount_currency': total, + # date is set when the user confirms the payment order + 'payslip_id': self.id, + } + return payment_line_model.create(vals) + + @api.multi + def create_payment_order(self): + + payment_order_model = self.env['payment.order'] + + for holerite in self: + if holerite.state != 'verify': + raise UserError(_( + "The payslip %s is not in Open state") % + holerite.contract_id.nome_contrato) + if not holerite.payment_mode_id: + raise UserError(_( + "No Payment Mode on holerite %s") % holerite.number) + + # Buscar ordens de pagamento do mesmo tipo + payorders = payment_order_model.search([ + ('mode', '=', holerite.payment_mode_id.id), + ('state', '=', 'draft')] + ) + + if payorders: + payorder = payorders[0] + else: + payorder = self.create_payorder(holerite.payment_mode_id) + + for rubrica in holerite.line_ids: + if rubrica.code == 'LIQUIDO': + self.create_payment_order_line( + payorder, rubrica.total, + 'SALARIO ' + holerite.data_mes_ano, rubrica.partner_id) + + if rubrica.code == 'PENSAO_ALIMENTICIA': + self.create_payment_order_line( + payorder, rubrica.total, + 'PENSAO ALIMENTICIA ' + holerite.data_mes_ano, + rubrica.partner_id) diff --git a/l10n_br_hr_payment_order/models/hr_payslip_run.py b/l10n_br_hr_payment_order/models/hr_payslip_run.py new file mode 100644 index 000000000..5942e3bf5 --- /dev/null +++ b/l10n_br_hr_payment_order/models/hr_payslip_run.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +# import logging +# from openerp import exceptions, _ +# from openerp import api, models +# +# _logger = logging.getLogger(__name__) +# +# class HrPayslipRun(models.Model): +# # _inherit = "hr.payslip.run" +# +# @api.multi +# def gerar_holerites(self): +# for lote in self: +# lote.verificar_holerites_gerados() +# for contrato in lote.contract_id: +# try: +# payslip_obj = self.env['hr.payslip'] +# payslip = payslip_obj.create({ +# 'contract_id': contrato.id, +# 'mes_do_ano': self.mes_do_ano, +# 'mes_do_ano2': self.mes_do_ano, +# 'ano': self.ano, +# 'employee_id': contrato.employee_id.id, +# 'tipo_de_folha': self.tipo_de_folha, +# 'payslip_run_id': self.id, +# }) +# payslip._compute_set_dates() +# payslip.compute_sheet() +# _logger.info(u"Holerite " + contrato.name + u" processado com sucesso!") +# # Mudado o processo para executar o hr_verify_sheet no +# # botão "Close" do Lote do Holerite ao invés do botão +# # "Gerar Holerites" +# # payslip.hr_verify_sheet() +# except: +# _logger.warning(u"Holerite " + contrato.name + u" falhou durante o cálculo!") +# payslip.unlink() +# continue +# lote.verificar_holerites_gerados() diff --git a/l10n_br_hr_payment_order/models/hr_salary_rule.py b/l10n_br_hr_payment_order/models/hr_salary_rule.py new file mode 100644 index 000000000..5b9c84311 --- /dev/null +++ b/l10n_br_hr_payment_order/models/hr_salary_rule.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields + + +class HrSalaryRule(models.Model): + _inherit = "hr.salary.rule" + + eh_pagavel = fields.Boolean( + string="É pagavel?" + ) diff --git a/l10n_br_hr_payment_order/models/payment_line.py b/l10n_br_hr_payment_order/models/payment_line.py new file mode 100644 index 000000000..da335ebee --- /dev/null +++ b/l10n_br_hr_payment_order/models/payment_line.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + + +class PaymentLine(models.Model): + _inherit = 'payment.line' + + payslip_id = fields.Many2one( + string="Ref do Holerite", + comodel_name="hr.payslip", + ) + + def _get_payment_line_reference(self): + res = super(PaymentLine, self)._get_payment_line_reference() + res.append(( + self.env['hr.payslip']._name, + self.env['hr.payslip']._description + )) + return res + + @api.multi + @api.depends('payslip_id', 'financial_id') + def _compute_reference_id(self): + + # if not mode == 'folha': + # return super + + for record in self: + if record.payslip_id: + record.reference_id = ( + record.payslip_id._name + + ',' + + str(record.payslip_id.id) + ) diff --git a/l10n_br_hr_payment_order/models/payment_order.py b/l10n_br_hr_payment_order/models/payment_order.py new file mode 100644 index 000000000..e5268c677 --- /dev/null +++ b/l10n_br_hr_payment_order/models/payment_order.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + +from openerp.addons.l10n_br_hr_payroll.models.hr_payslip import ( + TIPO_DE_FOLHA, +) + + +class PaymentOrder(models.Model): + _inherit = 'payment.order' + + tipo_de_folha = fields.Selection( + selection=TIPO_DE_FOLHA, + string=u'Tipo de folha', + default='normal', + states={'done': [('readonly', True)]}, + ) + + hr_payslip_ids = fields.One2many( + string='Holerites', + comodel_name='hr.payslip', + inverse_name='payment_order_id', + readonly=True, + states={'draft': [('readonly', False)]}, + ) + + mesmo_banco = fields.Boolean( + string="Somente para Mesmo Banco ?", + default=True, + ) + + cnab_file = fields.Binary( + string='CNAB File', + readonly=True, + ) + + cnab_filename = fields.Char("CNAB Filename") + + @api.multi + def _prepare_folha_payment_line(self, line): + self.ensure_one() +# date_to_pay = False # no payment date => immediate payment + state = 'normal' + communication = 'Holerite: ' + line.display_name or '-' + amount_currency = line.total + + # Seta no Holerite em qual remessa esta o pagamento + line.slip_id.payment_order_id = self.id + + if line.partner_id != line.slip_id.employee_id.address_home_id: + banco = line.partner_id.bank_ids[0].id, + else: + banco = line.slip_id.employee_id.bank_account_id.id, + + res = { + 'amount_currency': amount_currency, + 'bank_id': banco, + 'order_id': self.id, + 'partner_id': line.partner_id and line.partner_id.id or False, + # account banking + 'communication': communication, + 'state': state, + # end account banking + 'date': self.date_scheduled, + 'payslip_id': line.slip_id.id, + } + return res + + @api.one + def folha_payment_import(self): + """ A importação de holerites nas payment orders funciona da + seguinte maneira: + + 1. Busca holerites que estão no status: "Aguardando pagamento" e + coincidem com o tipo setado no filtro + + 2. Preparar: Prepara os dados para inclusão: + _prepare_financial_payment_line + 3. Criar + """ + + # Identifica o banco de pagamento + banco = self.mode.bank_id.bank.id + + self.line_ids.unlink() + self.hr_payslip_ids = False + + payslip_ids = self.env['hr.payslip'].search([ + ('tipo_de_folha', '=', self.tipo_de_folha), + ('state', '=', 'verify'), + ('payment_mode_id', '=', self.mode.id), + ]) + + rubricas_pagaveis = self.env['hr.salary.rule'].search([ + ('eh_pagavel', '=', True) + ]) + + payslip_line_ids = self.env['hr.payslip.line'].search([ + ('slip_id', 'in', payslip_ids.ids), + ('salary_rule_id', 'in', rubricas_pagaveis.ids) + ]) + + # Populate the current payment with new lines: + for line in payslip_line_ids: + + # Identifica o banco de pagamento do holerite + if line.partner_id != line.slip_id.employee_id.address_home_id: + if line.partner_id.bank_ids: + banco_holerite = line.partner_id.bank_ids[0].bank.id + else: + banco_holerite = False + else: + banco_holerite = \ + line.slip_id.employee_id.bank_account_id.bank.id + + if banco_holerite and banco == banco_holerite: + vals = self._prepare_folha_payment_line(line) + self.env['payment.line'].create(vals) + + return diff --git a/l10n_br_hr_payment_order/models/res_config.py b/l10n_br_hr_payment_order/models/res_config.py new file mode 100644 index 000000000..33789a69c --- /dev/null +++ b/l10n_br_hr_payment_order/models/res_config.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class AccountResConfig(models.Model): + _inherit = "account.config.settings" + + automated_payslip_payment_order = fields.Boolean( + string="Gerar Ordem de Pagamento dos Holerites Automaticamente", + ) diff --git a/l10n_br_hr_payment_order/payment_order_workflow.xml b/l10n_br_hr_payment_order/payment_order_workflow.xml new file mode 100644 index 000000000..e52aa37a7 --- /dev/null +++ b/l10n_br_hr_payment_order/payment_order_workflow.xml @@ -0,0 +1,15 @@ + + + + + + + cancel + + cancel() + function + True + + + + diff --git a/l10n_br_hr_payment_order/security/hr_payslip.xml b/l10n_br_hr_payment_order/security/hr_payslip.xml new file mode 100644 index 000000000..67fe21c16 --- /dev/null +++ b/l10n_br_hr_payment_order/security/hr_payslip.xml @@ -0,0 +1,20 @@ + + + + + + + + hr.payslip access name + + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/security/ir.model.access.csv b/l10n_br_hr_payment_order/security/ir.model.access.csv new file mode 100644 index 000000000..2be2aa486 --- /dev/null +++ b/l10n_br_hr_payment_order/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_payslip_payment_order_create,access_payslip_payment_order_create,model_payslip_payment_order_create,,1,0,1,0 +access_account_config_settings,access_account_config_settings,model_account_config_settings,,1,0,0,0 \ No newline at end of file diff --git a/l10n_br_hr_payment_order/security/payment_order.xml b/l10n_br_hr_payment_order/security/payment_order.xml new file mode 100644 index 000000000..3e7116a37 --- /dev/null +++ b/l10n_br_hr_payment_order/security/payment_order.xml @@ -0,0 +1,20 @@ + + + + + + + + payment.order access name + + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/static/description/icon.png b/l10n_br_hr_payment_order/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/l10n_br_hr_payment_order/static/description/icon.png differ diff --git a/l10n_br_hr_payment_order/views/hr_contract.xml b/l10n_br_hr_payment_order/views/hr_contract.xml new file mode 100644 index 000000000..f49156245 --- /dev/null +++ b/l10n_br_hr_payment_order/views/hr_contract.xml @@ -0,0 +1,20 @@ + + + + + + + + hr.contract.form (in l10n_br_hr_payment_order) + hr.contract + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/views/hr_payslip.xml b/l10n_br_hr_payment_order/views/hr_payslip.xml new file mode 100644 index 000000000..c31674519 --- /dev/null +++ b/l10n_br_hr_payment_order/views/hr_payslip.xml @@ -0,0 +1,70 @@ + + + + + + + + hr.payslip.form (in l10n_br_hr_payment_order) + hr.payslip + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/views/hr_salary_rule_view.xml b/l10n_br_hr_payment_order/views/hr_salary_rule_view.xml new file mode 100644 index 000000000..b9c61d9b0 --- /dev/null +++ b/l10n_br_hr_payment_order/views/hr_salary_rule_view.xml @@ -0,0 +1,17 @@ + + + + + + hr.salary.rule.payment + hr.salary.rule + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/views/payment_mode.xml b/l10n_br_hr_payment_order/views/payment_mode.xml new file mode 100644 index 000000000..ab0482be5 --- /dev/null +++ b/l10n_br_hr_payment_order/views/payment_mode.xml @@ -0,0 +1,38 @@ + + + + + + + + Pagamento Folha + payment.mode + tree,form + [('tipo_pagamento', '=', 'folha'), ('tipo_servico', '=', '30')] + {'default_tipo_pagamento': 'folha', 'default_tipo_servico': '30'} + + + + Pagamento Folha + + + + + + + + form + + + + + + + tree + + + + + + \ No newline at end of file diff --git a/l10n_br_hr_payment_order/views/payment_order.xml b/l10n_br_hr_payment_order/views/payment_order.xml new file mode 100644 index 000000000..740cac484 --- /dev/null +++ b/l10n_br_hr_payment_order/views/payment_order.xml @@ -0,0 +1,107 @@ + + + + + + + + + payment.order.folha.form (in l10n_br_financial_payment_order) + payment.order + primary + + + + Pagamento da Folha + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + {'no_create': True} + + + + {'no_create': True} + + + + + + + + + + + + + + Remessa de Folha de Pagamento + payment.order + tree,form + [('tipo_pagamento', '=', 'folha')] + {'default_tipo_pagamento': 'folha', 'default_date_prefered': 'fixed'} + + + + Remessa de Folha de Pagamento + + + + + + + + form + + + + + + + tree + + + + + + diff --git a/l10n_br_hr_payment_order/views/res_config_view.xml b/l10n_br_hr_payment_order/views/res_config_view.xml new file mode 100644 index 000000000..e8b8587f1 --- /dev/null +++ b/l10n_br_hr_payment_order/views/res_config_view.xml @@ -0,0 +1,24 @@ + + + + + + account.settings + account.config.settings + + + + + + + + + + + + diff --git a/l10n_br_hr_payment_order/wizard/__init__.py b/l10n_br_hr_payment_order/wizard/__init__.py new file mode 100644 index 000000000..4a8401a94 --- /dev/null +++ b/l10n_br_hr_payment_order/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import payslip_payment_create_order diff --git a/l10n_br_hr_payment_order/wizard/payslip_payment_create_order.py b/l10n_br_hr_payment_order/wizard/payslip_payment_create_order.py new file mode 100644 index 000000000..13686c6bf --- /dev/null +++ b/l10n_br_hr_payment_order/wizard/payslip_payment_create_order.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from openerp import api, models, fields, _ +from openerp.addons.l10n_br_hr_payroll.models.hr_payslip import \ + TIPO_DE_FOLHA, MES_DO_ANO + + +class PayslipPaymentCreateOrder(models.Model): + _name = "payslip.payment.order.create" + + tipo_de_folha = fields.Selection( + selection=TIPO_DE_FOLHA, + string=u'Tipo de folha', + default='normal', + ) + + mes_do_ano = fields.Selection( + selection=MES_DO_ANO, + string=u'Mês', + required=True, + default=datetime.now().month, + ) + + ano = fields.Integer( + strin="Ano", + default=datetime.now().year, + required=True, + ) + + entries = fields.Many2many( + comodel_name='hr.payslip.line', + rel='payorder_line_payslip_line_rel', + column1='pay_id', + column2='line_id', + string='Entries' + ) + + @api.model + def default_get(self, field_list): + res = super(PayslipPaymentCreateOrder, self).default_get(field_list) + context = self.env.context + if ('entries' in field_list and context.get('lines_id') and + context.get('populate_results')): + res.update({'entries': context['lines_id']}) + return res + + @api.multi + def buscar_linhas_holerites(self): + payslip_obj = self.env['hr.payslip'] + payslip_line_obj = self.env['hr.payslip.line'] + payslip_ids = payslip_obj.search( + [ + ('tipo_de_folha', '=', self.tipo_de_folha), + ('mes_do_ano', '=', self.mes_do_ano), + ('ano', '=', self.ano), + ('state', '=', 'verify') + ] + ) + rubricas_obj = self.env['hr.salary.rule'] + rubricas_pagaveis = rubricas_obj.search( + [ + ('eh_pagavel', '=', True) + ] + ) + payslip_line_ids = payslip_line_obj.search( + [ + ('slip_id', 'in', payslip_ids.ids), + ('salary_rule_id', 'in', rubricas_pagaveis.ids) + ] + ) + context = self.env.context.copy() + context['lines_id'] = payslip_line_ids.ids + context['populate_results'] = True + model_data_obj = self.env['ir.model.data'] + model_datas = model_data_obj.search( + [('model', '=', 'ir.ui.view'), + ('name', '=', 'payslip_payment_lines_create_order_view')]) + return {'name': _('Entry Lines'), + 'context': context, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'payslip.payment.order.create', + 'views': [(model_datas[0].res_id, 'form')], + 'type': 'ir.actions.act_window', + 'target': 'new', + } + + @api.multi + def _preparar_linha_do_holerite(self, payment, line): + self.ensure_one() + date_to_pay = False # no payment date => immediate payment + state = 'normal' + communication = 'Holerite: ' + line.display_name or '-' + amount_currency = line.total + res = { + 'amount_currency': amount_currency, + 'bank_id': line.contract_id.conta_bancaria_id.id, + 'order_id': payment.id, + 'partner_id': line.partner_id and line.partner_id.id or False, + # account banking + 'communication': communication, + 'state': state, + # end account banking + 'date': date_to_pay, + 'payslip_id': line.slip_id.id, + } + return res + + @api.multi + def create_payment(self): + if not self.entries: + return {'type': 'ir.actions.act_window_close'} + context = self.env.context + payment_line_obj = self.env['payment.line'] + payment = self.env['payment.order'].browse(context['active_id']) + # Populate the current payment with new lines: + for line in self.entries: + vals = self._preparar_linha_do_holerite(payment, line) + payment_line_obj.create(vals) + # Force reload of payment order view as a workaround for lp:1155525 + return {'name': _('Payment Orders'), + 'context': context, + 'view_type': 'form', + 'view_mode': 'form,tree', + 'res_model': 'payment.order', + 'res_id': context['active_id'], + 'type': 'ir.actions.act_window'} diff --git a/l10n_br_hr_payment_order/wizard/payslip_payment_create_order_view.xml b/l10n_br_hr_payment_order/wizard/payslip_payment_create_order_view.xml new file mode 100644 index 000000000..ea0a4b338 --- /dev/null +++ b/l10n_br_hr_payment_order/wizard/payslip_payment_create_order_view.xml @@ -0,0 +1,66 @@ + + + + + + payslip.payment.create.order.form + payslip.payment.order.create + +
+ + + + + +
+
+
+
+
+ + + payment.order.populate.account.payslip.line.tree + hr.payslip.line + + + + + + + + + + + + + payslip.payment.line.create.order + payslip.payment.order.create + +
+
+
+ + + + + +
+ + + Buscar rubricas a serem pagas nos holerites + ir.actions.act_window + payslip.payment.order.create + form + form + + new + + +
+
diff --git a/l10n_br_hr_payroll/__init__.py b/l10n_br_hr_payroll/__init__.py index 568e53582..3ebd92033 100644 --- a/l10n_br_hr_payroll/__init__.py +++ b/l10n_br_hr_payroll/__init__.py @@ -1,24 +1,6 @@ -# coding: utf-8 -############################################################################### -# -# Brazillian Human Resources Payroll module for OpenERP -# Copyright (C) 2014 KMEE (http://www.kmee.com.br) -# @author Luis Felipe Mileo -# @author Matheus Lima Felix -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### +# -*- 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 . import models +from . import wizards diff --git a/l10n_br_hr_payroll/__openerp__.py b/l10n_br_hr_payroll/__openerp__.py index 8a8ea9293..e7de61633 100644 --- a/l10n_br_hr_payroll/__openerp__.py +++ b/l10n_br_hr_payroll/__openerp__.py @@ -11,41 +11,61 @@ 'website': 'http://www.kmee.com.br', 'version': '8.0.0.0.1', 'depends': [ - 'l10n_br_hr_holiday', + 'hr_payroll', 'l10n_br_resource', + 'l10n_br_hr_holiday', 'l10n_br_hr_contract', - 'hr_payroll', + 'l10n_br_hr_vacation', ], 'external_dependencies': { 'python': ['pybrasil'], }, 'data': [ - 'data/l10n_br_hr_income_tax.xml', - 'data/l10n_br_hr_income_tax_deductable_amount_family.xml', - 'data/l10n_br_hr_payroll_data_rubricas.xml', - 'data/l10n_br_hr_payroll_data_tabela_INSS.xml', - 'views/hr_contract.xml', + 'data/l10n_br_hr_payroll_categorias.xml', + 'data/l10n_br_hr_contract_sequence.xml', + 'data/l10n_br_hr_tabela_INSS.xml', + 'data/l10n_br_hr_tabela_IR.xml', + 'data/l10n_br_hr_tabela_IR_dependente.xml', + 'data/l10n_br_hr_tabela_RAT_FAP.xml', + 'data/l10n_br_hr_payroll_decimal_precision.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/l10n_br_hr_payslip_security_rule.xml', + 'security/l10n_br_hr_rat_fap_security_rule.xml', + 'security/hr_telefonia_line_security_rule.xml', 'security/ir.model.access.csv', + + 'views/hr_contract.xml', + 'views/l10n_br_hr_employee.xml', + 'views/hr_payroll_structure.xml', + 'views/hr_payslip.xml', + 'views/hr_payslip_run.xml', + 'views/hr_salary_rule.xml', 'views/l10n_br_hr_child_benefit_view.xml', - 'views/l10n_br_hr_income_tax_view.xml', 'views/l10n_br_hr_income_tax_deductable_amount_family_view.xml', + 'views/l10n_br_hr_income_tax_view.xml', 'views/l10n_br_hr_minimum_wage_view.xml', 'views/l10n_br_hr_rat_fap_view.xml', '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', + 'views/res_config_view.xml', + 'views/hr_telefonia_view.xml', + # Alterações Contratuais + 'views/l10n_br_hr_contract_change/l10n_br_hr_contract_change_menu.xml', + 'views/l10n_br_hr_contract_change/l10n_br_hr_contract_change_base.xml', + 'views/l10n_br_hr_contract_change/remuneracao.xml', + 'views/l10n_br_hr_contract_change/lotacao_local.xml', + 'views/l10n_br_hr_contract_change/jornada.xml', + 'views/l10n_br_hr_contract_change/filiacao_sindical.xml', + 'views/l10n_br_hr_contract_change/cargo_atividade.xml', + + # wizards + 'wizards/hr_ateste_telefonia_wizard.xml', + ], 'demo': [ - 'demo/hr_contract.xml', + # 'demo/hr_contract.xml', + # 'demo/l10n_br_hr_payroll_rubricas.xml', + # 'demo/l10n_br_hr_payroll_estruturas.xml', ], 'installable': True, 'auto_install': False, diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_contract_sequence.xml b/l10n_br_hr_payroll/data/l10n_br_hr_contract_sequence.xml new file mode 100644 index 000000000..6754cd923 --- /dev/null +++ b/l10n_br_hr_payroll/data/l10n_br_hr_contract_sequence.xml @@ -0,0 +1,25 @@ + + + + + + + + Hr Contract code type + hr.contract + + + + >Hr Contract code + hr.contract + + + + + + Valores Iniciais do Contrato + + + + diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax_deductable_amount_family.xml b/l10n_br_hr_payroll/data/l10n_br_hr_income_tax_deductable_amount_family.xml deleted file mode 100644 index 94a0030a1..000000000 --- a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax_deductable_amount_family.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - 2017 - 189.59 - - - - \ No newline at end of file diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_categorias.xml b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_categorias.xml new file mode 100644 index 000000000..25cfb2e77 --- /dev/null +++ b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_categorias.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + Referência + REFERENCIA + + + + + + + Provento + PROVENTO + + + + + + + Bruto + BRUTO + + + + + + + Dedução + DEDUCAO + + + + + + + Liquido + LIQUIDO + + + + + + + INSS + INSS + + + + + + + IRPF + IRPF + + + + + + + FGTS + FGTS + + + + + + + SEFIP + SEFIP + + + + + + + FERIAS + FERIAS + + + + + + + \ No newline at end of file diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data.xml b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data.xml deleted file mode 100644 index 68a90bf32..000000000 --- a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data.xml +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_IR_rule.xml b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_IR_rule.xml index 732055987..dce511e36 100644 --- a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_IR_rule.xml +++ b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_IR_rule.xml @@ -7,71 +7,71 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + 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 deleted file mode 100644 index 679bd0aea..000000000 --- a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_rubricas.xml +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - - - Referência - REFERENCIA - - - Provento - PROVENTO - - - Bruto - BRUTO - - - Dedução - DEDUCAO - - - Liquido - LIQUIDO - - - INSS - INSS - - - IRPF - IRPF - - - FGTS - FGTS - - - SEFIP - SEFIP - - - FERIAS - FERIAS - - - - - BRUTO - Bruto - - code - none - result = categories.PROVENTO - - - - - LIQUIDO - Líquido - - code - none - result = BRUTO - TOTAL_DESCONTOS - - - - - INSS - INSS - - none - code - result = CALCULAR.INSS(BASE_INSS) - - - - - IRPF - IRPF - - none - code - result = CALCULAR.IRRF(BASE_IR, BASE_INSS) - - - - - BASE_INSS - Base INSS - - code - none - result = BASE_INSS - - - - - BASE_IRPF - Base IRRF - - code - none - result = BASE_IR - - - - - TOTAL_DESCONTOS - Total de Descontos - - code - none - result = categories.DEDUCAO + INSS + IRPF - - - - - BASE_FGTS - Base FGTS - - code - none - result = BASE_FGTS - - - - - FGTS - FGTS - - percentage - contract.wage - 1.0 - 8.00 - - - - - - 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 - FERIAS - - code - none - result = BASE_FERIAS / worked_days.SALDO_FERIAS.number_of_days -result_qty = worked_days.FERIAS.number_of_days - - - - - - - - 1/3_FERIAS - 1/3 FERIAS - - code - none - result = FERIAS / 3.0 - - - - - - - - ABONO_PECUNIARIO - Rubrica do Abono Pecuniario das FERIAS - - code - python - result = worked_days.FERIAS.number_of_days > 0 - result = worked_days.ABONO_PECUNIARIO.number_of_days * contract.wage / 30 - - - - - - - 1/3_ABONO_PECUNIARIO - Rubrica do 1/3 Abono Pecuniario - - code - python - result = worked_days.FERIAS.number_of_days > 0 - result = ABONO_PECUNIARIO / 3 - - - - - - - - VA/VR - Vale alimentação/Refeição - - code - none - if MEDIAS: - result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) -else: - result = CALCULAR.get_specific_rubric_value(rubrica.id) - - - - - SALARIO - Função Comissionada - - code - none - 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 - - - - - - - - - BASE_INSS_13 - Base INSS de 13º - - code - none - result = BASE_INSS - - - - - BASE_IRPF_13 - Base IRRF de 13º - - code - none - result = BASE_IR - - - - - BASE_FGTS_13 - Base FGTS de 13º - - code - none - result = BASE_FGTS/2 - - - - - REMBOLSO_SAUDE - Reembolso Plano de Saude - - code - none - if MEDIAS: - result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) -else: - result = CALCULAR.get_specific_rubric_value(rubrica.id) - - - - - - ADIANTAMENTO_13 - Adiantamento 13 Salario - - code - none - result = CALCULAR.BUSCAR_PRIMEIRA_PARCELA() - - - - - - - PRIMEIRA_PARCELA_13 - Primeira parcela 13 Salario - - code - none - result = MEDIAS.SALARIO.media/12.0 -result_qty = MEDIAS.SALARIO.meses - - - - - - - SEGUNDA_PARCELA_13 - Segunda parcela 13 Salário - - code - none - result = MEDIAS.SALARIO.media/12.0 -result_qty = MEDIAS.SALARIO.meses - - - - - - - - - - - - - FERIAS - Férias - - - - - - - BASE_INSS_IRPF - Base para INSS e IRPF - - - - - - - BASE_FGTS - Base para FGTS - - - - - - - BASE_INSS_IRPF_13 - Base para INSS e IRPF de 13 - - - - - - - BASE_FGTS_13 - Base para FGTS de 13 - - - - - - - FUNCAO_COMISSIONADA - Função Comissionada - - - - - - - HONORARIO_PRESIDENTE - Honorario Presidente - - - - - - - HONORARIO_DIRETORIA - Honorario Diretoria - - - - - - - HONORARIO_CONSELHO - Honorario Conselho - - - - - - - PRIMEIRA_PARCELA_13 - Primeira Parcela 13 Salário - - - - - - - SEGUNDA_PARCELA_13 - Segunda Parcela 13 Salário - - - - - - - diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_decimal_precision.xml b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_decimal_precision.xml new file mode 100644 index 000000000..98e01cbba --- /dev/null +++ b/l10n_br_hr_payroll/data/l10n_br_hr_payroll_decimal_precision.xml @@ -0,0 +1,15 @@ + + + + + + + + + Payroll + 4 + + + + diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_tabela_INSS.xml b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_INSS.xml similarity index 53% rename from l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_tabela_INSS.xml rename to l10n_br_hr_payroll/data/l10n_br_hr_tabela_INSS.xml index b6cecff90..0b0028f99 100644 --- a/l10n_br_hr_payroll/data/l10n_br_hr_payroll_data_tabela_INSS.xml +++ b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_INSS.xml @@ -2,7 +2,6 @@ - @@ -47,5 +46,45 @@ 11 + + + 2015 + 0 + 1399.12 + 8 + + + 2015 + 1399.13 + 2331.88 + 9 + + + 2015 + 2331.89 + 4663.75 + 11 + + + + + 2014 + 0 + 1317.07 + 8 + + + 2014 + 1317.08 + 2195.12 + 9 + + + 2014 + 2195.13 + 4390.24 + 11 + + diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR.xml similarity index 51% rename from l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml rename to l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR.xml index b8ee2d05e..8ceefe545 100644 --- a/l10n_br_hr_payroll/data/l10n_br_hr_income_tax.xml +++ b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR.xml @@ -2,9 +2,79 @@ - + + + 2014 + 0 + 0 + 0 + + + + 2014 + 1787.78 + 7.5 + 134.80 + + + + 2014 + 2679.30 + 15.00 + 335.03 + + + + 2014 + 3572.44 + 22.50 + 602.96 + + + + 2014 + 4463.81 + 27.5 + 82615 + + + + 2015 + 0 + 0 + 0 + + + + 2015 + 1903.99 + 7.5 + 142.80 + + + + 2015 + 2826.66 + 15.00 + 354.80 + + + + 2015 + 3751.06 + 22.50 + 636.13 + + + + 2015 + 4664.68 + 27.5 + 869.36 + + 2016 0 @@ -76,4 +146,4 @@ - \ No newline at end of file + diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR_dependente.xml b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR_dependente.xml new file mode 100644 index 000000000..000a80fe3 --- /dev/null +++ b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_IR_dependente.xml @@ -0,0 +1,30 @@ + + + + + + + + + 2017 + 189.59 + + + + 2016 + 189.59 + + + + 2015 + 189.59 + + + + 2014 + 179.71 + + + + diff --git a/l10n_br_hr_payroll/data/l10n_br_hr_tabela_RAT_FAP.xml b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_RAT_FAP.xml new file mode 100644 index 000000000..0da2df3a4 --- /dev/null +++ b/l10n_br_hr_payroll/data/l10n_br_hr_tabela_RAT_FAP.xml @@ -0,0 +1,46 @@ + + + + + + + + + + 2017 + 1 + 1 + 2.7 + 22.5 + + + + + 2016 + 1 + 1 + 2.7 + 22.5 + + + + + 2015 + 1 + 1 + 2.7 + 22.5 + + + + + 2014 + 1 + 1 + 2.7 + 22.5 + + + + diff --git a/l10n_br_hr_payroll/demo/hr_contract.xml b/l10n_br_hr_payroll/demo/hr_contract.xml deleted file mode 100644 index 9b907a7a2..000000000 --- a/l10n_br_hr_payroll/demo/hr_contract.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - 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/demo/l10n_br_hr_payroll_estruturas.xml b/l10n_br_hr_payroll/demo/l10n_br_hr_payroll_estruturas.xml new file mode 100644 index 000000000..58948dd10 --- /dev/null +++ b/l10n_br_hr_payroll/demo/l10n_br_hr_payroll_estruturas.xml @@ -0,0 +1,613 @@ + + + + + + + + + + + + + + + + + BASE + Base + + + + + + + + + + + base + + + + + INSS_EMPRESA_FUNCIONARIOS + INSS Empresa Funcionários + + + + + + + + + + + base + + + + + IRPF_FUNCIONARIOS + IRPF Funcionários + + + + + + + + + + + base + + + + + FGTS_FUNCIONARIOS + FGTS Funcionários + + + + + + + + + + + base + + + + + INSS_FUNCIONARIOS + INSS Funcionários + + + + + + + + + + + base + + + + + FERIAS + Férias + + + + + + + + + + + ferias + + + + + PRIMEIRA_PARCELA_13 + Primeira Parcela 13 Salário + + + + + + + + + + + adiantamento_13 + + + + + SEGUNDA_PARCELA_13 + Segunda Parcela 13 Salário + + + + + + + + + + + segunda_parcela_13 + + + + + FUNCAO_COMISSIONADA + Comissionado(a) + + + + + + + + + + + normal + + + + + BASE_INSS_IRPF + Base para INSS e IRPF + + + + + + + + + + + base + + + + + BASE_FGTS + Base para FGTS + + + + + + + + + + + base + + + + + BASE_INSS_IRPF_13 + Base para INSS e IRPF de 13 + + + + + + + + + + + base + + + + + BASE_FGTS_13 + Base para FGTS de 13 + + + + + + + + + + + base + + + + + FUNCAO_COMISSIONADA + Comissionado(a) + + + + + + + + + + + normal + + + + + INSS_EMPRESA_DIRIGENTES + INSS Empresa Dirigentes + + + + + + + + + + + base + + + + + IRPF_DIRIGENTES + IRPF Dirigentes + + + + + + + + + + + base + + + + + FGTS_DIRIGENTES + FGTS Dirigentes + + + + + + + + + + + base + + + + + PRESIDENCIA + Presidencia + + + + + + + + + + + normal + + + + + INSS_DIRIGENTES + INSS Dirigentes + + + + + + + + + + + base + + + + + DIRETORIA + Diretoria + + + + + + + + + + + normal + + + + + CONSELHO_ADM + Conselho Administração + + + + + + + + + + + normal + + + + + JUSTA_CAUSA + Rescisão dispensa com justa causa + + + + + + + + + + + rescisao + + + + + RESCISAO + Rescisão + + + + + + + + + + + rescisao + + + + + COMPLEMENTO_JUSTA_CAUSA + Rescisão Complementar (dispensa com justa causa) + + + + + + + + + + + rescisao + + + + + PROVISAO_13 + Provisão de Décimo Terceiro (13º) + + + + + + + + + + + segunda_parcela_13 + + + + + PROVISAO_FERIAS + Provisão de Férias + + + + + + + + + + + ferias + + + + + BASE_IRPF_SEM_INSS + Base para IRPF (sem INSS) + + + + + + + + + + + base + + + + + JUSTA_CAUSA + Rescisão dispensa com justa causa + + + + + + + + + + + rescisao + + + + + COMPLEMENTO_JUSTA_CAUSA + Rescisão Complementar (dispensa com justa causa) + + + + + + + + + + + rescisao + + + + + FUNCIONARIO_CEDIDO + Cedido(a) + + + + + + + + + + + normal + + + + + DIRETORIA_SEM_INSS + Diretoria (sem INSS) + + + + + + + + + + + normal + + + + + CONSELHO_FISCAL + Conselho Fiscal + + + + + + + + + + + normal + + + + + DIRETORIA_SEM_INSS_E_FGTS + Diretoria (sem INSS e FGTS) + + + + + + + + + + + normal + + + + + PROVISAO_13 + Provisão de Décimo Terceiro (13º) + + + + + + + + + + + segunda_parcela_13 + + + + + PROVISAO_13 + Provisão de Décimo Terceiro (13º) + + + + + + + + + + + segunda_parcela_13 + + + + + diff --git a/l10n_br_hr_payroll/demo/l10n_br_hr_payroll_rubricas.xml b/l10n_br_hr_payroll/demo/l10n_br_hr_payroll_rubricas.xml new file mode 100644 index 000000000..e6991e556 --- /dev/null +++ b/l10n_br_hr_payroll/demo/l10n_br_hr_payroll_rubricas.xml @@ -0,0 +1,3576 @@ + + + + + + + + + + + + + +XML da rubrica gerado automaticamente + + BRUTO + Bruto + True + 300.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = categories.PROVENTO + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + LIQUIDO + Líquido + True + 602.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BRUTO - TOTAL_DESCONTOS + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + +XML da rubrica gerado automaticamente + + INSS + INSS + True + 406.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.INSS(BASE_INSS) + base = BASE_INSS + +if 'BASE_INSS_FERIAS' in locals(): + base += BASE_INSS_FERIAS + +result = CALCULAR.INSS(base) + +if 'INSS_FERIAS' in locals(): + result -= INSS_FERIAS + +if result < 0: + result = 0 + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + IRPF + IRPF + True + 599.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.IRRF(BASE_IRPF, INSS if 'INSS' in locals() else 0) + result = CALCULAR.IRRF(BASE_IRPF, INSS if 'INSS' in locals() else 0) + +if 'IRPF_PROPORCIONAL_REGULAR' in locals(): + result = IRPF_PROPORCIONAL_REGULAR +if 'IRPF_PROPORCIONAL_FERIAS' in locals(): + result += IRPF_PROPORCIONAL_FERIAS + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + BASE_INSS + Base INSS + True + 405.0 + + none + result = 'BASE_INSS' not in locals() + result = BASE_INSS + result = BRUTO + BRUTO_FERIAS + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + BASE_IRPF + Base IRRF + True + 505.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BASE_IRRF(BASE_IR, INSS if 'INSS' in locals() else 0) + base = BASE_IR +inss = INSS if 'INSS' in locals() else 0 + +if 'PENSAO_ALIMENTICIA' in locals(): + base -= PENSAO_ALIMENTICIA + +result = CALCULAR.BASE_IRRF(base, inss) + +if 'BASE_IRPF_PROPORCIONAL_REGULAR' in locals() or 'BASE_IRPF_PROPORCIONAL_FERIAS' in locals(): + result = BASE_IRPF_PROPORCIONAL_REGULAR + result += BASE_IRPF_PROPORCIONAL_FERIAS + + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + TOTAL_DESCONTOS + Total de Descontos + True + 601.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = categories.DEDUCAO + INSS if 'INSS' in locals() else 0 + IRPF if 'IRPF' in locals() else 0 + result = categories.DEDUCAO +result += INSS if 'INSS' in locals() else 0 +result += IRPF if 'IRPF' in locals() else 0 + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + BASE_FGTS + Base FGTS + True + 698.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_FGTS + result = BASE_FGTS + +if 'BASE_INSS_FERIAS' in locals(): + result += BASE_INSS_FERIAS + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + FGTS + FGTS (F) + True + 699.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_FGTS * 0.08 + + code + code + + + + + + 8.0 + contract.wage + + + True + contract.wage + + + + + 1.0 + + + + + + + + + INSS_EMPRESA_BASE + INSS Empresa Base + True + 701.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=BASE_INSS +if 'BASE_INSS_13' in locals(): + result+=BASE_INSS_13 + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + INSS_DEDUCAO_PREVIDENCIARIA + INSS Dedução Previdenciária + True + 702.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=0 +if 'SALARIO_FAMILIA' in locals(): + result+=SALARIO_FAMILIA +if 'LICENCA_MATERNIDADE' in locals(): + result+=LICENCA_MATERNIDADE + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + INSS_EMPRESA + INSS Empresa (F) + True + 703.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.total_rate + + #if contract.categoria in ['721', '722']: +# result = 0 +# result_rate=RAT_FAP.total_rate +#else: +# result=INSS_EMPRESA_BASE +# result_rate=RAT_FAP.total_rate + +result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.total_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + +XML da rubrica gerado automaticamente + + INSS_OUTRAS_ENTIDADES + INSS Outras Entidades (F) + True + 704.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.other_entities_rate + + if contract.categoria in ['721', '722']: + result = 0 + result_rate=RAT_FAP.other_entities_rate +else: + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.other_entities_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_RAT_FAP + INSS RAT/FAP (F) + True + 705.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate + + if contract.categoria in ['721', '722']: + result = 0 + result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate +else: + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_EMPRESA_TOTAL + INSS Empresa Total + True + 706.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA +if 'INSS_RAT_FAP' in locals(): + result+=INSS_RAT_FAP +if 'INSS_OUTRAS_ENTIDADES' in locals(): + result+=INSS_OUTRAS_ENTIDADES + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + INSS_EMPRESA_LIQUIDO + INSS Empresa Líquido + True + 707.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_TOTAL-INSS_DEDUCAO_PREVIDENCIARIA + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + FERIAS + FERIAS + True + 140.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = contract.wage / 30 +result_qty = PEDIDO_FERIAS.DIAS_FERIAS + + result = Decimal(contract.wage) / Decimal(30) +#result_qty = PEDIDO_FERIAS.DIAS_FERIAS + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + 1/3_FERIAS + 1/3 FERIAS (F) + True + 146.0 + + none + result = '1/3_FERIAS' not in locals() + result = FERIAS / 3.0 + + if ('FERIAS' in locals()) and (payslip.tipo_de_folha == 'ferias'): + result = FERIAS / 3.0 +else: + result = CALCULAR.get_specific_rubric_value(rubrica.id) + if not result: + result = contract.wage / 3.0 / 12 + + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + ABONO_PECUNIARIO + Abono Pecuniário das FERIAS + True + 287.0 + + python + result = PEDIDO_FERIAS.DIAS_ABONO > 0 + result = contract.wage / 30 +result_qty = PEDIDO_FERIAS.DIAS_ABONO + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + 1/3_ABONO_PECUNIARIO + 1/3 Abono Pecuniario + True + 288.0 + + python + result = PEDIDO_FERIAS.DIAS_ABONO > 0 + result = ABONO_PECUNIARIO / 3.0 + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + LIQUIDO_FERIAS + Liquido Férias + True + 609.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = categories.FERIAS - INSS - IRPF + + result = BRUTO - TOTAL_DESCONTOS + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PAGAMENTO_FERIAS + Pagamento Antecipado de Férias + True + 351.0 + + python + result = worked_days.FERIAS.number_of_days > 0 and PAGAR_FERIAS + result = CALCULAR.rubrica_anterior_total(code='LIQUIDO_FERIAS', tipo_de_folha='ferias') + + result = CALCULAR.rubrica_anterior_total(code='LIQUIDO_FERIAS', tipo_de_folha='ferias') + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + VA/VR + Vale alimentação/Refeição + True + 301.0 + + python + if 'VA/VR' not in locals(): + result=payslip.tipo_de_folha in ('normal','rescisao') + if MEDIAS: + result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) +else: + result = CALCULAR.get_specific_rubric_value(rubrica.id) + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + ref = REF_VALOR_VALE if 'REF_VALOR_VALE' in locals() else 0 + result = ref * 0.01 + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + SALARIO + Função Comissionada + True + 1.0 + + none + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + valor + + + + + + + SALARIO + Honorario Presidente + True + 102.0 + + python + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + SALARIO + Honorario Diretoria + True + 103.0 + + python + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + SALARIO + Honorario Conselho Administração + True + 104.0 + + python + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = contract.wage + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + BASE_INSS_13 + Base INSS de 13º + True + 406.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_INSS + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + BASE_IRPF_13 + Base IRRF de 13º + True + 569.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_IR + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + BASE_FGTS_13 + Base FGTS de 13º + True + 698.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_FGTS/2 + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + REMBOLSO_SAUDE + Reembolso Plano de Saude + True + 121.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + if MEDIAS: + result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) +else: + 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) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PRIMEIRA_PARCELA_13 + Primeira parcela 13 Salario + True + 111.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = MEDIAS.SALARIO.media/12.0 +result_qty = MEDIAS.SALARIO.meses + + + code + code + + + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + SEGUNDA_PARCELA_13 + Segunda parcela 13 Salário + True + 112.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = MEDIAS.SALARIO.media/12.0 +result_qty = MEDIAS.SALARIO.meses + + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + REMBOLSO_AUXILIO_CRECHE + Reembolso Auxilio Creche + True + 122.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + if MEDIAS: + result = CALCULAR.get_specific_rubric_value(rubrica.id, MEDIAS) +else: + 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) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + AUXILIO_ALIMENTACAO + Auxílio Alimentação - Dir + True + 123.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + AUXILIO_MORADIA + Auxílio Moradia + True + 124.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + AUXILIO_SAUDE_DIRETOR + Auxílio Saúde - Diretor + True + 125.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + CONTRIBUICAO_SINDICAL + Contribuição Sindical + True + 321.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = contract.wage/30 + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + DESCONTO_LIGACOES_TELEFONICAS + Desconto Ligações Telefônicas + True + 302.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + DIF_REEMBOLSO_AUXILIO_SAUDE + Diferença Maior Reembolso Auxílio Saúde + True + 126.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + +XML da rubrica gerado automaticamente + + GRATIF_NATALINA_DIRETOR + Gratificação Natalina Diretor + True + 113.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + DIFERENCA_SALARIOS + Diferença de salários + True + 125.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + CONTRIB_CONFEDERATIVA + Contribuição Sindical - Acordo Coletivo + True + 311.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PENSAO_ALIMENTICIA + Pensão Alimentícia + True + 331.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + base = BASE_IR + base_ir = CALCULAR.BASE_IRRF(base, 0) + ir = CALCULAR.IRRF(base_ir, 0) + result = base - ir + result_rate = 10 + code + code + + True + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PREV_SUPLEMENTAR + Previdência Suplementar - Diretores + True + 171.0 + + python + result=payslip.tipo_de_folha in ('normal','rescisao') + result = CALCULAR.get_specific_rubric_value(rubrica.id) + + result = CALCULAR.get_specific_rubric_value(rubrica.id) + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PROP13 + Proporcional 13 Salário + True + 51.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('decimo_terceiro') + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PAGAMENTO_MES_ANTERIOR + Pagamento bruto do mês anterior + True + 172.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.rubrica_anterior_total(code='BRUTO', mes=payslip.mes_do_ano-1, tipo_de_folha='normal') + + + code + code + + + + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PROP_FERIAS + Proporcional Férias + True + 173.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('ferias') + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PROP_1/3_FERIAS + Proporcional 1/3 de Férias + True + 121.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('ferias', True) + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + FERIAS_VENCIDAS + Férias Vencidas + True + 122.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('ferias', ferias_vencida=True) + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + FERIAS_VENCIDAS_1/3 + Férias Vencidas 1/3 + True + 174.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('ferias', True, ferias_vencida=True) + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + AVISO_PREV_IND + Aviso Prévio Indenizado + True + 175.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_PROPORCIONAL('aviso_previo') + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PROP_13_AVISO_PREVIO + Proporcional 13 Salário Aviso Prévio + True + 176.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = CALCULAR.BUSCAR_VALOR_MEDIA_PROVENTO('decimo_terceiro') +result_qty = round(DIAS_AVISO_PREVIO/30.0) + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PROP_FERIAS_AVISO_PREVIO + Proporcional Férias Aviso Prévio + True + 177.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = AVISO_PREV_IND/12 +result_qty = round(DIAS_AVISO_PREVIO/30.0) + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + PROP_1/3_FERIAS_AVISO_PREVIO + Proporcional 1/3 Férias Aviso Prévio + True + 303.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = PROP_FERIAS_AVISO_PREVIO/3 + + code + code + True + True + True + + + + + + + True + contract.wage + + + + + 1.0 + + + + + + + + + BASE_IRPF_PROPORCIONAL_REGULAR + Base IRRF proporcional (regular) + True + 562.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + base = BASE_INSS +inss = INSS_PROPORCIONAL_REGULAR if 'INSS_PROPORCIONAL_REGULAR' in locals() else 0 +pensao = PENSAO_PROPORCIONAL_REGULAR if 'PENSAO_PROPORCIONAL_REGULAR' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# retirar da pensão o proporcional ao 1/3, +# e também fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + base -= terco + +base -= pensao +result = CALCULAR.BASE_IRRF(base, inss) + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + BASE_IRPF_PROPORCIONAL_FERIAS + Base IRRF proporcional (férias) + True + 563.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + base = BASE_IRPF +inss = INSS_PROPORCIONAL_FERIAS if 'INSS_PROPORCIONAL_FERIAS' in locals() else 0 +pensao = PENSAO_PROPORCIONAL_FERIAS if 'PENSAO_PROPORCIONAL_FERIAS' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# retirar da pensão o proporcional ao 1/3, +# e também fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + base = terco + base -= pensao +else: + base = 0 + +result = CALCULAR.BASE_IRRF(base, inss) + +if result < 0: + result = 0 + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + IRPF_PROPORCIONAL_REGULAR + IRRF proporcional (regular) + True + 564.0 + + python + result = 'BASE_IRPF_PROPORCIONAL_REGULAR' in locals() + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + inss = INSS_PROPORCIONAL_REGULAR if 'INSS_PROPORCIONAL_REGULAR' in locals() else 0 + +result = CALCULAR.IRRF(BASE_IRPF_PROPORCIONAL_REGULAR, inss) + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + IRPF_PROPORCIONAL_FERIAS + IRRF proporcional (férias) + True + 565.0 + + python + result = 'BASE_IRPF_PROPORCIONAL_FERIAS' in locals() + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + inss = INSS_PROPORCIONAL_FERIAS if 'INSS_PROPORCIONAL_FERIAS' in locals() else 0 + +result = CALCULAR.IRRF(BASE_IRPF_PROPORCIONAL_FERIAS, inss) + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_PROPORCIONAL_REGULAR + INSS proporcional (regular) + True + 407.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + #base = BASE_INSS +# +#if 'BASE_INSS_FERIAS' in locals(): +# base += BASE_INSS_FERIAS +# +#result = CALCULAR.INSS(base) +# +#if 'INSS_FERIAS' in locals(): +# result -= INSS_FERIAS +# +#if result < 0: +# result = 0 +# +#if 'INSS' not in locals(): +# result = 0 + +base = BASE_IR +inss = INSS if 'INSS' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + proporcao = 1 - (terco / float(base or 1)) + inss *= proporcao + +result = inss + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_PROPORCIONAL_FERIAS + INSS proporcional (férias) + True + 408.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + base = BASE_IR +inss = INSS if 'INSS' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + proporcao = (terco / float(base or 1)) + inss *= proporcao + +result = inss + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PENSAO_PROPORCIONAL_REGULAR + Pensão proporcional (regular) + True + 333.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + base = BASE_INSS +pensao = PENSAO_ALIMENTICIA if 'PENSAO_ALIMENTICIA' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + proporcao = 1 - (terco / float(base or 1)) + pensao *= proporcao + +result = pensao + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + PENSAO_PROPORCIONAL_FERIAS + Pensão proporcional (férias) + True + 332.0 + + python + result = payslip.tipo_de_folha == 'normal' + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + base = BASE_INSS +pensao = PENSAO_ALIMENTICIA if 'PENSAO_ALIMENTICIA' in locals() else 0 + +# +# Para os que recebem 1/3 de férias dentro da folha do mês, +# fazer a proporção do INSS correspondente +# +if '1/3_FERIAS' in locals(): + terco = locals()['1/3_FERIAS'] + proporcao = (terco / float(base or 1)) + pensao *= proporcao + +result = pensao + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + ADIANTAMENTO_13 + Adiantamento 13º + True + 199.0 + + python + # +# Adiantamento é recebido até maio para quem sair de férias +# +result = payslip.tipo_de_folha == 'ferias' and payslip.date_to[5:7] <= '05' +result = result and payslip.holidays_ferias and payslip.holidays_ferias.advance_13_salary + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + result = contract.wage / 2.0 + + fix + code + + + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + DESCONTO_VIAGEM + Desconto Adiantamento de Diárias de Viagem + True + 51.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + fix + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + REF_VALOR_VALE + Vale Alimentação/Refeição - Valor de Referência + True + 5.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + + fix + fix + + + + + 660.0 + + + + + + contract.wage + + + + True + 1.0 + 1 + + + + + + + + SALARIO + Honorario Conselho Fiscal + True + 104.0 + + python + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = contract.wage + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + SALARIO + Salário Substituição + True + 1.0 + + none + result = 'SALARIO' not in locals() + result = worked_days.DIAS_TRABALHADOS.number_of_days * contract.wage/30 + result = CALCULAR.get_specific_rubric_value(rubrica.id) + +if not result: + result = 0 + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + valor + + + + + + + SALARIO_13 + 13º Salário + True + 20.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days. +# inputs: object containing the computed inputs. + +# Note: returned value have to be set in the variable 'result' + +result = contract.wage * 0.10 + result = contract.wage /12 +result_qty = AVOS_13 + fix + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + valor + + + + + + + INSS_EMPRESA + INSS Empresa (D) + True + 703.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.total_rate + + #if contract.categoria in ['721', '722']: +# result = 0 +# result_rate=RAT_FAP.total_rate +#else: +# result=INSS_EMPRESA_BASE +# result_rate=RAT_FAP.total_rate + +result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.total_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_OUTRAS_ENTIDADES + INSS Outras Entidades (D) + True + 704.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.other_entities_rate + + if contract.categoria in ['721', '722']: + result = 0 + result_rate=RAT_FAP.other_entities_rate +else: + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.other_entities_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + INSS_RAT_FAP + INSS RAT/FAP (D) + True + 705.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result=INSS_EMPRESA_BASE +result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate + + if contract.categoria in ['721', '722']: + result = 0 + result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate +else: + result=INSS_EMPRESA_BASE + result_rate=RAT_FAP.rat_rate*RAT_FAP.fap_rate + code + code + + + + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + FGTS + FGTS (D) + True + 699.0 + + none + +# Available variables: +#---------------------- +# payslip: object containing the payslips +# employee: hr.employee object +# contract: hr.contract object +# rules: object containing the rules code (previously computed) +# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category). +# worked_days: object containing the computed worked days +# inputs: object containing the computed inputs + +# Note: returned value have to be set in the variable 'result' + +result = rules.NET > categories.NET * 0.10 + result = BASE_FGTS * 0.08 + + code + code + + + + + + 8.0 + contract.wage + + + True + contract.wage + + + + + 1.0 + + + + + + + + + 1/3_FERIAS + 1/3 FERIAS (D) + True + 146.0 + + none + result = '1/3_FERIAS' not in locals() + result = FERIAS / 3.0 + + if ('FERIAS' in locals()) and (payslip.tipo_de_folha == 'ferias'): + result = FERIAS / 3.0 +else: + result = CALCULAR.get_specific_rubric_value(rubrica.id) + if not result: + result = contract.wage / 3.0 / 12 + + code + code + True + True + True + + + + + + + True + contract.wage + + + + True + 1.0 + + + + + + + + + diff --git a/l10n_br_hr_payroll/i18n/pt_BR.po b/l10n_br_hr_payroll/i18n/pt_BR.po index 791b9054e..1893ffe74 100644 --- a/l10n_br_hr_payroll/i18n/pt_BR.po +++ b/l10n_br_hr_payroll/i18n/pt_BR.po @@ -15,3 +15,90 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,wage:0 +msgid "Salário" +msgstr "Salário" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,salary_unit:0 +msgid "Unidade Salarial " +msgstr "Unidade Salarial" + +#. module: l10n_br_hr_payroll +#: view:hr.payslip:l10n_br_hr_payroll.hr_payslip_form_view +#: model:ir.model,name:l10n_br_hr_payroll.model_hr_payroll_structure +#: field:l10n_br_hr.contract.change,struct_id:0 +msgid "Estrutura do Salário" +msgstr "Estrutura do Salário" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,working_hours:0 +msgid "Cronograma de Trabalho" +msgstr "Cronograma de Trabalho" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,schedule_pay:0 +msgid "Pagamento Agendado" +msgstr "Pagamento Agendado" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,weekly_hours:0 +msgid "Horas Semanais" +msgstr "Horas Semanais" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,monthly_hours:0 +msgid "Horas Mensais" +msgstr "Horas Mensais" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,job_id:0 +msgid "Titulo do Trabalho" +msgstr "Titulo do Trabalho" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,type_id:0 +msgid "Tipo de Contrato" +msgstr "Tipo de Contrato" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,admission_type_id:0 +msgid "Tipo de Admissão" +msgstr "Tipo de Admissão" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,labor_bond_type_id:0 +msgid "Tipo de Vínculo Trabalhista" +msgstr "Tipo de Vínculo Trabalhista" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,labor_regime_id:0 +msgid "Regime de Trabalho" +msgstr "Regime de Trabalho" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,union:0 +msgid "Sindicado" +msgstr "Sindicado" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,union_cnpj:0 +msgid "CNPJ Sindicado" +msgstr "CNPJ Sindicado" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,union_entity_code:0 +msgid "Código do Sindicado" +msgstr "Código do Sindicado" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,discount_union_contribution:0 +msgid "Contribuição Sindical Descontada na Admissão" +msgstr "Contribuição Sindical Descontada na Admissão" + +#. module: l10n_br_hr_payroll +#: field:l10n_br_hr.contract.change,month_base_date:0 +msgid "Mês Base" +msgstr "Mês Base" + diff --git a/l10n_br_hr_payroll/models/__init__.py b/l10n_br_hr_payroll/models/__init__.py index 40421af62..6bdcaa1e7 100644 --- a/l10n_br_hr_payroll/models/__init__.py +++ b/l10n_br_hr_payroll/models/__init__.py @@ -2,6 +2,7 @@ # Copyright (C) 2016 KMEE (http://www.kmee.com.br) # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import hr_campos_rescisao from . import hr_payslip from . import hr_payslip_line from . import l10n_br_hr_child_benefit @@ -19,3 +20,7 @@ from . import hr_payroll_structure from . import l10n_br_hr_medias from . import hr_payslip_run +from . import res_config +from . import hr_holidays +from . import hr_ramal +from . import hr_telefonia diff --git a/l10n_br_hr_payroll/models/hr_campos_rescisao.py b/l10n_br_hr_payroll/models/hr_campos_rescisao.py new file mode 100644 index 000000000..ac8b79751 --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_campos_rescisao.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields, api + + +class HrFieldRescission(models.Model): + _name = 'hr.campos.rescisao' + _order = 'codigo' + + codigo = fields.Float( + string=u'Código', + required=True, + ) + codigo_fmt = fields.Char( + string=u'Código', + compute="_calcula_codigo_fmt", + store=True, + ) + + @api.multi + @api.depends('codigo') + def _calcula_codigo_fmt(self): + for registro in self: + if registro.codigo == int(registro.codigo): + registro.codigo_fmt = "%.0f" % registro.codigo + else: + registro.codigo_fmt = "%.1f" % registro.codigo + + name = fields.Char( + string=u'Descrição', + required=True, + ) + valor = fields.Float( + string=u'Valor' + ) + tipo = fields.Char( + string=u'Tipo', + required=True, + ) + slip_id = fields.Many2one( + comodel_name='hr.payslip', + inverse_name='rescisao_ids', + string=u'Holerite' + ) diff --git a/l10n_br_hr_payroll/models/hr_contract.py b/l10n_br_hr_payroll/models/hr_contract.py index 31851350e..a4da77883 100644 --- a/l10n_br_hr_payroll/models/hr_contract.py +++ b/l10n_br_hr_payroll/models/hr_contract.py @@ -3,92 +3,205 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from openerp import api, fields, models -from datetime import datetime - +from openerp import exceptions +from datetime import datetime, timedelta class HrContract(models.Model): _inherit = 'hr.contract' _rec_name = 'nome_contrato' - @api.depends('employee_id', 'date_start') - def _nome_contrato(self): + codigo_contrato = fields.Char( + string='Codigo de Identificacao', + required=True, + default="/", + readonly=True + ) + + is_editable = fields.Boolean( + string="Pode Alterar ?", + compute="_is_editable", + default=True, + store=True, + ) + payslip_ids_confirmados = fields.One2many( + "hr.payslip", + "contract_id", + "Holerites Confirmados", + domain=[ + ('state', '!=', 'draft'), + ('is_simulacao', '=', False) + ] + ) + + @api.multi + @api.depends('payslip_ids_confirmados', 'payslip_ids_confirmados.state') + def _is_editable(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 + if len(contrato.payslip_ids_confirmados) != 0: + contrato.is_editable = False else: - fim_contrato = '' - matricula = contrato.name - nome_contrato = '[%s] %s - %s %s' % (matricula, - nome, inicio_contrato, - fim_contrato) - contrato.nome_contrato = nome_contrato + contrato.is_editable = True - nome_contrato = fields.Char(default="[mat] nome - inicio - fim", - compute="_nome_contrato", store=True) + @api.model + def create(self, vals): + if vals.get('codigo_contrato', '/') == '/': + vals['codigo_contrato'] = self.env['ir.sequence'].get(self._name) + return super(HrContract, self).create(vals) + + @api.depends('employee_id') + def _compute_nome_contrato(self): + for contrato in self: + nome = contrato.employee_id.name + matricula = contrato.codigo_contrato + nome_contrato = '[%s] %s' % (matricula, nome) + contrato.nome_contrato = nome_contrato if nome else '' + + nome_contrato = fields.Char( + default="[mat] nome - inicio - fim", + compute="_compute_nome_contrato", + store=True + ) @api.multi - def _buscar_salario_vigente_periodo(self, data_inicio, data_fim): + def _buscar_salario_vigente_periodo( + self, data_inicio, data_fim, inicial=False, final=False): 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, + + # + # Checa se há alterações contratuais em estado Rascunho + # Não continua se houver + # + change = contract_change_obj.search([ + ('contract_id', '=', self.id), + ('change_type', '=', 'remuneracao'), + ('state', '=', 'draft'), + ], order="change_date DESC", ) - return change.wage + if change: + raise exceptions.ValidationError( + "Há alteração de remuneração em estado Rascunho " + "neste contrato, por favor exclua a alteração " + "contratual ou Aplique-a para torná-la efetiva " + "antes de calcular um holerite!" + ) + + # Busca todas as alterações de remuneração deste contrato + # + change = contract_change_obj.search([ + ('contract_id', '=', self.id), + ('change_type', '=', 'remuneracao'), + ('state', '=', 'applied'), + ], order="change_date DESC", + ) + + # Calcular o salário proporcional dentro do período especificado + # Pega o salário do contrato caso nunca tenha havido uma alteração + # contratual + # + salario_medio = self.wage + for i in range(len(change)): + + # Dentro deste período houve alteração contratual ? + # + if data_inicio <= change[i].change_date <= data_fim: + i_2 = i + 1 + data_mudanca = \ + datetime.strptime(change[i].change_date, "%Y-%m-%d") + d_inicio = datetime.strptime(data_inicio, "%Y-%m-%d") + d_fim = datetime.strptime(data_fim, "%Y-%m-%d") + d_fim = d_fim.replace(day=30) + + dias = (d_fim - d_inicio) + timedelta(days=1) + + # Se a alteração salarial for exatamente no primeiro dia do + # período do holerite, Considere o salário no período inteiro + # + if data_mudanca == d_inicio: + # if i_2 in range(len(change)): + salario_medio = change[i].wage + salario_dia_1 = change[i].wage / dias.days + salario_dia_2 = change[i].wage / dias.days + else: + + # Calcula o número de dias dentro do período e quantos dias + # são de cada lado da alteração contratual + # + dias_2 = (dias.days - data_mudanca.day) + 1 + dias_1 = data_mudanca.day - d_inicio.day + + # Calcula cada valor de salário nos dias em com valores + # diferentes + # + salario_dia_2 = change[i].wage / dias.days + if i_2 in range(len(change)): + salario_dia_1 = change[i_2].wage / dias.days + else: + salario_dia_1 = change[i].wage / dias.days + salario_medio_2 = salario_dia_2 * dias_2 + salario_medio_1 = salario_dia_1 * dias_1 + + # Soma os 2 lados e temos o salário proporcional dentro + # do período + # + salario_medio = salario_medio_2 + salario_medio_1 + + # Se for para buscar o salário inicial + # + if inicial: + salario_medio = salario_dia_1 * dias.days + + # Se for para buscar o salário final + # + if final: + salario_medio = salario_dia_2 * dias.days + + break + + # Houve alteração contratual anterior ao período atual + # + elif change[i].change_date < data_inicio: + salario_medio = change[i].wage + break + + return salario_medio @api.multi def _salario_dia(self, data_inicio, data_fim): - 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 + return self._salario_mes_proporcional( + 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 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 - ) + wage = self._salario_mes_proporcional(data_inicio, data_fim) + hours_total = 220 if not self.monthly_hours else self.monthly_hours + return wage / hours_total @api.multi def _salario_mes(self, data_inicio, data_fim): - 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) + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim) + + @api.multi + def _salario_mes_proporcional(self, data_inicio, data_fim): + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim) + + @api.multi + def _salario_mes_inicial(self, data_inicio, data_fim): + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim, inicial=True) + + @api.multi + def _salario_mes_final(self, data_inicio, data_fim): + return self._buscar_salario_vigente_periodo( + data_inicio, data_fim, final=True) specific_rule_ids = fields.One2many( comodel_name='hr.contract.salary.rule', inverse_name='contract_id', string=u"Rúbricas específicas", + ondelete='cascade', ) change_salary_ids = fields.One2many( comodel_name='l10n_br_hr.contract.change', @@ -134,6 +247,7 @@ def _salario_mes(self, data_inicio, data_fim): comodel_name='res.company', string='Empresa', required=True, + default=lambda self: self.env.user.company_id or '', ) # Admissão @@ -179,17 +293,18 @@ def _salario_mes(self, data_inicio, data_fim): ) segunda_experiencia = fields.Integer( - string="Tempo em dias do 2º período de experiência" + string=u"Tempo em dias do 2º período de experiência" ) data_segunda_experiencia = fields.Date( - string="Início da segunda experiência" + string=u"Início da segunda experiência" ) - # Lotação - departamento_lotacao = fields.Selection( - selection=[], - string="Departamento/lotação" + department_id = fields.Many2one( + comodel_name='hr.department', + string='Departamento/Lotação', + related=False, + readonly=False, ) lotacao_cliente_fornecedor = fields.Selection( @@ -244,9 +359,9 @@ def _salario_mes(self, data_inicio, data_fim): string="Data de admissão no vínculo cedente" ) - onus_vinculo_cedente = fields.Selection( - selection=[], - string="Ônus para o cedente" + adiantamento_13_cedente = fields.Float( + string=u"Antecipação de 13º na Orgiem R$", + default=0.0, ) # Aba Saúde ocupacional @@ -304,6 +419,10 @@ def _salario_mes(self, data_inicio, data_fim): inverse_name='contrato_id', string="Afastamentos" ) + conta_bancaria_id = fields.Many2one( + string="Conta bancaria", + comodel_name='res.partner.bank', + ) class Exame(models.Model): @@ -370,10 +489,6 @@ class HrHoliday(models.Model): string="Valor INSS" ) - contrato_id = fields.Many2one( - comodel_name='hr.contract', - ) - class HrContractSalaryUnit(models.Model): _inherit = 'hr.contract.salary.unit' diff --git a/l10n_br_hr_payroll/models/hr_contract_salary_rule.py b/l10n_br_hr_payroll/models/hr_contract_salary_rule.py index f264ad834..48d50a111 100644 --- a/l10n_br_hr_payroll/models/hr_contract_salary_rule.py +++ b/l10n_br_hr_payroll/models/hr_contract_salary_rule.py @@ -9,6 +9,7 @@ class HrContractSalaryRule(models.Model): _name = 'hr.contract.salary.rule' _description = 'Rubricas especificas' + _order = "contract_id,date_start DESC,date_stop DESC,rule_id" contract_id = fields.Many2one( comodel_name="hr.contract", @@ -28,6 +29,7 @@ class HrContractSalaryRule(models.Model): ) specific_quantity = fields.Float( string=u"Quandidade especifica", + default=1.0, ) specific_percentual = fields.Float( string=u"Percentual especifico", @@ -36,3 +38,7 @@ class HrContractSalaryRule(models.Model): specific_amount = fields.Float( string=u"Valor especifico", ) + partner_id = fields.Many2one( + string=u'Beneficiário', + comodel_name='res.partner', + ) diff --git a/l10n_br_hr_payroll/models/hr_employee.py b/l10n_br_hr_payroll/models/hr_employee.py index c9b47fde8..6de40901e 100644 --- a/l10n_br_hr_payroll/models/hr_employee.py +++ b/l10n_br_hr_payroll/models/hr_employee.py @@ -2,7 +2,7 @@ # Copyright 2017 KMEE # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, models +from openerp import api, models, fields from openerp.exceptions import Warning as UserError @@ -11,11 +11,31 @@ class HrEmployee(models.Model): @api.multi def write(self, vals): - for dict_key in ['work_location', 'department_id', 'job_id', - 'registration', 'manager']: - if dict_key in vals: - raise UserError(u'Alteração no campo %s só pode ser ' - u'realizada através do menu ' - u'Alterações Contratuais' % dict_key) - + # Não permitir alterações do cadastro de funcionario. + # Apenas permitir pelo menu de alteração contratual. + # O menu de alteração contratual seta uma variavel de contexto para + # indicar que a modificação partiu de lá. + # + # Se a variavel de alteracaocontratual nao for setada validar alteração + if not self.env.context.get('alteracaocontratual'): + for dict_key in ['work_location', 'department_id', 'job_id', + 'registration', 'manager']: + if dict_key in vals: + # Se a variavel nao foi setada ainda, permitir setar via + # cadastro de contratoss, se ja estiver definida disparar + # erro informando ao usuário para alterar campo pelo + # menu de Alteração contratual. + if eval('self.'+dict_key): + raise UserError( + u'Alteração no campo %s só pode ser ' + u'realizada através do menu ' + u'Alterações Contratuais' % dict_key) return super(HrEmployee, self).write(vals) + + ramais = fields.Many2many( + comodel_name='hr.ramal', + relation='employee_ramal_rel', + column1='ramal_id', + column2='employee_id', + string=u'Ramais' + ) diff --git a/l10n_br_hr_payroll/models/hr_holidays.py b/l10n_br_hr_payroll/models/hr_holidays.py new file mode 100644 index 000000000..384e25d9c --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_holidays.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields + + +class HrHolidays(models.Model): + _inherit = 'hr.holidays' + + slip_ids = fields.One2many( + comodel_name='hr.payslip', + string=u'Holerites', + inverse_name='holidays_ferias', + ) + diff --git a/l10n_br_hr_payroll/models/hr_payroll_structure.py b/l10n_br_hr_payroll/models/hr_payroll_structure.py index 07537a78a..a9e54f57d 100644 --- a/l10n_br_hr_payroll/models/hr_payroll_structure.py +++ b/l10n_br_hr_payroll/models/hr_payroll_structure.py @@ -4,23 +4,220 @@ from openerp import fields, models +TIPO_SAQUE = ( + ('01', u'01 - Sem justa causa (incl. indireta)'), + ('02', u'02 - Por culpa recíproca ou força maior'), + ('03', u'03 - Extinção total da empresa'), + ('04', u'04 - Extinção do contrato por prazo determinado'), + (' ', u'Código mov. H, J e M'), + ('23', u'23 - Por falecimento'), +) + +TIPO_DESLIGAMENTO_RAIS = ( + ('10', u'10 - Rescisão de contrato de trabalho por justa causa e ' + u'iniciativa do empregador ou demissão de servidor'), + ('11', u'11 - Rescisão de contrato de trabalho sem justa causa por ' + u'iniciativa do empregador ou exoneração de oficio de servidor de ' + u'cargo efetivo ou exoneração de cargo em comissão'), + ('12', u'12 - Término do contrato de trabalho'), + ('20', u'20 - Rescisão com justa causa por iniciativa do empregado (' + u'rescisão indireta)'), + ('21', u'21 - Rescisão sem justa causa por iniciativa do empregado ou ' + u'exoneração de cargo efetivo a pedido do servidor'), + ('22', u'22 - Posse em outro cargo inacumulável (específico para servidor ' + u'público)'), + ('30', u'30 - Transferência de empregado entre estabelecimentos da mesma ' + u'empresa ou para outra empresa, com ônus para a cedente'), + ('31', u'31 - Transferência de empregado entre estabelecimentos da mesma ' + u'empresa ou para outra empresa, sem ônus para a cedente'), + ('32', u'32 - Readaptação (específico para servidor público)'), + ('33', u'33 - Cessão'), + ('34', u'34 - Redistribuição (específico para servidor público)'), + ('40', u'40 - Mudança de regime trabalhista'), + ('50', u'50 - Reforma de militar para a reserva remunerada'), + ('60', u'60 - Falecimento'), + ('62', u'62 - Falecimento decorrente de acidente do trabalho típico (que ' + u'ocorre no exercício de atividades profissionais a serviço da ' + u'empresa)'), + ('63', u'63 - Falecimento decorrente de acidente do trabalho de trajeto (' + u'ocorrido no trajeto residência–trabalho–residência)'), + ('64', u'64 - Falecimento decorrente de doença profissional'), + ('70', u'70 - Aposentadoria por tempo de contribuição, com rescisão ' + u'contratual'), + ('71', u'71 - Aposentadoria por tempo de contribuição, sem rescisão ' + u'contratual'), + ('72', u'72 - Aposentadoria por idade, com rescisão contratual'), + ('73', u'73 - Aposentadoria por invalidez, decorrente de acidente do ' + u'trabalho'), + ('74', u'74 - Aposentadoria por invalidez, decorrente de doença ' + u'profissional'), + ('75', u'75 - Aposentadoria compulsória'), + ('76', u'76 - Aposentadoria por invalidez, exceto a decorrente de doença ' + u'profissional ou acidente do trabalho'), + ('78', u'78 - Aposentadoria por idade, sem rescisão contratual'), + ('79', u'79 - Aposentadoria especial, com rescisão contratual'), + ('80', u'80 - Aposentadoria especial, sem rescisão contratual'), +) + +TIPO_AFASTAMENTO_SEFIP = ( + ('H', u'H - Rescisão, com justa causa, por iniciativa do empregador.'), + ('I1', u'I1 - Rescisão, sem justa causa, por iniciativa do empregador.'), + ('I2', u'I2 - Rescisão por culpa recíproca ou força maior.'), + ('I3', u'I3 - Rescisão por término do contrato a termo.'), + ('I4', u'I4 - Rescisão, sem justa causa, do contrato de trabalho do ' + u'empregado doméstico, por iniciativa do empregador .'), + ('J', u'J - Rescisão do contrato de trabalho por iniciativa do ' + u'empregado.'), + ('K', u'K - Rescisão a pedido do empregado ou por iniciativa do ' + u'empregador, com justa causa, no caso de empregado não optante, ' + u'com menos de um ano de serviço.'), + ('L', u'L - Outros motivos de rescisão do contrato de trabalho.'), + ('M', u'M - Mudança de regime estatutário.'), + ('N1', u'N1 - Transferência do empregado para outro estabelecimento da ' + u'mesma empresa.'), + ('N2', u'N2 - Transferência do empregado para outra empresa que tenha ' + u'assumido os encargos trabalhistas, sem que tenha havido rescisão ' + u'de contrato de trabalho.'), + ('N3', u'N3 - Empregado proveniente de transferência de outro ' + u'estabelecimento da mesma empresa ou de outra empresa, ' + u'sem rescisão do contrato de trabalho.'), + ('O1', u'O1 - Afastamento temporário por motivo de acidente do trabalho, ' + u'por período superior a 15 dias.'), + ('O2', u'O2 - Novo afastamento temporário em decorrência do mesmo ' + u'acidente do trabalho.'), + ('O3', u'O3 - Afastamento temporário por motivo de acidente do trabalho, ' + u'por período igual ou inferior a 15 dias.'), + ('P1', u'P1 - Afastamento temporário por motivo de doença, por período ' + u'superior a 15 dias.'), + ('P2', u'P2 - Novo afastamento temporário em decorrência da mesma doença, ' + u'dentro de 60 dias contados da cessação do afastamento anterior.'), + ('P3', u'P3 - Afastamento temporário por motivo de doença, por período ' + u'igual ou inferior a 15 dias.'), + ('Q1', u'Q1 - Afastamento temporário por motivo de licença-maternidade (' + u'120 dias).'), + ('Q2', u'Q2 - Prorrogação do afastamento temporário por motivo de ' + u'licença-maternidade.'), + ('Q3', u'Q3 - Afastamento temporário por motivo de aborto não criminoso.'), + ('Q4', u'Q4 - Afastamento temporário por motivo de licença-maternidade ' + u'decorrente de adoção ou guarda judicial de criança até (um) ano ' + u'de idade (120 dias).'), + ('Q5', u'Q5 - Afastamento temporário por motivo de licença-maternidade ' + u'decorrente de adoção ou guarda judicial de criança a partir de ' + u'1(um) ano até 4(quatro) anos de idade (60 dias).'), + ('Q6', u'Q6 - Afastamento temporário por motivo de licença-maternidade ' + u'decorrente de adoção ou guarda judicial de criança de 4 (quatro) ' + u'até 8 (oito) anos de idade (30 dias).'), + ('R', u'R - Afastamento temporário para prestar serviço militar.'), + ('S2', u'S2 - Falecimento.'), + ('S3', u'S3 - Falecimento motivado por acidente do trabalho.'), + ('U1', u'U1 - Aposentadoria.'), + ('U3', u'U3 - Aposentadoria por invalidez.'), + ('V3', u'V3 - Remuneração de comissão e/ou percentagens devidas após a ' + u'extinção de contrato de trabalho.'), + ('W', u'W - Afastamento temporário para exercício de mandato sindical.'), + ('X', u'X - Licença sem vencimentos.'), + ('Y', u'Y - Outros motivos de afastamento temporário.'), + ('Z1', u'Z1 - Retorno de afastamento temporário por motivo de ' + u'licença-maternidade'), + ('Z2', u'Z2 - Retorno de afastamento temporário por motivo de acidente do ' + u'trabalho'), + ('Z3', u'Z3 - Retorno de novo afastamento temporário em decorrência do ' + u'mesmo acidente do trabalho.'), + ('Z4', u'Z4 - Retorno de afastamento temporário por motivo de prestação ' + u'de serviço militar.'), + ('Z5', u'Z5 - Outros retornos de afastamento temporário e/ou licença.'), + ('Z6', u'Z6 - Retorno de afastamento temporário por motivo de acidente do ' + u'trabalho, por período igual ou inferior a 15 dias.'), +) + +TIPO_AFASTAMENTO_CEF = ( + ('CR0', u'CR0 -Rescisão culpa recíproca.'), + ('FE1', u'FE1 - Rescisão do contrato de trabalho por falecimento do ' + u'empregador individual por opção do empregado.'), + ('FE2', u'FE2 - Rescisão do contrato de trabalho por falecimento do ' + u'empregador individual sem continuidade da atividade da ' + u'empresa.'), + ('FM0', u'FM0 - Rescisão por força maior.'), + ('FT1', u'FT1 - Rescisão do contrato de trabalho por falecimento do ' + u'empregado.'), + ('JC2', u'JC2 - Despedida por justa causa pelo empregador.'), + ('PD0', u'PD0 - Extinção normal do contrato de trabalho por prazo ' + u'determinado.'), + ('RA1', u'RA1 - Rescisão antecipada, pelo empregado, do contrato de ' + u'trabalho por prazo determinado.'), + ('RA2', u'RA2 - Rescisão antecipada, pelo empregador do contrato de ' + u'trabalho por prazo determinado.'), + ('RI2', u'RI2 - Rescisão Indireta.'), + ('SJ1', u'SJ1 - Rescisão contratual a pedido do empregado.'), + ('SJ2', u'SJ2 - Rescisão contratual sem justa causa, pelo empregador.'), +) + 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'), + ('normal', u'Folha Normal'), + ('ferias', u'Férias'), + ('adiantamento_13', u'Adiantamento do 13º'), + ('segunda_parcela_13', u'Segunda Parcela do 13º'), + ('rescisao', u'Rescisão'), + ('base', u'Base') ], - string='Tipo de Estrutura de Salários', + string=u'Tipo de Estrutura de Salários', + required=True, + default='base' + ) + + estrutura_ferias_id = fields.Many2one( + 'hr.payroll.structure', + u'Estrutura de Férias', + domain="[('tipo_estrutura','=','ferias')]" + ) + + estrutura_adiantamento_13_id = fields.Many2one( + 'hr.payroll.structure', + u'Estrutura de 13º Salário - Adiantamento', + domain="[('tipo_estrutura','=','adiantamento_13')]" + ) + + estrutura_13_id = fields.Many2one( + 'hr.payroll.structure', + u'Estrutura de 13º Salário', + domain="[('tipo_estrutura','=','segunda_parcela_13')]" + ) + + # Aba de rescisão + afastamento_imediato = fields.Boolean( + string=u'Afastamento imediato na rescisão (justa causa, término de ' + u'contrato, etc.):', + ) + + dispensado_empregador = fields.Boolean( + string=u'Dispensa pelo empregador:', + ) + + tipo_afastamento_sefip = fields.Selection( + selection=TIPO_AFASTAMENTO_SEFIP, + string=u'Código do afastamento ou movimentação no SEFIP:', + ) + + tipo_afastamento_cef = fields.Selection( + selection=TIPO_AFASTAMENTO_CEF, + string=u'Código do afastamento ou movimentação na C.E.F.:', + ) + + tipo_saque = fields.Selection( + selection=TIPO_SAQUE, + string=u'Código de saque:', + ) + + tipo_desligamento_rais = fields.Selection( + selection=TIPO_DESLIGAMENTO_RAIS, + string=u'Código desligamento RAIS:', + ) + + children_ids = fields.One2many( + copy=False ) diff --git a/l10n_br_hr_payroll/models/hr_payslip.py b/l10n_br_hr_payroll/models/hr_payslip.py index 642a6344d..ce7f86815 100644 --- a/l10n_br_hr_payroll/models/hr_payslip.py +++ b/l10n_br_hr_payroll/models/hr_payslip.py @@ -3,22 +3,45 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html import logging -from openerp import api, fields, models, exceptions, _ -from datetime import datetime +from calendar import monthrange +from datetime import datetime, timedelta + from dateutil.relativedelta import relativedelta +from dateutil.rrule import rrule, MONTHLY from lxml import etree +from openerp import api, fields, models, exceptions, _ +from mako.template import Template _logger = logging.getLogger(__name__) try: from pybrasil import valor, data + from pybrasil.data import ultimo_dia_mes + from pybrasil.valor.decimal import Decimal + except ImportError: _logger.info('Cannot import pybrasil') MES_DO_ANO = [ (1, u'Janeiro'), (2, u'Fevereiro'), - (3, u'Marco'), + (3, u'Março'), + (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'), + (13, u'13º Salário'), +] + +MES_DO_ANO2 = [ + (1, u'Janeiro'), + (2, u'Fevereiro'), + (3, u'Março'), (4, u'Abril'), (5, u'Maio'), (6, u'Junho'), @@ -36,6 +59,8 @@ ('ferias', u'Férias'), ('decimo_terceiro', u'Décimo terceiro (13º)'), ('aviso_previo', u'Aviso Prévio'), + ('provisao_ferias', u'Provisão de Férias'), + ('provisao_decimo_terceiro', u'Provisão de Décimo terceiro (13º)'), ('licenca_maternidade', u'Licença maternidade'), ('auxilio_doenca', u'Auxílio doença'), ('auxílio_acidente_trabalho', u'Auxílio acidente de trabalho'), @@ -44,6 +69,49 @@ class HrPayslip(models.Model): _inherit = 'hr.payslip' + _order = 'ano desc, mes_do_ano desc, tipo_de_folha asc' + \ + ', company_id asc, employee_id asc, number desc' + _sql_constraints = [ + ('holerite_unico', + 'unique(contract_id, tipo_de_folha, date_from, date_to, is_simulacao, periodo_aquisitivo)', + 'Este Holerite já existe!') + ] + + @api.multi + def hr_verify_sheet(self): + for holerite in self: + if holerite.state == 'draft': + holerite.write({'state': 'verify'}) + # Atualizar o controle de férias, o controle de férias do + # contrato é baseado nos holerites validados + holerite.contract_id.action_button_update_controle_ferias() + + # setar as ligacoes telefonicas como debitadas + for ligacao_id in holerite.ligacoes_ids: + ligacao_id.state = 'paid' + + @api.multi + def draft(self): + for holerite in self: + if holerite.state == 'verify': + holerite.write({'state': 'draft'}) + # Atualizar o controle de férias, o controle de férias do + # contrato é baseado nos holerites validados + holerite.contract_id.action_button_update_controle_ferias() + + # setar as ligacoes telefonicas como atestadas + for ligacao_id in holerite.ligacoes_ids: + ligacao_id.write({'state': 'validate'}) + + @api.multi + def name_get(self): + result = [] + for record in self: + if record.data_mes_ano and record.contract_id: + name = record.contract_id.nome_contrato + ' ' + \ + record.data_mes_ano + result.append((record.id, name)) + return result @api.multi def _buscar_dias_aviso_previo(self): @@ -61,8 +129,21 @@ def _buscar_dias_aviso_previo(self): else: payslip.dias_aviso_previo = 30 + @api.model + def compute_payment_day(self, date): + res = fields.Datetime.from_string(date) + relativedelta(days=-1) + rc = self.env['resource.calendar'] + end = 1 + while end < 2: + if rc.data_eh_dia_util(res): + end += 1 + res = res + relativedelta(days=-1) + else: + res = res + relativedelta(days=-2) + return res.date() + @api.multi - def _valor_total_folha(self): + def _compute_valor_total_folha(self): for holerite in self: total = 0.00 total_proventos = 0.00 @@ -73,40 +154,40 @@ def _valor_total_folha(self): 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 +# 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'): + if line.code in ['BASE_FGTS', 'BASE_FGTS_13']: + base_fgts += line.total + elif line.code in ['BASE_INSS', 'BASE_INSS_13']: + base_inss += line.total + elif line.code == 'BASE_IRPF': base_irpf = line.total - elif codigo['FGTS']: + elif line.code == 'FGTS': fgts = line.total - elif codigo['INSS']: + elif line.code == 'INSS': inss = line.total - elif codigo['IRPF']: + elif line.code == 'IRPF': irpf = line.total holerite.total_folha = total holerite.total_proventos = total_proventos @@ -134,17 +215,77 @@ def _valor_total_folha(self): holerite.fgts_fmt = valor.formata_valor(holerite.fgts) holerite.inss_fmt = valor.formata_valor(holerite.inss) holerite.irpf_fmt = valor.formata_valor(holerite.irpf) + holerite.data_extenso = data.data_por_extenso(fields.Date.today()) + holerite.data_retorno = data.formata_data( + str((fields.Datetime.from_string(holerite.date_to) + + relativedelta(days=1)).date())) + holerite.data_pagamento = str( + self.compute_payment_day(holerite.date_from)) + + # TO DO Verificar datas de feriados. + # A biblioteca aceita os parametros de feriados, mas a utilizacao + # dos feriados é diferente do odoo. + # Logo o método só é utilizado para antecipar em casos de finais + # de semana. + holerite.data_pagamento = data.formata_data( + data.dia_util_pagamento( + data_vencimento=holerite.data_pagamento, antecipa=True, + ) + ) + holerite.inicio_aquisitivo_fmt = data.formata_data( + holerite.periodo_aquisitivo.inicio_aquisitivo + ) + holerite.fim_aquisitivo_fmt = data.formata_data( + holerite.periodo_aquisitivo.fim_aquisitivo + ) + holerite.inicio_gozo_fmt = data.formata_data( + holerite.date_from + ) + holerite.fim_gozo_fmt = data.formata_data( + holerite.date_to + ) - employee_id_readonly = fields.Many2one( + employee_id = fields.Many2one( string=u'Funcionário', comodel_name='hr.employee', - compute='set_employee_id', + compute='_compute_set_employee_id', + store=True, + required=False, + states={'draft': [('readonly', False)]} ) + is_simulacao = fields.Boolean( string=u"Simulação", ) + + @api.depends('contract_id', 'dias_aviso_previo_trabalhados') + @api.multi + def _calcular_dias_aviso_previo(self): + for payslip in self: + if payslip.contract_id: + periodos_aquisitivos = self.env['hr.vacation.control'].search( + [ + ('contract_id', '=', payslip.contract_id.id) + ] + )[1:] + tempo_trabalhado = [] + tempo_trabalhado = [ + ano.inicio_aquisitivo for ano in periodos_aquisitivos if + ano.inicio_aquisitivo not in tempo_trabalhado] + + payslip.dias_aviso_previo = \ + 30 + (len(tempo_trabalhado) * 3) - \ + payslip.dias_aviso_previo_trabalhados + else: + payslip.dias_aviso_previo = 0 + dias_aviso_previo = fields.Integer( string="Dias de Aviso Prévio", + compute=_calcular_dias_aviso_previo + ) + dias_aviso_previo_trabalhados = fields.Integer( + string="Dias de Aviso Prévio Trabalhados", + default=0, ) @api.depends('line_ids') @@ -163,10 +304,17 @@ def _buscar_payslip_line(self): default='normal', ) - struct_id_readonly = fields.Many2one( + struct_id = fields.Many2one( string=u'Estrutura de Salário', comodel_name='hr.payroll.structure', - compute='set_employee_id', + compute='_compute_set_employee_id', + readonly=True, + states={'draft': [('readonly', False)]}, + help=u'Defines the rules that have to be applied to this payslip, ' + u'accordingly to the contract chosen. If you let empty the field ' + u'contract, this field isn\'t mandatory anymore and thus the ' + u'rules applied will be all the rules set on the structure of all' + u' contracts of the employee valid for the chosen period' ) mes_do_ano = fields.Selection( @@ -176,6 +324,17 @@ def _buscar_payslip_line(self): default=datetime.now().month, ) + mes_do_ano2 = fields.Selection( + selection=MES_DO_ANO2, + string=u'Mês', + required=True, + default=datetime.now().month, + ) + + @api.onchange('mes_do_ano2') + def on_change_mes_do_ano(self): + self.mes_do_ano = self.mes_do_ano2 + ano = fields.Integer( string=u'Ano', default=datetime.now().year, @@ -183,127 +342,180 @@ def _buscar_payslip_line(self): data_mes_ano = fields.Char( string=u'Mês/Ano', - compute='computar_mes_ano', + compute='_compute_data_mes_ano', ) total_folha = fields.Float( string=u'Total', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) total_folha_fmt = fields.Char( string=u'Total', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) data_admissao_fmt = fields.Char( string=u'Data de admissao', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) salario_base_fmt = fields.Char( string=u'Salario Base', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) total_proventos = fields.Float( string=u'Total Proventos', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) total_proventos_fmt = fields.Char( string=u'Total Proventos', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) total_descontos = fields.Float( string=u'Total Descontos', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) total_descontos_fmt = fields.Char( string=u'Total Descontos', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_fgts = fields.Float( string=u'Base do FGTS', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_fgts_fmt = fields.Char( string=u'Base do FGTS', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_inss = fields.Float( string=u'Base do INSS', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_inss_fmt = fields.Char( string=u'Base do INSS', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_irpf = fields.Float( string=u'Base do IRPF', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) base_irpf_fmt = fields.Char( string=u'Base do IRPF', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) fgts = fields.Float( string=u'FGTS', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) fgts_fmt = fields.Char( string=u'FGTS', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) inss = fields.Float( string=u'INSS', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) inss_fmt = fields.Char( string=u'INSS', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) irpf = fields.Float( string=u'IRPF', default=0.00, - compute='_valor_total_folha' + compute='_compute_valor_total_folha' ) irpf_fmt = fields.Char( string=u'IRPF', default='0', - compute='_valor_total_folha' + compute='_compute_valor_total_folha' + ) + + data_extenso = fields.Char( + string=u'Data por Extenso', + compute='_compute_valor_total_folha' + ) + + data_retorno = fields.Char( + string=u'Data de Retorno', + compute='_compute_valor_total_folha' + ) + + data_pagamento = fields.Char( + string=u'Data de Pagamento de férias', + compute='_compute_valor_total_folha', + ) + + inicio_aquisitivo = fields.Date( + string=u'Início do Período Aquisitivo', + compute='_compute_periodo_aquisitivo', + store=True, + ) + fim_aquisitivo = fields.Date( + string=u'Fim do Período Aquisitivo', + compute='_compute_periodo_aquisitivo', + store=True, + ) + + @api.multi + @api.depends('periodo_aquisitivo') + def _compute_periodo_aquisitivo(self): + for holerite in self: + if holerite.periodo_aquisitivo: + holerite.inicio_aquisitivo = \ + holerite.periodo_aquisitivo.inicio_aquisitivo + holerite.fim_aquisitivo = \ + holerite.periodo_aquisitivo.fim_aquisitivo + + inicio_aquisitivo_fmt = fields.Char( + string=u'Inicio do Período Aquisitivo Formatado', + compute='_compute_valor_total_folha', + ) + fim_aquisitivo_fmt = fields.Char( + string=u'Fim do Período Aquisitivo Formatado', + compute='_compute_valor_total_folha', + ) + inicio_gozo_fmt = fields.Char( + string=u'Inicio do Período de Gozo Formatado', + compute='_compute_valor_total_folha', + ) + fim_gozo_fmt = fields.Char( + string=u'Fim do Periodo de Gozo Formatado', + compute='_compute_valor_total_folha', ) medias_proventos = fields.One2many( @@ -319,22 +531,114 @@ def _buscar_payslip_line(self): 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] + date_from = fields.Date( + string='Date From', + #readonly=True, + #states={'draft': [('readonly', False)]}, + required=True, + #compute='_compute_set_dates', + #store=True, + ) + + date_to = fields.Date( + string='Date To', + #readonly=True, + #states={'draft': [('readonly', False)]}, + required=True, + #compute='_compute_set_dates', + #store=True, + ) + + saldo_para_fins_rescisorios = fields.Float( + string='Saldo para fins Rescisorios', + ) + + holidays_ferias = fields.Many2one( + comodel_name='hr.holidays', + string=u'Solicitação de Férias', + help=u'Período de férias apontado pelo funcionário em ' + u'Pedidos de Férias', + ) periodo_aquisitivo = fields.Many2one( comodel_name='hr.vacation.control', - default='_get_periodo_aquisitivo', string="Período Aquisitivo", domain="[('contract_id','=',contract_id)]", + #compute='_compute_set_dates', + #store=True, + ) + + data_inicio_periodo_aquisitivo = fields.Date( + string="Inicio do Período Aquisitivo", + related="periodo_aquisitivo.inicio_aquisitivo", + store=True, + ) + + data_fim_periodo_aquisitivo = fields.Date( + string="Fim do Período Aquisitivo", + related="periodo_aquisitivo.fim_aquisitivo", + store=True, + ) + + # Rescisão + data_afastamento = fields.Date( + string="Data do afastamento" + ) + + data_pagamento_demissao = fields.Date( + string="Data do pagamento" + ) + + valor_saldo_fgts = fields.Float( + string="Valor do Saldo do FGTS" + ) + + valor_multa_fgts = fields.Float( + string="Valor da Multa do FGTS" + ) + + ferias_vencidas = fields.Many2one( + comodel_name='hr.vacation.control', + string="Ferias Vencidas", + domain="[('contract_id','=',contract_id)]", + compute='_verificar_ferias_vencidas', store=True, ) + company_id = fields.Many2one( + string="Empresa", + comodel_name="res.company", + related='contract_id.company_id', + store=True + ) + + rescisao_ids = fields.One2many( + comodel_name='hr.campos.rescisao', + inverse_name='slip_id', + string=u'Campo de Rescisão' + ) + + ligacoes_ids = fields.One2many( + comodel_name='hr.telefonia.line', + inverse_name='payslip_id', + string=u'Ligações', + ) + + @api.depends('periodo_aquisitivo') + @api.model + def _compute_saldo_periodo_aquisitivo(self): + for holerite in self: + if holerite.periodo_aquisitivo: + holerite.saldo_periodo_aquisitivo = \ + holerite.periodo_aquisitivo.saldo + + saldo_periodo_aquisitivo = fields.Integer( + string="Saldo de dias do Periodo Aquisitivo", + compute='_compute_saldo_periodo_aquisitivo', + help=u'Saldo de dias do funcionaŕio, de acordo com número de faltas' + u'dentro do período aquisitivo selecionado.', + ) + def get_attendances(self, nome, sequence, code, number_of_days, number_of_hours, contract_id): attendance = { @@ -347,6 +651,17 @@ def get_attendances(self, nome, sequence, code, number_of_days, } return attendance + @api.multi + def unlink(self): + for payslip in self: + if payslip.state not in ['draft', 'cancel']: + raise exceptions.Warning( + _('You cannot delete a payslip which is not ' + 'draft or cancelled or permission!') + ) + payslip.cancel_sheet() + return super(HrPayslip, self).unlink() + @api.multi def get_worked_day_lines(self, contract_id, date_from, date_to): """ @@ -358,10 +673,14 @@ def get_worked_day_lines(self, contract_id, date_from, date_to): 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( - fields.Datetime.from_string(date_from), - fields.Datetime.from_string(date_to) - ) + if self.tipo_de_folha == 'rescisao': + dias_mes = fields.Date.from_string(date_to).day - \ + fields.Date.from_string(date_from).day + 1 + else: + dias_mes = self.env['resource.calendar'].get_dias_base( + fields.Datetime.from_string(date_from), + fields.Datetime.from_string(date_to) + ) result += [self.get_attendances(u'Dias Base', 1, u'DIAS_BASE', dias_mes, 0.0, contract_id)] @@ -375,7 +694,7 @@ def get_worked_day_lines(self, contract_id, date_from, date_to): # get faltas leaves = {} hr_contract = self.env['hr.contract'].browse(contract_id.id) - leaves = self.env['resource.calendar'].get_ocurrences( + leaves = self.env['hr.holidays'].get_ocurrences( hr_contract.employee_id.id, date_from, date_to) if leaves.get('faltas_nao_remuneradas'): qtd_leaves = leaves['quantidade_dias_faltas_nao_remuneradas'] @@ -400,10 +719,15 @@ def get_worked_day_lines(self, contract_id, date_from, date_to): u'DSR_PARA_DESCONTAR', quantity_DSR_discount, 0.0, contract_id)] + # get dias de férias + get dias de abono pecuniario - quantidade_dias_ferias, quantidade_dias_abono = \ - self.env['resource.calendar'].get_quantidade_dias_ferias( - hr_contract.employee_id.id, date_from, date_to) + if self.tipo_de_folha == 'provisao_ferias' or self.is_simulacao: + quantidade_dias_abono = 0 + quantidade_dias_ferias = self.periodo_aquisitivo.saldo + else: + quantidade_dias_ferias, quantidade_dias_abono = \ + self.env['resource.calendar'].get_quantidade_dias_ferias( + hr_contract, date_from, date_to) result += [ self.get_attendances( @@ -419,21 +743,37 @@ def get_worked_day_lines(self, contract_id, 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: + # se o periodo aquisitivo ja estiver definido, pega o saldo de dias + if self.periodo_aquisitivo: 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 - ) - ] + if self.periodo_aquisitivo: + saldo_ferias = self.periodo_aquisitivo.saldo + #if self.tipo_de_folha in ['rescisao'] or fields.\ + # Date.from_string(self.date_from) < fields.Date.\ + # from_string(self.periodo_aquisitivo.inicio_concessivo): + # saldo_ferias = \ + # self.periodo_aquisitivo.dias_de_direito() *\ + # self.medias_proventos[0]['meses'] / 12.0 + #else: + #if self.tipo_de_folha == 'ferias' and self.is_simulacao and \ + # self.date_to and self.date_from: + # dias_saldo = fields.Datetime.from_string(self.date_to) - \ + # fields.Datetime.from_string(self.date_from) + # saldo_ferias = dias_saldo.days + #else: + # saldo_ferias = self.periodo_aquisitivo.avos * 2.5 + + 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 = \ - 30 - leaves['quantidade_dias_faltas_nao_remuneradas'] - \ + dias_mes - leaves['quantidade_dias_faltas_nao_remuneradas'] - \ quantity_DSR_discount - quantidade_dias_ferias result += [self.get_attendances(u'Dias Trabalhados', 34, u'DIAS_TRABALHADOS', @@ -465,12 +805,53 @@ def get_inputs(self, contract_ids, date_from, date_to): 'amount': contract._salario_hora(date_from, date_to), 'contract_id': contract.id, } + salario_mes_proporcional_dic = { + 'name': 'Salário Mês Proporcional', + 'code': 'SALARIO_MES_PROPORCIONAL', + 'amount': contract._salario_mes_proporcional(date_from, date_to), + 'contract_id': contract.id, + } + salario_mes_inicial_dic = { + 'name': 'Salário Mês Inicial', + 'code': 'SALARIO_MES_INICIAL', + 'amount': contract._salario_mes_inicial(date_from, date_to), + 'contract_id': contract.id, + } + + # Caso tenha alteracao contratual no mês corrente, o provisionamento + # devera calcular com a remuneração alterada. Por isso na busca + # do salario, levamos em conta o inicio e o fim do mes. O sistema entao + # encontrara as alterações caso existam naquele mes + if self.tipo_de_folha in ['provisao_ferias', 'provisao_decimo_terceiro']: + date_from = str(self.ano).zfill(4) + '-' + \ + str(self.mes_do_ano).zfill(2) + '-01' + date_to = str(ultimo_dia_mes(date_from)) + + salario_mes_final_dic = { + 'name': 'Salário Mês Final', + 'code': 'SALARIO_MES_FINAL', + 'amount': contract._salario_mes_final(date_from, date_to), + 'contract_id': contract.id, + } + res += [salario_mes_inicial_dic] + res += [salario_mes_final_dic] + res += [salario_mes_proporcional_dic] res += [salario_mes_dic] res += [salario_dia_dic] res += [salario_hora_dic] return res def INSS(self, BASE_INSS): + """ + Cálculo do INSS da folha de pagamento. Essa função é responsável por + disparar o cálculo do INSS, que fica embutido em sua classe. + Essa função está dísponivel no context das rubricas sendo possível + inserir o seguinte código python na rubrica: + result = CALCULAR.INSS() + *CALCULAR é a instancia corrente da payslip + :param BASE_INSS: - float - soma das rubricas que compoe o INSS + :return: float - Valor do inss a ser descontado do funcionario + """ tabela_inss_obj = self.env['l10n_br.hr.social.security.tax'] if BASE_INSS: inss = tabela_inss_obj._compute_inss(BASE_INSS, self.date_from) @@ -478,37 +859,280 @@ def INSS(self, BASE_INSS): else: return 0 - def IRRF(self, BASE_IR, BASE_INSS): + def BASE_IRRF(self, TOTAL_IRRF, INSS): + """ + Calcula a base de cálculo do IRRF. A formula se da por: + BASE_IRRF = BASE_INSS - INSS - + (DESCONTO_POR_DEPENDENTE * QTY_DE_DEPENDENTES) + + :param TOTAL_IRRF: - float - Soma das rubricas que compoem a BASE_IRRF + :param INSS: - float - Valor do INSS a ser descontado + do funcionario + :return: - float - base do IRRF + """ + ano = fields.Datetime.from_string(self.date_from).year + + deducao_dependente_obj = self.env[ + 'l10n_br.hr.income.tax.deductable.amount.family' + ] + deducao_dependente_value = deducao_dependente_obj.search( + [('year', '=', ano)], order='create_date DESC', limit=1 + ) + dependent_values = 0 + for dependente in self.employee_id.dependent_ids: + if dependente.dependent_verification and \ + dependente.dependent_dob < self.date_from: + dependent_values += deducao_dependente_value.amount + + return TOTAL_IRRF - INSS - dependent_values + + def IRRF(self, BASE_IRRF, INSS): tabela_irrf_obj = self.env['l10n_br.hr.income.tax'] - if BASE_INSS and BASE_IR: - inss = self.INSS(BASE_INSS) + if BASE_IRRF: irrf = tabela_irrf_obj._compute_irrf( - BASE_IR, self.employee_id.id, inss, self.date_from + BASE_IRRF, self.employee_id.id, INSS, self.date_from ) return irrf else: return 0 + def MEDIA_RUBRICA(self, codigo): + media = 0 + if self.tipo_de_folha in ['ferias', 'provisao_ferias']: + # + # Identifica os períodos aquisitivos com saldo de férias + # + periodo = self.periodo_aquisitivo + + # Seta a Data final do aquisitivo apartir da inicial, para sempre + # pegar os holerites validos do periodo cheio + dt_fim = fields.Datetime.from_string(periodo.inicio_aquisitivo) + relativedelta(years=1, days=-1) + + if periodo.saldo != 0: + # + # Buscar Holerites do Período Aquisitivo + # + domain = [ + ('date_to', '>=', periodo.inicio_aquisitivo), + ('date_to', '<', str(dt_fim.date())), + ('contract_id', '=', self.contract_id.id), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']), + ] + folhas_periodo = self.env['hr.payslip'].search(domain) + folhas_periodo = \ + folhas_periodo.sorted(key=lambda r: r.date_from) + + # + # Obter os valores das rubricas especificadas nos holerites + # + valor = 0 + for holerite in folhas_periodo: + for linha in holerite.line_ids: + if linha.code == codigo: + valor += linha.total + + # Conforme e-mail enviado pela GECON-ABGF no dia 14/03/2018 às + # 16h52, foi considerado para o cálculo das médias o mês "cheio" + # e para o cálculo dos avos, foi aplicada a regra dos 15 dias de + # trab. + + # media = valor / 12 + media += ((valor / 12) / 30) * periodo.saldo + + return media + + else: + # + # Calcular o período desejado e a quantidade de meses + # Para ser usado no cálculo da média + # + meses = 12 + if self.tipo_de_folha in \ + ['ferias', 'aviso_previo']: + # if self.tipo_de_folha in ['provisao_ferias', 'aviso_previo']: + # periodo_aquisitivo = \ + # self.contract_id.vacation_control_ids[0] + # else: + periodo_aquisitivo = self.periodo_aquisitivo + + if self.tipo_de_folha in ['aviso_previo']: + data_de_inicio = fields.Date.from_string( + self.date_from) - relativedelta(months=12) + data_inicio_mes = fields.Date.from_string( + self.date_from).replace(day=1) - relativedelta( + months=12) + else: + data_de_inicio = fields.Date.from_string( + periodo_aquisitivo.inicio_aquisitivo) + data_inicio_mes = fields.Date.from_string( + periodo_aquisitivo.inicio_aquisitivo).replace(day=1) + + # Se trabalhou mais do que 15 dias, contabilizar o mes corrente + if (data_de_inicio - data_inicio_mes).days < 15: + data_de_inicio = data_inicio_mes + # Senão começar contabilizar medias apartir do mes seguinte + else: + data_de_inicio = data_inicio_mes + relativedelta(months=1) + if self.tipo_de_folha in ['provisao_ferias']: + data_final = self.date_to + else: + data_final = data_inicio_mes + relativedelta(months=12) + + dtstart = datetime.strptime(data_de_inicio, '%Y-%m-%d') + dtend = datetime.strptime(data_final, '%Y-%m-%d') + dates = [dt for dt in rrule(MONTHLY, dtstart=dtstart, until=dtend)] + meses = len(dates) + + elif self.tipo_de_folha in [ + 'decimo_terceiro', 'provisao_decimo_terceiro' + ]: + if self.contract_id.date_start > str(self.ano - 1) + '-12-01': + data_de_inicio = self.contract_id.date_start + else: + data_de_inicio = str(self.ano - 1) + '-12-01' + data_final = str(self.ano) + '-11-30' + + # + # Buscar Holerites do Período + # + folha_obj = self.env['hr.payslip'] + domain = [ + ('date_from', '>=', data_de_inicio), + ('date_to', '<=', data_final), + ('contract_id', '=', self.contract_id.id), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']), + ] + folhas_periodo = folha_obj.search(domain) + folhas_periodo = folhas_periodo.sorted(key=lambda r: r.date_from) + + # + # Buscar dentro dos holerites pela rubrica requerida + # + valor = 0 + for folha in folhas_periodo: + for linha in folha.line_ids: + if linha.salary_rule_id.code == codigo: + valor += linha.total + + media = valor / meses + return media + @api.model 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 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 + applied_specific_rule = {} + for specific_rule in contract.specific_rule_ids: + if self.date_from >= specific_rule.date_start: + if not specific_rule.date_stop or \ + self.date_to <= specific_rule.date_stop: + + rule_ids.append((specific_rule.rule_id.id, + specific_rule.rule_id.sequence)) + + if specific_rule.rule_id.id not in applied_specific_rule: + applied_specific_rule[specific_rule.rule_id.id] = [] + + applied_specific_rule[specific_rule.rule_id.id].append( + specific_rule) + + return applied_specific_rule + + def get_ferias_rubricas(self, payslip, rule_ids): + holerite_ferias = self.search([ + ('tipo_de_folha', '=', 'ferias'), + ('date_from', '>=', payslip.date_from), + ('date_from', '<=', payslip.date_to), + ('contract_id', '=', payslip.contract_id.id), + ('state', 'in', ['done', 'verify']) + ]) + if holerite_ferias: + for line in holerite_ferias.line_ids: + if line.code == 'BASE_FERIAS': + continue + rule_ids.append( + (line.salary_rule_id.id, line.salary_rule_id.sequence)) + return rule_ids, holerite_ferias.holidays_ferias + + def buscar_ferias_do_mes(self, payslip): + """ + Buscar holerite de ferias que tem o inicio dentro do mes da competencia + que esta sendo calculada. Isto é, quando estiver sendo calculado o + holerite de OUTUBRO, buscar férias que tem a data inicial em OUT. + """ + holerite_ferias = self.search([ + ('tipo_de_folha', '=', 'ferias'), + ('date_from', '>=', payslip.date_from), + ('date_from', '<=', payslip.date_to), + ('contract_id', '=', payslip.contract_id.id), + ('state', 'in', ['done', 'verify']), + ('is_simulacao', '=', False) + ]) + if holerite_ferias: + lines = [] + for line in holerite_ferias.line_ids: + if line.category_id.code == 'PROVENTO': + lines.append(line) + else: + return False, False + return lines, holerite_ferias.holidays_ferias @api.model - def get_specific_rubric_value(self, rubrica_id, medias_obj=False): + def get_specific_rubric_value(self, rubrica_id, medias_obj=False, + rubricas_especificas_calculadas=False): + """ + Função dísponivel para as regras de salario, que busca o valor das + rubricas especificas cadastradas no contrato. + :param rubrica_id: int - id da regra de salario corrente + :param medias_obj: ? + :param rubricas_spec_calculadas - lista dos ids das rubricas + especificas que ja foram computadas + :return: valor da rubrica especifica cadastrado no contrato ou 0. + """ for rubrica in self.contract_id.specific_rule_ids: - if rubrica.rule_id.id == rubrica_id: + # Se a rubrica_especifica ja tiver sido calculada segue pra próxima + if rubricas_especificas_calculadas and \ + rubrica.id in rubricas_especificas_calculadas: + continue + if rubrica.rule_id.id == rubrica_id \ + and rubrica.date_start <= self.date_from and \ + (not rubrica.date_stop or rubrica.date_stop >= + self.date_to): 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_percentual / 100 * \ rubrica.specific_amount + @api.multi + def get_desconto_ligacao_telefonica(self): + """ + Função para buscar descontos de ligacoes telefonicas atestadas pelo + funcionario + :return: + """ + ligacao_obj = self.env['hr.telefonia.line'] + for holerite_id in self: + + # Desvincular antigas + holerite_id.ligacoes_ids = False + + domain = [ + ('state','=','validate'), + ('tipo','=','particular'), + ('employee_id','=', holerite_id.contract_id.employee_id.id), + ('payslip_id','=', False) + ] + ligacoes_ids = ligacao_obj.search(domain) + if ligacoes_ids: + # Criar relacao entre a ligacao e o holerite + for ligacao_id in ligacoes_ids: + ligacao_id.payslip_id = holerite_id.id + return sum(ligacoes_ids.mapped('valor')) + return 0.0 + @api.multi def _buscar_valor_salario(self, codigo): for tipo_salario in self.input_line_ids: @@ -535,24 +1159,296 @@ def buscar_estruturas_salario(self): 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' - ) + if self.is_simulacao: + estrutura_decimo_terceiro = \ + self.contract_id.struct_id.estrutura_13_id 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 + if self.mes_do_ano <= 12: + estrutura_decimo_terceiro = \ + self.contract_id.struct_id.estrutura_adiantamento_13_id + return estrutura_decimo_terceiro + else: + estrutura_decimo_terceiro = \ + self.contract_id.struct_id.estrutura_13_id + return estrutura_decimo_terceiro elif self.tipo_de_folha == "ferias": - estrutura_decimo_terceiro = self.env.ref( - 'l10n_br_hr_payroll.' - 'hr_salary_structure_FERIAS' + estrutura_ferias = \ + self.contract_id.struct_id.estrutura_ferias_id + return estrutura_ferias + elif self.tipo_de_folha == "rescisao": + estrutura_rescisao = self.env['hr.payroll.structure'].search( + [('code', '=', 'RESCISAO')], limit=1 + ) + return estrutura_rescisao + elif self.tipo_de_folha == "provisao_ferias": + estrutura_provisao_ferias = \ + self.contract_id.struct_id.estrutura_ferias_id + return estrutura_provisao_ferias + elif self.tipo_de_folha == "provisao_decimo_terceiro": + estrutura_provisao_decimo_terceiro = \ + self.contract_id.struct_id.estrutura_13_id + return estrutura_provisao_decimo_terceiro + + @api.depends('contract_id', 'date_from', 'date_to') + @api.one + def _verificar_ferias_vencidas(self): + for record in self: + periodo_ferias_vencida = self.env['hr.vacation.control'].search([ + ('contract_id', '=', record.contract_id.id), + ('fim_aquisitivo', '<', record.date_from), + ('fim_concessivo', '>=', record.date_to), + ('inicio_gozo', '=', False), + ], order='fim_aquisitivo desc') + return periodo_ferias_vencida + + @api.multi + def gerar_simulacao( + self, tipo_simulacao, mes_do_ano, ano, data_inicio, data_fim, + ferias_vencida=None, periodo_aquisitivo=None + ): + hr_payslip_obj = self.env['hr.payslip'] + vals = { + 'contract_id': self.contract_id.id, + 'tipo_de_folha': tipo_simulacao, + 'is_simulacao': True, + 'mes_do_ano': mes_do_ano, + 'ano': ano, + 'date_from': data_inicio, + 'date_to': data_fim, + 'employee_id': self.contract_id.employee_id.id + } + if tipo_simulacao == "aviso_previo": + vals.update({'dias_aviso_previo': self.dias_aviso_previo}) + payslip_simulacao_criada = hr_payslip_obj.create(vals) + if tipo_simulacao == "ferias": + #periodo_ferias_vencida = False + #if ferias_vencida: + # periodo_ferias_vencida = self._verificar_ferias_vencidas() + payslip_simulacao_criada.write( + { + 'periodo_aquisitivo': periodo_aquisitivo.id + # self.contract_id.vacation_control_ids[0].id if + # not periodo_ferias_vencida else + # periodo_ferias_vencida.id + } + ) + #payslip_simulacao_criada._compute_saldo_periodo_aquisitivo() + + if tipo_simulacao in ["ferias", "aviso_previo"]: + payslip_simulacao_criada.gerar_media_dos_proventos() + payslip_simulacao_criada._compute_set_employee_id() + # worked_days_line_ids = \ + # payslip_simulacao_criada.get_worked_day_lines( + # self.contract_id.id, data_inicio, data_fim + # ) + # input_line_ids = payslip_simulacao_criada.get_inputs( + # self.contract_id.id, data_inicio, data_fim + # ) + # 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_simulacao_criada.id}) + # worked_days_obj.create(worked_day) + # for input_id in input_line_ids: + # input_id.update({'payslip_id': payslip_simulacao_criada.id}) + # input_obj.create(input_id) + payslip_simulacao_criada.atualizar_worked_days_inputs() + payslip_simulacao_criada.compute_sheet() + payslip_simulacao_criada.write({'paid': True, 'state': 'done'}) + return payslip_simulacao_criada + + @api.multi + def _buscar_valor_bruto_simulacao( + self, payslip_simulacao, um_terco_ferias=None): + categoria_bruto = self.env.ref( + 'hr_payroll.BRUTO' + ) + for line in payslip_simulacao.line_ids: + if payslip_simulacao.tipo_de_folha == "ferias": + if line.salary_rule_id.code == "FERIAS" and \ + not um_terco_ferias: + return line.total + elif line.salary_rule_id.code == "1/3_FERIAS" and \ + um_terco_ferias: + return line.total + else: + if line.salary_rule_id.category_id.id == categoria_bruto.id: + return line.total + + @api.multi + def _checar_datas_gerar_simulacoes(self, mes_do_ano, ano): + if mes_do_ano > 1: + mes_do_ano -= 1 + else: + mes_do_ano = 12 + ano -= 1 + dias_no_mes = monthrange(ano, mes_do_ano) + data_inicio = str(ano) + "-" + str(mes_do_ano) + "-" + "01" + data_fim = str(ano) + "-" + str(mes_do_ano) + "-" + str(dias_no_mes[1]) + return mes_do_ano, ano, data_inicio, data_fim + + def _simulacao_ferias(self, ferias_vencida, um_terco_ferias): + + # + # Está buscando somente o último periodo aquisitivo, e se tiver férias vencida busca + # o penúltimo, essa lógica está incompleta, precisa refatorar esse código para buscar + # todos os periodos aquisitivos vencidos (TODO) + # + if not ferias_vencida: + periodo_id = self.contract_id.vacation_control_ids[0].id + else: + periodo_id = self.contract_id.vacation_control_ids[1].id + + self.contract_id.date_end = self.date_to + self.contract_id.atualizar_data_demissao() + + periodo = self.env['hr.vacation.control'].with_context(data_fim = self.data_afastamento).browse(periodo_id) + + if periodo.saldo: + data_inicio = self.data_afastamento + data_fim = data.parse_datetime(data_inicio) + relativedelta(days=periodo.avos * 2.5) + + domain = [ + ('tipo_de_folha', '=', 'ferias'), + ('is_simulacao', '=', True), + ('periodo_aquisitivo', '=', periodo_id) + ] + payslip_simulacao = self.env['hr.payslip'].search(domain) + if payslip_simulacao: + payslip_simulacao_criada = payslip_simulacao + else: + payslip_simulacao_criada = self.gerar_simulacao( + 'ferias', self.mes_do_ano, + self.ano, data_inicio, + data_fim, ferias_vencida=ferias_vencida, + periodo_aquisitivo=periodo + ) + return self._buscar_valor_bruto_simulacao( + payslip_simulacao_criada, um_terco_ferias) + + def _simulacao_decimo_terceiro(self): + + data_inicio = self.data_afastamento + data_fim = self.data_afastamento + + domain = [ + ('tipo_de_folha', '=', 'decimo_terceiro'), + ('is_simulacao', '=', True), + ('date_from', '=', data_inicio), + ('date_to', '=', data_fim), + ] + payslip_simulacao = self.env['hr.payslip'].search(domain) + if payslip_simulacao: + payslip_simulacao_criada = payslip_simulacao + else: + payslip_simulacao_criada = self.gerar_simulacao( + 'decimo_terceiro', self.mes_do_ano, + self.ano, data_inicio, + data_fim + ) + return self._buscar_valor_bruto_simulacao( + payslip_simulacao_criada) + + @api.multi + def BUSCAR_VALOR_PROPORCIONAL( + self, tipo_simulacao, um_terco_ferias=None, ferias_vencida=None): + + # Se simulação férias, faça e saia (ignorando o resto do método, precisa refatorar) (TODO) + if tipo_simulacao=='ferias': + return self._simulacao_ferias(ferias_vencida, um_terco_ferias) + + if tipo_simulacao=='decimo_terceiro': + return self._simulacao_decimo_terceiro() + + mes_verificacao, ano_verificacao, data_inicio, data_fim = \ + self._checar_datas_gerar_simulacoes( + self.mes_do_ano, self.ano + ) + payslip_simulacao = self.env['hr.payslip'] + if not ferias_vencida: + if not tipo_simulacao == "ferias": + payslip_simulacao = self.env['hr.payslip'].search( + [ + ('tipo_de_folha', '=', tipo_simulacao), + ('is_simulacao', '=', True), + ('mes_do_ano', '=', mes_verificacao), + ('ano', '=', ano_verificacao), + ('state', 'in', ['done', 'verify']), + ] + ) + else: + # periodos_ferias_simulacao = \ + # self.env['hr.vacation.control'].search( + # [ + # ('contract_id', '=', self.contract_id.id), + # ('inicio_gozo', '=', data_inicio), + # ('fim_gozo', '=', data_fim) + # ] + # ) + domain = [ + ('tipo_de_folha', '=', tipo_simulacao), + ('is_simulacao', '=', True), + ('mes_do_ano', '=', mes_verificacao), + ('ano', '=', ano_verificacao), + ('state', 'in', ['done', 'verify']), + ] + + domain.append( + ('periodo_aquisitivo', '=', + self.contract_id.vacation_control_ids[0].id if + not ferias_vencida else + self.contract_id.vacation_control_ids[1].id) + ) + payslip_simulacao = self.env['hr.payslip'].search(domain) + else: + # periodos_ferias_simulacao = \ + # self.env['hr.vacation.control'].search( + # [ + # ('contract_id', '=', self.contract_id.id), + # ('inicio_gozo', '=', data_inicio), + # ('fim_gozo', '=', data_fim) + # ] + # ) + domain = [ + ('tipo_de_folha', '=', tipo_simulacao), + ('is_simulacao', '=', True), + ('mes_do_ano', '=', mes_verificacao), + ('ano', '=', ano_verificacao), + ('state', 'in', ['done', 'verify']), + ] + domain.append( + ('periodo_aquisitivo', '=', + self.contract_id.vacation_control_ids[0].id if + not ferias_vencida else + self.contract_id.vacation_control_ids[1].id) + ) + payslip_simulacao = self.env['hr.payslip'].search(domain) + if len(payslip_simulacao) > 1: + # + # Excluir as simulações existentes para forçar uma nova + # + for payslip in payslip_simulacao: + payslip.unlink() + #raise exceptions.Warning( + # _( + # 'Existem duas simulações ' + # 'para %s neste período' % tipo_simulacao + # ) + #) + payslip_simulacao = self.env['hr.payslip'].search(domain) + + if len(payslip_simulacao) == 0: + payslip_simulacao_criada = self.gerar_simulacao( + tipo_simulacao, mes_verificacao, + ano_verificacao, data_inicio, + data_fim, ferias_vencida=ferias_vencida ) - return estrutura_decimo_terceiro + return self._buscar_valor_bruto_simulacao( + payslip_simulacao_criada, um_terco_ferias) + else: + return self._buscar_valor_bruto_simulacao( + payslip_simulacao, um_terco_ferias) # @api.multi # def buscar_media_rubrica(self, rubrica_id): @@ -561,6 +1457,81 @@ def buscar_estruturas_salario(self): # if rubrica.name == media.nome_rubrica: # return media.media + @api.multi + def verificar_adiantamento_13_aviso_ferias(self): + """ + Verifica se houve aidantamento de 13º salário no aviso de férias do + periodo + :return: Retorna a linha do adiantamento de 13º existente no aviso + de férias + """ + payslips_id = self.search( + [ + ('tipo_de_folha', '=', 'ferias'), + ('contract_id', '=', self.contract_id.id), + ('date_from', '>=', str(self.ano) + '-01-01'), + ('date_to', '<=', self.date_to), + ('state', 'in', ['done','verify']), + ('is_simulacao', '=', False), + ] + ) + salary_rule_id = self.env['hr.salary.rule'].search( + [ + ('code', '=', 'ADIANTAMENTO_13') + ] + ) + payslip_line_id = self.env['hr.payslip.line'].search( + [ + ('slip_id', 'in', payslips_id.ids), + ('salary_rule_id', '=', salary_rule_id.id), + ] + ) + return payslip_line_id + + @api.multi + def BUSCAR_ADIANTAMENTO_DECIMO_TERCEIRO(self): + """ + Retorna o valor total do adiantamento de 13º pedido no aviso de férias + :return: Valor total da linha do adiantamento do 13º no aviso de férias + """ + payslip_line_id = self.verificar_adiantamento_13_aviso_ferias() + return payslip_line_id.total + + @api.multi + def verificar_ferias_no_periodo(self): + """ + Função responsável por procurar a férias parcial tirada no periodo + atual referente ao último periodo vencido de férias. Calcular o valor + total pago pelos dias de gozo e o 1/3 sobre este valor. + :return: Total dos dias de gozo + 1/3 sobre o total dos dias de gozo + """ + periodo_aquisitivo = self._verificar_ferias_vencidas() + payslip_ferias = self.search( + [ + ('tipo_de_folha', '=', 'ferias'), + ('holidays_ferias', '!=', False), + ('contract_id', '=', self.contract_id.id), + ('date_from', '>=', periodo_aquisitivo.inicio_concessivo), + ('date_to', '<=', periodo_aquisitivo.fim_concessivo), + ] + ) + total = 0.0 + for line in payslip_ferias.line_ids: + if line.salary_rule_id.code == "FERIAS": + total += line.total + elif line.salary_rule_id.code == "1/3_FERIAS": + total += line.total + return total + + @api.multi + def BUSCAR_ADIANTAMENTO_FERIAS(self): + """ + Função chamada pela rúbrica Adiantamento de Férias para buscar o valor + das férias parciais que foi tirada no periodo pelo funcionario. + :return: Total pago na férias parcial do periodo + """ + return self.verificar_ferias_no_periodo() + @api.multi def BUSCAR_PRIMEIRA_PARCELA(self): primeira_parcela_struct_id = self.env.ref( @@ -584,6 +1555,139 @@ def BUSCAR_PRIMEIRA_PARCELA(self): for line in payslip_id.line_ids: if line.salary_rule_id.id == primeira_parcela_id.id: return line.total + return 0 + + def busca_adiantamento_13(self): + '''Metodo para recuperar valor pago de adiantamento de 13º no ano + :return: float - Valor pago neste ano + ''' + domain = [ + ('tipo_de_folha', 'in', ['decimo_terceiro', 'ferias']), + ('contract_id', '=', self.contract_id.id), + ('state', 'in', ['done', 'verify']), + ('ano', '=', self.ano), + ('is_simulacao', '=', False), + ('mes_do_ano', '<=', self.mes_do_ano), + ] + holerites = self.search(domain, order='mes_do_ano DESC') + + valor = 0 + if holerites: + for holerite in holerites: + for line in holerite.line_ids: + if line.code in [ + 'ADIANTAMENTO_13', + 'ADIANTAMENTO_13_FERIAS', + 'PRIMEIRA_PARCELA_13', + ]: + if not (self.tipo_de_folha == 'ferias' + and holerite.mes_do_ano == self.mes_do_ano): + valor += line.total + + # PAra contratos de PSS temos a rubrica de desconto de adiantamento do cedido + # como é uma rubrica de deducao, diminuir o valor caso a encontre + if holerite.contract_id.adiantamento_13_cedente: + + # Buscar nos holerites de primeira parcela se ja foi descontado + desconto_line_id = \ + holerites.mapped('line_ids').filtered( + lambda x: x.code == 'ADIANTAMENTO_13_CEDIDO') + + if desconto_line_id: + valor -= desconto_line_id.total or 0 + return valor + + # caso nao encontre holerite de adiantamento, + # retorne o valor cadastrado no contrato + # return self.contract_id.adiantamento_13_cedente or 0.0 + + def rubrica_anterior_total(self, code, mes=-1, tipo_de_folha='normal'): + '''Metodo para recuperar uma rubrica de um mes anterior + :param code: string - Code de identificação da rubrica + :param meses: int [1-12] Identificar um mes especifico + -1 Selecionar o ultimo payslip calculado + :param tipo_de_folha: + :return: + ''' + domain = [ + ('tipo_de_folha', '=', tipo_de_folha), + ('contract_id', '=', self.contract_id.id), + ('is_simulacao', '=', False), + ('state', 'in', ['done', 'verify']) + ] + + # Calcula mes_anterior + anos = [self.ano] + mes_anterior = self.mes_do_ano - 1 + if mes_anterior == 0: + mes_anterior = 12 + anos.append(self.ano - 1) + + if mes and mes > 0: + domain.append(('mes_do_ano', '=', mes)) + + if mes == -1 and tipo_de_folha == 'ferias': + domain.append(('mes_do_ano', 'in', + [self.mes_do_ano, mes_anterior])) + domain.append(('ano', 'in', anos)) + + holerite = self.search( + domain, order='date_from DESC', limit=1) + + valores = 0 + + if holerite: + if (self.date_from <= holerite.date_from <= self.date_to or + self.date_from <= holerite.date_to <= self.date_to): + for line in holerite.line_ids: + if line.code == code: + valores += line.total + + return valores + + def rubrica_pensao_alimentar(self): + ''' + :param code: string - Code de identificação da rubrica + :param meses: int [1-12] Identificar um mes especifico + -1 Selecionar o ultimo payslip calculado + :param tipo_de_folha: + :return: + ''' + + domain = [ + ('tipo_de_folha', '=', 'ferias'), + ('contract_id', '=', self.contract_id.id), + ('is_simulacao', '=', False), + ('state', 'in', ['done', 'verify']) + ] + + # Calcula mes_anterior + anos = [self.ano] + mes_anterior = self.mes_do_ano - 1 + if mes_anterior == 0: + mes_anterior = 12 + anos.append(self.ano - 1) + + if self.mes_do_ano and mes_anterior > 0: + domain.append(('mes_do_ano', 'in', [self.mes_do_ano, mes_anterior])) + + if mes_anterior == -1: + domain.append(('mes_do_ano', 'in', + [self.mes_do_ano, mes_anterior])) + domain.append(('ano', 'in', anos)) + + holerite = self.search( + domain, order='create_date DESC', limit=1) + + valores = 0 + + if holerite: + if self.date_from <= holerite.date_from <= self.date_to or \ + self.date_from <= holerite.date_to <= self.date_to: + for line in holerite.line_ids: + if line.code == 'PENSAO_ALIMENTICIA': + valores += line.total + return valores @api.multi def get_payslip_lines(self, payslip_id): @@ -678,6 +1782,7 @@ def sum(self, code, from_date, to_date=None): blacklist = [] payslip_obj = self.env['hr.payslip'] obj_rule = self.env['hr.salary.rule'] + rubricas_spec_model = self.env['hr.contract.salary.rule'] payslip = payslip_obj.browse(payslip_id) worked_days = {} for worked_days_line in payslip.worked_days_line_ids: @@ -688,12 +1793,14 @@ def sum(self, code, from_date, to_date=None): 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) @@ -707,6 +1814,92 @@ def sum(self, code, from_date, to_date=None): 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) + + # Construir um browsableObject para informações da quantidade de dias + # de ferias e de abono na payslip. + # payslip.holidays_ferias + dias_abono_ferias = {} + if payslip.holidays_ferias and not payslip.is_simulacao: + if payslip.holidays_ferias.vacations_days: + dias_abono_ferias.update( + {'DIAS_FERIAS': payslip.holidays_ferias.vacations_days} + ) + if payslip.holidays_ferias.sold_vacations_days: + dias_abono_ferias.update( + {'DIAS_ABONO': payslip.holidays_ferias.sold_vacations_days} + ) + elif payslip.struct_id.code == "FERIAS" and payslip.is_simulacao: + if not payslip.saldo_periodo_aquisitivo: + dias_abono_ferias.update({ + 'DIAS_FERIAS': worked_days[u'SALDO_FERIAS'].number_of_days + }) + else: + dias_abono_ferias.update( + {'DIAS_FERIAS': payslip.saldo_periodo_aquisitivo} + ) + else: + dias_abono_ferias.update( + {'DIAS_FERIAS': payslip.saldo_periodo_aquisitivo} + ) + ferias_abono = InputLine(payslip.employee_id.id, dias_abono_ferias) + + # Variavel para contabilizar os avos na contabilização do + # adiantamento do decimo terceiro salario + adiantamento_avos_13 = 12 + + # + # Calcula os Avos do payslip para Provisão de 13º Salário + # + + # Ajusta para casos que mes = 13 representando decimo terceiro salario + mes_do_ano = payslip.mes_do_ano if payslip.mes_do_ano < 12 else 12 + + # Se a data de contratação for no ano corrente, + # a variavel que indica a quantidade de meses de 13 salario + # deve ser contabilizada a partir da data de contratação + # exemplo.: + # Data Corrente: 01/12/2017 + # contratação: 31/05/2017 -> avos_13 = (mescorrente - 05) -1 = 6 + # contratação: 31/05/2016 -> avos_13 = mes corrente -1 = 11 + if fields.Date.from_string(payslip.contract_id.date_start) > \ + fields.Date.from_string(str(payslip.ano)+'-01-01'): + + dia_inicio_contrato = \ + fields.Date.from_string(payslip.contract_id.date_start).day + mes_inicio_contrato = \ + fields.Date.from_string(payslip.contract_id.date_start).month + + avos_13 = int(mes_do_ano) - int(mes_inicio_contrato) + 1 + + adiantamento_avos_13 = 13 - int(mes_inicio_contrato) + + if dia_inicio_contrato > 15: + avos_13 -= 1 + adiantamento_avos_13 -= 1 + else: + avos_13 = mes_do_ano + + # No contrato do PSS, o calculo do 13 Salario é diferente. + # mesmo que a data de contratação for no ano corrente, + # contabilizar todos os meses do ano. + # exemplo.: + # Data Corrente: 01/12/2017 + # contratação: 31/11/2017 -> avos_13 = 12 + # contratação: 31/05/2017 -> avos_13 = 12 + # contratação: 31/05/2016 -> avos_13 = 12 + if payslip.contract_id.data_admissao_cedente: + avos_13 = mes_do_ano + + if payslip.contract_id.date_end: + if datetime.strptime(payslip.contract_id.date_end, '%Y-%m-%d').month \ + == mes_do_ano: + dia_fim_contrato = \ + fields.Date.from_string(payslip.contract_id.date_end).day + if dia_fim_contrato <= 15: + avos_13 -= 1 + + # Obtém os avos do payslip para as Provisão de Férias + baselocaldict = { 'CALCULAR': payslip, 'BASE_INSS': 0.0, 'BASE_FGTS': 0.0, 'BASE_IR': 0.0, 'categories': categories_obj, 'rules': rules_obj, @@ -714,35 +1907,242 @@ def sum(self, code, from_date, to_date=None): 'inputs': input_obj, 'rubrica': None, 'SALARIO_MES': salario_mes, 'SALARIO_DIA': salario_dia, 'SALARIO_HORA': salario_hora, 'RAT_FAP': rat_fap, 'MEDIAS': medias_obj, + 'PEDIDO_FERIAS': ferias_abono, 'PAGAR_FERIAS': False, + 'AVOS_13': avos_13, 'adiantamento_avos_13': adiantamento_avos_13, + 'DIAS_AVISO_PREVIO': payslip.dias_aviso_previo, + 'RUBRICAS_ESPEC_CALCULADAS': [], + 'locals': locals, + 'globals': locals, + 'Decimal': Decimal, + 'D': Decimal, } 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() + # recuperar todas as estrututras que serao processadas + # (estruturas da payslip atual e as estruturas pai da payslip) structure_ids = payslip.struct_id._get_parent_structure() - # get the rules of the structure and thier children + # recuperar as regras das estruturas e filha de cada regra rule_ids = self.env['hr.payroll.structure'].browse( structure_ids).get_all_rules() - rule_ids = payslip.get_contract_specific_rubrics( - contract_ids, rule_ids) - # run the rules by sequence + applied_specific_rule = {} + # Caso nao esteja computando holerite de provisão de ferias ou + # de decimo terceiro recuperar as regras especificas do contrato + if not payslip.tipo_de_folha in \ + ['provisao_ferias', 'provisao_decimo_terceiro', 'decimo_terceiro']: + applied_specific_rule = payslip.get_contract_specific_rubrics( + contract_ids, rule_ids) + + # Buscar informações de férias dentro do mês que esta sendo + # processado. Isto é, fazer uma busca para verificar se no mês de + # outubro o funcionário ja gozou alguma férias. + lines, holidays_ferias = self.buscar_ferias_do_mes(payslip) + # Se encontrar informações de férias gozadas dentro do mês, + # trazer as informações de férias para o holerite mensal. + if holidays_ferias and worked_days_obj.FERIAS.number_of_days > 0 \ + and payslip.tipo_de_folha == 'normal': + # Atualiza o Holerite para colocar o holidays_ferias correspondente + self.env['hr.payslip'].browse(payslip_id).holidays_ferias = holidays_ferias + # Atualizar o baselocaldict para informar que tem que pagar + # ferias naquele holerite + baselocaldict.update({'PAGAR_FERIAS': True}) + # Recuperar configurações do RH para calcular férias proporcio + ferias_proporcionais = \ + self.env['ir.config_parameter'].get_param( + 'l10n_br_hr_payroll_ferias_proporcionais', + default=False) + + if ferias_proporcionais: + proporcao_ferias = \ + worked_days_obj.FERIAS.number_of_days / \ + holidays_ferias.vacations_days + + if holidays_ferias.sold_vacations_days == 0: + proporcao_abono = 0 + else: + proporcao_abono = \ + worked_days_obj.ABONO_PECUNIARIO.number_of_days / \ + holidays_ferias.sold_vacations_days + + for line in lines: + key = line.code + '_FERIAS' + amount = line.amount + qty = line.quantity + category_id = line.category_id + name = line.name + + if ferias_proporcionais: + if line.code in \ + ['ABONO_PECUNIARIO', '1/3_ABONO_PECUNIARIO']: + qty *= proporcao_abono + else: + qty *= proporcao_ferias + + if 'FERIAS' not in line.code: + name += u' (Férias)' + + result_dict[key] = { + 'salary_rule_id': line.salary_rule_id.id, + 'contract_id': payslip.contract_id.id, + 'name': name, + 'code': line.code + '_FERIAS', + 'category_id': category_id.id, + 'sequence': line.sequence - 0.01, + 'appears_on_payslip': line.appears_on_payslip, + 'condition_select': line.condition_select, + 'condition_python': line.condition_python, + 'condition_range': line.condition_range, + 'condition_range_min': line.condition_range_min, + 'condition_range_max': line.condition_range_max, + 'amount_select': line.amount_select, + 'amount_fix': line.amount_fix, + 'amount_python_compute': + line.amount_python_compute, + 'amount_percentage': line.amount_percentage, + 'amount_percentage_base': + line.amount_percentage_base, + 'register_id': line.register_id.id, + 'amount': amount, + 'employee_id': payslip.employee_id.id, + 'quantity': qty, + 'rate': line.rate, + 'partner_id': line.partner_id.id, + } + baselocaldict[line.code + '_FERIAS'] = line.total + + # sum the amount for its salary category + baselocaldict = _sum_salary_rule_category( + baselocaldict, line.salary_rule_id.category_id, + line.total) + + if line.category_id.code == 'DEDUCAO': + if line.salary_rule_id.compoeq: + baselocaldict['BASE_INSS'] -= line.total + if line.salary_rule_id.compoe_base_IR: + baselocaldict['BASE_IR'] -= line.total + if line.salary_rule_id.compoe_base_FGTS: + baselocaldict['BASE_FGTS'] -= line.total + else: + if line.salary_rule_id.compoe_base_INSS: + baselocaldict['BASE_INSS'] += line.total + if line.salary_rule_id.compoe_base_IR: + baselocaldict['BASE_IR'] += line.total + if line.salary_rule_id.compoe_base_FGTS: + baselocaldict['BASE_FGTS'] += line.total + + # Cria ajuste de INSS (Provento) proporcional às ferias + # Como ja foi pago o INSS no aviso de ferias, É criado + # uma rubrica de provento para devolver ao funcionario + # o valor descontado nas ferias proporcionalmente a + # competencia corrente. + # if line.code == 'INSS': + # line.copy({ + # 'slip_id': payslip.id, + # 'name': line.name + ' (ferias)', + # 'code': 'AJUSTE_' + line.code + '_FERIAS', + # 'sequence': 199, + # }) + # name = u'Ajuste INSS Férias' + # category_id = \ + # self.env.ref('hr_payroll.PROVENTO') + # # Ajuste do INSS compoe base do IR + # # mas nao compoe base do INSS + # baselocaldict['BASE_INSS'] -= line.total + # baselocaldict['BASE_IR'] += line.total + + # organizando as regras pela sequencia de execução definida sorted_rule_ids = \ - [id for id, sequence in sorted(rule_ids, key=lambda x:x[1])] + [id for id, sequence in sorted(rule_ids, key=lambda x: x[1])] + + if payslip.tipo_de_folha == "rescisao": + if not payslip.verificar_adiantamento_13_aviso_ferias(): + salary_rule_id = self.env['hr.salary.rule'].search( + [ + ('code', '=', 'ADIANTAMENTO_13_RESC'), + ] + ) + if salary_rule_id.id in sorted_rule_ids: + sorted_rule_ids.remove(salary_rule_id.id) + if not payslip._verificar_ferias_vencidas(): + ferias_vencida_rubrica = self.env['hr.salary.rule'].search( + [ + ('code', '=', 'FERIAS_VENCIDAS'), + ] + ) + sorted_rule_ids.remove(ferias_vencida_rubrica.id) + ferias_vencida_1_3_rubrica = \ + self.env['hr.salary.rule'].search( + [ + ('code', '=', 'FERIAS_VENCIDAS_1/3') + ] + ) + sorted_rule_ids.remove(ferias_vencida_1_3_rubrica.id) + adiantamento_ferias_vencida = \ + self.env['hr.salary.rule'].search( + [ + ('code', '=', 'ADIANTAMENTO_FERIAS_RESC') + ] + ) + sorted_rule_ids.remove(adiantamento_ferias_vencida.id) + if not payslip.dias_aviso_previo: + aviso_previo_indenizado = \ + self.env['hr.salary.rule'].search( + [ + ('code', '=', 'AVISO_PREV_IND') + ] + ) + ferias_aviso_previo = \ + self.env['hr.salary.rule'].search( + [ + ('code', '=', 'PROP_FERIAS_AVISO_PREVIO') + ] + ) + ferias_1_3_aviso_previo = \ + self.env['hr.salary.rule'].search( + [ + ('code', '=', 'PROP_1/3_FERIAS_AVISO_PREVIO') + ] + ) + if aviso_previo_indenizado: + sorted_rule_ids.remove(aviso_previo_indenizado.id) + if ferias_aviso_previo: + sorted_rule_ids.remove(ferias_aviso_previo.id) + if ferias_1_3_aviso_previo: + sorted_rule_ids.remove(ferias_1_3_aviso_previo.id) for contract in self.env['hr.contract'].browse(contract_ids.ids): employee = contract.employee_id localdict = dict( baselocaldict, employee=employee, contract=contract) for rule in obj_rule.browse(sorted_rule_ids): - key = rule.code + '-' + str(contract.id) + key = rule.code + '-' + str(payslip.id) localdict['result'] = None localdict['result_qty'] = 1.0 localdict['result_rate'] = 100 localdict['rubrica'] = rule + id_rubrica_especifica = 0 + beneficiario_id = False + + # + # Tratamos as rubricas específicas que têm beneficiários + # + if rule.id in applied_specific_rule: + lista_rubricas_especificas = \ + applied_specific_rule[rule.id] + + if len(lista_rubricas_especificas) > 0: + rubrica_especifica = lista_rubricas_especificas[0] + + if rubrica_especifica.partner_id: + beneficiario_id = \ + rubrica_especifica.partner_id.id + + del lista_rubricas_especificas[0] + + applied_specific_rule[rule.id] = \ + lista_rubricas_especificas + # check if the rule can be applied if obj_rule.satisfy_condition(rule.id, localdict) \ and rule.id not in blacklist: @@ -750,45 +2150,49 @@ def sum(self, code, from_date, to_date=None): 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 + # utilizar valor da media e multiplicar + # pela reinciden. + if medias.get(rule.code) and \ + not payslip.tipo_de_folha == 'aviso' \ + '_previo': + amount = medias.get(rule.code).media / 12 qty = medias.get(rule.code).meses - + rule.name += ' (Media) ' # check if there is already a rule computed # with that code previous_amount = \ rule.code in localdict and \ localdict[rule.code] or 0.0 + # previous_amount = 0 # set/overwrite the amount computed # for this rule in the localdict - tot_rule = amount * qty * rate / 100.0 + tot_rule = Decimal(amount or 0) * Decimal( + qty or 0) * Decimal(rate or 0) / 100.0 + tot_rule = tot_rule.quantize(Decimal('0.01')) localdict[rule.code] = tot_rule rules[rule.code] = rule - if rule.category_id.code == 'DEDUCAO': - if rule.compoe_base_INSS: - localdict['BASE_INSS'] -= tot_rule - if rule.compoe_base_IR: - localdict['BASE_IR'] -= tot_rule - if rule.compoe_base_FGTS: - localdict['BASE_FGTS'] -= tot_rule - else: - if rule.compoe_base_INSS: - localdict['BASE_INSS'] += tot_rule - if rule.compoe_base_IR: - localdict['BASE_IR'] += tot_rule - if rule.compoe_base_FGTS: - localdict['BASE_FGTS'] += tot_rule + + # Adiciona a rubrica especifica ao localdict + if id_rubrica_especifica: + localdict['RUBRICAS_ESPEC_CALCULADAS'].append( + id_rubrica_especifica) + # sum the amount for its salary category localdict = _sum_salary_rule_category( localdict, rule.category_id, tot_rule - previous_amount) + + # Definir o partner que recebera o pagamento da linha + if not beneficiario_id and \ + contract.employee_id.address_home_id: + beneficiario_id = \ + contract.employee_id.address_home_id.id + # create/overwrite the rule in the temporary results result_dict[key] = { 'salary_rule_id': rule.id, 'contract_id': contract.id, - 'name': u'Média de ' + rule.name - if medias_obj else rule.name, + 'name': rule.name, 'code': rule.code, 'category_id': rule.category_id.id, 'sequence': rule.sequence, @@ -796,8 +2200,10 @@ def sum(self, code, from_date, to_date=None): 'condition_select': rule.condition_select, 'condition_python': rule.condition_python, 'condition_range': rule.condition_range, - 'condition_range_min': rule.condition_range_min, - 'condition_range_max': rule.condition_range_max, + 'condition_range_min': + rule.condition_range_min, + 'condition_range_max': + rule.condition_range_max, 'amount_select': rule.amount_select, 'amount_fix': rule.amount_fix, 'amount_python_compute': @@ -810,17 +2216,42 @@ def sum(self, code, from_date, to_date=None): 'employee_id': contract.employee_id.id, 'quantity': qty, 'rate': rate, + 'partner_id': beneficiario_id or 1, } + + blacklist.append(rule.id) else: rules_seq = rule._model._recursive_search_of_rules( self._cr, self._uid, rule, self._context) blacklist += [id for id, seq in rules_seq] + continue + + if rule.category_id.code == 'DEDUCAO': + if rule.compoe_base_INSS: + localdict['BASE_INSS'] -= tot_rule + if rule.compoe_base_IR: + localdict['BASE_IR'] -= tot_rule + if rule.compoe_base_FGTS: + localdict['BASE_FGTS'] -= tot_rule + else: + if rule.compoe_base_INSS: + localdict['BASE_INSS'] += tot_rule + if rule.compoe_base_IR: + localdict['BASE_IR'] += tot_rule + if rule.compoe_base_FGTS: + localdict['BASE_FGTS'] += tot_rule result = [value for code, value in result_dict.items()] return result - @api.multi - def onchange_employee_id(self, date_from, date_to, contract_id): + def atualizar_worked_days_inputs(self): + """ + Atualizar os campos worked_days_line_ids e input_line_ids do holerite. + Com os campos de contrato, employee, date_from e date_to do holerite + ja setados, esse metodo exclui as variaveis base para calculo do + holerite e os instancia novamente atualizando os valore. + :return: Campos atualizados + """ worked_days_obj = self.env['hr.payslip.worked_days'] input_obj = self.env['hr.payslip.input'] @@ -837,64 +2268,59 @@ def onchange_employee_id(self, date_from, date_to, contract_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 + self.worked_days_line_ids = self.get_worked_day_lines( + self.contract_id.id, self.date_from, self.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, - } + self.input_line_ids = self.get_inputs( + self.contract_id.id, self.date_from, self.date_to ) - return res @api.multi - @api.onchange('contract_id') - def set_employee_id(self): + @api.depends('contract_id', 'mes_do_ano') + def _compute_set_employee_id(self): for record in self: 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() - - @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 - ) - def computar_mes_ano(self): + def _compute_data_mes_ano(self): for record in self: - record.data_mes_ano = MES_DO_ANO[record.mes_do_ano-1][1][:3] + \ + record.data_mes_ano = MES_DO_ANO[record.mes_do_ano - 1][1][:3] + \ '/' + str(record.ano) - def set_dates(self): + # @api.depends('mes_do_ano', 'ano', 'holidays_ferias', 'data_afastamento') + @api.multi + @api.onchange('mes_do_ano', 'ano', 'data_afastamento', 'date_from', 'date_to', 'holidays_ferias') + def _compute_set_dates(self): for record in self: + if not record.mes_do_ano: + record.mes_do_ano = datetime.now().months + record.mes_do_ano2 = datetime.now().month + if record.tipo_de_folha == 'ferias' and record.holidays_ferias: + if record.holidays_ferias.parent_id.controle_ferias_ids: + record.periodo_aquisitivo =\ + record.holidays_ferias.parent_id.controle_ferias_ids[0] + record.date_from = record.holidays_ferias.data_inicio + record.date_to = record.holidays_ferias.data_fim + record.mes_do_ano = \ + datetime.strptime(record.date_from, '%Y-%m-%d').month + record.ano = \ + datetime.strptime(record.date_from, '%Y-%m-%d').year + continue + if record.tipo_de_folha =='provisao_ferias': + continue + + mes = record.mes_do_ano + if mes > 12: + mes = 12 + ultimo_dia_do_mes = str( self.env['resource.calendar'].get_ultimo_dia_mes( - record.mes_do_ano, record.ano)) + mes, record.ano)) primeiro_dia_do_mes = str( - datetime.strptime(str(record.mes_do_ano) + '-' + + datetime.strptime(str(mes) + '-' + str(record.ano), '%m-%Y')) record.date_from = primeiro_dia_do_mes @@ -909,24 +2335,159 @@ def set_dates(self): if data_final and ultimo_dia_do_mes > data_final: record.date_to = record.contract_id.date_end + if record.data_afastamento and \ + ultimo_dia_do_mes > record.data_afastamento: + record.date_to = \ + fields.Date.from_string(record.data_afastamento) - \ + timedelta(days=1) + @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() + def _buscar_holerites_periodo_aquisitivo(self): + if not self.periodo_aquisitivo: + return False + else: + payslips = self.search( + [ + ('contract_id', '=', self.contract_id.id), + ('date_from', '>=', + self.periodo_aquisitivo.inicio_aquisitivo), + ('date_to', '<=', self.periodo_aquisitivo.fim_aquisitivo), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']), + ] + ) + return payslips - 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!') - ) + @api.multi + def _checar_holerites_aprovados(self): + return self.env['hr.payslip'].search( + [ + ('contract_id', '=', self.contract_id.id), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']) + ] + ) - self.validacao_holerites_anteriores( - data_de_inicio, data_final, self.contract_id) + @api.multi + def compute_sheet(self): + if self.tipo_de_folha == "rescisao": + + # Excluir todas as simulações pré-existentes referentes a esta + # Rescisão + # + simulacoes = self.env['hr.payslip'].search( + [ + ('contract_id', '=', self.contract_id.id), + ('is_simulacao', '=', True), + ] + ) + for simulacao in simulacoes: + simulacao.state = 'draft' + simulacao.unlink() + + if self.tipo_de_folha in [ + "decimo_terceiro", "ferias", "aviso_previo", + "provisao_ferias", "provisao_decimo_terceiro" + ]: + if not self.tipo_de_folha == 'ferias' and not self.\ + _buscar_holerites_periodo_aquisitivo(): + + hr_medias_ids, data_de_inicio, data_final = \ + self.gerar_media_dos_proventos() + + # Metodo pra validar se ja foram processadors + # todos os holerites usados no calculo das medias + # if self.tipo_de_folha in ["decimo_terceiro", "ferias"] and \ + if self.tipo_de_folha in ["decimo_terceiro"] and \ + not self.is_simulacao: + self.validacao_holerites_anteriores( + data_de_inicio, data_final, self.contract_id) + + if self.tipo_de_folha == 'ferias': + if not self.holidays_ferias and not self.is_simulacao: + raise exceptions.Warning( + _('Nenhum Pedido de Ferias encontrado!') + ) + + # Validação da quantidade de dias de férias + # sendo processada e a quantidade de saldo dísponivel + if self.holidays_ferias.number_of_days_temp > \ + self.saldo_periodo_aquisitivo: + raise exceptions.Warning( + _(u'Selecionado mais dias de ferias do que' + u'o saldo do periodo aquisitivo selecionado!') + ) + + self.atualizar_worked_days_inputs() super(HrPayslip, self).compute_sheet() - self._valor_total_folha() + self._compute_valor_total_folha() + self._compute_rescisao_ids() return True + def _compute_rescisao_ids(self): + self.rescisao_ids.unlink() + rescisao_ids = [] + + # Popula variáveis de descrição + base = 0 + uteis = 0 + ferias = 0 + abono = 0 + trabalhado = 0 + avos = 0 + peraq = '' + for variavel in self.worked_days_line_ids: + if variavel.code == 'DIAS_BASE': + base = variavel.number_of_days + if variavel.code == 'DIAS_UTEIS': + uteis = variavel.number_of_days + if variavel.code == 'FERIAS': + ferias = variavel.number_of_days + if variavel.code == 'ABONO_PECUNIARIO': + abono = variavel.number_of_days + if variavel.code == 'DIAS_TRABALHADOS': + trabalhado = variavel.number_of_days + if self.ferias_vencidas: + peraq = data.formata_data( + self.ferias_vencidas.inicio_aquisitivo) + " a " + \ + data.formata_data(self.ferias_vencidas.fim_aquisitivo) + + # Roda as linhas do holerite para popular as linhas de rescisão + for line in self.line_ids: + if line.salary_rule_id.campo_rescisao: + codigo = line.salary_rule_id.campo_rescisao.codigo + valor = line.total + encontrou = False + for item in rescisao_ids: + if item['codigo'] == codigo: + item['valor'] += valor + encontrou = True + + if not encontrou: + + # Interpreta as variáveis na descrição + if self.contract_id.wage != 0.0: + avos = line['valor_provento'] / (self.contract_id.wage / 12) + name = line.salary_rule_id.campo_rescisao.descricao + descricao = Template(name).render( + DIAS_BASE="%d" % (base), + DIAS_UTEIS="%d" % (uteis), + FERIAS="%d" % (ferias), + ABONO_PECUNIARIO="%d" % (abono), + DIAS_TRABALHADOS="%d" % (trabalhado), + PERIODO_FERIAS_VENCIDAS=peraq, + AVOS="%d" % (int(avos))) + + tipo = line.salary_rule_id.category_id.code + rescisao_ids.append({ + 'codigo': codigo, + 'name': descricao, + 'valor': valor, + 'tipo': tipo, + }) + self.rescisao_ids = rescisao_ids + + def validacao_holerites_anteriores(self, data_inicio, data_fim, contrato): """ VAlida se existe todos os holerites calculados e confirmados em @@ -938,46 +2499,145 @@ def validacao_holerites_anteriores(self, data_inicio, data_fim, contrato): folha_obj = self.env['hr.payslip'] domain = [ ('date_from', '>=', data_inicio), - ('date_to', '<=', data_fim), + ('date_from', '<=', data_fim), ('contract_id', '=', contrato.id), - ('state', '=', 'done'), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']), ] 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) - + mes = fields.Datetime.from_string(data_inicio) + relativedelta( + months=-1 + ) + mes_anterior = '' for folha in folhas_sorted: + if mes_anterior and mes_anterior == folha.mes_do_ano: + continue + mes_anterior = folha.mes_do_ano 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]) + # if folha.mes_do_ano != mes.month: + # raise exceptions.ValidationError( + # _("Faltando Holerite confirmado do mês de %s de %s") % + # (MES_DO_ANO[mes.month - 1][1], mes.year)) + # if mes.month != fields.Datetime.from_string(data_fim).month: + # mes = fields.Datetime.from_string(data_fim).month + # raise exceptions.ValidationError( + # _("Não foi encontrado o último holerite do periodo " + # "aquisitivo, \nreferente ao mês de %s de %s") % + # (MES_DO_ANO[mes - 1][1], + # fields.Datetime.from_string(data_fim).year)) @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: - 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 + if self.tipo_de_folha == 'ferias' and not self.\ + _buscar_holerites_periodo_aquisitivo(): + print("Não tem problema!") + # raise exceptions.Warning( + # "Não existem holerites normais confirmados " + # "suficientes no periodo " + # "aquisitivo para os cálculos " + # "das férias!" + # ) + else: + medias_obj = self.env['l10n_br.hr.medias'] + if self.tipo_de_folha in \ + ['ferias', 'aviso_previo', 'provisao_ferias']: + # if self.tipo_de_folha in ['provisao_ferias', 'aviso_previo']: + # periodo_aquisitivo = \ + # self.contract_id.vacation_control_ids[0] + # else: + periodo_aquisitivo = self.periodo_aquisitivo + + if self.tipo_de_folha in ['aviso_previo']: + data_de_inicio = fields.Date.from_string( + self.date_from) - relativedelta(months=12) + data_inicio_mes = fields.Date.from_string( + self.date_from).replace(day=1) - relativedelta( + months=12) + else: + data_de_inicio = fields.Date.from_string( + periodo_aquisitivo.inicio_aquisitivo) + data_inicio_mes = fields.Date.from_string( + periodo_aquisitivo.inicio_aquisitivo).replace(day=1) + + # Se trabalhou mais do que 15 dias, contabilizar o mes corrente + if (data_de_inicio - data_inicio_mes).days < 15: + data_de_inicio = data_inicio_mes + # Senão começar contabilizar medias apartir do mes seguinte + else: + data_de_inicio = data_inicio_mes + relativedelta(months=1) + if self.tipo_de_folha in ['provisao_ferias']: + data_final = self.date_to + else: + data_final = data_inicio_mes + relativedelta(months=12) + elif self.tipo_de_folha in [ + 'decimo_terceiro', 'provisao_decimo_terceiro' + ]: + if self.contract_id.date_start > str(self.ano) + '-01-01': + data_de_inicio = self.contract_id.date_start + else: + 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 BUSCAR_VALOR_MEDIA_PROVENTO(self, tipo_simulacao): + #mes_verificacao, ano_verificacao, data_inicio, data_fim = \ + # self._checar_datas_gerar_simulacoes( + # self.mes_do_ano, self.ano + # ) + mes_verificacao = self.mes_do_ano - 1 + if self.mes_do_ano == 1: + mes_verificacao = 12 + ano_verificacao = self.ano + if self.mes_do_ano == 1: + ano_verificacao = self.ano - 1 + dias_no_mes = monthrange(ano_verificacao, mes_verificacao) + data_inicio = str(ano_verificacao) + "-" + str(mes_verificacao) + "-" + "01" + data_fim = str(ano_verificacao) + "-" + str(ano_verificacao) + "-" + str(dias_no_mes[1]) + if not tipo_simulacao == "ferias": + payslip_simulacao = self.env['hr.payslip'].search( + [ + ('tipo_de_folha', '=', tipo_simulacao), + ('is_simulacao', '=', True), + ('mes_do_ano', '=', self.mes_do_ano), + ('ano', '=', ano_verificacao), + ('state', 'in', ['done', 'verify']), + ] + ) + else: + periodos_ferias_simulacao = \ + self.env['hr.vacation.control'].search( + [ + ('contract_id', '=', self.contract_id.id), + ('inicio_gozo', '=', data_inicio), + ('fim_gozo', '=', data_fim) + ] + ) + payslip_simulacao = self.env['hr.payslip'].search( + [ + ('tipo_de_folha', '=', tipo_simulacao), + ('is_simulacao', '=', True), + ('mes_do_ano', '=', mes_verificacao), + ('ano', '=', ano_verificacao), + ('periodo_aquisitivo', '=', + periodos_ferias_simulacao[0].id), + ('state', 'in', ['done', 'verify']), + ] + ) + if not payslip_simulacao: + payslip_simulacao_criada = self.gerar_simulacao( + tipo_simulacao, mes_verificacao, + ano_verificacao, data_inicio, + data_fim, ferias_vencida=ferias_vencida + ) + payslip_simulacao = payslip_simulacao_criada + media_id = payslip_simulacao.medias_proventos[-1] + return media_id.media / 12 @api.model def fields_view_get(self, view_id=None, view_type='form', diff --git a/l10n_br_hr_payroll/models/hr_payslip_line.py b/l10n_br_hr_payroll/models/hr_payslip_line.py index 3ec3f70c8..9645740c2 100644 --- a/l10n_br_hr_payroll/models/hr_payslip_line.py +++ b/l10n_br_hr_payroll/models/hr_payslip_line.py @@ -9,6 +9,8 @@ try: from pybrasil import valor + from pybrasil.valor.decimal import Decimal + except ImportError: _logger.info('Cannot import pybrasil') @@ -16,56 +18,121 @@ 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) + round_amount = fields.Float( + string=u'Valor', + store=True, + digits=(10, 2), + compute='_compute_arredondamento' + ) + + round_total = fields.Float( + string=u'Total', + store=True, + digits=(10, 2), + compute='_compute_arredondamento' + ) + + round_total_fmt = fields.Char( + string=u'Total', + default='', + compute='_compute_arredondamento' + ) + + #sequence = fields.Integer( + # string=u'Sequence', + #) + + rate = fields.Float( + digits=(18, 11), + ) + + amount = fields.Float( + digits=(18, 11), + ) + + quantity = fields.Float( + digits=(18, 11), + ) + + total = fields.Float( + digits=(18, 2), + compute='_compute_total', + store=True, + ) + + @api.depends('rate', 'amount', 'quantity') + def _compute_total(self): + for linha in self: + total = Decimal(linha.amount or 0) + total *= Decimal(linha.quantity or 0) + total *= Decimal(linha.rate or 0) + total /= 100 + total = total.quantize(Decimal('0.01')) + linha.total = total + + @api.depends('total') + def _compute_arredondamento(self): + for linha in self: + linha.round_amount = linha.amount + linha.round_total = linha.total + linha.round_total_fmt = valor.formata_valor(linha.round_total) + + @api.multi + @api.depends('category_id', 'total', 'amount') + def _compute_valor_provento(self): + for line in self: + line.quantity_fmt = valor.formata_valor(line.quantity) + if line.category_id.code in ['PROVENTO', 'FERIAS']: + line.valor_provento = line.total + line.valor_provento_fmt = \ + valor.formata_valor(line.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) + line.valor_provento = 0.00 + line.valor_provento_fmt = '' + + @api.multi + @api.depends('category_id', 'total', 'amount') + def _compute_valor_deducao(self): + for line in self: + if line.category_id.code in ['DEDUCAO', 'INSS', 'IRPF']: + line.valor_deducao = line.total + line.valor_deducao_fmt = \ + valor.formata_valor(line.valor_deducao) else: - record.valor_deducao = 0.00 - record.valor_deducao_fmt = '' + line.valor_deducao = 0.00 + line.valor_deducao_fmt = '' quantity_fmt = fields.Char( string=u'Quantidade', - compute=_valor_provento, + compute=_compute_valor_provento, default='', ) valor_provento = fields.Float( string=u'Provento', - compute=_valor_provento, + compute=_compute_valor_provento, default=0.00, ) valor_provento_fmt = fields.Char( string=u'Provento', - compute=_valor_provento, + compute=_compute_valor_provento, default='', ) + valor_deducao = fields.Float( string=u'Dedução', - compute=_valor_deducao, + compute=_compute_valor_deducao, default=0.00, ) valor_deducao_fmt = fields.Char( string=u'Dedução', - compute=_valor_deducao, + compute=_compute_valor_deducao, default='', ) + + partner_id = fields.Many2one( + string=u'Beneficiário', + comodel_name='res.partner', + ) diff --git a/l10n_br_hr_payroll/models/hr_payslip_run.py b/l10n_br_hr_payroll/models/hr_payslip_run.py index a8f0f090b..beac2ac4a 100644 --- a/l10n_br_hr_payroll/models/hr_payslip_run.py +++ b/l10n_br_hr_payroll/models/hr_payslip_run.py @@ -3,32 +3,52 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from datetime import datetime +from dateutil.relativedelta import relativedelta +from pybrasil.data import ultimo_dia_mes +from pybrasil.data import formata_data +import logging -from openerp import api, fields, models +from openerp import api, fields, models, _ +from openerp.exceptions import Warning as UserError +_logger = logging.getLogger(__name__) 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'Março'), + (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'), + (13, u'13º Salário') ] TIPO_DE_FOLHA = [ ('normal', u'Folha normal'), - ('decimo_terceiro', u'Décimo terceiro (13º)'), + ('adiantamento_13', u'13º Salário - Adiantamento'), + ('decimo_terceiro', u'13º Salário'), + ('provisao_ferias', u'Provisão de Férias'), + ('provisao_decimo_terceiro', u'Provisão de Décimo Terceiro (13º)'), ] class HrPayslipRun(models.Model): _inherit = "hr.payslip.run" + _order = "ano desc,mes_do_ano desc,tipo_de_folha asc, company_id asc" + _sql_constraints = [ + ('lote_unico', + 'unique(ano, mes_do_ano, tipo_de_folha, company_id)', + 'Este Lote de Holerite já existe!'), + ('nome', + 'unique(display_name)', + 'Este nome de Lote já existe ! ' + 'Por favor digite outro que não se repita') + ] mes_do_ano = fields.Selection( selection=MES_DO_ANO, @@ -60,102 +80,276 @@ class HrPayslipRun(models.Model): company_id = fields.Many2one( comodel_name='res.company', string='Empresa', + default=lambda self: self.env.user.company_id or '', ) - @api.multi - @api.onchange('mes_do_ano') + @api.onchange('tipo_de_folha') + def fixa_decimo_terceiro(self): + if self.tipo_de_folha == 'adiantamento_13' and self.mes_do_ano == 12: + self.tipo_de_folha = 'decimo_terceiro' + self.mes_do_ano = 13 + else: + if self.tipo_de_folha == 'decimo_terceiro': + self.mes_do_ano = 13 + elif self.mes_do_ano == 13: + self.mes_do_ano = datetime.now().month + + @api.onchange('mes_do_ano', 'ano') def buscar_datas_periodo(self): - for record in self: - record.set_dates() + if not self.mes_do_ano: + self.mes_do_ano = datetime.now().month + + if self.tipo_de_folha == 'adiantamento_13' and self.mes_do_ano == 12: + self.tipo_de_folha = 'decimo_terceiro' + self.mes_do_ano = 13 + + mes = self.mes_do_ano + if mes > 12: + mes = 12 + self.tipo_de_folha = 'decimo_terceiro' + elif self.tipo_de_folha == 'decimo_terceiro': + self.tipo_de_folha = 'normal' + + ultimo_dia_do_mes = str( + self.env['resource.calendar'].get_ultimo_dia_mes( + mes, self.ano)) - 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(mes) + '-' + + str(self.ano), '%m-%Y')) - primeiro_dia_do_mes = str( - datetime.strptime(str(record.mes_do_ano) + '-' + - str(record.ano), '%m-%Y')) + self.date_start = primeiro_dia_do_mes + self.date_end = ultimo_dia_do_mes - 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) + + # remover holerites que foram gerados pelo lote mas nao foram + # confirmados + # not_done = self.slip_ids.filtered(lambda s: not s.state == 'done') + # not_done.unlink() + + # holerites que ja foram confirmados + # done = self.slip_ids.filtered(lambda s: s.state == 'done') + + for lote in self: + # pegar todos contratos da empresa que são válidos, + dominio_contratos = [ + ('date_start', '<=', lote.date_end), + ('company_id', '=', lote.company_id.id), ] - ) - 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 + + # Se existir holerites ja concluidos nao buscar os contratos + # destes holerites + # if done: + # dominio_contratos += [ + # ('id', 'not in', done.mapped('contract_id').ids), + # ] + + # Se for lote de folha normal nao pegar as categorias inválidas + if lote.tipo_de_folha != 'normal': + dominio_contratos += [ + ('categoria', 'not in', ['721', '722']), + ] + # else: + # dominio_contratos += [ + # ('date_end', '>', lote.date_start), + # ] + + # Buscar contratos validos + contracts_id = self.env['hr.contract'].search(dominio_contratos) + + # Caso o tipo de lote for "Adiantamento de 13º", deverá ser trocado + # por "decimo_terceiro", já que os holerites são criados com esse + # tipo + if self.tipo_de_folha == 'adiantamento_13': + tipo_de_folha = 'decimo_terceiro' + else: + tipo_de_folha = self.tipo_de_folha + + # buscar payslip ja processadas dos contratos validos + dominio_payslips = [ + ('tipo_de_folha', '=', tipo_de_folha), + ('contract_id', 'in', contracts_id.ids) ] - 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 + + # se o lote for de provisao de ferias, buscar entre o periodo todo + if lote.tipo_de_folha != 'provisao_ferias': + dominio_payslips += [ + ('date_from', '>=', self.date_start), + ('date_to', '<=', self.date_end), + ] + + # Se o lote for de qualquer utro tipo, buscar apenas paylisps + # do mes setado no lote. + else: + dominio_payslips += [ + ('mes_do_ano', '=', self.mes_do_ano), + ('ano', '=', self.ano), + ] + + # Buscar payslips dos contratos validos que ja foram processadas + payslips = self.env['hr.payslip'].search(dominio_payslips) + + # grupo contendo os contratos que ja foram processados naquele período + contratos_com_holerites = [] + for payslip in payslips: + if payslip.contract_id.id not in contratos_com_holerites: + contratos_com_holerites.append(payslip.contract_id.id) + + contratos_sem_holerite = [] + for contrato in contracts_id: + # se o contrato valido nao esta nos contratos que ja possuem + # payslip naquele periodo + if contrato.id not in contratos_com_holerites: + # remover contratos finalizados + if not contrato.date_end: + contratos_sem_holerite.append(contrato.id) + else: + if contrato.date_end > lote.date_end: + contratos_sem_holerite.append(contrato.id) + + # Adiantamento eh uma rubrica + # no processamento do lote de adiantamento de 13, + # filtrar os contratos que ja foram processados naquele intervalo + # com aquela rubrica + if lote.tipo_de_folha == 'adiantamento_13': + # buscar as rubricas que foram processadas de adiantamento 13 + rubricas_ids = self.env['hr.payslip.line'].search([ + ('contract_id', 'in', contratos_sem_holerite), + ('code', '=', 'ADIANTAMENTO_13'), + ('slip_id.ano', '=', self.ano), + ('slip_id.mes_do_ano', '<=', self.mes_do_ano), + ('slip_id.state', '=', 'done'), + ('total', '>', 0), + ]) + + # filtrar os contratos dessas rubricas + contratos_ids = rubricas_ids.mapped('contract_id') + + # remover esses contratos dos contratos validos + contratos_sem_holerite = \ + list(set(contratos_sem_holerite) - set(contratos_ids.ids)) + + lote.write({ + 'contract_id': [(6, 0, contratos_sem_holerite)], + 'contract_id_readonly': [(6, 0, contratos_sem_holerite)], + }) @api.multi def gerar_holerites(self): + self.verificar_holerites_gerados() for contrato in self.contract_id: - try: - payslip_obj = self.env['hr.payslip'] - payslip = payslip_obj.create( - { + # Provisionamento de ferias + if self.tipo_de_folha == 'provisao_ferias': + + # recuperar primeiro dia do mes + inicio_mes = str(self.ano).zfill(4) + '-' + \ + str(self.mes_do_ano).zfill(2) + '-01' + + # se o contrato iniciou na metade do mes corrente + # ex.: provisionando mes marco e contrato iniciou 15/03 + if contrato.date_start > inicio_mes: + inicio_mes = contrato.date_start + + data_inicio = fields.Date.to_string(ultimo_dia_mes(inicio_mes)) + + contrato.action_button_update_controle_ferias( + data_referencia=data_inicio) + + for periodo in contrato.vacation_control_ids: + if periodo.saldo > 0 and not periodo.inicio_gozo: + try: + data_fim = fields.Date.from_string(inicio_mes) + \ + relativedelta(days=periodo.saldo) + payslip_obj = self.env['hr.payslip'] + + periodo_aquisitivo_provisao = \ + str(int(periodo.saldo)) + \ + ' dias referente a ' + \ + formata_data(periodo.inicio_aquisitivo) + \ + ' - ' + \ + formata_data(periodo.fim_aquisitivo) + + payslip = payslip_obj.create({ + 'contract_id': contrato.id, + 'periodo_aquisitivo': periodo.id, + 'mes_do_ano': self.mes_do_ano, + 'mes_do_ano2': self.mes_do_ano, + 'date_from': inicio_mes, + 'date_to': data_fim, + 'ano': self.ano, + 'employee_id': contrato.employee_id.id, + 'tipo_de_folha': self.tipo_de_folha, + 'payslip_run_id': self.id, + 'periodo_aquisitivo_provisao': + periodo_aquisitivo_provisao, + }) + # payslip._compute_set_dates() + payslip.compute_sheet() + self.env.cr.commit() + _logger.info(u"Holerite " + contrato.name + + u" processado com sucesso!") + except: + _logger.warning(u"Holerite " + contrato.name + + u" falhou durante o cálculo!") + payslip.unlink() + continue + contrato.action_button_update_controle_ferias() + else: + try: + tipo_de_folha = self.tipo_de_folha + if tipo_de_folha == 'adiantamento_13': + tipo_de_folha = 'decimo_terceiro' + payslip_obj = self.env['hr.payslip'] + + mes_do_ano = self.mes_do_ano + if mes_do_ano == 13: + mes_do_ano = 12 + + payslip = payslip_obj.create({ 'contract_id': contrato.id, 'mes_do_ano': self.mes_do_ano, + 'mes_do_ano2': 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, + 'tipo_de_folha': 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 + }) + payslip._compute_set_dates() + payslip._compute_set_employee_id() + payslip.compute_sheet() + _logger.info( + u"Holerite " + contrato.name + + u" processado com sucesso!") + self.env.cr.commit() + except: + _logger.warning( + u"Holerite " + contrato.name + + u" falhou durante o cálculo!") + payslip.unlink() + continue self.verificar_holerites_gerados() + + @api.multi + def close_payslip_run(self): + for lote in self: + for holerite in lote.slip_ids: + holerite.hr_verify_sheet() + super(HrPayslipRun, self).close_payslip_run() + + @api.multi + def unlink(self): + """ + Validacao para exclusao de lote de holerites + Nao permitir excluir o lote se ao menos um holerite nao estiver no + state draft.vali + """ + for lote in self: + if any(l != 'draft' for l in lote.slip_ids.mapped('state')): + raise UserError( + _('Erro na exclusão deste Lote !\n' + 'Há holerite(s) já confirmados!') + ) + return super(HrPayslipRun, self).unlink() diff --git a/l10n_br_hr_payroll/models/hr_ramal.py b/l10n_br_hr_payroll/models/hr_ramal.py new file mode 100644 index 000000000..68c6dd98c --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_ramal.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, models, fields +from openerp.exceptions import ValidationError + + +class HrRamal(models.Model): + _name = 'hr.ramal' + + @api.constrains('name') + def _check_description(self): + if len(self.search([('name', '=', self.name)]).ids) > 1: + raise ValidationError( + "Este ramal já está cadastrado!" + ) + + name = fields.Char( + string=u'Nº Ramal', + required=True, + ) diff --git a/l10n_br_hr_payroll/models/hr_salary_rule.py b/l10n_br_hr_payroll/models/hr_salary_rule.py index 9bed00117..64705c65d 100644 --- a/l10n_br_hr_payroll/models/hr_salary_rule.py +++ b/l10n_br_hr_payroll/models/hr_salary_rule.py @@ -4,19 +4,22 @@ import logging -from openerp import fields, models, exceptions, _ +import openerp.addons.decimal_precision as dp +from openerp import api +from openerp import fields, models, _ +from openerp.exceptions import Warning as UserError +from openerp.osv import osv 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 + from pybrasil.valor.decimal import Decimal except ImportError: _logger.info('Cannot import pybrasil') - CALCULO_FOLHA_PT_BR = { u'resultado': 'result', u'valor': 'result', @@ -33,6 +36,9 @@ class HrSalaryRule(models.Model): _inherit = 'hr.salary.rule' + sequence = fields.Integer( + string=u'Sequence', + ) compoe_base_INSS = fields.Boolean( string=u'Compõe Base INSS', ) @@ -45,44 +51,111 @@ class HrSalaryRule(models.Model): 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) + calculo_nao_padrao = fields.Boolean( + string=u'Usar Cálculo Não Padrão' + ) - try: - safe_eval(codigo_python, localdict, mode='exec', nocopy=True) - result = localdict['result'] + custom_amount_select = fields.Selection( + selection=[('percentage', 'Percentage (%)'), + ('fix', 'Fixed Amount'), + ('code', 'Python Code')], + default='code' + ) - if 'result_qty' in localdict: - result_qty = localdict['result_qty'] - else: - result_qty = 1 + custom_amount_fix = fields.Float( + digits_compute=dp.get_precision('Payroll'), + ) - if 'result_rate' in localdict: - result_rate = localdict['result_rate'] - else: - result_rate = 100 + custom_amount_percentage = fields.Float( + digits_compute=dp.get_precision('Payroll Rate'), + help='For example, enter 50.0 to apply a percentage of 50%' + ) + custom_quantity = fields.Char( + help="It is used in computation for percentage and fixed amount." + "For e.g. A rule for Meal Voucher having fixed amount of 1€ per " + "worked day can have its quantity defined in expression like " + "worked_days.WORK100.number_of_days." + ) - return result, result_qty, result_rate + custom_amount_python_compute = fields.Text('Python Code') - except: - msg = _('Wrong python code defined for salary rule %s (%s).') - raise exceptions.ValidationError(msg % (rule.name, rule.code)) + custom_amount_percentage_base = fields.Char( + required=False, + readonly=False, + help='result will be affected to a variable' + ) - def satisfy_condition(self, cr, uid, rule_id, localdict, context=None): - rule = self.browse(cr, uid, rule_id, context=context) + @api.multi + def compute_rule(self, rule_id, localdict): + rule = self.browse(rule_id) + if not rule.calculo_nao_padrao: + if rule.amount_select != 'code': + return super(HrSalaryRule, self).compute_rule(rule_id, + localdict) + + codigo_python = python_pt_BR(rule.amount_python_compute or '', + CALCULO_FOLHA_PT_BR) + else: + if rule.custom_amount_select == 'code': + codigo_python = python_pt_BR( + rule.custom_amount_python_compute or '', + CALCULO_FOLHA_PT_BR) + elif rule.custom_amount_select == 'fix': + try: + return rule.custom_amount_fix, Decimal( + safe_eval(rule.custom_quantity, localdict)), 100.0 + except: + raise osv.except_osv(_('Error!'), _( + 'Wrong quantity defined for salary rule %s (%s).') % ( + rule.name, rule.code)) + elif rule.custom_amount_select == 'percentage': + try: + return ( + Decimal( + safe_eval( + rule.custom_amount_percentage_base, localdict + ) + ), float( + safe_eval(rule.custom_quantity, localdict) + ), rule.custom_amount_percentage + ) + except: + raise osv.except_osv( + _('Error!'), _( + 'Wrong percentage base or quantity defined for ' + 'salary rule %s (%s).') % (rule.name, rule.code) + ) + + if codigo_python: + if True: # try: + for variavel in localdict: + if isinstance(localdict[variavel], float): + localdict[variavel] = Decimal(localdict[variavel] or 0) + 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 ValidationError(msg % (rule.name, rule.code)) + + @api.multi + def satisfy_condition(self, rule_id, localdict): + rule = self.browse(rule_id) if rule.condition_select != 'python': - return super(HrSalaryRule, self).satisfy_condition( - cr, uid, rule_id, localdict, context=context - ) + return super(HrSalaryRule, self).satisfy_condition(rule_id, + localdict) codigo_python = python_pt_BR(rule.condition_python or '', CALCULO_FOLHA_PT_BR) @@ -93,7 +166,7 @@ def satisfy_condition(self, cr, uid, rule_id, localdict, context=None): except: msg = _('Wrong python condition defined for salary rule %s (%s).') - raise exceptions.UserError(msg % (rule.name, rule.code)) + raise UserError(msg % (rule.name, rule.code)) tipo_media = fields.Selection( selection=[ diff --git a/l10n_br_hr_payroll/models/hr_telefonia.py b/l10n_br_hr_payroll/models/hr_telefonia.py new file mode 100644 index 000000000..146995ad6 --- /dev/null +++ b/l10n_br_hr_payroll/models/hr_telefonia.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, models, fields +from openerp.addons.l10n_br_hr_payroll.models.hr_payslip import MES_DO_ANO + + +class HrTelefonia(models.Model): + _name = 'hr.telefonia' + _rec_name = 'display_name' + + def _get_display_name(self): + for record in self: + display_name = "Registros Telefônicos - {}/{}".format( + record.mes, record.ano + ) + record.display_name = display_name + + display_name = fields.Char( + compute='_get_display_name' + ) + + arquivo_ligacoes = fields.Binary( + string='Arquivo Telefonia (PABX)', + filters='*.csv', + require=True, + copy=False, + ) + + arquivo_ramais = fields.Binary( + string='Listagem de Ramais', + filters='*.csv', + require=True, + copy=False, + ) + + mes = fields.Selection( + string=u'Mês Competência', + selection=MES_DO_ANO, + require=True, + ) + + ano = fields.Char( + string=u'Ano Competência', + required=True, + size=4, + ) + + ligacoes_id = fields.One2many( + comodel_name='hr.telefonia.line', + inverse_name='registro_telefonico_id', + ) + + @api.multi + def button_buscar_dono_ligacao(self): + + ligacoes_sem_dono_id = self.env['hr.telefonia.line'].search([('employee_id','=',False)]) + + for ligacoes_id in ligacoes_sem_dono_id: + + funcionario_id = self.env['hr.employee'].search([('ramais', '=', ligacoes_id.ramal.name)]) + + if len(funcionario_id) == 1: + ligacoes_id.employee_id = funcionario_id + + @api.multi + def button_import_ramais(self): + + ramal_obj = self.env['hr.ramal'] + + for record in self: + if record.arquivo_ramais: + + # import csv + import base64 + + arq = base64.b64decode(record.arquivo_ramais) + linhas = arq.splitlines(True) + + for linha in linhas: + + l = linha.split(',') + + email_ramal = l[1].strip() + numero_ramal = l[2].strip('\n') + + funcionario_id = self.env['hr.employee'].search([('work_email','=',email_ramal)]) + + if funcionario_id: + + ramal_id = ramal_obj.search([('name','=', numero_ramal)]) + + if not numero_ramal in funcionario_id.ramais.mapped('name'): + + if not ramal_id: + ramal_id = ramal_obj.create({'name': numero_ramal}) + + funcionario_id.ramais = [(4, ramal_id.id)] + + else: + print ("Nao encontrado funcionario: " + email_ramal) + + + @api.multi + def button_importar_csv(self): + + ramal_obj = self.env['hr.ramal'] + + for record in self: + if record.arquivo_ligacoes: + + # import csv + import base64 + + arq = base64.b64decode(record.arquivo_ligacoes) + linhas = arq.splitlines(True) + + for linha in linhas: + + l = linha.split(';') + + if len(l) > 7 and len(l[0]) == 4: + name_ramal = l[0] + ramal_id = ramal_obj.search([('name', '=', name_ramal)]) + + if not ramal_id: + ramal_id = ramal_obj.create({'name': name_ramal}) + + funcionario_id = self.env['hr.employee'].search([('ramais', '=', name_ramal)]) + + data = l[1] + numero_discado = l[2] + concessionaria = l[3] + localidade = l[4] + inicio = l[5] + duracao = l[6] + valor = float(l[7].replace(',','.')) + + vals = { + 'ramal': ramal_id.id, + 'data': data, + 'numero_discado': numero_discado, + 'concessionaria': concessionaria, + 'localidade': localidade, + 'inicio': inicio, + 'duracao': duracao, + 'valor': valor, + 'registro_telefonico_id': record.id, + 'employee_id': funcionario_id.id if len(funcionario_id) == 1 else False + } + + self.env['hr.telefonia.line'].create(vals) + + +class HrTelefoniaLine(models.Model): + _name = 'hr.telefonia.line' + _rec_name = 'display_name' + + @api.multi + def _get_telefonia_line_name(self): + for record in self: + title = '{} - {}{}'.format( + record.ramal.name, + record.employee_id.name + ' - ' if record.employee_id else '', + record.data + ) + record.display_name = title + + display_name = fields.Char( + compute='_get_telefonia_line_name' + ) + + name = fields.Char( + compute='compute_name', + string=u'Descrição', + store=True, + ) + + ramal = fields.Many2one( + string='Ramal', + comodel_name='hr.ramal', + required=True, + readonly=True, + ) + + employee_id = fields.Many2one( + string='Empregado', + comodel_name='hr.employee', + ) + + valor = fields.Float( + string='Valor', + readonly=True, + ) + + data = fields.Date( + string='Data', + required=True, + readonly=True, + ) + + tipo = fields.Selection( + string='Tipo', + selection=[ + ('', ''), + ('particular', 'Particular'), + ('empresa', 'Empresa') + ], + default='empresa', + # states={'validate': [('readonly', True)]}, + ) + + state = fields.Selection( + string=u'Situação', + selection=[ + ('open', 'Em aberto'), + ('validate', 'Atestado'), + ('paid', 'Debitado'), + ], + default='open', + ) + + payslip_id = fields.Many2one( + comodel_name='hr.payslip', + string='Holerite', + ) + + registro_telefonico_id = fields.Many2one( + string='Registro Telefonico', + comodel_name='hr.telefonia', + required=True, + # states={'validate': [('readonly', True)]}, + ) + + concessionaria = fields.Char( + string='Concessionária', + # states={'validate': [('readonly', True)]}, + ) + + localidade = fields.Char( + string='Localidade', + readonly=True, + ) + + hora_inicio = fields.Datetime( + string='Hora de Início', + # states={'validate': [('readonly', True)]}, + ) + + inicio = fields.Char( + string='Inicio', + required=True, + readonly=True, + ) + + duracao = fields.Char( + string='Duração da ligação', + required=True, + readonly=True, + ) + + numero_discado = fields.Char( + string='Numero Discado', + readonly=True, + ) + + @api.multi + @api.depends('ramal', 'employee_id') + def compute_name(self): + for record in self: + if record.employee_id and record.ramal: + record.name = 'Ligação Ramal: {} '.format(record.ramal.name) + + @api.multi + def set_validate_ligacoes(self): + """ + Rotina para atestar ligacoes como particulares ou nao + depois dessa rotina a ligacao sera bloqueada para edicoes + :return: + """ + for record in self: + # Atesta as ligacoes + record.state = 'validate' + + @api.multi + def set_particular(self): + """ + Setar as ligações para particular + :return: + """ + for record in self: + # record.particular = True + record.tipo = 'particular' + + + @api.multi + def set_empresa(self): + """ + Setar as ligações como ligações da empresa + :return: + """ + for record in self: + # record.particular = False + record.tipo = 'empresa' 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 f5a5327f1..bd201c190 100644 --- a/l10n_br_hr_payroll/models/l10n_br_hr_contract.py +++ b/l10n_br_hr_payroll/models/l10n_br_hr_contract.py @@ -2,11 +2,23 @@ # Copyright 2017 KMEE # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, fields, models +from openerp import api, fields, models, _ from openerp.exceptions import Warning as UserError +from openerp.exceptions import ValidationError +from pybrasil.data import formata_data -STATES = [('draft', 'Rascunho'), - ('applied', 'Aplicada')] +STATES = [ + ('draft', 'Rascunho'), + ('applied', 'Aplicada') +] + +CHANGE_TYPE = [ + ('remuneracao', u'Remuneração'), + ('jornada', u'Jornada'), + ('cargo-atividade', u'Cargo/Atividade'), + ('filiacao-sindical', u'Filiação Sindical'), + ('lotacao-local', u'Lotação/Local de trabalho'), +] class HrContractChangeReason(models.Model): @@ -20,181 +32,315 @@ class HrContractChangeReason(models.Model): class HrContractChange(models.Model): _name = 'l10n_br_hr.contract.change' - _description = u"Alteração contratual" + _rec_name = 'nome_alteracao' + _description = u'Alteração contratual' _inherit = 'hr.contract' + _order = 'change_date desc, contract_id' - def _get_default_type(self): - change_type = self._context.get('change_type', False) - if change_type: - return change_type - else: - raise UserError(u'Sem tipo de alteração definido!') + nome_alteracao = fields.Char( + string='Nome de exibição', + ) - @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 + name = fields.Char( + string='Nome do contrato', + ) contract_id = fields.Many2one( - 'hr.contract', - string="Contrato" + comodel_name='hr.contract', + string='Contrato', + ondelete='cascade', ) + change_type = fields.Selection( - selection=[ - ('remuneracao', u'Remuneração'), - ('jornada', u'Jornada'), - ('cargo-atividade', u'Cargo/Atividade'), - ('filiacao-sindical', u'Filiação Sindical'), - ('lotacao-local', u'Lotação/Local de trabalho'), - ], + selection=CHANGE_TYPE, 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", required=True, ) - change_date = fields.Date(u'Data da alteração') + + change_date = fields.Date( + string=u'Data da alteração', + required=True, + ) + change_history_ids = fields.Many2many( comodel_name='l10n_br_hr.contract.change', inverse_name='contract_id', string=u"Histórico", - compute=_get_change_history, + compute='_get_change_history', ) - name = fields.Char(string='Contract Reference', required=False) - employee_id = fields.Many2one(string='Employee', - comodel_name='hr.employee', - required=False) - 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') + + employee_id = fields.Many2one( + string='Employee', + comodel_name='hr.employee', + required=False + ) + + 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', + default=lambda self: self.env.user, + ) + + departamento_id = fields.Many2one( + comodel_name='hr.department', + string='Departamento', ) + @api.depends('contract_id', 'change_history_ids') + def _get_change_history(self): + change_type = self.change_type + full_history = self.search([ + ('contract_id', '=', self.contract_id.id), + ('change_type', '=', change_type), + ('state', '=', 'applied') + ]) + self.change_history_ids = full_history + @api.onchange('contract_id') def _onchange_contract_id(self): contract = self.contract_id - self.change_date = fields.datetime.now() + self.name = contract.name + self.change_date = fields.Date.today() self.notes = contract.notes + self.wage = contract.wage 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 + elif self.change_type == 'lotacao-local': + self.departamento_id = contract.department_id + # self.lotacao_cliente_fornecedor = contract.lotacao_cliente_fornecedor + # self.month_base_data = contract.month_base_data @api.multi - def apply_contract_changes(self): + def verificar_primeira_alteracao(self): + """ + Se for primeira alteração do contrato criar uma alteração para + registrar dados iniciais do contrato + :return: + """ 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': + # Buscar se existe alterações anteriores + domain = [ + ('id', '!=', change.id), + ('change_type', '=', change.change_type), + ('contract_id', '=', contract.id), + ('state', '=', 'applied'), + ] + alteracoes_anteriores = self.search_count(domain) + + # se nao existir alterações anteriores, criar a primeira alteração + # com registro dos dados iniciais do contrato. + if not alteracoes_anteriores: + reason = \ + self.env.ref('l10n_br_hr_payroll.' + 'l10n_br_hr_contract_change_valores_iniciais') + vals = { + 'contract_id': contract.id, + 'change_date': contract.date_start, + 'change_reason_id': reason.id, + 'wage': contract.wage, + 'struct_id': change.struct_id.id, + 'state': 'applied', + 'name': change.nome_alteracao, + } + + if change.change_type == 'jornada': + vals.update(working_hours=contract.working_hours.id) + elif change.change_type == 'cargo-atividade': + vals.update( + 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, + ) + elif change.change_type == 'filiacao-sindical': + vals.update( + 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 + month_base_date=contract.month_base_date + ) + elif change.change_type == 'lotacao-local': + vals.update(departamento_id=contract.department_id.id) + + # Criar o registro inicial + self.create(vals) + + @api.multi + def apply_contract_changes(self): + """ + Aplica a alteração no contrato, e se for a primeira alteração daquele + tipo cria um registro de alteração inicial. + :return: + """ + for alteracao in self: + # Verificar se ja existe alguma alteracao do mesmo tipo + alteracao.verificar_primeira_alteracao() + # alias para o contrato corrente + contract = alteracao.contract_id + if alteracao.change_type == 'remuneracao': + contract.wage = alteracao.wage + contract.salary_unit = alteracao.salary_unit + contract.struct_id = alteracao.struct_id + elif alteracao.change_type == 'jornada': + contract.working_hours = alteracao.working_hours + contract.schedule_pay = alteracao.schedule_pay + contract.monthly_hours = alteracao.monthly_hours + contract.weekly_hours = alteracao.weekly_hours + elif alteracao.change_type == 'cargo-atividade': + contract.job_id = alteracao.job_id + contract.type_id = alteracao.type_id + contract.admission_type_id = alteracao.admission_type_id + contract.labor_bond_type_id = alteracao.labor_bond_type_id + contract.labor_regime_id = alteracao.labor_regime_id + elif self.change_type == 'filiacao-sindical': + contract.union = alteracao.union + contract.union_cnpj = alteracao.union_cnpj + contract.union_entity_code = alteracao.union_entity_code contract.discount_union_contribution = \ - self.discount_union_contribution - contract.month_base_date = self.month_base_date + alteracao.discount_union_contribution + contract.month_base_date = alteracao.month_base_date + elif self.change_type == 'lotacao-local': + # Setar variavel de contexto para indicar que a alteração + # partiu do menu de alterações contratuais. + contract.with_context( + alteracaocontratual=True).department_id = \ + alteracao.departamento_id + # contract.lotacao_cliente_fornecedor = \ + # alteracao.lotacao_cliente_fornecedor + # contract.month_base_data = alteracao.month_base_data self.state = 'applied' + @api.multi + def action_back_to_draft(self): + """ + Permitir Suporte Voltar alterações para Rascunho, + desfazendo a alteração no contrato + :return: + """ + for alteracao in self: + # verificar se selecionou a ultima alteracao, pois nao sera + # possível desfazer alterações que nao forem a ultima + ultima_alteracao = self.search([ + ('change_type', '=', alteracao.change_type), + ('contract_id', '=', alteracao.contract_id.id), + ('state', '=', 'applied'), + ], order='change_date DESC', limit=1) + + if not ultima_alteracao.id == self.id: + raise UserError( + u'Só é possível desfazer a última alteração contratual.' + u'\nA última alteração é do dia %s' % + formata_data(ultima_alteracao.change_date)) + + # Aplicar a penultima alteração contratual + penultima_alteracao = self.search([ + ('id', '!=', alteracao.id), + ('change_type', '=', alteracao.change_type), + ('contract_id', '=', alteracao.contract_id.id), + ('state', '=', 'applied'), + ], order='change_date DESC', limit=1) + + # Se nao tiver uma alteração anterior, isso é, + # se for a primeira alteração contratual não é possível desfazer + if not penultima_alteracao: + raise UserError( + u'Não é possível desfazer a primeira alteração contratual.' + u'\nA primeira alteração contem as informações iniciais ' + u'do contrato.') + + penultima_alteracao.apply_contract_changes() + self.state = 'draft' + @api.model def create(self, vals): - vals.update({'user_id': self.env.user.id}) + # Criação de um nome para o _rec_name + # PS.: Nao consegui criar um campo computed. =( + change_type = vals.get('change_type') or \ + self.env.context.get('default_change_type') + if change_type and vals.get('contract_id') and \ + vals.get('change_date'): + contrato_id = \ + self.env['hr.contract'].browse(vals.get('contract_id')) + nome_contrato = \ + 'Contrato ' + contrato_id.nome_contrato[:6] + ' ' + \ + contrato_id.employee_id.name + nome_alteracao = \ + u'Alteração de ' + \ + dict(CHANGE_TYPE).get(change_type) + \ + ' ' + u'em ' + \ + formata_data(vals.get('change_date')) + ' ' + \ + nome_contrato + vals.update({ + 'nome_alteracao' : nome_alteracao, + 'name': nome_alteracao, + 'change_type': change_type, + }) return super(HrContractChange, self).create(vals) + + @api.multi + def unlink(self): + for alteracao in self: + if alteracao.state in ['applied']: + raise ValidationError(_('You can\'t delete applied changes!')) + return super(HrContractChange, self).unlink() + + @api.constrains('change_date') + def _check_date(self): + ''' + Não permitir incluir uma alteração de remuneração + com data anterior a última + :return: + ''' + ultima_alteracao = self.search([ + ('id', '!=', self.id), + ('change_type', '=', self.change_type), + ('contract_id', '=', self.contract_id.id), + ('state', '=', 'applied'), + ], order='change_date DESC', limit=1) + + if ultima_alteracao and \ + self.change_date <= ultima_alteracao.change_date: + raise UserError( + u'Não é possível criar uma alteração contratual com ' + u'data inferior à última alteração.' + u'\n Data da última alteração contratual: %s' % + formata_data(ultima_alteracao.change_date)) diff --git a/l10n_br_hr_payroll/models/l10n_br_hr_income_tax.py b/l10n_br_hr_payroll/models/l10n_br_hr_income_tax.py index 8448df979..f73b7a6b2 100644 --- a/l10n_br_hr_payroll/models/l10n_br_hr_income_tax.py +++ b/l10n_br_hr_payroll/models/l10n_br_hr_income_tax.py @@ -2,8 +2,18 @@ # (c) 2014 Kmee - Luis Felipe Mileo # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import logging + from openerp import api, fields, models, _ -from openerp.exceptions import Warning +from openerp.exceptions import Warning as UserError + +_logger = logging.getLogger(__name__) + +try: + from pybrasil.valor.decimal import Decimal + +except ImportError: + _logger.info('Cannot import pybrasil') class L10nBrHrIncomeTax(models.Model): @@ -32,29 +42,21 @@ def name_get(self): @api.multi def _compute_irrf(self, BASE_IRRF, employee_id, inss, date_from): ano = fields.Datetime.from_string(date_from).year - employee = self.env['hr.employee'].browse(employee_id) tabela_irrf_obj = self.env['l10n_br.hr.income.tax'] tabela_vigente = tabela_irrf_obj.search( [('year', '=', ano)], order='rate DESC' ) - deducao_dependente_obj = self.env[ - 'l10n_br.hr.income.tax.deductable.amount.family' - ] - deducao_dependente_value = deducao_dependente_obj.search( - [('year', '=', ano)] - ) - dependent_values = 0 - if employee.have_dependent: - dependent_values = deducao_dependente_value.amount * len( - employee.dependent_ids - ) if tabela_vigente: for faixa in tabela_vigente: if BASE_IRRF > faixa.max_wage: - return (BASE_IRRF - inss - dependent_values) * \ - (faixa.rate/100.00) - faixa.deductable + irrf = \ + Decimal(BASE_IRRF or 0) * ( + Decimal(faixa.rate) / 100.00 + ) - Decimal(faixa.deductable) + irrf = irrf.quantize(Decimal('0.01')) + return irrf else: - raise Warning( + raise UserError( _('Tabela de IRRF do ano Vigente Não encontrada!') ) diff --git a/l10n_br_hr_payroll/models/l10n_br_hr_medias.py b/l10n_br_hr_payroll/models/l10n_br_hr_medias.py index 6aecac966..2c3586fe9 100644 --- a/l10n_br_hr_payroll/models/l10n_br_hr_medias.py +++ b/l10n_br_hr_payroll/models/l10n_br_hr_medias.py @@ -74,14 +74,14 @@ class L10nBrHrMedias(models.Model): ) soma = fields.Float( string=u'Total dos Meses', - compute='calcular_soma' + compute='_compute_calcular_soma' ) meses = fields.Float( string=u'Meses do periodo', ) media = fields.Float( string=u'Média', - compute='calcular_media', + compute='_compute_calcular_media', ) media_texto = fields.Char( string=u'Média' @@ -92,7 +92,7 @@ class L10nBrHrMedias(models.Model): default=False, ) - def calcular_soma(self): + def _compute_calcular_soma(self): for linha in self: if not linha.linha_de_titulo: linha.soma = \ @@ -103,13 +103,26 @@ def calcular_soma(self): float(linha.mes_9) + float(linha.mes_10) + \ float(linha.mes_11) + float(linha.mes_12) - def calcular_media(self): + def _compute_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 + linha.media = linha.soma / linha.meses + + @api.multi + def _completar_colunas_vazias_linha_media(self, vals): + """ + Função responsável por completar com 0's os meses que não possuem + o provento no holerite. + :param vals: + :return: vals + """ + for i in range(1, 13): + if not vals.get('mes_' + str(i)): + vals.update({'mes_' + str(i): '0.00'}) + return vals @api.multi def gerar_media_dos_proventos(self, data_inicio, data_fim, holerite_id): @@ -128,29 +141,41 @@ def gerar_media_dos_proventos(self, data_inicio, data_fim, holerite_id): ('date_from', '>=', data_inicio), ('date_to', '<=', data_fim), ('contract_id', '=', holerite_id.contract_id.id), - ('state', '=', 'done'), + ('tipo_de_folha', '=', 'normal'), + ('state', 'in', ['done', 'verify']), ] folhas_periodo = folha_obj.search(domain) folhas_periodo = folhas_periodo.sorted(key=lambda r: r.date_from) + medias = {} + mes_anterior = '' for folha in folhas_periodo: + + if mes_anterior and mes_anterior == folha.mes_do_ano: + continue + mes_anterior = folha.mes_do_ano for linha in folha.line_ids: - if linha.salary_rule_id.category_id.code == "PROVENTO" \ - and linha.salary_rule_id.tipo_media: + if linha.salary_rule_id.category_id.code in \ + ["PROVENTO", "FERIAS"] 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], + 'mes': MES_DO_ANO[folha.mes_do_ano - 1][1], + 'ano': folha.ano, 'valor': linha.total, 'rubrica_id': linha.salary_rule_id.id, + 'codigo': linha.salary_rule_id.code, }] }) else: medias[linha.salary_rule_id.id].append({ - 'mes': MES_DO_ANO[folha.mes_do_ano-1][1], + 'mes': MES_DO_ANO[folha.mes_do_ano - 1][1], + 'ano': folha.ano, 'valor': linha.total, 'rubrica_id': linha.salary_rule_id.id, + 'codigo': linha.salary_rule_id.code, }) linha_obj = self.env['l10n_br.hr.medias'] @@ -159,13 +184,25 @@ def gerar_media_dos_proventos(self, data_inicio, data_fim, holerite_id): meses_titulos = [] # definindo titulo da visao tree + id_rubrica_salario = 0 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 medias[rubrica][0]['codigo'] == 'SALARIO': + id_rubrica_salario = rubrica + break + + mes_cont = 1 + #titulo.update({'meses': len(medias[id_rubrica_salario])}) + titulo.update({'meses': len(medias)}) + titulo.update({'holerite_id': holerite_id.id}) + titulo.update({'linha_de_titulo': True}) + if medias != {}: + for mes in medias[id_rubrica_salario]: + titulo.update( + { + 'mes_' + str(mes_cont): + str(mes['mes'])[:3] + '/' + str(mes['ano']), + } + ) if str(mes['mes']) in meses_titulos: meses_titulos.remove(str(mes['mes'])) meses_titulos.append(str(mes['mes'])) @@ -173,25 +210,45 @@ def gerar_media_dos_proventos(self, data_inicio, data_fim, holerite_id): linha_obj.create(titulo) # definindo a linha + l10n_br_medias_dict = {} 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)) + if rubrica not in l10n_br_medias_dict: + 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 + else: + vals = l10n_br_medias_dict[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['mes_' + str(mes_cont)] = str( + float(vals['mes_' + str(mes_cont)]) + + mes['valor'] + ) + break + mes_cont += 1 + + vals = self._completar_colunas_vazias_linha_media(vals) + l10n_br_medias_dict[rubrica] = vals + + for key in l10n_br_medias_dict: + hr_medias_ids.append(linha_obj.create(l10n_br_medias_dict[key])) return hr_medias_ids diff --git a/l10n_br_hr_payroll/models/l10n_br_hr_social_security_tax.py b/l10n_br_hr_payroll/models/l10n_br_hr_social_security_tax.py index 2c2076632..1da0ad46c 100644 --- a/l10n_br_hr_payroll/models/l10n_br_hr_social_security_tax.py +++ b/l10n_br_hr_payroll/models/l10n_br_hr_social_security_tax.py @@ -2,12 +2,18 @@ # (c) 2014 Kmee - Luis Felipe Mileo # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -# import logging +import logging from openerp import exceptions, _ from openerp import api, fields, models -# _logger = logging.getLogger(__name__) +_logger = logging.getLogger(__name__) + +try: + from pybrasil.valor.decimal import Decimal, ROUND_DOWN + +except ImportError: + _logger.info('Cannot import pybrasil') class L10nBrHrSocialTax(models.Model): @@ -43,9 +49,15 @@ def _compute_inss(self, BASE_INSS, date_from): if tabela_vigente: for faixa in tabela_vigente: if BASE_INSS < faixa.max_wage: - return BASE_INSS * faixa.rate / 100.00 - return \ - tabela_vigente[-1].max_wage * tabela_vigente[-1].rate / 100.00 + inss = Decimal(BASE_INSS) + inss *= Decimal(faixa.rate) / 100 + inss = inss.quantize(Decimal('0.01'), ROUND_DOWN) + return inss + + inss = Decimal(tabela_vigente[-1].max_wage) + inss *= Decimal(tabela_vigente[-1].rate) / 100 + inss = inss.quantize(Decimal('0.01'), ROUND_DOWN) + return inss else: raise exceptions.Warning( diff --git a/l10n_br_hr_payroll/models/res_config.py b/l10n_br_hr_payroll/models/res_config.py new file mode 100644 index 000000000..14ed0a5b3 --- /dev/null +++ b/l10n_br_hr_payroll/models/res_config.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE - Hendrix Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, models, fields + + +class HrConfigSettings(models.TransientModel): + _inherit = 'hr.config.settings' + + ferias_proporcionais = fields.Boolean( + string=u'Férias Proporcionais no Holerite', + help=u'Calcular as férias proporcionalmente no holerite.' + ) + + @api.model + def get_default_ferias_proporcionais(self, fields): + return { + 'ferias_proporcionais': + self.env["ir.config_parameter"].get_param( + "l10n_br_hr_payroll_ferias_proporcionais") + } + + @api.multi + def set_ferias_proporcionais(self): + self.ensure_one() + self.env['ir.config_parameter'].set_param( + "l10n_br_hr_payroll_ferias_proporcionais", + self.ferias_proporcionais or '' + ) diff --git a/l10n_br_hr_payroll/models/resource_calendar.py b/l10n_br_hr_payroll/models/resource_calendar.py index 4bef8f563..6fc4d5e77 100644 --- a/l10n_br_hr_payroll/models/resource_calendar.py +++ b/l10n_br_hr_payroll/models/resource_calendar.py @@ -63,7 +63,6 @@ def get_quantity_discount_DSR(self, leaves, holidays, data_from, data_to): semanas_sem_DSR.append(fields.Datetime.from_string( leave.date_to).isocalendar()[1]) data_inicio += timedelta(days=1) - quantity_DSR = len(set(semanas_sem_DSR)) # percorre os feriados de determinado período e incrementa a quantidade @@ -87,11 +86,10 @@ def get_quantity_discount_DSR(self, leaves, holidays, data_from, data_to): semanas_sem_DSR): quantity_DSR += 1 inicio_feriado += timedelta(days=1) - return quantity_DSR @api.multi - def get_quantidade_dias_ferias(self, employee_id, date_from, date_to): + def get_quantidade_dias_ferias(self, contract_id, date_from, date_to): """Calcular a quantidade de dias que o funcionario ficou de férias :param str: data_to - Data Inicial do intervalo str: data_from - Data final do intervalo @@ -101,20 +99,40 @@ def get_quantidade_dias_ferias(self, employee_id, date_from, date_to): quantidade_dias_ferias = 0 quantidade_dias_abono = 0 holiday_status_id = \ - self.env.ref('l10n_br_hr_vacation.holiday_status_vacation') + self.env.ref('l10n_br_hr_holiday.holiday_status_vacation') domain = [ ('state', '=', 'validate'), - ('employee_id', '=', employee_id), + ('contrato_id', '=', contract_id.id), ('type', '=', 'remove'), - ('date_from', '>=', date_from), - ('date_to', '<=', date_to), ('holiday_status_id', '=', holiday_status_id.id), ] ferias_holidays_ids = self.env['hr.holidays'].search(domain) - - for holiday in ferias_holidays_ids: - quantidade_dias_ferias += holiday.vacations_days - quantidade_dias_abono += holiday.sold_vacations_days + # filtrar apenas ferias que esta dentro do periodo a ser validado + ferias_atuais = ferias_holidays_ids.filtered( + lambda holiday: + (date_from <= holiday.data_inicio <= date_to) or + (date_from <= holiday.data_fim <= date_to) + ) + + for holiday in ferias_atuais: + data_inicio_holiday = fields.Date.from_string(holiday.data_inicio) + data_final_holiday = fields.Date.from_string(holiday.data_fim) + while data_inicio_holiday <= data_final_holiday: + if date_from <= str(data_inicio_holiday) <= date_to: + quantidade_dias_ferias += 1 + data_inicio_holiday += timedelta(days=1) + + # Se o funcionario for vender ferias + if holiday.sold_vacations_days: + # data inicial do abono pecuniario sera o + # dia anterior ao primeiro dia de férias + data_inicial_abono = fields.Date.from_string(holiday.date_from) + # Se a data inicial do abono estiver dentro do período que + # esta sendo validado (quando em divisão de férias em 2+partes) + # o abono será contabilizado. Senao o abono será contabilizado + # na proxima parte das ferias + if date_from == str(data_inicial_abono): + quantidade_dias_abono = holiday.sold_vacations_days return quantidade_dias_ferias, quantidade_dias_abono @@ -125,6 +143,7 @@ def get_ultimo_dia_mes(self, mes, ano): :return: int : ultimo dia do mes relativedelta(months=+1, days=-1) """ + data_mes = datetime.strptime(str(mes) + '-' + str(ano), '%m-%Y') data_final = \ data_mes + relativedelta(months=1) - relativedelta(days=1) diff --git a/l10n_br_hr_payroll/security/hr_telefonia_line_security_rule.xml b/l10n_br_hr_payroll/security/hr_telefonia_line_security_rule.xml new file mode 100644 index 000000000..9a3491036 --- /dev/null +++ b/l10n_br_hr_payroll/security/hr_telefonia_line_security_rule.xml @@ -0,0 +1,32 @@ + + + + + + + + + Gerenciar Ligações + + + + + + + User Ligações + + [('employee_id.user_id.id', '=', user.id)] + + + + + + Gerente de Ligacoes + + [(1,'=',1)] + + + + + diff --git a/l10n_br_hr_payroll/security/ir.model.access.csv b/l10n_br_hr_payroll/security/ir.model.access.csv index 7b71254f2..e83094b6e 100644 --- a/l10n_br_hr_payroll/security/ir.model.access.csv +++ b/l10n_br_hr_payroll/security/ir.model.access.csv @@ -7,7 +7,25 @@ "access_l10n_br_hr_social_security_tax","access_l10n_br_hr_social_security_tax","model_l10n_br_hr_social_security_tax","",1,0,0,0 "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 +"create_l10n_br_hr_contract_change","create_l10n_br_hr_contract_change","model_l10n_br_hr_contract_change","base.group_hr_manager",1,1,1,1 "access_hr_contract_salary_rule","access_hr_contract_salary_rule","model_hr_contract_salary_rule","",1,0,0,0 +"create_hr_contract_salary_rule","create_hr_contract_salary_rule","model_hr_contract_salary_rule","base.group_hr_manager",1,1,1,1 "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 +"create_hr_exame_medico","create_hr_exame_medico","model_hr_exame_medico","base.group_hr_manager",1,1,1,1 +"access_hr_curso","access_hr_curso","model_hr_curso","",1,0,0,0 +"create_hr_curso","create_hr_curso","model_hr_curso","base.group_hr_manager",1,1,1,1 +"access_hr_contract_officer","access_hr_contract_officer","model_hr_contract","base.group_hr_user",1,0,0,0 +"access_hr_payslip_worked_days_officer","access_hr_payslip_worked_days_officer","hr_payroll.model_hr_payslip_worked_days","base.group_hr_user",1,1,1,1 +"access_hr_payslip_input_officer","access_hr_payslip_input_officer","hr_payroll.model_hr_payslip_input","base.group_hr_user",1,1,1,1 +"access_hr_rule_input_officer","access_hr_rule_input_officer","hr_payroll.model_hr_rule_input","base.group_hr_user",1,1,1,1 +"access_hr_rule_rat_fap_officer","access_hr_rule_rat_fap_officer","model_l10n_br_hr_rat_fap","base.group_hr_user",1,1,1,1 +"access_hr_campos_rescisao","access_hr_campos_rescisao","model_hr_campos_rescisao","base.group_hr_user",1,1,1,1 +"access_hr_ramal_user","access_hr_ramal_user","model_hr_ramal","base.group_user",1,0,0,0 +"access_hr_ramal_manager","access_hr_ramal_manager","model_hr_ramal","group_manager_ligacoes",1,1,1,1 +"access_hr_telefonia_user","access_hr_telefonia_user","model_hr_telefonia","base.group_user",1,0,0,0 +"access_hr_telefonia_manager","access_hr_telefonia_manager","model_hr_telefonia","group_manager_ligacoes",1,1,1,1 +"access_hr_telefonia_line_user","access_hr_telefonia_line_user","model_hr_telefonia_line","base.group_user",1,1,0,0 +"access_hr_telefonia_line_manager","access_hr_telefonia_line_manager","model_hr_telefonia_line","group_manager_ligacoes",1,1,1,1 +"access_ir_property_manager_ligacao","access_ir_property_manager_ligacao","base.model_ir_property","group_manager_ligacoes",1,1,1,1 +"access_res_partner_manager_ligacao","access_res_partner_manager_ligacao","base.model_res_partner","group_manager_ligacoes",1,1,1,0 \ No newline at end of file diff --git a/l10n_br_hr_payroll/security/l10n_br_hr_payslip_security_rule.xml b/l10n_br_hr_payroll/security/l10n_br_hr_payslip_security_rule.xml new file mode 100644 index 000000000..00c593cc9 --- /dev/null +++ b/l10n_br_hr_payroll/security/l10n_br_hr_payslip_security_rule.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + Employee Payslip + + ['|', ('employee_id.user_id', '=',user.id), ('employee_id.department_id.manager_id.user_id', '=',user.id)] + + + + + + Official Payslip test + + [(1,'=',1)] + + + + + diff --git a/l10n_br_hr_payroll/security/l10n_br_hr_rat_fap_security_rule.xml b/l10n_br_hr_payroll/security/l10n_br_hr_rat_fap_security_rule.xml new file mode 100644 index 000000000..a5ee21c46 --- /dev/null +++ b/l10n_br_hr_payroll/security/l10n_br_hr_rat_fap_security_rule.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/l10n_br_hr_payroll/static/description/icon.png b/l10n_br_hr_payroll/static/description/icon.png index 3a0328b51..d00cbc2de 100644 Binary files a/l10n_br_hr_payroll/static/description/icon.png and b/l10n_br_hr_payroll/static/description/icon.png differ diff --git a/l10n_br_hr_payroll/static/img/hr_employee_payroll_tony-image.jpg b/l10n_br_hr_payroll/static/img/hr_employee_payroll_tony-image.jpg new file mode 100644 index 000000000..14009c9d2 Binary files /dev/null and b/l10n_br_hr_payroll/static/img/hr_employee_payroll_tony-image.jpg differ diff --git a/l10n_br_hr_payroll/tests/__init__.py b/l10n_br_hr_payroll/tests/__init__.py old mode 100755 new mode 100644 index 6f2b86507..aa2ad87c4 --- a/l10n_br_hr_payroll/tests/__init__.py +++ b/l10n_br_hr_payroll/tests/__init__.py @@ -4,3 +4,4 @@ from . import test_hr_payslip from . import test_hr_payslip_rubricas +from . import test_l10n_br_hr_contract diff --git a/l10n_br_hr_payroll/tests/test_hr_payslip.py b/l10n_br_hr_payroll/tests/test_hr_payslip.py index acca4aa6c..3adc88169 100644 --- a/l10n_br_hr_payroll/tests/test_hr_payslip.py +++ b/l10n_br_hr_payroll/tests/test_hr_payslip.py @@ -79,7 +79,7 @@ def test_01_get_quantity_discount_DSR(self): date_to = '2017-01-31 23:59:59' self.criar_falta_nao_remunerada() - leaves = self.env['resource.calendar'].get_ocurrences( + leaves = self.env['hr.holidays'].get_ocurrences( self.employee_hr_user_id.id, date_from, date_to) quantity_DSR_discount = self.resource_calendar.\ @@ -109,7 +109,7 @@ def test_02_get_quantity_discount_DSR(self): date_from = '2017-01-01 00:00:01' date_to = '2017-01-31 23:59:59' - leaves = self.env['resource.calendar'].get_ocurrences( + leaves = self.env['hr.holidays'].get_ocurrences( self.employee_hr_user_id.id, date_from, date_to) quantity_DSR_discount = self.resource_calendar.\ get_quantity_discount_DSR(leaves['faltas_nao_remuneradas'], @@ -151,7 +151,7 @@ def test_03_quantidade_DSR_em_intervalo(self): # # # Ferias aprovada pro funcionario # holiday_status_id = self.env.ref( - # 'l10n_br_hr_vacation.holiday_status_vacation') + # 'l10n_br_hr_holiday.holiday_status_vacation') # self.holiday_id = self.hr_holidays.create({ # 'name': 'Ferias', # 'type': 'add', @@ -165,7 +165,7 @@ def test_03_quantidade_DSR_em_intervalo(self): # # # Funcionario goza das ferias # holiday_status_id = self.env.ref( - # 'l10n_br_hr_vacation.holiday_status_vacation') + # 'l10n_br_hr_holiday.holiday_status_vacation') # self.holiday_id = self.hr_holidays.create({ # 'name': 'Ferias', # 'holiday_type': 'employee', diff --git a/l10n_br_hr_payroll/tests/test_hr_payslip_rubricas.py b/l10n_br_hr_payroll/tests/test_hr_payslip_rubricas.py index 9c1d2bcf1..3508e73a9 100644 --- a/l10n_br_hr_payroll/tests/test_hr_payslip_rubricas.py +++ b/l10n_br_hr_payroll/tests/test_hr_payslip_rubricas.py @@ -64,7 +64,7 @@ def atribuir_ferias(self, quantidade_dias, date_from, date_to, # Ferias aprovada pro funcionario holiday_status_id = self.env.ref( - 'l10n_br_hr_vacation.holiday_status_vacation') + 'l10n_br_hr_holiday.holiday_status_vacation') self.holiday_id = self.hr_holidays.create({ 'name': 'Ferias', 'type': 'add', @@ -78,7 +78,7 @@ def atribuir_ferias(self, quantidade_dias, date_from, date_to, # Funcionario goza das ferias holiday_status_id = self.env.ref( - 'l10n_br_hr_vacation.holiday_status_vacation') + 'l10n_br_hr_holiday.holiday_status_vacation') self.holiday_id = self.hr_holidays.create({ 'name': 'Ferias', 'holiday_type': 'employee', diff --git a/l10n_br_hr_payroll/tests/test_l10n_br_hr_contract.py b/l10n_br_hr_payroll/tests/test_l10n_br_hr_contract.py new file mode 100644 index 000000000..87eaf8041 --- /dev/null +++ b/l10n_br_hr_payroll/tests/test_l10n_br_hr_contract.py @@ -0,0 +1,319 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import exceptions +from openerp.tests import common +# from openerp.exceptions import Warning as UserError + + +class TestHrHoliday(common.TransactionCase): + + def setUp(self): + super(TestHrHoliday, self).setUp() + # Usefull models + self.hr_employee = self.env['hr.employee'] + self.hr_contract = self.env['hr.contract'] + self.hr_holidays = self.env['hr.holidays'] + + def buscar_periodo_aquisitivo(self, contrato, inicio_ferias, fim_ferias): + for controle_ferias in contrato.vacation_control_ids: + if controle_ferias.inicio_concessivo < inicio_ferias and \ + controle_ferias.fim_concessivo > fim_ferias: + if not controle_ferias.hr_holiday_ids: + controle_ferias.gerar_holidays_ferias() + holidays = controle_ferias.hr_holiday_ids + for holiday in holidays: + if holiday.type == 'add': + return holiday + + def atribuir_ferias(self, contrato, inicio_ferias, + fim_ferias, dias_ferias, dias_abono): + """ + Atribui férias ao funcionário. + Cria um holidays nos dias que o funcionario ira gozar as ferias . + """ + # Buscar periodo Aquisitivo de acordo com os dias de ferias gozadas + holiday_periodo_aquisitivo = self.buscar_periodo_aquisitivo( + contrato, inicio_ferias, fim_ferias) + + holiday_status_id = self.env.ref( + 'l10n_br_hr_holiday.holiday_status_vacation') + + # Solicitacao de férias do funcionario + ferias = self.hr_holidays.create({ + 'name': 'Ferias Do ' + contrato.employee_id.name, + 'type': 'remove', + 'parent_id': holiday_periodo_aquisitivo.id, + 'holiday_type': 'employee', + 'holiday_status_id': holiday_status_id.id, + 'employee_id': contrato.employee_id.id, + 'vacations_days': dias_ferias, + 'sold_vacations_days': dias_abono, + 'number_of_days_temp': dias_ferias + dias_abono, + 'date_from': inicio_ferias, + 'date_to': fim_ferias, + 'contrato_id': contrato.id, + }) + # Chamando Onchange manualmente para setar o controle de férias + ferias._compute_contract() + # Aprovacao da solicitacao do funcionario + ferias.holidays_validate() + + def criar_contrato(self, date_start): + """ + Criar um novo contrato para o funcionario + :param name: str - Nome Referencia do contrato + :param wage: + :param struct_id: + :param date_start: + :return: + """ + employee_id = self.criar_funcionario('ANA BEATRIZ CARVALHO') + estrutura_salario = self.env.ref( + 'l10n_br_hr_payroll.hr_salary_structure_FUNCAO_COMISSIONADA') + contrato_id = self.hr_contract.create({ + 'name': 'Contrato ' + employee_id.name, + 'employee_id': employee_id.id, + 'wage': 12345.67, + 'struct_id': estrutura_salario.id, + 'date_start': date_start, + }) + return contrato_id + + def criar_funcionario(self, nome): + """ + Criar um employee apartir de um nome e sua quantidade de dependentes + :param nome: str Nome do funcionario + :return: hr.employee + """ + funcionario = self.hr_employee.create({'name': nome}) + return funcionario + + def test_00_criacao_contrato(self): + """ + Criacao de um contrato simples + """ + contrato = self.criar_contrato('2014-01-01') + + self.assertEqual(contrato.employee_id.name, 'ANA BEATRIZ CARVALHO') + self.assertEqual(contrato.name, 'Contrato ANA BEATRIZ CARVALHO') + self.assertEqual(contrato.date_start, '2014-01-01') + self.assertEqual(contrato.wage, 12345.67) + # Testar controles de férias + self.assertEqual(len(contrato.vacation_control_ids), 4) + self.assertEqual( + contrato.vacation_control_ids[3].inicio_aquisitivo, '2014-01-01') + self.assertEqual( + contrato.vacation_control_ids[3].fim_aquisitivo, '2014-12-31') + self.assertEqual( + contrato.vacation_control_ids[2].inicio_aquisitivo, '2015-01-01') + self.assertEqual( + contrato.vacation_control_ids[2].fim_aquisitivo, '2015-12-31') + self.assertEqual( + contrato.vacation_control_ids[1].inicio_aquisitivo, '2016-01-01') + self.assertEqual( + contrato.vacation_control_ids[1].fim_aquisitivo, '2016-12-31') + self.assertEqual( + contrato.vacation_control_ids[0].inicio_aquisitivo, '2017-01-01') + self.assertEqual( + contrato.vacation_control_ids[0].fim_aquisitivo, '2017-12-31') + + def test_01_verificar_controle_ferias(self): + """ + Verificar a criação do controle de férias + :return: + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + # Verificar a criação do controle de férias + controle = contrato.vacation_control_ids[3] + self.assertEqual(len(contrato.vacation_control_ids), 4) + self.assertEqual(controle.inicio_aquisitivo, '2014-01-01') + self.assertEqual(controle.fim_aquisitivo, '2014-12-31') + self.assertEqual(controle.inicio_concessivo, '2015-01-01') + self.assertEqual(controle.fim_concessivo, '2015-12-31') + + # Verificar a criação dos holidays que atribuem férias ao funcionario + # A Criação só é feita automatica para os dois ultimos controlesferias + for controle in contrato.vacation_control_ids[:2]: + self.assertEqual(len(controle.hr_holiday_ids), 1) + self.assertEqual(controle.hr_holiday_ids.number_of_days_temp, 30) + self.assertEqual(controle.hr_holiday_ids.type, 'add') + + def test_02_editar_contrato(self): + """ + Para editar a data de admissao no contrato , nao se pode ter nenhum + holiday aprovado de férias do tipo remove, atrelado ao contrato. + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + # Edição do contrato + contrato.date_start = '2010-08-01' + + controle = contrato.vacation_control_ids[6] + self.assertEqual(len(contrato.vacation_control_ids), 7) + self.assertEqual(controle.inicio_aquisitivo, '2010-08-01') + self.assertEqual(controle.fim_aquisitivo, '2011-07-31') + self.assertEqual(controle.inicio_concessivo, '2011-08-01') + self.assertEqual(controle.fim_concessivo, '2012-07-31') + + # verificar a criação de novos holidays de férias + for controle in contrato.vacation_control_ids[:2]: + self.assertEqual(len(controle.hr_holiday_ids), 1) + self.assertEqual(controle.hr_holiday_ids.number_of_days_temp, 30) + self.assertEqual(controle.hr_holiday_ids.type, 'add') + + def test_03_gerar_novo_controle_ferias(self): + """ + Se apagar algum controle ou por algum motivo o usuario deseja acionar + o botão da visão e recalcular o controle de ferias + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + # Excluir controles + for controle in contrato.vacation_control_ids: + controle.unlink() + + # teste da exclusao do controle + self.assertEqual(len(contrato.vacation_control_ids), 0) + + # verificar a exclusão dos holidays antigos de férias + holiday_status_id = self.env.ref( + 'l10n_br_hr_holiday.holiday_status_vacation') + for holiday in contrato.afastamento_ids: + self.assertNotEqual(holiday.holiday_status_id.id, + holiday_status_id.id) + + # Método disparado pelo botão da visão + contrato.action_button_update_controle_ferias() + # Validar criação de novos controles de fériass + self.assertEqual(len(contrato.vacation_control_ids), 4) + # Validar a criação do holiday do controle de férias novamente + for controle in contrato.vacation_control_ids[:2]: + self.assertEqual(len(controle.hr_holiday_ids), 1) + + def test_04_editar_contrato_com_ferias(self): + """ + Não é possível editar um contrato que ja possui férias (holidays do + tipo 'remove') atrelado a ele, + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + # Atribuir holidays de solicitação de ferias aprovado + self.atribuir_ferias(contrato, '2016-10-01', '2016-10-30', 30, 0) + # Verificar se tem holidays de solicitação de férias aprovado para o + # contrato corrente + holiday_status_id = self.env.ref( + 'l10n_br_hr_holiday.holiday_status_vacation') + ferias_atribuida = False + for holidays in contrato.afastamento_ids: + if holidays.holiday_status_id.id == holiday_status_id.id and \ + holidays.type == 'remove': + ferias_atribuida = True + self.assertTrue(ferias_atribuida) + + # Se ja tiver holidays, nao permite alteração da data do contrato + with self.assertRaises(exceptions.Warning): + contrato.date_start = '2015-02-02' + + def run_tests_05(self, contrato): + """ + Testes das informacoes do ultimo controle de ferias para o bloco de + testes do item 05 + """ + self.assertEqual( + contrato.vacation_control_ids[0].fim_aquisitivo, '2017-06-12') + self.assertEqual( + contrato.vacation_control_ids[0].inicio_concessivo, False) + self.assertEqual( + contrato.vacation_control_ids[0].fim_concessivo, False) + self.assertEqual( + contrato.vacation_control_ids[0].avos, 5) + + def test_05_finalizar_contrato(self): + """ + Ao atribuir uma data de demissao ('date_end'), o controle de ferias + deve parar a contabilizacao do saldo de dias para ferias e atribuir + a data de demissao para o ultimo controle de ferias + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + # Encerrar contrato + contrato.date_end = '2017-06-12' + # Executar testes + self.run_tests_05(contrato) + + # Se Chamar atualizacao do controle de ferias pela view, deve continuar + # passando nos testes + contrato.action_button_update_controle_ferias() + # tests + self.run_tests_05(contrato) + + def test_06_reativar_contrato(self): + """ + Após finalizar um contrato, o controle de ferias é atualizado com + informacoes da data de demissao. Quando um contrato é reativado, + o controle de ferias deve voltar a calcular + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + # Encerrar contrato + contrato.date_end = '2017-06-12' + self.run_tests_05(contrato) + + # Reativar contrato + contrato.date_end = False + + self.assertEqual( + contrato.vacation_control_ids[0].inicio_aquisitivo, '2017-01-01') + self.assertEqual( + contrato.vacation_control_ids[0].fim_aquisitivo, '2017-12-31') + self.assertEqual( + contrato.vacation_control_ids[0].inicio_concessivo, '2018-01-01') + self.assertEqual( + contrato.vacation_control_ids[0].fim_concessivo, '2018-12-31') + + def test_07_finalizar_contrato_sem_controle_ferias(self): + """ + Finalizar um contrato quando nao há um controle de férias + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + # excluir controle férias + for controle in contrato.vacation_control_ids: + controle.unlink() + + # Garantir que o controle de férias foi apagado + self.assertEqual(len(contrato.vacation_control_ids), 0) + + # Finalizar contrato + contrato.date_end = '2017-06-12' + + # Verificar data do contrato + self.assertEqual(contrato.date_end, '2017-06-12') + + def test_08_criar_holidays_ultimos_controles(self): + """ + Criar holidays do tipo 'add' de férias para as duas ultimas linhas do + controle de férias. Sempre que um contrato for criado, o funcionario + ja pode selecionar seus holidays de férias do tipo 'remove'. + """ + # Criar Contrato + contrato = self.criar_contrato('2014-01-01') + + for controle in contrato.vacation_control_ids[:2]: + self.assertTrue(controle.hr_holiday_ids) + + ultimo_controle = contrato.vacation_control_ids[0] + self.assertEqual(ultimo_controle.inicio_aquisitivo, '2017-01-01') + self.assertEqual(ultimo_controle.fim_aquisitivo, '2017-12-31') + + penultimo_controle = contrato.vacation_control_ids[1] + self.assertEqual(penultimo_controle.inicio_aquisitivo, '2016-01-01') + self.assertEqual(penultimo_controle.fim_aquisitivo, '2016-12-31') diff --git a/l10n_br_hr_payroll/views/hr_contract.xml b/l10n_br_hr_payroll/views/hr_contract.xml index 97b8da154..8ac8cd0ca 100644 --- a/l10n_br_hr_payroll/views/hr_contract.xml +++ b/l10n_br_hr_payroll/views/hr_contract.xml @@ -10,9 +10,24 @@ hr.contract + + + + + {'readonly': [ + ('is_editable', '=', False)]} + + +

+
+ @@ -23,25 +38,34 @@ + - + + + + + + + + + - + - + - + - + @@ -68,7 +92,7 @@ - + @@ -93,7 +117,7 @@ - + @@ -141,18 +165,32 @@ - + - - - - + + + + + + + hr.contract.tree (in l10n_br_hr_payroll) + hr.contract + + + + + + + + + + diff --git a/l10n_br_hr_payroll/views/hr_payroll_structure.xml b/l10n_br_hr_payroll/views/hr_payroll_structure.xml index e3a02abf9..86e7d1e2c 100644 --- a/l10n_br_hr_payroll/views/hr_payroll_structure.xml +++ b/l10n_br_hr_payroll/views/hr_payroll_structure.xml @@ -6,15 +6,86 @@ + + hr.payroll.structure.view (in l10n_br_hr_payroll) + hr.payroll.structure + + + + + + + + + + + + hr.payroll.structure.search (in l10n_br_hr_payroll) + hr.payroll.structure + + + + + + + + + + + + + + + + + {'search_default_filtro_normal': 1} + + hr.payroll.structure.form (in l10n_br_hr_payroll) hr.payroll.structure - - - + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_hr_payroll/views/hr_payslip.xml b/l10n_br_hr_payroll/views/hr_payslip.xml index bd975f81d..bc8f9616c 100644 --- a/l10n_br_hr_payroll/views/hr_payslip.xml +++ b/l10n_br_hr_payroll/views/hr_payslip.xml @@ -3,348 +3,612 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - - - - - - 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 - - - - - - - - - - - - -
- + + + + + + + + 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 + + + + + + + + + + + + + - - - -
-
- - - -
- - - - - - - - - - - - - -
- + + + + + + + + + + + + + + + + +
+ + + + + - - - - + + + + - - - - - - - - - - - - - - - - -
- + + + + + + + + + + + + + + + + +
+ + + + + - - - + - - - - - - - - - - - - -
- + + + + + + + + + + + + + + + + + - - -
- -
-
- - -