initiale Version von Open Workshop Report
This commit is contained in:
parent
0135035a54
commit
d6a98cfbd0
2
open_workshop_report/__init__.py
Normal file
2
open_workshop_report/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from . import models
|
||||
from . import reports
|
||||
39
open_workshop_report/__manifest__.py
Normal file
39
open_workshop_report/__manifest__.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
'name': 'Open Workshop Reports',
|
||||
'license': 'AGPL-3',
|
||||
'version': '18.0.1.0.0',
|
||||
'summary': 'Nutzungsstatistiken und Reports für Open Workshop',
|
||||
'description': """
|
||||
Open Workshop Reports
|
||||
=====================
|
||||
|
||||
Dieses Modul bietet:
|
||||
- Dashboard mit Nutzungsstatistiken
|
||||
- Pivot-Tabellen und Diagramme für POS Sessions
|
||||
- Monatliche/Historische Auswertungen
|
||||
- PDF-Reports für Team-Meetings
|
||||
""",
|
||||
'depends': [
|
||||
'base',
|
||||
'point_of_sale',
|
||||
'open_workshop_pos',
|
||||
],
|
||||
'author': 'matthias.lotz',
|
||||
'category': 'Point of Sale',
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/cron_data.xml',
|
||||
'views/dashboard_views.xml',
|
||||
'views/usage_stats_views.xml',
|
||||
'reports/usage_report_templates.xml',
|
||||
'reports/usage_reports.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'open_workshop_report/static/src/css/dashboard.css',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
13
open_workshop_report/data/cron_data.xml
Normal file
13
open_workshop_report/data/cron_data.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Cron Job: Generate Monthly Usage Statistics -->
|
||||
<record id="ir_cron_generate_usage_stats" model="ir.cron">
|
||||
<field name="name">Generate Monthly Usage Statistics</field>
|
||||
<field name="model_id" ref="model_open_workshop_usage_stats"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.cron_generate_monthly_stats()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">months</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
</odoo>
|
||||
104
open_workshop_report/generate_stats.py
Normal file
104
open_workshop_report/generate_stats.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Skript zum Generieren von Nutzungsstatistiken aus vorhandenen POS-Sessions
|
||||
Aufruf: docker compose exec odoo-dev python3 /mnt/extra-addons/open_workshop/open_workshop_report/generate_stats.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '/usr/lib/python3/dist-packages')
|
||||
|
||||
import odoo
|
||||
from odoo import api, SUPERUSER_ID
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
# Konfiguration
|
||||
DB_NAME = 'hh18'
|
||||
CONFIG_FILE = '/etc/odoo/odoo-dev.conf'
|
||||
|
||||
# Odoo initialisieren
|
||||
odoo.tools.config.parse_config(['-c', CONFIG_FILE])
|
||||
odoo.cli.server.report_configuration()
|
||||
|
||||
# Registry laden
|
||||
registry = odoo.registry(DB_NAME)
|
||||
|
||||
def generate_monthly_stats():
|
||||
"""Generiert monatliche Statistiken für die letzten 12 Monate"""
|
||||
with registry.cursor() as cr:
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
UsageStats = env['open_workshop.usage.stats']
|
||||
|
||||
# Finde älteste und neueste Session
|
||||
cr.execute("""
|
||||
SELECT MIN(start_at), MAX(stop_at)
|
||||
FROM pos_session
|
||||
WHERE state = 'closed'
|
||||
""")
|
||||
result = cr.fetchone()
|
||||
|
||||
if not result or not result[0]:
|
||||
print("Keine geschlossenen POS-Sessions gefunden!")
|
||||
return
|
||||
|
||||
oldest_date = result[0]
|
||||
newest_date = result[1] or datetime.now()
|
||||
|
||||
print(f"Generiere Statistiken von {oldest_date} bis {newest_date}")
|
||||
|
||||
# Generiere Statistiken für jeden Monat
|
||||
current_date = oldest_date.replace(day=1)
|
||||
end_date = newest_date.replace(day=1)
|
||||
|
||||
stats_created = 0
|
||||
|
||||
while current_date <= end_date:
|
||||
period_start = current_date
|
||||
period_end = (current_date + relativedelta(months=1)) - timedelta(seconds=1)
|
||||
period_name = current_date.strftime('%B %Y')
|
||||
|
||||
# Prüfe ob Statistik bereits existiert
|
||||
existing = UsageStats.search([
|
||||
('period_start', '=', period_start),
|
||||
('period_type', '=', 'month')
|
||||
], limit=1)
|
||||
|
||||
if not existing:
|
||||
# Finde alle Sessions in diesem Monat
|
||||
sessions = env['pos.session'].search([
|
||||
('state', '=', 'closed'),
|
||||
('start_at', '>=', period_start),
|
||||
('start_at', '<=', period_end)
|
||||
])
|
||||
|
||||
if sessions:
|
||||
# Erstelle Statistik
|
||||
UsageStats.create({
|
||||
'period_name': period_name,
|
||||
'period_type': 'month',
|
||||
'period_start': period_start,
|
||||
'period_end': period_end,
|
||||
'session_ids': [(6, 0, sessions.ids)]
|
||||
})
|
||||
stats_created += 1
|
||||
print(f"✓ {period_name}: {len(sessions)} Sessions")
|
||||
else:
|
||||
print(f"- {period_name}: Keine Sessions")
|
||||
else:
|
||||
print(f"→ {period_name}: Bereits vorhanden")
|
||||
|
||||
# Nächster Monat
|
||||
current_date = current_date + relativedelta(months=1)
|
||||
|
||||
cr.commit()
|
||||
print(f"\n✅ {stats_created} Statistik-Einträge erstellt!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
generate_monthly_stats()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
1
open_workshop_report/models/__init__.py
Normal file
1
open_workshop_report/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import usage_stats
|
||||
114
open_workshop_report/models/usage_stats.py
Normal file
114
open_workshop_report/models/usage_stats.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, _
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class UsageStats(models.Model):
|
||||
"""Nutzungsstatistiken für Open Workshop"""
|
||||
_name = 'open_workshop.usage.stats'
|
||||
_description = 'Open Workshop Nutzungsstatistiken'
|
||||
_order = 'period_start desc'
|
||||
_rec_name = 'period_name'
|
||||
|
||||
period_name = fields.Char('Zeitraum', compute='_compute_period_name', store=True)
|
||||
period_start = fields.Date('Beginn', required=True)
|
||||
period_end = fields.Date('Ende', required=True)
|
||||
period_type = fields.Selection([
|
||||
('day', 'Tag'),
|
||||
('week', 'Woche'),
|
||||
('month', 'Monat'),
|
||||
('quarter', 'Quartal'),
|
||||
('year', 'Jahr'),
|
||||
], string='Periodentyp', default='month', required=True)
|
||||
|
||||
# Statistiken
|
||||
total_sessions = fields.Integer('Gesamt Nutzungen', compute='_compute_stats', store=True)
|
||||
total_revenue = fields.Monetary('Gesamtumsatz', compute='_compute_stats', store=True, currency_field='currency_id')
|
||||
avg_per_day = fields.Float('Durchschnitt pro Tag', compute='_compute_stats', store=True, digits=(5, 2))
|
||||
unique_users = fields.Integer('Einzigartige Nutzer', compute='_compute_stats', store=True)
|
||||
|
||||
currency_id = fields.Many2one('res.currency', string='Währung',
|
||||
default=lambda self: self.env.company.currency_id)
|
||||
|
||||
# POS Session IDs für diese Periode
|
||||
session_ids = fields.Many2many('pos.session', string='POS Sessions', compute='_compute_stats', store=True)
|
||||
|
||||
@api.depends('period_start', 'period_end', 'period_type')
|
||||
def _compute_period_name(self):
|
||||
for record in self:
|
||||
if record.period_type == 'month':
|
||||
record.period_name = record.period_start.strftime('%B %Y')
|
||||
elif record.period_type == 'week':
|
||||
record.period_name = f"KW {record.period_start.strftime('%W/%Y')}"
|
||||
elif record.period_type == 'day':
|
||||
record.period_name = record.period_start.strftime('%d.%m.%Y')
|
||||
else:
|
||||
record.period_name = f"{record.period_start.strftime('%d.%m.%Y')} - {record.period_end.strftime('%d.%m.%Y')}"
|
||||
|
||||
@api.depends('period_start', 'period_end')
|
||||
def _compute_stats(self):
|
||||
for record in self:
|
||||
# Konvertiere Date zu Datetime für die Suche
|
||||
start_datetime = datetime.combine(record.period_start, datetime.min.time())
|
||||
end_datetime = datetime.combine(record.period_end, datetime.max.time())
|
||||
|
||||
# Suche alle POS Sessions in diesem Zeitraum
|
||||
sessions = self.env['pos.session'].search([
|
||||
('start_at', '>=', start_datetime),
|
||||
('start_at', '<=', end_datetime),
|
||||
('state', 'in', ['closed', 'closing_control']),
|
||||
])
|
||||
|
||||
record.session_ids = sessions
|
||||
record.total_sessions = len(sessions)
|
||||
record.total_revenue = sum(sessions.mapped('total_payments_amount'))
|
||||
|
||||
# Berechne Durchschnitt pro Tag
|
||||
days = (record.period_end - record.period_start).days + 1
|
||||
record.avg_per_day = record.total_sessions / days if days > 0 else 0.0
|
||||
|
||||
# Einzigartige Nutzer (über POS Orders)
|
||||
orders = self.env['pos.order'].search([
|
||||
('session_id', 'in', sessions.ids),
|
||||
('partner_id', '!=', False),
|
||||
])
|
||||
record.unique_users = len(orders.mapped('partner_id'))
|
||||
|
||||
@api.model
|
||||
def generate_monthly_stats(self, year=None, month=None):
|
||||
"""Generiere Statistiken für einen bestimmten Monat"""
|
||||
if not year:
|
||||
year = datetime.now().year
|
||||
if not month:
|
||||
month = datetime.now().month
|
||||
|
||||
# Berechne Start und Ende des Monats
|
||||
period_start = datetime(year, month, 1).date()
|
||||
if month == 12:
|
||||
period_end = datetime(year + 1, 1, 1).date() - timedelta(days=1)
|
||||
else:
|
||||
period_end = datetime(year, month + 1, 1).date() - timedelta(days=1)
|
||||
|
||||
# Suche oder erstelle Statistik-Eintrag
|
||||
stats = self.search([
|
||||
('period_start', '=', period_start),
|
||||
('period_end', '=', period_end),
|
||||
('period_type', '=', 'month'),
|
||||
], limit=1)
|
||||
|
||||
if not stats:
|
||||
stats = self.create({
|
||||
'period_start': period_start,
|
||||
'period_end': period_end,
|
||||
'period_type': 'month',
|
||||
})
|
||||
|
||||
return stats
|
||||
|
||||
@api.model
|
||||
def cron_generate_monthly_stats(self):
|
||||
"""Cron-Job: Generiere Statistiken für letzten Monat"""
|
||||
today = datetime.now()
|
||||
last_month = today - relativedelta(months=1)
|
||||
self.generate_monthly_stats(last_month.year, last_month.month)
|
||||
2
open_workshop_report/reports/__init__.py
Normal file
2
open_workshop_report/reports/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Reports are loaded via XML data files
|
||||
121
open_workshop_report/reports/usage_report_templates.xml
Normal file
121
open_workshop_report/reports/usage_report_templates.xml
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Main Report Template -->
|
||||
<template id="usage_report_document">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<!-- Header -->
|
||||
<div class="text-center mb-4">
|
||||
<h2>Nutzungsstatistik Hobbyhimmel</h2>
|
||||
<h3 t-field="o.period_name"/>
|
||||
<p class="text-muted">
|
||||
<span t-field="o.period_start"/> bis <span t-field="o.period_end"/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- KPI Summary Boxes -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="text-primary"><t t-esc="o.total_sessions"/></h1>
|
||||
<p class="text-muted">Gesamt Nutzungen</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="text-success"><t t-esc="'%.1f' % o.avg_per_day"/></h1>
|
||||
<p class="text-muted">⌀ pro Tag</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="text-info"><t t-esc="o.unique_users"/></h1>
|
||||
<p class="text-muted">Einz. Nutzer</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="text-warning">
|
||||
<t t-esc="'%.2f' % o.total_revenue"/>
|
||||
<t t-esc="o.currency_id.symbol"/>
|
||||
</h1>
|
||||
<p class="text-muted">Umsatz</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart Placeholder -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h4>Nutzungsverlauf</h4>
|
||||
<div class="alert alert-info">
|
||||
<i class="fa fa-info-circle"/>
|
||||
Für detaillierte Diagramme öffnen Sie bitte die Dashboard-Ansicht in Odoo.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sessions Table -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>POS Sessions Detail</h4>
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th>Session</th>
|
||||
<th>Start</th>
|
||||
<th>Ende</th>
|
||||
<th>Benutzer</th>
|
||||
<th class="text-end">Umsatz</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="o.session_ids" t-as="session">
|
||||
<td><span t-field="session.name"/></td>
|
||||
<td><span t-field="session.start_at" t-options="{'widget': 'datetime'}"/></td>
|
||||
<td><span t-field="session.stop_at" t-options="{'widget': 'datetime'}"/></td>
|
||||
<td><span t-field="session.user_id"/></td>
|
||||
<td class="text-end">
|
||||
<span t-field="session.total_payments_amount"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="4" class="text-end">Gesamt:</th>
|
||||
<th class="text-end">
|
||||
<span t-field="o.total_revenue"
|
||||
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-center text-muted">
|
||||
<small>
|
||||
Generiert am <span t-esc="context_timestamp(datetime.datetime.now()).strftime('%d.%m.%Y %H:%M')"/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
13
open_workshop_report/reports/usage_reports.xml
Normal file
13
open_workshop_report/reports/usage_reports.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- PDF Report Definition -->
|
||||
<record id="action_usage_report_pdf" model="ir.actions.report">
|
||||
<field name="name">Nutzungsstatistik PDF</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">open_workshop_report.usage_report_document</field>
|
||||
<field name="report_file">open_workshop_report.usage_report_document</field>
|
||||
<field name="binding_model_id" ref="model_open_workshop_usage_stats"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
3
open_workshop_report/security/ir.model.access.csv
Normal file
3
open_workshop_report/security/ir.model.access.csv
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_usage_stats_user,access.usage.stats.user,model_open_workshop_usage_stats,base.group_user,1,0,0,0
|
||||
access_usage_stats_manager,access.usage.stats.manager,model_open_workshop_usage_stats,point_of_sale.group_pos_manager,1,1,1,1
|
||||
|
66
open_workshop_report/static/src/css/dashboard.css
Normal file
66
open_workshop_report/static/src/css/dashboard.css
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* Dashboard Styling for Open Workshop Usage Stats */
|
||||
|
||||
.o_dashboard {
|
||||
background: #f9f9f9;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate .o_value {
|
||||
font-size: 2.5em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate .o_label {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate.total_sessions .o_value {
|
||||
color: #0066cc;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate.avg_per_day .o_value {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate.unique_users .o_value {
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
.o_dashboard .o_aggregate.total_revenue .o_value {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.o_dashboard .o_graph_view,
|
||||
.o_dashboard .o_pivot_view {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.o_dashboard h3 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #0066cc;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
69
open_workshop_report/views/dashboard_views.xml
Normal file
69
open_workshop_report/views/dashboard_views.xml
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Kanban Dashboard View -->
|
||||
<record id="view_usage_dashboard" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.dashboard</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_dashboard">
|
||||
<field name="total_sessions"/>
|
||||
<field name="avg_per_day"/>
|
||||
<field name="unique_users"/>
|
||||
<field name="total_revenue"/>
|
||||
<field name="period_name"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click">
|
||||
<div class="o_kanban_record_top">
|
||||
<strong><field name="period_name"/></strong>
|
||||
</div>
|
||||
<div class="o_kanban_record_body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="text-muted">Nutzungen</div>
|
||||
<strong><field name="total_sessions"/></strong>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="text-muted">⌀/Tag</div>
|
||||
<strong><field name="avg_per_day"/></strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-6">
|
||||
<div class="text-muted">Nutzer</div>
|
||||
<strong><field name="unique_users"/></strong>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="text-muted">Umsatz</div>
|
||||
<strong><field name="total_revenue" widget="monetary"/></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Dashboard Action -->
|
||||
<record id="action_usage_dashboard" model="ir.actions.act_window">
|
||||
<field name="name">Nutzungs-Dashboard</field>
|
||||
<field name="res_model">open_workshop.usage.stats</field>
|
||||
<field name="view_mode">kanban,graph,pivot</field>
|
||||
<field name="view_id" ref="view_usage_dashboard"/>
|
||||
</record>
|
||||
|
||||
<!-- Submenu unter Point of Sale > Berichtswesen -->
|
||||
<menuitem id="menu_open_workshop_reports"
|
||||
name="Open Workshop"
|
||||
parent="point_of_sale.menu_point_rep"
|
||||
sequence="100"/>
|
||||
|
||||
<!-- Dashboard Menu -->
|
||||
<menuitem id="menu_usage_dashboard"
|
||||
name="Nutzungs-Dashboard"
|
||||
parent="menu_open_workshop_reports"
|
||||
action="action_usage_dashboard"
|
||||
sequence="1"/>
|
||||
</odoo>
|
||||
123
open_workshop_report/views/usage_stats_views.xml
Normal file
123
open_workshop_report/views/usage_stats_views.xml
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Tree View -->
|
||||
<record id="view_usage_stats_tree" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.stats.tree</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Nutzungsstatistiken" create="false">
|
||||
<field name="period_name"/>
|
||||
<field name="period_start"/>
|
||||
<field name="period_end"/>
|
||||
<field name="total_sessions" string="Gesamt Nutzungen"/>
|
||||
<field name="avg_per_day" string="⌀ pro Tag"/>
|
||||
<field name="unique_users" string="Einz. Nutzer"/>
|
||||
<field name="total_revenue" widget="monetary"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Form View -->
|
||||
<record id="view_usage_stats_form" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.stats.form</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Nutzungsstatistik" create="false">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1><field name="period_name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Zeitraum">
|
||||
<field name="period_type"/>
|
||||
<field name="period_start"/>
|
||||
<field name="period_end"/>
|
||||
</group>
|
||||
<group string="Statistiken">
|
||||
<field name="total_sessions"/>
|
||||
<field name="avg_per_day"/>
|
||||
<field name="unique_users"/>
|
||||
<field name="total_revenue"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="POS Sessions">
|
||||
<field name="session_ids" readonly="1">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="start_at"/>
|
||||
<field name="stop_at"/>
|
||||
<field name="user_id"/>
|
||||
<field name="total_payments_amount" widget="monetary"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Pivot View -->
|
||||
<record id="view_usage_stats_pivot" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.stats.pivot</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Nutzungsstatistiken" sample="1">
|
||||
<field name="period_start" type="row" interval="month"/>
|
||||
<field name="period_type" type="col"/>
|
||||
<field name="total_sessions" type="measure"/>
|
||||
<field name="avg_per_day" type="measure"/>
|
||||
<field name="total_revenue" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Graph View - Bar Chart -->
|
||||
<record id="view_usage_stats_graph_bar" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.stats.graph.bar</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Nutzungen pro Monat" type="bar" sample="1" stacked="0">
|
||||
<field name="period_start" interval="month"/>
|
||||
<field name="total_sessions" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Graph View - Line Chart (Historie) -->
|
||||
<record id="view_usage_stats_graph_line" model="ir.ui.view">
|
||||
<field name="name">open_workshop.usage.stats.graph.line</field>
|
||||
<field name="model">open_workshop.usage.stats</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Nutzungen (Historie)" type="line" sample="1">
|
||||
<field name="period_start" interval="month"/>
|
||||
<field name="total_sessions" type="measure"/>
|
||||
<field name="avg_per_day" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action -->
|
||||
<record id="action_usage_stats" model="ir.actions.act_window">
|
||||
<field name="name">Nutzungsstatistiken</field>
|
||||
<field name="res_model">open_workshop.usage.stats</field>
|
||||
<field name="view_mode">graph,pivot,list,form</field>
|
||||
<field name="context">{'group_by': ['period_start:month']}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Keine Statistiken vorhanden
|
||||
</p>
|
||||
<p>
|
||||
Klicken Sie auf "Statistiken generieren" um monatliche Nutzungsstatistiken zu erstellen.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Menu -->
|
||||
<menuitem id="menu_usage_stats"
|
||||
name="Nutzungsstatistiken"
|
||||
parent="menu_open_workshop_reports"
|
||||
action="action_usage_stats"
|
||||
sequence="10"/>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user