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