Belegvorlagen: Variablen in Texten nutzen können
[kivitendo-erp.git] / SL / DB / RecordTemplate.pm
1 package SL::DB::RecordTemplate;
2
3 use strict;
4
5 use DateTime::Format::Strptime;
6
7 use SL::DB::MetaSetup::RecordTemplate;
8 use SL::DB::Manager::RecordTemplate;
9
10 __PACKAGE__->meta->add_relationship(
11   record_template_items => {
12     type       => 'one to many',
13     class      => 'SL::DB::RecordTemplateItem',
14     column_map => { id => 'record_template_id' },
15   },
16 );
17
18 __PACKAGE__->meta->initialize;
19
20 sub items { goto &record_template_items; }
21
22 sub _replace_variables {
23   my ($self, %params) = @_;
24
25   foreach my $sub (@{ $params{fields} }) {
26     my $value = $params{object}->$sub;
27     next if ($value // '') eq '';
28
29     $value =~ s{ <\% ([a-z0-9_]+) ( \s+ format \s*=\s* (.*?) \s* )? \%> }{
30       my ($key, $format) = ($1, $3);
31       my $new_value;
32
33       if (!$params{variables}->{$key}) {
34         $new_value = '';
35
36       } elsif ($format) {
37         $new_value = DateTime::Format::Strptime->new(
38           pattern     => $format,
39           locale      => 'de_DE',
40           time_zone   => 'local',
41         )->format_datetime($params{variables}->{$key}->[0]);
42
43       } else {
44         $new_value = $params{variables}->{$1}->[1]->($params{variables}->{$1}->[0]);
45       }
46
47       $new_value;
48
49     }eigx;
50
51     $params{object}->$sub($value);
52   }
53 }
54
55 sub _generate_variables {
56   my ($self, $reference_date) = @_;
57
58   $reference_date           //= DateTime->today_local;
59   my @month_names             = (
60     $::locale->text('January'), $::locale->text('February'), $::locale->text('March'),     $::locale->text('April'),   $::locale->text('May'),      $::locale->text('June'),
61     $::locale->text('July'),    $::locale->text('August'),   $::locale->text('September'), $::locale->text('October'), $::locale->text('November'), $::locale->text('December'),
62   );
63
64   my $variables = {
65     current_quarter     => [ $reference_date->clone->truncate(to => 'month'),                        sub { $_[0]->quarter } ],
66     previous_quarter    => [ $reference_date->clone->truncate(to => 'month')->subtract(months => 3), sub { $_[0]->quarter } ],
67     next_quarter        => [ $reference_date->clone->truncate(to => 'month')->add(     months => 3), sub { $_[0]->quarter } ],
68
69     current_month       => [ $reference_date->clone->truncate(to => 'month'),                        sub { $_[0]->month } ],
70     previous_month      => [ $reference_date->clone->truncate(to => 'month')->subtract(months => 1), sub { $_[0]->month } ],
71     next_month          => [ $reference_date->clone->truncate(to => 'month')->add(     months => 1), sub { $_[0]->month } ],
72
73     current_month_long  => [ $reference_date->clone->truncate(to => 'month'),                        sub { $month_names[ $_[0]->month - 1 ] } ],
74     previous_month_long => [ $reference_date->clone->truncate(to => 'month')->subtract(months => 1), sub { $month_names[ $_[0]->month - 1 ] } ],
75     next_month_long     => [ $reference_date->clone->truncate(to => 'month')->add(     months => 1), sub { $month_names[ $_[0]->month - 1 ] } ],
76
77     current_year        => [ $reference_date->clone->truncate(to => 'year'),                         sub { $_[0]->year } ],
78     previous_year       => [ $reference_date->clone->truncate(to => 'year')->subtract(years => 1),   sub { $_[0]->year } ],
79     next_year           => [ $reference_date->clone->truncate(to => 'year')->add(     years => 1),   sub { $_[0]->year } ],
80
81     reference_date      => [ $reference_date->clone,                                                 sub { $::locale->format_date(\%::myconfig, $_[0]) } ],
82   };
83
84   return $variables;
85 }
86
87 sub _text_column_names {
88   my ($self, $object) = @_;
89   return map { $_->name } grep { ref($_) =~ m{::Text} } @{ $object->meta->columns };
90 }
91
92 sub substitute_variables {
93   my ($self, $reference_date) = @_;
94
95   my $variables    = $self->_generate_variables($reference_date);
96   my @text_columns = $self->_text_column_names($self);
97
98   $self->_replace_variables(
99     object    => $self,
100     variables => $variables,
101     fields    => \@text_columns,
102   );
103
104   @text_columns = $self->_text_column_names(SL::DB::RecordTemplateItem->new);
105
106   foreach my $item (@{ $self->items }) {
107     $self->_replace_variables(
108       object    => $item,
109       variables => $variables,
110       fields    => \@text_columns,
111     );
112   }
113 }
114
115 1;
116 __END__
117
118 =pod
119
120 =encoding utf8
121
122 =head1 NAME
123
124 SL::DB::RecordTemplate — Templates for accounts receivable
125 transactions, accounts payable transactions and generic ledger
126 transactiona
127
128 =head1 FUNCTIONS
129
130 =over 4
131
132 =item C<items>
133
134 An alias for C<record_template_items>.
135
136 =item C<substitute_variables> C<[$reference_date]>
137
138 Texts in record templates can contain placeholders. This function
139 replaces those placeholders by their actual value. Placeholders use
140 the syntax C<E<lt>%variableE<gt>> or C<E<lt>%variable format=…E<gt>>
141 for a custom format (see L<DateTime::Format::Strptime> for available
142 formatting characters).
143
144 The variables are calculated based on C<$reference_date> which must be
145 an instance of L<DateTime> if given. If left out, it defaults to the
146 current day.
147
148 Supported variables are:
149
150 =over 2
151
152 =item * C<current_quarter>, C<previous_quarter>, C<next_quarter> — the
153 quarter as a number between 1 and 4 inclusively
154
155 =item * C<current_month>, C<previous_month>, C<next_month> — the
156 month as a number between 1 and 12 inclusively
157
158 =item * C<current_month_long>, C<previous_month_long>,
159 C<next_month_long> — the month's name (e.g. C<August>).
160
161 =item * C<current_year>, C<previous_year>, C<next_year> — the
162 year (e.g. C<2017>)
163
164 =item * C<reference_date> — the reference date in the user's date style
165 (e.g. C<27.11.2017>)
166
167 =back
168
169 =back
170
171 =head1 AUTHOR
172
173 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
174
175 =cut