Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision a7ca8ba2

Von Jan Büren vor mehr als 9 Jahren hinzugefügt

  • ID a7ca8ba215c2a039e97c27bc3c637712fca837e9
  • Vorgänger 46c987b8
  • Nachfolger f515825d

DeliveryOrder um convert_invoice erweitert

Diesselbe Idee wie bei SalesOrder->convert_invoice. Der ursprüngliche
Lieferschein wird geschlossen und das neue Objekt mittels record_links
verknüpft.
Entsprechend Testfall mitgeliefert.

Unterschiede anzeigen:

SL/DB/DeliveryOrder.pm
187 187
  $_[0]->is_sales ? $_[0]->customer : $_[0]->vendor;
188 188
}
189 189

  
190
sub convert_to_invoice {
191
  my ($self, %params) = @_;
192

  
193
  croak("Conversion to invoices is only supported for sales records") unless $self->customer_id;
194

  
195
  my $invoice;
196
  if (!$self->db->with_transaction(sub {
197
    require SL::DB::Invoice;
198
    $invoice = SL::DB::Invoice->new_from($self)->post(%params) || die;
199
    $self->link_to_record($invoice);
200
    foreach my $item (@{ $invoice->items }) {
201
      foreach (qw(delivery_order_items)) {    # expand if needed (delivery_order_items)
202
        if ($item->{"converted_from_${_}_id"}) {
203
          die unless $item->{id};
204
          RecordLinks->create_links('mode'       => 'ids',
205
                                    'from_table' => $_,
206
                                    'from_ids'   => $item->{"converted_from_${_}_id"},
207
                                    'to_table'   => 'invoice',
208
                                    'to_id'      => $item->{id},
209
          ) || die;
210
          delete $item->{"converted_from_${_}_id"};
211
        }
212
      }
213
    }
214
    $self->update_attributes(closed => 1);
215
    1;
216
  })) {
217
    return undef;
218
  }
219

  
220
  return $invoice;
221
}
222

  
190 223
1;
191 224
__END__
192 225

  
......
288 321
Returns a string describing this record's type: either
289 322
C<sales_delivery_order> or C<purchase_delivery_order>.
290 323

  
324
=item C<convert_to_invoice %params>
325

  
326
Creates a new invoice with C<$self> as the basis by calling
327
L<SL::DB::Invoice::new_from>. That invoice is posted, and C<$self> is
328
linked to the new invoice via L<SL::DB::RecordLink>. C<$self>'s
329
C<closed> attribute is set to C<true>, and C<$self> is saved.
330

  
331
The arguments in C<%params> are passed to L<SL::DB::Invoice::post>.
332

  
333
Returns the new invoice instance on success and C<undef> on
334
failure. The whole process is run inside a transaction. On failure
335
nothing is created or changed in the database.
336

  
337
At the moment only sales delivery orders can be converted.
338

  
291 339
=back
292 340

  
293 341
=head1 BUGS
t/db_helper/convert_invoice.t
1
use Test::More tests => 29;
2

  
3
use strict;
4

  
5
use lib 't';
6
use utf8;
7

  
8
use Support::TestSetup;
9

  
10
use Carp;
11
use Data::Dumper;
12
use Support::TestSetup;
13
use Test::Exception;
14
use List::Util qw(max);
15

  
16
use SL::DB::Buchungsgruppe;
17
use SL::DB::Currency;
18
use SL::DB::Customer;
19
use SL::DB::Employee;
20
use SL::DB::Invoice;
21
use SL::DB::Order;
22
use SL::DB::DeliveryOrder;
23
use SL::DB::Part;
24
use SL::DB::Unit;
25
use SL::DB::TaxZone;
26

  
27
my ($customer, $currency_id, $buchungsgruppe, $employee, $vendor, $taxzone, $buchungsgruppe7, $tax, $tax7,
28
    $unit, @parts);
29

  
30
sub clear_up {
31
  foreach (qw(DeliveryOrderItem DeliveryOrder InvoiceItem Invoice Part Customer Vendor Employee Department PaymentTerm)) {
32
    "SL::DB::Manager::${_}"->delete_all(all => 1);
33
  }
34
};
35

  
36
sub reset_state {
37
  my %params = @_;
38

  
39
  clear_up();
40

  
41
  $buchungsgruppe   = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 19%', %{ $params{buchungsgruppe} }) || croak "No accounting group 19\%";
42
  $buchungsgruppe7  = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%', %{ $params{buchungsgruppe} })  || croak "No accounting group 7\%";
43
  $taxzone          = SL::DB::Manager::TaxZone->find_by( description => 'Inland')                                           || croak "No taxzone";
44
  $tax              = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{tax} })                           || croak "No tax for 19\%";
