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

Auch abrufbar als: Unified diff