Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 23b40897

Von Jan Büren vor fast 3 Jahren hinzugefügt

  • ID 23b40897da260096305ac051291d95623dcf075a
  • Vorgänger 672464a1
  • Nachfolger d53cd961

Payment-Helper pay_invoice case 'difference_as_skonto' entfernt

'difference_as_skonto' lässt sich über 'free_skonto' abbilden.
Ein Fall weniger der die Methode etwas wartungsfreundlicher macht.
POD und Testfall angepasst.

Unterschiede anzeigen:

SL/DB/Helper/Payment.pm
41 41
  validate_payment_type($params{payment_type});
42 42

  
43 43
  # check for required parameters and optional params depending on payment_type
44
  Common::check_params(\%params, qw(chart_id transdate));
44
  Common::check_params(\%params, qw(chart_id transdate amount));
45 45
  Common::check_params(\%params, qw(bt_id)) unless $params{payment_type} eq 'without_skonto';
46

  
47
  # three valid cases, test logical params in depth, before proceeding ...
46 48
  if ( $params{'payment_type'} eq 'without_skonto' && abs($params{'amount'}) < 0) {
47 49
    croak "invalid amount for payment_type 'without_skonto': $params{'amount'}\n";
48
  }
49
  if ($params{'payment_type'} eq 'free_skonto') {
50
  } elsif ($params{'payment_type'} eq 'free_skonto') {
50 51
    # we dont like too much automagic for this payment type.
51 52
    # we force caller input for amount and skonto amount
52
    Common::check_params(\%params, qw(amount skonto_amount));
53
    Common::check_params(\%params, qw(skonto_amount));
53 54
    # secondly we dont want to handle credit notes and purchase credit notes
54 55
    croak("Cannot use 'free skonto' for credit or debit notes") if ($params{amount} < 0 || $params{skonto_amount} <= 0);
55 56
    # both amount have to be rounded
......
59 60
    if ($params{skonto_amount} > _round($self->open_amount)) {
60 61
      croak("Skonto amount:" . $params{skonto_amount} . " higher than the payment or open invoice amount:" . $self->open_amount);
61 62
    }
63
  } elsif ( $params{'payment_type'} eq 'with_skonto_pt' ) {
64
    # options with_skonto_pt doesn't require the parameter
65
    # amount, but if amount is passed, make sure it matches the expected value
66
    # note: the parameter isn't used at all - amount_less_skonto will always be used
67
    # partial skonto payments are therefore impossible to book
68
    croak "amount $params{amount} doesn't match amount less skonto: " . $self->amount_less_skonto . "\n" if $params{amount} && abs($self->amount_less_skonto - $params{amount} ) > 0.0000001;
69
    croak "payment type with_skonto_pt can't be used if payments have already been made" if $self->paid != 0;
62 70
  }
63 71

  
72

  
64 73
  my $transdate_obj;
65 74
  if (ref($params{transdate}) eq 'DateTime') {
66 75
    $transdate_obj = $params{transdate};
......
106 115
    $exchangerate = 1;
107 116
  };
108 117

  
109
  # options with_skonto_pt and difference_as_skonto don't require the parameter
110
  # amount, but if amount is passed, make sure it matches the expected value
111
  if ( $params{'payment_type'} eq 'difference_as_skonto' ) {
112
    croak "amount $params{amount} doesn't match open amount " . $self->open_amount . ", diff = " . ($params{amount}-$self->open_amount) if $params{amount} && abs($self->open_amount - $params{amount} ) > 0.0000001;
113
  } elsif ( $params{'payment_type'} eq 'with_skonto_pt' ) {
114
    croak "amount $params{amount} doesn't match amount less skonto: " . $self->amount_less_skonto . "\n" if $params{amount} && abs($self->amount_less_skonto - $params{amount} ) > 0.0000001;
115
    croak "payment type with_skonto_pt can't be used if payments have already been made" if $self->paid != 0;
116
  };
117

  
118 118
  # absolute skonto amount for invoice, use as reference sum to see if the
