Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 0fed2b9a

Von G. Richardson vor mehr als 5 Jahren hinzugefügt

  • ID 0fed2b9ab81651006e63659c67940874cbf199d8
  • Vorgänger e450ac30
  • Nachfolger e592e0bc

GLTransaction - Dialogbuchungen per Rose erstellen

neue Methoden in GLTransaction zum Erstellen von Dialogbuchungen
  • post
  • validate
  • add_chart_booking

An einigen Stellen im Code werden Dialogbuchungen per Hand erstellt,
inkl. Steuern, das soll hiermit vereinheitlicht und vereinfacht
werden.

Acc_trans-Einträge können nun mit wenigen Parametern zu Dialogbuchungen
hinzugefügt werden, die Parameter orientieren sich dabei an den Werten,
wie sie auch an der Oberfläche eingegeben werden (Konto, Soll/Haben,
Steuer). Dabei werden einige der Werte aus der GLTransaction
automatisch übernommen.

Beim Buchen wird eine neue Transaktion gestartet, die Buchung wird
validiert und es wird ein Historieneintrag erstellt.

Unterschiede anzeigen:

SL/DB/GLTransaction.pm
5 5
use SL::DB::MetaSetup::GLTransaction;
6 6
use SL::Locale::String qw(t8);
7 7
use List::Util qw(sum);
8
use SL::DATEV;
9
use Carp;
10
use Data::Dumper;
8 11

  
9 12
# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
10 13
__PACKAGE__->meta->make_manager_class;
......
57 60

  
58 61
sub date { goto &gldate }
59 62

  
63
sub post {
64
  my ($self) = @_;
65

  
66
  my @errors = $self->validate;
67
  croak t8("Errors in GL transaction:") . "\n" . join("\n", @errors) . "\n" if scalar @errors;
68

  
69
  # make sure all the defaults are set:
70
  require SL::DB::Employee;
71
  my $employee_id = SL::DB::Manager::Employee->current->id;
72
  $self->type(undef);
73
  $self->employee_id($employee_id) unless defined $self->employee_id || defined $self->employee;
74
  $self->ob_transaction('f') unless defined $self->ob_transaction;
75
  $self->cb_transaction('f') unless defined $self->cb_transaction;
76
  $self->gldate(DateTime->today_local) unless defined $self->gldate; # should user even be allowed to set this manually?
77
  $self->transdate(DateTime->today_local) unless defined $self->transdate;
78

  
79
  $self->db->with_transaction(sub {
80
    $self->save;
81

  
82
    if ($::instance_conf->get_datev_check_on_gl_transaction) {
83
      my $datev = SL::DATEV->new(
84
        dbh      => $self->dbh,
85
        trans_id => $self->id,
86
      );
87

  
88
      $datev->generate_datev_data;
89

  
90
      if ($datev->errors) {
91
         die join "\n", t8('DATEV check returned errors:'), $datev->errors;
92
      }
93
    }
94

  
95
    require SL::DB::History;
96
    SL::DB::History->new(
97
      trans_id    => $self->id,
98
      snumbers    => 'gltransaction_' . $self->id,
99
      employee_id => $employee_id,
100
      addition    => 'POSTED',
101
      what_done   => 'gl transaction',
102
    )->save;
103

  
104
    1;
105
  }) or die t8("Error when saving: #1", $self->db->error);
106

  
107
  return $self;
108
}
109

  
110
sub add_chart_booking {
111
  my ($self, %params) = @_;
112

  
113
  require SL::DB::Chart;
114
  die "add_chart_booking needs a transdate" unless $self->transdate;
115
  die "add_chart_booking needs taxincluded" unless defined $self->taxincluded;
116
  die "chart missing"  unless $params{chart} && ref($params{chart}) eq 'SL::DB::Chart';
117
  die t8('Booking needs at least one debit and one credit booking!')
118
    unless $params{debit} or $params{credit}; # must exist and not be 0
119
  die t8('Cannot have a value in both Debit and Credit!')
120
    if defined($params{debit}) and defined($params{credit});
121

  
122
  my $chart = $params{chart};
123

  
124
  my $dec = delete $params{dec} // 2;
125

  
126
  my ($netamount,$taxamount) = (0,0);
127
  my $amount = $params{credit} // $params{debit}; # only one can exist
128

  
129
  croak t8('You cannot use a negative amount with debit/credit!') if $amount < 0;
130

  
131
  require SL::DB::Tax;
132
  my $tax = SL::DB::Manager::Tax->find_by(id => $params{tax_id})
133
    // croak "Can't find tax with id " . $params{tax_id};
134

  
135
  if ( $tax and $tax->rate != 0 ) {
136
    ($netamount, $taxamount) = Form->calculate_tax($amount, $tax->rate, $self->taxincluded, $dec);
137
  } else {
138
    $netamount = $amount;
139
  };
140

  
141
  if ( $params{debit} ) {
142
    $amount    *= -1;
143
    $netamount *= -1;
144
    $taxamount *= -1;
145
  };
146

  
147
  next unless $netamount; # skip entries with netamount 0
148

  
149
  # initialise transactions if it doesn't exist yet
150
  $self->transactions([]) unless $self->transactions;
151

  
152
  require SL::DB::AccTransaction;
153
  $self->add_transactions( SL::DB::AccTransaction->new(
154
    chart_id       => $chart->id,
155
    chart_link     => $chart->link,
156
    amount         => $netamount,
157
    taxkey         => $tax->taxkey,
158
    tax_id         => $tax->id,
159
    transdate      => $self->transdate,
160
    source         => $params{source} // '',
161
    memo           => $params{memo}   // '',
162
    ob_transaction => $self->ob_transaction,
163
    cb_transaction => $self->cb_transaction,
164
    project_id     => $params{project_id},
165
  ));
166

  
167
  # only add tax entry if amount is >= 0.01, defaults to 2 decimals
168
  if ( $::form->round_amount(abs($taxamount), $dec) > 0 ) {
169
    my $tax_chart = $tax->chart;
170
    if ( $tax->chart ) {
171
      $self->add_transactions(SL::DB::AccTransaction->new(
172
                                chart_id       => $tax_chart->id,
173
                                chart_link     => $tax_chart->link,
174
                                amount         => $taxamount,
175
                                taxkey         => $tax->taxkey,
176
                                tax_id         => $tax->id,
177
                                transdate      => $self->transdate,
178
                                ob_transaction => $self->ob_transaction,
179
                                cb_transaction => $self->cb_transaction,
180
                                source         => $params{source} // '',
181
                                memo           => $params{memo}   // '',
182
                                project_id     => $params{project_id},
183
                              ));
184
    };
185
  };
186
  return $self;
187
};
188

  
189
sub validate {
190
  my ($self) = @_;
191

  
192
  my @errors;
193

  
194
  if ( $self->transactions && scalar @{ $self->transactions } ) {
195
    my $debit_count  = map { $_->amount } grep { $_->amount > 0 } @{ $self->transactions };
196
    my $credit_count = map { $_->amount } grep { $_->amount < 0 } @{ $self->transactions };
197

  
198
    if ( $debit_count > 1 && $credit_count > 1 ) {
199
      push @errors, t8('Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. ' .
200
                       'Due to known problems involving accounting software kivitendo does not allow these.');
201
    } elsif ( $credit_count == 0 && $debit_count == 0 ) {
202
      push @errors, t8('Booking needs at least one debit and one credit booking!');
203
    } else {
204
      # transactions formally ok, now check for out of balance:
205
      my $sum = sum map { $_->amount } @{ $self->transactions };
206
      # compare rounded amount to 0, to get around floating point problems, e.g.
207
      # $sum = -2.77555756156289e-17
208
      push @errors, t8('Out of balance transaction!') unless $::form->round_amount($sum,5) == 0;
209
    };
210
  } else {
211
    push @errors, t8('Empty transaction!');
212
  };
213

  
214
  # fields enforced by interface
215
  push @errors, t8('Reference missing!')   unless $self->reference;
216
  push @errors, t8('Description missing!') unless $self->description;
217

  
218
  # date checks
219
  push @errors, t8('Transaction Date missing!') unless $self->transdate && ref($self->transdate) eq 'DateTime';
220

  
221
  if ( $self->transdate ) {
222
    if ( $::form->date_closed( $self->transdate, \%::myconfig) ) {
223
      if ( !$self->id ) {
224
        push @errors, t8('Cannot post transaction for a closed period!')
225
      } else {
226
        push @errors, t8('Cannot change transaction in a closed period!')
227
      };
228
    };
229

  
230
    push @errors, t8('Cannot post transaction above the maximum future booking date!')
231
      if $::form->date_max_future($self->transdate, \%::myconfig);
232
  }
233

  
234
  return @errors;
235
}
236

  
60 237
1;
238

  
239
__END__
240

  
241
=pod
242

  
243
=encoding UTF-8
244

  
245
=head1 NAME
246

  
247
SL::DB::GLTransaction: Rose model for GL transactions (table "gl")
248

  
249
=head1 FUNCTIONS
250

  
251
=over 4
252

  
253
=item C<post>
254

  
255
Takes an unsaved but initialised GLTransaction object and saves it, but first
256
validates the object, sets certain defaults (e.g. employee), and then also runs
257
various checks, writes history, runs DATEV check, ...
258

  
259
Returns C<$self> on success and dies otherwise. The whole process is run inside
260
a transaction. If it fails then nothing is saved to or changed in the database.
261
A new transaction is only started if none are active.
262

  
263
Example of posting a GL transaction from scratch:
264

  
265
  my $tax_0 = SL::DB::Manager::Tax->find_by(taxkey => 0, rate => 0.00);
