Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision d3353176

Von Kivitendo Admin vor fast 2 Jahren hinzugefügt

  • ID d33531761ee2888fc29c9b369f8d6146f8c5d831
  • Vorgänger 8fdaae3a
  • Nachfolger 234666a9

Konten ausziffern - Controller

mit Templates, Javascript und CSS

Unterschiede anzeigen:

SL/Controller/Clearing.pm
package SL::Controller::Clearing;
use strict;
use parent qw(SL::Controller::Base);
use SL::Clearing;
use SL::DB::ClearedGroup;
use SL::DB::Project;
use SL::DB::Department;
use SL::DB::Chart;
use SL::Locale::String qw(t8);
use SL::DBUtils qw(selectall_hashref_query);
use List::MoreUtils qw(uniq);
use Rose::Object::MakeMethods::Generic (
'scalar' => [ qw(chart chart_transactions cleared_group_transactions fromdate todate project_id department_id susa_link) ]
);
__PACKAGE__->run_before('check_auth');
sub action_form {
my ($self) = @_;
$self->_parse_form;
if ( $self->chart && !$self->chart->clearing ) {
return $self->render('clearing/chart_missing', { layout => 1, process => 1 },
chart => $self->chart,
);
}
my @susa_url_params = (
controller => 'ca.pl',
action => 'list_transactions',
method => 'cash'
);
my %susa_params = (
accno => $self->chart ? $self->chart->accno : '',
fromdate => $self->fromdate ? $self->fromdate->to_kivitendo : undef,
todate => $self->todate ? $self->todate->to_kivitendo : undef,
description => $self->chart ? $self->chart->description : '',
);
foreach my $key ( keys %susa_params ) {
if ( $susa_params{$key} ) {
push(@susa_url_params, ( $key => $susa_params{$key} ));
}
}
$self->susa_link($self->url_for(@susa_url_params));
$self->setup_action_bar;
$self->{all_departments} = SL::DB::Manager::Department->get_all_sorted();
$::request->layout->use_javascript("${_}.js") for qw(knockout-3.5.1 knockout.kivitendo clearing);
$::request->layout->use_stylesheet("css/clearing.css");
$self->render('clearing/form', { layout => 1, process => 1 },
chart_id => $self->chart ? $self->chart->id : '',
susa_link => $self->susa_link,
);
}
sub _parse_form {
my ($self) = @_;
# chart, fromdate, todate, project_id, department_id, load_cleared
if ( $::form->{accno} ) { # only needed for link from ca list_transactions
# here we assume that there is only one chart per accno, though the old code for CA all_transactions allows for several chart.id to be summed up
$self->chart( SL::DB::Manager::Chart->find_by(accno => delete $::form->{accno}) );
} elsif ( $::form->{chart_id} ) {
$self->chart( SL::DB::Chart->new(id => delete $::form->{chart_id})->load);
};
# TODO: check that chart has clearing attribute
if ( $::form->{filter}->{fromdate} || $::form->{fromdate} ) {
my $fromdate = $::form->{filter}->{fromdate} || $::form->{fromdate};
$self->fromdate( $::locale->parse_date_to_object($fromdate) );
}
if ( $::form->{filter}->{todate} || $::form->{todate} ) {
my $todate = $::form->{filter}->{todate} || $::form->{todate};
$self->todate( $::locale->parse_date_to_object($todate) );
}
if ( $::form->{filter}->{project_id} || $::form->{project_id} ) {
$self->project_id($::form->{filter}->{project_id} || $::form->{project_id});
}
if ( $::form->{filter}->{department_id} || $::form->{department_id} ) {
$self->department_id($::form->{filter}->{department_id} || $::form->{department_id});
}
}
sub action_create_cleared_group {
my ($self) = @_;
my $cleared_group_transactions = $::request->post_data;
my @acc_trans_ids = map { $_->{acc_trans_id} } @{ $cleared_group_transactions };
my $result = SL::Clearing::create_cleared_group(\@acc_trans_ids);
if ( $result ) {
$self->js->flash('info', t8('Cleared bookings'));
} else {
$self->js->flash('error', t8('Error while clearing'));
}
return $self->js->render;
}
sub action_remove_cleared_group {
my ($self) = @_;
my $cleared_group_transactions = $::request->post_data;
my @cleared_group_ids= map { $_->{cleared_group_id} } @{ $cleared_group_transactions };
die "no unique cleared group" unless scalar uniq @cleared_group_ids == 1;
my $cleared_group_id = $cleared_group_ids[0];
my $result = SL::Clearing::remove_cleared_group($cleared_group_id);
if ( $result ) {
$self->js->flash('info', t8('Removed cleared group'));
} else {
$self->js->flash('error', t8('error while unclearing'));
}
return $self->js->render;
}
# actions returning JSON
sub action_list {
my ($self) = @_;
$self->_parse_form;
die "no valid clearing chart" unless $self->chart && $self->chart->clearing;
my $filter = delete $::form->{filter};
my %params = (
chart_id => $self->chart->id,
fromdate => $self->fromdate,
todate => $self->todate,
project_id => $self->project_id,
department_id => $self->department_id,
load_cleared => $filter->{load_cleared} ? 1 : 0,
);
my $chart_transactions = SL::Clearing::load_chart_transactions(\%params);
$self->chart_transactions($chart_transactions);
return $self->render(\ SL::JSON::to_json( $self->chart_transactions ), { layout => 0, type => 'json', process => 0 });
}
sub action_fetch_cleared_group {
my ($self) = @_;
$self->load_cleared_group_transactions($::form->{cleared_group_id});
return $self->render(\ SL::JSON::to_json( $self->cleared_group_transactions ), { layout => 0, type => 'json', process => 0 });
}
sub load_cleared_group_transactions {
my ($self, $cleared_group_id) = @_;
my $cleared_group_transactions = SL::Clearing::load_cleared_group_transactions_by_group_id($cleared_group_id);
# convert itime to locale_formatted itime
foreach my $line ( @$cleared_group_transactions ) {
my $dt = DateTime::Format::Pg->parse_datetime( $line->{itime} );
$line->{formatted_itime} = $::locale->format_date_object($dt, precision => 'seconds');
}
$self->cleared_group_transactions($cleared_group_transactions);
}
sub setup_action_bar {
my ($self, %params) = @_;
for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
link => [
t8('List Transactions'),
link => $self->susa_link,
],
);
}
}
sub add_javascripts {
$::request->layout->add_javascripts(qw(knockout-3.5.1.js knockout.kivitendo.js));
}
sub check_auth {
$::auth->assert('general_ledger');
}
1;
css/clearing.css
/* the selection box */
div.selection {
background-color: lightgray;
}
.bookings tr.cleared {
background-color: lightgreen;
}
.bookings tr.selected {
background-color: lightgray;
}
.bookings tr.selected_cleared_group {
background-color: orange;
}
.bookings tr:hover {
border-color: black;
outline: thin solid;
}
div.overflow {
padding: 20px;
resize: both;
overflow-y: auto;
max-height: 500px;
}
.table { border-collapse: collapse; }
.table th, .table td { border: 1px solid silver; padding: 3px}
.sortable { cursor: pointer; }
.sorted { background-color: #B5E0FF; }
table.grid th { text-align: right; }
.flex-container {
display:flex;
align-items: flex-start;
flex-wrap: wrap;
}
.flexing {
position: sticky;
top: 0;
margin: 2px;
min-width: 200px;
border: 2px solid;
}
js/clearing.js
function BookingModel(data) {
var self = this;
var precision = 2;
self.selected = ko.observable(false); // user selected this element
self.acc_trans_id = data.acc_trans_id;
self.accno = data.accno;
self.description = data.description;
self.reference = data.reference;
self.itime = data.itime; // only used for loaded groups?
self.formatted_itime = data.formatted_itime; // only used for loaded groups?
// amounts
self.amount = Number(data.amount); // amount in db format
self.debit = Number(data.debit);
self.credit = Number(data.credit);
self.orig_transdate = data.transdate; // unformatted date, needed when matching transdate filter
self.transdate = ko.observable(kivi.parse_date(data.transdate)).extend( { formatted: function(date) {
return kivi.format_date(date);
} });
// maybe directly add/precompute self.transdate_gettime, as we often use getTime() when comparing dates
self.cleared_group_id = ko.observable(data.cleared_group_id); // will be null or int. needs to be updated if json create_cleared_group was successful
self.cleared = ko.computed(function() {
return self.cleared_group_id() !== null ? true : false
}).extend({ formatted: function(val) {
return val ? '✓' : '';
} });
self.gegen_chart_accnos = data.gegen_chart_accnos;
self.employee = data.employee;
self.project = data.projectnumber === null ? null : data.projectnumber + ' ' + data.projectdescription;
self.project_id = ko.observable(Number(data.project_id));
self.toggle_selected = function() {
self.selected( !self.selected() );
}
// calculate class for css
self.xclass = ko.computed(function() {
if ( self.cleared() ) {
return 'cleared';
} else {
return self.selected() ? 'selected' : undefined;
}
});
}
// the main ViewModel
var BookingListViewModel = (function() {
var self = this;
self.precision = ko.observable(2); // used by formatted_amount bindingHandler
self.bookings = ko.observableArray([])
.extend({ fibu_sums: true, trackArrayChanges: true });
// fibu_sums adds bookings.debitSum, bookings.creditSum
self.selectedChartId = ko.observable(undefined); // bound to input of chartpicker. maybe make a computed and switch between true or false depending on whether value is seted?
// doesn't really work as a two-way binding, as it doesn't set dummy chart.displayable_name
self.selectedChart = ko.observable(); // contains the chart fat item
self.calink = ko.computed(function() {
if ( self.selectedChart() !== undefined ) {
return "ca.pl?action=list_transactions&method=cash&accno=" + self.selectedChart().accno;
} else {
return undefined;
}
});
self.redirect_chartlist = function() {
if ( self.calink() !== undefined ) {
window.location.href = self.calink();
}
}
self.selectedBookings = ko.computed(function() {
return ko.utils.arrayFilter(self.bookings(), function(booking) {
return booking.selected();
});
}).extend({ fibu_sums: true });
self.show_filter = ko.observable(true);
// settings
self.hideCleared = ko.observable(false); // whether to filter out cleared booking groups
self.automaticClearing = ko.observable(false); // automatically created a cleared_group when sum(amount) == 0
// filters
self.automaticAmountFiltering = ko.observable(false); // when clicking on a line, filter all lines which have the same amount (with opposite sign)
self.automaticDateFiltering = ko.observable(false); // when clicking on a line, filter all lines with the same date
self.automaticReferenceFiltering = ko.observable(false);
self.automaticProjectFiltering = ko.observable(false);
self.automaticEmployeeFiltering = ko.observable(false);
self.automaticDepartmentFiltering = ko.observable(false);
self.hasAutomaticFiltering = ko.computed(function() {
self.automaticAmountFiltering() ||
self.automaticDateFiltering() ||
self.automaticReferenceFiltering() ||
self.automaticEmployeeFiltering() ||
self.automaticProjectFiltering() ||
self.automaticDepartmentFiltering()
});
self.clicked_booking = ko.observable(); // needed for column line filters
self.resetClickedBooking = function() {
self.clicked_booking(undefined);
}
self.selectedClearedBookings = ko.observableArray().extend({ fibu_sums: true, trackArrayChanges: true }); // tAC needed?
self.isClearedGroupSelected = ko.computed(function() {
return self.selectedClearedBookings.hasBookings();
});
self.showSelectedBookings = ko.computed(function() {
return self.selectedBookings.hasBookings() && !self.isClearedGroupSelected();
});
// <!-- fuzzy settings with default values-->
self.fuzzyDateFilter = ko.observable(false);
self.fuzzyDateDays = ko.observable(2);
self.fuzzyAmountFilter = ko.observable(false);
self.fuzzyAmount = ko.observable(10);
self.fuzzyAmountFormatted = ko.computed({
read: function() {
return kivi.format_amount(self.fuzzyAmount(), 0);
},
write: function(formattedAmount) {
var parsed_amount = kivi.parse_amount(formattedAmount) || 1; // if user enters 0, assume he wants to disable fuzzyness, silently make it 1 to prevent divbyzero
self.fuzzyAmount(parsed_amount);
},
owner: self
});
// input fields for Column filters, searching inside results
self.searchReference = ko.observable('');
self.searchTransdate = ko.observable(null);
self.searchGegenChart = ko.observable('');
self.searchEmployee = ko.observable('');
self.searchProject = ko.observable(undefined);
self.searchAmount = ko.observable('0,00').extend({ to_kivitendo_not_zero_reformat: 2, throttle: 300 }); // will not display anything if value is 0
self.hasColumnFilter= ko.computed(function() {
return (
self.searchTransdate() !== null ||
self.searchReference() !== '' ||
self.searchGegenChart() !== '' ||
self.searchEmployee() !== '' ||
( self.searchAmount() !== '' && self.searchAmount() !== undefined ) ||
( self.searchProject() !== '' && self.searchProject() !== undefined )
);
});
self.resetColumnFilters = function() {
self.searchTransdate(null);
self.searchReference('');
self.searchGegenChart('');
self.searchEmployee('');
self.searchAmount(undefined);
self.searchProject('');
}
// ACTIONS
// action that happens when clicking on a booking
self.click = function(booking) {
if ( booking.cleared() ) {
// if there are already selected elements
// * switch group if different group_id
// * toggle group if save group_id
if ( self.selectedClearedGroupId() === booking.cleared_group_id() ) {
self.selectedClearedBookings.removeAll();
} else {
self.get_cleared_group(booking.cleared_group_id());
}
} else {
if ( self.isClearedGroupSelected() ) {
self.selectedClearedBookings.removeAll();
}
booking.toggle_selected();
if ( booking.selected() ) {
self.clicked_booking(booking);
} else {
self.resetClickedBooking();
}
}
}
self.filteredBookings = ko.computed(function () {
return ko.utils.arrayFilter(self.bookings(), function (booking) {
return (
booking.selected() // always display any line that was selected, regardless of filter
||
(
!( self.hideCleared() && booking.cleared() )
&& // header filters (manual): transdate, reference, gegenchart, employee, department, project
(
// transdate
( self.searchTransdate() === null // empty date
|| booking.transdate().getTime() === self.searchTransdate().getTime()
|| (
self.fuzzyDateFilter() && self.searchTransdate() && self.fuzzyDateDays() > 0
&& ((Math.abs( (self.searchTransdate() - booking.transdate())/(86400000) )) <= self.fuzzyDateDays())
)
)
&& // reference
( self.searchReference() && self.searchReference().length == 0
|| booking.reference.toLowerCase().indexOf(self.searchReference().toLowerCase()) > -1
)
&& // amount
// value in searchAmount, 2 modes possible: fuzzy mode or exact mode
( self.searchAmount() === undefined
||
(
( self.fuzzyAmountFilter()
&&
(
( Math.abs(Number(booking.amount)) <= Math.abs(kivi.parse_amount(self.searchAmount())) * (1 + Number(self.fuzzyAmount()/100)) )
&&
( Math.abs(Number(booking.amount)) >= Math.abs(kivi.parse_amount(self.searchAmount())) * (1 - Number(self.fuzzyAmount()/100)) )
)
)
||
(
Math.abs(Number(booking.amount)) === Math.abs(Number(kivi.parse_amount(self.searchAmount())))
)
)
)
&& // employee
( self.searchEmployee().length == 0
|| booking.employee.toLowerCase().indexOf (self.searchEmployee().toLowerCase()) > -1
)
&& // project
( ( self.searchProject() === undefined || self.searchProject() === null || self.searchProject() === '' )
|| booking.project_id() === Number(self.searchProject())
)
&& // gegenchart
( self.searchGegenChart().length == 0
|| booking.gegen_chart_accnos.toLowerCase().indexOf (self.searchGegenChart().toLowerCase()) > -1
)
)
&& ( // automatic click filters (automatic)
( // skip this whole section if nothing is selected and no automatic matching filters are selected
!self.selectedBookings.hasBookings() // skip all automatic click filter checks if nothing is selected
||
// also skip all automatic click filters if all automatic click filters are inactive
!self.hasAutomaticFiltering
)
||
( // individual automatic matching filters, we have already checked that self.clicked_booking() is valid
1 === 1
&& ( // employee filtering
!self.automaticEmployeeFiltering()
||
( self.automaticEmployeeFiltering()
&& self.clicked_booking()
&& booking.employee === self.clicked_booking().employee
)
)
&& ( // reference filtering
!self.automaticReferenceFiltering()
||
( self.automaticReferenceFiltering()
&& self.clicked_booking()
&& booking.reference === self.clicked_booking().reference
)
)
&& ( // project filtering
!self.automaticProjectFiltering()
||
// booking.project_id should always be defined, but may be 0, match all bookings without project_id (also 0)
( self.automaticProjectFiltering()
&& self.clicked_booking()
&& booking.project_id() === self.clicked_booking().project_id()
)
)
&& ( // date filtering
!self.automaticDateFiltering()
||
(
( self.automaticDateFiltering()
&& self.clicked_booking()
&& ( !self.fuzzyDateFilter()
&& self.clicked_booking().transdate().getTime() === booking.transdate().getTime()
)
||
(
self.fuzzyDateFilter() && self.fuzzyDateDays() > 0
&& ((Math.abs( (self.clicked_booking().transdate() - booking.transdate())/(86400000) )) <= self.fuzzyDateDays())
)
||
(
self.fuzzyDateFilter()
&& self.fuzzyDateDays() === '0'
&& self.clicked_booking().transdate().getTime() === booking.transdate().getTime()
)
)
)
)
&& ( // amount filtering
!self.automaticAmountFiltering()
||
(
( self.automaticAmountFiltering()
&& self.clicked_booking()
&& self.selectedBookings.hasBookings()
&& (
!self.fuzzyAmountFilter()
&&
Math.abs(self.clicked_booking().amount) === Math.abs(booking.amount)
&&
Math.sign(self.clicked_booking().amount) !== Math.sign(booking.amount)
)
||
(
self.fuzzyAmountFilter()
&&
Math.sign(self.clicked_booking().amount) !== Math.sign(booking.amount)
&&
( Math.abs(self.selectedBookings.saldo()) / Math.abs(Number(booking.amount)) > (1- Number(self.fuzzyAmount()/100)) )
&&
( Math.abs(self.selectedBookings.saldo()) / Math.abs(Number(booking.amount)) < (1+ Number(self.fuzzyAmount()/100)) )
)
)
)
) // end amount filtering
) // end of individual filters
) // automatic click filter
) //
) // return
});
}).extend( { throttle: 300, fibu_sums: true } );
// <!--------------------------- HEADERS -------------------------------->
// create headers programmatically
self.headers = ko.observable([
{title: kivi.t8('Transdate') , key: 'transdate' , datatype: 'date' , cssClass: '' },
{title: kivi.t8('Reference') , key: 'reference' , datatype: 'text' , cssClass: '' },
{title: kivi.t8('Debit') , key: 'amount' , datatype: 'numeric', cssClass: 'numeric' },
{title: kivi.t8('Credit') , key: 'amount' , datatype: 'numeric', cssClass: 'numeric' },
{title: kivi.t8('Contra accounts'), key: 'gegen_chart_accnos', datatype: 'text' , cssClass: '' },
{title: kivi.t8('Employee') , key: 'employee' , datatype: 'text' , cssClass: '' },
{title: kivi.t8('Project') , key: 'project_id' , datatype: 'numeric', cssClass: '' },
{title: kivi.t8('Cleared') , key: 'cleared' , datatype: 'boolean', cssClass: '' },
]);
self.sortHeader = ko.observable(self.headers[0]);
self.sortDirection = ko.observable(1);
self.toggleSort = function (header) {
if (header === self.sortHeader()) {
self.sortDirection(self.sortDirection() * -1);
} else {
self.sortHeader(header);
self.sortDirection(1);
}
};
// use a computed to subscribe to both self.sortHeader() and self.sortDirection()
self.sortBookings = ko.computed(function () {
var sortHeader = self.sortHeader(),
dir = self.sortDirection(),
tempBookings = self.bookings(),
prop = sortHeader ? sortHeader.key : "";
if (!prop) return;
tempBookings.sort(function (a, b) {
var va = ko.unwrap(a[prop]),
vb = ko.unwrap(b[prop]);
if ( sortHeader.key === 'amount' ) {
va = Math.abs(va);
vb = Math.abs(vb);
};
return va < vb ? -dir : va > vb ? dir : 0;
});
self.bookings.notifySubscribers();
});
// where the group_id of the currently selected cleared group is stored
self.selectedClearedGroupId = ko.computed( function() {
if ( self.isClearedGroupSelected() ) {
return self.selectedClearedBookings()[0].cleared_group_id();
} else {
return undefined;
}
});
// <!-- behaviour -->
self.deselectAll = function() {
ko.utils.arrayForEach(self.selectedBookings(), function(booking) {
if ( booking.selected() ) {
booking.selected(false);
}
});
};
self.selectAll = function() {
ko.utils.arrayForEach(self.filteredBookings(), function(booking) {
if ( !booking.selected() ) {
booking.selected(true);
}
})
};
self.deselectClearedGroup = function() {
self.selectedClearedGroupId(null);
self.selectedClearedBookings([]);
self.resetClickedBooking();
};
// <!--------- ajax actions ---------------------------------------------------->
// <!-- create_cleared_group, remove_cleared_group, get_cleared_group, update -->
self.create_cleared_group = function() {
if ( self.selectedBookings().length == 0 ) {
alert("create_cleared_group called with 0 selectedBookings, do nothing and return");
return;
};
$.ajax({
url: 'controller.pl?action=Clearing/create_cleared_group.json',
type: 'POST',
data: ko.toJSON(self.selectedBookings()),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: false,
success: function(data) {
kivi.eval_json_result(data);
self.resetClickedBooking();
self.update();
},
error: function(data) {
alert(data);
}
});
};
self.remove_cleared_group = function() {
var current_cleared_group_id = self.selectedClearedGroupId();
$.ajax({
url: 'controller.pl?action=Clearing/remove_cleared_group.json',
type: 'POST',
data: ko.toJSON(self.selectedClearedBookings()),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: false,
success: function(data) {
self.selectedClearedBookings([]);
kivi.eval_json_result(data); // flash
ko.utils.arrayForEach(self.bookings(), function(booking) {
if (booking.cleared_group_id() === current_cleared_group_id) {
booking.cleared_group_id(null);
}
}); //notify
self.resetClickedBooking();
},
error: function(data) {
alert('something went wrong');
}
});
}; // end of removed_cleared_group
self.get_cleared_group = function(cleared_group_id) {
var controller = 'controller.pl?action=Clearing/fetch_cleared_group';
$.getJSON(controller + '&cleared_group_id=' + cleared_group_id, function(data) {
self.selectedClearedBookings( ko.utils.arrayMap(data, function(item) { return new BookingModel(item) }) )
})
};
self.update = function() {
// kivi.clear_flash('error', 5000);
if ( !self.selectedChartId() ) {
self.bookings([]);
kivi.display_flash('error', kivi.t8('No chart selected'),0);
$("#chart_id_name").focus();
return 0;
}
var controller = 'controller.pl?action=Clearing/list';
var chart_id = $("#chart_id").val();
var filterdata = $('#clearing_filter').serialize()
var url = controller + '&' + 'chart_id=' + chart_id + '&' + filterdata;
$.getJSON(url, function(data) {
self.bookings( ko.utils.arrayMap(data, function(item) { return new BookingModel(item) }) )
})
};
self.init = function () {
ko.applyBindings(BookingListViewModel);
};
$(init);
// the returns are exposed to wm namespace!
return {
update: update,
click: click,
create_cleared_group: create_cleared_group,
remove_cleared_group: remove_cleared_group,
deselectClearedGroup: deselectClearedGroup,
selectedBookings: selectedBookings,
filteredBookings: filteredBookings,
selectedChartId: selectedChartId,
selectedChart: selectedChart,
selectedClearedGroupId: selectedClearedGroupId,
deselectAll: deselectAll,
searchProject: searchProject,
headers: headers,
sortHeader: sortHeader,
hideCleared: hideCleared,
toggleSort: toggleSort,
automaticAmountFiltering: automaticAmountFiltering,
automaticDateFiltering: automaticDateFiltering,
automaticReferenceFiltering: automaticReferenceFiltering,
automaticProjectFiltering: automaticProjectFiltering,
automaticEmployeeFiltering: automaticEmployeeFiltering,
automaticDepartmentFiltering: automaticDepartmentFiltering,
automaticClearing: automaticClearing
};
})();
$( document ).ready(function() {
$('#chart_id').on('change', function(event) {
BookingListViewModel.update();
});
$('#chart_id').on('set_item:ChartPicker', function (e, item) {
self.selectedChartId(item.id)
self.selectedChart(item)
});
$('#clearing_filter').on('change', 'input', function() {
$('form#clearing_filter').submit();
});
$('#project_id').change( function() {
BookingListViewModel.searchProject( $('#project_id').val() );
});
$('#automaticClearing').hover( function() { $('#automatic_clearing_info').toggle() });
$(document).keyup(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13') { <!-- enter -->
if ( BookingListViewModel.selectedBookings.hasBookings() && BookingListViewModel.selectedBookings.saldo() == 0 ) {
BookingListViewModel.create_cleared_group();
}
}
if(keycode == '27') { <!-- ESC -->
if ( BookingListViewModel.selectedBookings.hasBookings() ) {
BookingListViewModel.deselectAll();
}
}
});
$('#reset_form_filter').click(function() {
document.getElementById("clearing_filter").reset();
});
// automatically call create_cleared_group when certain conditions are met:
BookingListViewModel.selectedBookings.saldo.subscribe(function(new_sum) {
if ( BookingListViewModel.automaticClearing()
&& BookingListViewModel.selectedBookings.hasBookings()
&& new_sum == 0
) {
BookingListViewModel.create_cleared_group();
}
});
});
js/locale/de.js
"Carry over shipping address":"Lieferadresse übernehmen",
"Chart picker":"Kontenauswahl",
"Clear":"Löschen",
"Cleared":"Ausgeziffert",
"Contra accounts":"Gegenkonten",
"Copy":"Kopieren",
"Copy requirement spec":"Pflichtenheft kopieren",
"Copy template":"Vorlage kopieren",
......
"Create new quotation/order":"Neues Angebot/neuen Auftrag anlegen",
"Create new qutoation/order":"Neues Angebot/neuen Auftrag anlegen",
"Create new version":"Neue Version anlegen",
"Credit":"Haben",
"Customer details":"Kundendetails",
"Customer missing!":"Kundenname fehlt!",
"Database Connection Test":"Test der Datenbankverbindung",
"Debit":"Soll",
"Dec":"Dez",
"December":"Dezember",
"Delete":"Löschen",
......
"Edit project link":"Projektverknüpfung bearbeiten",
"Edit text block":"Textblock bearbeiten",
"Edit the configuration for periodic invoices":"Konfiguration für wiederkehrende Rechnungen bearbeiten",
"Employee":"Bearbeiter",
"Enter longdescription":"Langtext eingeben",
"Error: #1":"Fehler: #1",
"Error: Name missing":"Fehler: Name fehlt",
......
"Next month":"nächster Monat",
"No":"Nein",
"No article has been selected yet.":"Es wurde noch kein Artikel ausgewählt.",
"No chart selected":"Kein Konto ausgewählt",
"No delivery orders have been selected.":"Es wurden keine Lieferscheine ausgewählt.",
"No entries have been selected.":"Es wurden keine Einträge ausgewählt.",
"No file selected, please set one checkbox!":"Kein Element selektiert,bitte eine Box anklicken",
......
"Price Types":"Preistypen",
"Print options":"Druckoptionen",
"Print record":"Beleg drucken",
"Project":"Projekt",
"Project link actions":"Projektverknüpfungs-Aktionen",
"Project picker":"Projektauswahl",
"Quotations/Orders actions":"Aktionen für Angebote/Aufträge",
"Re-numbering all sections and function blocks in the order they are currently shown cannot be undone.":"Das Neu-Nummerieren aller Abschnitte und Funktionsblöcke kann nicht rückgängig gemacht werden.",
"Reference":"Referenz",
"Remove article":"Artikel entfernen",
"Rename attachment":"Dateianhang umbenennen",
"Renumber sections and function blocks":"Abschnitte/Funktionsblöcke neu nummerieren",
......
"Today":"heute",
"Toggle marker":"Markierung umschalten",
"Transaction description":"Vorgangsbezeichnung",
"Transdate":"Buchungsdatum",
"Transfer stock":"Lagerbewegungen",
"Tue":"Di",
"Tuesday":"Dienstag",
js/locale/en.js
"Carry over shipping address":"",
"Chart picker":"",
"Clear":"",
"Cleared":"",
"Contra accounts":"",
"Copy":"",
"Copy requirement spec":"",
"Copy template":"",
......
"Create new quotation/order":"",
"Create new qutoation/order":"",
"Create new version":"",
"Credit":"",
"Customer details":"",
"Customer missing!":"",
"Database Connection Test":"",
"Debit":"",
"Dec":"",
"December":"",
"Delete":"",
......
"Edit project link":"",
"Edit text block":"",
"Edit the configuration for periodic invoices":"",
"Employee":"",
"Enter longdescription":"",
"Error: #1":"",
"Error: Name missing":"",
......
"Next month":"",
"No":"",
"No article has been selected yet.":"",
"No chart selected":"",
"No delivery orders have been selected.":"",
"No entries have been selected.":"",
"No file selected, please set one checkbox!":"",
......
"Price Types":"",
"Print options":"",
"Print record":"",
"Project":"",
"Project link actions":"",
"Project picker":"",
"Quotations/Orders actions":"",
"Re-numbering all sections and function blocks in the order they are currently shown cannot be undone.":"",
"Reference":"",
"Remove article":"",
"Rename attachment":"",
"Renumber sections and function blocks":"",
......
"Today":"",
"Toggle marker":"",
"Transaction description":"",
"Transdate":"Booking Date",
"Transfer stock":"",
"Tue":"",
"Tuesday":"",
locale/de/all
'Author' => 'Verfasser/in',
'Auto Send?' => 'Auto. Versand?',
'Automatic Foreign Exchange Bank Fees' => 'Automatische Bankgebühren für Auslandsüberweisungen',
'Automatic clearing' => 'Automatisch ausziffern',
'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 sales' => 'Skontoautomatik Verkauf',
'Automatically create new bins' => 'Automatisches Zuweisen der Lagerplätze',
'Automatically created invoice for fee and interest for dunning %s' => 'Automatisch erzeugte Rechnung für Gebühren und Zinsen zu Mahnung %s',
'Automatically filter when matching' => 'Automatisch filtern bei Gleichheit von',
'Available' => 'Verfügbar',
'Available Prices' => 'Mögliche Preise',
'Available qty' => 'Lagerbestand',
......
'Cancel' => 'Abbrechen',
'Cancel Accounts Payables Transaction' => 'Kreditorenbuchung stornieren',
'Cancel Accounts Receivables Transaction' => 'Debitorenbuchung stornieren',
'Cancel clearing' => 'Ausziffern aufheben',
'Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount' => 'Storno verboten, da Zahlungen zum Beleg vorhanden sind. Entweder die Zahlungen löschen oder mit umgekehrten Vorzeichen ausbuchen, sodass der offene Betrag dem Rechnungsbetrag entspricht.',
'Cannot Post AP transaction with tax included!' => 'Kann diesen kreditorischen Beleg nicht mit "Steuer im Preis inbegriffen" verbuchen!',
'Cannot add Booking, reason: #1 DB: #2 ' => 'Kann die Buchung nicht hinzufügen, Grund: #1 DB: #2',
......
'Chargenumbers' => 'Chargennummern',
'Charset' => 'Zeichensatz',
'Chart' => 'Buchungskonto',
'Chart #1 isn\'t configured for clearing' => 'Das Konto #1 ist nicht für das Ausziffern konfiguriert',
'Chart Test' => 'Konten-Test',
'Chart Type' => 'Kontentyp',
'Chart assignments' => 'Chart-Zuordnungen',
......
'City' => 'Stadt',
'Classification' => 'Klassifizierung',
'Clear' => 'Löschen',
'Clear bookings' => 'Buchungen ausziffern',
'Clear chart' => 'Ausziffern',
'Clear fields' => 'Felder leeren',
'Cleared' => 'Ausgeziffert',
'Cleared Balance' => 'abgeschlossen',
'Cleared at' => 'Ausgeziffert am',
'Cleared bookings' => 'Buchungen ausgeziffert',
'Cleared group' => 'Ausziffergruppe',
'Cleared/uncleared only' => 'Status abgeglichen',
'Clearing Tax Received (No 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
'Clearing account for advance payments' => 'Verrechnungskonto für Anzahlungen',
......
'Continue' => 'Weiter',
'Contra' => 'gegen',
'Contra Account' => 'Gegenkonto',
'Contra Amount' => 'Gegenbetrag',
'Contra accounts' => 'Gegenkonten',
'Contrary to Reduced Master Data this will be shown as discount in records.' => 'Im Gegensatz zu Abschlag wird der Rabatt in Belegen ausgewiesen',
'Conversion of "birthday" contact person attribute' => 'Umstellung des Kontaktpersonenfeldes "Geburtstag"',
'Conversion to PDF failed: #1' => 'Konvertierung zu PDF schlug fehl: #1',
......
'Datev export encoding' => 'DATEV-Export Kodierung',
'Datevautomatik' => 'Datev-Automatik',
'Datum von' => 'Datum von',
'Days' => 'Tage',
'Deactivate by default' => 'Deaktiviert als Voreinstellung',
'Deadline' => 'Fristsetzung',
'Dear Sir or Madam,' => 'Sehr geehrte Damen und Herren,',
......
'Edit bank account' => 'Bankkonto bearbeiten',
'Edit booking group' => 'Buchungsgruppe bearbeiten',
'Edit business' => 'Kunden-/Lieferantentyp bearbeiten',
'Edit chart' => 'Konto bearbeiten',
'Edit complexity' => 'Komplexitätsgrad bearbeiten',
'Edit custom report query' => 'Benutzerdefinierte Berichts-Abfrage bearbeiten',
'Edit custom shipto' => 'Individuelle Lieferadresse bearbeiten',
......
'Error message from the webshop api:' => 'Fehlermeldung der Webshop Api',
'Error when saving: #1' => 'Fehler beim Speichern: #1',
'Error while applying year-end bookings!' => 'Fehler beim Durchführen der Abschlußbuchungen!',
'Error while clearing' => '',
'Error while creating project with project number of new order number, project number #1 already exists!' => 'Fehler beim Erstellen eines Projekts mit der Projektnummer der neuen Auftragsnummer, Projektnummer #1 existiert bereits!',
'Error while saving shop order #1. DB Error #2. Generic exception #3.' => 'Fehler beim Speichern der Shop-Bestellung #1. DB Fehler #2. Genereller Fehler #3.',
'Error with default taxzone' => 'Ungültige Standardsteuerzone',
......
'Filter for item variables' => 'Filter für benutzerdefinierte Artikelvariablen',
'Filter parts' => 'Artikel filtern',
'Filter record template' => 'Filter für Buchungsvorlagen',
'Filtered' => 'Gefiltert',
'Final Invoice' => 'Schlussrechnung',
'Final Invoice (one letter abbreviation)' => 'F',
'Final Invoice, please use mark as paid manually' => 'Rechnungstyp Schlussrechnung, bitte den Beleg manuell als bezahlt markieren',
......
'Function block number format' => 'Format der Funktionsblocknummerierung',
'Function/position' => 'Funktion/Position',
'Further Invoice for Advance Payment' => 'Weitere Anzahlungsrechnung',
'Fuzzy filter' => 'Unscharfer Filter',
'GL Transaction' => 'Dialogbuchung',
'GL Transaction (abbreviation)' => 'DB',
'GL Transactions' => 'Dialogbuchungen',
......
'Hide chart details' => 'Konteninformation verstecken',
'Hide chart list' => 'Kontenliste verstecken',
'Hide charts' => 'Konten verstecken',
'Hide cleared' => 'Ausgeziffert verstecken',
'Hide details' => 'Details verbergen',
'Hide help text' => 'Hilfetext verbergen',
'Hide mappings (csv_import)' => 'Spaltenzuordnungen verbergen',
......
'Listprice' => 'Listenpreis',
'Load' => 'Laden',
'Load an existing draft' => 'Einen bestehenden Entwurf laden',
'Load cleared bookings' => 'Ausgezifferte Buchungen laden',
'Load letter draft' => 'Briefentwurf laden',
'Load profile' => 'Profil laden',
'Loading...' => 'Wird geladen...',
......
'No business selected or found!' => 'Kein Kunden-/Lieferantentyp ausgewählt oder gefunden!',
'No carry-over chart configured!' => 'Kein Saldenvortragskonto konfiguriert!',
'No changes since previous version.' => 'Keine Änderungen seit der letzten Version.',
'No chart selected' => 'Kein Konto ausgewählt',
'No clients have been created yet.' => 'Es wurden noch keine Mandanten angelegt.',
'No contact selected to delete' => 'Keine Ansprechperson zum Löschen ausgewählt',
'No contra account selected!' => 'Kein Gegenkonto ausgewählt!',
......
'Number missing in Row' => 'Nummer fehlt in Zeile',
'Number of Data: ' => 'Anzahl Datensätze',
'Number of bins' => 'Anzahl Lagerplätze',
'Number of bookings' => 'Anzahl Buchungen',
'Number of columns of custom variables in form details (second row)' => 'Anzahl der Spalten für benutzerdef. Variablen in den Formulardetails (zweite Positionszeile)',
'Number of copies' => 'Anzahl Kopien',
'Number of data sets' => 'Anzahl Datensätze',
......
'Please ask your administrator to create warehouses and bins.' => 'Bitten Sie Ihren Administrator, dass er Lager und Lagerplätze anlegt.',
'Please change the partnumber of the following parts and run the update again:' => 'Bitte ändern Sie daher die Artikelnummer folgender Artikel:',
'Please choose a part.' => 'Bitte wählen Sie einen Artikel aus.',
'Please choose an account for clearing:' => 'Bitte wählen Sie ein Konto zum Ausziffern aus:',
'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => 'Bitte wählen Sie für welche Kontoart die Steuer angezeigt werden soll (ansonsten einfach die Häkchen entfernen)',
'Please choose the action to be processed for your target quantity:' => 'Bitte wählen Sie eine Aktion, die mit Ihrer gezählten Zielmenge durchgeführt werden soll:',
'Please configure the carry over and profit and loss accounts for year-end closing in the client configuration!' => 'Bitte konfigurieren Sie in der Mandantenkonfiguration das Saldenvortragskonto, das Gewinnvortragskonto und das Verlustvortragskonto!',
......
'Reconciliation with bank' => 'Kontenabgleich mit Bank',
'Record Type' => 'Belegtyp',
'Record Vendor Invoice' => 'Einkaufsrechnung erfassen',
'Record filters' => 'Belegfilter',
'Record in' => 'Buchen auf',
'Record number' => 'Belegnummer',
'Record numbers and dates' => 'Belegnummern &amp; Datum',
......
'Remove' => 'Entfernen',
'Remove Draft' => 'Entwurf löschen',
'Remove article' => 'Artikel entfernen',
'Removed cleared group' => 'Ausziffergruppe erfolgreich aufgelöst',
'Removed sections and function blocks: #1' => 'Entfernte Abschnitte und Funktionsblöcke: #1',
'Removed spoolfiles!' => 'Druckdateien entfernt!',
'Removed text blocks: #1' => 'Entfernte Textblöcke: #1',
......
'Requirement specs' => 'Pflichtenhefte',
'Reset' => 'Zurücksetzen',
'Reset Filter' => 'Filter zurücksetzen',
'Reset column filters' => 'Spaltenfilter zurücksetzen',
'Reset selected bookings' => 'Ausgewählte Buchungen zurücksetzen',
'Result' => 'Ergebnis',
'Result of SQL query' => 'Ergebnis einer SQL-Abfrage',
'Results per page' => 'Treffer pro Seite',
......
'Select Shipping Address' => 'Lieferadresse auswählen',
'Select a Customer' => 'Endkunde auswählen',
'Select a period' => 'Bitte Zeitraum auswählen',
'Select all' => 'Alle auswählen',
'Select article' => 'Artikel auswählen',
'Select federal state...' => 'Bundesland auswählen...',
'Select file to upload' => 'Datei zum Hochladen auswählen',
......
'entries imported' => 'Einträge importiert',
'error while disassembling for trans_ids #1 : #2' => 'Fehler beim Zerlegen von Erzeugnis für Transaktions-Id #1: #2',
'error while paying invoice #1 : ' => 'Fehler beim Bezahlen von Rechnung #1 : ',
'error while unclearing' => '',
'error while unlinking payment #1 : ' => 'Fehler beim Zurücksetzen von Zahlung #1:',
'every third month' => 'vierteljährlich',
'every time' => 'immer',
......
'warehouse_journal_list' => 'lagerbuchungsliste',
'warehouse_report_list' => 'lagerbestandsliste',
'warehouse_usage_list' => 'Lagerentnahmeliste',
'when sum 0 is reached / bookings are balanced' => 'Wenn Summe 0 erreicht ist / Buchungen ausgeglichen sind',
'will be set upon posting' => 'wird beim Buchen vergeben',
'will be set upon saving' => 'wird beim Speichern vergeben',
'with skonto acc. to pt' => 'mit Skonto nach ZB',
locale/en/all
'Author' => '',
'Auto Send?' => '',
'Automatic Foreign Exchange Bank Fees' => '',
'Automatic clearing' => '',
'Automatic date calculation' => '',
'Automatic deletion of leading, trailing and excessive (repetitive) spaces in customer or vendor names' => '',
'Automatic deletion of leading, trailing and excessive (repetitive) spaces in part description and part notes. Affects the CSV import as well.' => '',
......
'Automatic skonto chart sales' => '',
'Automatically create new bins' => '',
'Automatically created invoice for fee and interest for dunning %s' => '',
'Automatically filter when matching' => '',
'Available' => '',
'Available Prices' => '',
'Available qty' => '',
......
'Cancel' => '',
'Cancel Accounts Payables Transaction' => '',
'Cancel Accounts Receivables Transaction' => '',
'Cancel clearing' => '',
'Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount' => '',
'Cannot Post AP transaction with tax included!' => '',
'Cannot add Booking, reason: #1 DB: #2 ' => '',
......
'Chargenumbers' => '',
'Charset' => '',
'Chart' => '',
'Chart #1 isn\'t configured for clearing' => '',
'Chart Test' => '',
'Chart Type' => '',
'Chart assignments' => '',
......
'City' => '',
'Classification' => '',
'Clear' => '',
'Clear bookings' => '',
'Clear chart' => '',
'Clear fields' => '',
'Cleared' => '',
'Cleared Balance' => '',
'Cleared at' => '',
'Cleared bookings' => '',
'Cleared group' => '',
'Cleared/uncleared only' => '',
'Clearing Tax Received (No 71)' => '',
'Clearing account for advance payments' => '',
......
'Continue' => '',
'Contra' => '',
'Contra Account' => '',
'Contra Amount' => '',
'Contra accounts' => '',
'Contrary to Reduced Master Data this will be shown as discount in records.' => '',
'Conversion of "birthday" contact person attribute' => '',
'Conversion to PDF failed: #1' => '',
......
'Datev export encoding' => '',
'Datevautomatik' => '',
'Datum von' => '',
'Days' => '',
'Deactivate by default' => '',
'Deadline' => '',
'Dear Sir or Madam,' => '',
......
'Edit bank account' => '',
'Edit booking group' => '',
'Edit business' => '',
'Edit chart' => '',
'Edit complexity' => '',
'Edit custom report query' => '',
'Edit custom shipto' => '',
......
'Error message from the webshop api:' => '',
'Error when saving: #1' => '',
'Error while applying year-end bookings!' => '',
'Error while clearing' => '',
'Error while creating project with project number of new order number, project number #1 already exists!' => '',
'Error while saving shop order #1. DB Error #2. Generic exception #3.' => '',
'Error with default taxzone' => '',
......
'Filter for item variables' => '',
'Filter parts' => '',
'Filter record template' => '',
'Filtered' => '',
'Final Invoice' => '',
'Final Invoice (one letter abbreviation)' => '',
'Final Invoice, please use mark as paid manually' => '',
......
'Function block number format' => '',
'Function/position' => '',
'Further Invoice for Advance Payment' => '',
'Fuzzy filter' => '',
'GL Transaction' => '',
'GL Transaction (abbreviation)' => '',
'GL Transactions' => '',
......
'Hide chart details' => '',
'Hide chart list' => '',
'Hide charts' => '',
'Hide cleared' => '',
'Hide details' => '',
'Hide help text' => '',
'Hide mappings (csv_import)' => '',
......
'Listprice' => '',
'Load' => '',
'Load an existing draft' => '',
'Load cleared bookings' => '',
'Load letter draft' => '',
'Load profile' => '',
'Loading...' => '',
......
'No business selected or found!' => '',
'No carry-over chart configured!' => '',
'No changes since previous version.' => '',
'No chart selected' => '',
'No clients have been created yet.' => '',
'No contact selected to delete' => '',
'No contra account selected!' => '',
......
'Number missing in Row' => '',
'Number of Data: ' => '',
'Number of bins' => '',
'Number of bookings' => '',
'Number of columns of custom variables in form details (second row)' => '',
'Number of copies' => '',
'Number of data sets' => '',
......
'Please ask your administrator to create warehouses and bins.' => '',
'Please change the partnumber of the following parts and run the update again:' => '',
'Please choose a part.' => '',
'Please choose an account for clearing:' => '',
'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => '',
'Please choose the action to be processed for your target quantity:' => '',
'Please configure the carry over and profit and loss accounts for year-end closing in the client configuration!' => '',
......
'Reconciliation with bank' => '',
'Record Type' => '',
'Record Vendor Invoice' => '',
'Record filters' => '',
'Record in' => '',
'Record number' => '',
'Record numbers and dates' => '',
......
'Remove' => '',
'Remove Draft' => '',
'Remove article' => '',
'Removed cleared group' => '',
'Removed sections and function blocks: #1' => '',
'Removed spoolfiles!' => '',
'Removed text blocks: #1' => '',
......
'Requirement specs' => '',
'Reset' => '',
'Reset Filter' => '',
'Reset column filters' => '',
'Reset selected bookings' => '',
'Result' => '',
'Result of SQL query' => '',
'Results per page' => '',
......
'Select Shipping Address' => '',
'Select a Customer' => '',
'Select a period' => '',
'Select all' => '',
'Select article' => '',
'Select federal state...' => '',
'Select file to upload' => '',
......
'entries imported' => '',
'error while disassembling for trans_ids #1 : #2' => '',
'error while paying invoice #1 : ' => '',
'error while unclearing' => '',
'error while unlinking payment #1 : ' => '',
'every third month' => '',
'every time' => '',
......
'warehouse_journal_list' => '',
'warehouse_report_list' => '',
'warehouse_usage_list' => '',
'when sum 0 is reached / bookings are balanced' => '',
'will be set upon posting' => '',
'will be set upon saving' => '',
'with skonto acc. to pt' => '',
templates/webpages/clearing/form.html
[%- USE HTML -%]
[%- USE LxERP -%]
[%- USE T8 -%]
[%- USE L -%]
[%- USE P -%]
[% INCLUDE 'common/flash.html' %]
[% SET style="width: 300px" %]
[% SET style2="width: 200px" %]
<h1>[% 'Clear bookings' | $T8 %]: <!-- ko if: selectedChart --><span data-bind="text: selectedChart().displayable_name"></span><!-- /ko --></h1>
<div id="chart">
<div data-bind="hidden: selectedChartId">
[% 'Please choose an account for clearing:' | $T8 %]
</div>
<div>
[% 'Chart' | $T8 %]: [% P.chart.picker('chart_id', chart_id,
fat_set_item=1,
style=style,
type='clearing'
) %]
<button type="button" data-bind="visible: selectedChartId && calink() !== undefined, click: redirect_chartlist">[% 'List Transactions' | $T8 %]</button>
</div>
</div> <!-- chart -->
<div id="top" data-bind="visible: selectedChartId">
<div id="filter">
<div data-bind="visible: false">
[% 'Precision' %]: <input data-bind="value: precision"></input>
</div>
<form id="clearing_filter" name="clearing_filter" data-bind="submit: BookingListViewModel.update">
<fieldset>
<legend>[% 'Record filters' | $T8 %] <button data-bind="toggle: show_filter"><span data-bind="text: show_filter() ? '⌃' : '⌄'"></span></button></legend>
<div data-bind="visible: show_filter">
<table class="grid">
<tbody>
<tr>
<th>[% 'Invoice Date' | $T8 %]</th>
<td>[% 'From' | $T8 %] [% L.date_tag("filter.fromdate", SELF.fromdate || FORM.filter.fromdate, class="filter") %]
[% 'Until' | $T8 %] [% L.date_tag('filter.todate', SELF.todate || FORM.filter.todate, class="filter") %]
</td>
</tr>
<tr>
<th>[% 'Project' | $T8 %]</th>
<td>[% P.project.picker('filter.project_id', SELF.project_id, description_style='both', style=style, class="filter") %]</td>
</tr>
[% IF SELF.all_departments.size %]
<tr>
<th>[% 'Department' | $T8 %]</th><td>[% L.select_tag('filter.department_id', SELF.all_departments, default=SELF.department_id, title_key='description', with_empty=1, style='width:300px') %]</td>
</tr>
[% END %]
<tr>
<th>[% 'Load cleared bookings' | $T8 %]</th>
<td>[% P.checkbox_tag("filter.load_cleared", value=1, checked=FORM.load_cleared, class="filter") %]</td>
</tr>
</tbody>
</table>
<button type="submit" data-bind="enable: selectedChartId">[% 'Update' | $T8 %]</button>
<button type="button" id="reset_form_filter">[% 'Reset' | $T8 %]</button>
</div>
</fieldset>
</form>
</div> <!-- filter -->
</div> <!-- top -->
<div id="flexsettings" class="flex-container" data-bind="visible: selectedChartId">
<div id="settings" class="flexing">
<h2>[% 'Settings' | $T8 %]</h2>
<table>
<tr>
<td>[% 'Hide cleared' | $T8 %]:</td>
<td><input type="checkbox" data-bind="checked: hideCleared"></input>
</tr>
<tr>
<td>[% 'Automatic clearing' | $T8 %]:</td>
<td><input id="automaticClearing" type="checkbox" data-bind="checked: automaticClearing"></input>
<span style="display: none" id="automatic_clearing_info">([% 'when sum 0 is reached / bookings are balanced' | $T8 %])</span>
</td>
</tr>
</table>
</div> <!-- settings flex -->
<div id="autofilters" class="flexing">
<h2>[% 'Automatically filter when matching' | $T8 %]:</h2>
<table>
<tbody>
<tr>
<td>[% 'Contra Amount' | $T8 %]:</td><td><input type="checkbox" data-bind="checked: automaticAmountFiltering"></input></td>
<td></td>
<td>[% 'Employee' | $T8 %]:</td><td><input type="checkbox" data-bind="checked: automaticEmployeeFiltering"></input></td>
</tr>
<tr>
<td>[% 'Reference' | $T8 %]</td><td><input type="checkbox" data-bind="checked: automaticReferenceFiltering"></input></td>
<td></td>
<td>[% 'Project' | $T8 %]</td><td><input type="checkbox" data-bind="checked: automaticProjectFiltering"</input></td>
</tr>
<tr>
<td>[% 'Date' | $T8%]</td>
<td><input type="checkbox" data-bind="checked: automaticDateFiltering"></input></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div> <!-- autofilters flex -->
<div id="fuzzyfilters" class="flexing">
<h2>[% 'Fuzzy filter' | $T8 %]</h2>
<table>
<tr>
<td>[% 'Amount' | $T8 %]</td>
<td><input type="checkbox" data-bind="checked: fuzzyAmountFilter"></input>
±<input size="2" class="numeric" data-bind="value: fuzzyAmountFormatted, enable: fuzzyAmountFilter"></input>%
</tr>
<tr>
<td>[% 'Date' | $T8 %]</td>
<td><input type="checkbox" data-bind="checked: fuzzyDateFilter"></input>
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff