This commit was generated by cvs2svn to compensate for changes in r3880,
[freeside.git] / sql-ledger / sql-ledger / SL / OE.pm
1 #=====================================================================
2 # SQL-Ledger Accounting
3 # Copyright (C) 2001
4 #
5 #  Author: Dieter Simader
6 #   Email: dsimader@sql-ledger.org
7 #     Web: http://www.sql-ledger.org
8 #
9 #  Contributors:
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #======================================================================
24 #
25 # Order entry module
26 # Quotation
27 #
28 #======================================================================
29
30 package OE;
31
32
33 sub transactions {
34   my ($self, $myconfig, $form) = @_;
35
36   # connect to database
37   my $dbh = $form->dbconnect($myconfig);
38  
39   my $query;
40   my $ordnumber = 'ordnumber';
41   my $quotation = '0';
42   my ($null, $department_id) = split /--/, $form->{department};
43
44   my $department = " AND o.department_id = $department_id" if $department_id;
45   
46   my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
47
48   ($form->{transdatefrom}, $form->{transdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
49
50   if ($form->{type} =~ /_quotation$/) {
51     $quotation = '1';
52     $ordnumber = 'quonumber';
53   }
54   
55   my $number = $form->like(lc $form->{$ordnumber});
56   my $name = $form->like(lc $form->{$form->{vc}});
57  
58   my $query = qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate,
59                  o.amount, ct.name, o.netamount, o.$form->{vc}_id,
60                  ex.$rate AS exchangerate,
61                  o.closed, o.quonumber, o.shippingpoint, o.shipvia,
62                  e.name AS employee, m.name AS manager, o.curr
63                  FROM oe o
64                  JOIN $form->{vc} ct ON (o.$form->{vc}_id = ct.id)
65                  LEFT JOIN employee e ON (o.employee_id = e.id)
66                  LEFT JOIN employee m ON (e.managerid = m.id)
67                  LEFT JOIN exchangerate ex ON (ex.curr = o.curr
68                                                AND ex.transdate = o.transdate)
69                  WHERE o.quotation = '$quotation'
70                  $department|;
71
72   my %ordinal = ( 'id' => 1,
73                   'ordnumber' => 2,
74                   'transdate' => 3,
75                   'reqdate' => 4,
76                   'name' => 6,
77                   'quonumber' => 11,
78                   'shipvia' => 13,
79                   'employee' => 14,
80                   'manager' => 15
81                 );
82
83   my @a = (transdate, $ordnumber, name);
84   push @a, "employee" if $form->{l_employee};
85   if ($form->{type} !~ /(ship|receive)_order/) {
86     push @a, "manager" if $form->{l_manager};
87   }
88   my $sortorder = $form->sort_order(\@a, \%ordinal);
89   
90   
91   # build query if type eq (ship|receive)_order
92   if ($form->{type} =~ /(ship|receive)_order/) {
93     
94     my ($warehouse, $warehouse_id) = split /--/, $form->{warehouse};
95
96     $query =  qq|SELECT DISTINCT o.id, o.ordnumber, o.transdate,
97                  o.reqdate, o.amount, ct.name, o.netamount, o.$form->{vc}_id,
98                  ex.$rate AS exchangerate,
99                  o.closed, o.quonumber, o.shippingpoint, o.shipvia,
100                  e.name AS employee, o.curr
101                  FROM oe o
102                  JOIN $form->{vc} ct ON (o.$form->{vc}_id = ct.id)
103                  JOIN orderitems oi ON (oi.trans_id = o.id)
104                  JOIN parts p ON (p.id = oi.parts_id)|;
105
106       if ($warehouse_id && $form->{type} eq 'ship_order') {
107         $query .= qq|
108                  JOIN inventory i ON (oi.parts_id = i.parts_id)
109                  |;
110       }
111
112     $query .= qq|
113                  LEFT JOIN employee e ON (o.employee_id = e.id)
114                  LEFT JOIN exchangerate ex ON (ex.curr = o.curr
115                                                AND ex.transdate = o.transdate)
116                  WHERE o.quotation = '0'
117                  AND (p.inventory_accno_id > 0 OR p.assembly = '1')
118                  AND oi.qty != oi.ship
119                  $department|;
120                  
121     if ($warehouse_id && $form->{type} eq 'ship_order') {
122       $query .= qq|
123                  AND i.warehouse_id = $warehouse_id
124                  AND i.qty >= (oi.qty - oi.ship)
125                  |;
126     }
127
128   }
129  
130   if ($form->{"$form->{vc}_id"}) {
131     $query .= qq| AND o.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
132   } else {
133     if ($form->{$form->{vc}}) {
134       $query .= " AND lower(ct.name) LIKE '$name'";
135     }
136   }
137   if (!$form->{open} && !$form->{closed}) {
138     $query .= " AND o.id = 0";
139   } elsif (!($form->{open} && $form->{closed})) {
140     $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
141   }
142
143   if ($form->{$ordnumber}) {
144     $query .= " AND lower($ordnumber) LIKE '$number'";
145   }
146   if ($form->{shipvia}) {
147     $var = $form->like(lc $form->{shipvia});
148     $query .= " AND lower(o.shipvia) LIKE '$var'";
149   }
150   if ($form->{transdatefrom}) {
151     $query .= " AND o.transdate >= '$form->{transdatefrom}'";
152   }
153   if ($form->{transdateto}) {
154     $query .= " AND o.transdate <= '$form->{transdateto}'";
155   }
156
157   $query .= " ORDER by $sortorder";
158
159   my $sth = $dbh->prepare($query);
160   $sth->execute || $form->dberror($query);
161
162   my %id = ();
163   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
164     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
165     push @{ $form->{OE} }, $ref if $ref->{id} != $id{$ref->{id}};
166     $id{$ref->{id}} = $ref->{id};
167   }
168
169   $sth->finish;
170   $dbh->disconnect;
171   
172 }
173
174
175 sub save {
176   my ($self, $myconfig, $form) = @_;
177   
178   # connect to database, turn off autocommit
179   my $dbh = $form->dbconnect_noauto($myconfig);
180
181   my $query;
182   my $sth;
183   my $null;
184   my $exchangerate = 0;
185
186   ($null, $form->{employee_id}) = split /--/, $form->{employee};
187   unless ($form->{employee_id}) {
188     ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
189     $form->{employee} = "$form->{employee}--$form->{employee_id}";
190   }
191   
192   my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
193   
194   if ($form->{id}) {
195     
196     &adj_onhand($dbh, $form, $ml) if $form->{type} =~ /_order$/;
197     
198     $query = qq|DELETE FROM orderitems
199                 WHERE trans_id = $form->{id}|;
200     $dbh->do($query) || $form->dberror($query);
201
202     $query = qq|DELETE FROM shipto
203                 WHERE trans_id = $form->{id}|;
204     $dbh->do($query) || $form->dberror($query);
205
206   } else {
207     my $uid = time;
208     $uid .= $form->{login};
209     
210     $query = qq|INSERT INTO oe (ordnumber, employee_id)
211                 VALUES ('$uid', $form->{employee_id})|;
212     $dbh->do($query) || $form->dberror($query);
213    
214     $query = qq|SELECT id FROM oe
215                 WHERE ordnumber = '$uid'|;
216     $sth = $dbh->prepare($query);
217     $sth->execute || $form->dberror($query);
218
219     ($form->{id}) = $sth->fetchrow_array;
220     $sth->finish;
221     
222   }
223
224   my $amount;
225   my $linetotal;
226   my $discount;
227   my $project_id;
228   my $taxrate;
229   my $taxamount;
230   my $fxsellprice;
231   my %taxbase;
232   my @taxaccounts;
233   my %taxaccounts;
234   my $netamount = 0;
235
236   for my $i (1 .. $form->{rowcount}) {
237
238     map { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship);
239      
240     $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
241     $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
242  
243     if ($form->{"qty_$i"}) {
244
245       $fxsellprice = $form->{"sellprice_$i"};
246
247       my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
248       $dec = length $dec;
249       my $decimalplaces = ($dec > 2) ? $dec : 2;
250       
251       $discount = $form->round_amount($form->{"sellprice_$i"} * $form->{"discount_$i"}, $decimalplaces);
252       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} - $discount, $decimalplaces);
253       
254       $form->{"inventory_accno_$i"} *= 1;
255       $form->{"expense_accno_$i"} *= 1;
256       
257       $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
258       
259       @taxaccounts = split / /, $form->{"taxaccounts_$i"};
260       $taxrate = 0;
261       $taxdiff = 0;
262       
263       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
264
265       if ($form->{taxincluded}) {
266         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
267         $taxbase = $linetotal - $taxamount;
268         # we are not keeping a natural price, do not round
269         $form->{"sellprice_$i"} = $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
270       } else {
271         $taxamount = $linetotal * $taxrate;
272         $taxbase = $linetotal;
273       }
274
275       if (@taxaccounts && $form->round_amount($taxamount, 2) == 0) {
276         if ($form->{taxincluded}) {
277           foreach $item (@taxaccounts) {
278             $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
279
280             $taxaccounts{$item} += $taxamount;
281             $taxdiff += $taxamount; 
282
283             $taxbase{$item} += $taxbase;
284           }
285           $taxaccounts{$taxaccounts[0]} += $taxdiff;
286         } else {
287           foreach $item (@taxaccounts) {
288             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
289             $taxbase{$item} += $taxbase;
290           }
291         }
292       } else {
293         foreach $item (@taxaccounts) {
294           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
295           $taxbase{$item} += $taxbase;
296         }
297       }
298
299
300       $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"};
301       
302       $project_id = 'NULL';
303       if ($form->{"projectnumber_$i"}) {
304         ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
305         $project_id *= 1;
306       }
307       
308       # save detail record in orderitems table
309       $query = qq|INSERT INTO orderitems (|;
310       $query .= "id, " if $form->{"orderitems_id_$i"};
311       $query .= qq|trans_id, parts_id, description, qty, sellprice, discount,
312                    unit, reqdate, project_id, serialnumber, ship)
313                    VALUES (|;
314       $query .= qq|$form->{"orderitems_id_$i"},| if $form->{"orderitems_id_$i"};
315       $query .= qq|$form->{id}, $form->{"id_$i"}, |
316                    .$dbh->quote($form->{"description_$i"}).qq|,
317                    $form->{"qty_$i"}, $fxsellprice, $form->{"discount_$i"}, |
318                    .$dbh->quote($form->{"unit_$i"}).qq|, |
319                    .$form->dbquote($form->{"reqdate_$i"}, SQL_DATE).qq|, 
320                    $project_id, |
321                    .$dbh->quote($form->{"serialnumber_$i"}).qq|,
322                    $form->{"ship_$i"})|;
323       $dbh->do($query) || $form->dberror($query);
324
325       $form->{"sellprice_$i"} = $fxsellprice;
326       $form->{"discount_$i"} *= 100;
327     }
328   }
329
330
331   # set values which could be empty
332   map { $form->{$_} *= 1 } qw(vendor_id customer_id taxincluded closed quotation);
333
334   # add up the tax
335   my $tax = 0;
336   map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
337   
338   $amount = $form->round_amount($netamount + $tax, 2);
339   $netamount = $form->round_amount($netamount, 2);
340
341   if ($form->{currency} eq $form->{defaultcurrency}) {
342     $form->{exchangerate} = 1;
343   } else {
344     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
345   }
346   
347   $form->{exchangerate} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{exchangerate});
348   
349   my $quotation = ($form->{type} =~ /_order$/) ? '0' : '1';
350   
351   ($null, $form->{department_id}) = split(/--/, $form->{department});
352   $form->{department_id} *= 1;
353   
354   # save OE record
355   $query = qq|UPDATE oe set
356               ordnumber = |.$dbh->quote($form->{ordnumber}).qq|,
357               quonumber = |.$dbh->quote($form->{quonumber}).qq|,
358               transdate = '$form->{transdate}',
359               vendor_id = $form->{vendor_id},
360               customer_id = $form->{customer_id},
361               amount = $amount,
362               netamount = $netamount,
363               reqdate = |.$form->dbquote($form->{reqdate}, SQL_DATE).qq|,
364               taxincluded = '$form->{taxincluded}',
365               shippingpoint = |.$dbh->quote($form->{shippingpoint}).qq|,
366               shipvia = |.$dbh->quote($form->{shipvia}).qq|,
367               notes = |.$dbh->quote($form->{notes}).qq|,
368               intnotes = |.$dbh->quote($form->{intnotes}).qq|,
369               curr = '$form->{currency}',
370               closed = '$form->{closed}',
371               quotation = '$quotation',
372               department_id = $form->{department_id},
373               employee_id = $form->{employee_id},
374               language_code = '$form->{language_code}'
375               WHERE id = $form->{id}|;
376   $dbh->do($query) || $form->dberror($query);
377
378   $form->{ordtotal} = $amount;
379
380   # add shipto
381   $form->{name} = $form->{$form->{vc}};
382   $form->{name} =~ s/--$form->{"$form->{vc}_id"}//;
383   $form->add_shipto($dbh, $form->{id});
384
385   # save printed, emailed, queued
386   $form->save_status($dbh); 
387     
388   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
389     if ($form->{vc} eq 'customer') {
390       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
391     }
392     if ($form->{vc} eq 'vendor') {
393       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
394     }
395   }
396   
397
398   if ($form->{type} =~ /_order$/) {
399     # adjust onhand
400     &adj_onhand($dbh, $form, $ml * -1);
401     &adj_inventory($dbh, $myconfig, $form);
402   }
403
404   my %audittrail = ( tablename  => 'oe',
405                      reference  => ($form->{type} =~ /_order$/) ? $form->{ordnumber} : $form->{quonumber},
406                      formname   => $form->{type},
407                      action     => 'saved',
408                      id         => $form->{id} );
409
410   $form->audittrail($dbh, "", \%audittrail);
411   
412   my $rc = $dbh->commit;
413   $dbh->disconnect;
414
415   $rc;
416   
417 }
418
419
420
421 sub delete {
422   my ($self, $myconfig, $form, $spool) = @_;
423
424   # connect to database
425   my $dbh = $form->dbconnect_noauto($myconfig);
426
427   # delete spool files
428   my $query = qq|SELECT spoolfile FROM status
429                  WHERE trans_id = $form->{id}
430                  AND spoolfile IS NOT NULL|;
431   $sth = $dbh->prepare($query);
432   $sth->execute || $form->dberror($query);
433
434   my $spoolfile;
435   my @spoolfiles = ();
436
437   while (($spoolfile) = $sth->fetchrow_array) {
438     push @spoolfiles, $spoolfile;
439   }
440   $sth->finish;
441
442
443   $query = qq|SELECT o.parts_id, o.ship, p.inventory_accno_id
444               FROM orderitems o
445               JOIN parts p ON (p.id = o.parts_id)
446               WHERE trans_id = $form->{id}|;
447   $sth = $dbh->prepare($query);
448   $sth->execute || $form->dberror($query);
449
450   if ($form->{type} =~ /_order$/) {
451     $ml = ($form->{type} eq 'purchase_order') ? -1 : 1;
452     while (my ($id, $ship, $inv) = $sth->fetchrow_array) {
453       $form->update_balance($dbh,
454                             "parts",
455                             "onhand",
456                             qq|id = $id|,
457                             $ship * $ml) if $inv;
458     }
459   }
460   $sth->finish;
461
462   # delete inventory
463   $query = qq|DELETE FROM inventory
464               WHERE oe_id = $form->{id}|;
465   $dbh->do($query) || $form->dberror($query);
466   
467   # delete status entries
468   $query = qq|DELETE FROM status
469               WHERE trans_id = $form->{id}|;
470   $dbh->do($query) || $form->dberror($query);
471   
472   # delete OE record
473   $query = qq|DELETE FROM oe
474               WHERE id = $form->{id}|;
475   $dbh->do($query) || $form->dberror($query);
476
477   # delete individual entries
478   $query = qq|DELETE FROM orderitems
479               WHERE trans_id = $form->{id}|;
480   $dbh->do($query) || $form->dberror($query);
481
482   $query = qq|DELETE FROM shipto
483               WHERE trans_id = $form->{id}|;
484   $dbh->do($query) || $form->dberror($query);
485   
486   my %audittrail = ( tablename  => 'oe',
487                      reference  => ($form->{type} =~ /_order$/) ? $form->{ordnumber} : $form->{quonumber},
488                      formname   => $form->{type},
489                      action     => 'deleted',
490                      id         => $form->{id} );
491
492   $form->audittrail($dbh, "", \%audittrail);
493   
494   my $rc = $dbh->commit;
495   $dbh->disconnect;
496
497   if ($rc) {
498     foreach $spoolfile (@spoolfiles) {
499       unlink "$spool/$spoolfile" if $spoolfile;
500     }
501   }
502   
503   $rc;
504   
505 }
506
507
508
509 sub retrieve {
510   my ($self, $myconfig, $form) = @_;
511   
512   # connect to database
513   my $dbh = $form->dbconnect($myconfig);
514
515   my $query;
516   my $var;
517
518   if ($form->{id}) {
519     # get default accounts and last order number
520     $query = qq|SELECT (SELECT c.accno FROM chart c
521                         WHERE d.inventory_accno_id = c.id) AS inventory_accno,
522                        (SELECT c.accno FROM chart c
523                         WHERE d.income_accno_id = c.id) AS income_accno,
524                        (SELECT c.accno FROM chart c
525                         WHERE d.expense_accno_id = c.id) AS expense_accno,
526                        (SELECT c.accno FROM chart c
527                         WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
528                        (SELECT c.accno FROM chart c
529                         WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
530                 d.curr AS currencies
531                 FROM defaults d|;
532   } else {
533     $query = qq|SELECT (SELECT c.accno FROM chart c
534                         WHERE d.inventory_accno_id = c.id) AS inventory_accno,
535                        (SELECT c.accno FROM chart c
536                         WHERE d.income_accno_id = c.id) AS income_accno,
537                        (SELECT c.accno FROM chart c
538                         WHERE d.expense_accno_id = c.id) AS expense_accno,
539                        (SELECT c.accno FROM chart c
540                         WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
541                        (SELECT c.accno FROM chart c
542                         WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
543                 d.curr AS currencies,
544                 current_date AS transdate
545                 FROM defaults d|;
546   }
547   my $sth = $dbh->prepare($query);
548   $sth->execute || $form->dberror($query);
549
550   my $ref = $sth->fetchrow_hashref(NAME_lc);
551   map { $form->{$_} = $ref->{$_} } keys %$ref;
552   $sth->finish;
553
554   
555   if ($form->{id}) {
556     
557     # retrieve order
558     $query = qq|SELECT o.ordnumber, o.transdate, o.reqdate,
559                 o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
560                 o.curr AS currency, e.name AS employee, o.employee_id,
561                 o.$form->{vc}_id, cv.name AS $form->{vc}, o.amount AS invtotal,
562                 o.closed, o.reqdate, o.quonumber, o.department_id,
563                 d.description AS department, o.language_code
564                 FROM oe o
565                 JOIN $form->{vc} cv ON (o.$form->{vc}_id = cv.id)
566                 LEFT JOIN employee e ON (o.employee_id = e.id)
567                 LEFT JOIN department d ON (o.department_id = d.id)
568                 WHERE o.id = $form->{id}|;
569     $sth = $dbh->prepare($query);
570     $sth->execute || $form->dberror($query);
571
572     $ref = $sth->fetchrow_hashref(NAME_lc);
573     map { $form->{$_} = $ref->{$_} } keys %$ref;
574     $sth->finish;
575     
576    
577     $query = qq|SELECT * FROM shipto
578                 WHERE trans_id = $form->{id}|;
579     $sth = $dbh->prepare($query);
580     $sth->execute || $form->dberror($query);
581
582     $ref = $sth->fetchrow_hashref(NAME_lc);
583     map { $form->{$_} = $ref->{$_} } keys %$ref;
584     $sth->finish;
585
586     # get printed, emailed and queued
587     $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname
588                 FROM status s
589                 WHERE s.trans_id = $form->{id}|;
590     $sth = $dbh->prepare($query);
591     $sth->execute || $form->dberror($query);
592
593     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
594       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
595       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
596       $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
597     }
598     $sth->finish;
599     map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
600
601
602     my %oid = ( 'Pg'            => 'oid',
603                 'PgPP'          => 'oid',
604                 'Oracle'        => 'rowid',
605                 'DB2'           => '1=1'
606               );
607
608     # retrieve individual items
609     $query = qq|SELECT o.id AS orderitems_id,
610                 c1.accno AS inventory_accno,
611                 c2.accno AS income_accno,
612                 c3.accno AS expense_accno,
613                 p.partnumber, p.assembly, o.description, o.qty,
614                 o.sellprice, o.parts_id AS id, o.unit, o.discount, p.bin,
615                 o.reqdate, o.project_id, o.serialnumber, o.ship,
616                 pr.projectnumber,
617                 pg.partsgroup, p.partsgroup_id, p.partnumber AS sku,
618                 p.listprice, p.lastcost, p.weight,
619                 t.description AS partsgrouptranslation
620                 FROM orderitems o
621                 JOIN parts p ON (o.parts_id = p.id)
622                 LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
623                 LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
624                 LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
625                 LEFT JOIN project pr ON (o.project_id = pr.id)
626                 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
627                 LEFT JOIN translation t ON (t.trans_id = p.partsgroup_id AND t.language_code = '$form->{language_code}')
628                 WHERE o.trans_id = $form->{id}
629                 ORDER BY o.$oid{$myconfig->{dbdriver}}|;
630     $sth = $dbh->prepare($query);
631     $sth->execute || $form->dberror($query);
632
633     # foreign exchange rates
634     &exchangerate_defaults($dbh, $form);
635
636     # query for price matrix
637     my $pmh = &price_matrix_query($dbh, $form);
638     
639     # taxes
640     $query = qq|SELECT c.accno
641                 FROM chart c
642                 JOIN partstax pt ON (pt.chart_id = c.id)
643                 WHERE pt.parts_id = ?|;
644     my $tth = $dbh->prepare($query) || $form->dberror($query);
645    
646     my $taxrate;
647     my $ptref;
648     my $sellprice;
649     my $listprice;
650     
651     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
652
653       ($decimalplaces) = ($ref->{sellprice} =~ /\.(\d+)/);
654       $decimalplaces = length $decimalplaces;
655       $decimalplaces = 2 unless $decimalplaces;
656
657       $tth->execute($ref->{id});
658       $ref->{taxaccounts} = "";
659       $taxrate = 0;
660       
661       while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
662         $ref->{taxaccounts} .= "$ptref->{accno} ";
663         $taxrate += $form->{"$ptref->{accno}_rate"};
664       }
665       $tth->finish;
666       chop $ref->{taxaccounts};
667
668       # preserve prices
669       $sellprice = $ref->{sellprice};
670       $listprice = $ref->{listprice};
671       
672       # multiply by exchangerate
673       $ref->{sellprice} = $form->round_amount($ref->{sellprice} * $form->{$form->{currency}}, $decimalplaces);
674       $ref->{listprice} = $form->round_amount($ref->{listprice} * $form->{$form->{currency}}, $decimalplaces);
675       
676       # partnumber and price matrix
677       &price_matrix($pmh, $ref, $form->{transdate}, $decimalplaces, $form, $myconfig, 1);
678
679       $ref->{sellprice} = $sellprice;
680       $ref->{listprice} = $listprice;
681
682       $ref->{partsgroup} = $ref->{partsgrouptranslation} if $ref->{partsgrouptranslation};
683       
684       push @{ $form->{form_details} }, $ref;
685       
686     }
687     $sth->finish;
688
689   } else {
690
691     # get last name used
692     $form->lastname_used($dbh, $myconfig, $form->{vc}) unless $form->{"$form->{vc}_id"};
693     delete $form->{notes};
694
695   }
696
697   $dbh->disconnect;
698
699 }
700
701
702 sub price_matrix_query {
703   my ($dbh, $form) = @_;
704
705   my $query;
706   my $sth;
707
708   if ($form->{customer_id}) {
709     $query = qq|SELECT p.*, g.pricegroup
710              FROM partscustomer p
711              LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
712              WHERE p.parts_id = ?
713              AND p.customer_id = $form->{customer_id}
714
715              UNION
716
717              SELECT p.*, g.pricegroup
718              FROM partscustomer p
719              LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
720              JOIN customer c ON (c.pricegroup_id = g.id)
721              WHERE p.parts_id = ?
722              AND c.id = $form->{customer_id}
723
724              UNION
725
726              SELECT p.*, '' AS pricegroup
727              FROM partscustomer p
728              WHERE p.customer_id = 0
729              AND p.pricegroup_id = 0
730              AND p.parts_id = ?
731
732              ORDER BY customer_id DESC, pricegroup_id DESC, pricebreak
733              |;
734     $sth = $dbh->prepare($query) || $form->dberror($query);
735   }
736   
737   if ($form->{vendor_id}) {
738     # price matrix and vendor's partnumber
739     $query = qq|SELECT partnumber
740                 FROM partsvendor
741                 WHERE parts_id = ?
742                 AND vendor_id = $form->{vendor_id}|;
743     $sth = $dbh->prepare($query) || $form->dberror($query);
744   }
745   
746   $sth;
747
748 }
749
750
751 sub price_matrix {
752   my ($pmh, $ref, $transdate, $decimalplaces, $form, $myconfig, $init) = @_;
753
754   $ref->{pricematrix} = "";
755   my $customerprice = 0;
756   my $pricegroup = 0;
757   my $sellprice;
758   my $mref;
759   
760   # depends if this is a customer or vendor
761   if ($form->{customer_id}) {
762     $pmh->execute($ref->{id}, $ref->{id}, $ref->{id});
763
764     while ($mref = $pmh->fetchrow_hashref(NAME_lc)) {
765
766       # check date
767       if ($mref->{validfrom}) {
768         next if $transdate < $form->datetonum($mref->{validfrom}, $myconfig);
769       }
770       if ($mref->{validto}) {
771         next if $transdate > $form->datetonum($mref->{validto}, $myconfig);
772       }
773
774       # convert price
775       $sellprice = $form->round_amount($mref->{sellprice} * $form->{$mref->{curr}}, $decimalplaces);
776       
777       if ($mref->{customer_id}) {
778         $ref->{sellprice} = $sellprice unless $mref->{pricebreak};
779         $ref->{pricematrix} .= "$mref->{pricebreak}:$sellprice ";
780         $customerprice = 1;
781       }
782
783       if ($mref->{pricegroup_id}) {
784         if (! $customerprice) {
785           $ref->{sellprice} = $sellprice unless $mref->{pricebreak};
786           $ref->{pricematrix} .= "$mref->{pricebreak}:$sellprice ";
787           $pricegroup = 1;
788         }
789       }
790
791       if (! $customerprice && ! $pricegroup) {
792         $ref->{sellprice} = $sellprice unless $mref->{pricebreak};
793         $ref->{pricematrix} .= "$mref->{pricebreak}:$sellprice ";
794       }
795
796     }
797     $pmh->finish;
798
799     if ($ref->{pricematrix} !~ /^0:/) {
800       if ($init) {
801         $sellprice = $form->round_amount($ref->{sellprice}, $decimalplaces);
802       } else {
803         $sellprice = $form->round_amount($ref->{sellprice} * (1 - $form->{tradediscount}), $decimalplaces);
804       }
805       $ref->{pricematrix} = "0:$sellprice ".$ref->{pricematrix};
806     }
807     chop $ref->{pricematrix};
808
809   }
810
811
812   if ($form->{vendor_id}) {
813     $pmh->execute($ref->{id});
814     
815     $mref = $pmh->fetchrow_hashref(NAME_lc);
816
817     if ($mref->{partnumber}) {
818       $ref->{partnumber} = $mref->{partnumber};
819     }
820
821     if ($mref->{lastcost}) {
822       # do a conversion
823       $ref->{sellprice} = $form->round_amount($mref->{lastcost} * $form->{$mref->{curr}}, $decimalplaces);
824     }
825     $pmh->finish;
826
827     $ref->{sellprice} *= 1;
828
829     # add 0:price to matrix
830     $ref->{pricematrix} = "0:$ref->{sellprice}";
831
832   }
833
834 }
835
836
837 sub exchangerate_defaults {
838   my ($dbh, $form) = @_;
839
840   my $var;
841   my $buysell = ($form->{vc} eq "customer") ? "buy" : "sell";
842   
843   # get default currencies
844   my $query = qq|SELECT substr(curr,1,3), curr FROM defaults|;
845   my $eth = $dbh->prepare($query) || $form->dberror($query);
846   $eth->execute;
847   ($form->{defaultcurrency}, $form->{currencies}) = $eth->fetchrow_array;
848   $eth->finish;
849
850   $query = qq|SELECT $buysell
851               FROM exchangerate
852               WHERE curr = ?
853               AND transdate = ?|;
854   my $eth1 = $dbh->prepare($query) || $form->dberror($query);
855   $query = qq~SELECT max(transdate || ' ' || $buysell || ' ' || curr)
856               FROM exchangerate
857               WHERE curr = ?~;
858   my $eth2 = $dbh->prepare($query) || $form->dberror($query);
859
860   # get exchange rates for transdate or max
861   foreach $var (split /:/, substr($form->{currencies},4)) {
862     $eth1->execute($var, $form->{transdate});
863     ($form->{$var}) = $eth1->fetchrow_array;
864     if (! $form->{$var} ) {
865       $eth2->execute($var);
866       
867       ($form->{$var}) = $eth2->fetchrow_array;
868       ($null, $form->{$var}) = split / /, $form->{$var};
869       $form->{$var} = 1 unless $form->{$var};
870       $eth2->finish;
871     }
872     $eth1->finish;
873   }
874
875   $form->{$form->{defaultcurrency}} = 1;
876       
877 }
878
879
880 sub order_details {
881   my ($self, $myconfig, $form) = @_;
882
883   # connect to database
884   my $dbh = $form->dbconnect($myconfig);
885   my $query;
886   my $sth;
887     
888   my $item;
889   my $i;
890   my @sortlist = ();
891   my $projectnumber;
892   my $projectnumber_id;
893   my $translation;
894   my $partsgroup;
895
896   my %oid = ( 'Pg'      => 'oid',
897               'PgPP'    => 'oid',
898               'Oracle'  => 'rowid',
899               'DB2'     => '1=1'
900             );
901   
902   # sort items by project and partsgroup
903   for $i (1 .. $form->{rowcount}) {
904     $projectnumber = "";
905     $partsgroup = "";
906     $projectnumber_id = 0;
907     if ($form->{"projectnumber_$i"} && $form->{groupprojectnumber}) {
908       ($projectnumber, $projectnumber_id) = split /--/, $form->{"projectnumber_$i"};
909     }
910     if ($form->{"partsgroup_$i"} && $form->{grouppartsgroup}) {
911       ($partsgroup) = split /--/, $form->{"partsgroup_$i"};
912     }
913     push @sortlist, [ $i, "$projectnumber$partsgroup", $projectnumber, $projectnumber_id, $partsgroup ];
914
915     # sort the whole thing by project and group
916     @sortlist = sort { $a->[1] cmp $b->[1] } @sortlist;
917
918   }
919
920   # if there is a warehouse limit picking
921   if ($form->{warehouse_id} && $form->{formname} =~ /(pick|packing)_list/) {
922     # run query to check for inventory
923     $query = qq|SELECT sum(qty) AS qty
924                 FROM inventory
925                 WHERE parts_id = ?
926                 AND warehouse_id = ?|;
927     $sth = $dbh->prepare($query) || $form->dberror($query);
928
929     for $i (1 .. $form->{rowcount}) {
930       $sth->execute($form->{"id_$i"}, $form->{warehouse_id}) || $form->dberror;
931
932       ($qty) = $sth->fetchrow_array;
933       $sth->finish;
934
935       $form->{"qty_$i"} = 0 if $qty == 0;
936       
937       if ($form->parse_amount($myconfig, $form->{"ship_$i"}) > $qty) {
938         $form->{"ship_$i"} = $form->format_amount($myconfig, $qty);
939       }
940     }
941   }
942     
943   my @taxaccounts;
944   my %taxaccounts;
945   my $taxrate;
946   my $taxamount;
947   my $taxbase;
948   my $taxdiff;
949  
950   $query = qq|SELECT p.description, t.description
951               FROM project p
952               LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
953               WHERE id = ?|;
954   my $prh = $dbh->prepare($query) || $form->dberror($query);
955
956   my $runningnumber = 1;
957   my $sameitem = "";
958   my $subtotal;
959   my $k = scalar @sortlist;
960   my $j = 0;
961   
962   foreach $item (@sortlist) {
963     $i = $item->[0];
964     $j++;
965
966     if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
967       if ($item->[1] ne $sameitem) {
968
969         $projectnumber = "";
970         if ($form->{groupprojectnumber} && $item->[2]) {
971           # get project description
972           $prh->execute($item->[3]) || $form->dberror($query);
973
974           ($projectnumber, $translation) = $prh->fetchrow_array;
975           $prh->finish;
976
977           $projectnumber = ($translation) ? "$item->[2], $translation" : "$item->[2], $projectnumber";
978         }
979           
980         if ($form->{grouppartsgroup} && $item->[4]) {
981           $projectnumber .= " / " if $projectnumber;
982           $projectnumber .= $item->[4];
983         }
984         
985         $form->{projectnumber} = $projectnumber;
986         $form->format_string(projectnumber);
987         
988         push(@{ $form->{description} }, qq|$form->{projectnumber}|);
989         $sameitem = $item->[1];
990
991         map { push(@{ $form->{$_} }, "") } qw(runningnumber number sku qty ship unit bin serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate linetotal weight);
992       }
993     }
994
995     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
996     
997     if ($form->{"qty_$i"} != 0) {
998
999       $form->{totalqty} += $form->{"qty_$i"};
1000       $form->{totalship} += $form->{"ship_$i"};
1001       $form->{totalweight} += ($form->{"weight_$i"} * $form->{"qty_$i"});
1002
1003       # add number, description and qty to $form->{number}, ....
1004       push(@{ $form->{runningnumber} }, $runningnumber++);
1005       push(@{ $form->{number} }, qq|$form->{"partnumber_$i"}|);
1006       push(@{ $form->{sku} }, qq|$form->{"sku_$i"}|);
1007       push(@{ $form->{description} }, qq|$form->{"description_$i"}|);
1008       push(@{ $form->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"}));
1009       push(@{ $form->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"}));
1010       push(@{ $form->{unit} }, qq|$form->{"unit_$i"}|);
1011       push(@{ $form->{bin} }, qq|$form->{"bin_$i"}|);
1012       push(@{ $form->{serialnumber} }, qq|$form->{"serialnumber_$i"}|);
1013       push(@{ $form->{reqdate} }, qq|$form->{"reqdate_$i"}|);
1014       push(@{ $form->{projectnumber} }, qq|$form->{"projectnumber_$i"}|);
1015       
1016       push(@{ $form->{sellprice} }, $form->{"sellprice_$i"});
1017  
1018       push(@{ $form->{listprice} }, $form->{"listprice_$i"});
1019       
1020       push(@{ $form->{weight} }, $form->{"weight_$i"});
1021
1022       my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1023       my ($dec) = ($sellprice =~ /\.(\d+)/);
1024       $dec = length $dec;
1025       my $decimalplaces = ($dec > 2) ? $dec : 2;
1026
1027       my $discount = $form->round_amount($sellprice * $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100, $decimalplaces);
1028
1029       # keep a netprice as well, (sellprice - discount)
1030       $form->{"netprice_$i"} = $sellprice - $discount;
1031
1032       my $linetotal = $form->round_amount($form->{"qty_$i"} * $form->{"netprice_$i"}, 2);
1033
1034       push(@{ $form->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : " ");
1035       
1036       $discount = ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, $decimalplaces) : " ";
1037       $linetotal = ($linetotal != 0) ? $linetotal : " ";
1038
1039       push(@{ $form->{discount} }, $discount);
1040       push(@{ $form->{discountrate} }, $form->format_amount($myconfig, $form->{"discount_$i"}));
1041       
1042       $form->{ordtotal} += $linetotal;
1043
1044       # this is for the subtotals for grouping
1045       $subtotal += $linetotal;
1046
1047       push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2));
1048       
1049       $taxrate = 0;
1050       
1051       map { $taxrate += $form->{"${_}_rate"} } split / /, $form->{"taxaccounts_$i"};
1052
1053       if ($form->{taxincluded}) {
1054         # calculate tax
1055         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
1056         $taxbase = $linetotal / (1 + $taxrate);
1057       } else {
1058         $taxamount = $linetotal * $taxrate;
1059         $taxbase = $linetotal;
1060       }
1061
1062
1063       if ($form->round_amount($taxamount, 2) != 0) {
1064         foreach my $item (split / /, $form->{"taxaccounts_$i"}) {
1065           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
1066           $taxbase{$item} += $taxbase;
1067         }
1068       }
1069
1070       if ($form->{"assembly_$i"}) {
1071         $form->{stagger} = -1;
1072         &assembly_details($dbh, $form, $form->{"id_$i"}, $oid{$myconfig->{dbdriver}}, $form->{"qty_$i"});
1073       }
1074
1075     }
1076
1077     # add subtotal
1078     if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
1079       if ($subtotal) {
1080         if ($j < $k) {
1081           # look at next item
1082           if ($sortlist[$j]->[1] ne $sameitem) {
1083
1084             map { push(@{ $form->{$_} }, "") } qw(runningnumber number sku qty ship unit bin serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate weight);
1085
1086             push(@{ $form->{description} }, $form->{groupsubtotaldescription});
1087
1088             if (exists $form->{groupsubtotaldescription}) {
1089               push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
1090             } else {
1091               push(@{ $form->{linetotal} }, "");
1092             }
1093
1094             $subtotal = 0;
1095           }
1096
1097         } else {
1098
1099           # got last item
1100           if (exists $form->{groupsubtotaldescription}) {
1101             
1102             map { push(@{ $form->{$_} }, "") } qw(runningnumber number sku qty ship unit bin serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate weight);
1103
1104             push(@{ $form->{description} }, $form->{groupsubtotaldescription});
1105             push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
1106           }
1107         }
1108       }
1109     }
1110   }
1111
1112
1113   my $tax = 0;
1114   foreach $item (sort keys %taxaccounts) {
1115     if ($form->round_amount($taxaccounts{$item}, 2) != 0) {
1116       push(@{ $form->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
1117       
1118       $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
1119       
1120       push(@{ $form->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
1121       push(@{ $form->{taxdescription} }, $form->{"${item}_description"});
1122       push(@{ $form->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
1123       push(@{ $form->{taxnumber} }, $form->{"${item}_taxnumber"});
1124     }
1125   }
1126
1127   map { $form->{$_} = $form->format_amount($myconfig, $form->{$_}) } qw(totalqty totalship totalweight);
1128   $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1129   $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
1130
1131   use SL::CP;
1132   my $c;
1133   if ($form->{language_code}) {
1134     $c = new CP $form->{language_code};
1135   } else {
1136     $c = new CP $myconfig->{countrycode};
1137   }
1138   $c->init;
1139   my $whole;
1140   ($whole, $form->{decimal}) = split /\./, $form->{ordtotal};
1141   $form->{decimal} .= "00";
1142   $form->{decimal} = substr($form->{decimal}, 0, 2);
1143   $form->{text_amount} = $c->num2text($whole);
1144   
1145   # format amounts
1146   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1147
1148   $dbh->disconnect;
1149
1150 }
1151
1152
1153 sub assembly_details {
1154   my ($dbh, $form, $id, $oid, $qty) = @_;
1155
1156   my $sm = "";
1157   my $spacer;
1158
1159   $form->{stagger}++;
1160   if ($form->{format} eq 'html') {
1161     $spacer = "&nbsp;" x (3 * ($form->{stagger} - 1)) if $form->{stagger} > 1;
1162   }
1163   if ($form->{format} =~ /(postscript|pdf)/) {
1164     if ($form->{stagger} > 1) {
1165       $spacer = ($form->{stagger} - 1) * 3;
1166       $spacer = '\rule{'.$spacer.'mm}{0mm}';
1167     }
1168   }
1169
1170   # get parts and push them onto the stack
1171   my $sortorder = "";
1172   
1173   if ($form->{grouppartsgroup}) {
1174     $sortorder = qq|ORDER BY pg.partsgroup, a.$oid|;
1175   } else {
1176     $sortorder = qq|ORDER BY a.$oid|;
1177   }
1178   
1179   my $where = ($form->{formname} eq 'work_order') ? "1 = 1" : "a.bom = '1'";
1180   
1181   my $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty,
1182                  pg.partsgroup, p.partnumber AS sku, p.assembly, p.id, p.bin
1183                  FROM assembly a
1184                  JOIN parts p ON (a.parts_id = p.id)
1185                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1186                  WHERE $where
1187                  AND a.id = '$id'
1188                  $sortorder|;
1189   my $sth = $dbh->prepare($query);
1190   $sth->execute || $form->dberror($query);
1191
1192   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1193    
1194     if ($form->{grouppartsgroup} && $ref->{partsgroup} ne $sm) {
1195       map { push(@{ $form->{$_} }, "") } qw(number sku unit qty runningnumber ship bin serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate linetotal);
1196       $sm = ($ref->{partsgroup}) ? $ref->{partsgroup} : "";
1197       push(@{ $form->{description} }, "$spacer$sm");
1198     }
1199     
1200     if ($form->{stagger}) {
1201       push(@{ $form->{description} }, qq|$spacer$ref->{sku}, $ref->{description}|);
1202       map { push(@{ $form->{$_} }, "") } qw(number sku runningnumber ship serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate linetotal);
1203     } else {
1204       push(@{ $form->{description} }, qq|$ref->{description}|);
1205       push(@{ $form->{sku} }, $ref->{partnumber});
1206       push(@{ $form->{number} }, $ref->{partnumber});
1207       
1208       map { push(@{ $form->{$_} }, "") } qw(runningnumber ship serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate linetotal);
1209     }
1210       
1211     push(@{ $form->{qty} }, $form->format_amount($myconfig, $ref->{qty} * $qty));
1212     map { push(@{ $form->{$_} }, $ref->{$_}) } qw(unit bin);
1213
1214     
1215     if ($ref->{assembly} && $form->{formname} eq 'work_order') {
1216       &assembly_details($dbh, $form, $ref->{id}, $oid, $ref->{qty} * $qty);
1217     }
1218     
1219   }
1220   $sth->finish;
1221
1222   $form->{stagger}--;
1223   
1224 }
1225
1226
1227 sub project_description {
1228   my ($self, $dbh, $id) = @_;
1229
1230   my $query = qq|SELECT description
1231                  FROM project
1232                  WHERE id = $id|;
1233   ($_) = $dbh->selectrow_array;
1234   
1235   $_;
1236
1237 }
1238
1239
1240 sub get_warehouses {
1241   my ($self, $myconfig, $form) = @_;
1242   
1243   my $dbh = $form->dbconnect($myconfig);
1244   # setup warehouses
1245   my $query = qq|SELECT id, description
1246                  FROM warehouse
1247                  ORDER BY 2|;
1248
1249   my $sth = $dbh->prepare($query);
1250   $sth->execute || $form->dberror($query);
1251
1252   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1253     push @{ $form->{all_warehouses} }, $ref;
1254   }
1255   $sth->finish;
1256
1257   $dbh->disconnect;
1258
1259 }
1260
1261
1262 sub save_inventory {
1263   my ($self, $myconfig, $form) = @_;
1264   
1265   my ($null, $warehouse_id) = split /--/, $form->{warehouse};
1266   $warehouse_id *= 1;
1267
1268   my $ml = ($form->{type} eq 'ship_order') ? -1 : 1;
1269   
1270   my $dbh = $form->dbconnect_noauto($myconfig);
1271   my $sth;
1272   my $wth;
1273   my $serialnumber;
1274   my $ship;
1275   
1276   my $employee_id;
1277   ($null, $employee_id) = split /--/, $form->{employee};
1278   ($null, $employee_id) = $form->get_employee($dbh) if ! $employee_id;
1279  
1280   $query = qq|SELECT serialnumber, ship
1281               FROM orderitems
1282               WHERE trans_id = ?
1283               AND id = ?
1284               FOR UPDATE|;
1285   $sth = $dbh->prepare($query) || $form->dberror($query);
1286
1287   $query = qq|SELECT sum(qty)
1288               FROM inventory
1289               WHERE parts_id = ?
1290               AND warehouse_id = ?|;
1291   $wth = $dbh->prepare($query) || $form->dberror($query);
1292   
1293
1294   for my $i (1 .. $form->{rowcount}) {
1295
1296     $ship = (abs($form->{"ship_$i"}) > abs($form->{"qty_$i"})) ? $form->{"qty_$i"} : $form->{"ship_$i"};
1297     
1298     if ($warehouse_id && $form->{type} eq 'ship_order') {
1299
1300       $wth->execute($form->{"id_$i"}, $warehouse_id) || $form->dberror;
1301
1302       ($qty) = $wth->fetchrow_array;
1303       $wth->finish;
1304
1305       if ($ship > $qty) {
1306         $ship = $qty;
1307       }
1308     }
1309
1310     
1311     if ($ship != 0) {
1312
1313       $ship *= $ml;
1314       $query = qq|INSERT INTO inventory (parts_id, warehouse_id,
1315                   qty, oe_id, orderitems_id, shippingdate, employee_id)
1316                   VALUES ($form->{"id_$i"}, $warehouse_id,
1317                   $ship, $form->{"id"},
1318                   $form->{"orderitems_id_$i"}, '$form->{shippingdate}',
1319                   $employee_id)|;
1320       $dbh->do($query) || $form->dberror($query);
1321      
1322       # add serialnumber, ship to orderitems
1323       $sth->execute($form->{id}, $form->{"orderitems_id_$i"}) || $form->dberror;
1324       ($serialnumber, $ship) = $sth->fetchrow_array;
1325       $sth->finish;
1326
1327       $serialnumber .= " " if $serialnumber;
1328       $serialnumber .= qq|$form->{"serialnumber_$i"}|;
1329       $ship += $form->{"ship_$i"};
1330
1331       $query = qq|UPDATE orderitems SET
1332                   serialnumber = '$serialnumber',
1333                   ship = $ship,
1334                   reqdate = '$form->{shippingdate}'
1335                   WHERE trans_id = $form->{id}
1336                   AND id = $form->{"orderitems_id_$i"}|;
1337       $dbh->do($query) || $form->dberror($query);
1338       
1339       
1340       # update order with ship via
1341       $query = qq|UPDATE oe SET
1342                   shippingpoint = '$form->{shippingpoint}',
1343                   shipvia = '$form->{shipvia}'
1344                   WHERE id = $form->{id}|;
1345       $dbh->do($query) || $form->dberror($query);
1346       
1347                   
1348       # update onhand for parts
1349       $form->update_balance($dbh,
1350                             "parts",
1351                             "onhand",
1352                             qq|id = $form->{"id_$i"}|,
1353                             $form->{"ship_$i"} * $ml);
1354
1355     }
1356   }
1357
1358   my $rc = $dbh->commit;
1359   $dbh->disconnect;
1360
1361   $rc;
1362
1363 }
1364
1365
1366 sub adj_onhand {
1367   my ($dbh, $form, $ml) = @_;
1368
1369   my $query = qq|SELECT oi.parts_id, oi.ship, p.inventory_accno_id, p.assembly
1370                  FROM orderitems oi
1371                  JOIN parts p ON (p.id = oi.parts_id)
1372                  WHERE oi.trans_id = $form->{id}|;
1373   my $sth = $dbh->prepare($query);
1374   $sth->execute || $form->dberror($query);
1375
1376   $query = qq|SELECT sum(p.inventory_accno_id)
1377               FROM parts p
1378               JOIN assembly a ON (a.parts_id = p.id)
1379               WHERE a.id = ?|;
1380   my $ath = $dbh->prepare($query) || $form->dberror($query);
1381
1382   my $ispa;
1383   my $ref;
1384   
1385   while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1386
1387     if ($ref->{inventory_accno_id} || $ref->{assembly}) {
1388
1389       # do not update if assembly consists of all services
1390       if ($ref->{assembly}) {
1391         $ath->execute($ref->{parts_id}) || $form->dberror($query);
1392
1393         ($ispa) = $ath->fetchrow_array;
1394         $ath->finish;
1395
1396         next unless $ispa;
1397         
1398       }
1399
1400       # adjust onhand in parts table
1401       $form->update_balance($dbh,
1402                             "parts",
1403                             "onhand",
1404                             qq|id = $ref->{parts_id}|,
1405                             $ref->{ship} * $ml);
1406     }
1407   }
1408   
1409   $sth->finish;
1410
1411 }
1412
1413
1414 sub adj_inventory {
1415   my ($dbh, $myconfig, $form) = @_;
1416
1417   my %oid = ( 'Pg'      => 'oid',
1418               'PgPP'    => 'oid',
1419               'Oracle'  => 'rowid',
1420               'DB2'     => '1=1'
1421             );
1422   
1423   # increase/reduce qty in inventory table
1424   my $query = qq|SELECT oi.id, oi.parts_id, oi.ship
1425                  FROM orderitems oi
1426                  WHERE oi.trans_id = $form->{id}|;
1427   my $sth = $dbh->prepare($query);
1428   $sth->execute || $form->dberror($query);
1429
1430   $query = qq|SELECT $oid{$myconfig->{dbdriver}} AS oid, qty,
1431                      (SELECT SUM(qty) FROM inventory
1432                       WHERE oe_id = $form->{id}
1433                       AND orderitems_id = ?) AS total
1434               FROM inventory
1435               WHERE oe_id = $form->{id}
1436               AND orderitems_id = ?|;
1437   my $ith = $dbh->prepare($query) || $form->dberror($query);
1438   
1439   my $qty;
1440   my $ml = ($form->{type} =~ /(ship|sales)_order/) ? -1 : 1;
1441   
1442   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1443
1444     $ith->execute($ref->{id}, $ref->{id}) || $form->dberror($query);
1445
1446     while (my $inv = $ith->fetchrow_hashref(NAME_lc)) {
1447
1448       if (($qty = (($inv->{total} * $ml) - $ref->{ship})) >= 0) {
1449         $qty = $inv->{qty} if ($qty > ($inv->{qty} * $ml));
1450         
1451         $form->update_balance($dbh,
1452                               "inventory",
1453                               "qty",
1454                               qq|$oid{$myconfig->{dbdriver}} = $inv->{oid}|,
1455                               $qty * -1 * $ml);
1456       }
1457     }
1458     $ith->finish;
1459
1460   }
1461   $sth->finish;
1462
1463   # delete inventory entries if qty = 0
1464   $query = qq|DELETE FROM inventory
1465               WHERE oe_id = $form->{id}
1466               AND qty = 0|;
1467   $dbh->do($query) || $form->dberror($query);
1468
1469 }
1470
1471
1472 sub get_inventory {
1473   my ($self, $myconfig, $form) = @_;
1474   
1475   my ($null, $warehouse_id) = split /--/, $form->{warehouse};
1476   $warehouse_id *= 1;
1477
1478   my $dbh = $form->dbconnect($myconfig);
1479   
1480   my $query = qq|SELECT p.id, p.partnumber, p.description, p.onhand,
1481                  pg.partsgroup
1482                  FROM parts p
1483                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1484                  WHERE p.onhand > 0|;
1485
1486   if ($form->{partnumber}) {
1487     $var = $form->like(lc $form->{partnumber});
1488     $query .= "
1489                  AND lower(p.partnumber) LIKE '$var'";
1490   }
1491   if ($form->{description}) {
1492     $var = $form->like(lc $form->{description});
1493     $query .= "
1494                  AND lower(p.description) LIKE '$var'";
1495   }
1496   if ($form->{partsgroup}) {
1497     $var = $form->like(lc $form->{partsgroup});
1498     $query .= "
1499                  AND lower(pg.partsgroup) LIKE '$var'";
1500   }
1501   
1502   $sth = $dbh->prepare($query);
1503   $sth->execute || $form->dberror($query);
1504   
1505
1506   $query = qq|SELECT sum(i.qty), w.description, w.id
1507               FROM inventory i
1508               LEFT JOIN warehouse w ON (w.id = i.warehouse_id)
1509               WHERE i.parts_id = ?
1510               AND i.warehouse_id != $warehouse_id
1511               GROUP BY w.description, w.id|;
1512   $wth = $dbh->prepare($query) || $form->dberror($query);
1513
1514   while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1515     
1516     $wth->execute($ref->{id}) || $form->dberror;
1517     
1518     while (($qty, $warehouse, $warehouse_id) = $wth->fetchrow_array) {
1519       push @{ $form->{all_inventory} }, {'id' => $ref->{id},
1520                                          'partnumber' => $ref->{partnumber},
1521                                          'description' => $ref->{description},
1522                                          'partsgroup' => $ref->{partsgroup},
1523                                          'qty' => $qty,
1524                                          'warehouse_id' => $warehouse_id,
1525                                          'warehouse' => $warehouse} if $qty > 0;
1526     }
1527     $wth->finish;
1528   }
1529   $sth->finish;
1530
1531   $dbh->disconnect;
1532
1533   # sort inventory
1534   @{ $form->{all_inventory} } = sort { $a->{$form->{sort}} cmp $b->{$form->{sort}} } @{ $form->{all_inventory} };
1535
1536 }
1537
1538
1539 sub transfer {
1540   my ($self, $myconfig, $form) = @_;
1541   
1542   my $dbh = $form->dbconnect_noauto($myconfig);
1543   
1544   my $query = qq|INSERT INTO inventory
1545                  (warehouse_id, parts_id, qty, shippingdate, employee_id)
1546                  VALUES (?, ?, ?, ?, ?)|;
1547   $sth = $dbh->prepare($query) || $form->dberror($query);
1548
1549   ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
1550
1551   my @a = localtime; $a[5] += 1900; $a[4]++;
1552   $shippingdate = "$a[5]-$a[4]-$a[3]";
1553
1554   for my $i (1 .. $form->{rowcount}) {
1555     $qty = $form->parse_amount($myconfig, $form->{"transfer_$i"});
1556
1557     $qty = $form->{"qty_$i"} if ($qty > $form->{"qty_$i"});
1558     
1559     if ($qty) {
1560       # to warehouse
1561       $sth->execute($form->{warehouse_id}, $form->{"id_$i"}, $qty, $shippingdate, $form->{employee_id}) || $form->dberror;
1562
1563       $sth->finish;
1564       
1565       # from warehouse
1566       $sth->execute($form->{"warehouse_id_$i"}, $form->{"id_$i"}, $qty * -1, $shippingdate, $form->{employee_id}) || $form->dberror;
1567
1568       $sth->finish;
1569     }
1570   }
1571
1572   my $rc = $dbh->commit;
1573   $dbh->disconnect;
1574
1575   $rc;
1576
1577 }
1578
1579
1580 1;
1581