266
  my $gl_transaction = SL::DB::GLTransaction->new(
267
    taxincluded => 1,
268
    description => 'bar',
269
    reference   => 'bla',
270
    transdate   => DateTime->today_local,
271
  )->add_chart_booking(
272
    chart  => SL::DB::Manager::Chart->find_by( description => 'Kasse' ),
273
    credit => 100,
274
    tax_id => $tax_0->id,
275
  )->add_chart_booking(
276
    chart  => SL::DB::Manager::Chart->find_by( description => 'Bank' ),
277
    debit  => 100,
278
    tax_id => $tax_0->id,
279
  )->post;
280

  
281
=item C<add_chart_booking %params>
282

  
283
Adds an acc_trans entry to an existing GL transaction, depending on the tax it
284
will also automatically create the tax entry. The GL transaction already needs
285
to have certain values, e.g. transdate, taxincluded, ...
286

  
287
Mandatory params are
288

  
289
=over 2
290

  
291
=item * chart as an RDBO object
292

  
293
=item * tax_id
294

  
295
=item * either debit OR credit (positive values)
296

  
297
=back
298

  
299
Optional params:
300

  
301
=over 2
302

  
303
=item * dec - number of decimals to round to, defaults to 2
304

  
305
=item * source
306

  
307
=item * memo
308

  
309
=item * project_id
310

  
311
=back
312

  
313
All other values are taken directly from the GL transaction.
314

  
315
For an example, see C<post>.
316

  
317
After adding an acc_trans entry the GL transaction shouldn't be modified (e.g.
318
values affecting the acc_trans entries, such as transdate or taxincluded
319
shouldn't be changed). There is currently no method for recalculating the
320
acc_trans entries after they were added.
321

  
322
Return C<$self>, so it allows chaining.
323

  
324
=item C<validate>
325

  
326
Runs various checks to see if the GL transaction is ready to be C<post>ed.
327

  
328
Will return an array of error strings if any necessary conditions aren't met.
329

  
330
=back
331

  
332
=head1 TODO
333

  
334
Nothing here yet.
335

  
336
=head1 AUTHOR
337

  
338
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>,
339
G. Richardson E<lt>grichardson@kivitec.deE<gt>
340

  
341
=cut
locale/de/all
476 476
  'Booking group (database ID)' => 'Buchungsgruppe (database ID)',
477 477
  'Booking group (name)'        => 'Buchungsgruppe (name)',
478 478
  'Booking groups'              => 'Buchungsgruppen',
479
  'Booking needs at least one debit and one credit booking!' => 'Die Buchung benötigt mindestens eine Buchung im Soll eine im Haben!',
479 480
  'Bookinggroup/Tax'            => 'Buchungsgruppe/Steuer',
480 481
  'Books are open'              => 'Die Bücher sind geöffnet.',
481 482
  'Books closed up to'          => 'Bücher abgeschlossen bis zum',
......
525 526
  'Cancel Accounts Payables Transaction' => 'Kreditorenbuchung stornieren',
526 527
  'Cancel Accounts Receivables Transaction' => 'Debitorenbuchung stornieren',
527 528
  '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.',
529
  'Cannot change transaction in a closed period!' => 'In einem bereits abgeschlossenen Zeitraum kann keine Buchung verändert werden!',
528 530
  'Cannot check correct WebDAV folder' => 'Kann nicht den richtigen WebDAV Pfad überprüfen',
529 531
  'Cannot delete account!'      => 'Konto kann nicht gelöscht werden!',
530 532
  'Cannot delete customer!'     => 'Kunde kann nicht gelöscht werden!',
......
1338 1340
  'Error: unknown local bank account id' => 'Fehler: unbekannte Bankkonto-ID',
