1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger Accounting
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
31 # Inventory received module
33 #======================================================================
42 $main::lxdebug->enter_sub();
44 my ($self, $myconfig, $form) = @_;
46 # connect to database, turn off autocommit
47 my $dbh = $form->dbconnect_noauto($myconfig);
49 my ($query, $sth, @values, $project_id);
50 my ($allocated, $taxrate, $taxamount, $taxdiff, $item);
51 my ($amount, $linetotal, $lastinventoryaccno, $lastexpenseaccno);
52 my ($netamount, $invoicediff, $expensediff) = (0, 0, 0);
55 my $all_units = AM->retrieve_units($myconfig, $form);
59 &reverse_invoice($dbh, $form);
62 ($form->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('glid')|);
64 do_query($form, $dbh, qq|INSERT INTO ap (id, invnumber) VALUES (?, '')|, $form->{id});
67 if ($form->{currency} eq $form->{defaultcurrency}) {
68 $form->{exchangerate} = 1;
71 $form->check_exchangerate($myconfig, $form->{currency},
72 $form->{transdate}, 'sell');
75 $form->{exchangerate} =
78 : $form->parse_amount($myconfig, $form->{exchangerate});
80 $form->{exchangerate} = 1 unless ($form->{exchangerate} * 1);
83 my $q_item_unit = qq|SELECT unit FROM parts WHERE id = ?|;
84 my $h_item_unit = prepare_query($form, $dbh, $q_item_unit);
86 for my $i (1 .. $form->{rowcount}) {
87 next unless $form->{"id_$i"};
89 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
91 if ($form->{storno}) {
92 $form->{"qty_$i"} *= -1;
96 $form->{"inventory_accno_$i"} = $form->{"expense_accno_$i"};
100 if (!$item_units{$form->{"id_$i"}}) {
101 do_statement($form, $h_item_unit, $q_item_unit, $form->{"id_$i"});
102 ($item_units{$form->{"id_$i"}}) = $h_item_unit->fetchrow_array();
105 my $item_unit = $item_units{$form->{"id_$i"}};
107 if (defined($all_units->{$item_unit}->{factor})
108 && ($all_units->{$item_unit}->{factor} ne '')
109 && ($all_units->{$item_unit}->{factor} * 1 != 0)) {
110 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
114 $baseqty = $form->{"qty_$i"} * $basefactor;
116 @taxaccounts = split / /, $form->{"taxaccounts_$i"};
121 $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
122 my $fxsellprice = $form->{"sellprice_$i"};
124 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
126 my $decimalplaces = ($dec > 2) ? $dec : 2;
128 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
130 if ($form->{"inventory_accno_$i"}) {
132 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
134 if ($form->{taxincluded}) {
135 $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
136 $form->{"sellprice_$i"} = $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
138 $taxamount = $linetotal * $taxrate;
141 $netamount += $linetotal;
143 if ($form->round_amount($taxrate, 7) == 0) {
144 if ($form->{taxincluded}) {
145 foreach $item (@taxaccounts) {
147 $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
148 $taxdiff += $taxamount;
149 $form->{amount}{ $form->{id} }{$item} -= $taxamount;
151 $form->{amount}{ $form->{id} }{ $taxaccounts[0] } += $taxdiff;
154 map { $form->{amount}{ $form->{id} }{$_} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
158 map { $form->{amount}{ $form->{id} }{$_} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
161 # add purchase to inventory, this one is without the tax!
162 $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate};
163 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2) * $form->{exchangerate};
164 $linetotal = $form->round_amount($linetotal, 2);
166 # this is the difference for the inventory
167 $invoicediff += ($amount - $linetotal);
169 $form->{amount}{ $form->{id} }{ $form->{"inventory_accno_$i"} } -= $linetotal;
171 # adjust and round sellprice
172 $form->{"sellprice_$i"} =
173 $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
176 $query = qq|UPDATE parts SET lastcost = ? WHERE id = ?|;
177 @values = ($form->{"sellprice_$i"}, conv_i($form->{"id_$i"}));
178 do_query($form, $dbh, $query, @values);
180 if (!$form->{shipped}) {
181 $form->update_balance($dbh, "parts", "onhand", qq|id = ?|, $baseqty, $form->{"id_$i"})
184 # check if we sold the item already and
185 # make an entry for the expense and inventory
187 qq|SELECT i.id, i.qty, i.allocated, i.trans_id,
188 p.inventory_accno_id, p.expense_accno_id, a.transdate
189 FROM invoice i, ar a, parts p
190 WHERE (i.parts_id = p.id)
192 AND ((i.base_qty + i.allocated) > 0)
193 AND (i.trans_id = a.id)
195 $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
197 my $totalqty = $base_qty;
199 while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
201 my $qty = $ref->{base_qty} + $ref->{allocated};
203 if (($qty - $totalqty) > 0) {
207 $linetotal = $form->round_amount(($form->{"sellprice_$i"} * $qty) / $basefactor, 2);
209 if ($ref->{allocated} < 0) {
211 # we have an entry for it already, adjust amount
212 $form->update_balance($dbh, "acc_trans", "amount",
213 qq| (trans_id = $ref->{trans_id})
214 AND (chart_id = $ref->{inventory_accno_id})
215 AND (transdate = '$ref->{transdate}')|,
218 $form->update_balance($dbh, "acc_trans", "amount",
219 qq| (trans_id = $ref->{trans_id})
220 AND (chart_id = $ref->{expense_accno_id})
221 AND (transdate = '$ref->{transdate}')|,
224 } elsif ($linetotal != 0) {
225 # add entry for inventory, this one is for the sold item
226 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey)
227 VALUES (?, ?, ?, ?, (SELECT taxkey_id FROM chart WHERE id = ?))|;
228 @values = ($ref->{trans_id}, $ref->{inventory_accno_id}, $linetotal,
229 $ref->{transdate}, $ref->{inventory_accno_id});
230 do_query($form, $dbh, $query, @values);
233 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey)
234 VALUES (?, ?, ?, ?, (SELECT taxkey from tax WHERE chart_id = ?))|;
235 @values = ($ref->{trans_id}, $ref->{expense_accno_id}, ($linetotal * -1),
236 $ref->{transdate}, $ref->{expense_accno_id});
237 do_query($form, $dbh, $query, @values);
240 # update allocated for sold item
241 $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|, $qty * -1);
245 last if (($totalqty -= $qty) <= 0);
250 $lastinventoryaccno = $form->{"inventory_accno_$i"};
254 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
256 if ($form->{taxincluded}) {
257 $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
258 $form->{"sellprice_$i"} = $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
261 $taxamount = $linetotal * $taxrate;
264 $netamount += $linetotal;
266 if ($form->round_amount($taxrate, 7) == 0) {
267 if ($form->{taxincluded}) {
268 foreach $item (@taxaccounts) {
270 $linetotal * $form->{"${item}_rate"}
271 / (1 + abs($form->{"${item}_rate"}));
272 $totaltax += $taxamount;
273 $form->{amount}{ $form->{id} }{$item} -= $taxamount;
277 map { $form->{amount}{ $form->{id} }{$_} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
281 map { $form->{amount}{ $form->{id} }{$_} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
284 $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate};
285 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2) * $form->{exchangerate};
286 $linetotal = $form->round_amount($linetotal, 2);
288 # this is the difference for expense
289 $expensediff += ($amount - $linetotal);
291 # add amount to expense
292 $form->{amount}{ $form->{id} }{ $form->{"expense_accno_$i"} } -= $linetotal;
294 $lastexpenseaccno = $form->{"expense_accno_$i"};
296 # adjust and round sellprice
297 $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
300 $query = qq|UPDATE parts SET lastcost = ? WHERE id = ?|;
301 do_query($form, $dbh, $query, $form->{"sellprice_$i"}, conv_i($form->{"id_$i"}));
304 # save detail record in invoice table
306 qq|INSERT INTO invoice (trans_id, parts_id, description, qty, base_qty,
307 sellprice, fxsellprice, allocated, unit, deliverydate,
308 project_id, serialnumber)
309 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
310 @values = (conv_i($form->{id}), conv_i($form->{"id_$i"}),
311 $form->{"description_$i"}, $form->{"qty_$i"} * -1,
312 $baseqty * -1, $form->{"sellprice_$i"}, $fxsellprice, $allocated,
313 $form->{"unit_$i"}, conv_date($form->{deliverydate}),
314 conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"});
315 do_query($form, $dbh, $query, @values);
318 $h_item_unit->finish();
320 my $project_id = conv_i($form->{"globalproject_id"});
322 $form->{datepaid} = $form->{invdate};
324 # all amounts are in natural state, netamount includes the taxes
325 # if tax is included, netamount is rounded to 2 decimal places,
329 for my $i (1 .. $form->{paidaccounts}) {
330 $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
331 $form->{paid} += $form->{"paid_$i"};
332 $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
335 my ($tax, $paiddiff) = (0, 0);
337 $netamount = $form->round_amount($netamount, 2);
339 # figure out rounding errors for amount paid and total amount
340 if ($form->{taxincluded}) {
342 $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
343 $paiddiff = $amount - $netamount * $form->{exchangerate};
344 $netamount = $amount;
346 foreach $item (split / /, $form->{taxaccounts}) {
347 $amount = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
348 $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
350 $amount = $form->{amount}{ $form->{id} }{$item} * -1;
352 $netamount -= $amount;
355 $invoicediff += $paiddiff;
356 $expensediff += $paiddiff;
358 ######## this only applies to tax included
359 if ($lastinventoryaccno) {
360 $form->{amount}{ $form->{id} }{$lastinventoryaccno} -= $invoicediff;
362 if ($lastexpenseaccno) {
363 $form->{amount}{ $form->{id} }{$lastexpenseaccno} -= $expensediff;
367 $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
368 $paiddiff = $amount - $netamount * $form->{exchangerate};
369 $netamount = $amount;
371 foreach my $item (split / /, $form->{taxaccounts}) {
372 $form->{amount}{ $form->{id} }{$item} = $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
373 $amount = $form->round_amount( $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate} * -1, 2);
374 $paiddiff += $amount - $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate} * -1;
375 $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount * -1, 2);
376 $amount = $form->{amount}{ $form->{id} }{$item} * -1;
381 $form->{amount}{ $form->{id} }{ $form->{AP} } = $netamount + $tax;
383 if ($form->{paid} != 0) {
384 $form->{paid} = $form->round_amount($form->{paid} * $form->{exchangerate} + $paiddiff, 2);
387 # update exchangerate
388 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
389 $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate}, 0, $form->{exchangerate});
392 # record acc_trans transactions
393 foreach my $trans_id (keys %{ $form->{amount} }) {
394 foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
395 $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
396 next unless $form->{amount}{$trans_id}{$accno};
398 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
399 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
400 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
401 @values = ($trans_id, $accno, $form->{amount}{$trans_id}{$accno},
402 conv_date($form->{invdate}), $accno, $project_id);
403 do_query($form, $dbh, $query, @values);
407 # deduct payment differences from paiddiff
408 for my $i (1 .. $form->{paidaccounts}) {
409 if ($form->{"paid_$i"} != 0) {
410 $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
411 $paiddiff -= $amount - $form->{"paid_$i"} * $form->{exchangerate};
415 # force AP entry if 0
416 if ($form->{amount}{ $form->{id} }{ $form->{AP} } == 0) {
417 $form->{amount}{ $form->{id} }{ $form->{AP} } = $form->{paid};
420 # record payments and offsetting AP
421 for my $i (1 .. $form->{paidaccounts}) {
422 next if ($form->{"paid_$i"} == 0);
424 my ($accno) = split /--/, $form->{"AP_paid_$i"};
425 $form->{"datepaid_$i"} = $form->{invdate} unless ($form->{"datepaid_$i"});
426 $form->{datepaid} = $form->{"datepaid_$i"};
428 $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} + $paiddiff, 2) * -1;
431 if ($form->{amount}{ $form->{id} }{ $form->{AP} } != 0) {
432 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
433 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
434 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
435 @values = (conv_i($form->{id}), $form->{AP}, $amount,
436 $form->{"datepaid_$i"}, $form->{AP}, $project_id);
437 do_query($form, $dbh, $query, @values);
441 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, taxkey, project_id)
442 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?,
443 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
444 @values = (conv_i($form->{id}), $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"},
445 $form->{"source_$i"}, $form->{"memo_$i"}, $accno, $project_id);
446 do_query($form, $dbh, $query, @values);
450 if ($form->{currency} eq $form->{defaultcurrency}) {
451 $form->{"exchangerate_$i"} = 1;
453 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'sell');
455 $form->{"exchangerate_$i"} =
458 : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
461 # exchangerate difference
462 $form->{fx}{$accno}{ $form->{"datepaid_$i"} } +=
463 $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) + $paiddiff;
467 ($form->{"paid_$i"} * $form->{exchangerate}) -
468 ($form->{"paid_$i"} * $form->{"exchangerate_$i"});
470 $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } += $amount;
473 $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } += $amount;
478 # update exchange rate
479 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
480 $form->update_exchangerate($dbh, $form->{currency},
481 $form->{"datepaid_$i"},
482 0, $form->{"exchangerate_$i"});
486 # record exchange rate differences and gains/losses
487 foreach my $accno (keys %{ $form->{fx} }) {
488 foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
489 $form->{fx}{$accno}{$transdate} = $form->round_amount($form->{fx}{$accno}{$transdate}, 2);
490 next if ($form->{fx}{$accno}{$transdate} == 0);
492 $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, taxkey, project_id)
493 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, '0', '1', 0, ?)|;
494 @values = (conv_i($form->{id}), $accno, $form->{fx}{$accno}{$transdate}, conv_date($transdate), $project_id);
495 do_query($form, $dbh, $query, @values);
499 $amount = $netamount + $tax;
501 # set values which could be empty
502 my $taxzone_id = $form->{taxzone_id} * 1;
503 $form->{department_id} = (split /--/, $form->{department})[1];
504 $form->{invnumber} = $form->{id} unless $form->{invnumber};
506 $taxzone_id = 0 if ((3 < $taxzone_id) || (0 > $taxzone_id));
509 $query = qq|UPDATE ap SET
530 globalproject_id = ?,
534 @values = ($form->{invnumber}, $form->{ordnumber}, $form->{quonumber},
535 conv_date($form->{invdate}), conv_date($form->{orddate}), conv_date($form->{quodate}),
536 conv_i($form->{vendor_id}), $amount, $netamount, $form->{paid},
537 $form->{paid} ? conv_date($form->{datepaid}) : undef,
538 conv_date($form->{duedate}), $taxzone_id,
539 $form->{taxincluded} ? 't' : 'f',
540 $form->{notes}, $form->{intnotes}, $form->{currency}, conv_i($form->{department_id}),
541 $form->{storno} ? 't' : 'f',
542 conv_i($form->{globalproject_id}), conv_i($form->{cp_id}),
543 conv_i($form->{employee_id}),
544 conv_i($form->{id}));
545 do_query($form, $dbh, $query, @values);
547 if ($form->{storno}) {
548 $query = qq|UPDATE ap SET paid = paid + amount WHERE id = ?|;
549 do_query($form, $dbh, $query, conv_i($form->{storno_id}));
551 $query = qq|UPDATE ap SET storno = 't' WHERE id = ?|;
552 do_query($form, $dbh, $query, conv_i($form->{storno_id}));
554 $query = qq!UPDATE ap SET intnotes = ? || intnotes WHERE id = ?!;
555 do_query($form, $dbh, $query, 'Rechnung storniert am $form->{invdate} ', conv_i($form->{storno_id}));
557 $query = qq|UPDATE ap SET paid = amount WHERE id = ?|;
558 do_query($form, $dbh, $query, conv_i($form->{id}));
563 $form->{name} = $form->{vendor};
564 $form->{name} =~ s/--$form->{vendor_id}//;
565 $form->add_shipto($dbh, $form->{id}, "AP");
567 # delete zero entries
568 do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE amount = 0|);
570 Common::webdav_folder($form) if ($main::webdav);
572 my $rc = $dbh->commit;
575 $main::lxdebug->leave_sub();
580 sub reverse_invoice {
581 $main::lxdebug->enter_sub();
583 my ($dbh, $form) = @_;
585 # reverse inventory items
587 qq|SELECT i.parts_id, p.inventory_accno_id, p.expense_accno_id, i.qty, i.allocated, i.sellprice
588 FROM invoice i, parts p
589 WHERE (i.parts_id = p.id)
590 AND (i.trans_id = ?)|;
591 my $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
595 while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
596 $netamount += $form->round_amount($ref->{sellprice} * $ref->{qty} * -1, 2);
598 next unless $ref->{inventory_accno_id};
601 $form->update_balance($dbh, "parts", "onhand", qq|id = $ref->{parts_id}|, $ref->{qty});
603 # if $ref->{allocated} > 0 than we sold that many items
604 next if ($ref->{allocated} <= 0);
606 # get references for sold items
608 qq|SELECT i.id, i.trans_id, i.allocated, a.transdate
610 WHERE (i.parts_id = ?)
611 AND (i.allocated < 0)
612 AND (i.trans_id = a.id)
613 ORDER BY transdate DESC|;
614 my $sth2 = prepare_execute_query($form, $dbh, $query, $ref->{parts_id});
616 while (my $pthref = $sth2->fetchrow_hashref(NAME_lc)) {
617 my $qty = $ref->{allocated};
618 if (($ref->{allocated} + $pthref->{allocated}) > 0) {
619 $qty = $pthref->{allocated} * -1;
622 my $amount = $form->round_amount($ref->{sellprice} * $qty, 2);
625 $form->update_balance($dbh, "invoice", "allocated", qq|id = $pthref->{id}|, $qty);
627 $form->update_balance($dbh, "acc_trans", "amount",
628 qq| (trans_id = $pthref->{trans_id})
629 AND (chart_id = $ref->{expense_accno_id})
630 AND (transdate = '$pthref->{transdate}')|,
633 $form->update_balance($dbh, "acc_trans", "amount",
634 qq| (trans_id = $pthref->{trans_id})
635 AND (chart_id = $ref->{inventory_accno_id})
636 AND (transdate = '$pthref->{transdate}')|,
639 last if (($ref->{allocated} -= $qty) <= 0);
645 my $id = conv_i($form->{id});
648 $query = qq|DELETE FROM acc_trans WHERE trans_id = ?|;
649 do_query($form, $dbh, $query, $id);
651 # delete invoice entries
652 $query = qq|DELETE FROM invoice WHERE trans_id = ?|;
653 do_query($form, $dbh, $query, $id);
655 $query = qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AP')|;
656 do_query($form, $dbh, $query, $id);
658 $main::lxdebug->leave_sub();
662 $main::lxdebug->enter_sub();
664 my ($self, $myconfig, $form) = @_;
666 # connect to database
667 my $dbh = $form->dbconnect_noauto($myconfig);
669 &reverse_invoice($dbh, $form);
671 # delete zero entries
672 my $query = qq|DELETE FROM acc_trans WHERE amount = 0|;
673 do_query($form, $dbh, $query);
676 my $query = qq|DELETE FROM ap WHERE id = $form->{id}|;
677 do_query($form, $dbh, $query, conv_i($form->{id}));
679 my $rc = $dbh->commit;
682 $main::lxdebug->leave_sub();
687 sub retrieve_invoice {
688 $main::lxdebug->enter_sub();
690 my ($self, $myconfig, $form) = @_;
692 # connect to database
693 my $dbh = $form->dbconnect($myconfig);
695 my ($query, $sth, $ref, $q_invdate);
697 $q_invdate = ", current_date AS invdate" unless $form->{id};
699 # get default accounts and last invoice number
703 (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
704 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
705 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
706 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
707 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
711 $ref = selectfirst_hashref_query($form, $dbh, $query);
712 map { $form->{$_} = $ref->{$_} } keys %$ref;
716 $main::lxdebug->leave_sub();
723 qq|SELECT cp_id, invnumber, transdate AS invdate, duedate,
724 orddate, quodate, globalproject_id,
725 ordnumber, quonumber, paid, taxincluded, notes, taxzone_id, storno, gldate,
726 intnotes, curr AS currency
729 $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
730 map { $form->{$_} = $ref->{$_} } keys %$ref;
732 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "sell");
735 $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AP')|;
736 $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
738 map { $form->{$_} = $ref->{$_} } keys %$ref;
740 my $transdate = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
741 my $taxzone_id = $form->{taxzone_id} * 1;
743 $taxzone_id = 0 if ((3 < $taxzone_id) || (0 > $taxzone_id));
745 # retrieve individual items
748 c1.accno AS inventory_accno,
749 c1.new_chart_id AS inventory_new_chart,
750 date($transdate) - c1.valid_from AS inventory_valid,
752 c2.accno AS income_accno,
753 c2.new_chart_id AS income_new_chart,
754 date($transdate) - c2.valid_from AS income_valid,
756 c3.accno AS expense_accno,
757 c3.new_chart_id AS expense_new_chart,
758 date($transdate) - c3.valid_from AS expense_valid,
760 i.description, i.qty, i.fxsellprice AS sellprice,
761 i.parts_id AS id, i.unit, i.deliverydate, i.project_id, i.serialnumber,
763 p.partnumber, p.inventory_accno_id AS part_inventory_accno_id, p.bin,
768 JOIN parts p ON (i.parts_id = p.id)
769 LEFT JOIN chart c1 ON
770 ((SELECT inventory_accno_id
772 WHERE id = p.buchungsgruppen_id) = c1.id)
773 LEFT JOIN chart c2 ON
774 ((SELECT income_accno_id_${taxzone_id}
776 WHERE id = p.buchungsgruppen_id) = c2.id)
777 LEFT JOIN chart c3 ON
778 ((SELECT expense_accno_id_${taxzone_id}
780 WHERE id = p.buchungsgruppen_id) = c3.id)
781 LEFT JOIN project pr ON (i.project_id = pr.id)
782 LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
787 $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
789 while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
790 if (!$ref->{"part_inventory_accno_id"}) {
791 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
793 delete($ref->{"part_inventory_accno_id"});
795 foreach my $type (qw(inventory income expense)) {
796 while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
798 qq|SELECT accno, new_chart_id, date($transdate) - valid_from
801 ($ref->{"${type}_accno"},
802 $ref->{"${type}_new_chart"},
803 $ref->{"${type}_valid"})
804 = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
808 # get tax rates and description
809 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
811 qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
813 LEFT JOIN chart c ON (c.id = t.chart_id)
821 AND (startdate <= $transdate)
822 ORDER BY startdate DESC
825 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
826 $ref->{taxaccounts} = "";
829 while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
830 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
835 $ref->{taxaccounts} .= "$ptr->{accno} ";
837 if (!($form->{taxaccounts} =~ /$ptr->{accno}/)) {
838 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
839 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
840 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
841 $form->{taxaccounts} .= "$ptr->{accno} ";
846 chop $ref->{taxaccounts};
847 push @{ $form->{invoice_details} }, $ref;
852 Common::webdav_folder($form) if ($main::webdav);
856 $main::lxdebug->leave_sub();
860 $main::lxdebug->enter_sub();
862 my ($self, $myconfig, $form) = @_;
864 # connect to database
865 my $dbh = $form->dbconnect($myconfig);
867 my $dateformat = $myconfig->{dateformat};
868 $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
870 my $vid = conv_i($form->{vendor_id});
874 ? "to_date(" . $dbh->quote($form->{invdate}) . ", '$dateformat')"
880 v.name AS vendor, v.creditlimit, v.terms, v.notes AS intnotes,
881 v.email, v.cc, v.bcc, v.language_id, v.payment_id,
882 v.street, v.zipcode, v.city, v.country, v.taxzone_id,
883 $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
884 b.description AS business
886 LEFT JOIN business b ON (b.id = v.business_id)
887 LEFT JOIN payment_terms pt ON (v.payment_id = pt.id)
889 $ref = selectfirst_hashref_query($form, $dbh, $query, $vid);
890 map { $form->{$_} = $ref->{$_} } keys %$ref;
892 $form->{creditremaining} = $form->{creditlimit};
894 $query = qq|SELECT SUM(amount - paid) FROM ap WHERE vendor_id = ?|;
895 my ($unpaid_invoices) = selectfirst_array_query($form, $dbh, $query, $vid);
896 $form->{creditremaining} -= $unpaid_invoices;
898 $query = qq|SELECT o.amount,
901 WHERE (e.curr = o.curr)
902 AND (e.transdate = o.transdate)) AS exch
904 WHERE (o.vendor_id = ?) AND (o.quotation = '0') AND (o.closed = '0')|;
905 my $sth = prepare_execute_query($form, $dbh, $query, $vid);
906 while (my ($amount, $exch) = $sth->fetchrow_array()) {
907 $exch = 1 unless $exch;
908 $form->{creditremaining} -= $amount * $exch;
912 # get shipto if we do not convert an order or invoice
913 if (!$form->{shipto}) {
914 delete @{$form}{qw(shiptoname shiptostreet shiptozipcode shiptocity shiptocountry shiptocontact shiptophone shiptofax shiptoemail)};
916 $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module= 'CT')|;
917 $ref = selectfirst_hashref_query($form, $dbh, $query, $vid);
918 @{$form}{keys %$ref} = @{$ref}{keys %$ref};
919 map { $form->{$_} = $ref->{$_} } keys %$ref;
922 if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
923 # setup last accounts used
925 qq|SELECT c.id, c.accno, c.description, c.link, c.category
927 JOIN acc_trans ac ON (ac.chart_id = c.id)
928 JOIN ap a ON (a.id = ac.trans_id)
929 WHERE (a.vendor_id = ?)
930 AND (NOT ((c.link LIKE '%_tax%') OR (c.link LIKE '%_paid%')))
931 AND (a.id IN (SELECT max(a2.id) FROM ap a2 WHERE a2.vendor_id = ?))|;
932 my $refs = selectall_hashref_query($form, $dbh, $query, $vid, $vid);
936 if ($ref->{category} eq 'E') {
939 if ($form->{initial_transdate}) {
940 my $tax_query = qq|SELECT tk.tax_id, t.rate FROM taxkeys tk
941 LEFT JOIN tax t ON (tk.tax_id = t.id)
942 WHERE (tk.chart_id = ?) AND (startdate <= ?)
943 ORDER BY tk.startdate DESC
945 my ($tax_id, $rate) = selectrow_query($form, $dbh, $tax_query, $ref->{id}, $form->{initial_transdate});
946 $form->{"taxchart_$i"} = "${tax_id}--${rate}";
949 $form->{"AP_amount_$i"} = "$ref->{accno}--$tax_id";
952 if ($ref->{category} eq 'L') {
953 $form->{APselected} = $form->{AP_1} = $ref->{accno};
956 $form->{rowcount} = $i if ($i && !$form->{type});
961 $main::lxdebug->leave_sub();
965 $main::lxdebug->enter_sub();
967 my ($self, $myconfig, $form) = @_;
969 # connect to database
970 my $dbh = $form->dbconnect($myconfig);
972 my $i = $form->{rowcount};
974 # don't include assemblies or obsolete parts
975 my $where = "NOT p.assembly = '1' AND NOT p.obsolete = '1'";
978 foreach my $table_column (qw(p.partnumber p.description pg.partsgroup)) {
979 my $field = (split m{\.}, $table_column)[1];
980 next unless $form->{"${field}_${i}"};
981 $where .= " AND lower(${table_column}) LIKE ?";
982 push @values, '%' . $form->{"${field}_${i}"} . '%';
985 if ($form->{"description_$i"}) {
986 $where .= " ORDER BY p.description";
988 $where .= " ORDER BY p.partnumber";
992 if ($form->{type} eq "invoice") {
993 $transdate = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
995 $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
998 my $taxzone_id = $form->{taxzone_id} * 1;
999 $taxzone_id = 0 if ((3 < $taxzone_id) || (0 > $taxzone_id));
1003 p.id, p.partnumber, p.description, p.lastcost AS sellprice, p.listprice,
1004 p.unit, p.assembly, p.bin, p.onhand, p.formel,
1005 p.notes AS partnotes, p.notes AS longdescription, p.not_discountable,
1006 p.inventory_accno_id,
1008 c1.accno AS inventory_accno,
1009 c1.new_chart_id AS inventory_new_chart,
1010 date($transdate) - c1.valid_from AS inventory_valid,
1012 c2.accno AS income_accno,
1013 c2.new_chart_id AS income_new_chart,
1014 date($transdate) - c2.valid_from AS income_valid,
1016 c3.accno AS expense_accno,
1017 c3.new_chart_id AS expense_new_chart,
1018 date($transdate) - c3.valid_from AS expense_valid,
1023 LEFT JOIN chart c1 ON
1024 ((SELECT inventory_accno_id
1025 FROM buchungsgruppen
1026 WHERE id = p.buchungsgruppen_id) = c1.id)
1027 LEFT JOIN chart c2 ON
1028 ((SELECT income_accno_id_${taxzone_id}
1029 FROM buchungsgruppen
1030 WHERE id = p.buchungsgruppen_id) = c2.id)
1031 LEFT JOIN chart c3 ON
1032 ((SELECT expense_accno_id_${taxzone_id}
1033 FROM buchungsgruppen
1034 WHERE id = p.buchungsgruppen_id) = c3.id)
1035 LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1037 my $sth = prepare_execute_query($form, $dbh, $query, @values);
1039 $form->{item_list} = [];
1040 while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1042 # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
1043 # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
1044 # Buchungskonto also aus dem Ergebnis rausgenommen werden.
1045 if (!$ref->{inventory_accno_id}) {
1046 map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
1048 delete($ref->{inventory_accno_id});
1050 # get tax rates and description
1051 $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1053 qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
1055 LEFT JOIN chart c on (c.id = t.chart_id)
1063 AND (startdate <= $transdate)
1064 ORDER BY startdate DESC
1067 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1069 $ref->{taxaccounts} = "";
1071 while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1073 # if ($customertax{$ref->{accno}}) {
1074 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1079 $ref->{taxaccounts} .= "$ptr->{accno} ";
1081 if (!($form->{taxaccounts} =~ /$ptr->{accno}/)) {
1082 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1083 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1084 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1085 $form->{taxaccounts} .= "$ptr->{accno} ";
1091 chop $ref->{taxaccounts};
1093 push @{ $form->{item_list} }, $ref;
1100 $main::lxdebug->leave_sub();
1103 sub vendor_details {
1104 $main::lxdebug->enter_sub();
1106 my ($self, $myconfig, $form, @wanted_vars) = @_;
1108 # connect to database
1109 my $dbh = $form->dbconnect($myconfig);
1113 # get contact id, set it if nessessary
1114 $form->{cp_id} = (split /--/, $form->{contact})[1];
1116 if ($form->{cp_id}) {
1117 $contact = "AND cp.cp_id = ?";
1118 push @values, $form->{cp_id};
1121 # get rest for the vendor
1122 # fax and phone and email as vendor*
1124 qq|SELECT ct.*, cp.*, ct.notes as vendornotes, phone as vendorphone, fax as vendorfax, email as vendoremail
1126 LEFT JOIN contacts cp ON (ct.id = cp.cp_cv_id)
1127 WHERE (ct.id = ?) $contact
1130 my $ref = selectfirst_hashref_query($form, $dbh, $query, $form->{vendor_id}, @values);
1132 # remove id and taxincluded before copy back
1133 delete @$ref{qw(id taxincluded)};
1135 @wanted_vars = grep({ $_ } @wanted_vars);
1136 if (scalar(@wanted_vars) > 0) {
1138 map({ $h_wanted_vars{$_} = 1; } @wanted_vars);
1139 map({ delete($ref->{$_}) unless ($h_wanted_vars{$_}); } keys(%{$ref}));
1142 map { $form->{$_} = $ref->{$_} } keys %$ref;
1146 $main::lxdebug->leave_sub();
1150 $main::lxdebug->enter_sub();
1152 my ($self, $myconfig, $form) = @_;
1154 # connect to database
1155 my $dbh = $form->dbconnect($myconfig);
1158 qq|SELECT accno, description, link
1160 WHERE link LIKE '%IC%'
1162 my $sth = prepare_execute_query($query, $dbh, $query);
1164 while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1165 foreach my $key (split(/:/, $ref->{link})) {
1167 push @{ $form->{IC_links}{$key} },
1168 { accno => $ref->{accno},
1169 description => $ref->{description} };
1177 $main::lxdebug->leave_sub();
1181 $main::lxdebug->enter_sub();
1183 my ($self, $myconfig, $form, $locale) = @_;
1185 # connect to database, turn off autocommit
1186 my $dbh = $form->dbconnect_noauto($myconfig);
1188 $form->{datepaid} = $form->{invdate};
1190 # total payments, don't move we need it here
1191 for my $i (1 .. $form->{paidaccounts}) {
1192 $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
1193 $form->{paid} += $form->{"paid_$i"};
1194 $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
1197 $form->{exchangerate} =
1198 $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
1200 my $project_id = conv_i($form->{"globalproject_id"});
1202 # record payments and offsetting AP
1203 for my $i (1 .. $form->{paidaccounts}) {
1204 next if $form->{"paid_$i"} == 0;
1206 my ($accno) = split /--/, $form->{"AP_paid_$i"};
1207 $form->{"datepaid_$i"} = $form->{invdate} unless $form->{"datepaid_$i"};
1208 $form->{datepaid} = $form->{"datepaid_$i"};
1211 if (($form->{currency} eq $form->{defaultcurrency}) || ($form->{defaultcurrency} eq "")) {
1212 $form->{"exchangerate_$i"} = 1;
1215 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
1217 $form->{"exchangerate_$i"} =
1220 : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
1224 $amount = $form->round_amount($form->{"paid_$i"} * $form->{"exchangerate"}, 2) * -1;
1227 qq|DELETE FROM acc_trans
1228 WHERE (trans_id = ?)
1229 AND (chart_id = (SELECT c.id FROM chart c WHERE c.accno = ?))
1231 AND (transdate = ?)|;
1232 @values = (conv_i($form->{id}), $form->{AP}, $amount, conv_date($form->{"datepaid_$i"}));
1233 do_query($form, $dbh, $query, @values);
1236 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
1237 VALUES (?, (SELECT c.id FROM chart c WHERE c.accno = ?), ?, ?,
1238 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
1239 @values = (conv_i($form->{id}), $form->{AP}, $amount,
1240 conv_date($form->{"datepaid_$i"}), $form->{AP}, $project_id);
1241 do_query($form, $dbh, $query, @values);
1244 qq|DELETE FROM acc_trans
1245 WHERE (trans_id = ?)
1246 AND (chart_id=(SELECT c.id FROM chart c WHERE c.accno = ?))
1251 @values = (conv_i($form->{id}), $accno, $form->{"paid_$i"},
1252 conv_date($form->{"datepaid_$i"}), $form->{"source_$i"},
1253 $form->{"memo_$i"});
1254 do_query($form, $dbh, $query, @values);
1257 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, taxkey, project_id)
1258 VALUES (?, (SELECT c.id FROM chart c WHERE c.accno = ?), ?, ?, ?, ?,
1259 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
1260 @values = (conv_i($form->{id}), $accno, $form->{"paid_$i"},
1261 conv_date($form->{"datepaid_$i"}), $form->{"source_$i"},
1262 $form->{"memo_$i"}, $accno, $project_id);
1263 do_query($form, $dbh, $query, @values);
1266 $amount = $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} * $form->{"exchangerate_$i"};
1269 $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } += $amount;
1271 $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } += $amount;
1276 # update exchange rate
1277 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
1278 $form->update_exchangerate($dbh, $form->{currency},
1279 $form->{"datepaid_$i"},
1280 $form->{"exchangerate_$i"}, 0);
1284 # record exchange rate differences and gains/losses
1285 foreach my $accno (keys %{ $form->{fx} }) {
1286 foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
1287 $form->{fx}{$accno}{$transdate} = $form->round_amount($form->{fx}{$accno}{$transdate}, 2);
1288 next if $form->{fx}{$accno}{$transdate} == 0;
1291 qq|DELETE FROM acc_trans
1292 WHERE (trans_id = ?)
1293 AND (chart_id = (SELECT id FROM chart WHERE accno = ?))
1297 AND (fx_transaction = '1')|;
1298 @values = (conv_i($form->{id}), $accno, $form->{fx}{$accno}{$transdate}, $transdate);
1299 do_query($form, $dbh, $query, @values);
1302 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, taxkey, project_id)
1303 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, '0', '1',
1304 (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
1305 @values = (conv_i($form->{id}), $accno, $form->{fx}{$accno}{$transdate},
1306 $transdate, $accno, $project_id);
1307 do_query($form, $dbh, $query, @values);
1311 my $datepaid = $form->{paid} ? qq|'$form->{datepaid}'| : "NULL";
1314 my $query = qq|UPDATE ap
1315 SET paid = ?, datepaid = ?
1317 @values = ($form->{paid}, $form->{paid} ? conv_date($form->{datepaid}) : undef, conv_i($form->{id}));
1318 do_query($form, $dbh, $query, @values);
1320 my $rc = $dbh->commit();
1323 $main::lxdebug->leave_sub();