119 119
  # calculated skontos add up
120 120
  # only needed for payment_term "with_skonto_pt"
......
140 140
    my $new_acc_trans;
141 141

  
142 142
    # all three payment type create 1 AR/AP booking (the paid part)
143
    # difference_as_skonto creates n skonto bookings (1 for each buchungsgruppe type)
144
    # with_skonto_pt creates 1 bank booking and n skonto bookings (1 for each buchungsgruppe type)
143
    # with_skonto_pt creates 1 bank booking and n skonto bookings (1 for each tax type)
144
    # and one tax correction as a gl booking
145 145
    # without_skonto creates 1 bank booking
146 146

  
147
    # as long as there is no automatic tax, payments are always booked with
148
    # taxkey 0
149

  
150
    unless ( $rounded_params_amount == 0 || $params{payment_type} eq 'difference_as_skonto' ) {
147
    unless ( $rounded_params_amount == 0 ) {
151 148
      # cases with_skonto_pt, free_skonto and without_skonto
152 149

  
153 150
      # for case with_skonto_pt we need to know the corrected amount at this
154
      # stage if we are going to use $params{amount}
151
      # stage because we don't use $params{amount} ?!
155 152

  
156 153
      my $pay_amount = $rounded_params_amount;
157 154
      $pay_amount = $self->amount_less_skonto if $params{payment_type} eq 'with_skonto_pt';
......
216 213
        }
217 214
      }
218 215
    }
219
    # better everything except without_skonto
