Revision 0fed2b9a
Von G. Richardson vor mehr als 5 Jahren hinzugefügt
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
GLTransaction - Dialogbuchungen per Rose erstellen
neue Methoden in GLTransaction zum Erstellen von DialogbuchungenAn 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.