__PACKAGE__->run_before('check_auth');
__PACKAGE__->run_before('load_payment_term', only => [ qw( edit update destroy) ]);
__PACKAGE__->run_before('load_languages', only => [ qw(new list edit create update) ]);
+__PACKAGE__->run_before('setup', only => [ qw(new edit) ]);
#
# actions
sub action_new {
my ($self) = @_;
- $self->{payment_term} = SL::DB::PaymentTerm->new;
+ $self->{payment_term} = SL::DB::PaymentTerm->new(auto_calculation => 1);
$self->render('payment_term/form', title => $::locale->text('Create a new payment term'));
}
sub action_edit {
my ($self) = @_;
+
$self->render('payment_term/form', title => $::locale->text('Edit payment term'));
}
$::auth->assert('config');
}
+sub setup {
+ $::request->layout->use_javascript("kivi.PaymentTerm.js");
+}
+
#
# helpers
#
my $params = delete($::form->{payment_term}) || { };
$self->{payment_term}->assign_attributes(%{ $params });
+ $self->{payment_term}->terms_netto(0) if !$self->{payment_term}->auto_calculation;
my @errors = $self->{payment_term}->validate;
__PACKAGE__->meta->table('payment_terms');
__PACKAGE__->meta->columns(
+ auto_calculation => { type => 'boolean', not_null => 1 },
description => { type => 'text' },
description_long => { type => 'text' },
id => { type => 'integer', not_null => 1, sequence => 'id' },
use strict;
+use List::Util qw(max);
+
use SL::DB::MetaSetup::PaymentTerm;
use SL::DB::Manager::PaymentTerm;
use SL::DB::Helper::ActsAsList;
my $reference_date = $params{reference_date} || DateTime->today_local;
$reference_date = DateTime->from_kivitendo($reference_date) unless ref($reference_date) eq 'DateTime';
+ if (!$self->auto_calculation) {
+ my $due_date = $params{due_date} || $reference_date;
+ $due_date = DateTime->from_kivitendo($due_date) unless ref($due_date) eq 'DateTime';
+
+ return max $due_date, $reference_date;
+ }
+
my $terms = ($params{terms} // 'net') eq 'discount' ? 'terms_skonto' : 'terms_netto';
my $date = $reference_date->add(days => $self->$terms);
=item C<calc_date [%params]>
Calculates and returns a due date as an instance of L<DateTime> by
-adding one of C<$self>'s terms fields. Note that the resulting date
-will be the following Monday if the result falls on a weekend.
+adding one of C<$self>'s terms fields if automatic calculation is on;
+otherwise returns the currently-set due date (which must be provided)
+or the reference date, whichever is later.
+
+Note that for automatich calculation the resulting date will be the
+following Monday if the result falls on a weekend.
C<%params> can contain the following parameters:
Defaults to the current date if unset.
+=item C<due_date>
+
+A currently set due date. If automatic calculation is off then this
+date will be returned if it is provided and greater than or equal to
+the C<reference_date>. Otherwise the reference date will be returned.
+
=item C<terms>
Can be either C<net> or C<discount>. For C<net> the number of days to
}
sub set_payment_options {
- $main::lxdebug->enter_sub();
-
my ($self, $myconfig, $transdate) = @_;
- return $main::lxdebug->leave_sub() unless ($self->{payment_id});
-
- my $dbh = $self->get_standard_dbh($myconfig);
+ my $terms = $self->{payment_id} ? SL::DB::PaymentTerm->new(id => $self->{payment_id})->load : undef;
+ return if !$terms;
- my $query =
- qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long , p.description | .
- qq|FROM payment_terms p | .
- qq|WHERE p.id = ?|;
+ $transdate ||= $self->{invdate} || $self->{transdate};
+ my $due_date = $self->{duedate} || $self->{reqdate};
- ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
- $self->{payment_terms}, $self->{payment_description}) =
- selectrow_query($self, $dbh, $query, $self->{payment_id});
-
- if ($transdate eq "") {
- if ($self->{invdate}) {
- $transdate = $self->{invdate};
- } else {
- $transdate = $self->{transdate};
- }
- }
-
- $query =
- qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
- qq|FROM payment_terms|;
- ($self->{netto_date}, $self->{skonto_date}) =
- selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
+ $self->{$_} = $terms->$_ for qw(terms_netto terms_skonto percent_skonto);
+ $self->{payment_terms} = $terms->description_long;
+ $self->{payment_description} = $terms->description;
+ $self->{netto_date} = $terms->calc_date(reference_date => $transdate, due_date => $due_date, terms => 'net')->to_kivitendo;
+ $self->{skonto_date} = $terms->calc_date(reference_date => $transdate, due_date => $due_date, terms => 'discount')->to_kivitendo;
my ($invtotal, $total);
my (%amounts, %formatted_amounts);
}
if ($self->{"language_id"}) {
- $query =
+ my $dbh = $self->get_standard_dbh($myconfig);
+ my $query =
qq|SELECT t.translation, l.output_numberformat, l.output_dateformat, l.output_longdates | .
qq|FROM generic_translations t | .
qq|LEFT JOIN language l ON t.language_id = l.id | .
$self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
- $main::lxdebug->leave_sub();
-
}
sub get_template_language {
$main::lxdebug->leave_sub();
}
-sub get_duedate {
- $main::lxdebug->enter_sub();
-
- my ($self, $myconfig, $reference_date) = @_;
-
- my $terms = $self->{payment_id} ? SL::DB::PaymentTerm->new(id => $self->{payment_id}) ->load
- : $self->{customer_id} ? SL::DB::Customer ->new(id => $self->{customer_id})->load->payment
- : $self->{vendor_id} ? SL::DB::Vendor ->new(id => $self->{vendor_id}) ->load->payment
- : $self->{invdate} ? undef # no payment terms, therefore invdate == duedate
- : croak("Missing field in \$::form: payment_id, customer_id, vendor_id or invdate");
- my $duedate = $terms ? $terms->calc_date(reference_date => $reference_date)->to_kivitendo : undef;
-
- $main::lxdebug->leave_sub();
-
- return $duedate;
-}
-
sub _get_contacts {
$main::lxdebug->enter_sub();
my $dateformat = $myconfig->{dateformat};
$dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
- my (@values, $duedate, $ref, $query);
-
- if ($form->{invdate}) {
- $duedate = "to_date(?, '$dateformat')";
- push @values, $form->{invdate};
- } else {
- $duedate = "current_date";
- }
+ my (@values, $ref, $query);
my $cid = conv_i($form->{customer_id});
my $payment_id;
- if ($form->{payment_id}) {
- $payment_id = "(pt.id = ?) OR";
- push @values, conv_i($form->{payment_id});
- }
-
# get customer
$query =
qq|SELECT
c.street, c.zipcode, c.city, c.country,
c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr,
c.taxincluded_checked, c.direct_debit,
- $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
b.discount AS tradediscount, b.description AS business
FROM customer c
LEFT JOIN business b ON (b.id = c.business_id)
- LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
LEFT JOIN currencies cu ON (c.currency_id=cu.id)
WHERE c.id = ?|;
push @values, $cid;
$ref = selectfirst_hashref_query($form, $dbh, $query, @values);
delete $ref->{salesman_id} if !$ref->{salesman_id};
+ delete $ref->{payment_id} if $form->{payment_id};
map { $form->{$_} = $ref->{$_} } keys %$ref;
+ if ($form->{payment_id}) {
+ my $reference_date = $form->{invdate} ? DateTime->from_kivitendo($form->{invdate}) : undef;
+ $form->{duedate} = SL::DB::PaymentTerm->new(id => $form->{payment_id})->load->calc_date(reference_date => $reference_date)->to_kivitendo;
+ } else {
+ $form->{duedate} = DateTime->today_local->to_kivitendo;
+ }
+
# use customer currency
$form->{currency} = $form->{curr};
use List::MoreUtils qw(any uniq apply);
use List::Util qw(min max first);
+use SL::ClientJS;
use SL::CVar;
use SL::Common;
+use SL::Controller::Base;
use SL::CT;
use SL::Locale::String qw(t8);
use SL::IC;
$main::lxdebug->leave_sub();
}
-sub set_duedate {
- $main::lxdebug->enter_sub();
+sub get_payment_terms_for_invoice {
+ my $terms = $::form->{payment_id} ? SL::DB::PaymentTerm->new(id => $::form->{payment_id}) ->load
+ : $::form->{customer_id} ? SL::DB::Customer ->new(id => $::form->{customer_id})->load->payment
+ : $::form->{vendor_id} ? SL::DB::Vendor ->new(id => $::form->{vendor_id}) ->load->payment
+ : undef;
- my $form = $main::form;
- my %myconfig = %main::myconfig;
+ return $terms;
+}
+sub set_duedate {
_check_io_auth();
- my $invdate = $form->{invdate} eq 'undefined' ? undef : $form->{invdate};
- my $duedate = $form->get_duedate(\%myconfig, $invdate);
+ my $js = SL::ClientJS->new(controller => SL::Controller::Base->new);
+ my $terms = get_payment_terms_for_invoice();
+ my $invdate = $::form->{invdate} eq 'undefined' ? DateTime->today_local : DateTime->from_kivitendo($::form->{invdate});
+ my $duedate = $terms ? $terms->calc_date(reference_date => $invdate, due_date => $::form->{duedate})->to_kivitendo : ($::form->{duedate} || $invdate->to_kivitendo);
- print $form->ajax_response_header() . ($duedate || $invdate);
+ if ($terms && $terms->auto_calculation) {
+ $js->hide('#duedate_container')
+ ->show('#duedate_fixed')
+ ->html('#duedate_fixed', $duedate);
- $main::lxdebug->leave_sub();
+ } else {
+ $js->show('#duedate_container')
+ ->hide('#duedate_fixed');
+ }
+
+ $js->val('#duedate', $duedate)
+ ->render;
}
sub _update_part_information {
), @custom_hiddens,
map { $_.'_rate', $_.'_description', $_.'_taxnumber' } split / /, $form->{taxaccounts}];
- $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part));
+ $TMPL_VAR{payment_terms_obj} = get_payment_terms_for_invoice();
+ $form->{duedate} = $TMPL_VAR{payment_terms_obj}->calc_date(reference_date => $form->{invdate}, due_date => $form->{due_due})->to_kivitendo if $TMPL_VAR{payment_terms_obj};
+
+ $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part client_js));
$form->header();
use SL::DB::Default;
use SL::DB::Customer;
+use SL::DB::PaymentTerm;
require "bin/mozilla/io.pl";
require "bin/mozilla/invoice_io.pl";
), @custom_hiddens,
map { $_.'_rate', $_.'_description', $_.'_taxnumber' } split / /, $form->{taxaccounts}];
- $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part));
+ $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part client_js));
+
+ $TMPL_VAR{payment_terms_obj} = get_payment_terms_for_invoice();
+ $form->{duedate} = $TMPL_VAR{payment_terms_obj}->calc_date(reference_date => $form->{invdate}, due_date => $form->{due_due})->to_kivitendo if $TMPL_VAR{payment_terms_obj};
$form->header();
relink_accounts();
+ my $terms = get_payment_terms_for_invoice();
+ $form->{duedate} = $terms->calc_date(reference_date => $form->{invdate}, due_date => $form->{due_due})->to_kivitendo if $terms;
+
# If transfer_out is requested, get rose db handle and do post and
# transfer out in one transaction. Otherwise just post the invoice.
if ($::instance_conf->get_is_transfer_out && $form->{type} ne 'credit_note' && !$form->{storno}) {
$form->{rowcount}--;
$form->{paidaccounts} = 1;
$form->{invdate} = $form->current_date(\%myconfig);
- $form->{duedate} = $form->get_duedate(\%myconfig, $form->{invdate}) || $form->{invdate};
+ my $terms = get_payment_terms_for_invoice();
+ $form->{duedate} = $terms ? $terms->calc_date(reference_date => $form->{invdate})->to_kivitendo : $form->{invdate};
$form->{employee_id} = SL::DB::Manager::Employee->current->id;
$form->{forex} = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
$form->{exchangerate} = $form->{forex} if $form->{forex};
--- /dev/null
+namespace('kivi.PaymentTerm', function(ns) {
+ ns.auto_calculation_changed = function() {
+ var $ctrl = $('#payment_term_terms_netto_as_number');
+
+ if ($('#payment_term_auto_calculation').val() == 0)
+ $ctrl.prop('disabled', true);
+ else
+ $ctrl.prop('disabled', false).focus();
+ };
+});
+
+$(function() {
+ $('#payment_term_auto_calculation').change(kivi.PaymentTerm.auto_calculation_changed);
+});
this.init_on_submit_checks = function() {
$('input[type=submit]').click(kivi.SalesPurchase.on_submit_checks);
};
+
+ this.set_duedate_on_reference_date_change = function(reference_field_id) {
+ setTimeout(function() {
+ var data = {
+ action: 'set_duedate',
+ invdate: $('#' + reference_field_id).val(),
+ duedate: $('#duedate').val(),
+ payment_id: $('#payment_id').val(),
+ };
+ $.post('is.pl', data, kivi.eval_json_result);
+ });
+ };
});
'Authentification database creation' => 'Anlegen der Datenbank zur Benutzerauthentifizierung',
'Authentification tables creation' => 'Anlegen der Tabellen zur Benutzerauthentifizierung',
'Auto Send?' => 'Auto. Versand?',
+ 'Automatic date calculation' => 'Automatische Datumsberechnung',
'Automatic deletion of leading, trailing and excessive (repetitive) spaces in customer or vendor names' => 'Automatisches Löschen von voran-/nachgestellten und aufeinanderfolgenden Leerzeichen im Kunden- oder Lieferantennamen',
'Automatic deletion of leading, trailing and excessive (repetitive) spaces in part description and part notes. Affects the CSV import as well.' => 'Automatisches Löschen von voran-/nachgestellten und aufeinanderfolgenden Leerzeichen in Artikelbeschreibungen und -bemerkungen. Betrifft auch den CSV-Import.',
'Automatic skonto chart purchase' => 'Skontoautomatik Einkauf',
'CSV import: shipping addresses' => 'CSV-Import: Lieferadressen',
'CTI settings' => 'CTI-Einstellungen',
'Calculate' => 'Berechnen',
+ 'Calculate due date automatically' => 'Fälligkeitsdatum automatisch berechnen',
'Calculate the value of goods for delivery plan (WARNING: Experimental (not taxincluded safe!)' => 'Warenverkaufswert anzeigen (Brutto / Netto), so wie im Auftrags-Beleg gespeichert. Benötigt das Recht: Verkaufs-Aufträge bearbeiten.',
'Calling #1 now' => 'Wähle jetzt #1',
'Can not create that quantity with current stock' => 'Diese Anzahl kann mit dem gegenwärtigen Lagerbestand nicht hergestellt werden.',
--- /dev/null
+-- @tag: payment_terms_automatic_calculation
+-- @description: Zahlungsbedingungen: Einstellmöglichkeit zur automatischen/manuellen Datumsberechnung
+-- @depends: release_3_2_0
+
+ALTER TABLE payment_terms ADD COLUMN auto_calculation BOOLEAN;
+UPDATE payment_terms SET auto_calculation = TRUE;
+ALTER TABLE payment_terms ALTER COLUMN auto_calculation SET NOT NULL;
description_long => 'payment',
terms_netto => '30',
terms_skonto => '5',
- percent_skonto => '0.05'
+ percent_skonto => '0.05',
+ auto_calculation => 1,
)->save;
$vendor = SL::DB::Vendor->new(
[%- END %]
</form>
-
-<script type="text/javascript">
-<!--
-function set_duedate() {
- $.ajax({
- url: 'is.pl?action=set_duedate',
- data: {
- invdate: $('#transdate').val(),
- vendor_id: $('[name=vendor_id]').val(),
- },
- dataType: 'text',
- success: function(data) {
- $('#duedate').val(data);
- }
- });
- }
-//-->
-</script>
</tr>
<tr>
<th align="right">[% 'Credit Note Date' | $T8 %]</th>
- <td>[% L.date_tag('invdate', invdate, onChange='set_duedate(this)') %]</td>
+ <td>[% L.date_tag('invdate', invdate) %]</td>
</tr>
[%- ELSE %]
<tr>
</tr>
<tr>
<th align="right">[% 'Invoice Date' | $T8 %]</th>
- <td>[% L.date_tag('invdate', invdate, onChange='set_duedate(this)') %]</td>
+ <td>[% L.date_tag('invdate', invdate, onChange='kivi.SalesPurchase.set_duedate_on_reference_date_change("invdate")') %]</td>
</tr>
<tr>
<th align="right">[% 'Due Date' | $T8 %]</th>
- <td>[% L.date_tag('duedate', duedate) %]</td>
+ <td>
+ <span id="duedate_container"[% IF payment_terms_obj.auto_calculation %] style="display:none"[% END %]>[% L.date_tag('duedate', duedate) %]</span>
+ <span id="duedate_fixed"[% IF !payment_terms_obj.auto_calculation %] style="display:none"[% END %]>[% HTML.escape(duedate) %]</span>
+ </td>
</tr>
[%- END %]
[% ELSE %]
[% END %]
});
- function set_duedate() {
- setTimeout(function() {
- $.ajax({
- url: 'ir.pl?action=get_duedate_vendor',
- data: {
- invdate: $('#invdate').val(),
- vendor_id: $('input[name="vendor_id"]').val(),
- old_duedate: $('#duedate').val(),
- },
- dataType: 'text',
- success: function (data) { $('#duedate').val(data); }
- })
- }, 0);
- }
//-->
</script>
<input type="hidden" name="webdav" value="[% webdav | html %]">
label_key = 'description',
show_empty = 1
allow_textbox = 0 -%]
- <script type='text/javascript'>$('#payment_id').change(function(){ if (this.value) set_duedate()})</script>
+ <script type='text/javascript'>$('#payment_id').change(function(){ kivi.SalesPurchase.set_duedate_on_reference_date_change("invdate"); })</script>
</td>
</tr>
<tr>
</tr>
<tr>
<th align="right">[% 'Credit Note Date' | $T8 %]</th>
- <td>[% L.date_tag('invdate', invdate, onchange='set_duedate()') %]</td>
+ <td>[% L.date_tag('invdate', invdate, onchange='kivi.SalesPurchase.set_duedate_on_reference_date_change("invdate")') %]</td>
</tr>
[%- ELSE %]
<tr>
</tr>
<tr>
<th align="right">[% 'Invoice Date' | $T8 %]</th>
- <td>[% L.date_tag('invdate', invdate, onchange='set_duedate()') %]</td>
+ <td>[% L.date_tag('invdate', invdate, onchange='kivi.SalesPurchase.set_duedate_on_reference_date_change("invdate")') %]</td>
</tr>
<tr>
<th align="right">[% 'Due Date' | $T8 %]</th>
- <td>[% L.date_tag('duedate', duedate) %]</td>
+ <td>
+ <span id="duedate_container"[% IF payment_terms_obj.auto_calculation %] style="display:none"[% END %]>[% L.date_tag('duedate', duedate) %]</span>
+ <span id="duedate_fixed"[% IF !payment_terms_obj.auto_calculation %] style="display:none"[% END %]>[% HTML.escape(duedate) %]</span>
+ </td>
</tr>
<tr>
<th align="right" nowrap>[% 'Delivery Order Number' | $T8 %]</th>
[% ELSE %]
[% END %]
});
- function set_duedate() {
- setTimeout(function() {
- $.ajax({
- url: 'is.pl?action=set_duedate',
- data: {
- invdate: $('#invdate').val(),
- payment_id: $('#payment_id').val(),
- },
- dataType: 'text',
- success: function (data) {
- $('#duedate').val(data);
- }
- })
- }, 0);
- }
//-->
</script>
<table width="100%">
</tr>
[%- END %]
+ <tr>
+ <td>[% LxERP.t8("Calculate due date automatically") %]</td>
+ <td>[% L.yes_no_tag("payment_term.auto_calculation", SELF.payment_term.auto_calculation, "data-auto-calculation-toggle"="1") %]</td>
+ </tr>
+
<tr>
<td>[%- 'Netto Terms' | $T8 %]</td>
<td>
- <input name="payment_term.terms_netto_as_number" value="[%- HTML.escape(SELF.payment_term.terms_netto_as_number) %]" size="6">
+ [% L.input_tag("payment_term.terms_netto_as_number", SELF.payment_term.terms_netto_as_number, size="6", disabled=(SELF.payment_term.auto_calculation ? '' : 1)) %]
</td>
</tr>
<tr><td><%bank_code%></td><td>[% LxERP.t8("Your bank code") %]</td></tr>
</table>
</form>
-
<th align="center"><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]"></th>
<th>[%- 'Description' | $T8 %]</th>
<th>[%- 'Long Description' | $T8 %]</th>
+ <th>[% 'Automatic date calculation' | $T8 %]</th>
<th align="right">[%- 'Netto Terms' | $T8 %]</th>
<th align="right">[%- 'Skonto Terms' | $T8 %]</th>
<th align="right">[%- 'Skonto' | $T8 %]</th>
</a>
</td>
<td>[%- HTML.escape(payment_term.description_long) %]</td>
+ <td>[% IF payment_term.auto_calculation %][% LxERP.t8("yes") %][% ELSE %][% LxERP.t8("no") %][% END %]</td>
<td align="right">[%- HTML.escape(payment_term.terms_netto_as_number) %]</td>
<td align="right">[%- HTML.escape(payment_term.terms_skonto_as_number) %]</td>
<td align="right">[%- HTML.escape(payment_term.percent_skonto_as_percent) %] %</td>
</form>
[% L.sortable_element('#payment_term_list tbody', url => 'controller.pl?action=PaymentTerm/reorder', with => 'payment_term_id') %]
-