1339 1341
  'Errors during conversion:'   => 'Umwandlungsfehler:',
1340 1342
  'Errors during printing:'     => 'Druckfehler:',
1343
  'Errors in GL transaction:'   => 'Fehler in Dialogbuchung:',
1341 1344
  'Ertrag'                      => 'Ertrag',
1342 1345
  'Ertrag prozentual'           => 'Ertrag prozentual',
1343 1346
  'Escape character'            => 'Escape-Zeichen',
......
3935 3938
  'You cannot create an invoice for delivery orders from different vendors.' => 'Sie können keine Rechnung aus Lieferscheinen von verschiedenen Lieferanten erstellen.',
3936 3939
  'You cannot modify individual assigments from additional articles to line items.' => 'Eine individuelle Zuordnung der zusätzlichen Artikel zu Positionen kann nicht vorgenommen werden.',
3937 3940
  'You cannot paste function blocks or sub function blocks if there is no section.' => 'Sie können keine Funktionsblöcke oder Unterfunktionsblöcke einfügen, wenn es noch keinen Abschnitt gibt.',
3941
  'You cannot use a negative amount with debit/credit!' => 'Sie dürfen für Soll/Haben keine negativen Werte benutzen!',
3938 3942
  'You do not have access to any custom data export.' => 'Sie haben auf keine benutzerdefinierten Datenexporte Zugriff.',
3939 3943
  'You do not have permission to access this entry.' => 'Sie verfügen nicht über die Berechtigung, auf diesen Eintrag zuzugreifen.',
3940 3944
  'You do not have the permissions to access this function.' => 'Sie verfügen nicht über die notwendigen Rechte, um auf diese Funktion zuzugreifen.',
locale/en/all
476 476
  'Booking group (database ID)' => '',
477 477
  'Booking group (name)'        => '',
478 478
  'Booking groups'              => '',
479
  'Booking needs at least one debit and one credit booking!' => '',
479 480
  'Bookinggroup/Tax'            => '',
480 481
  'Books are open'              => '',
481 482
  'Books closed up to'          => '',
......
525 526
  'Cancel Accounts Payables Transaction' => '',
526 527
  'Cancel Accounts Receivables Transaction' => '',
527 528
  'Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount' => '',
529
  'Cannot change transaction in a closed period!' => '',
528 530
  'Cannot check correct WebDAV folder' => '',
529 531
  'Cannot delete account!'      => '',
530 532
  'Cannot delete customer!'     => '',
......
1338 1340
  'Error: unknown local bank account id' => '',
1339 1341
  'Errors during conversion:'   => '',
1340 1342
  'Errors during printing:'     => '',
1343
  'Errors in GL transaction:'   => '',
1341 1344
  'Ertrag'                      => '',
1342 1345
  'Ertrag prozentual'           => '',
1343 1346
  'Escape character'            => '',
......
3934 3937
  'You cannot create an invoice for delivery orders from different vendors.' => '',
3935 3938
  'You cannot modify individual assigments from additional articles to line items.' => '',
3936 3939
  'You cannot paste function blocks or sub function blocks if there is no section.' => '',
3940
  'You cannot use a negative amount with debit/credit!' => '',
3937 3941
  'You do not have access to any custom data export.' => '',
3938 3942
  'You do not have permission to access this entry.' => '',
3939 3943
  'You do not have the permissions to access this function.' => '',
