Revision 614c48e0
Von Moritz Bunkus vor fast 6 Jahren hinzugefügt
SL/AR.pm | ||
---|---|---|
516 | 516 |
|
517 | 517 |
my $where = "1 = 1"; |
518 | 518 |
|
519 |
unless ( $::auth->assert('show_ar_transactions', 1) ) { |
|
520 |
$where .= " AND NOT invoice = 'f' "; # remove ar transactions from Sales -> Reports -> Invoices |
|
521 |
}; |
|
519 |
# Permissions: |
|
520 |
# - Always return invoices & AR transactions for projects the employee has "view invoices" permissions for, no matter what the other rules say. |
|
521 |
# - Exclude AR transactions if no permissions for them exist. |
|
522 |
# - Limit to own invoices unless may edit all invoices. |
|
523 |
# - If may edit all, allow filtering by employee/salesman. |
|
524 |
my (@permission_where, @permission_values); |
|
525 |
|
|
526 |
if ($::auth->assert('invoice_edit', 1)) { |
|
527 |
if (!$::auth->assert('show_ar_transactions', 1) ) { |
|
528 |
push @permission_where, "NOT invoice = 'f'"; # remove ar transactions from Sales -> Reports -> Invoices |
|
529 |
} |
|
530 |
|
|
531 |
if (!$::auth->assert('sales_all_edit', 1)) { |
|
532 |
# only show own invoices |
|
533 |
push @permission_where, "a.employee_id = ?"; |
|
534 |
push @permission_values, SL::DB::Manager::Employee->current->id; |
|
535 |
|
|
536 |
} else { |
|
537 |
if ($form->{employee_id}) { |
|
538 |
push @permission_where, "a.employee_id = ?"; |
|
539 |
push @permission_values, conv_i($form->{employee_id}); |
|
540 |
} |
|
541 |
if ($form->{salesman_id}) { |
|
542 |
push @permission_where, "a.salesman_id = ?"; |
|
543 |
push @permission_values, conv_i($form->{salesman_id}); |
|
544 |
} |
|
545 |
} |
|
546 |
} |
|
547 |
|
|
548 |
if (@permission_where || !$::auth->assert('invoice_edit', 1)) { |
|
549 |
my $permission_where_str = @permission_where ? "OR (" . join(" AND ", map { "($_)" } @permission_where) . ")" : ""; |
|
550 |
$where .= qq| |
|
551 |
AND ( (a.globalproject_id IN ( |
|
552 |
SELECT epi.project_id |
|
553 |
FROM employee_project_invoices epi |
|
554 |
WHERE epi.employee_id = ?)) |
|
555 |
$permission_where_str) |
|
556 |
|; |
|
557 |
push @values, SL::DB::Manager::Employee->current->id, @permission_values; |
|
558 |
} |
|
522 | 559 |
|
523 | 560 |
if ($form->{customer}) { |
524 | 561 |
$where .= " AND c.name ILIKE ?"; |
... | ... | |
578 | 615 |
} |
579 | 616 |
} |
580 | 617 |
|
581 |
if (!$main::auth->assert('sales_all_edit', 1)) { |
|
582 |
# only show own invoices |
|
583 |
$where .= " AND a.employee_id = (select id from employee where login= ?)"; |
|
584 |
push (@values, $::myconfig{login}); |
|
585 |
} else { |
|
586 |
if ($form->{employee_id}) { |
|
587 |
$where .= " AND a.employee_id = ?"; |
|
588 |
push @values, conv_i($form->{employee_id}); |
|
589 |
} |
|
590 |
if ($form->{salesman_id}) { |
|
591 |
$where .= " AND a.salesman_id = ?"; |
|
592 |
push @values, conv_i($form->{salesman_id}); |
|
593 |
} |
|
594 |
}; |
|
595 |
|
|
596 | 618 |
if ($form->{parts_partnumber}) { |
597 | 619 |
$where .= <<SQL; |
598 | 620 |
AND EXISTS ( |
SL/Controller/Project.pm | ||
---|---|---|
12 | 12 |
use SL::CVar; |
13 | 13 |
use SL::DB::Customer; |
14 | 14 |
use SL::DB::DeliveryOrder; |
15 |
use SL::DB::Employee; |
|
15 | 16 |
use SL::DB::Invoice; |
16 | 17 |
use SL::DB::Order; |
17 | 18 |
use SL::DB::Project; |
... | ... | |
29 | 30 |
use Rose::Object::MakeMethods::Generic |
30 | 31 |
( |
31 | 32 |
scalar => [ qw(project) ], |
32 |
'scalar --get_set_init' => [ qw(models customers project_types project_statuses projects linked_records) ], |
|
33 |
'scalar --get_set_init' => [ qw(models customers project_types project_statuses projects linked_records employees may_edit_invoice_permissions) ],
|
|
33 | 34 |
); |
34 | 35 |
|
35 | 36 |
__PACKAGE__->run_before('check_auth', except => [ qw(ajax_autocomplete) ]); |
36 | 37 |
__PACKAGE__->run_before('load_project', only => [ qw(edit update destroy) ]); |
38 |
__PACKAGE__->run_before('use_multiselect_js', only => [ qw(new create edit update) ]); |
|
37 | 39 |
|
38 | 40 |
# |
39 | 41 |
# actions |
... | ... | |
166 | 168 |
|
167 | 169 |
sub init_project_statuses { SL::DB::Manager::ProjectStatus->get_all_sorted } |
168 | 170 |
sub init_project_types { SL::DB::Manager::ProjectType->get_all_sorted } |
171 |
sub init_employees { SL::DB::Manager::Employee->get_all_sorted } |
|
172 |
sub init_may_edit_invoice_permissions { $::auth->assert('project_edit_view_invoices_permission', 1) } |
|
169 | 173 |
|
170 | 174 |
sub init_linked_records { |
171 | 175 |
my ($self) = @_; |
... | ... | |
223 | 227 |
return SL::DB::Manager::Customer->get_all_sorted(where => [ or => [ obsolete => 0, obsolete => undef, @customer_id ]]); |
224 | 228 |
} |
225 | 229 |
|
230 |
sub use_multiselect_js { |
|
231 |
$::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side); |
|
232 |
} |
|
233 |
|
|
226 | 234 |
sub display_form { |
227 | 235 |
my ($self, %params) = @_; |
228 | 236 |
|
... | ... | |
246 | 254 |
my $is_new = !$self->project->id; |
247 | 255 |
my $params = delete($::form->{project}) || { }; |
248 | 256 |
|
257 |
if (!$self->may_edit_invoice_permissions) { |
|
258 |
delete $params->{employee_invoice_permissions}; |
|
259 |
} elsif (!$params->{employee_invoice_permissions}) { |
|
260 |
$params->{employee_invoice_permissions} = []; |
|
261 |
} |
|
262 |
|
|
249 | 263 |
delete $params->{id}; |
250 | 264 |
$self->project->assign_attributes(%{ $params }); |
251 | 265 |
|
SL/DB/Employee.pm | ||
---|---|---|
5 | 5 |
use SL::DB::MetaSetup::Employee; |
6 | 6 |
use SL::DB::Manager::Employee; |
7 | 7 |
|
8 |
__PACKAGE__->meta->add_relationship( |
|
9 |
project_invoice_permissions => { |
|
10 |
type => 'many to many', |
|
11 |
map_class => 'SL::DB::EmployeeProjectInvoices', |
|
12 |
}, |
|
13 |
); |
|
14 |
|
|
8 | 15 |
__PACKAGE__->meta->initialize; |
9 | 16 |
|
10 | 17 |
sub has_right { |
SL/DB/EmployeeProjectInvoices.pm | ||
---|---|---|
1 |
package SL::DB::EmployeeProjectInvoices; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use SL::DB::MetaSetup::EmployeeProjectInvoices; |
|
6 |
use SL::DB::Manager::EmployeeProjectInvoices; |
|
7 |
|
|
8 |
__PACKAGE__->meta->initialize; |
|
9 |
|
|
10 |
1; |
SL/DB/Helper/ALL.pm | ||
---|---|---|
52 | 52 |
use SL::DB::EmailJournal; |
53 | 53 |
use SL::DB::EmailJournalAttachment; |
54 | 54 |
use SL::DB::Employee; |
55 |
use SL::DB::EmployeeProjectInvoices; |
|
55 | 56 |
use SL::DB::Exchangerate; |
56 | 57 |
use SL::DB::File; |
57 | 58 |
use SL::DB::Finanzamt; |
SL/DB/Helper/Mappings.pm | ||
---|---|---|
136 | 136 |
email_journal => 'EmailJournal', |
137 | 137 |
email_journal_attachments => 'EmailJournalAttachment', |
138 | 138 |
employee => 'employee', |
139 |
employee_project_invoices => 'EmployeeProjectInvoices', |
|
139 | 140 |
exchangerate => 'exchangerate', |
140 | 141 |
files => 'file', |
141 | 142 |
finanzamt => 'finanzamt', |
SL/DB/Manager/EmployeeProjectInvoices.pm | ||
---|---|---|
1 |
package SL::DB::Manager::EmployeeProjectInvoices; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(SL::DB::Helper::Manager); |
|
6 |
|
|
7 |
sub object_class { 'SL::DB::EmployeeProjectInvoices' } |
|
8 |
|
|
9 |
__PACKAGE__->make_manager_methods; |
|
10 |
|
|
11 |
1; |
SL/DB/MetaSetup/EmployeeProjectInvoices.pm | ||
---|---|---|
1 |
# This file has been auto-generated. Do not modify it; it will be overwritten |
|
2 |
# by rose_auto_create_model.pl automatically. |
|
3 |
package SL::DB::EmployeeProjectInvoices; |
|
4 |
|
|
5 |
use strict; |
|
6 |
|
|
7 |
use parent qw(SL::DB::Object); |
|
8 |
|
|
9 |
__PACKAGE__->meta->table('employee_project_invoices'); |
|
10 |
|
|
11 |
__PACKAGE__->meta->columns( |
|
12 |
employee_id => { type => 'integer', not_null => 1 }, |
|
13 |
project_id => { type => 'integer', not_null => 1 }, |
|
14 |
); |
|
15 |
|
|
16 |
__PACKAGE__->meta->primary_key_columns([ 'employee_id', 'project_id' ]); |
|
17 |
|
|
18 |
__PACKAGE__->meta->foreign_keys( |
|
19 |
employee => { |
|
20 |
class => 'SL::DB::Employee', |
|
21 |
key_columns => { employee_id => 'id' }, |
|
22 |
}, |
|
23 |
|
|
24 |
project => { |
|
25 |
class => 'SL::DB::Project', |
|
26 |
key_columns => { project_id => 'id' }, |
|
27 |
}, |
|
28 |
); |
|
29 |
|
|
30 |
1; |
|
31 |
; |
SL/DB/Project.pm | ||
---|---|---|
12 | 12 |
cvars_alias => 1, |
13 | 13 |
); |
14 | 14 |
|
15 |
__PACKAGE__->meta->add_relationship( |
|
16 |
employee_invoice_permissions => { |
|
17 |
type => 'many to many', |
|
18 |
map_class => 'SL::DB::EmployeeProjectInvoices', |
|
19 |
}, |
|
20 |
); |
|
21 |
|
|
15 | 22 |
__PACKAGE__->meta->initialize; |
16 | 23 |
|
17 | 24 |
sub validate { |
... | ... | |
84 | 91 |
return $description; |
85 | 92 |
} |
86 | 93 |
|
94 |
sub may_employee_view_project_invoices { |
|
95 |
my ($self, $employee) = @_; |
|
96 |
|
|
97 |
return undef if !$self->id; |
|
98 |
|
|
99 |
my $employee_id = ref($employee) ? $employee->id : $employee * 1; |
|
100 |
my $query = <<EOSQL; |
|
101 |
SELECT project_id |
|
102 |
FROM employee_project_invoices |
|
103 |
WHERE (employee_id = ?) |
|
104 |
AND (project_id = ?) |
|
105 |
LIMIT 1 |
|
106 |
EOSQL |
|
107 |
|
|
108 |
return !!$self->db->dbh->selectrow_arrayref($query, undef, $employee_id, $self->id)->[0]; |
|
109 |
} |
|
110 |
|
|
87 | 111 |
1; |
88 | 112 |
|
89 | 113 |
__END__ |
bin/mozilla/ar.pl | ||
---|---|---|
89 | 89 |
# $locale->text('Nov') |
90 | 90 |
# $locale->text('Dec') |
91 | 91 |
|
92 |
sub _may_view_or_edit_this_invoice { |
|
93 |
return 1 if $::auth->assert('ar_transactions', 1); # may edit all invoices |
|
94 |
return 0 if !$::form->{id}; # creating new invoices isn't allowed without invoice_edit |
|
95 |
return 0 if !$::form->{globalproject_id}; # existing records without a project ID are not allowed |
|
96 |
return SL::DB::Project->new(id => $::form->{globalproject_id})->load->may_employee_view_project_invoices(SL::DB::Manager::Employee->current); |
|
97 |
} |
|
98 |
|
|
99 |
sub _assert_access { |
|
100 |
my $cache = $::request->cache('ar.pl::_assert_access'); |
|
101 |
|
|
102 |
$cache->{_may_view_or_edit_this_invoice} = _may_view_or_edit_this_invoice() if !exists $cache->{_may_view_or_edit_this_invoice}; |
|
103 |
$::form->show_generic_error($::locale->text("You do not have the permissions to access this function.")) if ! $cache->{_may_view_or_edit_this_invoice}; |
|
104 |
} |
|
105 |
|
|
92 | 106 |
sub load_record_template { |
93 | 107 |
$::auth->assert('ar_transactions'); |
94 | 108 |
|
... | ... | |
249 | 263 |
sub edit { |
250 | 264 |
$main::lxdebug->enter_sub(); |
251 | 265 |
|
252 |
$main::auth->assert('ar_transactions'); |
|
266 |
# Delay access check to after the invoice's been loaded in |
|
267 |
# "create_links" so that project-specific invoice rights can be |
|
268 |
# evaluated. |
|
253 | 269 |
|
254 | 270 |
my $form = $main::form; |
255 | 271 |
|
... | ... | |
268 | 284 |
sub display_form { |
269 | 285 |
$main::lxdebug->enter_sub(); |
270 | 286 |
|
271 |
$main::auth->assert('ar_transactions');
|
|
287 |
_assert_access();
|
|
272 | 288 |
|
273 | 289 |
my $form = $main::form; |
274 | 290 |
|
... | ... | |
287 | 303 |
sub create_links { |
288 | 304 |
$main::lxdebug->enter_sub(); |
289 | 305 |
|
290 |
$main::auth->assert('ar_transactions'); |
|
306 |
# Delay access check to after the invoice's been loaded so that |
|
307 |
# project-specific invoice rights can be evaluated. |
|
291 | 308 |
|
292 | 309 |
my %params = @_; |
293 | 310 |
my $form = $main::form; |
... | ... | |
296 | 313 |
$form->create_links("AR", \%myconfig, "customer"); |
297 | 314 |
$form->{invoice_obj} = _retrieve_invoice_object(); |
298 | 315 |
|
316 |
_assert_access(); |
|
317 |
|
|
299 | 318 |
my %saved; |
300 | 319 |
if (!$params{dont_save}) { |
301 | 320 |
%saved = map { ($_ => $form->{$_}) } qw(direct_debit id taxincluded); |
... | ... | |
329 | 348 |
sub form_header { |
330 | 349 |
$main::lxdebug->enter_sub(); |
331 | 350 |
|
332 |
$main::auth->assert('ar_transactions');
|
|
351 |
_assert_access();
|
|
333 | 352 |
|
334 | 353 |
my $form = $main::form; |
335 | 354 |
my %myconfig = %main::myconfig; |
... | ... | |
528 | 547 |
sub form_footer { |
529 | 548 |
$main::lxdebug->enter_sub(); |
530 | 549 |
|
531 |
$main::auth->assert('ar_transactions');
|
|
550 |
_assert_access();
|
|
532 | 551 |
|
533 | 552 |
my $form = $main::form; |
534 | 553 |
my %myconfig = %main::myconfig; |
... | ... | |
885 | 904 |
} |
886 | 905 |
|
887 | 906 |
sub setup_ar_transactions_action_bar { |
888 |
my %params = @_; |
|
907 |
my %params = @_; |
|
908 |
my $may_edit_create = $::auth->assert('invoice_edit', 1); |
|
889 | 909 |
|
890 | 910 |
for my $bar ($::request->layout->get('actionbar')) { |
891 | 911 |
$bar->add( |
892 | 912 |
action => [ |
893 | 913 |
$::locale->text('Print'), |
894 | 914 |
call => [ 'kivi.MassInvoiceCreatePrint.showMassPrintOptionsOrDownloadDirectly' ], |
895 |
disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef, |
|
915 |
disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') |
|
916 |
: !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') |
|
917 |
: undef, |
|
896 | 918 |
], |
897 | 919 |
|
898 | 920 |
combobox => [ |
899 | 921 |
action => [ $::locale->text('Create new') ], |
900 | 922 |
action => [ |
901 | 923 |
$::locale->text('AR Transaction'), |
902 |
submit => [ '#create_new_form', { action => 'ar_transaction' } ], |
|
924 |
submit => [ '#create_new_form', { action => 'ar_transaction' } ], |
|
925 |
disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef, |
|
903 | 926 |
], |
904 | 927 |
action => [ |
905 | 928 |
$::locale->text('Sales Invoice'), |
906 |
submit => [ '#create_new_form', { action => 'sales_invoice' } ], |
|
929 |
submit => [ '#create_new_form', { action => 'sales_invoice' } ], |
|
930 |
disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef, |
|
907 | 931 |
], |
908 | 932 |
], # end of combobox "Create new" |
909 | 933 |
); |
... | ... | |
913 | 937 |
sub search { |
914 | 938 |
$main::lxdebug->enter_sub(); |
915 | 939 |
|
916 |
$main::auth->assert('invoice_edit'); |
|
917 |
|
|
918 | 940 |
my $form = $main::form; |
919 | 941 |
my %myconfig = %main::myconfig; |
920 | 942 |
my $locale = $main::locale; |
... | ... | |
969 | 991 |
sub ar_transactions { |
970 | 992 |
$main::lxdebug->enter_sub(); |
971 | 993 |
|
972 |
$main::auth->assert('invoice_edit'); |
|
973 |
|
|
974 | 994 |
my $form = $main::form; |
975 | 995 |
my %myconfig = %main::myconfig; |
976 | 996 |
my $locale = $main::locale; |
... | ... | |
1254 | 1274 |
|
1255 | 1275 |
my $is_storno = IS->is_storno(\%::myconfig, $::form, 'ar', $::form->{id}); |
1256 | 1276 |
my $has_storno = IS->has_storno(\%::myconfig, $::form, 'ar'); |
1277 |
my $may_edit_create = $::auth->assert('ar_transactions', 1); |
|
1257 | 1278 |
|
1258 | 1279 |
for my $bar ($::request->layout->get('actionbar')) { |
1259 | 1280 |
$bar->add( |
... | ... | |
1262 | 1283 |
submit => [ '#form', { action => "update" } ], |
1263 | 1284 |
id => 'update_button', |
1264 | 1285 |
checks => [ 'kivi.validate_form' ], |
1286 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') : undef, |
|
1265 | 1287 |
accesskey => 'enter', |
1266 | 1288 |
], |
1267 | 1289 |
|
... | ... | |
1270 | 1292 |
t8('Post'), |
1271 | 1293 |
submit => [ '#form', { action => "post" } ], |
1272 | 1294 |
checks => [ 'kivi.validate_form', 'kivi.AR.check_fields_before_posting' ], |
1273 |
disabled => $is_closed ? t8('The billing period has already been locked.') |
|
1295 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1296 |
: $is_closed ? t8('The billing period has already been locked.') |
|
1274 | 1297 |
: $is_storno ? t8('A canceled invoice cannot be posted.') |
1275 | 1298 |
: ($::form->{id} && $change_never) ? t8('Changing invoices has been disabled in the configuration.') |
1276 | 1299 |
: ($::form->{id} && $change_on_same_day_only) ? t8('Invoices can only be changed on the day they are posted.') |
... | ... | |
1279 | 1302 |
action => [ |
1280 | 1303 |
t8('Post Payment'), |
1281 | 1304 |
submit => [ '#form', { action => "post_payment" } ], |
1282 |
disabled => !$::form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
1305 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1306 |
: !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1307 |
: undef, |
|
1283 | 1308 |
], |
1284 | 1309 |
action => [ t8('Mark as paid'), |
1285 | 1310 |
submit => [ '#form', { action => "mark_as_paid" } ], |
1286 | 1311 |
confirm => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'), |
1287 |
disabled => !$::form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
1312 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1313 |
: !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1314 |
: undef, |
|
1288 | 1315 |
only_if => $::instance_conf->get_is_show_mark_as_paid, |
1289 | 1316 |
], |
1290 | 1317 |
], # end of combobox "Post" |
... | ... | |
1294 | 1321 |
submit => [ '#form', { action => "storno" } ], |
1295 | 1322 |
checks => [ 'kivi.validate_form', 'kivi.AR.check_fields_before_posting' ], |
1296 | 1323 |
confirm => t8('Do you really want to cancel this invoice?'), |
1297 |
disabled => !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1298 |
: $has_storno ? t8('This invoice has been canceled already.') |
|
1299 |
: $is_storno ? t8('Reversal invoices cannot be canceled.') |
|
1300 |
: $::form->{totalpaid} ? t8('Invoices with payments cannot be canceled.') |
|
1301 |
: undef, |
|
1324 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1325 |
: !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1326 |
: $has_storno ? t8('This invoice has been canceled already.') |
|
1327 |
: $is_storno ? t8('Reversal invoices cannot be canceled.') |
|
1328 |
: $::form->{totalpaid} ? t8('Invoices with payments cannot be canceled.') |
|
1329 |
: undef, |
|
1302 | 1330 |
], |
1303 | 1331 |
action => [ t8('Delete'), |
1304 | 1332 |
submit => [ '#form', { action => "delete" } ], |
1305 | 1333 |
confirm => t8('Do you really want to delete this object?'), |
1306 |
disabled => !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1334 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1335 |
: !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1307 | 1336 |
: $change_never ? t8('Changing invoices has been disabled in the configuration.') |
1308 | 1337 |
: $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.') |
1309 | 1338 |
: $is_closed ? t8('The billing period has already been locked.') |
... | ... | |
1319 | 1348 |
t8('Use As New'), |
1320 | 1349 |
submit => [ '#form', { action => "use_as_new" } ], |
1321 | 1350 |
checks => [ 'kivi.validate_form' ], |
1322 |
disabled => !$::form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
1351 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1352 |
: !$::form->{id} ? t8('This invoice has not been posted yet.') |
|
1353 |
: undef, |
|
1323 | 1354 |
], |
1324 | 1355 |
], # end of combobox "Workflow" |
1325 | 1356 |
|
... | ... | |
1337 | 1368 |
], |
1338 | 1369 |
action => [ |
1339 | 1370 |
t8('Record templates'), |
1340 |
call => [ 'kivi.RecordTemplate.popup', 'ar_transaction' ], |
|
1371 |
call => [ 'kivi.RecordTemplate.popup', 'ar_transaction' ], |
|
1372 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') : undef, |
|
1341 | 1373 |
], |
1342 | 1374 |
action => [ |
1343 | 1375 |
t8('Drafts'), |
1344 | 1376 |
call => [ 'kivi.Draft.popup', 'ar', 'invoice', $::form->{draft_id}, $::form->{draft_description} ], |
1345 |
disabled => $::form->{id} ? t8('This invoice has already been posted.') |
|
1346 |
: $is_closed ? t8('The billing period has already been locked.') |
|
1347 |
: undef, |
|
1377 |
disabled => !$may_edit_create ? t8('You must not change this AR transaction.') |
|
1378 |
: $::form->{id} ? t8('This invoice has already been posted.') |
|
1379 |
: $is_closed ? t8('The billing period has already been locked.') |
|
1380 |
: undef, |
|
1348 | 1381 |
], |
1349 | 1382 |
], # end of combobox "more" |
1350 | 1383 |
); |
bin/mozilla/io.pl | ||
---|---|---|
126 | 126 |
sub display_row { |
127 | 127 |
$main::lxdebug->enter_sub(); |
128 | 128 |
|
129 |
_check_io_auth(); |
|
130 |
|
|
131 | 129 |
my $form = $main::form; |
132 | 130 |
my %myconfig = %main::myconfig; |
133 | 131 |
my $locale = $main::locale; |
... | ... | |
1655 | 1653 |
my $form = $main::form; |
1656 | 1654 |
my %myconfig = %main::myconfig; |
1657 | 1655 |
|
1658 |
_check_io_auth(); |
|
1659 |
|
|
1660 | 1656 |
$form->{"taxaccounts"} =~ s/\s*$//; |
1661 | 1657 |
$form->{"taxaccounts"} =~ s/^\s*//; |
1662 | 1658 |
foreach my $accno (split(/\s*/, $form->{"taxaccounts"})) { |
bin/mozilla/is.pl | ||
---|---|---|
58 | 58 |
|
59 | 59 |
# end of main |
60 | 60 |
|
61 |
sub _may_view_or_edit_this_invoice { |
|
62 |
return 1 if $::auth->assert('invoice_edit', 1); # may edit all invoices |
|
63 |
return 0 if !$::form->{id}; # creating new invoices isn't allowed without invoice_edit |
|
64 |
return 0 if !$::form->{globalproject_id}; # existing records without a project ID are not allowed |
|
65 |
return SL::DB::Project->new(id => $::form->{globalproject_id})->load->may_employee_view_project_invoices(SL::DB::Manager::Employee->current); |
|
66 |
} |
|
67 |
|
|
68 |
sub _assert_access { |
|
69 |
my $cache = $::request->cache('is.pl::_assert_access'); |
|
70 |
|
|
71 |
$cache->{_may_view_or_edit_this_invoice} = _may_view_or_edit_this_invoice() if !exists $cache->{_may_view_or_edit_this_invoice}; |
|
72 |
$::form->show_generic_error($::locale->text("You do not have the permissions to access this function.")) if ! $cache->{_may_view_or_edit_this_invoice}; |
|
73 |
} |
|
74 |
|
|
61 | 75 |
sub add { |
62 | 76 |
$main::lxdebug->enter_sub(); |
63 | 77 |
|
... | ... | |
92 | 106 |
sub edit { |
93 | 107 |
$main::lxdebug->enter_sub(); |
94 | 108 |
|
109 |
# Delay access check to after the invoice's been loaded in |
|
110 |
# "invoice_links" so that project-specific invoice rights can be |
|
111 |
# evaluated. |
|
112 |
|
|
95 | 113 |
my $form = $main::form; |
96 | 114 |
my $locale = $main::locale; |
97 | 115 |
|
98 |
$main::auth->assert('invoice_edit'); |
|
99 |
|
|
100 | 116 |
$form->{show_details} = $::myconfig{show_form_details}; |
101 | 117 |
$form->{taxincluded_changed_by_user} = 1; |
102 | 118 |
|
... | ... | |
134 | 150 |
sub invoice_links { |
135 | 151 |
$main::lxdebug->enter_sub(); |
136 | 152 |
|
153 |
# Delay access check to after the invoice's been loaded so that |
|
154 |
# project-specific invoice rights can be evaluated. |
|
155 |
|
|
137 | 156 |
my $form = $main::form; |
138 | 157 |
my %myconfig = %main::myconfig; |
139 | 158 |
|
140 |
$main::auth->assert('invoice_edit'); |
|
141 |
|
|
142 | 159 |
$form->{vc} = 'customer'; |
143 | 160 |
|
144 | 161 |
# create links |
145 | 162 |
$form->create_links("AR", \%myconfig, "customer"); |
146 | 163 |
|
164 |
_assert_access(); |
|
165 |
|
|
147 | 166 |
my $editing = $form->{id}; |
148 | 167 |
|
149 | 168 |
$form->backup_vars(qw(payment_id language_id taxzone_id salesman_id |
... | ... | |
206 | 225 |
sub prepare_invoice { |
207 | 226 |
$main::lxdebug->enter_sub(); |
208 | 227 |
|
228 |
_assert_access(); |
|
229 |
|
|
209 | 230 |
my $form = $main::form; |
210 | 231 |
my %myconfig = %main::myconfig; |
211 | 232 |
|
212 |
$main::auth->assert('invoice_edit'); |
|
213 |
|
|
214 | 233 |
if ($form->{type} eq "credit_note") { |
215 | 234 |
$form->{type} = "credit_note"; |
216 | 235 |
$form->{formname} = "credit_note"; |
... | ... | |
258 | 277 |
my $change_on_same_day_only = $::instance_conf->get_is_changeable == 2 && ($form->current_date(\%::myconfig) ne $form->{gldate}); |
259 | 278 |
my $payments_balanced = ($::form->{oldtotalpaid} == 0); |
260 | 279 |
my $has_storno = ($::form->{storno} && !$::form->{storno_id}); |
280 |
my $may_edit_create = $::auth->assert('invoice_edit', 1); |
|
261 | 281 |
|
262 | 282 |
for my $bar ($::request->layout->get('actionbar')) { |
263 | 283 |
$bar->add( |
264 | 284 |
action => [ |
265 | 285 |
t8('Update'), |
266 | 286 |
submit => [ '#form', { action => "update" } ], |
267 |
disabled => $form->{locked} ? t8('The billing period has already been locked.') : undef, |
|
287 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
288 |
: $form->{locked} ? t8('The billing period has already been locked.') |
|
289 |
: undef, |
|
268 | 290 |
id => 'update_button', |
269 | 291 |
checks => [ 'kivi.validate_form' ], |
270 | 292 |
accesskey => 'enter', |
... | ... | |
275 | 297 |
t8('Post'), |
276 | 298 |
submit => [ '#form', { action => "post" } ], |
277 | 299 |
checks => [ 'kivi.validate_form' ], |
278 |
disabled => $form->{locked} ? t8('The billing period has already been locked.') |
|
300 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
301 |
: $form->{locked} ? t8('The billing period has already been locked.') |
|
279 | 302 |
: $form->{storno} ? t8('A canceled invoice cannot be posted.') |
280 | 303 |
: ($form->{id} && $change_never) ? t8('Changing invoices has been disabled in the configuration.') |
281 | 304 |
: ($form->{id} && $change_on_same_day_only) ? t8('Invoices can only be changed on the day they are posted.') |
... | ... | |
285 | 308 |
t8('Post Payment'), |
286 | 309 |
submit => [ '#form', { action => "post_payment" } ], |
287 | 310 |
checks => [ 'kivi.validate_form' ], |
288 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
311 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
312 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
313 |
: undef, |
|
289 | 314 |
], |
290 | 315 |
action => [ t8('Mark as paid'), |
291 | 316 |
submit => [ '#form', { action => "mark_as_paid" } ], |
292 | 317 |
confirm => t8('This will remove the invoice from showing as unpaid even if the unpaid amount does not match the amount. Proceed?'), |
293 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
318 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
319 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
320 |
: undef, |
|
294 | 321 |
only_if => $::instance_conf->get_is_show_mark_as_paid, |
295 | 322 |
], |
296 | 323 |
], # end of combobox "Post" |
... | ... | |
300 | 327 |
submit => [ '#form', { action => "storno" } ], |
301 | 328 |
confirm => t8('Do you really want to cancel this invoice?'), |
302 | 329 |
checks => [ 'kivi.validate_form' ], |
303 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') |
|
330 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
331 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
304 | 332 |
: !$payments_balanced ? t8('Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount') |
305 | 333 |
: undef, |
306 | 334 |
], |
... | ... | |
308 | 336 |
submit => [ '#form', { action => "delete" } ], |
309 | 337 |
confirm => t8('Do you really want to delete this object?'), |
310 | 338 |
checks => [ 'kivi.validate_form' ], |
311 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') |
|
339 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
340 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
312 | 341 |
: $form->{locked} ? t8('The billing period has already been locked.') |
313 | 342 |
: $change_never ? t8('Changing invoices has been disabled in the configuration.') |
314 | 343 |
: $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.') |
... | ... | |
325 | 354 |
t8('Use As New'), |
326 | 355 |
submit => [ '#form', { action => "use_as_new" } ], |
327 | 356 |
checks => [ 'kivi.validate_form' ], |
328 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
357 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
358 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
359 |
: undef, |
|
329 | 360 |
], |
330 | 361 |
action => [ |
331 | 362 |
t8('Credit Note'), |
332 | 363 |
submit => [ '#form', { action => "credit_note" } ], |
333 | 364 |
checks => [ 'kivi.validate_form' ], |
334 |
disabled => $form->{type} eq "credit_note" ? t8('Credit notes cannot be converted into other credit notes.') |
|
365 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
366 |
: $form->{type} eq "credit_note" ? t8('Credit notes cannot be converted into other credit notes.') |
|
335 | 367 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
336 | 368 |
: undef, |
337 | 369 |
], |
... | ... | |
349 | 381 |
($form->{id} ? t8('Print') : t8('Preview')), |
350 | 382 |
call => [ 'kivi.SalesPurchase.show_print_dialog', $form->{id} ? 'print' : 'preview' ], |
351 | 383 |
checks => [ 'kivi.validate_form' ], |
352 |
disabled => !$form->{id} && $form->{locked} ? t8('The billing period has already been locked.') : undef, |
|
384 |
disabled => !$may_edit_create ? t8('You must not print this invoice.') |
|
385 |
: !$form->{id} && $form->{locked} ? t8('The billing period has already been locked.') |
|
386 |
: undef, |
|
353 | 387 |
], |
354 | 388 |
action => [ t8('Print and Post'), |
355 | 389 |
call => [ 'kivi.SalesPurchase.show_print_dialog', $form->{id} ? 'print' : 'print_and_post' ], |
356 | 390 |
checks => [ 'kivi.validate_form' ], |
357 |
disabled => $form->{id} ? t8('This invoice has already been posted.') : undef,, |
|
391 |
disabled => !$may_edit_create ? t8('You must not print this invoice.') |
|
392 |
: $form->{id} ? t8('This invoice has already been posted.') |
|
393 |
: undef,, |
|
358 | 394 |
], |
359 | 395 |
action => [ t8('E Mail'), |
360 | 396 |
call => [ 'kivi.SalesPurchase.show_email_dialog' ], |
361 | 397 |
checks => [ 'kivi.validate_form' ], |
362 |
disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, |
|
398 |
disabled => !$may_edit_create ? t8('You must not print this invoice.') |
|
399 |
: !$form->{id} ? t8('This invoice has not been posted yet.') |
|
400 |
: undef, |
|
363 | 401 |
], |
364 | 402 |
], # end of combobox "Export" |
365 | 403 |
|
... | ... | |
378 | 416 |
action => [ |
379 | 417 |
t8('Drafts'), |
380 | 418 |
call => [ 'kivi.Draft.popup', 'is', 'invoice', $form->{draft_id}, $form->{draft_description} ], |
381 |
disabled => $form->{id} ? t8('This invoice has already been posted.') |
|
382 |
: $form->{locked} ? t8('The billing period has already been locked.') |
|
383 |
: undef, |
|
419 |
disabled => !$may_edit_create ? t8('You must not change this invoice.') |
|
420 |
: $form->{id} ? t8('This invoice has already been posted.') |
|
421 |
: $form->{locked} ? t8('The billing period has already been locked.') |
|
422 |
: undef, |
|
384 | 423 |
], |
385 | 424 |
], # end of combobox "more" |
386 | 425 |
); |
... | ... | |
391 | 430 |
sub form_header { |
392 | 431 |
$main::lxdebug->enter_sub(); |
393 | 432 |
|
433 |
_assert_access(); |
|
434 |
|
|
394 | 435 |
my $form = $main::form; |
395 | 436 |
my %myconfig = %main::myconfig; |
396 | 437 |
my $locale = $main::locale; |
397 | 438 |
my $cgi = $::request->{cgi}; |
398 | 439 |
|
399 |
$main::auth->assert('invoice_edit'); |
|
400 |
|
|
401 | 440 |
my %TMPL_VAR = (); |
402 | 441 |
my @custom_hiddens; |
403 | 442 |
|
... | ... | |
526 | 565 |
sub form_footer { |
527 | 566 |
$main::lxdebug->enter_sub(); |
528 | 567 |
|
568 |
_assert_access(); |
|
569 |
|
|
529 | 570 |
my $form = $main::form; |
530 | 571 |
my %myconfig = %main::myconfig; |
531 | 572 |
my $locale = $main::locale; |
532 | 573 |
|
533 |
$main::auth->assert('invoice_edit'); |
|
534 |
|
|
535 | 574 |
$form->{invtotal} = $form->{invsubtotal}; |
536 | 575 |
|
537 | 576 |
# note rows |
... | ... | |
658 | 697 |
sub update { |
659 | 698 |
$main::lxdebug->enter_sub(); |
660 | 699 |
|
700 |
_assert_access(); |
|
701 |
|
|
661 | 702 |
my $form = $main::form; |
662 | 703 |
my %myconfig = %main::myconfig; |
663 | 704 |
|
664 |
$main::auth->assert('invoice_edit'); |
|
665 |
|
|
666 | 705 |
my ($recursive_call) = @_; |
667 | 706 |
|
668 | 707 |
$form->{print_and_post} = 0 if $form->{second_run}; |
... | ... | |
1180 | 1219 |
sub display_form { |
1181 | 1220 |
$::lxdebug->enter_sub; |
1182 | 1221 |
|
1183 |
$::auth->assert('invoice_edit');
|
|
1222 |
_assert_access();
|
|
1184 | 1223 |
|
1185 | 1224 |
relink_accounts(); |
1186 | 1225 |
|
locale/de/all | ||
---|---|---|
250 | 250 |
'All as list' => 'Alle als Liste', |
251 | 251 |
'All changes in that file have been reverted.' => 'Alle Änderungen in dieser Datei wurden rückgängig gemacht.', |
252 | 252 |
'All clients' => 'Alle Mandanten', |
253 |
'All employees' => 'Alle Angestellten', |
|
253 | 254 |
'All general ledger entries' => 'Alle Hauptbucheinträge', |
254 | 255 |
'All groups' => 'Alle Gruppen', |
255 | 256 |
'All modules' => 'Alle Module', |
... | ... | |
1233 | 1234 |
'Employee (database ID)' => 'Bearbeiter (Datenbank-ID)', |
1234 | 1235 |
'Employee from the original invoice' => 'Mitarbeiter der Ursprungs-Rechnung', |
1235 | 1236 |
'Employees' => 'Benutzer', |
1237 |
'Employees with read access to the project\'s invoices' => 'Angestellte mit Leserechten auf die Projektrechnungen', |
|
1236 | 1238 |
'Empty selection for warehouse will not be added, even if the old bin is still visible (use back and forth to edit again).' => 'Leere Lager-Auswahl wird ignoriert, selbst wenn noch ein Lagerplatz ausgewählt ist. Alle Daten können durch zurück und vorwärts korrigiert werden.', |
1237 | 1239 |
'Empty transaction!' => 'Buchung ist leer!', |
1238 | 1240 |
'Enabled Quick Searched' => 'Aktivierte Schnellsuchen', |
... | ... | |
2267 | 2269 |
'Periodic inventory' => 'Aufwandsmethode', |
2268 | 2270 |
'Periodic invoices active' => 'Wiederkehrende Rechnungen aktiv', |
2269 | 2271 |
'Periodic invoices inactive' => 'Wiederkehrende Rechnungen inaktiv', |
2272 |
'Permissions for invoices' => 'Ansehrechte für Rechnungen', |
|
2270 | 2273 |
'Perpetual inventory' => 'Bestandsmethode', |
2271 | 2274 |
'Personal settings' => 'Persönliche Einstellungen', |
2272 | 2275 |
'Phone' => 'Telefon', |
... | ... | |
2451 | 2454 |
'Project type' => 'Projekttyp', |
2452 | 2455 |
'Project types' => 'Projekttypen', |
2453 | 2456 |
'Projects' => 'Projekte', |
2457 |
'Projects: edit the list of employees allowed to view invoices' => 'Projekte: Liste der Angestellten bearbeiten, die Projektrechnungen ansehen dürfen', |
|
2454 | 2458 |
'Projecttransactions' => 'Projektbuchungen', |
2455 | 2459 |
'Proposal' => 'Vorschlag', |
2456 | 2460 |
'Proposals' => 'Vorschläge', |
... | ... | |
3922 | 3926 |
'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.', |
3923 | 3927 |
'You must chose a user.' => 'Sie müssen einen Benutzer auswählen.', |
3924 | 3928 |
'You must enter a name for your new print templates.' => 'Sie müssen einen Namen für die neuen Druckvorlagen angeben.', |
3929 |
'You must not change this AR transaction.' => 'Sie dürfen diese Debitorenbuchung nicht verändern.', |
|
3930 |
'You must not change this invoice.' => 'Sie dürfen diese Rechnung nicht verändern.', |
|
3931 |
'You must not print this invoice.' => 'Sie dürfen diese Rechnung nicht drucken.', |
|
3925 | 3932 |
'You must select existing print templates or create a new set.' => 'Sie müssen vorhandene Druckvorlagen auswählen oder einen neuen Satz anlegen.', |
3926 | 3933 |
'You should create a backup of the database before proceeding because the backup might not be reversible.' => 'Sie sollten eine Sicherungskopie der Datenbank erstellen, bevor Sie fortfahren, da die Aktualisierung unter Umständen nicht umkehrbar ist.', |
3927 | 3934 |
'You\'re not editing a file.' => 'Sie bearbeiten momentan keine Datei.', |
menus/user/00-erp.yaml | ||
---|---|---|
308 | 308 |
name: Invoices, Credit Notes & AR Transactions |
309 | 309 |
icon: invoices_report |
310 | 310 |
order: 500 |
311 |
access: invoice_edit |
|
312 | 311 |
module: ar.pl |
313 | 312 |
params: |
314 | 313 |
action: search |
sql/Pg-upgrade2-auth/rights_for_viewing_project_specific_invoices.sql | ||
---|---|---|
1 |
-- @tag: rights_for_viewing_project_specific_invoices |
|
2 |
-- @description: Rechte zum Anzeigen von Rechnungen, die zu Projekten gehören |
|
3 |
-- @depends: release_3_5_3 |
|
4 |
-- @locales: Projects: edit the list of employees allowed to view invoices |
|
5 |
INSERT INTO auth.master_rights (position, name, description, category) |
|
6 |
VALUES ( |
|
7 |
(SELECT position + 2 |
|
8 |
FROM auth.master_rights |
|
9 |
WHERE name = 'project_edit'), |
|
10 |
'project_edit_view_invoices_permission', |
|
11 |
'Projects: edit the list of employees allowed to view invoices', |
|
12 |
false |
|
13 |
); |
|
14 |
|
|
15 |
INSERT INTO auth.group_rights (group_id, "right", granted) |
|
16 |
SELECT id, 'project_edit_view_invoices_permission', true |
|
17 |
FROM auth.group |
|
18 |
WHERE name = 'Vollzugriff'; |
sql/Pg-upgrade2/add_emloyee_project_assignment_for_viewing_invoices.sql | ||
---|---|---|
1 |
-- @tag: add_emloyee_project_assignment_for_viewing_invoices |
|
2 |
-- @description: Mitarbeiter*innen Projekten zuweisen können, damit diese Projektrechnungen anschauen dürfen |
|
3 |
-- @depends: release_3_5_3 |
|
4 |
CREATE TABLE employee_project_invoices ( |
|
5 |
employee_id INTEGER NOT NULL, |
|
6 |
project_id INTEGER NOT NULL, |
|
7 |
|
|
8 |
CONSTRAINT employee_project_invoices_pkey PRIMARY KEY (employee_id, project_id), |
|
9 |
CONSTRAINT employee_project_invoices_employee_id_fkey FOREIGN KEY (employee_id) REFERENCES employee (id) ON DELETE CASCADE, |
|
10 |
CONSTRAINT employee_project_invoices_project_id_fkey FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE |
|
11 |
); |
templates/webpages/project/_invoice_permissions.html | ||
---|---|---|
1 |
[%- USE LxERP -%][%- USE L -%]<div class="clearfix"> |
|
2 |
[% L.select_tag("project.employee_invoice_permissions[]", SELF.employees, id="employee_invoice_permissions", title_key="safe_name", default=SELF.project.employee_invoice_permissions, default_value_key='id', multiple=1) %] |
|
3 |
[% L.multiselect2side("employee_invoice_permissions", labelsx => LxERP.t8("All employees"), labeldx => LxERP.t8("Employees with read access to the project's invoices")) %] |
|
4 |
</div> |
templates/webpages/project/form.html | ||
---|---|---|
17 | 17 |
[%- IF CUSTOM_VARIABLES.size %] |
18 | 18 |
<li><a href="#custom_variables">[% 'Custom Variables' | $T8 %]</a></li> |
19 | 19 |
[%- END %] |
20 |
[%- IF SELF.may_edit_invoice_permissions %] |
|
21 |
<li><a href="#invoice_permissions">[% 'Permissions for invoices' | $T8 %]</a></li> |
|
22 |
[%- END %] |
|
20 | 23 |
[%- IF SELF.project.id and AUTH.assert('record_links', 1) %] |
21 | 24 |
<li><a href="#linked_records">[% 'Linked Records' | $T8 %]</a></li> |
22 | 25 |
[%- END %] |
... | ... | |
32 | 35 |
</div> |
33 | 36 |
[%- END %] |
34 | 37 |
|
38 |
[%- IF SELF.may_edit_invoice_permissions %] |
|
39 |
<div id="invoice_permissions"> |
|
40 |
[%- PROCESS 'project/_invoice_permissions.html' %] |
|
41 |
</div> |
|
42 |
[%- END %] |
|
43 |
|
|
35 | 44 |
[%- IF SELF.project.id and AUTH.assert('record_links', 1) %] |
36 | 45 |
<div id="linked_records"> |
37 | 46 |
[%- PROCESS 'project/_linked_records.html' records=SELF.linked_records %] |
Auch abrufbar als: Unified diff
Berechtigung, Verkaufsrechnungen persönlich zugeordneter Projekte einzusehen
Man kann nun Mitarbeiter*innen zu Projekten zuordnen, indem man sie in
den Projektstammdaten hinzufügt.
Ist eine Mitarbeiter*in zu einem Projekt zugeordnet, so darf sie alle
Rechnungen ansehen, die über die Projektnummer der Rechnung (nicht der
Positionen) dem Projekt zugeordnet sind, auch dann, wenn sie nicht das
allgemeine Recht zum Erstellen und Ansehen von Rechnungen hat.
Verändern oder Ausdrucken der Rechnungen ist nicht gestattet.
Die Verwaltung dieser Projektberechtigungen ist über ein neues
Gruppenrecht eingeschränkt.
Betrifft Verkaufsrechnungen, Verkaufsgutschriften und Debitorenbuchungen.