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