t/gl/gl.t
1
use strict;
2
use Test::More tests => 4;
3

  
4
use lib 't';
5
use Support::TestSetup;
6
use Carp;
7
use Test::Exception;
8
use SL::DB::Chart;
9
use SL::DB::TaxKey;
10
use SL::DB::GLTransaction;
11
use Data::Dumper;
12
use SL::DBUtils qw(selectall_hashref_query);
13

  
14
Support::TestSetup::login();
15

  
16
clear_up();
17

  
18
my $cash           = SL::DB::Manager::Chart->find_by( description => 'Kasse'          );
19
my $bank           = SL::DB::Manager::Chart->find_by( description => 'Bank'           );
20
my $betriebsbedarf = SL::DB::Manager::Chart->find_by( description => 'Betriebsbedarf' );
21

  
22
my $tax_9 = SL::DB::Manager::Tax->find_by(taxkey => 9, rate => 0.19);
23
my $tax_8 = SL::DB::Manager::Tax->find_by(taxkey => 8, rate => 0.07);
24
my $tax_0 = SL::DB::Manager::Tax->find_by(taxkey => 0, rate => 0.00);
25

  
26
my $dbh = SL::DB->client->dbh;
27

  
28
# example with chaining of add_chart_booking
29
my $gl_transaction = SL::DB::GLTransaction->new(
30
  taxincluded => 1,
31
  reference   => 'bank/cash',
32
  description => 'bank/cash',
33
  transdate   => DateTime->today_local,
34
)->add_chart_booking(
35
  chart  => $cash,
36
  credit => 100,
37
  tax_id => $tax_0->id,
38
)->add_chart_booking(
39
  chart  => $bank,
40
  debit  => 100,
41
  tax_id => $tax_0->id,
42
)->post;
43

  
44
# example where bookings is prepared separately as an arrayref
45
my $gl_transaction_2 = SL::DB::GLTransaction->new(
46
  reference   => 'betriebsbedarf several rows',
47
  description => 'betriebsbedarf',
48
  taxincluded => 1,
49
  transdate   => DateTime->today_local,
50
);
51

  
52
my $bookings = [
53
                {
54
                  chart  => $betriebsbedarf,
55
                  memo   => 'foo 1',
56
                  source => 'foo 1',
57
                  debit  => 119,
58
                  tax_id => $tax_9->id,
59
                },
60
                {
61
                  chart  => $betriebsbedarf,
62
                  memo   => 'foo 2',
63
                  source => 'foo 2',
64
                  debit  => 119,
65
                  tax_id => $tax_9->id,
66
                },
67
                {
68
                  chart  => $cash,
69
                  credit => 238,
70
                  memo   => 'foo 1+2',
71
                  source => 'foo 1+2',
72
                  tax_id => $tax_0->id,
73
                },
74
               ];
75
$gl_transaction_2->add_chart_booking(%{$_}) foreach @{ $bookings };
76
$gl_transaction_2->post;
77

  
78

  
79
# example where add_chart_booking is called via a foreach
80
my $gl_transaction_3 = SL::DB::GLTransaction->new(
81
  reference   => 'betriebsbedarf tax included',
82
  description => 'bar',
83
  taxincluded => 1,
84
  transdate   => DateTime->today_local,
85
);
86
$gl_transaction_3->add_chart_booking(%{$_}) foreach (
87
    {
88
      chart  => $betriebsbedarf,
89
      debit  => 119,
90
      tax_id => $tax_9->id,
91
    },
92
    {
93
      chart  => $betriebsbedarf,
94
      debit  => 107,
95
      tax_id => $tax_8->id,
96
    },
97
    {
98
      chart  => $betriebsbedarf,
99
      debit  => 100,
100
      tax_id => $tax_0->id,
101
    },
102
    {
103
      chart  => $cash,
104
      credit => 326,
105
      tax_id => $tax_0->id,
106
    },
107
);
108
$gl_transaction_3->post;
109

  
110
my $gl_transaction_4 = SL::DB::GLTransaction->new(
111
  reference   => 'betriebsbedarf tax not included',
112
  description => 'bar',
113
  taxincluded => 0,
114
  transdate   => DateTime->today_local,
115
);
116
$gl_transaction_4->add_chart_booking(%{$_}) foreach (
117
    {
118
      chart  => $betriebsbedarf,
119
      debit  => 100,
120
      tax_id => $tax_9->id,
121
    },
122
    {
123
      chart  => $betriebsbedarf,
124
      debit  => 100,
125
      tax_id => $tax_8->id,
126
    },
127
    {
128
      chart  => $betriebsbedarf,
129
      debit  => 100,
130
      tax_id => $tax_0->id,
131
    },
132
    {
133
      chart  => $cash,
134
      credit => 326,
135
      tax_id => $tax_0->id,
136
    },
137
);
138
$gl_transaction_4->post;
139

  
140
is(SL::DB::Manager::GLTransaction->get_all_count(), 4, "gl transactions created ok");
141

  
142
is_deeply(&get_account_balances,
143
          [
144
            {
145
              'accno' => '1000',
146
              'sum' => '990.00000'
147
            },
148
            {
149
              'accno' => '1200',
150
              'sum' => '-100.00000'
151
            },
152
            {
153
              'accno' => '1571',
154
              'sum' => '-14.00000'
155
            },
156
            {
157
              'accno' => '1576',
158
              'sum' => '-76.00000'
159
            },
160
            {
161
              'accno' => '4980',
162
              'sum' => '-800.00000'
163
            }
164
          ],
165
          "chart balances ok"
166
         );