45
  $tax7             = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                                              || croak "No tax for 7\%";
46
  $unit             = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} })                                      || croak "No unit";
47
  $currency_id     = $::instance_conf->get_currency_id;
48

  
49
  $customer     = SL::DB::Customer->new(
50
    name        => '520484567dfaedc9e60fc',
51
    currency_id => $currency_id,
52
    taxzone_id  => $taxzone->id,
53
    %{ $params{customer} }
54
  )->save;
55

  
56
  # some od.rnr real anonym data
57
  my $employee_bk = SL::DB::Employee->new(
58
                'id' => 31915,
59
                'login' => 'barbuschka.kappes',
60
                'name' => 'Barbuschka Kappes',
61
  )->save;
62

  
63
  my $department_do = SL::DB::Department->new(
64
                 'description' => 'Maisenhaus-Versand',
65
                 'id' => 32149,
66
                 'itime' => undef,
67
                 'mtime' => undef
68
  )->save;
69

  
70
  my $payment_do = SL::DB::PaymentTerm->new(
71
                 'description' => '14Tage 2%Skonto, 30Tage netto',
72
                 'description_long' => "Innerhalb von 14 Tagen abzüglich 2 % Skonto, innerhalb von 30 Tagen rein netto.|Bei einer Zahlung bis zum <%skonto_date%> gewähren wir 2 % Skonto (EUR <%skonto_amount%>) entspricht EUR <%total_wo_skonto%>.Bei einer Zahlung bis zum <%netto_date%> ist der fällige Betrag in Höhe von <%total%> <%currency%> zu überweisen.",
73
                 'id' => 11276,
74
                 'itime' => undef,
75
                 'mtime' => undef,
76
                 'percent_skonto' => '0.02',
77
                 'ranking' => undef,
78
                 'sortkey' => 4,
79
                 'terms_netto' => 30,
80
                 'auto_calculation' => undef,
81
                 'terms_skonto' => 14
82
  )->save;
83

  
84
  # two real parts
85
  @parts = ();
86
  push @parts, SL::DB::Part->new(
87
                 'id' => 26321,
88
                 'image' => '',
89
                 'lastcost' => '49.95000',
90
                 'listprice' => '0.00000',
91
                 'onhand' => '5.00000',
92
                 'partnumber' => 'v-519160549',
93
                 #'partsgroup_id' => 111645,
94
                 'rop' => '0',
95
                 'sellprice' => '242.20000',
96
                 #'warehouse_id' => 64702,
97
                 'weight' => '0.79',
98
                 description        => "Nussbaum, Gr.5, Unterfilz weinrot, genietet[[Aufschnittbreite: 11,0, Kernform: US]]\"" ,
99
                 buchungsgruppen_id => $buchungsgruppe->id,
100
                 unit               => $unit->name,
101
                 id                 => 26321,
102
  )->save;
103

  
104
  push @parts, SL::DB::Part->new(
105
                 'description' => "[[0640]]Flügel Hammerstiele bestehend aus:
106
70 Stielen Standard in Weißbuche und
107
20 Stielen Diskant abgekehlt in Weißbuche
108
mit Röllchen aus Synthetikleder,
109
Kapseln mit Yamaha Profil, Kerbenabstand 3,6 mm mit eingedrehten Abnickschrauben",
110
                 'id' => 25505,
111
                 'lastcost' => '153.00000',
112
                 'listprice' => '0.00000',
113
                 'onhand' => '9.00000',
114
                 'partnumber' => 'v-120160086',
115
                 # 'partsgroup_id' => 111639,
116
                 'rop' => '0',
117
                 'sellprice' => '344.30000',
118
                 'weight' => '0.9',
119
                  buchungsgruppen_id => $buchungsgruppe->id,
120
                  unit               => $unit->name,
121
  )->save;
