1178 lines
50 KiB
Plaintext
1178 lines
50 KiB
Plaintext
============== ESTRUTURA DO PROJETO ==============
|
|
.
|
|
./__manifest__.py
|
|
./models
|
|
./models/financeiro.py
|
|
./models/plano.py
|
|
./models/__init__.py
|
|
./models/crianca.py
|
|
./models/diario.py
|
|
./models/res_partner.py
|
|
./models/report_parser.py
|
|
./__init__.py
|
|
./reports
|
|
./reports/report.xml
|
|
./reports/financeiro_report.xml
|
|
./reports/historico_template.xml
|
|
./reports/financeiro_template.xml
|
|
./static
|
|
./static/manifest.json
|
|
./static/img
|
|
./static/img/favicon.png
|
|
./static/img/icon_192.png
|
|
./static/img/icon_512.png
|
|
./static/img/favicon.ico
|
|
./wizard
|
|
./wizard/financeiro_wizard.py
|
|
./wizard/__init__.py
|
|
./security
|
|
./security/ir.model.access.csv
|
|
./views
|
|
./views/financeiro_wizard_view.xml
|
|
./views/web_layout.xml
|
|
./views/crianca_view.xml
|
|
./views/plano_view.xml
|
|
./views/res_partner_view.xml
|
|
./views/diario_view.xml
|
|
./views/financeiro_view.xml
|
|
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./__manifest__.py
|
|
==================================================================
|
|
# ARQUIVO: ./__manifest__.py
|
|
{
|
|
'name': 'Gestão de Recreação (Lite)',
|
|
'version': '3.0',
|
|
'category': 'Services',
|
|
'summary': 'Alunos, Pais e Financeiro Simples',
|
|
'depends': ['base', 'contacts'], # REMOVIDO 'account'
|
|
'data': [
|
|
'security/ir.model.access.csv',
|
|
'views/plano_view.xml',
|
|
'views/diario_view.xml',
|
|
'views/crianca_view.xml',
|
|
'views/financeiro_view.xml',
|
|
'views/res_partner_view.xml',
|
|
'views/web_layout.xml',
|
|
'views/financeiro_wizard_view.xml', # NOVO
|
|
'reports/report.xml',
|
|
'reports/historico_template.xml',
|
|
'reports/financeiro_template.xml', # NOVO
|
|
],
|
|
'application': True,
|
|
'license': 'LGPL-3',
|
|
}
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/financeiro.py
|
|
==================================================================
|
|
# ARQUIVO: ./models/financeiro.py
|
|
from odoo import models, fields, api
|
|
|
|
class RecreacaoFinanceiro(models.Model):
|
|
_name = 'recreacao.financeiro'
|
|
_description = 'Movimentação Financeira'
|
|
_order = 'data_vencimento desc'
|
|
|
|
name = fields.Char(string='Descrição', required=True)
|
|
|
|
tipo = fields.Selection([
|
|
('receita', '🟢 Receita (Entrada)'),
|
|
('despesa', '🔴 Despesa (Saída)')
|
|
], string='Tipo', required=True, default='receita')
|
|
|
|
valor = fields.Float(string='Valor (R$)', required=True)
|
|
|
|
data_vencimento = fields.Date(string='Vencimento', required=True, default=fields.Date.context_today)
|
|
data_pagamento = fields.Date(string='Data Pagamento')
|
|
|
|
# Status do pagamento
|
|
status = fields.Selection([
|
|
('pendente', 'Pendente'),
|
|
('pago', 'Pago'),
|
|
('cancelado', 'Cancelado')
|
|
], string='Status', default='pendente', tracking=True)
|
|
|
|
# Relacionamentos
|
|
partner_id = fields.Many2one('res.partner', string='Pessoa/Fornecedor')
|
|
crianca_id = fields.Many2one('recreacao.crianca', string='Referente ao Aluno')
|
|
|
|
def action_pagar(self):
|
|
for rec in self:
|
|
rec.status = 'pago'
|
|
rec.data_pagamento = fields.Date.today()
|
|
|
|
def action_cancelar(self):
|
|
for rec in self:
|
|
rec.status = 'cancelado'
|
|
|
|
def action_redefinir(self):
|
|
for rec in self:
|
|
rec.status = 'pendente'
|
|
rec.data_pagamento = False
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/plano.py
|
|
==================================================================
|
|
from odoo import models, fields
|
|
|
|
class RecreacaoPlano(models.Model):
|
|
_name = 'recreacao.plano'
|
|
_description = 'Planos de Cobrança'
|
|
|
|
name = fields.Char(string='Nome do Plano', required=True)
|
|
|
|
# NOVO: Tipo de período
|
|
tipo_cobranca = fields.Selection([
|
|
('mensal', 'Mensalidade (Recorrente)'),
|
|
('diaria', 'Diária / Avulso')
|
|
], string='Tipo de Cobrança', default='mensal', required=True)
|
|
|
|
valor = fields.Float(string='Valor (R$)', required=True)
|
|
produto_id = fields.Many2one('product.product', string='Produto/Serviço Vinculado')
|
|
|
|
horario_inicio = fields.Float(string='Entrada')
|
|
horario_fim = fields.Float(string='Saída')
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/__init__.py
|
|
==================================================================
|
|
from . import plano
|
|
from . import diario
|
|
from . import crianca
|
|
from . import res_partner
|
|
from . import financeiro
|
|
from . import report_parser
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/crianca.py
|
|
==================================================================
|
|
# 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"
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/diario.py
|
|
==================================================================
|
|
from odoo import models, fields, api
|
|
from datetime import datetime
|
|
import pytz
|
|
|
|
class RecreacaoDiario(models.Model):
|
|
_name = 'recreacao.diario'
|
|
_description = 'Diário de Classe'
|
|
_order = 'data desc, create_date desc' # Ordena pelo mais recente
|
|
|
|
data = fields.Date(string='Data', required=True, default=fields.Date.context_today)
|
|
|
|
# Campo para guardar a hora exata (para o relatório)
|
|
hora_registro = fields.Char(string='Hora', default=lambda self: self._get_hora_atual())
|
|
|
|
crianca_id = fields.Many2one('recreacao.crianca', string='Criança', required=True)
|
|
|
|
tipo = fields.Selection([
|
|
('entrada', '🟢 Entrada / Chegada'),
|
|
('saida', '🔴 Saída / Foi Embora'),
|
|
('ocorrencia', '⚠️ Ocorrência / Incidente'),
|
|
('saude', '💊 Medicamento / Saúde'),
|
|
('rotina', '📝 Rotina / Anotação')
|
|
], string='Tipo', required=True, default='rotina')
|
|
|
|
descricao = fields.Text(string='Observações')
|
|
|
|
def _get_hora_atual(self):
|
|
# Pega a hora atual no fuso de SP (Hardcoded para facilitar)
|
|
tz = pytz.timezone('America/Sao_Paulo')
|
|
return datetime.now(tz).strftime('%H:%M')
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/res_partner.py
|
|
==================================================================
|
|
from odoo import models, fields
|
|
|
|
class ResPartner(models.Model):
|
|
_inherit = 'res.partner'
|
|
|
|
# CORREÇÃO AQUI: O segundo parâmetro deve ser igual ao nome do campo na criança
|
|
crianca_ids = fields.One2many(
|
|
'recreacao.crianca',
|
|
'responsavel_financeiro_id',
|
|
string='Filhos (Responsável Fin.)'
|
|
)
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./models/report_parser.py
|
|
==================================================================
|
|
from odoo import models, api
|
|
|
|
class RelatorioFinanceiroParser(models.AbstractModel):
|
|
_name = 'report.plugin_recre.template_financeiro_mensal'
|
|
_description = 'Lógica do Relatório Financeiro'
|
|
|
|
@api.model
|
|
def _get_report_values(self, docids, data=None):
|
|
start = data.get('data_inicio')
|
|
end = data.get('data_fim')
|
|
|
|
# Busca todos os movimentos no período
|
|
movimentos = self.env['recreacao.financeiro'].search([
|
|
('data_vencimento', '>=', start),
|
|
('data_vencimento', '<=', end),
|
|
# Opcional: filtrar só os pagos? Para fluxo de caixa real, sim.
|
|
# ('status', '=', 'pago')
|
|
], order='data_vencimento asc')
|
|
|
|
entradas = movimentos.filtered(lambda r: r.tipo == 'receita')
|
|
saidas = movimentos.filtered(lambda r: r.tipo == 'despesa')
|
|
|
|
total_entradas = sum(entradas.mapped('valor'))
|
|
total_saidas = sum(saidas.mapped('valor'))
|
|
|
|
return {
|
|
'doc_ids': docids,
|
|
'doc_model': 'recreacao.financeiro',
|
|
'data_inicio': start,
|
|
'data_fim': end,
|
|
'entradas': entradas,
|
|
'saidas': saidas,
|
|
'total_entradas': total_entradas,
|
|
'total_saidas': total_saidas,
|
|
'saldo_final': total_entradas - total_saidas,
|
|
}
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./__init__.py
|
|
==================================================================
|
|
from . import models
|
|
from . import wizard
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./reports/report.xml
|
|
==================================================================
|
|
<odoo>
|
|
<record id="action_report_historico_aluno" model="ir.actions.report">
|
|
<field name="name">Histórico Mensal</field>
|
|
<field name="model">recreacao.crianca</field>
|
|
<field name="report_type">qweb-pdf</field>
|
|
<!-- O nome agora começa com plugin_recre -->
|
|
<field name="report_name">plugin_recre.report_historico_aluno</field>
|
|
<field name="report_file">plugin_recre.report_historico_aluno</field>
|
|
<field name="binding_model_id" ref="model_recreacao_crianca"/>
|
|
<field name="binding_type">report</field>
|
|
</record>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./reports/historico_template.xml
|
|
==================================================================
|
|
<odoo>
|
|
<template id="report_historico_aluno">
|
|
<t t-call="web.html_container">
|
|
<t t-foreach="docs" t-as="doc">
|
|
<t t-call="web.external_layout">
|
|
<div class="page">
|
|
<h2>Relatório do Aluno: <span t-field="doc.name"/></h2>
|
|
|
|
<div class="row mt32 mb32">
|
|
<div class="col-6">
|
|
<strong>Idade:</strong> <span t-field="doc.idade"/><br/>
|
|
<strong>Responsável:</strong> <span t-field="doc.responsavel_financeiro_id.name"/>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>Diário de Atividades</h3>
|
|
<table class="table table-sm table-striped mt-4">
|
|
<thead>
|
|
<tr>
|
|
<th>Data</th>
|
|
<th>Hora</th> <!-- Coluna Nova -->
|
|
<th>Tipo</th>
|
|
<th>Detalhes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr t-foreach="doc.diario_ids" t-as="line">
|
|
<td><span t-field="line.data"/></td>
|
|
<td><span t-field="line.hora_registro"/></td> <!-- Hora -->
|
|
<td>
|
|
<!-- Ícones no PDF -->
|
|
<span t-if="line.tipo == 'entrada'" style="color:green;">🟢 Entrada</span>
|
|
<span t-if="line.tipo == 'saida'" style="color:red;">🔴 Saída</span>
|
|
<span t-if="line.tipo == 'ocorrencia'" style="color:orange; font-weight:bold;">⚠️ Ocorrência</span>
|
|
<span t-if="line.tipo == 'saude'">💊 Saúde</span>
|
|
<span t-if="line.tipo == 'rotina'">Rotina</span>
|
|
</td>
|
|
<td><span t-field="line.descricao"/></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./reports/financeiro_template.xml
|
|
==================================================================
|
|
<odoo>
|
|
<!-- AÇÃO DO RELATÓRIO -->
|
|
<record id="action_report_financeiro_mensal" model="ir.actions.report">
|
|
<field name="name">Fluxo de Caixa Mensal</field>
|
|
<field name="model">recreacao.financeiro.wizard</field>
|
|
<field name="report_type">qweb-pdf</field>
|
|
<field name="report_name">plugin_recre.template_financeiro_mensal</field>
|
|
<field name="report_file">plugin_recre.template_financeiro_mensal</field>
|
|
</record>
|
|
|
|
<!-- O TEMPLATE VISUAL -->
|
|
<template id="template_financeiro_mensal">
|
|
<t t-call="web.html_container">
|
|
<t t-call="web.external_layout"> <!-- Usa o cabeçalho oficial da empresa -->
|
|
<div class="page">
|
|
<div class="text-center mt-4 mb-4">
|
|
<h2>Relatório de Fluxo de Caixa</h2>
|
|
<p>Período: <span t-esc="data_inicio"/> até <span t-esc="data_fim"/></p>
|
|
</div>
|
|
|
|
<!-- BLOCO 1: ENTRADAS (RECEITAS) -->
|
|
<div class="mt-4">
|
|
<h3 style="color: #28a745; border-bottom: 2px solid #28a745;">
|
|
🟢 Entradas (Receitas)
|
|
</h3>
|
|
<table class="table table-sm table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Data</th>
|
|
<th>Descrição / Aluno</th>
|
|
<th>Responsável</th>
|
|
<th>Status</th>
|
|
<th class="text-end">Valor (R$)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr t-foreach="entradas" t-as="line">
|
|
<td><span t-field="line.data_vencimento"/></td>
|
|
<td><span t-field="line.name"/></td>
|
|
<td><span t-field="line.partner_id.name"/></td>
|
|
<td>
|
|
<span t-if="line.status == 'pago'" class="badge rounded-pill text-bg-success">Pago</span>
|
|
<span t-if="line.status == 'pendente'" class="badge rounded-pill text-bg-warning">Pendente</span>
|
|
</td>
|
|
<td class="text-end"><span t-field="line.valor"/></td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="fw-bold">
|
|
<td colspan="4" class="text-end">TOTAL ENTRADAS:</td>
|
|
<td class="text-end" style="color: #28a745;">
|
|
R$ <span t-esc="'{:.2f}'.format(total_entradas).replace('.', ',')"/>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- BLOCO 2: SAÍDAS (DESPESAS) -->
|
|
<div class="mt-5">
|
|
<h3 style="color: #dc3545; border-bottom: 2px solid #dc3545;">
|
|
🔴 Saídas (Despesas)
|
|
</h3>
|
|
<table class="table table-sm table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Data</th>
|
|
<th>Descrição / Motivo</th>
|
|
<th>Fornecedor</th>
|
|
<th>Status</th>
|
|
<th class="text-end">Valor (R$)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr t-foreach="saidas" t-as="line">
|
|
<td><span t-field="line.data_vencimento"/></td>
|
|
<td><span t-field="line.name"/></td>
|
|
<td><span t-field="line.partner_id.name"/></td>
|
|
<td>
|
|
<span t-if="line.status == 'pago'" class="badge rounded-pill text-bg-success">Pago</span>
|
|
<span t-if="line.status == 'pendente'" class="badge rounded-pill text-bg-warning">Pendente</span>
|
|
</td>
|
|
<td class="text-end"><span t-field="line.valor"/></td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="fw-bold">
|
|
<td colspan="4" class="text-end">TOTAL SAÍDAS:</td>
|
|
<td class="text-end" style="color: #dc3545;">
|
|
R$ <span t-esc="'{:.2f}'.format(total_saidas).replace('.', ',')"/>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- BLOCO 3: RESUMO FINAL -->
|
|
<div class="row mt-5">
|
|
<div class="col-6 offset-6">
|
|
<table class="table table-bordered">
|
|
<tr style="background-color: #f8f9fa;">
|
|
<td><strong>Total Entradas (+)</strong></td>
|
|
<td class="text-end text-success"><span t-esc="'{:.2f}'.format(total_entradas)"/></td>
|
|
</tr>
|
|
<tr style="background-color: #f8f9fa;">
|
|
<td><strong>Total Saídas (-)</strong></td>
|
|
<td class="text-end text-danger"><span t-esc="'{:.2f}'.format(total_saidas)"/></td>
|
|
</tr>
|
|
<tr style="background-color: #333; color: white; font-size: 1.2em;">
|
|
<td><strong>SALDO DO PERÍODO (=)</strong></td>
|
|
<td class="text-end">
|
|
R$ <span t-esc="'{:.2f}'.format(saldo_final).replace('.', ',')"/>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-muted text-center mt-5" style="font-size: 0.8em;">
|
|
Relatório gerado automaticamente pelo Sistema de Gestão de Recreação.
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./static/manifest.json
|
|
==================================================================
|
|
{
|
|
"name": "CRM Dente de leão",
|
|
"short_name": "Recreação",
|
|
"start_url": "/web",
|
|
"display": "standalone",
|
|
"background_color": "#ffffff",
|
|
"theme_color": "#714B67",
|
|
"scope": "/",
|
|
"icons": [
|
|
{
|
|
"src": "/plugin_recre/static/img/icon_192.png",
|
|
"sizes": "192x192",
|
|
"type": "image/png"
|
|
},
|
|
{
|
|
"src": "/plugin_recre/static/img/icon_512.png",
|
|
"sizes": "512x512",
|
|
"type": "image/png"
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./wizard/financeiro_wizard.py
|
|
==================================================================
|
|
from odoo import models, fields, api
|
|
|
|
class FinanceiroRelatorioWizard(models.TransientModel):
|
|
_name = 'recreacao.financeiro.wizard'
|
|
_description = 'Wizard de Relatório Financeiro'
|
|
|
|
data_inicio = fields.Date(string='Data Inicial', required=True, default=lambda self: fields.Date.today().replace(day=1))
|
|
data_fim = fields.Date(string='Data Final', required=True, default=fields.Date.today())
|
|
|
|
def action_gerar_pdf(self):
|
|
# Passa as datas para o relatório
|
|
data = {
|
|
'data_inicio': self.data_inicio,
|
|
'data_fim': self.data_fim,
|
|
}
|
|
return self.env.ref('plugin_recre.action_report_financeiro_mensal').report_action(self, data=data)
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./wizard/__init__.py
|
|
==================================================================
|
|
from . import financeiro_wizard
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./security/ir.model.access.csv
|
|
==================================================================
|
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
access_recreacao_plano,recreacao.plano,model_recreacao_plano,base.group_user,1,1,1,1
|
|
access_recreacao_diario,recreacao.diario,model_recreacao_diario,base.group_user,1,1,1,1
|
|
access_recreacao_crianca,recreacao.crianca,model_recreacao_crianca,base.group_user,1,1,1,1
|
|
access_recreacao_financeiro,recreacao.financeiro,model_recreacao_financeiro,base.group_user,1,1,1,1
|
|
access_recreacao_financeiro_wizard,recreacao.financeiro.wizard,model_recreacao_financeiro_wizard,base.group_user,1,1,1,1
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/financeiro_wizard_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<record id="view_recreacao_financeiro_wizard_form" model="ir.ui.view">
|
|
<field name="name">recreacao.financeiro.wizard.form</field>
|
|
<field name="model">recreacao.financeiro.wizard</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Relatório Financeiro Mensal">
|
|
<group>
|
|
<group>
|
|
<field name="data_inicio"/>
|
|
</group>
|
|
<group>
|
|
<field name="data_fim"/>
|
|
</group>
|
|
</group>
|
|
<footer>
|
|
<button name="action_gerar_pdf" string="Imprimir PDF" type="object" class="btn-primary"/>
|
|
<button string="Cancelar" class="btn-secondary" special="cancel"/>
|
|
</footer>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_recreacao_financeiro_wizard" model="ir.actions.act_window">
|
|
<field name="name">Relatório Mensal</field>
|
|
<field name="res_model">recreacao.financeiro.wizard</field>
|
|
<field name="view_mode">form</field>
|
|
<field name="target">new</field>
|
|
</record>
|
|
|
|
<!-- Adiciona o botão no menu Financeiro -->
|
|
<menuitem id="menu_recreacao_relatorio"
|
|
name="Relatórios"
|
|
parent="menu_recreacao_root"
|
|
sequence="20"/>
|
|
|
|
<menuitem id="menu_recreacao_relatorio_mensal"
|
|
name="Fluxo de Caixa (PDF)"
|
|
parent="menu_recreacao_relatorio"
|
|
action="action_recreacao_financeiro_wizard"/>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/web_layout.xml
|
|
==================================================================
|
|
<odoo>
|
|
<template id="custom_favicon_backend" inherit_id="web.layout" name="Custom Favicon & Manifest">
|
|
<xpath expr="//head" position="inside">
|
|
<!-- Ícones de navegador -->
|
|
<link rel="shortcut icon" href="/plugin_recre/static/img/favicon.ico" type="image/x-icon"/>
|
|
<link rel="icon" href="/plugin_recre/static/img/favicon.ico" type="image/x-icon"/>
|
|
|
|
<!-- O SEGREDO DO ANDROID: Link para o nosso JSON -->
|
|
<link rel="manifest" href="/plugin_recre/static/manifest.json"/>
|
|
|
|
<!-- Meta tag para pintar a barra do navegador do celular -->
|
|
<meta name="theme-color" content="#714B67"/>
|
|
</xpath>
|
|
</template>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/crianca_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<!-- KANBAN -->
|
|
<record id="view_recreacao_crianca_kanban" model="ir.ui.view">
|
|
<field name="name">recreacao.crianca.kanban</field>
|
|
<field name="model">recreacao.crianca</field>
|
|
<field name="arch" type="xml">
|
|
<kanban class="o_kanban_mobile">
|
|
<field name="id"/>
|
|
<field name="name"/>
|
|
<field name="foto"/>
|
|
<field name="status_dia"/>
|
|
<field name="tem_alergia"/>
|
|
<field name="plano_id"/>
|
|
<field name="idade"/>
|
|
<templates>
|
|
<t t-name="kanban-box">
|
|
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban">
|
|
<!-- Foto -->
|
|
<t t-if="record.foto.raw_value">
|
|
<div class="o_kanban_image">
|
|
<img t-att-src="kanban_image('recreacao.crianca', 'foto', record.id.raw_value)" alt="Foto"/>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="o_kanban_image">
|
|
<img alt="Sem Foto" src="/base/static/img/avatar_grey.png"/>
|
|
</div>
|
|
</t>
|
|
|
|
<div class="oe_kanban_details">
|
|
<div class="o_kanban_record_top">
|
|
<div class="o_kanban_record_headings">
|
|
<strong class="o_kanban_record_title"><field name="name"/></strong>
|
|
</div>
|
|
<div class="o_dropdown_kanban dropdown">
|
|
<a class="dropdown-toggle o-no-caret btn" role="button" data-bs-toggle="dropdown" href="#" aria-label="Dropdown">
|
|
<span class="fa fa-ellipsis-v"/>
|
|
</a>
|
|
<div class="dropdown-menu" role="menu">
|
|
<a role="menuitem" type="edit" class="dropdown-item">Editar</a>
|
|
<a role="menuitem" type="object" name="action_abrir_historico" class="dropdown-item">Ver Diário</a>
|
|
<!-- CORRIGIDO AQUI: Nome da nova função -->
|
|
<a role="menuitem" type="object" name="action_gerar_cobranca" class="dropdown-item">Gerar Cobrança</a>
|
|
<a role="menuitem" type="delete" class="dropdown-item text-danger">Excluir</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ul>
|
|
<li><span class="text-muted"><field name="idade"/></span></li>
|
|
<li><span class="badge text-bg-light"><field name="plano_id"/></span></li>
|
|
<li t-if="record.tem_alergia.raw_value">
|
|
<span class="badge text-bg-danger">⚠️ ALÉRGICO</span>
|
|
</li>
|
|
</ul>
|
|
<div class="o_kanban_record_bottom" style="margin-top: 8px;">
|
|
<div class="oe_kanban_bottom_left">
|
|
<button name="action_abrir_whatsapp" type="object" class="btn btn-sm btn-outline-success" style="border-radius: 50%; width: 32px; height: 32px; padding: 0;">
|
|
<i class="fa fa-whatsapp" style="font-size: 18px;"/>
|
|
</button>
|
|
</div>
|
|
<div class="oe_kanban_bottom_right">
|
|
<t t-if="record.status_dia.raw_value == 'ausente'">
|
|
<button name="action_marcar_entrada" type="object" class="btn btn-primary btn-sm">
|
|
<i class="fa fa-sign-in"/> Entrada
|
|
</button>
|
|
</t>
|
|
<t t-if="record.status_dia.raw_value == 'presente'">
|
|
<button name="action_marcar_saida" type="object" class="btn btn-danger btn-sm">
|
|
<i class="fa fa-sign-out"/> Saída
|
|
</button>
|
|
</t>
|
|
<t t-if="record.status_dia.raw_value == 'finalizado'">
|
|
<span class="badge rounded-pill text-bg-secondary">
|
|
<i class="fa fa-home"/> JÁ SAIU
|
|
</span>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</templates>
|
|
</kanban>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- LISTA GERAL -->
|
|
<record id="view_recreacao_crianca_tree" model="ir.ui.view">
|
|
<field name="name">recreacao.crianca.tree</field>
|
|
<field name="model">recreacao.crianca</field>
|
|
<field name="arch" type="xml">
|
|
<tree>
|
|
<field name="name"/>
|
|
<field name="idade"/>
|
|
<field name="responsavel_financeiro_id"/>
|
|
<field name="plano_id"/>
|
|
<field name="status_dia" widget="badge"
|
|
decoration-success="status_dia == 'presente'"
|
|
decoration-muted="status_dia == 'finalizado'"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- FORMULÁRIO -->
|
|
<record id="view_recreacao_crianca_form" model="ir.ui.view">
|
|
<field name="name">recreacao.crianca.form</field>
|
|
<field name="model">recreacao.crianca</field>
|
|
<field name="arch" type="xml">
|
|
<form>
|
|
<header>
|
|
<button name="action_marcar_entrada" string="Registrar Entrada" type="object" class="oe_highlight" invisible="status_dia != 'ausente'"/>
|
|
<button name="action_marcar_saida" string="Registrar Saída" type="object" class="btn-danger" invisible="status_dia != 'presente'"/>
|
|
|
|
<!-- CORRIGIDO AQUI: Botão novo -->
|
|
<button name="action_gerar_cobranca" string="Gerar Cobrança" type="object" class="btn-primary"/>
|
|
|
|
<field name="status_dia" widget="statusbar"/>
|
|
</header>
|
|
<sheet>
|
|
<field name="foto" widget="image" class="oe_avatar"/>
|
|
<div class="oe_title">
|
|
<h1><field name="name" placeholder="Nome da Criança"/></h1>
|
|
</div>
|
|
<group>
|
|
<group string="Filiação">
|
|
<field name="mae_id" context="{'default_is_company': False}"/>
|
|
<field name="pai_id" context="{'default_is_company': False}"/>
|
|
<field name="responsavel_financeiro_id"/>
|
|
</group>
|
|
<group string="Plano">
|
|
<field name="plano_id"/>
|
|
<field name="valor_plano"/>
|
|
<field name="data_nascimento"/>
|
|
<field name="idade"/>
|
|
</group>
|
|
</group>
|
|
|
|
<div class="alert alert-danger" role="alert" invisible="not tem_alergia and not toma_remedio">
|
|
<strong>ATENÇÃO:</strong> Criança com restrições médicas!
|
|
</div>
|
|
|
|
<notebook>
|
|
<page string="🏥 Saúde">
|
|
<group>
|
|
<group>
|
|
<field name="tem_alergia"/>
|
|
<field name="alergias" invisible="not tem_alergia"/>
|
|
</group>
|
|
<group>
|
|
<field name="toma_remedio"/>
|
|
<field name="medicamentos" invisible="not toma_remedio"/>
|
|
<field name="horario_medicacao" invisible="not toma_remedio"/>
|
|
</group>
|
|
</group>
|
|
<field name="observacoes_saude"/>
|
|
</page>
|
|
|
|
<page string="Histórico / Diário">
|
|
<field name="diario_ids">
|
|
<tree editable="bottom" default_order="data desc, create_date desc"
|
|
decoration-success="tipo=='entrada'"
|
|
decoration-danger="tipo=='saida'">
|
|
<field name="data"/>
|
|
<field name="hora_registro"/>
|
|
<field name="tipo"/>
|
|
<field name="descricao"/>
|
|
</tree>
|
|
</field>
|
|
</page>
|
|
|
|
<!-- NOVO: Aba Financeiro -->
|
|
<page string="💲 Financeiro">
|
|
<field name="financeiro_ids" readonly="1">
|
|
<tree decoration-success="status=='pago'" decoration-danger="status=='pendente'">
|
|
<field name="data_vencimento"/>
|
|
<field name="name"/>
|
|
<field name="valor"/>
|
|
<field name="status" widget="badge"/>
|
|
</tree>
|
|
</field>
|
|
</page>
|
|
|
|
</notebook>
|
|
</sheet>
|
|
<!-- CHATTER (Rodapé para mensagens) -->
|
|
<div class="oe_chatter">
|
|
<field name="message_follower_ids"/>
|
|
<field name="message_ids"/>
|
|
</div>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_recreacao_crianca" model="ir.actions.act_window">
|
|
<field name="name">Alunos</field>
|
|
<field name="res_model">recreacao.crianca</field>
|
|
<field name="view_mode">kanban,tree,form</field>
|
|
</record>
|
|
<menuitem id="menu_recreacao_crianca" name="Painel de Alunos" parent="menu_recreacao_root" action="action_recreacao_crianca" sequence="1"/>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/plano_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<record id="view_recreacao_plano_tree" model="ir.ui.view">
|
|
<field name="name">recreacao.plano.tree</field>
|
|
<field name="model">recreacao.plano</field>
|
|
<field name="arch" type="xml">
|
|
<tree editable="bottom">
|
|
<field name="name"/>
|
|
<field name="tipo_cobranca"/> <!-- Campo Novo -->
|
|
<field name="valor"/>
|
|
<field name="horario_inicio" widget="float_time"/>
|
|
<field name="horario_fim" widget="float_time"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_recreacao_plano" model="ir.actions.act_window">
|
|
<field name="name">Planos e Preços</field>
|
|
<field name="res_model">recreacao.plano</field>
|
|
<field name="view_mode">tree,form</field>
|
|
</record>
|
|
|
|
<menuitem id="menu_recreacao_root" name="Recreação" web_icon="contacts,static/description/icon.png"/>
|
|
<menuitem id="menu_recreacao_config" name="Configurações" parent="menu_recreacao_root" sequence="100"/>
|
|
<menuitem id="menu_recreacao_plano" name="Planos" parent="menu_recreacao_config" action="action_recreacao_plano"/>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/res_partner_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<record id="view_partner_form_recreacao" model="ir.ui.view">
|
|
<field name="name">res.partner.form.recreacao</field>
|
|
<field name="model">res.partner</field>
|
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
|
<field name="arch" type="xml">
|
|
<notebook position="inside">
|
|
<page string="Filhos na Recreação">
|
|
<field name="crianca_ids">
|
|
<tree>
|
|
<field name="name"/>
|
|
<field name="plano_id"/>
|
|
<field name="idade"/>
|
|
</tree>
|
|
</field>
|
|
</page>
|
|
</notebook>
|
|
</field>
|
|
</record>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/diario_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<!-- LISTA GERAL DO DIÁRIO -->
|
|
<record id="view_recreacao_diario_tree" model="ir.ui.view">
|
|
<field name="name">recreacao.diario.tree</field>
|
|
<field name="model">recreacao.diario</field>
|
|
<field name="arch" type="xml">
|
|
<tree string="Diário de Classe"
|
|
decoration-success="tipo=='entrada'"
|
|
decoration-danger="tipo=='saida'"
|
|
decoration-warning="tipo=='ocorrencia'">
|
|
<field name="data"/>
|
|
<field name="hora_registro"/> <!-- Novo -->
|
|
<field name="crianca_id"/>
|
|
<field name="tipo" widget="badge"/>
|
|
<field name="descricao"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- FORMULÁRIO DO DIÁRIO -->
|
|
<record id="view_recreacao_diario_form" model="ir.ui.view">
|
|
<field name="name">recreacao.diario.form</field>
|
|
<field name="model">recreacao.diario</field>
|
|
<field name="arch" type="xml">
|
|
<form>
|
|
<sheet>
|
|
<group>
|
|
<group>
|
|
<field name="data"/>
|
|
<field name="hora_registro"/>
|
|
<field name="crianca_id" options="{'no_create': True}"/>
|
|
</group>
|
|
<group>
|
|
<field name="tipo"/>
|
|
</group>
|
|
</group>
|
|
<field name="descricao" placeholder="Descreva o que aconteceu..."/>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_recreacao_diario" model="ir.actions.act_window">
|
|
<field name="name">Diário Geral</field>
|
|
<field name="res_model">recreacao.diario</field>
|
|
<field name="view_mode">tree,form</field>
|
|
</record>
|
|
|
|
<menuitem id="menu_recreacao_diario" name="Diário Geral" parent="menu_recreacao_root" action="action_recreacao_diario" sequence="10"/>
|
|
</odoo>
|
|
|
|
|
|
==================================================================
|
|
ARQUIVO: ./views/financeiro_view.xml
|
|
==================================================================
|
|
<odoo>
|
|
<record id="view_recreacao_financeiro_tree" model="ir.ui.view">
|
|
<field name="name">recreacao.financeiro.tree</field>
|
|
<field name="model">recreacao.financeiro</field>
|
|
<field name="arch" type="xml">
|
|
<!-- editable="bottom" permite criar e editar direto na linha, igual Excel -->
|
|
<tree editable="bottom"
|
|
decoration-success="tipo=='receita'"
|
|
decoration-danger="tipo=='despesa'"
|
|
decoration-muted="status=='cancelado'">
|
|
|
|
<field name="data_vencimento"/>
|
|
|
|
<field name="name" placeholder="Ex: Mensalidade João ou Conta de Luz"/>
|
|
|
|
<!-- AQUI: O campo Tipo agora é editável livremente na lista -->
|
|
<field name="tipo" widget="badge"
|
|
decoration-success="tipo=='receita'"
|
|
decoration-danger="tipo=='despesa'"/>
|
|
|
|
<field name="partner_id" widget="res_partner_many2one" optional="show"/>
|
|
<field name="crianca_id" optional="hide"/>
|
|
|
|
<!-- Soma total no rodapé da tabela -->
|
|
<field name="valor" sum="Total Movimentado"/>
|
|
|
|
<field name="status" widget="badge"
|
|
decoration-success="status=='pago'"
|
|
decoration-warning="status=='pendente'"/>
|
|
|
|
<!-- Botão rápido de pagar -->
|
|
<button name="action_pagar" string="Pagar/Receber" type="object"
|
|
icon="fa-check" class="btn-success btn-sm"
|
|
invisible="status != 'pendente'"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- 2. O GRÁFICO -->
|
|
<record id="view_recreacao_financeiro_graph" model="ir.ui.view">
|
|
<field name="name">recreacao.financeiro.graph</field>
|
|
<field name="model">recreacao.financeiro</field>
|
|
<field name="arch" type="xml">
|
|
<graph string="Fluxo de Caixa" type="bar" stacked="True">
|
|
<field name="data_vencimento" type="row" interval="month"/>
|
|
<field name="tipo" type="col"/>
|
|
<field name="valor" type="measure"/>
|
|
</graph>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- 3. FILTROS (SEARCH) -->
|
|
<record id="view_recreacao_financeiro_search" model="ir.ui.view">
|
|
<field name="name">recreacao.financeiro.search</field>
|
|
<field name="model">recreacao.financeiro</field>
|
|
<field name="arch" type="xml">
|
|
<search>
|
|
<field name="name"/>
|
|
<field name="partner_id"/>
|
|
<filter string="Entradas (Receitas)" name="filter_receitas" domain="[('tipo','=','receita')]"/>
|
|
<filter string="Saídas (Despesas)" name="filter_despesas" domain="[('tipo','=','despesa')]"/>
|
|
<separator/>
|
|
<filter string="Pendentes" name="filter_pendentes" domain="[('status','=','pendente')]"/>
|
|
<filter string="Pagos" name="filter_pagos" domain="[('status','=','pago')]"/>
|
|
<group expand="1" string="Agrupar Por">
|
|
<filter string="Mês" name="group_month" context="{'group_by':'data_vencimento:month'}"/>
|
|
<filter string="Tipo" name="group_tipo" context="{'group_by':'tipo'}"/>
|
|
</group>
|
|
</search>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- 4. AÇÃO DA LISTA -->
|
|
<record id="action_recreacao_financeiro" model="ir.actions.act_window">
|
|
<field name="name">Movimentações</field>
|
|
<field name="res_model">recreacao.financeiro</field>
|
|
<field name="view_mode">tree,graph,form</field>
|
|
<field name="help" type="html">
|
|
<p class="o_view_nocontent_smiling_face">
|
|
Nenhuma movimentação encontrada!
|
|
</p>
|
|
<p>
|
|
Crie uma nova despesa manualmente ou gere cobranças através dos alunos.
|
|
</p>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- ======================================================== -->
|
|
<!-- ORGANIZAÇÃO DOS MENUS (AQUI ESTAVA A CONFUSÃO) -->
|
|
<!-- ======================================================== -->
|
|
|
|
<!-- Menu Pai: Financeiro -->
|
|
<menuitem id="menu_recreacao_financeiro_root"
|
|
name="Financeiro"
|
|
parent="menu_recreacao_root"
|
|
sequence="10"/>
|
|
|
|
<!-- Submenu 1: Movimentações (A Lista/Histórico) -->
|
|
<menuitem id="menu_recreacao_financeiro_movimentos"
|
|
name="Movimentações (Caixa)"
|
|
parent="menu_recreacao_financeiro_root"
|
|
action="action_recreacao_financeiro"
|
|
sequence="1"/>
|
|
|
|
<!-- Submenu 2: Relatórios (O PDF) -->
|
|
<menuitem id="menu_recreacao_relatorio_mensal"
|
|
name="Imprimir Fluxo (PDF)"
|
|
parent="menu_recreacao_financeiro_root"
|
|
action="action_recreacao_financeiro_wizard"
|
|
sequence="2"/>
|
|
|
|
</odoo>
|
|
|
|
|