160 lines
6.4 KiB
Python
160 lines
6.4 KiB
Python
# ARQUIVO: ./models/crianca.py
|
|
from odoo import models, fields, api, _
|
|
from datetime import date
|
|
|
|
class RecreacaoCrianca(models.Model):
|
|
_name = 'recreacao.crianca'
|
|
_description = 'Aluno / Criança'
|
|
_inherit = ['mail.thread'] # Permite o log de mensagens e chat no rodapé
|
|
|
|
# --- DADOS PESSOAIS ---
|
|
name = fields.Char(string='Nome da Criança', required=True, tracking=True)
|
|
foto = fields.Binary(string="Foto")
|
|
data_nascimento = fields.Date(string='Data de Nascimento')
|
|
idade = fields.Char(compute='_compute_idade', string='Idade')
|
|
|
|
# --- FAMÍLIA ---
|
|
pai_id = fields.Many2one('res.partner', string='Pai', domain="[('is_company', '=', False)]")
|
|
mae_id = fields.Many2one('res.partner', string='Mãe', domain="[('is_company', '=', False)]")
|
|
responsavel_financeiro_id = fields.Many2one('res.partner', string='Responsável Financeiro', required=True)
|
|
|
|
# --- SAÚDE ---
|
|
tem_alergia = fields.Boolean(string="Tem Alergia?", tracking=True)
|
|
alergias = fields.Text(string='Quais Alergias?')
|
|
toma_remedio = fields.Boolean(string="Toma Remédio?")
|
|
medicamentos = fields.Text(string='Quais Medicamentos?')
|
|
horario_medicacao = fields.Char(string='Horários')
|
|
observacoes_saude = fields.Text(string='Obs. Médicas')
|
|
|
|
# --- PLANO & FINANCEIRO ---
|
|
plano_id = fields.Many2one('recreacao.plano', string='Plano Contratado', tracking=True)
|
|
valor_plano = fields.Float(related='plano_id.valor', string='Valor do Plano (R$)', readonly=True)
|
|
|
|
# Novo relacionamento com o Financeiro Simplificado
|
|
financeiro_ids = fields.One2many('recreacao.financeiro', 'crianca_id', string='Histórico de Pagamentos')
|
|
|
|
# --- HISTÓRICO DE FREQUÊNCIA ---
|
|
diario_ids = fields.One2many('recreacao.diario', 'crianca_id', string='Histórico Diário')
|
|
|
|
# --- CONTROLE DE STATUS (Lógica Corrigida) ---
|
|
status_dia = fields.Selection([
|
|
('ausente', 'Ausente'),
|
|
('presente', 'Na Creche'),
|
|
('finalizado', 'Já Saiu')
|
|
], compute='_compute_status_dia', string='Status Hoje', store=False)
|
|
|
|
@api.depends('diario_ids')
|
|
def _compute_status_dia(self):
|
|
hoje = fields.Date.today()
|
|
for rec in self:
|
|
# Busca o ÚLTIMO movimento de entrada ou saída de hoje
|
|
ultimo_movimento = self.env['recreacao.diario'].search([
|
|
('crianca_id', '=', rec.id),
|
|
('data', '=', hoje),
|
|
('tipo', 'in', ['entrada', 'saida'])
|
|
], order='create_date desc', limit=1)
|
|
|
|
if not ultimo_movimento:
|
|
rec.status_dia = 'ausente'
|
|
elif ultimo_movimento.tipo == 'entrada':
|
|
rec.status_dia = 'presente'
|
|
elif ultimo_movimento.tipo == 'saida':
|
|
rec.status_dia = 'finalizado'
|
|
else:
|
|
rec.status_dia = 'ausente'
|
|
|
|
# --- AÇÕES DE FREQUÊNCIA ---
|
|
def action_marcar_entrada(self):
|
|
"""Botão Verde do Kanban"""
|
|
for rec in self:
|
|
self.env['recreacao.diario'].create({
|
|
'crianca_id': rec.id,
|
|
'tipo': 'entrada',
|
|
'descricao': 'Chegou na recreação.'
|
|
})
|
|
# Força atualização da interface
|
|
rec._compute_status_dia()
|
|
|
|
def action_marcar_saida(self):
|
|
"""Botão Vermelho do Kanban"""
|
|
for rec in self:
|
|
self.env['recreacao.diario'].create({
|
|
'crianca_id': rec.id,
|
|
'tipo': 'saida',
|
|
'descricao': 'Foi embora.'
|
|
})
|
|
rec._compute_status_dia()
|
|
|
|
# --- AÇÕES DE UTILIDADE ---
|
|
def action_abrir_whatsapp(self):
|
|
self.ensure_one()
|
|
if not self.responsavel_financeiro_id.phone and not self.responsavel_financeiro_id.mobile:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {'title': 'Erro', 'message': 'Responsável sem telefone cadastrado!', 'type': 'danger'}
|
|
}
|
|
|
|
numero = self.responsavel_financeiro_id.mobile or self.responsavel_financeiro_id.phone
|
|
phone = ''.join(filter(str.isdigit, numero))
|
|
msg = f"Olá, gostaria de falar sobre: {self.name}"
|
|
return {
|
|
'type': 'ir.actions.act_url',
|
|
'url': f"https://api.whatsapp.com/send?phone={phone}&text={msg}",
|
|
'target': 'new',
|
|
}
|
|
|
|
def action_abrir_historico(self):
|
|
"""Abre a lista filtrada do diário dessa criança"""
|
|
return {
|
|
'name': f'Histórico: {self.name}',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'recreacao.diario',
|
|
'domain': [('crianca_id', '=', self.id)],
|
|
'view_mode': 'tree,form',
|
|
}
|
|
|
|
# --- NOVA AÇÃO FINANCEIRA (SIMPLIFICADA) ---
|
|
def action_gerar_cobranca(self):
|
|
"""Gera um lançamento de Receita no Financeiro Simplificado"""
|
|
self.ensure_one()
|
|
if not self.plano_id:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {'title': 'Atenção', 'message': 'Selecione um Plano antes de gerar cobrança.', 'type': 'warning'}
|
|
}
|
|
|
|
# Cria a cobrança no novo modelo
|
|
self.env['recreacao.financeiro'].create({
|
|
'name': f"Mensalidade: {self.name} - {fields.Date.today().strftime('%m/%Y')}",
|
|
'tipo': 'receita',
|
|
'valor': self.valor_plano,
|
|
'partner_id': self.responsavel_financeiro_id.id,
|
|
'crianca_id': self.id,
|
|
'data_vencimento': fields.Date.today(),
|
|
'status': 'pendente'
|
|
})
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Sucesso',
|
|
'message': f'Cobrança de R$ {self.valor_plano} gerada no Financeiro!',
|
|
'type': 'success',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
# --- CÁLCULO DE IDADE ---
|
|
@api.depends('data_nascimento')
|
|
def _compute_idade(self):
|
|
for rec in self:
|
|
if rec.data_nascimento:
|
|
today = date.today()
|
|
# Lógica precisa de cálculo de idade (considera dia e mês)
|
|
years = today.year - rec.data_nascimento.year - ((today.month, today.day) < (rec.data_nascimento.month, rec.data_nascimento.day))
|
|
rec.idade = f"{years} anos"
|
|
else:
|
|
rec.idade = "Sem data" |