122
}
123

  
124
sub new_delivery_order {
125
  my %params  = @_;
126

  
127
  return SL::DB::DeliveryOrder->new(
128
   currency_id => $currency_id,
129
   taxzone_id  => $taxzone->id,
130
    %params,
131
  )->save;
132
}
133

  
134
Support::TestSetup::login();
135

  
136
reset_state();
137

  
138
# we create L20199 with two items
139
my $do1 = new_delivery_order('department_id'    => 32149,
140
                             'donumber'         => 'L20199',
141
                             'employee_id'      => 31915,
142
                             'intnotes'         => 'Achtung: Neue Lieferadresse ab 16.02.2015 in der Carl-von-Ossietzky-Str.32!   13.02.2015/MH
143

  
144
                                            Steinway-Produkte (201...) immer plus 25% dazu rechnen / BK 13.02.2014',
145
                              'ordnumber'       => 'A16399',
146
                              'payment_id'      => 11276,
147
                              'salesman_id'     => 31915,
148
                              'shippingpoint'   => 'Maisenhaus',
149
                              # 'shipto_id'     => 451463,
150
                              'is_sales'        => 'true',
151
                              'shipvia'         => 'DHL, Versand am 06.03.2015, 1 Paket  17,00 kg',
152
                              'taxzone_id'      => 4,
153
                              'closed'          => undef,
154
                              # 'currency_id'   => 1,
155
                              'cusordnumber'    => 'b84da',
156
                              'customer_id'     => $customer->id,
157
                              'id'              => 464003,
158
);
159

  
160
my $do1_item1 = SL::DB::DeliveryOrderItem->new('delivery_order_id' => 464003,
161
                                               'description' => "Flügel Hammerkopf bestehend aus:
162
                                                                 Bass/Diskant 26/65 Stück, Gesamtlänge 80/72, Bohrlänge 56/48
163
                                                                 Nussbaum, Gr.5, Unterfilz weinrot, genietet[[Aufschnittbreite: 11,0, Kernform: US]]",
164
                                               'discount' => '0.25',
165
                                               'id' => 144736,
166
                                               'lastcost' => '49.95000',
167
                                               'longdescription' => '',
168
                                               'marge_price_factor' => 1,
169
                                               'mtime' => undef,
170
                                               'ordnumber' => 'A16399',
171
                                               'parts_id' => 26321,
172
                                               'position' => 1,
173
                                               'price_factor' => 1,
174
                                               'qty' => '2.00000',
175
                                               'sellprice' => '242.20000',
176
                                               'transdate' => '06.03.2015',
177
                                               'unit' => 'kg')->save;
178

  
179
my $do1_item2 = SL::DB::DeliveryOrderItem->new('delivery_order_id' => 464003,
180
                 'description' => "[[0640]]Flügel Hammerstiele bestehend aus:
181
70 Stielen Standard in Weißbuche und
182
20 Stielen Diskant abgekehlt in Weißbuche
183
mit Röllchen aus Synthetikleder,
184
Kapseln mit Yamaha Profil, Kerbenabstand 3,6 mm mit eingedrehten Abnickschrauben",
185
                 'discount' => '0.25',
186
                 'id' => 144737,
187
                 'itime' => undef,
188
                 'lastcost' => '153.00000',
189
                 'longdescription' => '',
190
                 'marge_price_factor' => 1,
191
                 'mtime' => undef,
192
                 'ordnumber' => 'A16399',
193
                 'parts_id' => 25505,
194
                 'position' => 2,
195
                 'price_factor' => 1,
196
                 'price_factor_id' => undef,
197
                 'pricegroup_id' => undef,
198
                 'project_id' => undef,
199
                 'qty' => '3.00000',
200
                 'reqdate' => undef,
201
                 'sellprice' => '344.30000',
202
                 'serialnumber' => '',
203
                 'transdate' => '06.03.2015',
204
                 'unit' => 'kg')->save;
