Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision d3353176

Von Kivitendo Admin vor mehr als 1 Jahr hinzugefügt

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

Konten ausziffern - Controller

mit Templates, Javascript und CSS

Unterschiede anzeigen:

SL/Controller/Clearing.pm
1
package SL::Controller::Clearing;
2

  
3
use strict;
4

  
5
use parent qw(SL::Controller::Base);
6

  
7
use SL::Clearing;
8
use SL::DB::ClearedGroup;
9
use SL::DB::Project;
10
use SL::DB::Department;
11
use SL::DB::Chart;
12
use SL::Locale::String qw(t8);
13
use SL::DBUtils qw(selectall_hashref_query);
14
use List::MoreUtils qw(uniq);
15

  
16
use Rose::Object::MakeMethods::Generic (
17
  'scalar' => [ qw(chart chart_transactions cleared_group_transactions fromdate todate project_id department_id susa_link) ]
18
);
19

  
20
__PACKAGE__->run_before('check_auth');
21

  
22
sub action_form {
23
  my ($self) = @_;
24

  
25
  $self->_parse_form;
26

  
27
  if ( $self->chart && !$self->chart->clearing ) {
28
    return $self->render('clearing/chart_missing', { layout => 1, process => 1 },
29
      chart => $self->chart,
30
    );
31
  }
32

  
33
  my @susa_url_params = (
34
    controller => 'ca.pl',
35
    action     => 'list_transactions',
36
    method     => 'cash'
37
  );
38

  
39
  my %susa_params = (
40
    accno       => $self->chart    ? $self->chart->accno           : '',
41
    fromdate    => $self->fromdate ? $self->fromdate->to_kivitendo : undef,
42
    todate      => $self->todate   ? $self->todate->to_kivitendo   : undef,
43
    description => $self->chart    ? $self->chart->description     : '',
44
  );
45

  
46
  foreach my $key ( keys %susa_params ) {
47
    if ( $susa_params{$key} ) {
48
      push(@susa_url_params, ( $key => $susa_params{$key} ));
49
    }
50
  }
51

  
52
  $self->susa_link($self->url_for(@susa_url_params));
53
  $self->setup_action_bar;
54

  
55
  $self->{all_departments} = SL::DB::Manager::Department->get_all_sorted();
56

  
57
  $::request->layout->use_javascript("${_}.js") for qw(knockout-3.5.1 knockout.kivitendo clearing);
58
  $::request->layout->use_stylesheet("css/clearing.css");
59

  
60
  $self->render('clearing/form', { layout => 1, process => 1 },
61
                 chart_id  => $self->chart ? $self->chart->id : '',
62
                 susa_link => $self->susa_link,
63
               );
64
}
65

  
66
sub _parse_form {
67
  my ($self) = @_;
68

  
69
  # chart, fromdate, todate, project_id, department_id, load_cleared
70

  
71
  if ( $::form->{accno} ) { # only needed for link from ca list_transactions
72
    # 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
73
    $self->chart( SL::DB::Manager::Chart->find_by(accno => delete $::form->{accno}) );
74
  } elsif ( $::form->{chart_id} ) {
75
    $self->chart( SL::DB::Chart->new(id => delete $::form->{chart_id})->load);
76
  };
77
  # TODO: check that chart has clearing attribute
78

  
79
  if ( $::form->{filter}->{fromdate} || $::form->{fromdate} ) {
80
    my $fromdate = $::form->{filter}->{fromdate} || $::form->{fromdate};
81
    $self->fromdate( $::locale->parse_date_to_object($fromdate) );
82
  }
83

  
84
  if ( $::form->{filter}->{todate} || $::form->{todate} ) {
85
    my $todate = $::form->{filter}->{todate} || $::form->{todate};
86
    $self->todate( $::locale->parse_date_to_object($todate) );
87
  }
88

  
89
  if ( $::form->{filter}->{project_id} || $::form->{project_id} ) {
90
    $self->project_id($::form->{filter}->{project_id} || $::form->{project_id});
91
  }
92

  
93
  if ( $::form->{filter}->{department_id} || $::form->{department_id} ) {
94
    $self->department_id($::form->{filter}->{department_id} || $::form->{department_id});
95
  }
96
}
97

  
98
sub action_create_cleared_group {
99
  my ($self) = @_;
100

  
101
  my $cleared_group_transactions = $::request->post_data;
102

  
103
  my @acc_trans_ids = map { $_->{acc_trans_id} } @{ $cleared_group_transactions };
104

  
105
  my $result = SL::Clearing::create_cleared_group(\@acc_trans_ids);
106
  if ( $result ) {
107
    $self->js->flash('info', t8('Cleared bookings'));
108
  } else {
109
    $self->js->flash('error', t8('Error while clearing'));
110
  }
111
  return $self->js->render;
112
}
113

  
114
sub action_remove_cleared_group {
115
  my ($self) = @_;
116

  
117
  my $cleared_group_transactions = $::request->post_data;
118

  
119
  my @cleared_group_ids= map { $_->{cleared_group_id} } @{ $cleared_group_transactions };
120
  die "no unique cleared group" unless scalar uniq @cleared_group_ids == 1;
121
  my $cleared_group_id = $cleared_group_ids[0];
122

  
123
  my $result = SL::Clearing::remove_cleared_group($cleared_group_id);
124
  if ( $result ) {
125
    $self->js->flash('info', t8('Removed cleared group'));
126
  } else {
127
    $self->js->flash('error', t8('error while unclearing'));
128
  }
129
  return $self->js->render;
130
}
131

  
132
# actions returning JSON
133

  
134
sub action_list {
135
  my ($self) = @_;
136

  
137
  $self->_parse_form;
138

  
139
  die "no valid clearing chart" unless $self->chart && $self->chart->clearing;
140

  
141
  my $filter = delete $::form->{filter};
142

  
143
  my %params = (
144
    chart_id      => $self->chart->id,
145
    fromdate      => $self->fromdate,
146
    todate        => $self->todate,
147
    project_id    => $self->project_id,
148
    department_id => $self->department_id,
149
    load_cleared  => $filter->{load_cleared} ? 1 : 0,
150
  );
151

  
152
  my $chart_transactions = SL::Clearing::load_chart_transactions(\%params);
153

  
154
  $self->chart_transactions($chart_transactions);
155

  
156
  return $self->render(\ SL::JSON::to_json( $self->chart_transactions ), { layout => 0, type => 'json', process => 0 });
157
}
158

  
159
sub action_fetch_cleared_group {
160
  my ($self) = @_;
161

  
162
  $self->load_cleared_group_transactions($::form->{cleared_group_id});
163
  return $self->render(\ SL::JSON::to_json( $self->cleared_group_transactions ), { layout => 0, type => 'json', process => 0 });
164
}
165

  
166
sub load_cleared_group_transactions {
167
  my ($self, $cleared_group_id) = @_;
168

  
169
  my $cleared_group_transactions = SL::Clearing::load_cleared_group_transactions_by_group_id($cleared_group_id);
170

  
171
  # convert itime to locale_formatted itime
172
  foreach my $line ( @$cleared_group_transactions ) {
173
    my $dt = DateTime::Format::Pg->parse_datetime( $line->{itime} );
174
    $line->{formatted_itime} = $::locale->format_date_object($dt, precision => 'seconds');
175
  }
176
  $self->cleared_group_transactions($cleared_group_transactions);
177
}
178

  
179
sub setup_action_bar {
180
  my ($self, %params) = @_;
181
  for my $bar ($::request->layout->get('actionbar')) {
182
    $bar->add(
183
      link => [
184
        t8('List Transactions'),
185
        link => $self->susa_link,
186
      ],
187
    );
188
  }
189
}
190

  
191
sub add_javascripts  {
192
  $::request->layout->add_javascripts(qw(knockout-3.5.1.js knockout.kivitendo.js));
193
}
194

  
195
sub check_auth {
196
  $::auth->assert('general_ledger');
197
}
198

  
199
1;
css/clearing.css
1
/* the selection box */
2
div.selection {
3
  background-color: lightgray;
4
}
5

  
6
.bookings tr.cleared {
7
  background-color: lightgreen;
8
}
9

  
10
.bookings tr.selected {
11
  background-color: lightgray;
12
}
13

  
14
.bookings tr.selected_cleared_group {
15
  background-color: orange;
16
}
17

  
18
.bookings tr:hover {
19
  border-color: black;
20
  outline: thin solid;
21
}
22

  
23
div.overflow {
24
   padding: 20px;
25
   resize: both;
26
   overflow-y: auto;
27
   max-height: 500px;
28
}
29

  
30
.table { border-collapse: collapse; }
31
.table th, .table td { border: 1px solid silver; padding: 3px}
32
.sortable { cursor: pointer; }
33
.sorted { background-color: #B5E0FF; }
34

  
35
table.grid th { text-align: right; }
36

  
37
.flex-container {
38
  display:flex;
39
  align-items: flex-start;
40
  flex-wrap: wrap;
41
}
42

  
43
.flexing {
44
  position: sticky;
45
  top: 0;
46
  margin: 2px;
47
  min-width: 200px;
48
  border: 2px solid;
49
}
js/clearing.js
1
function BookingModel(data) {
2
  var self = this;
3

  
4
  var precision = 2;
5

  
6
  self.selected         = ko.observable(false);  // user selected this element
7

  
8
  self.acc_trans_id     = data.acc_trans_id;
9
  self.accno            = data.accno;
10
  self.description      = data.description;
11
  self.reference        = data.reference;
12
  self.itime            = data.itime; // only used for loaded groups?
13
  self.formatted_itime  = data.formatted_itime; // only used for loaded groups?
14

  
15
  // amounts
16
  self.amount           = Number(data.amount); // amount in db format
17
  self.debit            = Number(data.debit);
18
  self.credit           = Number(data.credit);
19

  
20
  self.orig_transdate   = data.transdate; // unformatted date, needed when matching transdate filter
21

  
22
  self.transdate        = ko.observable(kivi.parse_date(data.transdate)).extend( { formatted: function(date) {
23
    return kivi.format_date(date);
24
  } });
25
  // maybe directly add/precompute self.transdate_gettime, as we often use getTime() when comparing dates
26

  
27
  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
28

  
29
  self.cleared = ko.computed(function() {
30
    return self.cleared_group_id() !== null ? true : false
31
  }).extend({ formatted: function(val) {
32
    return val ? '✓' : '';
33
  } });
34

  
35
  self.gegen_chart_accnos = data.gegen_chart_accnos;
36

  
37
  self.employee    = data.employee;
38

  
39
  self.project     = data.projectnumber === null ? null : data.projectnumber + ' ' + data.projectdescription;
40
  self.project_id  = ko.observable(Number(data.project_id));
41

  
42
  self.toggle_selected = function() {
43
    self.selected( !self.selected() );
44
  }
45

  
46
  // calculate class for css
47
  self.xclass = ko.computed(function() {
48
    if ( self.cleared() ) {
49
      return 'cleared';
50
    } else {
51
      return self.selected() ? 'selected' : undefined;
52
    }
53
  });
54
}
55

  
56
// the main ViewModel
57
var BookingListViewModel = (function() {
58
  var self = this;
59

  
60
  self.precision = ko.observable(2);  // used by formatted_amount bindingHandler
61

  
62
  self.bookings = ko.observableArray([])
63
    .extend({ fibu_sums: true, trackArrayChanges: true });
64
  // fibu_sums adds bookings.debitSum, bookings.creditSum
65

  
66
  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?
67
  // doesn't really work as a two-way binding, as it doesn't set dummy chart.displayable_name
68
  self.selectedChart = ko.observable(); // contains the chart fat item
69

  
70
  self.calink = ko.computed(function() {
71
    if ( self.selectedChart() !== undefined ) {
72
      return "ca.pl?action=list_transactions&method=cash&accno=" + self.selectedChart().accno;
73
    } else {
74
      return undefined;
75
    }
76
  });
77

  
78
  self.redirect_chartlist = function() {
79
    if ( self.calink() !== undefined ) {
80
      window.location.href = self.calink();
81
    }
82
  }
83

  
84
  self.selectedBookings = ko.computed(function() {
85
    return ko.utils.arrayFilter(self.bookings(), function(booking) {
86
      return booking.selected();
87
    });
88
  }).extend({ fibu_sums: true });
89

  
90
  self.show_filter = ko.observable(true);
91

  
92
  // settings
93
  self.hideCleared                  = ko.observable(false); // whether to filter out cleared booking groups
94
  self.automaticClearing            = ko.observable(false); // automatically created a cleared_group when sum(amount) == 0
95

  
96
  // filters
97
  self.automaticAmountFiltering     = ko.observable(false); // when clicking on a line, filter all lines which have the same amount (with opposite sign)
98
  self.automaticDateFiltering       = ko.observable(false); // when clicking on a line, filter all lines with the same date
99
  self.automaticReferenceFiltering  = ko.observable(false);
100
  self.automaticProjectFiltering    = ko.observable(false);
101
  self.automaticEmployeeFiltering   = ko.observable(false);
102
  self.automaticDepartmentFiltering = ko.observable(false);
103

  
104
  self.hasAutomaticFiltering = ko.computed(function() {
105
    self.automaticAmountFiltering()    ||
106
    self.automaticDateFiltering()      ||
107
    self.automaticReferenceFiltering() ||
108
    self.automaticEmployeeFiltering()  ||
109
    self.automaticProjectFiltering()   ||
110
    self.automaticDepartmentFiltering()
111
  });
112
   
113
  self.clicked_booking = ko.observable(); // needed for column line filters
114

  
115
  self.resetClickedBooking = function() {
116
    self.clicked_booking(undefined);
117
  }
118

  
119
  self.selectedClearedBookings = ko.observableArray().extend({ fibu_sums: true, trackArrayChanges: true }); // tAC needed?
120

  
121
  self.isClearedGroupSelected = ko.computed(function() {
122
    return self.selectedClearedBookings.hasBookings();
123
  });
124

  
125
  self.showSelectedBookings = ko.computed(function() {
126
    return self.selectedBookings.hasBookings() && !self.isClearedGroupSelected();
127
  });
128

  
129
  // <!-- fuzzy settings with default values-->
130
  self.fuzzyDateFilter   = ko.observable(false);
131
  self.fuzzyDateDays     = ko.observable(2);
132
  self.fuzzyAmountFilter = ko.observable(false);
133
  self.fuzzyAmount       = ko.observable(10);
134

  
135
  self.fuzzyAmountFormatted = ko.computed({
136
    read: function() {
137
      return kivi.format_amount(self.fuzzyAmount(), 0);
138
    },
139
    write: function(formattedAmount) {
140
      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
141
      self.fuzzyAmount(parsed_amount);
142
    },
143
    owner: self
144
  });
145

  
146
  // input fields for Column filters, searching inside results
147
  self.searchReference  = ko.observable('');
148
  self.searchTransdate  = ko.observable(null);
149
  self.searchGegenChart = ko.observable('');
150
  self.searchEmployee   = ko.observable('');
151
  self.searchProject    = ko.observable(undefined);
152
  self.searchAmount     = ko.observable('0,00').extend({ to_kivitendo_not_zero_reformat: 2, throttle: 300 }); // will not display anything if value is 0
153

  
154
  self.hasColumnFilter= ko.computed(function() {
155
    return (
156
      self.searchTransdate()  !== null      ||
157
      self.searchReference()  !== ''        ||
158
      self.searchGegenChart() !== ''        ||
159
      self.searchEmployee()   !== ''        ||
160
      ( self.searchAmount()   !== '' && self.searchAmount()  !== undefined ) || 
161
      ( self.searchProject()  !== '' && self.searchProject() !== undefined )
162
     );
163
   });
164

  
165
  self.resetColumnFilters = function() {
166
    self.searchTransdate(null);
167
    self.searchReference('');
168
    self.searchGegenChart('');
169
    self.searchEmployee('');
170
    self.searchAmount(undefined);
171
    self.searchProject('');
172
  }
173

  
174
  // ACTIONS
175

  
176
  // action that happens when clicking on a booking
177
  self.click = function(booking) {
178
    if ( booking.cleared() ) {
179
      // if there are already selected elements
180
      // * switch group if different group_id
181
      // * toggle group if save group_id
182
      if ( self.selectedClearedGroupId() === booking.cleared_group_id() ) {
183
        self.selectedClearedBookings.removeAll();
184
      } else {
185
        self.get_cleared_group(booking.cleared_group_id());
186
      }
187
    } else {
188
      if ( self.isClearedGroupSelected() ) {
189
        self.selectedClearedBookings.removeAll();
190
      }
191
      booking.toggle_selected();
192

  
193
      if ( booking.selected() ) {
194
        self.clicked_booking(booking);
195
      } else {
196
        self.resetClickedBooking();
197
      }
198
    }
199
  }
200

  
201
  self.filteredBookings = ko.computed(function () {
202
    return ko.utils.arrayFilter(self.bookings(), function (booking) {
203
      return (
204
                booking.selected()  // always display any line that was selected, regardless of filter
205

  
206
                ||
207

  
208
                (
209
                     !( self.hideCleared() && booking.cleared() )
210
                  && // header filters (manual): transdate, reference, gegenchart, employee, department, project
211
                     (
212
                          // transdate
213
                          (    self.searchTransdate() === null   // empty date
214
                            || booking.transdate().getTime() === self.searchTransdate().getTime()
215
                            || (
216
                                    self.fuzzyDateFilter() && self.searchTransdate() && self.fuzzyDateDays() > 0
217
                                 && ((Math.abs( (self.searchTransdate() - booking.transdate())/(86400000) )) <= self.fuzzyDateDays())
218
                               )
219
                          )
220
                       && // reference
221
                          (    self.searchReference() && self.searchReference().length == 0
222
                            || booking.reference.toLowerCase().indexOf(self.searchReference().toLowerCase()) > -1
223
                          )
224
                       && // amount
225
                          // value in searchAmount, 2 modes possible: fuzzy mode or exact mode
226
                          ( self.searchAmount() === undefined
227
                            ||
228
                            (
229
                              ( self.fuzzyAmountFilter()
230
                              &&
231
                                (
232
                                 ( Math.abs(Number(booking.amount)) <= Math.abs(kivi.parse_amount(self.searchAmount())) * (1 + Number(self.fuzzyAmount()/100)) )
233
                                   &&
234
                                 ( Math.abs(Number(booking.amount)) >= Math.abs(kivi.parse_amount(self.searchAmount())) * (1 - Number(self.fuzzyAmount()/100)) )
235
                                )
236
                              )
237
                              ||
238
                              (
239
                                Math.abs(Number(booking.amount)) === Math.abs(Number(kivi.parse_amount(self.searchAmount())))
240
                              )
241
                            )
242
                          )
243
                       && // employee
244
                          (    self.searchEmployee().length == 0
245
                            || booking.employee.toLowerCase().indexOf (self.searchEmployee().toLowerCase()) > -1
246
                          )
247
                       && // project
248
                          (  ( self.searchProject() === undefined || self.searchProject() === null || self.searchProject() === '' )
249
                             || booking.project_id() ===  Number(self.searchProject())
250
                          )
251
                       && // gegenchart
252
                          (    self.searchGegenChart().length == 0
253
                            || booking.gegen_chart_accnos.toLowerCase().indexOf (self.searchGegenChart().toLowerCase()) > -1
254
                          )
255
                     )
256
                  && ( // automatic click filters (automatic)
257
                       ( // skip this whole section if nothing is selected and no automatic matching filters are selected
258
                         !self.selectedBookings.hasBookings() // skip all automatic click filter checks if nothing is selected
259
                         ||
260
                         // also skip all automatic click filters if all automatic click filters are inactive
261
                         !self.hasAutomaticFiltering
262
                       )
263
                       ||
264
                       (  // individual automatic matching filters, we have already checked that self.clicked_booking() is valid
265
                         1 === 1
266
                         && ( // employee filtering
267
                             !self.automaticEmployeeFiltering()
268
                             ||
269
                             (    self.automaticEmployeeFiltering()
270
                               && self.clicked_booking()
271
                               && booking.employee === self.clicked_booking().employee
272
                             )
273
                            )
274

  
275
                         && ( // reference filtering
276
                               !self.automaticReferenceFiltering()
277
                             ||
278
                               (    self.automaticReferenceFiltering()
279
                                 && self.clicked_booking()
280
                                 && booking.reference === self.clicked_booking().reference
281
                               )
282
                            )
283

  
284
                         && ( // project filtering
285
                              !self.automaticProjectFiltering()
286
                             ||
287
                               // booking.project_id should always be defined, but may be 0, match all bookings without project_id (also 0)
288
                               ( self.automaticProjectFiltering()
289
                                 && self.clicked_booking()
290
                                 && booking.project_id() === self.clicked_booking().project_id()
291
                               )
292
                            )
293

  
294
                         && ( // date filtering
295
                               !self.automaticDateFiltering()
296
                             ||
297
                               (
298
                                 (    self.automaticDateFiltering()
299
                                   && self.clicked_booking()
300
                                   && ( !self.fuzzyDateFilter()
301
                                        && self.clicked_booking().transdate().getTime() === booking.transdate().getTime()
302
                                      )
303
                                      ||
304
                                      (
305
                                           self.fuzzyDateFilter() && self.fuzzyDateDays() > 0
306
                                        && ((Math.abs( (self.clicked_booking().transdate() - booking.transdate())/(86400000) )) <= self.fuzzyDateDays())
307
                                      )
308
                                      ||
309
                                      (
310
                                           self.fuzzyDateFilter()
311
                                        && self.fuzzyDateDays() === '0'
312
                                        && self.clicked_booking().transdate().getTime() === booking.transdate().getTime()
313
                                      )
314
                                  )
315
                               )
316
                            )
317

  
318
                         && ( // amount filtering
319
                                 !self.automaticAmountFiltering()
320
                              ||
321
                                 (
322
                                   ( self.automaticAmountFiltering()
323
                                     && self.clicked_booking()
324
                                     && self.selectedBookings.hasBookings()
325
                                     &&  (
326
                                           !self.fuzzyAmountFilter()
327
                                           &&
328
                                           Math.abs(self.clicked_booking().amount) === Math.abs(booking.amount)
329
                                           &&
330
                                           Math.sign(self.clicked_booking().amount) !== Math.sign(booking.amount)
331
                                         )
332
                                         ||
333
                                         (
334
                                           self.fuzzyAmountFilter()
335
                                           &&
336
                                           Math.sign(self.clicked_booking().amount) !== Math.sign(booking.amount)
337
                                           &&
338
                                           ( Math.abs(self.selectedBookings.saldo()) / Math.abs(Number(booking.amount)) > (1- Number(self.fuzzyAmount()/100)) )
339
                                           &&
340
                                           ( Math.abs(self.selectedBookings.saldo()) / Math.abs(Number(booking.amount)) < (1+ Number(self.fuzzyAmount()/100)) )
341
                                         )
342
                                    )
343
                                  )
344
                     ) // end amount filtering
345
                   ) // end of individual filters
346
                 ) // automatic click filter
347
             ) //
348
      ) // return
349
    });
350
  }).extend( { throttle: 300, fibu_sums: true } );
351

  
352
  // <!---------------------------  HEADERS -------------------------------->
353
  // create headers programmatically 
354
  self.headers = ko.observable([
355
    {title: kivi.t8('Transdate')      , key: 'transdate'         , datatype: 'date'   , cssClass: ''        },
356
    {title: kivi.t8('Reference')      , key: 'reference'         , datatype: 'text'   , cssClass: ''        },
357
    {title: kivi.t8('Debit')          , key: 'amount'            , datatype: 'numeric', cssClass: 'numeric' },
358
    {title: kivi.t8('Credit')         , key: 'amount'            , datatype: 'numeric', cssClass: 'numeric' },
359
    {title: kivi.t8('Contra accounts'), key: 'gegen_chart_accnos', datatype: 'text'   , cssClass: ''        },
360
    {title: kivi.t8('Employee')       , key: 'employee'          , datatype: 'text'   , cssClass: ''        },
361
    {title: kivi.t8('Project')        , key: 'project_id'        , datatype: 'numeric', cssClass: ''        },
362
    {title: kivi.t8('Cleared')        , key: 'cleared'           , datatype: 'boolean', cssClass: ''        },
363
  ]);
364

  
365
  self.sortHeader = ko.observable(self.headers[0]);
366
  self.sortDirection = ko.observable(1);
367
  self.toggleSort = function (header) {
368
    if (header === self.sortHeader()) {
369
        self.sortDirection(self.sortDirection() * -1);
370
    } else {
371
        self.sortHeader(header);
372
        self.sortDirection(1);
373
    }
374
  };
375

  
376
  // use a computed to subscribe to both self.sortHeader() and self.sortDirection()
377
  self.sortBookings = ko.computed(function () {
378
      var sortHeader = self.sortHeader(),
379
          dir = self.sortDirection(),
380
          tempBookings = self.bookings(),
381
          prop = sortHeader ? sortHeader.key : "";
382

  
383
      if (!prop) return;
384
      tempBookings.sort(function (a, b) {
385
          var va = ko.unwrap(a[prop]),
386
              vb = ko.unwrap(b[prop]);
387
          if ( sortHeader.key === 'amount' ) {
388
            va = Math.abs(va);
389
            vb = Math.abs(vb);
390
          };
391
          return va < vb ? -dir : va > vb ? dir : 0;
392
      });
393
      self.bookings.notifySubscribers();
394
  });
395

  
396
  // where the group_id of the currently selected cleared group is stored
397
  self.selectedClearedGroupId = ko.computed( function() {
398
    if ( self.isClearedGroupSelected() ) {
399
      return self.selectedClearedBookings()[0].cleared_group_id();
400
    } else {
401
      return undefined;
402
    }
403
  });
404

  
405
  // <!-- behaviour -->
406

  
407
  self.deselectAll = function() {
408
    ko.utils.arrayForEach(self.selectedBookings(), function(booking) {
409
      if ( booking.selected() ) {
410
        booking.selected(false);
411
      }
412
    });
413
  };
414

  
415
  self.selectAll = function() {
416
    ko.utils.arrayForEach(self.filteredBookings(), function(booking) {
417
      if ( !booking.selected() ) {
418
        booking.selected(true);
419
      }
420
    })
421
  };
422

  
423
  self.deselectClearedGroup = function() {
424
    self.selectedClearedGroupId(null);
425
    self.selectedClearedBookings([]);
426
    self.resetClickedBooking();
427
  };
428

  
429

  
430
  // <!--------- ajax actions ---------------------------------------------------->
431
  // <!-- create_cleared_group, remove_cleared_group, get_cleared_group, update -->
432

  
433
  self.create_cleared_group = function() {
434
    if ( self.selectedBookings().length == 0 ) {
435
      alert("create_cleared_group called with 0 selectedBookings, do nothing and return");
436
      return;
437
    };
438
    $.ajax({
439
      url: 'controller.pl?action=Clearing/create_cleared_group.json',
440
      type: 'POST',
441
      data: ko.toJSON(self.selectedBookings()),
442
      contentType: 'application/json; charset=utf-8',
443
      dataType: 'json',
444
      async: false,
445
      success: function(data) {
446
        kivi.eval_json_result(data);
447
        self.resetClickedBooking();
448
        self.update();
449
      },
450
      error: function(data) {
451
        alert(data);
452
      }
453
    });
454
  };
455

  
456
  self.remove_cleared_group = function() {
457
    var current_cleared_group_id = self.selectedClearedGroupId();
458
    $.ajax({
459
      url: 'controller.pl?action=Clearing/remove_cleared_group.json',
460
      type: 'POST',
461
      data: ko.toJSON(self.selectedClearedBookings()),
462
      contentType: 'application/json; charset=utf-8',
463
      dataType: 'json',
464
      async: false,
465
      success: function(data) {
466
        self.selectedClearedBookings([]);
467
        kivi.eval_json_result(data); // flash
468

  
469
        ko.utils.arrayForEach(self.bookings(), function(booking) {
470
          if (booking.cleared_group_id() === current_cleared_group_id) {
471
            booking.cleared_group_id(null);
472
          }
473
        }); //notify
474
        self.resetClickedBooking();
475
      },
476
      error: function(data) {
477
        alert('something went wrong');
478
      }
479
    });
480
  }; // end of removed_cleared_group
481

  
482
  self.get_cleared_group = function(cleared_group_id) {
483
    var controller = 'controller.pl?action=Clearing/fetch_cleared_group';
484
    $.getJSON(controller + '&cleared_group_id=' + cleared_group_id, function(data) {
485
      self.selectedClearedBookings( ko.utils.arrayMap(data, function(item) { return new BookingModel(item) }) )
486
    })
487
  };
488

  
489
  self.update = function() {
490
    // kivi.clear_flash('error', 5000);
491
    if ( !self.selectedChartId() ) {
492
      self.bookings([]);
493
      kivi.display_flash('error', kivi.t8('No chart selected'),0);
494
      $("#chart_id_name").focus();
495
      return 0;
496
    }
497
    var controller = 'controller.pl?action=Clearing/list';
498
    var chart_id = $("#chart_id").val();
499

  
500
    var filterdata = $('#clearing_filter').serialize()
501

  
502
    var url = controller + '&' + 'chart_id=' + chart_id + '&' + filterdata; 
503
    $.getJSON(url, function(data) {
504
      self.bookings( ko.utils.arrayMap(data, function(item) { return new BookingModel(item) }) )
505
    })
506
  };
507

  
508
  self.init = function () {
509
    ko.applyBindings(BookingListViewModel);
510
  };
511
  $(init);
512

  
513
  // the returns are exposed to wm namespace!
514
  return {
515
    update: update,
516
    click: click,
517

  
518
    create_cleared_group: create_cleared_group,
519
    remove_cleared_group: remove_cleared_group,
520
    deselectClearedGroup: deselectClearedGroup,
521

  
522
    selectedBookings: selectedBookings,
523
    filteredBookings: filteredBookings,
524
    selectedChartId: selectedChartId,
525
    selectedChart: selectedChart,
526
    selectedClearedGroupId: selectedClearedGroupId,
527

  
528
    deselectAll: deselectAll,
529

  
530
    searchProject: searchProject,
531

  
532
    headers: headers,
533
    sortHeader: sortHeader,
534

  
535
    hideCleared: hideCleared,
536

  
537
    toggleSort: toggleSort,
538

  
539
    automaticAmountFiltering: automaticAmountFiltering,
540
    automaticDateFiltering: automaticDateFiltering,
541
    automaticReferenceFiltering: automaticReferenceFiltering,
542
    automaticProjectFiltering: automaticProjectFiltering,
543
    automaticEmployeeFiltering: automaticEmployeeFiltering,
544
    automaticDepartmentFiltering: automaticDepartmentFiltering,
545

  
546
    automaticClearing: automaticClearing
547
  };
548
})();
549

  
550

  
551
$( document ).ready(function() {
552

  
553
  $('#chart_id').on('change', function(event) {
554
    BookingListViewModel.update();
555
  });
556

  
557
  $('#chart_id').on('set_item:ChartPicker', function (e, item) {
558
    self.selectedChartId(item.id)
559
    self.selectedChart(item)
560
  });
561

  
562
  $('#clearing_filter').on('change', 'input', function() {
563
    $('form#clearing_filter').submit();
564
  });
565

  
566
  $('#project_id').change( function() {
567
    BookingListViewModel.searchProject(  $('#project_id').val() );
568
  });
569

  
570
  $('#automaticClearing').hover( function() { $('#automatic_clearing_info').toggle() });
571

  
572
  $(document).keyup(function(event){
573
    var keycode = (event.keyCode ? event.keyCode : event.which);
574
    if(keycode == '13') { <!-- enter -->
575
      if ( BookingListViewModel.selectedBookings.hasBookings() && BookingListViewModel.selectedBookings.saldo() == 0 ) {
576
        BookingListViewModel.create_cleared_group();
577
      }
578
    }
579
    if(keycode == '27') { <!-- ESC -->
580
      if ( BookingListViewModel.selectedBookings.hasBookings() ) {
581
        BookingListViewModel.deselectAll();
582
      }
583
    }
584
  });
585

  
586
  $('#reset_form_filter').click(function() {
587
    document.getElementById("clearing_filter").reset();
588
  });
589

  
590
  // automatically call create_cleared_group when certain conditions are met:
591
  BookingListViewModel.selectedBookings.saldo.subscribe(function(new_sum) {
592
    if (    BookingListViewModel.automaticClearing()
593
         && BookingListViewModel.selectedBookings.hasBookings()
594
         && new_sum == 0 
595
       ) {
596
      BookingListViewModel.create_cleared_group();
597
    }
598
  });
599
});
js/locale/de.js
24 24
"Carry over shipping address":"Lieferadresse übernehmen",
25 25
"Chart picker":"Kontenauswahl",
26 26
"Clear":"Löschen",
27
"Cleared":"Ausgeziffert",
28
"Contra accounts":"Gegenkonten",
27 29
"Copy":"Kopieren",
28 30
"Copy requirement spec":"Pflichtenheft kopieren",
29 31
"Copy template":"Vorlage kopieren",
......
36 38
"Create new quotation/order":"Neues Angebot/neuen Auftrag anlegen",
37 39
"Create new qutoation/order":"Neues Angebot/neuen Auftrag anlegen",
38 40
"Create new version":"Neue Version anlegen",
41
"Credit":"Haben",
39 42
"Customer details":"Kundendetails",
40 43
"Customer missing!":"Kundenname fehlt!",
41 44
"Database Connection Test":"Test der Datenbankverbindung",
45
"Debit":"Soll",
42 46
"Dec":"Dez",
43 47
"December":"Dezember",
44 48
"Delete":"Löschen",
......
69 73
"Edit project link":"Projektverknüpfung bearbeiten",
70 74
"Edit text block":"Textblock bearbeiten",
71 75
"Edit the configuration for periodic invoices":"Konfiguration für wiederkehrende Rechnungen bearbeiten",
76
"Employee":"Bearbeiter",
72 77
"Enter longdescription":"Langtext eingeben",
73 78
"Error: #1":"Fehler: #1",
74 79
"Error: Name missing":"Fehler: Name fehlt",
......
109 114
"Next month":"nächster Monat",
110 115
"No":"Nein",
111 116
"No article has been selected yet.":"Es wurde noch kein Artikel ausgewählt.",
117
"No chart selected":"Kein Konto ausgewählt",
112 118
"No delivery orders have been selected.":"Es wurden keine Lieferscheine ausgewählt.",
113 119
"No entries have been selected.":"Es wurden keine Einträge ausgewählt.",
114 120
"No file selected, please set one checkbox!":"Kein Element selektiert,bitte eine Box anklicken",
......
133 139
"Price Types":"Preistypen",
134 140
"Print options":"Druckoptionen",
135 141
"Print record":"Beleg drucken",
142
"Project":"Projekt",
136 143
"Project link actions":"Projektverknüpfungs-Aktionen",
137 144
"Project picker":"Projektauswahl",
138 145
"Quotations/Orders actions":"Aktionen für Angebote/Aufträge",
139 146
"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.",
147
"Reference":"Referenz",
140 148
"Remove article":"Artikel entfernen",
141 149
"Rename attachment":"Dateianhang umbenennen",
142 150
"Renumber sections and function blocks":"Abschnitte/Funktionsblöcke neu nummerieren",
......
193 201
"Today":"heute",
194 202
"Toggle marker":"Markierung umschalten",
195 203
"Transaction description":"Vorgangsbezeichnung",
204
"Transdate":"Buchungsdatum",
196 205
"Transfer stock":"Lagerbewegungen",
197 206
"Tue":"Di",
198 207
"Tuesday":"Dienstag",
js/locale/en.js
24 24
"Carry over shipping address":"",
25 25
"Chart picker":"",
26 26
"Clear":"",
27
"Cleared":"",
28
"Contra accounts":"",
27 29
"Copy":"",
28 30
"Copy requirement spec":"",
29 31
"Copy template":"",
......
36 38
"Create new quotation/order":"",
37 39
"Create new qutoation/order":"",
38 40
"Create new version":"",
41
"Credit":"",
39 42
"Customer details":"",
40 43
"Customer missing!":"",
41 44
"Database Connection Test":"",
45
"Debit":"",
42 46
"Dec":"",
43 47
"December":"",
44 48
"Delete":"",
......
69 73
"Edit project link":"",
70 74
"Edit text block":"",
71 75
"Edit the configuration for periodic invoices":"",
76
"Employee":"",
72 77
"Enter longdescription":"",
73 78
"Error: #1":"",
74 79
"Error: Name missing":"",
......
109 114
"Next month":"",
110 115
"No":"",
111 116
"No article has been selected yet.":"",
117
"No chart selected":"",
112 118
"No delivery orders have been selected.":"",
113 119
"No entries have been selected.":"",
114 120
"No file selected, please set one checkbox!":"",
......
133 139
"Price Types":"",
134 140
"Print options":"",
135 141
"Print record":"",
142
"Project":"",
136 143
"Project link actions":"",
137 144
"Project picker":"",
138 145
"Quotations/Orders actions":"",
139 146
"Re-numbering all sections and function blocks in the order they are currently shown cannot be undone.":"",
147
"Reference":"",
140 148
"Remove article":"",
141 149
"Rename attachment":"",
142 150
"Renumber sections and function blocks":"",
......
193 201
"Today":"",
194 202
"Toggle marker":"",
195 203
"Transaction description":"",
204
"Transdate":"Booking Date",
196 205
"Transfer stock":"",
197 206
"Tue":"",
198 207
"Tuesday":"",
locale/de/all
437 437
  'Author'                      => 'Verfasser/in',
438 438
  'Auto Send?'                  => 'Auto. Versand?',
439 439
  'Automatic Foreign Exchange Bank Fees' => 'Automatische Bankgebühren für Auslandsüberweisungen',
440
  'Automatic clearing'          => 'Automatisch ausziffern',
440 441
  'Automatic date calculation'  => 'Automatische Datumsberechnung',
441 442
  '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',
442 443
  '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.',
......
444 445
  'Automatic skonto chart sales' => 'Skontoautomatik Verkauf',
445 446
  'Automatically create new bins' => 'Automatisches Zuweisen der Lagerplätze',
446 447
  'Automatically created invoice for fee and interest for dunning %s' => 'Automatisch erzeugte Rechnung für Gebühren und Zinsen zu Mahnung %s',
448
  'Automatically filter when matching' => 'Automatisch filtern bei Gleichheit von',
447 449
  'Available'                   => 'Verfügbar',
448 450
  'Available Prices'            => 'Mögliche Preise',
449 451
  'Available qty'               => 'Lagerbestand',
......
620 622
  'Cancel'                      => 'Abbrechen',
621 623
  'Cancel Accounts Payables Transaction' => 'Kreditorenbuchung stornieren',
622 624
  'Cancel Accounts Receivables Transaction' => 'Debitorenbuchung stornieren',
625
  'Cancel clearing'             => 'Ausziffern aufheben',
623 626
  '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.',
624 627
  'Cannot Post AP transaction with tax included!' => 'Kann diesen kreditorischen Beleg nicht mit "Steuer im Preis inbegriffen" verbuchen!',
625 628
  'Cannot add Booking, reason: #1 DB: #2 ' => 'Kann die Buchung nicht hinzufügen, Grund: #1 DB: #2',
......
701 704
  'Chargenumbers'               => 'Chargennummern',
702 705
  'Charset'                     => 'Zeichensatz',
703 706
  'Chart'                       => 'Buchungskonto',
707
  'Chart #1 isn\'t configured for clearing' => 'Das Konto #1 ist nicht für das Ausziffern konfiguriert',
704 708
  'Chart Test'                  => 'Konten-Test',
705 709
  'Chart Type'                  => 'Kontentyp',
706 710
  'Chart assignments'           => 'Chart-Zuordnungen',
......
733 737
  'City'                        => 'Stadt',
734 738
  'Classification'              => 'Klassifizierung',
735 739
  'Clear'                       => 'Löschen',
740
  'Clear bookings'              => 'Buchungen ausziffern',
736 741
  'Clear chart'                 => 'Ausziffern',
737 742
  'Clear fields'                => 'Felder leeren',
743
  'Cleared'                     => 'Ausgeziffert',
738 744
  'Cleared Balance'             => 'abgeschlossen',
745
  'Cleared at'                  => 'Ausgeziffert am',
746
  'Cleared bookings'            => 'Buchungen ausgeziffert',
747
  'Cleared group'               => 'Ausziffergruppe',
739 748
  'Cleared/uncleared only'      => 'Status abgeglichen',
740 749
  'Clearing Tax Received (No 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
741 750
  'Clearing account for advance payments' => 'Verrechnungskonto für Anzahlungen',
......
802 811
  'Continue'                    => 'Weiter',
803 812
  'Contra'                      => 'gegen',
804 813
  'Contra Account'              => 'Gegenkonto',
814
  'Contra Amount'               => 'Gegenbetrag',
815
  'Contra accounts'             => 'Gegenkonten',
805 816
  'Contrary to Reduced Master Data this will be shown as discount in records.' => 'Im Gegensatz zu Abschlag wird der Rabatt in Belegen ausgewiesen',
806 817
  'Conversion of "birthday" contact person attribute' => 'Umstellung des Kontaktpersonenfeldes "Geburtstag"',
807 818
  'Conversion to PDF failed: #1' => 'Konvertierung zu PDF schlug fehl: #1',
......
1068 1079
  'Datev export encoding'       => 'DATEV-Export Kodierung',
1069 1080
  'Datevautomatik'              => 'Datev-Automatik',
1070 1081
  'Datum von'                   => 'Datum von',
1082
  'Days'                        => 'Tage',
1071 1083
  'Deactivate by default'       => 'Deaktiviert als Voreinstellung',
1072 1084
  'Deadline'                    => 'Fristsetzung',
1073 1085
  'Dear Sir or Madam,'          => 'Sehr geehrte Damen und Herren,',
......
1403 1415
  'Edit bank account'           => 'Bankkonto bearbeiten',
1404 1416
  'Edit booking group'          => 'Buchungsgruppe bearbeiten',
1405 1417
  'Edit business'               => 'Kunden-/Lieferantentyp bearbeiten',
1418
  'Edit chart'                  => 'Konto bearbeiten',
1406 1419
  'Edit complexity'             => 'Komplexitätsgrad bearbeiten',
1407 1420
  'Edit custom report query'    => 'Benutzerdefinierte Berichts-Abfrage bearbeiten',
1408 1421
  'Edit custom shipto'          => 'Individuelle Lieferadresse bearbeiten',
......
1515 1528
  'Error message from the webshop api:' => 'Fehlermeldung der Webshop Api',
1516 1529
  'Error when saving: #1'       => 'Fehler beim Speichern: #1',
1517 1530
  'Error while applying year-end bookings!' => 'Fehler beim Durchführen der Abschlußbuchungen!',
1531
  'Error while clearing'        => '',
1518 1532
  '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!',
1519 1533
  '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.',
1520 1534
  'Error with default taxzone'  => 'Ungültige Standardsteuerzone',
......
1708 1722
  'Filter for item variables'   => 'Filter für benutzerdefinierte Artikelvariablen',
1709 1723
  'Filter parts'                => 'Artikel filtern',
1710 1724
  'Filter record template'      => 'Filter für Buchungsvorlagen',
1725
  'Filtered'                    => 'Gefiltert',
1711 1726
  'Final Invoice'               => 'Schlussrechnung',
1712 1727
  'Final Invoice (one letter abbreviation)' => 'F',
1713 1728
  'Final Invoice, please use mark as paid manually' => 'Rechnungstyp Schlussrechnung, bitte den Beleg manuell als bezahlt markieren',
......
1785 1800
  'Function block number format' => 'Format der Funktionsblocknummerierung',
1786 1801
  'Function/position'           => 'Funktion/Position',
1787 1802
  'Further Invoice for Advance Payment' => 'Weitere Anzahlungsrechnung',
1803
  'Fuzzy filter'                => 'Unscharfer Filter',
1788 1804
  'GL Transaction'              => 'Dialogbuchung',
1789 1805
  'GL Transaction (abbreviation)' => 'DB',
1790 1806
  'GL Transactions'             => 'Dialogbuchungen',
......
1854 1870
  'Hide chart details'          => 'Konteninformation verstecken',
1855 1871
  'Hide chart list'             => 'Kontenliste verstecken',
1856 1872
  'Hide charts'                 => 'Konten verstecken',
1873
  'Hide cleared'                => 'Ausgeziffert verstecken',
1857 1874
  'Hide details'                => 'Details verbergen',
1858 1875
  'Hide help text'              => 'Hilfetext verbergen',
1859 1876
  'Hide mappings (csv_import)'  => 'Spaltenzuordnungen verbergen',
......
2203 2220
  'Listprice'                   => 'Listenpreis',
2204 2221
  'Load'                        => 'Laden',
2205 2222
  'Load an existing draft'      => 'Einen bestehenden Entwurf laden',
2223
  'Load cleared bookings'       => 'Ausgezifferte Buchungen laden',
2206 2224
  'Load letter draft'           => 'Briefentwurf laden',
2207 2225
  'Load profile'                => 'Profil laden',
2208 2226
  'Loading...'                  => 'Wird geladen...',
......
2419 2437
  'No business selected or found!' => 'Kein Kunden-/Lieferantentyp ausgewählt oder gefunden!',
2420 2438
  'No carry-over chart configured!' => 'Kein Saldenvortragskonto konfiguriert!',
2421 2439
  'No changes since previous version.' => 'Keine Änderungen seit der letzten Version.',
2440
  'No chart selected'           => 'Kein Konto ausgewählt',
2422 2441
  'No clients have been created yet.' => 'Es wurden noch keine Mandanten angelegt.',
2423 2442
  'No contact selected to delete' => 'Keine Ansprechperson zum Löschen ausgewählt',
2424 2443
  'No contra account selected!' => 'Kein Gegenkonto ausgewählt!',
......
2529 2548
  'Number missing in Row'       => 'Nummer fehlt in Zeile',
2530 2549
  'Number of Data: '            => 'Anzahl Datensätze',
2531 2550
  'Number of bins'              => 'Anzahl Lagerplätze',
2551
  'Number of bookings'          => 'Anzahl Buchungen',
2532 2552
  'Number of columns of custom variables in form details (second row)' => 'Anzahl der Spalten für benutzerdef. Variablen in den Formulardetails (zweite Positionszeile)',
2533 2553
  'Number of copies'            => 'Anzahl Kopien',
2534 2554
  'Number of data sets'         => 'Anzahl Datensätze',
......
2773 2793
  'Please ask your administrator to create warehouses and bins.' => 'Bitten Sie Ihren Administrator, dass er Lager und Lagerplätze anlegt.',
2774 2794
  'Please change the partnumber of the following parts and run the update again:' => 'Bitte ändern Sie daher die Artikelnummer folgender Artikel:',
2775 2795
  'Please choose a part.'       => 'Bitte wählen Sie einen Artikel aus.',
2796
  'Please choose an account for clearing:' => 'Bitte wählen Sie ein Konto zum Ausziffern aus:',
2776 2797
  '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)',
2777 2798
  '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:',
2778 2799
  '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!',
......
3085 3106
  'Reconciliation with bank'    => 'Kontenabgleich mit Bank',
3086 3107
  'Record Type'                 => 'Belegtyp',
3087 3108
  'Record Vendor Invoice'       => 'Einkaufsrechnung erfassen',
3109
  'Record filters'              => 'Belegfilter',
3088 3110
  'Record in'                   => 'Buchen auf',
3089 3111
  'Record number'               => 'Belegnummer',
3090 3112
  'Record numbers and dates'    => 'Belegnummern &amp; Datum',
......
3124 3146
  'Remove'                      => 'Entfernen',
3125 3147
  'Remove Draft'                => 'Entwurf löschen',
3126 3148
  'Remove article'              => 'Artikel entfernen',
3149
  'Removed cleared group'       => 'Ausziffergruppe erfolgreich aufgelöst',
3127 3150
  'Removed sections and function blocks: #1' => 'Entfernte Abschnitte und Funktionsblöcke: #1',
3128 3151
  'Removed spoolfiles!'         => 'Druckdateien entfernt!',
3129 3152
  'Removed text blocks: #1'     => 'Entfernte Textblöcke: #1',
......
3187 3210
  'Requirement specs'           => 'Pflichtenhefte',
3188 3211
  'Reset'                       => 'Zurücksetzen',
3189 3212
  'Reset Filter'                => 'Filter zurücksetzen',
3213
  'Reset column filters'        => 'Spaltenfilter zurücksetzen',
3214
  'Reset selected bookings'     => 'Ausgewählte Buchungen zurücksetzen',
3190 3215
  'Result'                      => 'Ergebnis',
3191 3216
  'Result of SQL query'         => 'Ergebnis einer SQL-Abfrage',
3192 3217
  'Results per page'            => 'Treffer pro Seite',
......
3370 3395
  'Select Shipping Address'     => 'Lieferadresse auswählen',
3371 3396
  'Select a Customer'           => 'Endkunde auswählen',
3372 3397
  'Select a period'             => 'Bitte Zeitraum auswählen',
3398
  'Select all'                  => 'Alle auswählen',
3373 3399
  'Select article'              => 'Artikel auswählen',
3374 3400
  'Select federal state...'     => 'Bundesland auswählen...',
3375 3401
  'Select file to upload'       => 'Datei zum Hochladen auswählen',
......
4831 4857
  'entries imported'            => 'Einträge importiert',
4832 4858
  'error while disassembling for trans_ids #1 : #2' => 'Fehler beim Zerlegen von Erzeugnis für Transaktions-Id #1: #2',
4833 4859
  'error while paying invoice #1 : ' => 'Fehler beim Bezahlen von Rechnung #1 : ',
4860
  'error while unclearing'      => '',
4834 4861
  'error while unlinking payment #1 : ' => 'Fehler beim Zurücksetzen von Zahlung #1:',
4835 4862
  'every third month'           => 'vierteljährlich',
4836 4863
  'every time'                  => 'immer',
......
5062 5089
  'warehouse_journal_list'      => 'lagerbuchungsliste',
5063 5090
  'warehouse_report_list'       => 'lagerbestandsliste',
5064 5091
  'warehouse_usage_list'        => 'Lagerentnahmeliste',
5092
  'when sum 0 is reached / bookings are balanced' => 'Wenn Summe 0 erreicht ist / Buchungen ausgeglichen sind',
5065 5093
  'will be set upon posting'    => 'wird beim Buchen vergeben',
5066 5094
  'will be set upon saving'     => 'wird beim Speichern vergeben',
5067 5095
  'with skonto acc. to pt'      => 'mit Skonto nach ZB',
locale/en/all
437 437
  'Author'                      => '',
438 438
  'Auto Send?'                  => '',
439 439
  'Automatic Foreign Exchange Bank Fees' => '',
440
  'Automatic clearing'          => '',
440 441
  'Automatic date calculation'  => '',
441 442
  'Automatic deletion of leading, trailing and excessive (repetitive) spaces in customer or vendor names' => '',
442 443
  'Automatic deletion of leading, trailing and excessive (repetitive) spaces in part description and part notes. Affects the CSV import as well.' => '',
......
444 445
  'Automatic skonto chart sales' => '',
445 446
  'Automatically create new bins' => '',
446 447
  'Automatically created invoice for fee and interest for dunning %s' => '',
448
  'Automatically filter when matching' => '',
447 449
  'Available'                   => '',
448 450
  'Available Prices'            => '',
449 451
  'Available qty'               => '',
......
620 622
  'Cancel'                      => '',
621 623
  'Cancel Accounts Payables Transaction' => '',
622 624
  'Cancel Accounts Receivables Transaction' => '',
625
  'Cancel clearing'             => '',
623 626
  'Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount' => '',
624 627
  'Cannot Post AP transaction with tax included!' => '',
625 628
  'Cannot add Booking, reason: #1 DB: #2 ' => '',
......
701 704
  'Chargenumbers'               => '',
702 705
  'Charset'                     => '',
703 706
  'Chart'                       => '',
707
  'Chart #1 isn\'t configured for clearing' => '',
704 708
  'Chart Test'                  => '',
705 709
  'Chart Type'                  => '',
706 710
  'Chart assignments'           => '',
......
733 737
  'City'                        => '',
734 738
  'Classification'              => '',
735 739
  'Clear'                       => '',
740
  'Clear bookings'              => '',
736 741
  'Clear chart'                 => '',
737 742
  'Clear fields'                => '',
743
  'Cleared'                     => '',
738 744
  'Cleared Balance'             => '',
745
  'Cleared at'                  => '',
746
  'Cleared bookings'            => '',
747
  'Cleared group'               => '',
739 748
  'Cleared/uncleared only'      => '',
740 749
  'Clearing Tax Received (No 71)' => '',
741 750
  'Clearing account for advance payments' => '',
......
802 811
  'Continue'                    => '',
803 812
  'Contra'                      => '',
804 813
  'Contra Account'              => '',
814
  'Contra Amount'               => '',
815
  'Contra accounts'             => '',
805 816
  'Contrary to Reduced Master Data this will be shown as discount in records.' => '',
806 817
  'Conversion of "birthday" contact person attribute' => '',
807 818
  'Conversion to PDF failed: #1' => '',
......
1068 1079
  'Datev export encoding'       => '',
1069 1080
  'Datevautomatik'              => '',
1070 1081
  'Datum von'                   => '',
1082
  'Days'                        => '',
1071 1083
  'Deactivate by default'       => '',
1072 1084
  'Deadline'                    => '',
1073 1085
  'Dear Sir or Madam,'          => '',
......
1403 1415
  'Edit bank account'           => '',
1404 1416
  'Edit booking group'          => '',
1405 1417
  'Edit business'               => '',
1418
  'Edit chart'                  => '',
1406 1419
  'Edit complexity'             => '',
1407 1420
  'Edit custom report query'    => '',
1408 1421
  'Edit custom shipto'          => '',
......
1515 1528
  'Error message from the webshop api:' => '',
1516 1529
  'Error when saving: #1'       => '',
1517 1530
  'Error while applying year-end bookings!' => '',
1531
  'Error while clearing'        => '',
1518 1532
  'Error while creating project with project number of new order number, project number #1 already exists!' => '',
1519 1533
  'Error while saving shop order #1. DB Error #2. Generic exception #3.' => '',
1520 1534
  'Error with default taxzone'  => '',
......
1708 1722
  'Filter for item variables'   => '',
1709 1723
  'Filter parts'                => '',
1710 1724
  'Filter record template'      => '',
1725
  'Filtered'                    => '',
1711 1726
  'Final Invoice'               => '',
1712 1727
  'Final Invoice (one letter abbreviation)' => '',
1713 1728
  'Final Invoice, please use mark as paid manually' => '',
......
1784 1799
  'Function block number format' => '',
1785 1800
  'Function/position'           => '',
1786 1801
  'Further Invoice for Advance Payment' => '',
1802
  'Fuzzy filter'                => '',
1787 1803
  'GL Transaction'              => '',
1788 1804
  'GL Transaction (abbreviation)' => '',
1789 1805
  'GL Transactions'             => '',
......
1853 1869
  'Hide chart details'          => '',
1854 1870
  'Hide chart list'             => '',
1855 1871
  'Hide charts'                 => '',
1872
  'Hide cleared'                => '',
1856 1873
  'Hide details'                => '',
1857 1874
  'Hide help text'              => '',
1858 1875
  'Hide mappings (csv_import)'  => '',
......
2202 2219
  'Listprice'                   => '',
2203 2220
  'Load'                        => '',
2204 2221
  'Load an existing draft'      => '',
2222
  'Load cleared bookings'       => '',
2205 2223
  'Load letter draft'           => '',
2206 2224
  'Load profile'                => '',
2207 2225
  'Loading...'                  => '',
......
2418 2436
  'No business selected or found!' => '',
2419 2437
  'No carry-over chart configured!' => '',
2420 2438
  'No changes since previous version.' => '',
2439
  'No chart selected'           => '',
2421 2440
  'No clients have been created yet.' => '',
2422 2441
  'No contact selected to delete' => '',
2423 2442
  'No contra account selected!' => '',
......
2528 2547
  'Number missing in Row'       => '',
2529 2548
  'Number of Data: '            => '',
2530 2549
  'Number of bins'              => '',
2550
  'Number of bookings'          => '',
2531 2551
  'Number of columns of custom variables in form details (second row)' => '',
2532 2552
  'Number of copies'            => '',
2533 2553
  'Number of data sets'         => '',
......
2772 2792
  'Please ask your administrator to create warehouses and bins.' => '',
2773 2793
  'Please change the partnumber of the following parts and run the update again:' => '',
2774 2794
  'Please choose a part.'       => '',
2795
  'Please choose an account for clearing:' => '',
2775 2796
  'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => '',
2776 2797
  'Please choose the action to be processed for your target quantity:' => '',
2777 2798
  'Please configure the carry over and profit and loss accounts for year-end closing in the client configuration!' => '',
......
3084 3105
  'Reconciliation with bank'    => '',
3085 3106
  'Record Type'                 => '',
3086 3107
  'Record Vendor Invoice'       => '',
3108
  'Record filters'              => '',
3087 3109
  'Record in'                   => '',
3088 3110
  'Record number'               => '',
3089 3111
  'Record numbers and dates'    => '',
......
3123 3145
  'Remove'                      => '',
3124 3146
  'Remove Draft'                => '',
3125 3147
  'Remove article'              => '',
3148
  'Removed cleared group'       => '',
3126 3149
  'Removed sections and function blocks: #1' => '',
3127 3150
  'Removed spoolfiles!'         => '',
3128 3151
  'Removed text blocks: #1'     => '',
......
3186 3209
  'Requirement specs'           => '',
3187 3210
  'Reset'                       => '',
3188 3211
  'Reset Filter'                => '',
3212
  'Reset column filters'        => '',
3213
  'Reset selected bookings'     => '',
3189 3214
  'Result'                      => '',
3190 3215
  'Result of SQL query'         => '',
3191 3216
  'Results per page'            => '',
......
3369 3394
  'Select Shipping Address'     => '',
3370 3395
  'Select a Customer'           => '',
3371 3396
  'Select a period'             => '',
3397
  'Select all'                  => '',
3372 3398
  'Select article'              => '',
3373 3399
  'Select federal state...'     => '',
3374 3400
  'Select file to upload'       => '',
......
4827 4853
  'entries imported'            => '',
4828 4854
  'error while disassembling for trans_ids #1 : #2' => '',
4829 4855
  'error while paying invoice #1 : ' => '',
4856
  'error while unclearing'      => '',
4830 4857
  'error while unlinking payment #1 : ' => '',
4831 4858
  'every third month'           => '',
4832 4859
  'every time'                  => '',
......
5058 5085
  'warehouse_journal_list'      => '',
5059 5086
  'warehouse_report_list'       => '',
5060 5087
  'warehouse_usage_list'        => '',
5088
  'when sum 0 is reached / bookings are balanced' => '',
5061 5089
  'will be set upon posting'    => '',
5062 5090
  'will be set upon saving'     => '',
5063 5091
  'with skonto acc. to pt'      => '',
templates/webpages/clearing/form.html
1
[%- USE HTML -%]
2
[%- USE LxERP -%]
3
[%- USE T8 -%]
4
[%- USE L -%]
5
[%- USE P -%]
6

  
7
[% INCLUDE 'common/flash.html' %]
8

  
9
[% SET style="width: 300px" %]
10
[% SET style2="width: 200px" %]
11

  
12
<h1>[% 'Clear bookings' | $T8 %]: <!-- ko if: selectedChart --><span data-bind="text: selectedChart().displayable_name"></span><!-- /ko --></h1>
13

  
14
<div id="chart">
15
 <div data-bind="hidden: selectedChartId">
16
  [% 'Please choose an account for clearing:' | $T8 %]
17
 </div>
18
 <div>
19
  [% 'Chart' | $T8 %]: [% P.chart.picker('chart_id', chart_id,
20
                                          fat_set_item=1,
21
                                          style=style,
22
                                          type='clearing'
23
                                        ) %]
24
  <button type="button" data-bind="visible: selectedChartId && calink() !== undefined, click: redirect_chartlist">[% 'List Transactions' | $T8 %]</button>
25
 </div>
26
</div> <!-- chart -->
27

  
28
<div id="top" data-bind="visible: selectedChartId">
29
 <div id="filter">
30
  <div data-bind="visible: false">
31
   [% 'Precision' %]: <input data-bind="value: precision"></input>
32
  </div>
33
  <form id="clearing_filter" name="clearing_filter" data-bind="submit: BookingListViewModel.update">
34
   <fieldset>
35
    <legend>[% 'Record filters' | $T8 %] <button data-bind="toggle: show_filter"><span data-bind="text: show_filter() ? '⌃' : '⌄'"></span></button></legend>
36
    <div data-bind="visible: show_filter">
37
    <table class="grid">
38
     <tbody>
39
      <tr>
40
       <th>[% 'Invoice Date' | $T8 %]</th>
41
       <td>[% 'From'  | $T8 %] [% L.date_tag("filter.fromdate", SELF.fromdate || FORM.filter.fromdate, class="filter") %]
42
           [% 'Until' | $T8 %] [% L.date_tag('filter.todate',   SELF.todate   || FORM.filter.todate, class="filter")   %]
43
       </td>
44
      </tr>
45
      <tr>
46
       <th>[% 'Project' | $T8 %]</th>
47
       <td>[% P.project.picker('filter.project_id', SELF.project_id, description_style='both', style=style, class="filter") %]</td>
48

  
49
      </tr>
50
      [% IF SELF.all_departments.size %]
51
      <tr>
52
       <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>
53
      </tr>
54
      [% END %]
55
      <tr>
56
       <th>[% 'Load cleared bookings' | $T8 %]</th>
57
       <td>[% P.checkbox_tag("filter.load_cleared", value=1, checked=FORM.load_cleared, class="filter") %]</td>
58
      </tr>
59
     </tbody>
60
    </table>
61
    <button type="submit" data-bind="enable: selectedChartId">[% 'Update' | $T8 %]</button>
62
    <button type="button" id="reset_form_filter">[% 'Reset' | $T8 %]</button>
63
    </div>
64
   </fieldset>
65
  </form>
66
 </div> <!-- filter -->
67
</div> <!-- top -->
68

  
69
<div id="flexsettings" class="flex-container" data-bind="visible: selectedChartId">
70
 <div id="settings" class="flexing">
71
 <h2>[% 'Settings' | $T8 %]</h2>
72
  <table>
73
   <tr>
74
     <td>[% 'Hide cleared' | $T8 %]:</td>
75
     <td><input type="checkbox" data-bind="checked: hideCleared"></input>
76
   </tr>
77
   <tr>
78
     <td>[% 'Automatic clearing' | $T8 %]:</td>
79
     <td><input id="automaticClearing" type="checkbox" data-bind="checked: automaticClearing"></input>
80
         <span style="display: none" id="automatic_clearing_info">([% 'when sum 0 is reached / bookings are balanced' | $T8 %])</span>
81
     </td>
82
   </tr>
83
  </table>
84
 </div> <!-- settings flex -->
85

  
86
  <div id="autofilters" class="flexing">
87
   <h2>[% 'Automatically filter when matching' | $T8 %]:</h2>
88
   <table>
89
   <tbody>
90
   <tr>
91
     <td>[% 'Contra Amount' | $T8 %]:</td><td><input type="checkbox" data-bind="checked: automaticAmountFiltering"></input></td>
92
     <td></td>
93
     <td>[% 'Employee' | $T8 %]:</td><td><input type="checkbox" data-bind="checked: automaticEmployeeFiltering"></input></td>
94
   </tr>
95
   <tr>
96
     <td>[% 'Reference' | $T8 %]</td><td><input type="checkbox" data-bind="checked: automaticReferenceFiltering"></input></td>
97
     <td></td>
98
     <td>[% 'Project' | $T8 %]</td><td><input type="checkbox" data-bind="checked: automaticProjectFiltering"</input></td>
99
   </tr>
100
   <tr>
101
     <td>[% 'Date' | $T8%]</td>
102
     <td><input type="checkbox" data-bind="checked: automaticDateFiltering"></input></td>
103
     <td></td>
104
     <td></td>
105
   </tr>
106
   </tbody>
107
   </table>
108
  </div> <!-- autofilters flex -->
109

  
110
  <div id="fuzzyfilters" class="flexing">
111
   <h2>[% 'Fuzzy filter' | $T8 %]</h2>
112
   <table>
113
    <tr>
114
      <td>[% 'Amount' | $T8 %]</td>
115
      <td><input type="checkbox" data-bind="checked: fuzzyAmountFilter"></input>
116
          ±<input size="2" class="numeric" data-bind="value: fuzzyAmountFormatted, enable: fuzzyAmountFilter"></input>%
117
    </tr>
118
    <tr>
119
      <td>[% 'Date' | $T8 %]</td>
120
      <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