220
    if ($params{payment_type} eq 'difference_as_skonto' or $params{payment_type} eq 'with_skonto_pt'
221
        or $params{payment_type} eq 'free_skonto' ) {
216
    # skonto cases
217
    if ($params{payment_type} eq 'with_skonto_pt' or $params{payment_type} eq 'free_skonto' ) {
222 218

  
223 219
      my $total_skonto_amount;
224 220
      if ( $params{payment_type} eq 'with_skonto_pt' ) {
225 221
        $total_skonto_amount = $self->skonto_amount;
226
      } elsif ( $params{payment_type} eq 'difference_as_skonto' ) {
227
        # only used for tests. no real code calls this payment_type!
228
        $total_skonto_amount = $self->open_amount;
229 222
      } elsif ( $params{payment_type} eq 'free_skonto') {
230 223
        $total_skonto_amount = $params{skonto_amount};
231 224
      }
232 225
      my @skonto_bookings = $self->_skonto_charts_and_tax_correction(amount => $total_skonto_amount, bt_id => $params{bt_id},
233 226
                                                                     transdate_obj => $transdate_obj, memo => $params{memo},
234 227
                                                                     source => $params{source});
235
      # error checking:
236
      if ( $params{payment_type} eq 'difference_as_skonto' ) {
237
        my $calculated_skonto_sum  = sum map { $_->{skonto_amount} } @skonto_bookings;
238
        croak "calculated skonto for difference_as_skonto = $calculated_skonto_sum doesn't add up open amount: " . $self->open_amount unless _round($calculated_skonto_sum) == _round($self->open_amount);
239
      };
240

  
241 228
      my $reference_amount = $total_skonto_amount;
242 229

  
243 230
      # create an acc_trans entry for each result of $self->skonto_charts
244
      # TODO create internal sub _skonto_bookings
245 231
      foreach my $skonto_booking ( @skonto_bookings ) {
246 232
        next unless $skonto_booking->{'chart_id'};
247 233
        next unless $skonto_booking->{'skonto_amount'} != 0;
......
263 249
        $paid_amount      += -1 * $amount * $exchangerate;
264 250
        $skonto_amount_check -= $skonto_booking->{'skonto_amount'};
265 251
      }
266
      if ( $params{payment_type} eq 'difference_as_skonto' ) {
267
          die "difference_as_skonto calculated incorrectly, sum of calculated payments doesn't add up to open amount $total_open_amount, reference_amount = $reference_amount\n" unless _round($reference_amount) == 0;
268
      }
269 252
    }
270

  
271 253
    my $arap_amount = 0;
272 254

  
273
    if ( $params{payment_type} eq 'difference_as_skonto' ) {
274
      $arap_amount = $total_open_amount;
275
    } elsif ( $params{payment_type} eq 'without_skonto' ) {
255
    if ( $params{payment_type} eq 'without_skonto' ) {
276 256
      $arap_amount = $rounded_params_amount;
277 257
    } elsif ( $params{payment_type} eq 'with_skonto_pt' ) {
278 258
      # this should be amount + sum(amount+skonto), but while we only allow
......
797 777
    $self->{invoice_amount_suggestion} = $open_amount;
798 778
    # difference_as_skonto doesn't make any sense for SEPA transfer, as this doesn't cause any actual payment
799 779
    if ( $self->valid_skonto_amount($self->open_amount) && not $params{sepa} ) {
780
      # probably also dead code
781
      die "This case is as dead as the dead cat. Go to start, don't pick 2,000 \$";
800 782
      push(@{$self->{payment_select_options}} , { payment_type => 'difference_as_skonto',  display => t8('difference as skonto') , selected => 0 });
801 783
    };
802 784
  };
......
807 789
#
808 790
# $main::locale->text('without_skonto')
809 791
# $main::locale->text('with_skonto_pt')
810
# $main::locale->text('difference_as_skonto')
811 792
#
812 793

  
813 794
sub validate_payment_type {
814 795
  my $payment_type = shift;
815 796

  
816
  my %allowed_payment_types = map { $_ => 1 } qw(without_skonto with_skonto_pt difference_as_skonto free_skonto);
797
  my %allowed_payment_types = map { $_ => 1 } qw(without_skonto with_skonto_pt free_skonto);
817 798
  croak "illegal payment type: $payment_type, must be one of: " . join(' ', keys %allowed_payment_types) unless $allowed_payment_types{ $payment_type };
818 799

  
819 800
  return 1;
......
860 841
a configured bank account.
861 842

  
862 843
This function deals with all the acc_trans entries and also updates paid and datepaid.
863
The params C<transdate> and C<chart_id> are mandantory.
864
If the default payment ('without_skonto') is used the param amount is also
865
mandantory.
866
If the payment type ('free_skonto') is used the number params skonto_amount and amount
867
are as well mandantory and need to be positive. Furthermore the skonto amount has
868
to be lower than the payment or open invoice amount.
844
The params C<transdate>, C<amount> and C<chart_id> are mandantory.
845

  
846
For all valid skonto types ('free_skonto' or 'with_skonto_pt') the source of
847
the bank_transaction is needed, therefore pay_invoice expects the param
848
C<bt_id> with a valid bank_transactions.id.
849

  
850
If the payment type ('free_skonto') is used the number param skonto_amount is
851
as well mandantory and needs to be positive. Furthermore the skonto amount has
852
to be lower or equal than the open invoice amount.
853
Payments with only skonto and zero bank transaction amount are possible.
869 854

  
870 855
Transdate can either be a date object or a date string.
871 856
Chart_id is the id of the payment booking chart.
872
Amount is either a positive or negative number, but never 0.
857
Amount is either a positive or negative number, and for the case 'free_skonto' might be zero.
873 858

  
874 859
CAVEAT! The helper tries to get the sign right and all calls from BankTransaction are
875 860
positive (abs($value)) values.
......
908 893
                  );
909 894

  
910 895
Allowed payment types are:
911
  without_skonto with_skonto_pt difference_as_skonto
896
  without_skonto with_skonto_pt
912 897

  
913 898
The option C<payment_type> allows for a basic skonto mechanism.
914 899

  
......
922 907
tax key. If an amount is passed it is ignored and the actual configured skonto
923 908
amount is used.
924 909

  
925
C<difference_as_skonto> can only be used after partial payments have been made,
926
the whole specified amount is booked according to the skonto charts configured
927
in the tax settings for each tax key.
928

  
929
So passing amount doesn't have any effect for the cases C<with_skonto_pt> and
930
C<difference_as_skonto>, as all necessary values are taken from the stored
931
invoice.
910
So passing amount doesn't have any effect for the case C<with_skonto_pt>.
932 911

  
933 912
The skonto modes automatically calculate the relative amounts for a mix of
934 913
taxes, e.g. items with 7% and 19% in one invoice. There is a helper method
935
skonto_charts, which calculates the relative percentages according to the
936
amounts in acc_trans (which are grouped by tax).
914
_skonto_charts_and_tax_correction, which calculates the relative percentages
915
according to the amounts in acc_trans grouped by different tax rates.
916

  
917
The helper method also generates the tax correction for the skonto booking
918
and links this to the original bank transaction and the selected record.
937 919

  
938 920
There is currently no way of excluding certain items in an invoice from having
939 921
skonto applied to them.  If this feature was added to parts the calculation
940 922
method of relative skonto would have to be completely rewritten using the
941 923
invoice items rather than acc_trans.
942 924

  
943
The skonto modes also still don't automatically correct the tax, this still has
944
to be done manually. Therefore all payments generated by pay_invoice have
945
taxkey 0.
946

  
947
There is currently no way to directly pay an invoice via this method if the
948
effective skonto differs from the skonto according to the payment terms
949
configured for the invoice/vendor.
950

  
951
In this case one has to pay in two steps: first the actual paid amount via
952
"without skonto", and then the remainder via "difference_as_skonto". The user
953
has to there actively decide whether to accept the differing skonto.
954

  
955 925
Because of the way skonto_charts works the calculation doesn't work if there
956 926
are negative values in acc_trans. E.g. one invoice with a positive value for
957 927
19% tax and a negative value for the acc_trans line with 7%
......
964 934
invoice is assumed to be the payment currency.
965 935

  
966 936
If successful the return value will be 1 in scalar context or in list context
967
the two ids (acc_trans_id) of the newly created bookings.
937
the two or more (gl transaction for skonto tax correction) ids (acc_trans_id)
938
of the newly created bookings.
939

  
968 940

  
969 941
=item C<reference_account>
970 942

  
......
1020 992
   # ... do something
1021 993
 }
1022 994

  
1023
=item C<skonto_charts [$amount]>
995
=item C<_skonto_charts_and_tax_correction [amount => $amount, bt_id => $bank_transaction.id, transdate_ojb => DateTime]>
996

  
997
Needs a valid bank_transaction id and the transdate of the bank_transaction as
998
a DateTime object.
999
If no amout is passed, the currently open invoice amount will be used.
1024 1000

  
1025 1001
Returns a list of chart_ids and some calculated numbers that can be used for
1026 1002
paying the invoice with skonto. This function will automatically calculate the
......
1029 1005

  
1030 1006
Example usage:
1031 1007
  my $invoice = SL::DB::Manager::Invoice->find_by(invnumber => '211');
1032
  my @skonto_charts = $invoice->skonto_charts;
1008
  my @skonto_charts = $invoice->_skonto_charts_and_tax_correction(bt_id         => $bt_id,
1009
                                                                  transdate_obj => $transdate_obj);
1033 1010

  
1034 1011
or with the total skonto amount as an argument:
1035
  my @skonto_charts = $invoice->skonto_charts($invoice->open_amount);
1012
  my @skonto_charts = $invoice->_skonto_charts_and_tax_correction(amount => $invoice->open_amount,
1013
                                                                  bt_id  => $bt_id,
1014
                                                                  transdate_obj => $transdate_obj);
1036 1015

  
1037 1016
The following values are generated for each chart:
1038 1017

  
......
1046 1025

  
1047 1026
The total amount to be paid to the account
1048 1027

  
1049
=item C<skonto_percent>
1050

  
1051
The relative percentage of that skonto chart. This can be useful if the actual
1052
ekonto that is paid deviates from the granted skonto, e.g. customer effectively
1053
pays 2.6% skonto instead of 2%, and we accept this. Then we can still calculate
1054
the relative skonto amounts for different taxes based on the absolute
1055
percentages. Used for case C<difference_as_skonto>.
1056

  
1057
=item C<skonto_percent_abs>
1058

  
1059
The absolute percentage of that skonto chart in relation to the total amount.
1060
Used to calculate skonto_amount for case C<with_skonto_pt>.
1061

  
1062 1028
=back
1063 1029

  
1064 1030
If the invoice contains several types of taxes then skonto_charts can be used
1065 1031
to calculate the relative amounts.
1066 1032

  
1067
Example in console of an invoice with 100 Euro at 7% and 100 Euro at 19% with
1068
tax not included:
1069

  
1070
  my $invoice = invoice(invnumber => '144');
1071
  $invoice->amount
1072
  226.00000
1073
  $invoice->payment_terms->percent_skonto
1074
  0.02
1075
  $invoice->skonto_charts
1076
  pp $invoice->skonto_charts
1077
  #             $VAR1 = {
1078
  #               'chart_id'       => 128,
1079
  #               'skonto_amount'  => '2.14',
1080
  #               'skonto_percent' => '47.3451327433627'
1081
  #             };
1082
  #             $VAR2 = {
1083
  #               'chart_id'       => 130,
1084
  #               'skonto_amount'  => '2.38',
1085
  #               'skonto_percent' => '52.654867256637'
1086
  #             };
1087

  
1088
C<skonto_charts> always returns positive values (abs) for C<skonto_amount> and
1089
C<skonto_percent>.
1090

  
1091
C<skonto_charts> generates one entry for each acc_trans entry. ar and ap
1092
bookings only have one acc_trans entry for each taxkey (e.g. 7% and 19%).  This
1093
is because all the items are grouped according to the Buchungsgruppen mechanism
1094
and the totals are written to acc_trans.  For is and ir it is possible to have
1095
several acc_trans entries with the same tax. In this case skonto_charts
1096
generates a skonto booking for each acc_trans income/expense entry.
1097

  
1098
In the future this function may also be used to calculate the corrections for
1099
the income tax.
1033
C<_skonto_charts_and_tax_correction> generates one entry for each tax type entry.
1100 1034

  
1101 1035
=item C<open_amount>
1102 1036

  
......
1120 1054

  
1121 1055
Creates data intended for an L.select_tag dropdown that can be used in a
1122 1056
template. Depending on the rules it will choose from the options
1123
without_skonto, with_skonto_pt and difference_as_skonto, and select the most
1057
without_skonto and with_skonto_pt and select the most
1124 1058
likely one.
1125 1059

  
1126 1060
If the parameter "sepa" is passed, the SEPA export payments that haven't been
......
1134 1068

  
1135 1069
=item * with_skonto_pt is only offered if there haven't been any payments yet and the current date is within the skonto period.
1136 1070

  
1137
=item * difference_as_skonto is only offered if there have already been payments made and the open amount is smaller than 10% of the total amount.
1138

  
1139 1071
with_skonto_pt will only be offered, if all the AR_amount/AP_amount have a
1140 1072
taxkey with a configured skonto chart
1141 1073

  
......
1182 1114
This is a helper function for BankTransaction/ajax_payment_suggestion and
1183 1115
template/webpages/bank_transactions/invoices.html
1184 1116

  
1185
We are working with an existing payment, so difference_as_skonto never makes sense.
1117
We are working with an existing payment, so (deprecated) difference_as_skonto never makes sense.
1186 1118

  
1187 1119
If skonto is not possible (skonto_date does not exists) simply return
1188 1120
the single 'no skonto' option as a visual hint.
......
1215 1147
when looking at open amount, maybe consider that there may already be queued
1216 1148
amounts in SEPA Export
1217 1149

  
1218
=item * C<skonto_charts>
1150
=item * C<_skonto_charts_and_tax_correction>
1219 1151

  
1220 1152
Cannot handle negative skonto amounts, will always calculate the skonto amount
1221 1153
for credit notes or negative ap transactions with a positive sign.

Auch abrufbar als: Unified diff