205

  
206
# TESTS
207

  
208

  
209
# test delivery order before any conversion
210
ok($do1->donumber eq "L20199", 'Delivery Order Number created');
211
ok((not $do1->closed) , 'Delivery Order is not closed');
212
ok($do1_item1->parts_id eq '26321', 'doi linked with part');
213
ok($do1_item1->qty == 2, 'qty check doi');
214
ok($do1_item2->position == 2, 'doi2 position check');
215
ok(2 ==  scalar@{ SL::DB::Manager::DeliveryOrderItem->get_all(where => [ delivery_order_id => $do1->id ]) }, 'two doi linked');
216

  
217

  
218
# convert this do to invoice
219
my $invoice = $do1->convert_to_invoice();
220

  
221
# test invoice afterwards
222

  
223
ok ($invoice->shipvia eq "DHL, Versand am 06.03.2015, 1 Paket  17,00 kg", "ship via check");
224
ok ($invoice->shippingpoint eq "Maisenhaus", "shipping point check");
225
ok ($invoice->ordnumber eq "A16399", "ordnumber check");
226
ok ($invoice->donumber eq "L20199", "donumber check");
227
ok(($do1->closed) , 'Delivery Order is closed after conversion');
228
ok (SL::DB::PaymentTerm->new(id => $invoice->{payment_id})->load->description eq "14Tage 2%Skonto, 30Tage netto", 'payment term description check');
229

  
230
# some test data from original client invoice console (!)
231
# my $invoice3 = SL::DB::Manager::Invoice->find_by( ordnumber => 'A16399' );
232
# which will fail due to PTC Calculation differs from GUI-Calculation, see issue: http://redmine.kivitendo-premium.de/issues/82
233
# pp $invoice3
234
# values from gui should be:
235
#ok($invoice->amount == 1354.20000, 'amount check');
236
#ok($invoice->marge_percent == 50.88666, 'marge percent check');
237
#ok($invoice->marge_total == 579.08000, 'marge total check');
238
#ok($invoice->netamount == 1137.98000, 'netamount check');
239

  
240

  
241
# the values change if one reloads the object
242
# without reloading we get this failures
243
#not ok 17 - amount check
244
#   Failed test 'amount check'
245
#   at t/db_helper/convert_invoice.t line 272.
246
#          got: '1354.17'
247
#     expected: '1354.17000'
248
#not ok 18 - marge percent check
249
#   Failed test 'marge percent check'
250
#   at t/db_helper/convert_invoice.t line 273.
251
#          got: '50.8857956342929'
252
#     expected: '50.88580'
253
#not ok 19 - marge total check
254
#   Failed test 'marge total check'
255
#   at t/db_helper/convert_invoice.t line 274.
256
#          got: '579.06'
257
#     expected: '579.06000'
258
#not ok 20 - netamount check
259
#   Failed test 'netamount check'
260
#   at t/db_helper/convert_invoice.t line 275.
261
#          got: '1137.96'
262
#     expected: '1137.96000'
263

  
264
$invoice->load;
265

  
266
ok($invoice->currency_id eq '1', 'currency_id');
267
ok($invoice->cusordnumber eq 'b84da', 'cusordnumber check');
268
ok(SL::DB::Department->new(id => $invoice->{department_id})->load->description eq "Maisenhaus-Versand", 'department description');
269
is($invoice->amount, '1354.17000', 'amount check');
270
is($invoice->marge_percent, '50.88580', 'marge percent check');
271
is($invoice->marge_total, '579.06000', 'marge total check');
272
is($invoice->netamount, '1137.96000', 'netamount check');
273

  
274
# some item checks
275
ok(@ {$invoice->items_sorted}[0]->parts_id eq '26321', 'invoiceitem 1 linked with part');
276
ok(2 ==  scalar@{ $invoice->invoiceitems }, 'two invoice items linked with invoice');
277
is(@ {$invoice->items_sorted}[0]->position, 1, "position 1 order correct");
278
is(@ {$invoice->items_sorted}[1]->position, 2, "position 2 order correct");
279
is(@ {$invoice->items_sorted}[0]->part->partnumber, 'v-519160549', "partnumber 1 correct");
280
is(@ {$invoice->items_sorted}[1]->part->partnumber, 'v-120160086', "partnumber 2 correct");
281
is(@ {$invoice->items_sorted}[0]->qty, '2.00000', "pos 1 qty");
282
is(@ {$invoice->items_sorted}[1]->qty, '3.00000', "pos 2 qty");
283
is(@ {$invoice->items_sorted}[0]->discount, 0.25, "pos 1 discount");
284
is(@ {$invoice->items_sorted}[1]->discount, 0.25, "pos 2 discount");
285

  
286
# more ideas: check onhand, lastcost (parsed lastcost)
287

  
288
clear_up();
289

  
290
1;

Auch abrufbar als: Unified diff