Revision 82ff5451
Von Moritz Bunkus vor fast 10 Jahren hinzugefügt
SL/BackgroundJob/CreatePeriodicInvoices.pm | ||
---|---|---|
74 | 74 |
sub _generate_time_period_variables { |
75 | 75 |
my $config = shift; |
76 | 76 |
my $period_start_date = shift; |
77 |
my $period_end_date = $period_start_date->clone->truncate(to => 'month')->add(months => $config->get_period_length)->subtract(days => 1); |
|
77 |
my $period_end_date = $period_start_date->clone->truncate(to => 'month')->add(months => $config->get_billing_period_length)->subtract(days => 1);
|
|
78 | 78 |
|
79 | 79 |
my @month_names = ('', |
80 | 80 |
$::locale->text('January'), $::locale->text('February'), $::locale->text('March'), $::locale->text('April'), $::locale->text('May'), $::locale->text('June'), |
SL/Controller/FinancialControllingReport.pm | ||
---|---|---|
148 | 148 |
$cur_date->add(years => 1); |
149 | 149 |
} |
150 | 150 |
|
151 |
return $num_years * $order->netamount * (12 / $order->periodic_invoices_config->get_period_length); |
|
151 |
return $num_years * $order->netamount * (12 / $order->periodic_invoices_config->get_billing_period_length);
|
|
152 | 152 |
} |
153 | 153 |
|
154 | 154 |
sub sum_items { |
SL/Controller/FinancialOverview.pm | ||
---|---|---|
148 | 148 |
return if $params{config}->start_date > $params{end_date}; |
149 | 149 |
|
150 | 150 |
my $first_date = $params{config}->start_date->clone->set_year($self->year); |
151 |
my $net = $params{config}->order->netamount * (12 / $params{config}->get_period_length); |
|
151 |
my $net = $params{config}->order->netamount * (12 / $params{config}->get_billing_period_length);
|
|
152 | 152 |
my $sord = $self->data->{sales_orders}; |
153 | 153 |
|
154 | 154 |
$sord->{months }->[ $first_date->month - 1 ] += $net; |
SL/DB/MetaSetup/PeriodicInvoicesConfig.pm | ||
---|---|---|
17 | 17 |
first_billing_date => { type => 'date' }, |
18 | 18 |
id => { type => 'integer', not_null => 1, sequence => 'id' }, |
19 | 19 |
oe_id => { type => 'integer', not_null => 1 }, |
20 |
periodicity => { type => 'varchar', length => 10, not_null => 1 }, |
|
20 |
order_value_periodicity => { type => 'varchar', length => 1, not_null => 1 }, |
|
21 |
periodicity => { type => 'varchar', length => 1, not_null => 1 }, |
|
21 | 22 |
print => { type => 'boolean', default => 'false' }, |
22 | 23 |
printer_id => { type => 'integer' }, |
23 | 24 |
start_date => { type => 'date' }, |
SL/DB/PeriodicInvoicesConfig.pm | ||
---|---|---|
11 | 11 |
# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all. |
12 | 12 |
__PACKAGE__->meta->make_manager_class; |
13 | 13 |
|
14 |
our @PERIODICITIES = qw(m q f b y); |
|
15 |
our %PERIOD_LENGTHS = ( m => 1, q => 3, f => 4, b => 6, y => 12 ); |
|
14 |
our %PERIOD_LENGTHS = ( m => 1, q => 3, b => 6, y => 12 ); |
|
15 |
our %ORDER_VALUE_PERIOD_LENGTHS = ( %PERIOD_LENGTHS, 2 => 24, 3 => 36, 4 => 48, 5 => 60 ); |
|
16 |
our @PERIODICITIES = keys %PERIOD_LENGTHS; |
|
17 |
our @ORDER_VALUE_PERIODICITIES = keys %ORDER_VALUE_PERIOD_LENGTHS; |
|
16 | 18 |
|
17 |
sub get_period_length { |
|
19 |
sub get_billing_period_length {
|
|
18 | 20 |
my $self = shift; |
19 | 21 |
return $PERIOD_LENGTHS{ $self->periodicity } || 1; |
20 | 22 |
} |
21 | 23 |
|
24 |
sub get_order_value_period_length { |
|
25 |
my $self = shift; |
|
26 |
return $self->get_billing_period_length if $self->order_value_periodicity eq 'p'; |
|
27 |
return $ORDER_VALUE_PERIOD_LENGTHS{ $self->order_value_periodicity } || 1; |
|
28 |
} |
|
29 |
|
|
22 | 30 |
sub _log_msg { |
23 | 31 |
$::lxdebug->message(LXDebug->DEBUG1(), join('', @_)); |
24 | 32 |
} |
... | ... | |
78 | 86 |
sub calculate_invoice_dates { |
79 | 87 |
my ($self, %params) = @_; |
80 | 88 |
|
81 |
my $period_len = $self->get_period_length; |
|
89 |
my $period_len = $self->get_billing_period_length;
|
|
82 | 90 |
my $cur_date = $self->first_billing_date || $self->start_date; |
83 | 91 |
my $end_date = $self->terminated ? $self->end_date : undef; |
84 | 92 |
$end_date //= DateTime->today_local->add(years => 100); |
... | ... | |
100 | 108 |
} |
101 | 109 |
|
102 | 110 |
1; |
111 |
__END__ |
|
112 |
|
|
113 |
=pod |
|
114 |
|
|
115 |
=encoding utf8 |
|
116 |
|
|
117 |
=head1 NAME |
|
118 |
|
|
119 |
SL::DB::PeriodicInvoicesConfig - DB model for the configuration for periodic invoices |
|
120 |
|
|
121 |
=head1 FUNCTIONS |
|
122 |
|
|
123 |
=over 4 |
|
124 |
|
|
125 |
=item C<calculate_invoice_dates %params> |
|
126 |
|
|
127 |
Calculates dates for which invoices will have to be created. Returns a |
|
128 |
list of L<DateTime> objects. |
|
129 |
|
|
130 |
This function looks at the configuration settings and at the list of |
|
131 |
invoices that have already been created for this configuration. The |
|
132 |
date range for which dates are created are controlled by several |
|
133 |
values: |
|
134 |
|
|
135 |
=over 2 |
|
136 |
|
|
137 |
=item * The properties C<first_billing_date> and C<start_date> |
|
138 |
determine the start date. |
|
139 |
|
|
140 |
=item * The properties C<end_date> and C<terminated> determine the end |
|
141 |
date. |
|
142 |
|
|
143 |
=item * The optional parameter C<past_dates> determines whether or not |
|
144 |
dates for which invoices have already been created will be included in |
|
145 |
the list. The default is not to include them. |
|
146 |
|
|
147 |
=item * The optional parameters C<start_date> and C<end_date> override |
|
148 |
the start and end dates from the configuration. |
|
149 |
|
|
150 |
=item * If no end date is set or implied via the configuration and no |
|
151 |
C<end_date> parameter is given then the function will use 100 years |
|
152 |
in the future as the end date. |
|
153 |
|
|
154 |
=back |
|
155 |
|
|
156 |
=item C<get_billing_period_length> |
|
157 |
|
|
158 |
Returns the number of months corresponding to the billing |
|
159 |
periodicity. This means that a new invoice has to be created every x |
|
160 |
months starting with the value in C<first_billing_date> (or |
|
161 |
C<start_date> if C<first_billing_date> is unset). |
|
162 |
|
|
163 |
=item C<get_order_value_period_length> |
|
164 |
|
|
165 |
Returns the number of months the order's value refers to. This looks |
|
166 |
at the C<order_value_periodicity>. |
|
167 |
|
|
168 |
Each invoice's value is calculated as C<order value * |
|
169 |
billing_period_length / order_value_period_length>. |
|
170 |
|
|
171 |
=item C<get_previous_billed_period_start_date> |
|
172 |
|
|
173 |
Returns the highest date (as an instance of L<DateTime>) for which an |
|
174 |
invoice has been created from this configuration. |
|
175 |
|
|
176 |
=item C<handle_automatic_extension> |
|
177 |
|
|
178 |
Configurations which haven't been terminated and which have an end |
|
179 |
date set may be eligible for automatic extension by a certain number |
|
180 |
of months. This what the function implements. |
|
181 |
|
|
182 |
If the configuration is not eligible or if the C<end_date> hasn't been |
|
183 |
reached yet then nothing is done and C<undef> is returned. Otherwise |
|
184 |
its behavior is determined by the C<extend_automatically_by> property. |
|
185 |
|
|
186 |
If the property C<extend_automatically_by> is not 0 then the |
|
187 |
C<end_date> will be extended by C<extend_automatically_by> months, and |
|
188 |
the configuration will be saved. In this case the new end date will be |
|
189 |
returned. |
|
190 |
|
|
191 |
Otherwise (if C<extend_automatically_by> is 0) the property C<active> |
|
192 |
will be set to 1, and the configuration will be saved. In this case |
|
193 |
C<undef> will be returned. |
|
194 |
|
|
195 |
=back |
|
196 |
|
|
197 |
=head1 BUGS |
|
198 |
|
|
199 |
Nothing here yet. |
|
200 |
|
|
201 |
=head1 AUTHOR |
|
202 |
|
|
203 |
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt> |
|
204 |
|
|
205 |
=cut |
SL/OE.pm | ||
---|---|---|
738 | 738 |
my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id}); |
739 | 739 |
|
740 | 740 |
if ($config_obj) { |
741 |
my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity start_date_as_date end_date_as_date first_billing_date_as_date extend_automatically_by ar_chart_id |
|
741 |
my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity order_value_periodicity start_date_as_date end_date_as_date first_billing_date_as_date extend_automatically_by ar_chart_id
|
|
742 | 742 |
print printer_id copies) }; |
743 | 743 |
$form->{periodic_invoices_config} = YAML::Dump($config); |
744 | 744 |
} |
bin/mozilla/oe.pl | ||
---|---|---|
2000 | 2000 |
$config = YAML::Load($::form->{periodic_invoices_config}) if $::form->{periodic_invoices_config}; |
2001 | 2001 |
|
2002 | 2002 |
if ('HASH' ne ref $config) { |
2003 |
$config = { periodicity => 'y', |
|
2003 |
$config = { periodicity => 'm', |
|
2004 |
order_value_periodicity => 'p', # = same as periodicity |
|
2004 | 2005 |
start_date_as_date => $::form->{transdate} || $::form->current_date, |
2005 | 2006 |
extend_automatically_by => 12, |
2006 | 2007 |
active => 1, |
2007 | 2008 |
}; |
2008 | 2009 |
} |
2009 | 2010 |
|
2010 |
$config->{periodicity} = 'm' if none { $_ eq $config->{periodicity} } qw(m q b y); |
|
2011 |
$config->{periodicity} = 'm' if none { $_ eq $config->{periodicity} } @SL::DB::PeriodicInvoicesConfig::PERIODICITIES; |
|
2012 |
$config->{order_value_periodicity} = 'p' if none { $_ eq $config->{order_value_periodicity} } ('p', @SL::DB::PeriodicInvoicesConfig::ORDER_VALUE_PERIODICITIES); |
|
2011 | 2013 |
|
2012 | 2014 |
$::form->get_lists(printers => "ALL_PRINTERS", |
2013 | 2015 |
charts => { key => 'ALL_CHARTS', |
... | ... | |
2033 | 2035 |
|
2034 | 2036 |
my $config = { active => $::form->{active} ? 1 : 0, |
2035 | 2037 |
terminated => $::form->{terminated} ? 1 : 0, |
2036 |
periodicity => (any { $_ eq $::form->{periodicity} } qw(m q b y)) ? $::form->{periodicity} : 'm', |
|
2038 |
periodicity => (any { $_ eq $::form->{periodicity} } @SL::DB::PeriodicInvoicesConfig::PERIODICITIES) ? $::form->{periodicity} : 'm', |
|
2039 |
order_value_periodicity => (any { $_ eq $::form->{order_value_periodicity} } ('p', @SL::DB::PeriodicInvoicesConfig::ORDER_VALUE_PERIODICITIES)) ? $::form->{order_value_periodicity} : 'p', |
|
2037 | 2040 |
start_date_as_date => $::form->{start_date_as_date}, |
2038 | 2041 |
end_date_as_date => $::form->{end_date_as_date}, |
2039 | 2042 |
first_billing_date_as_date => $::form->{first_billing_date_as_date}, |
locale/de/all | ||
---|---|---|
35 | 35 |
'...on the TODO list' => '...auf der Aufgabenliste', |
36 | 36 |
'0% tax with taxkey' => '0% Steuer mit Steuerschlüssel ', |
37 | 37 |
'1. Quarter' => '1. Quartal', |
38 |
'2 years' => '2 Jahre', |
|
38 | 39 |
'2. Quarter' => '2. Quartal', |
40 |
'3 years' => '3 Jahre', |
|
39 | 41 |
'3. Quarter' => '3. Quartal', |
42 |
'4 years' => '4 Jahre', |
|
40 | 43 |
'4. Quarter' => '4. Quartal', |
44 |
'5 years' => '5 Jahre', |
|
41 | 45 |
'<b> I DO CARE!</b> Please check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '<b>ICH KÜMMER MICH</b> Bitte haken Sie Lager und Lagerplätze erzeugen an (Automatisches Zuweisen der Lagerplätze) und vergeben einen Namen für dieses Lager (Lagerplätze werden automatisch übernommen). Danach auf weiter.', |
42 | 46 |
'<b> I DO CARE!</b> Please click back and cancel the update and come back after there has been at least one warehouse defined with bin(s).:' => '<b>ICH KÜMMER MICH</b> Brechen Sie das Update ab und legen selber mindestens ein Lager mit Lagerplätzen unter dem Menü System / Lager an.', |
43 | 47 |
'<b> I DO NOT CARE</b> Please click continue and the following data (see list) will be deleted:' => '<b>IST MIR EGAL</b> Mit einem Klick auf Weiter (rot) werden keine Daten übernommen, bzw. migriert und die folgende Information in der untenstehenden Liste wird gelöscht.', |
... | ... | |
354 | 358 |
'Billed amount' => 'Abgerechneter Betrag', |
355 | 359 |
'Billed extra expenses' => 'Abgerechnete Nebenkosten', |
356 | 360 |
'Billing Address' => 'Rechnungsadresse', |
361 |
'Billing Periodicity' => 'Abrechnungsperiodizität', |
|
357 | 362 |
'Billing/shipping address (city)' => 'Rechnungsadresse (Stadt)', |
358 | 363 |
'Billing/shipping address (country)' => 'Rechnungsadresse (Land)', |
359 | 364 |
'Billing/shipping address (street)' => 'Rechnungsadresse (Straße)', |
... | ... | |
1731 | 1736 |
'Order deleted!' => 'Auftrag gelöscht!', |
1732 | 1737 |
'Order probability' => 'Auftragswahrscheinlichkeit', |
1733 | 1738 |
'Order probability & expected billing date' => 'Auftragswahrscheinlichkeit & vorrauss. Abrechnungsdatum', |
1739 |
'Order value periodicity' => 'Auftragswert basiert auf Periodizität', |
|
1734 | 1740 |
'Order/Item row name' => 'Name der Auftrag-/Positions-Zeilen', |
1735 | 1741 |
'OrderItem' => 'Position', |
1736 | 1742 |
'Ordered' => 'Von Kunden bestellt', |
... | ... | |
1821 | 1827 |
'Periodic inventory' => 'Aufwandsmethode', |
1822 | 1828 |
'Periodic invoices active' => 'Wiederkehrende Rechnungen aktiv', |
1823 | 1829 |
'Periodic invoices inactive' => 'Wiederkehrende Rechnungen inaktiv', |
1824 |
'Periodicity' => 'Periodizität', |
|
1825 | 1830 |
'Perpetual inventory' => 'Bestandsmethode', |
1826 | 1831 |
'Person' => 'Person', |
1827 | 1832 |
'Personal settings' => 'Persönliche Einstellungen', |
... | ... | |
3240 | 3245 |
'sales_order' => 'Kundenauftrag', |
3241 | 3246 |
'sales_order_list' => 'auftragsliste', |
3242 | 3247 |
'sales_quotation' => 'Verkaufsangebot', |
3248 |
'same as periodicity' => 'stimmt mit Abrechnungsperiodizität überein', |
|
3243 | 3249 |
'saved' => 'gespeichert', |
3244 | 3250 |
'saved!' => 'gespeichert', |
3245 | 3251 |
'saving data' => 'Speichere Daten', |
sql/Pg-upgrade2/periodic_invoices_order_value_periodicity.sql | ||
---|---|---|
1 |
-- @tag: periodic_invoices_order_value_periodicity |
|
2 |
-- @description: Wiederkehrende Rechnungen: Einstellung für Periode, auf die sich der Auftragswert bezieht |
|
3 |
-- @depends: release_3_1_0 |
|
4 |
|
|
5 |
-- Spalte »periodicity«: nur ein Zeichen, und Check auf gültige Werte |
|
6 |
ALTER TABLE periodic_invoices_configs |
|
7 |
ADD CONSTRAINT periodic_invoices_configs_valid_periodicity |
|
8 |
CHECK (periodicity IN ('m', 'q', 'b', 'y')); |
|
9 |
|
|
10 |
ALTER TABLE periodic_invoices_configs |
|
11 |
ALTER COLUMN periodicity TYPE varchar(1); |
|
12 |
|
|
13 |
-- Neue Spalte »order_value_periodicity« |
|
14 |
ALTER TABLE periodic_invoices_configs |
|
15 |
ADD COLUMN order_value_periodicity varchar(1); |
|
16 |
|
|
17 |
UPDATE periodic_invoices_configs |
|
18 |
SET order_value_periodicity = 'p'; |
|
19 |
|
|
20 |
ALTER TABLE periodic_invoices_configs |
|
21 |
ALTER COLUMN order_value_periodicity |
|
22 |
SET NOT NULL; |
|
23 |
|
|
24 |
ALTER TABLE periodic_invoices_configs |
|
25 |
ADD CONSTRAINT periodic_invoices_configs_valid_order_value_periodicity |
|
26 |
CHECK (order_value_periodicity IN ('p', 'm', 'q', 'b', 'y', '2', '3', '4', '5')); |
templates/webpages/oe/edit_periodic_invoices_config.html | ||
---|---|---|
1 | 1 |
[% USE HTML %] |
2 | 2 |
[% USE LxERP %] |
3 | 3 |
[% USE L %] |
4 |
[% SET style="width: 400px" %] |
|
4 | 5 |
<h1>[% title %]</h1> |
5 | 6 |
|
6 | 7 |
<form name="Form" action="oe.pl" method="post"> |
... | ... | |
20 | 21 |
</tr> |
21 | 22 |
|
22 | 23 |
<tr> |
23 |
<th align="right" valign="top">[%- LxERP.t8('Periodicity') %]</th> |
|
24 |
<th align="right" valign="top">[%- LxERP.t8('Billing Periodicity') %]</th>
|
|
24 | 25 |
<td valign="top"> |
25 |
[% L.radio_button_tag("periodicity", value => "m", label => LxERP.t8("monthly"), checked => periodicity == 'm') %] |
|
26 |
<br> |
|
27 |
[% L.radio_button_tag("periodicity", value => "q", label => LxERP.t8("every third month"), checked => periodicity == 'q') %] |
|
28 |
<br> |
|
29 |
[% L.radio_button_tag("periodicity", value => "b", label => LxERP.t8("semiannually"), checked => periodicity == 'b') %] |
|
30 |
<br> |
|
31 |
[% L.radio_button_tag("periodicity", value => "y", label => LxERP.t8("yearly"), checked => periodicity == 'y') %] |
|
26 |
[% L.select_tag("periodicity", [ [ "m", LxERP.t8("monthly") ], [ "q", LxERP.t8("every third month") ], [ "b", LxERP.t8("semiannually") ], [ "y", LxERP.t8("yearly") ] ], default=periodicity, style=style) %] |
|
27 |
</td> |
|
28 |
</tr> |
|
29 |
|
|
30 |
<tr> |
|
31 |
<th align="right" valign="top">[%- LxERP.t8('Order value periodicity') %]</th> |
|
32 |
<td valign="top"> |
|
33 |
[% L.select_tag("order_value_periodicity", |
|
34 |
[ [ "p", LxERP.t8("same as periodicity") ], [ "m", LxERP.t8("monthly") ], [ "q", LxERP.t8("every third month") ], [ "b", LxERP.t8("semiannually") ], [ "y", LxERP.t8("yearly") ], |
|
35 |
[ "2", LxERP.t8("2 years") ], [ "3", LxERP.t8("3 years") ], [ "4", LxERP.t8("4 years") ], [ "5", LxERP.t8("5 years") ], ], |
|
36 |
default=order_value_periodicity, style=style) %] |
|
32 | 37 |
</td> |
33 | 38 |
</tr> |
34 | 39 |
|
... | ... | |
63 | 68 |
<tr> |
64 | 69 |
<th align="right">[%- LxERP.t8('Record in') %]</th> |
65 | 70 |
<td valign="top"> |
66 |
[% L.select_tag("ar_chart_id", AR, title_key => 'description', default => ar_chart_id) %] |
|
71 |
[% L.select_tag("ar_chart_id", AR, title_key => 'description', default => ar_chart_id, style=style) %]
|
|
67 | 72 |
</td> |
68 | 73 |
</tr> |
69 | 74 |
|
... | ... | |
77 | 82 |
<tr> |
78 | 83 |
<th align="right">[%- LxERP.t8('Printer') %]</th> |
79 | 84 |
<td valign="top"> |
80 |
[% L.select_tag("printer_id", ALL_PRINTERS, title_key = 'printer_description', default = printer_id, disabled = !print) %] |
|
85 |
[% L.select_tag("printer_id", ALL_PRINTERS, title_key = 'printer_description', default = printer_id, disabled = !print, style=style) %]
|
|
81 | 86 |
</td> |
82 | 87 |
</tr> |
83 | 88 |
|
Auch abrufbar als: Unified diff
Wiederkehrende Rechnungen: Auftragswerts-Periodizität setzen können
Noch keine Anpassung der eigentlichen Berechnung. Wohl aber
Dokumentation von SL::DB::PeriodicInvoicesConfig.