167

  
168

  
169
note('testing subcent');
170

  
171
my $gl_transaction_5_taxinc = SL::DB::GLTransaction->new(
172
  taxincluded => 1,
173
  reference   => 'subcent tax included',
174
  description => 'subcent tax included',
175
  transdate   => DateTime->today_local,
176
)->add_chart_booking(
177
  chart  => $betriebsbedarf,
178
  debit  => 0.02,
179
  tax_id => $tax_9->id,
180
)->add_chart_booking(
181
  chart  => $cash,
182
  credit => 0.02,
183
  tax_id => $tax_0->id,
184
)->post;
185

  
186
my $gl_transaction_5_taxnoinc = SL::DB::GLTransaction->new(
187
  taxincluded => 0,
188
  reference   => 'subcent tax not included',
189
  description => 'subcent tax not included',
190
  transdate   => DateTime->today_local,
191
)->add_chart_booking(
192
  chart  => $betriebsbedarf,
193
  debit  => 0.02,
194
  tax_id => $tax_9->id,
195
)->add_chart_booking(
196
  chart  => $cash,
197
  credit => 0.02,
198
  tax_id => $tax_0->id,
199
)->post;
200

  
201
my $gl_transaction_6_taxinc = SL::DB::GLTransaction->new(
202
  taxincluded => 1,
203
  reference   => 'cent tax included',
204
  description => 'cent tax included',
205
  transdate   => DateTime->today_local,
206
)->add_chart_booking(
207
  chart  => $betriebsbedarf,
208
  debit  => 0.05,
209
  tax_id => $tax_9->id,
210
)->add_chart_booking(
211
  chart  => $cash,
212
  credit => 0.05,
213
  tax_id => $tax_0->id,
214
)->post;
215

  
216
my $gl_transaction_6_taxnoinc = SL::DB::GLTransaction->new(
217
  taxincluded => 0,
218
  reference   => 'cent tax included',
219
  description => 'cent tax included',
220
  transdate   => DateTime->today_local,
221
)->add_chart_booking(
222
  chart  => $betriebsbedarf,
223
  debit  => 0.04,
224
  tax_id => $tax_9->id,
225
)->add_chart_booking(
226
  chart  => $cash,
227
  credit => 0.05,
228
  tax_id => $tax_0->id,
229
)->post;
230

  
231
is(SL::DB::Manager::GLTransaction->get_all_count(), 8, "gl transactions created ok");
232

  
233

  
234
is_deeply(&get_account_balances,
235
          [
236
            {
237
              'accno' => '1000',
238
              'sum' => '990.14000'
239
            },
240
            {
241
              'accno' => '1200',
242
              'sum' => '-100.00000'
243
            },
244
            {
245
              'accno' => '1571',
246
              'sum' => '-14.00000'
247
            },
248
            {
249
              'accno' => '1576',
250
              'sum' => '-76.02000'
251
            },
252
            {
253
              'accno' => '4980',
254
              'sum' => '-800.12000'
255
            }
256
          ],
257
          "chart balances ok"
258
         );
259

  
260
done_testing;
261
clear_up();
262

  
263
1;
264

  
265
sub clear_up {
266
  "SL::DB::Manager::${_}"->delete_all(all => 1) for qw(
267
                                                       AccTransaction
268
                                                       GLTransaction
269
                                                      );
270
};
271

  
272
sub get_account_balances {
273
  my $query = <<SQL;
274
  select c.accno,
275
         sum(a.amount)
276
    from acc_trans a
277
         left join chart c on (c.id = a.chart_id)
278
group by c.accno
279
order by c.accno;
280
SQL
281

  
282
  my $result = selectall_hashref_query($::form, $dbh, $query);
283
  return $result;
284
};

Auch abrufbar als: Unified diff