Revision b251cc22
Von Sven Schöling vor mehr als 9 Jahren hinzugefügt
SL/Layout/Admin.pm | ||
---|---|---|
sub init_sub_layouts {
|
||
[
|
||
SL::Layout::None->new,
|
||
SL::Layout::CssMenu->new(menu => Menu->new('menus/admin.ini')),
|
||
SL::Layout::CssMenu->new(menu => SL::Menu->new('admin')),
|
||
]
|
||
}
|
||
|
SL/Layout/Base.pm | ||
---|---|---|
}
|
||
|
||
sub init_menu {
|
||
my @menu_files = qw(menus/erp.ini);
|
||
unshift @menu_files, 'menus/crm.ini' if $::instance_conf->crm_installed;
|
||
Menu->new(@menu_files);
|
||
SL::Menu->new('user');
|
||
}
|
||
|
||
sub init_auto_reload_resources_param {
|
SL/Layout/CssMenu.pm | ||
---|---|---|
use strict;
|
||
use parent qw(SL::Layout::Base);
|
||
|
||
use URI;
|
||
|
||
sub print_menu {
|
||
my ($self, $parent, $depth) = @_;
|
||
|
||
my $html;
|
||
|
||
die if ($depth * 1 > 5);
|
||
|
||
my @menuorder;
|
||
my $menu = $self->menu;
|
||
|
||
@menuorder = $menu->access_control(\%::myconfig, $parent);
|
||
|
||
$parent .= "--" if ($parent);
|
||
|
||
foreach my $item (@menuorder) {
|
||
substr($item, 0, length($parent)) = "";
|
||
next if (($item eq "") || ($item =~ /--/));
|
||
|
||
my $menu_item = $menu->{"${parent}${item}"};
|
||
my $menu_title = $::locale->text($item);
|
||
my $menu_text = $menu_title;
|
||
|
||
if ($menu_item->{"submenu"} || !defined($menu_item->{"module"}) && !defined($menu_item->{href})) {
|
||
|
||
my $h = $self->print_menu("${parent}${item}", $depth * 1 + 1)."\n";
|
||
if (!$parent) {
|
||
$html .= qq|<ul><li><h2>${menu_text}</h2><ul>${h}</ul></li></ul>\n|;
|
||
} else {
|
||
$html .= qq|<li><div class="x">${menu_text}</div><ul>${h}</ul></li>\n|;
|
||
}
|
||
} else {
|
||
if ($self->{sub_class} && $depth > 1) {
|
||
$html .= qq|<li class='sub'>|;
|
||
} else {
|
||
$html .= qq|<li>|;
|
||
}
|
||
$html .= $self->menuitem_v3("${parent}$item", { "title" => $menu_title });
|
||
$html .= qq|${menu_text}</a></li>\n|;
|
||
}
|
||
}
|
||
|
||
return $html;
|
||
}
|
||
|
||
sub menuitem_v3 {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my ($self, $item, $other) = @_;
|
||
my $menuitem = $self->menu->{$item};
|
||
|
||
my $action = "section_menu";
|
||
my $module;
|
||
|
||
if ($menuitem->{module}) {
|
||
$module = $menuitem->{module};
|
||
}
|
||
if ($menuitem->{action}) {
|
||
$action = $menuitem->{action};
|
||
}
|
||
|
||
my $level = $::form->escape($item);
|
||
|
||
my @vars;
|
||
my $target = $menuitem->{target} ? qq| target="| . $::form->escape($menuitem->{target}) . '"' : '';
|
||
my $str = qq|<a${target} href="|;
|
||
|
||
if ($menuitem->{href}) {
|
||
$main::lxdebug->leave_sub();
|
||
return $str . $menuitem->{href} . '">';
|
||
}
|
||
|
||
$str .= qq|$module?action=| . $::form->escape($action);
|
||
|
||
map { delete $menuitem->{$_} } qw(module action target href);
|
||
|
||
# add other params
|
||
foreach my $key (keys %{ $menuitem }) {
|
||
$str .= "&" . $::form->escape($key, 1) . "=";
|
||
my ($value, $conf) = split(/=/, $menuitem->{$key}, 2);
|
||
$value = $::myconfig{$value} . "/$conf" if ($conf);
|
||
$str .= $::form->escape($value, 1);
|
||
}
|
||
|
||
$str .= '"';
|
||
|
||
if ($other) {
|
||
foreach my $key (keys(%{$other})) {
|
||
$str .= qq| ${key}="| . $::form->quote($other->{$key}) . qq|"|;
|
||
}
|
||
}
|
||
|
||
$str .= ">";
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return $str;
|
||
}
|
||
|
||
sub use_stylesheet {
|
||
qw(icons16.css frame_header/header.css),
|
||
}
|
||
|
||
sub pre_content {
|
||
$_[0]->presenter->render('menu/menuv3',
|
||
menu => $_[0]->print_menu,
|
||
);
|
||
$_[0]->presenter->render('menu/menuv3', menu => $_[0]->menu);
|
||
}
|
||
|
||
1;
|
SL/Layout/Javascript.pm | ||
---|---|---|
$self->SUPER::use_javascript(@_);
|
||
}
|
||
|
||
sub javascripts_inline {
|
||
$_[0]->SUPER::javascripts_inline,
|
||
<<'EOJS'
|
||
DHTMLSuite.createStandardObjects();
|
||
DHTMLSuite.configObj.setImagePath('image/dhtmlsuite/');
|
||
var menu_model = new DHTMLSuite.menuModel();
|
||
menu_model.addItemsFromMarkup('main_menu_model');
|
||
menu_model.init();
|
||
var menu_bar = new DHTMLSuite.menuBar();
|
||
menu_bar.addMenuItems(menu_model);
|
||
menu_bar.setTarget('main_menu_div');
|
||
menu_bar.init();
|
||
EOJS
|
||
}
|
||
|
||
sub pre_content {
|
||
$_[0]->SUPER::pre_content .
|
||
&display
|
||
$_[0]->presenter->render("menu/menunew",
|
||
force_ul_width => 1,
|
||
menu => $_[0]->menu,
|
||
icon_path => sub { my $img = "image/icons/16x16/$_[0].png"; -f $img ? $img : () },
|
||
max_width => sub { 10 * max map { length $::locale->text($_->{name}) } @{ $_[0]{children} || [] } },
|
||
);
|
||
}
|
||
|
||
sub start_content {
|
||
... | ... | |
$_[0]->SUPER::stylesheets;
|
||
}
|
||
|
||
sub display {
|
||
my ($self) = @_;
|
||
|
||
$self->presenter->render("menu/menunew",
|
||
force_ul_width => 1,
|
||
menu_items => $self->acc_menu,
|
||
);
|
||
}
|
||
|
||
sub acc_menu {
|
||
my ($self) = @_;
|
||
|
||
my $menu = $self->menu;
|
||
|
||
my $all_items = [];
|
||
$self->create_menu($menu, $all_items);
|
||
|
||
my $item = { 'subitems' => $all_items };
|
||
calculate_width($item);
|
||
|
||
return $all_items;
|
||
}
|
||
|
||
sub calculate_width {
|
||
my $item = shift;
|
||
|
||
$item->{max_width} = max map { length $_->{title} } @{ $item->{subitems} };
|
||
|
||
foreach my $subitem (@{ $item->{subitems} }) {
|
||
calculate_width($subitem) if ($subitem->{subitems});
|
||
}
|
||
}
|
||
|
||
sub create_menu {
|
||
my ($self, $menu, $all_items, $parent, $depth) = @_;
|
||
my $html;
|
||
|
||
my $form = $main::form;
|
||
my %myconfig = %main::myconfig;
|
||
|
||
$depth ||= 0;
|
||
|
||
die if ($depth * 1 > 5);
|
||
|
||
my @menuorder = $menu->access_control(\%myconfig, $parent);
|
||
$parent .= "--" if ($parent);
|
||
$parent ||= '';
|
||
|
||
foreach my $name (@menuorder) {
|
||
substr($name, 0, length($parent), "");
|
||
next if (($name eq "") || ($name =~ /--/));
|
||
|
||
my $menu_item = $menu->{"${parent}${name}"};
|
||
my $item = { 'title' => $::locale->text($name) };
|
||
push @{ $all_items }, $item;
|
||
|
||
if ($menu_item->{submenu} || (!defined($menu_item->{module}) && !defined($menu_item->{href}))) {
|
||
$item->{subitems} = [];
|
||
$item->{image} = _icon_path($menu_item->{ICON});
|
||
$self->create_menu($menu, $item->{subitems}, "${parent}${name}", $depth * 1 + 1);
|
||
|
||
} else {
|
||
$item->{image} = _icon_path("${parent}${name}.png");
|
||
$menu->menuitem_new("${parent}${name}", $item);
|
||
}
|
||
}
|
||
}
|
||
|
||
sub _icon_path {
|
||
my ($label, $size) = @_;
|
||
|
||
$size ||= 16;
|
||
|
||
my $img = "image/icons/${size}x${size}/$label.png";
|
||
|
||
return unless -f $img;
|
||
return $img;
|
||
}
|
||
|
||
1;
|
SL/Layout/MenuLeft.pm | ||
---|---|---|
|
||
sub section_menu {
|
||
my ($menu) = @_;
|
||
my @menuorder = @{ $menu->{ORDER} };
|
||
my @items;
|
||
my @id_stack = (-1);
|
||
|
||
for my $item (@menuorder) {
|
||
my $menuitem = $menu->{$item};
|
||
my $olabel = apply { s/.*--// } $item;
|
||
my $ml = apply { s/--.*// } $item;
|
||
my $icon_class = apply { $_ = lc $_; s/[^a-z0-9_-]/-/g } $menuitem->{ICON};
|
||
my $level = (0 + $item =~ s/--/--/g);
|
||
my $spacer = "s" . $level;
|
||
for my $node ($menu->tree_walk) {
|
||
my $level = $node->{level};
|
||
|
||
# do id stack
|
||
push @id_stack, -1 if $level > $#id_stack;
|
||
pop @id_stack while $level < $#id_stack;
|
||
$id_stack[-1]++;
|
||
|
||
my $label = $::locale->text($olabel);
|
||
my $label = $::locale->text($node->{name});
|
||
my $href = $menu->href_for_node($node);
|
||
|
||
$menuitem->{module} ||= $::form->{script};
|
||
$menuitem->{action} ||= "section_menu";
|
||
$menuitem->{href} ||= "$menuitem->{module}?action=$menuitem->{action}";
|
||
my @common_args = ($label, "s" . $level, join '_', @id_stack);
|
||
|
||
# add other params
|
||
foreach my $key (keys %$menuitem) {
|
||
next if $key =~ /target|module|action|href|ICON/;
|
||
$menuitem->{href} .= "&" . $::form->escape($key, 1) . "=";
|
||
my ($value, $conf) = split(/=/, $menuitem->{$key}, 2);
|
||
$value = $::myconfig{$value} . "/$conf" if ($conf);
|
||
$menuitem->{href} .= $::form->escape($value, 1);
|
||
}
|
||
|
||
my @common_args = ($label, $spacer, join '_', @id_stack);
|
||
|
||
if ($spacer eq 's0') { # toplevel
|
||
push @items, [ @common_args, "icon24 $icon_class", 'm' ];
|
||
} elsif ($menuitem->{submenu}) {
|
||
if (!$node->{parent}) { # toplevel
|
||
push @items, [ @common_args, "icon24 $node->{icon}", 'm' ];
|
||
} elsif ($node->{children}) {
|
||
push @items, [ @common_args, "icon16 submenu", 'sm' ];
|
||
} elsif ($menuitem->{module}) {
|
||
push @items, [ @common_args, "icon16 $icon_class", 'i', $menuitem->{href}, $menuitem->{target} ];
|
||
} else {
|
||
push @items, [ @common_args, "icon16 $node->{icon}", 'i', $href, $node->{target} ];
|
||
}
|
||
}
|
||
|
SL/Menu.pm | ||
---|---|---|
#=====================================================================
|
||
# LX-Office ERP
|
||
# Copyright (C) 2004
|
||
# Based on SQL-Ledger Version 2.1.9
|
||
# Web http://www.lx-office.org
|
||
#
|
||
#=====================================================================
|
||
# SQL-Ledger Accounting
|
||
# Copyright (C) 2001
|
||
#
|
||
# Author: Dieter Simader
|
||
# Email: dsimader@sql-ledger.org
|
||
# Web: http://www.sql-ledger.org
|
||
#
|
||
# Contributors:
|
||
#
|
||
# This program is free software; you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation; either version 2 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 General Public License for more details.
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program; if not, write to the Free Software
|
||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
#=====================================================================
|
||
#
|
||
# routines for menu items
|
||
#
|
||
#=====================================================================
|
||
|
||
package Menu;
|
||
package SL::Menu;
|
||
|
||
use strict;
|
||
|
||
use SL::Auth;
|
||
use SL::Inifile;
|
||
|
||
our @ISA = qw(Inifile);
|
||
|
||
use YAML::XS ();
|
||
use File::Spec;
|
||
use SL::MoreCommon qw(uri_encode);
|
||
|
||
sub new {
|
||
$main::lxdebug->enter_sub();
|
||
my ($package, $domain) = @_;
|
||
|
||
my ($package, @menufiles) = @_;
|
||
my $path = File::Spec->catdir('menus', $domain);
|
||
|
||
my $self = $package->SUPER::new($menufiles[0]);
|
||
opendir my $dir, $path or die "can't open $path: $!";
|
||
my @files = sort grep -f "$path/$_", readdir $dir;
|
||
close $dir;
|
||
|
||
for (@menufiles[1..$#menufiles]) {
|
||
my $inifile = Inifile->new($_);
|
||
push @{ $self->{ORDER} }, @{ delete $inifile->{ORDER} };
|
||
$self->{$_} = $inifile->{$_} for keys %$inifile;
|
||
my $nodes = [];
|
||
my $nodes_by_id = {};
|
||
for my $file (@files) {
|
||
my $data = YAML::XS::LoadFile(File::Spec->catfile($path, $file));
|
||
_merge($nodes, $nodes_by_id, $data);
|
||
}
|
||
|
||
$self->set_access;
|
||
|
||
$main::lxdebug->leave_sub();
|
||
my $self = bless {
|
||
nodes => $nodes,
|
||
by_id => $nodes_by_id,
|
||
}, $package;
|
||
|
||
$self->build_tree;
|
||
$self->set_access;
|
||
|
||
return $self;
|
||
}
|
||
|
||
sub menuitem_new {
|
||
$main::lxdebug->enter_sub(LXDebug::DEBUG2());
|
||
sub _merge {
|
||
my ($nodes, $by_id, $data) = @_;
|
||
|
||
my ($self, $name, $item) = @_;
|
||
die 'not an array ref' unless $data && 'ARRAY' eq ref $data; # TODO check this sooner, to get better diag to user
|
||
|
||
my $module = $self->{$name}->{module} || $::form->{script};
|
||
my $action = $self->{$name}->{action};
|
||
for my $node (@$data) {
|
||
my $id = $node->{id};
|
||
|
||
$item->{target} = $self->{$name}->{target} || "main_window";
|
||
$item->{href} = $self->{$name}->{href} || "${module}?action=" . $::form->escape($action);
|
||
my $merge_to = $by_id->{$id};
|
||
|
||
my @vars = qw(module target href);
|
||
push @vars, 'action' unless ($self->{$name}->{href});
|
||
if (!$merge_to) {
|
||
push @$nodes, $node;
|
||
$by_id->{$id} = $node;
|
||
next;
|
||
}
|
||
|
||
map { delete $self->{$name}{$_} } @vars;
|
||
# TODO make this a real recursive merge
|
||
# TODO add support for arrays
|
||
|
||
# merge keys except params
|
||
for my $key (keys %$node) {
|
||
if (ref $node->{$key}) {
|
||
if ('HASH' eq ref $node->{$key}) {
|
||
$merge_to->{$key} = {} if !exists $merge_to->{$key} || 'HASH' ne ref $merge_to->{$key};
|
||
for (keys %{ $node->{params} }) {
|
||
$merge_to->{$key}{$_} = $node->{params}{$_};
|
||
}
|
||
} else {
|
||
die "unsupported structure @{[ ref $node->{$key} ]}";
|
||
}
|
||
} else {
|
||
$merge_to->{$key} = $node->{$key};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# add other params
|
||
foreach my $key (keys %{ $self->{$name} }) {
|
||
my ($value, $conf) = split(m/=/, $self->{$name}->{$key}, 2);
|
||
$value = $::myconfig->{$value} . "/$conf" if ($conf);
|
||
$item->{href} .= "&" . $::form->escape($key) . "=" . $::form->escape($value);
|
||
sub build_tree {
|
||
my ($self) = @_;
|
||
|
||
# first, some sanity check. are all parents valid ids or empty?
|
||
for my $node ($self->nodes) {
|
||
next if !exists $node->{parent} || !$node->{parent} || $self->{by_id}->{$node->{id}};
|
||
die "menu: node $node->{id} has non-existant parent $node->{parent}";
|
||
}
|
||
|
||
$main::lxdebug->leave_sub(LXDebug::DEBUG2());
|
||
}
|
||
my %by_parent;
|
||
# order them by parent
|
||
for my $node ($self->nodes) {
|
||
push @{ $by_parent{ $node->{parent} } //= [] }, $node;
|
||
}
|
||
|
||
sub access_control {
|
||
$main::lxdebug->enter_sub(2);
|
||
my $tree = { };
|
||
$self->{by_id}{''} = $tree;
|
||
|
||
my ($self, $myconfig, $menulevel) = @_;
|
||
|
||
my @menu = ();
|
||
for (keys %by_parent) {
|
||
my $parent = $self->{by_id}{$_};
|
||
$parent->{children} = [ sort { $a->{order} <=> $b->{order} } @{ $by_parent{$_} } ];
|
||
}
|
||
|
||
_set_level_rec($tree->{children}, 0);
|
||
|
||
$self->{tree} = $tree->{children};
|
||
}
|
||
|
||
sub _set_level_rec {
|
||
my ($ary_ref, $level) = @_;
|
||
|
||
if (!$menulevel) {
|
||
@menu = grep { !/--/ } @{ $self->{ORDER} };
|
||
} else {
|
||
@menu = grep { /^${menulevel}--/ } @{ $self->{ORDER} };
|
||
for (@$ary_ref) {
|
||
$_->{level} = $level;
|
||
_set_level_rec($_->{children}, $level + 1) if $_->{children};
|
||
}
|
||
}
|
||
|
||
$main::lxdebug->leave_sub(2);
|
||
sub nodes {
|
||
@{ $_[0]{nodes} }
|
||
}
|
||
|
||
sub tree_walk {
|
||
my ($self, $all) = @_;
|
||
|
||
return @menu;
|
||
_tree_walk_rec($self->{tree}, $all);
|
||
}
|
||
|
||
sub parse_access_string {
|
||
my $self = shift;
|
||
my $key = shift;
|
||
my $access = shift;
|
||
sub _tree_walk_rec {
|
||
my ($ary_ref, $all) = @_;
|
||
map { $_->{children} ? ($_, _tree_walk_rec($_->{children}, $all)) : ($_) } grep { $all || $_->{visible} } @$ary_ref;
|
||
}
|
||
|
||
my $form = $main::form;
|
||
my $auth = $main::auth;
|
||
my $myconfig = \%main::myconfig;
|
||
sub parse_access_string {
|
||
my ($self, $node) = @_;
|
||
|
||
my @stack;
|
||
my $cur_ary = [];
|
||
|
||
push @stack, $cur_ary;
|
||
|
||
while ($access =~ m/^([a-z_]+|\||\&|\(|\)|\s+)/) {
|
||
my $access = $node->{access};
|
||
|
||
while ($access =~ m/^([a-z_\/]+|\||\&|\(|\)|\s+)/) {
|
||
my $token = $1;
|
||
substr($access, 0, length($1)) = "";
|
||
|
||
... | ... | |
} elsif ($token eq ")") {
|
||
pop @stack;
|
||
if (!@stack) {
|
||
$form->error("Error in menu.ini for entry ${key}: missing '('");
|
||
die "Error in menu.ini for entry $node->{id}: missing '('";
|
||
}
|
||
$cur_ary = $stack[-1];
|
||
|
||
... | ... | |
push @{$cur_ary}, $token;
|
||
|
||
} else {
|
||
push @{$cur_ary}, $auth->check_right($::myconfig{login}, $token, 1);
|
||
if ($token =~ m{^ client / (.*) }x) {
|
||
push @{$cur_ary}, $self->parse_instance_conf_string($1);
|
||
} else {
|
||
push @{$cur_ary}, $::auth->check_right($::myconfig{login}, $token, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($access) {
|
||
$form->error("Error in menu.ini for entry ${key}: unrecognized token at the start of '$access'\n");
|
||
die "Error in menu.ini for entry $node->{id}: unrecognized token at the start of '$access'\n";
|
||
}
|
||
|
||
if (1 < scalar @stack) {
|
||
$main::form->error("Error in menu.ini for entry ${key}: Missing ')'\n");
|
||
die "Error in menu.ini for entry $node->{id}: Missing ')'\n";
|
||
}
|
||
|
||
return SL::Auth::evaluate_rights_ary($stack[0]);
|
||
}
|
||
|
||
sub parse_instance_conf_string {
|
||
my ($self, $setting) = @_;
|
||
return $::instance_conf->data->{$setting};
|
||
}
|
||
|
||
sub set_access {
|
||
my $self = shift;
|
||
|
||
my $key;
|
||
|
||
foreach $key (@{ $self->{ORDER} }) {
|
||
my $entry = $self->{$key};
|
||
|
||
$entry->{GRANTED} = $entry->{ACCESS} ? $self->parse_access_string($key, $entry->{ACCESS}) : 1;
|
||
$entry->{GRANTED} &&= $self->parse_instance_conf_string($entry->{INSTANCE_CONF}) if $entry->{INSTANCE_CONF};
|
||
$entry->{IS_MENU} = $entry->{submenu} || ($key !~ m/--/);
|
||
$entry->{NUM_VISIBLE_CHILDREN} = 0;
|
||
|
||
if ($key =~ m/--/) {
|
||
my $parent = $key;
|
||
substr($parent, rindex($parent, '--')) = '';
|
||
$entry->{GRANTED} &&= $self->{$parent}->{GRANTED};
|
||
}
|
||
|
||
$entry->{VISIBLE} = $entry->{GRANTED};
|
||
}
|
||
|
||
foreach $key (reverse @{ $self->{ORDER} }) {
|
||
my $entry = $self->{$key};
|
||
sub href_for_node {
|
||
my ($self, $node) = @_;
|
||
|
||
if ($entry->{IS_MENU}) {
|
||
$entry->{VISIBLE} &&= $entry->{NUM_VISIBLE_CHILDREN} > 0;
|
||
}
|
||
return undef if !$node->{href} && !$node->{module} && !$node->{params};
|
||
|
||
next if (($key !~ m/--/) || !$entry->{VISIBLE});
|
||
my $href = $node->{href} || $node->{module} || 'controller.pl';
|
||
my @tokens;
|
||
|
||
my $parent = $key;
|
||
substr($parent, rindex($parent, '--')) = '';
|
||
$self->{$parent}->{NUM_VISIBLE_CHILDREN}++;
|
||
while (my ($key, $value) = each %{ $node->{params} }) {
|
||
push @tokens, uri_encode($key, 1) . "=" . uri_encode($value, 1);
|
||
}
|
||
|
||
# $self->dump_visible();
|
||
return join '?', $href, grep $_, join '&', @tokens;
|
||
}
|
||
|
||
$self->{ORDER} = [ grep { $self->{$_}->{VISIBLE} } @{ $self->{ORDER} } ];
|
||
sub name_for_node {
|
||
$::locale->text($_[1]{name})
|
||
}
|
||
|
||
{ no strict 'refs';
|
||
# ToDO: fix this. nuke and pave algorithm without type checking screams for problems.
|
||
map { delete @{$self->{$_}}{qw(GRANTED IS_MENU NUM_VISIBLE_CHILDREN VISIBLE ACCESS)} if ($_ ne 'ORDER') } keys %{ $self };
|
||
}
|
||
sub parse_instance_conf_string {
|
||
my ($self, $setting) = @_;
|
||
return $::instance_conf->data->{$setting};
|
||
}
|
||
|
||
sub dump_visible {
|
||
my $self = shift;
|
||
foreach my $key (@{ $self->{ORDER} }) {
|
||
my $entry = $self->{$key};
|
||
$main::lxdebug->message(0, "$entry->{GRANTED} $entry->{VISIBLE} $entry->{NUM_VISIBLE_CHILDREN} $key");
|
||
sub set_access {
|
||
my ($self) = @_;
|
||
# 1. evaluate access for all
|
||
# 2. if a menu has no visible children, its not visible either
|
||
|
||
for my $node (reverse $self->tree_walk("all")) {
|
||
$node->{visible} = $node->{access} ? $self->parse_access_string($node)
|
||
: !$node->{children} ? 1
|
||
: $node->{visible_children} ? 1
|
||
: 0;
|
||
if ($node->{visible} && $node->{parent}) {
|
||
$self->{by_id}{ $node->{parent} }{visible_children} = 1;
|
||
}
|
||
}
|
||
}
|
||
|
menus/admin/00-admin.yaml | ||
---|---|---|
---
|
||
- id: users_clients_and_user_groups
|
||
name: Users, Clients and User Groups
|
||
order: 100
|
||
- parent: users_clients_and_user_groups
|
||
id: users_clients_and_user_groups_list_users_clients_and_user_groups
|
||
name: List Users, Clients and User Groups
|
||
order: 100
|
||
params:
|
||
action: Admin/show
|
||
- parent: users_clients_and_user_groups
|
||
id: users_clients_and_user_groups_add_user
|
||
name: Add User
|
||
order: 200
|
||
params:
|
||
action: Admin/new_user
|
||
- parent: users_clients_and_user_groups
|
||
id: users_clients_and_user_groups_add_client
|
||
name: Add Client
|
||
order: 300
|
||
params:
|
||
action: Admin/new_client
|
||
- parent: users_clients_and_user_groups
|
||
id: users_clients_and_user_groups_add_user_group
|
||
name: Add User Group
|
||
order: 400
|
||
params:
|
||
action: Admin/new_group
|
||
- id: database_management
|
||
name: Database Management
|
||
order: 200
|
||
- parent: database_management
|
||
id: database_management_create_dataset
|
||
name: Create Dataset
|
||
order: 100
|
||
params:
|
||
action: Admin/create_dataset_login
|
||
- parent: database_management
|
||
id: database_management_delete_dataset
|
||
name: Delete Dataset
|
||
order: 200
|
||
params:
|
||
action: Admin/delete_dataset_login
|
||
- id: printer_management
|
||
name: Printer Management
|
||
order: 300
|
||
- parent: printer_management
|
||
id: printer_management_list_printers
|
||
name: List Printers
|
||
order: 100
|
||
params:
|
||
action: Admin/list_printers
|
||
- parent: printer_management
|
||
id: printer_management_add_printer
|
||
name: Add Printer
|
||
order: 200
|
||
params:
|
||
action: Admin/new_printer
|
||
- id: system
|
||
name: System
|
||
icon: system
|
||
order: 400
|
||
- parent: system
|
||
id: system_lock_and_unlock_installation
|
||
name: Lock and unlock installation
|
||
order: 100
|
||
params:
|
||
action: Admin/show_lock
|
||
- parent: system
|
||
id: system_documentation_in_german_
|
||
name: Documentation (in German)
|
||
order: 200
|
||
href: doc/kivitendo-Dokumentation.pdf
|
||
target: _blank
|
||
- parent: system
|
||
id: system_kivitendo_website_external_
|
||
name: kivitendo website (external)
|
||
order: 300
|
||
href: http://www.kivitendo.de/
|
||
target: _blank
|
||
- parent: system
|
||
id: system_to_user_login
|
||
name: To user login
|
||
order: 400
|
||
params:
|
||
action: LoginScreen/user_login
|
||
- parent: system
|
||
id: system_logout
|
||
name: Logout
|
||
order: 500
|
||
params:
|
||
action: Admin/logout
|
menus/user/00-erp.yaml | ||
---|---|---|
---
|
||
- id: master_data
|
||
name: Master Data
|
||
icon: master_data
|
||
order: 100
|
||
- parent: master_data
|
||
id: master_data_add_customer
|
||
name: Add Customer
|
||
icon: customer_add
|
||
order: 100
|
||
access: customer_vendor_edit
|
||
params:
|
||
action: CustomerVendor/add
|
||
db: customer
|
||
- parent: master_data
|
||
id: master_data_add_vendor
|
||
name: Add Vendor
|
||
icon: vendor_add
|
||
order: 200
|
||
access: customer_vendor_edit
|
||
params:
|
||
action: CustomerVendor/add
|
||
db: vendor
|
||
- parent: master_data
|
||
id: master_data_add_part
|
||
name: Add Part
|
||
icon: part_add
|
||
order: 300
|
||
access: part_service_assembly_edit
|
||
module: ic.pl
|
||
params:
|
||
action: add
|
||
item: part
|
||
- parent: master_data
|
||
id: master_data_add_service
|
||
name: Add Service
|
||
icon: service_add
|
||
order: 400
|
||
access: part_service_assembly_edit
|
||
module: ic.pl
|
||
params:
|
||
action: add
|
||
item: service
|
||
- parent: master_data
|
||
id: master_data_add_assembly
|
||
name: Add Assembly
|
||
icon: assembly_add
|
||
order: 500
|
||
access: part_service_assembly_edit
|
||
module: ic.pl
|
||
params:
|
||
action: add
|
||
item: assembly
|
||
- parent: master_data
|
||
id: master_data_add_project
|
||
name: Add Project
|
||
icon: project_add
|
||
order: 600
|
||
access: project_edit
|
||
params:
|
||
action: Project/new
|
||
- parent: master_data
|
||
id: master_data_add_requirement_spec_template
|
||
name: Add Requirement Spec Template
|
||
order: 700
|
||
access: requirement_spec_edit
|
||
params:
|
||
action: RequirementSpec/new
|
||
is_template: 1
|
||
- parent: master_data
|
||
id: master_data_update_prices
|
||
name: Update Prices
|
||
icon: prices_update
|
||
order: 800
|
||
access: part_service_assembly_edit
|
||
module: ic.pl
|
||
params:
|
||
action: search_update_prices
|
||
- parent: master_data
|
||
id: master_data_price_rules
|
||
name: Price Rules
|
||
order: 900
|
||
access: part_service_assembly_edit
|
||
params:
|
||
action: PriceRule/list
|
||
filter.obsolete: 0
|
||
- parent: master_data
|
||
id: master_data_reports
|
||
name: Reports
|
||
icon: master_data_report
|
||
order: 1000
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: master_data_reports
|
||
id: master_data_reports_customers
|
||
name: Customers
|
||
icon: customer_report
|
||
order: 100
|
||
access: customer_vendor_edit
|
||
params:
|
||
action: CustomerVendor/search
|
||
db: customer
|
||
- parent: master_data_reports
|
||
id: master_data_reports_vendors
|
||
name: Vendors
|
||
icon: vendor_report
|
||
order: 200
|
||
access: customer_vendor_edit
|
||
params:
|
||
action: CustomerVendor/search
|
||
db: vendor
|
||
- parent: master_data_reports
|
||
id: master_data_reports_contacts
|
||
name: Contacts
|
||
order: 300
|
||
access: customer_vendor_edit
|
||
params:
|
||
action: CustomerVendor/search_contact
|
||
db: customer
|
||
- parent: master_data_reports
|
||
id: master_data_reports_parts
|
||
name: Parts
|
||
icon: part_report
|
||
order: 400
|
||
access: part_service_assembly_details
|
||
module: ic.pl
|
||
params:
|
||
action: search
|
||
searchitems: part
|
||
- parent: master_data_reports
|
||
id: master_data_reports_services
|
||
name: Services
|
||
icon: service_report
|
||
order: 500
|
||
access: part_service_assembly_details
|
||
module: ic.pl
|
||
params:
|
||
action: search
|
||
searchitems: service
|
||
- parent: master_data_reports
|
||
id: master_data_reports_assemblies
|
||
name: Assemblies
|
||
icon: assembly_report
|
||
order: 600
|
||
access: part_service_assembly_details
|
||
module: ic.pl
|
||
params:
|
||
action: search
|
||
searchitems: assembly
|
||
- parent: master_data_reports
|
||
id: master_data_reports_projects
|
||
name: Projects
|
||
icon: project_report
|
||
order: 700
|
||
access: project_edit
|
||
params:
|
||
action: Project/list
|
||
filter.active: active
|
||
filter.valid: valid
|
||
- parent: master_data_reports
|
||
id: master_data_reports_requirement_spec_templates
|
||
name: Requirement Spec Templates
|
||
order: 800
|
||
access: requirement_spec_edit
|
||
params:
|
||
action: RequirementSpec/list
|
||
is_template: 1
|
||
- id: ar
|
||
name: AR
|
||
icon: ar
|
||
order: 200
|
||
- parent: ar
|
||
id: ar_add_requirement_spec
|
||
name: Add Requirement Spec
|
||
order: 100
|
||
access: requirement_spec_edit
|
||
params:
|
||
action: RequirementSpec/new
|
||
- parent: ar
|
||
id: ar_add_quotation
|
||
name: Add Quotation
|
||
icon: quotation_add
|
||
order: 200
|
||
access: sales_quotation_edit
|
||
module: oe.pl
|
||
params:
|
||
action: add
|
||
type: sales_quotation
|
||
- parent: ar
|
||
id: ar_add_sales_order
|
||
name: Add Sales Order
|
||
icon: sales_order_add
|
||
order: 300
|
||
access: sales_order_edit
|
||
module: oe.pl
|
||
params:
|
||
action: add
|
||
type: sales_order
|
||
- parent: ar
|
||
id: ar_add_delivery_order
|
||
name: Add Delivery Order
|
||
icon: delivery_order_add
|
||
order: 400
|
||
access: sales_delivery_order_edit
|
||
module: do.pl
|
||
params:
|
||
action: add
|
||
type: sales_delivery_order
|
||
- parent: ar
|
||
id: ar_add_sales_invoice
|
||
name: Add Sales Invoice
|
||
icon: sales_invoice_add
|
||
order: 500
|
||
access: invoice_edit
|
||
module: is.pl
|
||
params:
|
||
action: add
|
||
type: invoice
|
||
- parent: ar
|
||
id: ar_add_credit_note
|
||
name: Add Credit Note
|
||
icon: credit_note_add
|
||
order: 600
|
||
access: invoice_edit
|
||
module: is.pl
|
||
params:
|
||
action: add
|
||
type: credit_note
|
||
- parent: ar
|
||
id: ar_add_dunning
|
||
name: Add Dunning
|
||
icon: dunning_add
|
||
order: 700
|
||
access: dunning_edit
|
||
module: dn.pl
|
||
params:
|
||
action: add
|
||
- parent: ar
|
||
id: ar_add_letter
|
||
name: Add Letter
|
||
order: 800
|
||
access: sales_letter_edit
|
||
module: letter.pl
|
||
params:
|
||
action: add
|
||
- parent: ar
|
||
id: ar_reports
|
||
name: Reports
|
||
icon: ar_report
|
||
order: 900
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: ar_reports
|
||
id: ar_reports_requirement_specs
|
||
name: Requirement Specs
|
||
order: 100
|
||
access: requirement_spec_edit
|
||
params:
|
||
action: RequirementSpec/list
|
||
- parent: ar_reports
|
||
id: ar_reports_quotations
|
||
name: Quotations
|
||
icon: report_quotations
|
||
order: 200
|
||
access: sales_quotation_edit
|
||
module: oe.pl
|
||
params:
|
||
action: search
|
||
type: sales_quotation
|
||
- parent: ar_reports
|
||
id: ar_reports_sales_orders
|
||
name: Sales Orders
|
||
icon: report_sales_orders
|
||
order: 300
|
||
access: sales_order_edit
|
||
module: oe.pl
|
||
params:
|
||
action: search
|
||
type: sales_order
|
||
- parent: ar_reports
|
||
id: ar_reports_delivery_orders
|
||
name: Delivery Orders
|
||
icon: delivery_order_report
|
||
order: 400
|
||
access: sales_delivery_order_edit
|
||
module: do.pl
|
||
params:
|
||
action: search
|
||
type: sales_delivery_order
|
||
- parent: ar_reports
|
||
id: ar_reports_invoices_credit_notes_ar_transactions
|
||
name: Invoices, Credit Notes & AR Transactions
|
||
icon: invoices_report
|
||
order: 500
|
||
access: invoice_edit
|
||
module: ar.pl
|
||
params:
|
||
action: search
|
||
nextsub: ar_transactions
|
||
- parent: ar_reports
|
||
id: ar_reports_sales_report
|
||
name: Sales Report
|
||
order: 600
|
||
access: invoice_edit
|
||
module: vk.pl
|
||
params:
|
||
action: search_invoice
|
||
nextsub: invoice_transactions
|
||
- parent: ar_reports
|
||
id: ar_reports_dunnings
|
||
name: Dunnings
|
||
icon: dunnings_report
|
||
order: 700
|
||
access: dunning_edit
|
||
module: dn.pl
|
||
params:
|
||
action: search
|
||
- parent: ar_reports
|
||
id: ar_reports_delivery_plan
|
||
name: Delivery Plan
|
||
order: 800
|
||
access: delivery_plan
|
||
params:
|
||
action: DeliveryPlan/list
|
||
vc: customer
|
||
mode: delivery_plan
|
||
- parent: ar_reports
|
||
id: ar_reports_delivery_value_report
|
||
name: Delivery Value Report
|
||
order: 900
|
||
access: delivery_value_report
|
||
params:
|
||
action: DeliveryPlan/list
|
||
mode: delivery_value_report
|
||
vc: customer
|
||
- parent: ar_reports
|
||
id: ar_reports_financial_controlling
|
||
name: Financial Controlling
|
||
order: 1000
|
||
access: sales_order_edit
|
||
params:
|
||
action: FinancialControllingReport/list
|
||
- parent: ar_reports
|
||
id: ar_reports_letters
|
||
name: Letters
|
||
order: 1100
|
||
access: sales_letter_report
|
||
module: letter.pl
|
||
params:
|
||
action: search
|
||
- id: ap
|
||
name: AP
|
||
icon: ap
|
||
order: 300
|
||
- parent: ap
|
||
id: ap_add_rfq
|
||
name: Add RFQ
|
||
icon: rfq_add
|
||
order: 100
|
||
access: request_quotation_edit
|
||
module: oe.pl
|
||
params:
|
||
action: add
|
||
type: request_quotation
|
||
- parent: ap
|
||
id: ap_add_purchase_order
|
||
name: Add Purchase Order
|
||
icon: purchase_order_add
|
||
order: 200
|
||
access: purchase_order_edit
|
||
module: oe.pl
|
||
params:
|
||
action: add
|
||
type: purchase_order
|
||
- parent: ap
|
||
id: ap_add_delivery_note
|
||
name: Add Delivery Note
|
||
order: 300
|
||
access: client/allow_new_purchase_delivery_order & purchase_delivery_order_edit
|
||
module: do.pl
|
||
params:
|
||
action: add
|
||
type: purchase_delivery_order
|
||
- parent: ap
|
||
id: ap_add_vendor_invoice
|
||
name: Add Vendor Invoice
|
||
order: 400
|
||
access: client/allow_new_purchase_invoice & vendor_invoice_edit
|
||
module: ir.pl
|
||
params:
|
||
action: add
|
||
type: invoice
|
||
- parent: ap
|
||
id: ap_reports
|
||
name: Reports
|
||
icon: ap_report
|
||
order: 500
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: ap_reports
|
||
id: ap_reports_rfqs
|
||
name: RFQs
|
||
icon: rfq_report
|
||
order: 100
|
||
access: request_quotation_edit
|
||
module: oe.pl
|
||
params:
|
||
action: search
|
||
type: request_quotation
|
||
- parent: ap_reports
|
||
id: ap_reports_purchase_orders
|
||
name: Purchase Orders
|
||
icon: purchase_order_report
|
||
order: 200
|
||
access: purchase_order_edit
|
||
module: oe.pl
|
||
params:
|
||
action: search
|
||
type: purchase_order
|
||
- parent: ap_reports
|
||
id: ap_reports_delivery_orders
|
||
name: Delivery Orders
|
||
order: 300
|
||
access: purchase_delivery_order_edit
|
||
module: do.pl
|
||
params:
|
||
action: search
|
||
type: purchase_delivery_order
|
||
- parent: ap_reports
|
||
id: ap_reports_vendor_invoices_ap_transactions
|
||
name: Vendor Invoices & AP Transactions
|
||
order: 400
|
||
access: vendor_invoice_edit
|
||
module: ap.pl
|
||
params:
|
||
action: search
|
||
nextsub: ap_transactions
|
||
- parent: ap_reports
|
||
id: ap_reports_delivery_plan
|
||
name: Delivery Plan
|
||
order: 500
|
||
access: delivery_plan
|
||
params:
|
||
action: DeliveryPlan/list
|
||
mode: delivery_plan
|
||
vc: vendor
|
||
- parent: ap_reports
|
||
id: ap_reports_delivery_value_report
|
||
name: Delivery Value Report
|
||
order: 600
|
||
access: delivery_value_report
|
||
params:
|
||
action: DeliveryPlan/list
|
||
vc: vendor
|
||
mode: delivery_value_report
|
||
- id: warehouse
|
||
name: Warehouse
|
||
icon: warehouse
|
||
order: 400
|
||
- parent: warehouse
|
||
id: warehouse_stock
|
||
name: Stock
|
||
order: 100
|
||
access: warehouse_management
|
||
params:
|
||
action: Inventory/stock_in
|
||
- parent: warehouse
|
||
id: warehouse_produce_assembly
|
||
name: Produce Assembly
|
||
icon: assembly_produce
|
||
order: 200
|
||
access: warehouse_management
|
||
module: wh.pl
|
||
params:
|
||
action: transfer_warehouse_selection
|
||
trans_type: assembly
|
||
- parent: warehouse
|
||
id: warehouse_transfer
|
||
name: Transfer
|
||
order: 300
|
||
access: warehouse_management
|
||
module: wh.pl
|
||
params:
|
||
action: transfer_warehouse_selection
|
||
trans_type: transfer
|
||
- parent: warehouse
|
||
id: warehouse_removal
|
||
name: Removal
|
||
order: 400
|
||
access: warehouse_management
|
||
module: wh.pl
|
||
params:
|
||
action: transfer_warehouse_selection
|
||
trans_type: removal
|
||
- parent: warehouse
|
||
id: warehouse_reports
|
||
name: Reports
|
||
order: 500
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: warehouse_reports
|
||
id: warehouse_reports_warehouse_content
|
||
name: Warehouse content
|
||
order: 100
|
||
access: warehouse_contents | warehouse_management
|
||
module: wh.pl
|
||
params:
|
||
action: report
|
||
- parent: warehouse_reports
|
||
id: warehouse_reports_whjournal
|
||
name: WHJournal
|
||
order: 200
|
||
access: warehouse_management
|
||
module: wh.pl
|
||
params:
|
||
action: journal
|
||
- id: general_ledger
|
||
name: General Ledger
|
||
icon: gl
|
||
order: 500
|
||
- parent: general_ledger
|
||
id: general_ledger_add_transaction
|
||
name: Add Transaction
|
||
icon: transaction_add
|
||
order: 100
|
||
access: general_ledger
|
||
module: gl.pl
|
||
params:
|
||
action: add
|
||
- parent: general_ledger
|
||
id: general_ledger_add_ar_transaction
|
||
name: Add AR Transaction
|
||
icon: ar_transaction_add
|
||
order: 200
|
||
access: general_ledger
|
||
module: ar.pl
|
||
params:
|
||
action: add
|
||
- parent: general_ledger
|
||
id: general_ledger_add_ap_transaction
|
||
name: Add AP Transaction
|
||
icon: ap_transaction_add
|
||
order: 300
|
||
access: general_ledger
|
||
module: ap.pl
|
||
params:
|
||
action: add
|
||
- parent: general_ledger
|
||
id: general_ledger_datev_export_assistent
|
||
name: DATEV - Export Assistent
|
||
icon: datev
|
||
order: 400
|
||
access: datev_export
|
||
module: datev.pl
|
||
params:
|
||
action: export
|
||
- parent: general_ledger
|
||
id: general_ledger_reports
|
||
name: Reports
|
||
icon: gl_report
|
||
order: 500
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: general_ledger_reports
|
||
id: general_ledger_reports_ar_aging
|
||
name: AR Aging
|
||
icon: ar_aging
|
||
order: 100
|
||
access: general_ledger
|
||
module: rp.pl
|
||
params:
|
||
action: report
|
||
report: ar_aging
|
||
- parent: general_ledger_reports
|
||
id: general_ledger_reports_ap_aging
|
||
name: AP Aging
|
||
icon: ap_aging
|
||
order: 200
|
||
access: general_ledger
|
||
module: rp.pl
|
||
params:
|
||
action: report
|
||
report: ap_aging
|
||
- parent: general_ledger_reports
|
||
id: general_ledger_reports_journal
|
||
name: Journal
|
||
icon: journal
|
||
order: 300
|
||
access: general_ledger
|
||
module: gl.pl
|
||
params:
|
||
action: search
|
||
- id: cash
|
||
name: Cash
|
||
icon: cash
|
||
order: 600
|
||
- parent: cash
|
||
id: cash_receipt
|
||
name: Receipt
|
||
icon: receipt
|
||
order: 100
|
||
access: cash
|
||
module: cp.pl
|
||
params:
|
||
action: payment
|
||
vc: customer
|
||
type: receipt
|
||
- parent: cash
|
||
id: cash_payment
|
||
name: Payment
|
||
icon: payment
|
||
order: 200
|
||
access: cash
|
||
module: cp.pl
|
||
params:
|
||
action: payment
|
||
vc: vendor
|
||
type: check
|
||
- parent: cash
|
||
id: cash_bank_collection_via_sepa
|
||
name: Bank collection via SEPA
|
||
order: 300
|
||
access: cash
|
||
module: sepa.pl
|
||
params:
|
||
action: bank_transfer_add
|
||
vc: customer
|
||
- parent: cash
|
||
id: cash_bank_transfer_via_sepa
|
||
name: Bank transfer via SEPA
|
||
order: 400
|
||
access: cash
|
||
module: sepa.pl
|
||
params:
|
||
action: bank_transfer_add
|
||
vc: vendor
|
||
- parent: cash
|
||
id: cash_bank_import
|
||
name: Bank Import
|
||
order: 500
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: cash_bank_import
|
||
id: cash_bank_import_csv
|
||
name: CSV
|
||
order: 100
|
||
access: bank_transaction
|
||
params:
|
||
action: CsvImport/new
|
||
profile.type: bank_transactions
|
||
- parent: cash_bank_import
|
||
id: cash_bank_import_mt940
|
||
name: MT940
|
||
order: 200
|
||
access: bank_transaction
|
||
params:
|
||
action: BankImport/upload_mt940
|
||
- parent: cash
|
||
id: cash_bank_transactions_mt940
|
||
name: Bank transactions MT940
|
||
order: 600
|
||
access: bank_transaction
|
||
params:
|
||
action: BankTransaction/search
|
||
- parent: cash
|
||
id: cash_reconciliation_with_bank
|
||
name: Reconciliation with bank
|
||
order: 700
|
||
access: bank_transaction
|
||
params:
|
||
action: Reconciliation/search
|
||
next_sub: Reconciliation/reconciliation
|
||
- parent: cash
|
||
id: cash_reconciliation
|
||
name: Reconciliation
|
||
icon: reconcilliation
|
||
order: 800
|
||
access: cash
|
||
module: rc.pl
|
||
params:
|
||
action: reconciliation
|
||
- parent: cash
|
||
id: cash_reports
|
||
name: Reports
|
||
icon: cash_report
|
||
order: 900
|
||
module: menu.pl
|
||
params:
|
||
action: acc_menu
|
||
- parent: cash_reports
|
||
id: cash_reports_receipts
|
||
name: Receipts
|
||
icon: receipt_report
|
||
order: 100
|
||
access: cash
|
||
module: rp.pl
|
||
params:
|
||
action: report
|
||
report: receipts
|
||
- parent: cash_reports
|
||
id: cash_reports_payments
|
||
name: Payments
|
||
icon: payment_report
|
||
order: 200
|
||
access: cash
|
||
module: rp.pl
|
Auch abrufbar als: Unified diff
Menüstruktur auf YAML geändert