Sprache auf ungültig setzen im Admin-Bereich
[kivitendo-erp.git] / SL / Controller / SimpleSystemSetting.pm
1 package SL::Controller::SimpleSystemSetting;
2
3 use strict;
4 use utf8;
5
6 use parent qw(SL::Controller::Base);
7
8 use SL::Helper::Flash;
9 use SL::Locale::String;
10 use SL::DB::Default;
11 use SL::System::Process;
12
13 use Rose::Object::MakeMethods::Generic (
14   scalar                  => [ qw(type config) ],
15   'scalar --get_set_init' => [ qw(defaults object all_objects class manager_class list_attributes list_url supports_reordering) ],
16 );
17
18 __PACKAGE__->run_before('check_type_and_auth');
19 __PACKAGE__->run_before('setup_javascript', only => [ qw(add create edit update delete) ]);
20
21 # Make locales.pl happy: $self->render("simple_system_setting/_default_form")
22
23 my %supported_types = (
24   bank_account => {
25     # Make locales.pl happy: $self->render("simple_system_setting/_bank_account_form")
26     class  => 'BankAccount',
27     titles => {
28       list => t8('Bank accounts'),
29       add  => t8('Add bank account'),
30       edit => t8('Edit bank account'),
31     },
32     list_attributes => [
33       { method => 'name',                                      title => t8('Name'), },
34       { method => 'iban',                                      title => t8('IBAN'), },
35       { method => 'bank',                                      title => t8('Bank'), },
36       { method => 'bank_code',                                 title => t8('Bank code'), },
37       { method => 'bank_account_id',                           title => t8('Bank Account Id Number (Swiss)'), },
38       { method => 'bic',                                       title => t8('BIC'), },
39       {                                                        title => t8('Use for Factur-X/ZUGFeRD'), formatter => sub { $_[0]->use_for_zugferd ? t8('yes') : t8('no') } },
40       {                                                        title => t8('Use for Swiss QR-Bill'), formatter => sub { $_[0]->use_for_qrbill ? t8('yes') : t8('no') } },
41       { method => 'reconciliation_starting_date_as_date',      title => t8('Date'),    align => 'right' },
42       { method => 'reconciliation_starting_balance_as_number', title => t8('Balance'), align => 'right' },
43     ],
44   },
45
46   business => {
47     # Make locales.pl happy: $self->render("simple_system_setting/_business_form")
48     class  => 'Business',
49     titles => {
50       list => t8('Businesses'),
51       add  => t8('Add business'),
52       edit => t8('Edit business'),
53     },
54     list_attributes => [
55       { method => 'description',         title => t8('Description'), },
56       {                                  title => t8('Discount'), formatter => sub { $_[0]->discount_as_percent . ' %' }, align => 'right' },
57       { method => 'customernumberinit',  title => t8('Customernumberinit'), },
58     ],
59   },
60
61   contact_department => {
62     class  => 'ContactDepartment',
63     auth   => 'config',
64     titles => {
65       list => t8('Contact Departments'),
66       add  => t8('Add department'),
67       edit => t8('Edit department'),
68     },
69   },
70
71   contact_title => {
72     class  => 'ContactTitle',
73     auth   => 'config',
74     titles => {
75       list => t8('Contact Titles'),
76       add  => t8('Add title'),
77       edit => t8('Edit title'),
78     },
79   },
80
81   department => {
82     class  => 'Department',
83     titles => {
84       list => t8('Departments'),
85       add  => t8('Add department'),
86       edit => t8('Edit department'),
87     },
88   },
89
90   greeting => {
91     class  => 'Greeting',
92     auth   => 'config',
93     titles => {
94       list => t8('Greetings'),
95       add  => t8('Add greeting'),
96       edit => t8('Edit greeting'),
97     },
98   },
99
100   language => {
101     # Make locales.pl happy: $self->render("simple_system_setting/_language_form")
102     class  => 'Language',
103     titles => {
104       list => t8('Languages'),
105       add  => t8('Add language'),
106       edit => t8('Edit language'),
107     },
108     list_attributes => [
109       { method => 'description',   title => t8('Description'), },
110       { method => 'template_code', title => t8('Template Code'), },
111       { method => 'article_code',  title => t8('Article Code'), },
112       {                            title => t8('Number Format'), formatter => sub { $_[0]->output_numberformat || t8('use program settings') } },
113       {                            title => t8('Date Format'),   formatter => sub { $_[0]->output_dateformat   || t8('use program settings') } },
114       {                            title => t8('Long Dates'),    formatter => sub { $_[0]->output_longdates ? t8('yes') : t8('no') } },
115       {                            title => t8('Obsolete'),      formatter => sub { $_[0]->obsolete  ? t8('yes') : t8('no') } },
116     ],
117   },
118
119   part_classification => {
120     # Make locales.pl happy: $self->render("simple_system_setting/_part_classification_form")
121     class  => 'PartClassification',
122     titles => {
123       list => t8('Part classifications'),
124       add  => t8('Add part classification'),
125       edit => t8('Edit part classification'),
126     },
127     list_attributes => [
128       { title => t8('Description'),       formatter => sub { t8($_[0]->description) } },
129       { title => t8('Type abbreviation'), formatter => sub { t8($_[0]->abbreviation) } },
130       { title => t8('Used for Purchase'), formatter => sub { $_[0]->used_for_purchase ? t8('yes') : t8('no') } },
131       { title => t8('Used for Sale'),     formatter => sub { $_[0]->used_for_sale     ? t8('yes') : t8('no') } },
132       { title => t8('Report separately'), formatter => sub { $_[0]->report_separate   ? t8('yes') : t8('no') } },
133     ],
134   },
135
136   parts_group => {
137     # Make locales.pl happy: $self->render("simple_system_setting/_parts_group_form")
138     class  => 'PartsGroup',
139     titles => {
140       list => t8('Partsgroups'),
141       add  => t8('Add partsgroup'),
142       edit => t8('Edit partsgroup'),
143     },
144     list_attributes => [
145       { method => 'partsgroup', title => t8('Description') },
146       { method => 'obsolete',   title => t8('Obsolete'), formatter => sub { $_[0]->obsolete ? t8('yes') : t8('no') } },
147     ],
148   },
149
150   price_factor => {
151     # Make locales.pl happy: $self->render("simple_system_setting/_price_factor_form")
152     class  => 'PriceFactor',
153     titles => {
154       list => t8('Price Factors'),
155       add  => t8('Add Price Factor'),
156       edit => t8('Edit Price Factor'),
157     },
158     list_attributes => [
159       { method => 'description',      title => t8('Description') },
160       { method => 'factor_as_number', title => t8('Factor'), align => 'right' },
161     ],
162   },
163
164   pricegroup => {
165     # Make locales.pl happy: $self->render("simple_system_setting/_pricegroup_form")
166     class  => 'Pricegroup',
167     titles => {
168       list => t8('Pricegroups'),
169       add  => t8('Add pricegroup'),
170       edit => t8('Edit pricegroup'),
171     },
172     list_attributes => [
173       { method => 'pricegroup', title => t8('Description') },
174       { method => 'obsolete',   title => t8('Obsolete'), formatter => sub { $_[0]->obsolete ? t8('yes') : t8('no') } },
175     ],
176   },
177
178   project_status => {
179     class  => 'ProjectStatus',
180     titles => {
181       list => t8('Project statuses'),
182       add  => t8('Add project status'),
183       edit => t8('Edit project status'),
184     },
185   },
186
187   project_type => {
188     class  => 'ProjectType',
189     titles => {
190       list => t8('Project types'),
191       add  => t8('Add project type'),
192       edit => t8('Edit project type'),
193     },
194   },
195
196   requirement_spec_acceptance_status => {
197     # Make locales.pl happy: $self->render("simple_system_setting/_requirement_spec_acceptance_status_form")
198     class  => 'RequirementSpecAcceptanceStatus',
199     titles => {
200       list => t8('Acceptance Statuses'),
201       add  => t8('Add acceptance status'),
202       edit => t8('Edit acceptance status'),
203     },
204     list_attributes => [
205       { method => 'name',        title => t8('Name') },
206       { method => 'description', title => t8('Description') },
207     ],
208   },
209
210   requirement_spec_complexity => {
211     class  => 'RequirementSpecComplexity',
212     titles => {
213       list => t8('Complexities'),
214       add  => t8('Add complexity'),
215       edit => t8('Edit complexity'),
216     },
217   },
218
219   requirement_spec_predefined_text => {
220     # Make locales.pl happy: $self->render("simple_system_setting/_requirement_spec_predefined_text_form")
221     class  => 'RequirementSpecPredefinedText',
222     titles => {
223       list => t8('Pre-defined Texts'),
224       add  => t8('Add pre-defined text'),
225       edit => t8('Edit pre-defined text'),
226     },
227     list_attributes => [
228       { method => 'description', title => t8('Description') },
229       { method => 'title',       title => t8('Title') },
230       {                          title => t8('Content'),                 formatter => sub { my $t = $_[0]->text_as_stripped_html; length($t) > 50 ? substr($t, 0, 50) . '…' : $t } },
231       {                          title => t8('Useable for text blocks'), formatter => sub { $_[0]->useable_for_text_blocks ? t8('yes') : t8('no') } },
232       {                          title => t8('Useable for sections'),    formatter => sub { $_[0]->useable_for_sections    ? t8('yes') : t8('no') } },
233     ],
234   },
235
236   requirement_spec_risk => {
237     class  => 'RequirementSpecRisk',
238     titles => {
239       list => t8('Risk levels'),
240       add  => t8('Add risk level'),
241       edit => t8('Edit risk level'),
242     },
243   },
244
245   requirement_spec_status => {
246     # Make locales.pl happy: $self->render("simple_system_setting/_requirement_spec_status_form")
247     class  => 'RequirementSpecStatus',
248     titles => {
249       list => t8('Requirement Spec Statuses'),
250       add  => t8('Add requirement spec status'),
251       edit => t8('Edit requirement spec status'),
252     },
253     list_attributes => [
254       { method => 'name',        title => t8('Name') },
255       { method => 'description', title => t8('Description') },
256     ],
257   },
258
259   requirement_spec_type => {
260     # Make locales.pl happy: $self->render("simple_system_setting/_requirement_spec_type_form")
261     class  => 'RequirementSpecType',
262     titles => {
263       list => t8('Requirement Spec Types'),
264       add  => t8('Add requirement spec type'),
265       edit => t8('Edit requirement spec type'),
266     },
267     list_attributes => [
268       { method => 'description',                  title => t8('Description') },
269       { method => 'section_number_format',        title => t8('Section number format') },
270       { method => 'function_block_number_format', title => t8('Function block number format') },
271     ],
272   },
273
274   time_recording_article => {
275     # Make locales.pl happy: $self->render("simple_system_setting/_time_recording_article_form")
276     class  => 'TimeRecordingArticle',
277     auth   => 'config',
278     titles => {
279       list => t8('Time Recording Articles'),
280       add  => t8('Add time recording article'),
281       edit => t8('Edit time recording article'),
282     },
283     list_attributes => [
284       { title => t8('Article'), formatter => sub { $_[0]->part->displayable_name } },
285     ],
286   },
287
288 );
289
290 my @default_list_attributes = (
291   { method => 'description', title => t8('Description') },
292 );
293
294 #
295 # actions
296 #
297
298 sub action_list {
299   my ($self) = @_;
300
301   $self->setup_list_action_bar;
302   $self->render('simple_system_setting/list', title => $self->config->{titles}->{list});
303 }
304
305 sub action_new {
306   my ($self) = @_;
307
308   $self->object($self->class->new);
309   $self->render_form(title => $self->config->{titles}->{add});
310 }
311
312 sub action_edit {
313   my ($self) = @_;
314
315   $self->render_form(title => $self->config->{titles}->{edit});
316 }
317
318 sub action_create {
319   my ($self) = @_;
320
321   $self->object($self->class->new);
322   $self->create_or_update;
323 }
324
325 sub action_update {
326   my ($self) = @_;
327
328   $self->create_or_update;
329 }
330
331 sub action_delete {
332   my ($self) = @_;
333
334   if ($self->object->can('orphaned') && !$self->object->orphaned) {
335     flash_later('error', t8('The object is in use and cannot be deleted.'));
336
337   } elsif ( eval { $self->object->delete; 1; } ) {
338     flash_later('info',  t8('The object has been deleted.'));
339
340   } else {
341     flash_later('error', t8('The object is in use and cannot be deleted.'));
342   }
343
344   $self->redirect_to($self->list_url);
345 }
346
347 sub action_reorder {
348   my ($self) = @_;
349
350   $self->class->reorder_list(@{ $::form->{object_id} || [] });
351   $self->render(\'', { type => 'json' });
352 }
353
354 #
355 # filters
356 #
357
358 sub check_type_and_auth {
359   my ($self) = @_;
360
361   $self->type($::form->{type});
362   $self->config($supported_types{$self->type}) || die "Unsupported type";
363
364   $::auth->assert($self->config->{auth} || 'config');
365
366   my $pm = (map { s{::}{/}g; "${_}.pm" } $self->class)[0];
367   require $pm;
368
369   my $setup = "setup_" . $self->type;
370   $self->$setup if $self->can($setup);
371
372   1;
373 }
374
375 sub setup_javascript {
376   $::request->layout->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery);
377 }
378
379 sub init_class               { "SL::DB::"          . $_[0]->config->{class}                  }
380 sub init_manager_class       { "SL::DB::Manager::" . $_[0]->config->{class}                  }
381 sub init_object              { $_[0]->class->new(id => $::form->{id})->load                  }
382 sub init_all_objects         { $_[0]->manager_class->get_all_sorted                          }
383 sub init_list_url            { $_[0]->url_for(action => 'list', type => $_[0]->type)         }
384 sub init_supports_reordering { $_[0]->class->new->can('reorder_list')                        }
385 sub init_defaults            { SL::DB::Default->get                                          }
386
387 sub init_list_attributes {
388   my ($self) = @_;
389
390   my $method = "list_attributes_" . $self->type;
391
392   return $self->$method if $self->can($method);
393   return $self->config->{list_attributes} // \@default_list_attributes;
394 }
395
396 #
397 # helpers
398 #
399
400 sub create_or_update {
401   my ($self) = @_;
402   my $is_new = !$self->object->id;
403
404   my $params = delete($::form->{object}) || { };
405
406   $self->object->assign_attributes(%{ $params });
407
408   my @errors;
409
410   push @errors, $self->object->validate if $self->object->can('validate');
411
412   if (@errors) {
413     flash('error', @errors);
414     return $self->render_form(title => $self->config->{titles}->{$is_new ? 'add' : 'edit'});
415   }
416
417   $self->object->save;
418
419   flash_later('info', $is_new ? t8('The object has been created.') : t8('The object has been saved.'));
420
421   $self->redirect_to($self->list_url);
422 }
423
424 sub render_form {
425   my ($self, %params) = @_;
426
427   my $sub_form_template = SL::System::Process->exe_dir . '/templates/webpages/simple_system_setting/_' . $self->type . '_form.html';
428
429   $self->setup_render_form_action_bar;
430   $self->render(
431     'simple_system_setting/form',
432     %params,
433     sub_form_template => (-f $sub_form_template ? $self->type : 'default'),
434   );
435 }
436
437 #
438 # type-specific helper functions
439 #
440
441 sub setup_requirement_spec_acceptance_status {
442   my ($self) = @_;
443
444   no warnings 'once';
445   $self->{valid_names} = \@SL::DB::RequirementSpecAcceptanceStatus::valid_names;
446 }
447
448 sub setup_requirement_spec_status {
449   my ($self) = @_;
450
451   no warnings 'once';
452   $self->{valid_names} = \@SL::DB::RequirementSpecStatus::valid_names;
453 }
454
455 sub setup_language {
456   my ($self) = @_;
457
458   $self->{numberformats} = [ '1,000.00', '1000.00', '1.000,00', '1000,00', "1'000.00" ];
459   $self->{dateformats}   = [ qw(mm/dd/yy dd/mm/yy dd.mm.yy yyyy-mm-dd) ];
460 }
461
462 #
463 # action bar
464 #
465
466 sub setup_list_action_bar {
467   my ($self, %params) = @_;
468
469   for my $bar ($::request->layout->get('actionbar')) {
470     $bar->add(
471       link => [
472         t8('Add'),
473         link => $self->url_for(action => 'new', type => $self->type),
474       ],
475     );
476   }
477 }
478
479 sub setup_render_form_action_bar {
480   my ($self) = @_;
481
482   my $is_new         = !$self->object->id;
483   my $can_be_deleted = !$is_new
484                     && (!$self->object->can("orphaned")       || $self->object->orphaned)
485                     && (!$self->object->can("can_be_deleted") || $self->object->can_be_deleted);
486
487   for my $bar ($::request->layout->get('actionbar')) {
488     $bar->add(
489       action => [
490         t8('Save'),
491         submit    => [ '#form', { action => 'SimpleSystemSetting/' . ($is_new ? 'create' : 'update') } ],
492         checks    => [ 'kivi.validate_form' ],
493         accesskey => 'enter',
494       ],
495
496       action => [
497         t8('Delete'),
498         submit   => [ '#form', { action => 'SimpleSystemSetting/delete' } ],
499         confirm  => t8('Do you really want to delete this object?'),
500         disabled => $is_new          ? t8('This object has not been saved yet.')
501                   : !$can_be_deleted ? t8('The object is in use and cannot be deleted.')
502                   :                    undef,
503       ],
504
505       link => [
506         t8('Abort'),
507         link => $self->list_url,
508       ],
509     );
510   }
511   $::request->layout->add_javascripts('kivi.Validator.js');
512 }
513
514 1;
515
516 __END__
517
518 =encoding utf-8
519
520 =head1 NAME
521
522 SL::Controller::SimpleSystemSettings — a common CRUD controller for
523 various settings in the "System" menu
524
525 =head1 AUTHOR
526
527 Moritz Bunkus <m.bunkus@linet-services.de>
528
529 =cut