summaryrefslogtreecommitdiff
path: root/LedgerSMB/IC.pm
blob: 52ad9654d3ac94411786b0be6d86612987a745ef (plain)
  1. #=====================================================================
  2. # LedgerSMB
  3. # Small Medium Business Accounting software
  4. #
  5. # See COPYRIGHT file for copyright information
  6. #======================================================================
  7. #
  8. # This file has NOT undergone whitespace cleanup.
  9. #
  10. #======================================================================
  11. #
  12. # Inventory Control backend
  13. #
  14. #======================================================================
  15. package IC;
  16. sub get_part {
  17. my ($self, $myconfig, $form) = @_;
  18. # connect to db
  19. my $dbh = $form->dbconnect($myconfig);
  20. my $i;
  21. my $query = qq|SELECT p.*,
  22. c1.accno AS inventory_accno, c1.description AS inventory_description,
  23. c2.accno AS income_accno, c2.description AS income_description,
  24. c3.accno AS expense_accno, c3.description AS expense_description,
  25. pg.partsgroup
  26. FROM parts p
  27. LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
  28. LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
  29. LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
  30. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  31. WHERE p.id = $form->{id}|;
  32. my $sth = $dbh->prepare($query);
  33. $sth->execute || $form->dberror($query);
  34. my $ref = $sth->fetchrow_hashref(NAME_lc);
  35. # copy to $form variables
  36. for (keys %$ref) { $form->{$_} = $ref->{$_} }
  37. $sth->finish;
  38. my %oid = ('Pg' => 'a.oid',
  39. 'PgPP' => 'a.oid',
  40. 'Oracle' => 'a.rowid',
  41. 'DB2' => '1=1'
  42. );
  43. # part, service item or labor
  44. $form->{item} = ($form->{inventory_accno_id}) ? 'part' : 'service';
  45. $form->{item} = 'labor' if ! $form->{income_accno_id};
  46. if ($form->{assembly}) {
  47. $form->{item} = 'assembly';
  48. # retrieve assembly items
  49. $query = qq|SELECT p.id, p.partnumber, p.description,
  50. p.sellprice, p.weight, a.qty, a.bom, a.adj, p.unit,
  51. p.lastcost, p.listprice,
  52. pg.partsgroup, p.assembly, p.partsgroup_id
  53. FROM parts p
  54. JOIN assembly a ON (a.parts_id = p.id)
  55. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  56. WHERE a.id = $form->{id}
  57. ORDER BY $oid{$myconfig->{dbdriver}}|;
  58. $sth = $dbh->prepare($query);
  59. $sth->execute || $form->dberror($query);
  60. $form->{assembly_rows} = 0;
  61. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  62. $form->{assembly_rows}++;
  63. foreach my $key ( keys %{ $ref } ) {
  64. $form->{"${key}_$form->{assembly_rows}"} = $ref->{$key};
  65. }
  66. }
  67. $sth->finish;
  68. }
  69. # setup accno hash for <option checked> {amount} is used in create_links
  70. for (qw(inventory income expense)) { $form->{amount}{"IC_$_"} = { accno => $form->{"${_}_accno"}, description => $form->{"${_}_description"} } }
  71. if ($form->{item} =~ /(part|assembly)/) {
  72. # get makes
  73. if ($form->{makemodel} ne "") {
  74. $query = qq|SELECT make, model
  75. FROM makemodel
  76. WHERE parts_id = $form->{id}|;
  77. $sth = $dbh->prepare($query);
  78. $sth->execute || $form->dberror($query);
  79. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  80. push @{ $form->{makemodels} }, $ref;
  81. }
  82. $sth->finish;
  83. }
  84. }
  85. # now get accno for taxes
  86. $query = qq|SELECT c.accno
  87. FROM chart c, partstax pt
  88. WHERE pt.chart_id = c.id
  89. AND pt.parts_id = $form->{id}|;
  90. $sth = $dbh->prepare($query);
  91. $sth->execute || $form->dberror($query);
  92. while (($key) = $sth->fetchrow_array) {
  93. $form->{amount}{$key} = $key;
  94. }
  95. $sth->finish;
  96. # is it an orphan
  97. $query = qq|SELECT parts_id
  98. FROM invoice
  99. WHERE parts_id = $form->{id}
  100. UNION
  101. SELECT parts_id
  102. FROM orderitems
  103. WHERE parts_id = $form->{id}
  104. UNION
  105. SELECT parts_id
  106. FROM assembly
  107. WHERE parts_id = $form->{id}
  108. UNION
  109. SELECT parts_id
  110. FROM jcitems
  111. WHERE parts_id = $form->{id}|;
  112. ($form->{orphaned}) = $dbh->selectrow_array($query);
  113. $form->{orphaned} = !$form->{orphaned};
  114. $form->{orphaned} = 0 if $form->{project_id};
  115. if ($form->{item} eq 'assembly') {
  116. if ($form->{orphaned}) {
  117. $form->{orphaned} = !$form->{onhand};
  118. }
  119. }
  120. if ($form->{item} =~ /(part|service)/) {
  121. # get vendors
  122. $query = qq|SELECT v.id, v.name, pv.partnumber,
  123. pv.lastcost, pv.leadtime, pv.curr AS vendorcurr
  124. FROM partsvendor pv
  125. JOIN vendor v ON (v.id = pv.vendor_id)
  126. WHERE pv.parts_id = $form->{id}
  127. ORDER BY 2|;
  128. $sth = $dbh->prepare($query);
  129. $sth->execute || $form->dberror($query);
  130. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  131. push @{ $form->{vendormatrix} }, $ref;
  132. }
  133. $sth->finish;
  134. }
  135. # get matrix
  136. if ($form->{item} ne 'labor') {
  137. $query = qq|SELECT pc.pricebreak, pc.sellprice AS customerprice,
  138. pc.curr AS customercurr,
  139. pc.validfrom, pc.validto,
  140. c.name, c.id AS cid, g.pricegroup, g.id AS gid
  141. FROM partscustomer pc
  142. LEFT JOIN customer c ON (c.id = pc.customer_id)
  143. LEFT JOIN pricegroup g ON (g.id = pc.pricegroup_id)
  144. WHERE pc.parts_id = $form->{id}
  145. ORDER BY c.name, g.pricegroup, pc.pricebreak|;
  146. $sth = $dbh->prepare($query);
  147. $sth->execute || $form->dberror($query);
  148. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  149. push @{ $form->{customermatrix} }, $ref;
  150. }
  151. $sth->finish;
  152. }
  153. $dbh->disconnect;
  154. }
  155. sub save {
  156. my ($self, $myconfig, $form) = @_;
  157. ($form->{inventory_accno}) = split(/--/, $form->{IC_inventory});
  158. ($form->{expense_accno}) = split(/--/, $form->{IC_expense});
  159. ($form->{income_accno}) = split(/--/, $form->{IC_income});
  160. # connect to database, turn off AutoCommit
  161. my $dbh = $form->dbconnect_noauto($myconfig);
  162. # save the part
  163. # make up a unique handle and store in partnumber field
  164. # then retrieve the record based on the unique handle to get the id
  165. # replace the partnumber field with the actual variable
  166. # add records for makemodel
  167. # if there is a $form->{id} then replace the old entry
  168. # delete all makemodel entries and add the new ones
  169. # undo amount formatting
  170. for (qw(rop weight listprice sellprice lastcost stock)) { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
  171. $form->{makemodel} = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
  172. $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
  173. for (qw(alternate obsolete onhand)) { $form->{$_} *= 1 }
  174. my $query;
  175. my $sth;
  176. my $i;
  177. my $null;
  178. my $vendor_id;
  179. my $customer_id;
  180. if ($form->{id}) {
  181. # get old price
  182. $query = qq|SELECT id, listprice, sellprice, lastcost, weight, project_id
  183. FROM parts
  184. WHERE id = $form->{id}|;
  185. my ($id, $listprice, $sellprice, $lastcost, $weight, $project_id) = $dbh->selectrow_array($query);
  186. if ($id) {
  187. if (!$project_id) {
  188. # if item is part of an assembly adjust all assemblies
  189. $query = qq|SELECT id, qty, adj
  190. FROM assembly
  191. WHERE parts_id = $form->{id}|;
  192. $sth = $dbh->prepare($query);
  193. $sth->execute || $form->dberror($query);
  194. while (my ($id, $qty, $adj) = $sth->fetchrow_array) {
  195. &update_assembly($dbh, $form, $id, $qty, $adj, $listprice * 1, $sellprice * 1, $lastcost * 1, $weight * 1);
  196. }
  197. $sth->finish;
  198. }
  199. if ($form->{item} =~ /(part|service)/) {
  200. # delete partsvendor records
  201. $query = qq|DELETE FROM partsvendor
  202. WHERE parts_id = $form->{id}|;
  203. $dbh->do($query) || $form->dberror($query);
  204. }
  205. if ($form->{item} !~ /(service|labor)/) {
  206. # delete makemodel records
  207. $query = qq|DELETE FROM makemodel
  208. WHERE parts_id = $form->{id}|;
  209. $dbh->do($query) || $form->dberror($query);
  210. }
  211. if ($form->{item} eq 'assembly') {
  212. if ($form->{onhand}) {
  213. &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
  214. }
  215. if ($form->{orphaned}) {
  216. # delete assembly records
  217. $query = qq|DELETE FROM assembly
  218. WHERE id = $form->{id}|;
  219. $dbh->do($query) || $form->dberror($query);
  220. } else {
  221. for $i (1 .. $form->{assembly_rows} - 1) {
  222. # update BOM, A only
  223. for (qw(bom adj)) { $form->{"${_}_$i"} *= 1 }
  224. $query = qq|UPDATE assembly SET
  225. bom = '$form->{"bom_$i"}',
  226. adj = '$form->{"adj_$i"}'
  227. WHERE id = $form->{id}
  228. AND parts_id = $form->{"id_$i"}|;
  229. $dbh->do($query) || $form->dberror($query);
  230. }
  231. }
  232. $form->{onhand} += $form->{stock};
  233. }
  234. # delete tax records
  235. $query = qq|DELETE FROM partstax
  236. WHERE parts_id = $form->{id}|;
  237. $dbh->do($query) || $form->dberror($query);
  238. # delete matrix
  239. $query = qq|DELETE FROM partscustomer
  240. WHERE parts_id = $form->{id}|;
  241. $dbh->do($query) || $form->dberror($query);
  242. } else {
  243. $query = qq|INSERT INTO parts (id)
  244. VALUES ($form->{id})|;
  245. $dbh->do($query) || $form->dberror($query);
  246. }
  247. }
  248. if (!$form->{id}) {
  249. my $uid = localtime;
  250. $uid .= "$$";
  251. $query = qq|INSERT INTO parts (partnumber)
  252. VALUES ('$uid')|;
  253. $dbh->do($query) || $form->dberror($query);
  254. $query = qq|SELECT id FROM parts
  255. WHERE partnumber = '$uid'|;
  256. $sth = $dbh->prepare($query);
  257. $sth->execute || $form->dberror($query);
  258. ($form->{id}) = $sth->fetchrow_array;
  259. $sth->finish;
  260. $form->{orphaned} = 1;
  261. $form->{onhand} = ($form->{stock} * 1) if $form->{item} eq 'assembly';
  262. }
  263. my $partsgroup_id;
  264. ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
  265. $partsgroup_id *= 1;
  266. $form->{partnumber} = $form->update_defaults($myconfig, "partnumber", $dbh) if ! $form->{partnumber};
  267. $query = qq|UPDATE parts SET
  268. partnumber = |.$dbh->quote($form->{partnumber}).qq|,
  269. description = |.$dbh->quote($form->{description}).qq|,
  270. makemodel = '$form->{makemodel}',
  271. alternate = '$form->{alternate}',
  272. assembly = '$form->{assembly}',
  273. listprice = $form->{listprice},
  274. sellprice = $form->{sellprice},
  275. lastcost = $form->{lastcost},
  276. weight = $form->{weight},
  277. priceupdate = |.$form->dbquote($form->{priceupdate}, SQL_DATE).qq|,
  278. unit = |.$dbh->quote($form->{unit}).qq|,
  279. notes = |.$dbh->quote($form->{notes}).qq|,
  280. rop = $form->{rop},
  281. bin = |.$dbh->quote($form->{bin}).qq|,
  282. inventory_accno_id = (SELECT id FROM chart
  283. WHERE accno = '$form->{inventory_accno}'),
  284. income_accno_id = (SELECT id FROM chart
  285. WHERE accno = '$form->{income_accno}'),
  286. expense_accno_id = (SELECT id FROM chart
  287. WHERE accno = '$form->{expense_accno}'),
  288. obsolete = '$form->{obsolete}',
  289. image = '$form->{image}',
  290. drawing = '$form->{drawing}',
  291. microfiche = '$form->{microfiche}',
  292. partsgroup_id = $partsgroup_id
  293. WHERE id = $form->{id}|;
  294. $dbh->do($query) || $form->dberror($query);
  295. # insert makemodel records
  296. if ($form->{item} =~ /(part|assembly)/) {
  297. for $i (1 .. $form->{makemodel_rows}) {
  298. if (($form->{"make_$i"} ne "") || ($form->{"model_$i"} ne "")) {
  299. $query = qq|INSERT INTO makemodel (parts_id, make, model)
  300. VALUES ($form->{id},|
  301. .$dbh->quote($form->{"make_$i"}).qq|, |
  302. .$dbh->quote($form->{"model_$i"}).qq|)|;
  303. $dbh->do($query) || $form->dberror($query);
  304. }
  305. }
  306. }
  307. # insert taxes
  308. for (split / /, $form->{taxaccounts}) {
  309. if ($form->{"IC_tax_$_"}) {
  310. $query = qq|INSERT INTO partstax (parts_id, chart_id)
  311. VALUES ($form->{id},
  312. (SELECT id
  313. FROM chart
  314. WHERE accno = '$_'))|;
  315. $dbh->do($query) || $form->dberror($query);
  316. }
  317. }
  318. @a = localtime;
  319. $a[5] += 1900;
  320. $a[4]++;
  321. $a[4] = substr("0$a[4]", -2);
  322. $a[3] = substr("0$a[3]", -2);
  323. my $shippingdate = "$a[5]$a[4]$a[3]";
  324. ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
  325. # add assembly records
  326. if ($form->{item} eq 'assembly' && !$project_id) {
  327. if ($form->{orphaned}) {
  328. for $i (1 .. $form->{assembly_rows}) {
  329. $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
  330. if ($form->{"qty_$i"}) {
  331. for (qw(bom adj)) { $form->{"${_}_$i"} *= 1 }
  332. $query = qq|INSERT INTO assembly (id, parts_id, qty, bom, adj)
  333. VALUES ($form->{id}, $form->{"id_$i"},
  334. $form->{"qty_$i"}, '$form->{"bom_$i"}',
  335. '$form->{"adj_$i"}')|;
  336. $dbh->do($query) || $form->dberror($query);
  337. }
  338. }
  339. }
  340. # adjust onhand for the parts
  341. if ($form->{onhand}) {
  342. &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
  343. }
  344. }
  345. # add vendors
  346. if ($form->{item} ne 'assembly') {
  347. $updparts{$form->{id}} = 1;
  348. for $i (1 .. $form->{vendor_rows}) {
  349. if (($form->{"vendor_$i"} ne "") && $form->{"lastcost_$i"}) {
  350. ($null, $vendor_id) = split /--/, $form->{"vendor_$i"};
  351. for (qw(lastcost leadtime)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})}
  352. $query = qq|INSERT INTO partsvendor (vendor_id, parts_id, partnumber,
  353. lastcost, leadtime, curr)
  354. VALUES ($vendor_id, $form->{id},|
  355. .$dbh->quote($form->{"partnumber_$i"}).qq|,
  356. $form->{"lastcost_$i"},
  357. $form->{"leadtime_$i"}, '$form->{"vendorcurr_$i"}')|;
  358. $dbh->do($query) || $form->dberror($query);
  359. }
  360. }
  361. }
  362. # add pricematrix
  363. for $i (1 .. $form->{customer_rows}) {
  364. for (qw(pricebreak customerprice)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})}
  365. if ($form->{"customerprice_$i"}) {
  366. ($null, $customer_id) = split /--/, $form->{"customer_$i"};
  367. $customer_id *= 1;
  368. ($null, $pricegroup_id) = split /--/, $form->{"pricegroup_$i"};
  369. $pricegroup_id *= 1;
  370. $query = qq|INSERT INTO partscustomer (parts_id, customer_id,
  371. pricegroup_id, pricebreak, sellprice, curr,
  372. validfrom, validto)
  373. VALUES ($form->{id}, $customer_id,
  374. $pricegroup_id, $form->{"pricebreak_$i"},
  375. $form->{"customerprice_$i"}, '$form->{"customercurr_$i"}',|
  376. .$form->dbquote($form->{"validfrom_$i"}, SQL_DATE).qq|, |
  377. .$form->dbquote($form->{"validto_$i"}, SQL_DATE).qq|)|;
  378. $dbh->do($query) || $form->dberror($query);
  379. }
  380. }
  381. # commit
  382. my $rc = $dbh->commit;
  383. $dbh->disconnect;
  384. $rc;
  385. }
  386. sub update_assembly {
  387. my ($dbh, $form, $id, $qty, $adj, $listprice, $sellprice, $lastcost, $weight) = @_;
  388. my $formlistprice = $form->{listprice};
  389. my $formsellprice = $form->{sellprice};
  390. if (!$adj) {
  391. $formlistprice = $listprice;
  392. $formsellprice = $sellprice;
  393. }
  394. my $query = qq|SELECT id, qty, adj
  395. FROM assembly
  396. WHERE parts_id = $id|;
  397. my $sth = $dbh->prepare($query);
  398. $sth->execute || $form->dberror($query);
  399. $form->{$id} = 1;
  400. while (my ($pid, $aqty, $aadj) = $sth->fetchrow_array) {
  401. &update_assembly($dbh, $form, $pid, $aqty * $qty, $aadj, $listprice, $sellprice, $lastcost, $weight) if !$form->{$pid};
  402. }
  403. $sth->finish;
  404. $query = qq|UPDATE parts
  405. SET listprice = listprice +
  406. $qty * ($formlistprice - $listprice),
  407. sellprice = sellprice +
  408. $qty * ($formsellprice - $sellprice),
  409. lastcost = lastcost +
  410. $qty * ($form->{lastcost} - $lastcost),
  411. weight = weight +
  412. $qty * ($form->{weight} - $weight)
  413. WHERE id = $id|;
  414. $dbh->do($query) || $form->dberror($query);
  415. delete $form->{$id};
  416. }
  417. sub retrieve_assemblies {
  418. my ($self, $myconfig, $form) = @_;
  419. # connect to database
  420. my $dbh = $form->dbconnect($myconfig);
  421. my $where = '1 = 1';
  422. if ($form->{partnumber} ne "") {
  423. my $partnumber = $form->like(lc $form->{partnumber});
  424. $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
  425. }
  426. if ($form->{description} ne "") {
  427. my $description = $form->like(lc $form->{description});
  428. $where .= " AND lower(p.description) LIKE '$description'";
  429. }
  430. $where .= qq| AND p.obsolete = '0'
  431. AND p.project_id IS NULL|;
  432. my %ordinal = ( 'partnumber' => 2,
  433. 'description' => 3,
  434. 'bin' => 4
  435. );
  436. my @a = qw(partnumber description bin);
  437. my $sortorder = $form->sort_order(\@a, \%ordinal);
  438. # retrieve assembly items
  439. my $query = qq|SELECT p.id, p.partnumber, p.description,
  440. p.bin, p.onhand, p.rop
  441. FROM parts p
  442. WHERE $where
  443. AND p.assembly = '1'
  444. ORDER BY $sortorder|;
  445. my $sth = $dbh->prepare($query);
  446. $sth->execute || $form->dberror($query);
  447. $query = qq|SELECT sum(p.inventory_accno_id), p.assembly
  448. FROM parts p
  449. JOIN assembly a ON (a.parts_id = p.id)
  450. WHERE a.id = ?
  451. GROUP BY p.assembly|;
  452. my $svh = $dbh->prepare($query) || $form->dberror($query);
  453. my $inh;
  454. if ($form->{checkinventory}) {
  455. $query = qq|SELECT p.id, p.onhand, a.qty
  456. FROM parts p
  457. JOIN assembly a ON (a.parts_id = p.id)
  458. WHERE (p.inventory_accno_id > 0 OR p.assembly)
  459. AND p.income_accno_id > 0
  460. AND a.id = ?|;
  461. $inh = $dbh->prepare($query) || $form->dberror($query);
  462. }
  463. my %available = ();
  464. my %required;
  465. my $ref;
  466. my $aref;
  467. my $stock;
  468. my $howmany;
  469. my $ok;
  470. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  471. $svh->execute($ref->{id});
  472. ($ref->{inventory}, $ref->{assembly}) = $svh->fetchrow_array;
  473. $svh->finish;
  474. if ($ref->{inventory} || $ref->{assembly}) {
  475. $ok = 1;
  476. if ($form->{checkinventory}) {
  477. $inh->execute($ref->{id}) || $form->dberror($query);;
  478. $ok = 0;
  479. %required = ();
  480. while ($aref = $inh->fetchrow_hashref(NAME_lc)) {
  481. $available{$aref->{id}} = (exists $available{$aref->{id}}) ? $available{$aref->{id}} : $aref->{onhand};
  482. $required{$aref->{id}} = $aref->{qty};
  483. if ($available{$aref->{id}} >= $aref->{qty}) {
  484. $howmany = ($aref->{qty}) ? int $available{$aref->{id}}/$aref->{qty} : 1;
  485. if ($stock) {
  486. $stock = ($stock > $howmany) ? $howmany : $stock;
  487. } else {
  488. $stock = $howmany;
  489. }
  490. $ok = 1;
  491. $available{$aref->{id}} -= $aref->{qty} * $stock;
  492. } else {
  493. $ok = 0;
  494. for (keys %required) { $available{$_} += $required{$_} * $stock }
  495. $stock = 0;
  496. last;
  497. }
  498. }
  499. $inh->finish;
  500. $ref->{stock} = $stock;
  501. }
  502. push @{ $form->{assembly_items} }, $ref if $ok;
  503. }
  504. }
  505. $sth->finish;
  506. $dbh->disconnect;
  507. }
  508. sub restock_assemblies {
  509. my ($self, $myconfig, $form) = @_;
  510. # connect to database
  511. my $dbh = $form->dbconnect_noauto($myconfig);
  512. for my $i (1 .. $form->{rowcount}) {
  513. $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
  514. if ($form->{"qty_$i"}) {
  515. &adjust_inventory($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
  516. }
  517. }
  518. my $rc = $dbh->commit;
  519. $dbh->disconnect;
  520. $rc;
  521. }
  522. sub adjust_inventory {
  523. my ($dbh, $form, $id, $qty) = @_;
  524. my $query = qq|SELECT p.id, p.inventory_accno_id, p.assembly, a.qty
  525. FROM parts p
  526. JOIN assembly a ON (a.parts_id = p.id)
  527. WHERE a.id = $id|;
  528. my $sth = $dbh->prepare($query);
  529. $sth->execute || $form->dberror($query);
  530. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  531. # is it a service item then loop
  532. if (! $ref->{inventory_accno_id}) {
  533. next if ! $ref->{assembly}; # assembly
  534. }
  535. # adjust parts onhand
  536. $form->update_balance($dbh,
  537. "parts",
  538. "onhand",
  539. qq|id = $ref->{id}|,
  540. $qty * $ref->{qty} * -1);
  541. }
  542. $sth->finish;
  543. # update assembly
  544. $form->update_balance($dbh,
  545. "parts",
  546. "onhand",
  547. qq|id = $id|,
  548. $qty);
  549. }
  550. sub delete {
  551. my ($self, $myconfig, $form) = @_;
  552. # connect to database, turn off AutoCommit
  553. my $dbh = $form->dbconnect_noauto($myconfig);
  554. my $query;
  555. $query = qq|DELETE FROM parts
  556. WHERE id = $form->{id}|;
  557. $dbh->do($query) || $form->dberror($query);
  558. $query = qq|DELETE FROM partstax
  559. WHERE parts_id = $form->{id}|;
  560. $dbh->do($query) || $form->dberror($query);
  561. if ($form->{item} ne 'assembly') {
  562. $query = qq|DELETE FROM partsvendor
  563. WHERE parts_id = $form->{id}|;
  564. $dbh->do($query) || $form->dberror($query);
  565. }
  566. # check if it is a part, assembly or service
  567. if ($form->{item} ne 'service') {
  568. $query = qq|DELETE FROM makemodel
  569. WHERE parts_id = $form->{id}|;
  570. $dbh->do($query) || $form->dberror($query);
  571. }
  572. if ($form->{item} eq 'assembly') {
  573. $query = qq|DELETE FROM assembly
  574. WHERE id = $form->{id}|;
  575. $dbh->do($query) || $form->dberror($query);
  576. }
  577. if ($form->{item} eq 'alternate') {
  578. $query = qq|DELETE FROM alternate
  579. WHERE id = $form->{id}|;
  580. $dbh->do($query) || $form->dberror($query);
  581. }
  582. $query = qq|DELETE FROM inventory
  583. WHERE parts_id = $form->{id}|;
  584. $dbh->do($query) || $form->dberror($query);
  585. $query = qq|DELETE FROM partscustomer
  586. WHERE parts_id = $form->{id}|;
  587. $dbh->do($query) || $form->dberror($query);
  588. $query = qq|DELETE FROM translation
  589. WHERE trans_id = $form->{id}|;
  590. $dbh->do($query) || $form->dberror($query);
  591. # commit
  592. my $rc = $dbh->commit;
  593. $dbh->disconnect;
  594. $rc;
  595. }
  596. sub assembly_item {
  597. my ($self, $myconfig, $form) = @_;
  598. my $i = $form->{assembly_rows};
  599. my $var;
  600. my $null;
  601. my $where = "p.obsolete = '0'";
  602. if ($form->{"partnumber_$i"} ne "") {
  603. $var = $form->like(lc $form->{"partnumber_$i"});
  604. $where .= " AND lower(p.partnumber) LIKE '$var'";
  605. }
  606. if ($form->{"description_$i"} ne "") {
  607. $var = $form->like(lc $form->{"description_$i"});
  608. $where .= " AND lower(p.description) LIKE '$var'";
  609. }
  610. if ($form->{"partsgroup_$i"} ne "") {
  611. ($null, $var) = split /--/, $form->{"partsgroup_$i"};
  612. $where .= qq| AND p.partsgroup_id = $var|;
  613. }
  614. if ($form->{id}) {
  615. $where .= " AND p.id != $form->{id}";
  616. }
  617. if ($form->{"description_$i"} ne "") {
  618. $where .= " ORDER BY p.description";
  619. } else {
  620. $where .= " ORDER BY p.partnumber";
  621. }
  622. # connect to database
  623. my $dbh = $form->dbconnect($myconfig);
  624. my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
  625. p.weight, p.onhand, p.unit, p.lastcost,
  626. pg.partsgroup, p.partsgroup_id
  627. FROM parts p
  628. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  629. WHERE $where|;
  630. my $sth = $dbh->prepare($query);
  631. $sth->execute || $form->dberror($query);
  632. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  633. push @{ $form->{item_list} }, $ref;
  634. }
  635. $sth->finish;
  636. $dbh->disconnect;
  637. }
  638. sub all_parts {
  639. my ($self, $myconfig, $form) = @_;
  640. my $where = '1 = 1';
  641. my $null;
  642. my $var;
  643. my $ref;
  644. for (qw(partnumber drawing microfiche)) {
  645. if ($form->{$_} ne "") {
  646. $var = $form->like(lc $form->{$_});
  647. $where .= " AND lower(p.$_) LIKE '$var'";
  648. }
  649. }
  650. # special case for description
  651. if ($form->{description} ne "") {
  652. unless ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered} || $form->{rfq} || $form->{quoted}) {
  653. $var = $form->like(lc $form->{description});
  654. $where .= " AND lower(p.description) LIKE '$var'";
  655. }
  656. }
  657. # assembly components
  658. my $assemblyflds;
  659. if ($form->{searchitems} eq 'component') {
  660. $assemblyflds = qq|, p1.partnumber AS assemblypartnumber, a.id AS assembly_id|;
  661. }
  662. # special case for serialnumber
  663. if ($form->{l_serialnumber}) {
  664. if ($form->{serialnumber} ne "") {
  665. $var = $form->like(lc $form->{serialnumber});
  666. $where .= " AND lower(i.serialnumber) LIKE '$var'";
  667. }
  668. }
  669. if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
  670. $form->{l_warehouse} = 1;
  671. }
  672. if ($form->{searchitems} eq 'part') {
  673. $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id > 0";
  674. }
  675. if ($form->{searchitems} eq 'assembly') {
  676. $form->{bought} = "";
  677. $where .= " AND p.assembly = '1'";
  678. }
  679. if ($form->{searchitems} eq 'service') {
  680. $where .= " AND p.assembly = '0' AND p.inventory_accno_id IS NULL";
  681. }
  682. if ($form->{searchitems} eq 'labor') {
  683. $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id IS NULL";
  684. }
  685. # items which were never bought, sold or on an order
  686. if ($form->{itemstatus} eq 'orphaned') {
  687. $where .= qq| AND p.onhand = 0
  688. AND p.id NOT IN (SELECT p.id FROM parts p
  689. JOIN invoice i ON (p.id = i.parts_id))
  690. AND p.id NOT IN (SELECT p.id FROM parts p
  691. JOIN assembly a ON (p.id = a.parts_id))
  692. AND p.id NOT IN (SELECT p.id FROM parts p
  693. JOIN orderitems o ON (p.id = o.parts_id))
  694. AND p.id NOT IN (SELECT p.id FROM parts p
  695. JOIN jcitems j ON (p.id = j.parts_id))|;
  696. }
  697. if ($form->{itemstatus} eq 'active') {
  698. $where .= " AND p.obsolete = '0'";
  699. }
  700. if ($form->{itemstatus} eq 'obsolete') {
  701. $where .= " AND p.obsolete = '1'";
  702. }
  703. if ($form->{itemstatus} eq 'onhand') {
  704. $where .= " AND p.onhand > 0";
  705. }
  706. if ($form->{itemstatus} eq 'short') {
  707. $where .= " AND p.onhand < p.rop";
  708. }
  709. my $makemodelflds = qq|, '', ''|;;
  710. my $makemodeljoin;
  711. if (($form->{make} ne "") || $form->{l_make} || ($form->{model} ne "") || $form->{l_model}) {
  712. $makemodelflds = qq|, m.make, m.model|;
  713. $makemodeljoin = qq|LEFT JOIN makemodel m ON (m.parts_id = p.id)|;
  714. if ($form->{make} ne "") {
  715. $var = $form->like(lc $form->{make});
  716. $where .= " AND lower(m.make) LIKE '$var'";
  717. }
  718. if ($form->{model} ne "") {
  719. $var = $form->like(lc $form->{model});
  720. $where .= " AND lower(m.model) LIKE '$var'";
  721. }
  722. }
  723. if ($form->{partsgroup} ne "") {
  724. ($null, $var) = split /--/, $form->{partsgroup};
  725. $where .= qq| AND p.partsgroup_id = $var|;
  726. }
  727. # connect to database
  728. my $dbh = $form->dbconnect($myconfig);
  729. my %ordinal = ( 'partnumber' => 2,
  730. 'description' => 3,
  731. 'bin' => 6,
  732. 'priceupdate' => 13,
  733. 'drawing' => 15,
  734. 'microfiche' => 16,
  735. 'partsgroup' => 18,
  736. 'make' => 21,
  737. 'model' => 22,
  738. 'assemblypartnumber' => 23
  739. );
  740. my @a = qw(partnumber description);
  741. my $sortorder = $form->sort_order(\@a, \%ordinal);
  742. my $query = qq|SELECT curr FROM defaults|;
  743. my ($curr) = $dbh->selectrow_array($query);
  744. $curr =~ s/:.*//;
  745. my $flds = qq|p.id, p.partnumber, p.description, p.onhand, p.unit,
  746. p.bin, p.sellprice, p.listprice, p.lastcost, p.rop,
  747. p.avgcost,
  748. p.weight, p.priceupdate, p.image, p.drawing, p.microfiche,
  749. p.assembly, pg.partsgroup, '$curr' AS curr,
  750. c1.accno AS inventory, c2.accno AS income, c3.accno AS expense,
  751. p.notes
  752. $makemodelflds $assemblyflds
  753. |;
  754. $query = qq|SELECT $flds
  755. FROM parts p
  756. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  757. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  758. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  759. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  760. $makemodeljoin
  761. WHERE $where
  762. ORDER BY $sortorder|;
  763. # redo query for components report
  764. if ($form->{searchitems} eq 'component') {
  765. $flds =~ s/p.onhand/a.qty AS onhand/;
  766. $query = qq|SELECT $flds
  767. FROM assembly a
  768. JOIN parts p ON (a.parts_id = p.id)
  769. JOIN parts p1 ON (a.id = p1.id)
  770. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  771. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  772. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  773. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  774. $makemodeljoin
  775. WHERE $where
  776. ORDER BY $sortorder|;
  777. }
  778. # rebuild query for bought and sold items
  779. if ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered} || $form->{rfq} || $form->{quoted}) {
  780. $form->sort_order();
  781. @a = qw(partnumber description curr employee name serialnumber id);
  782. push @a, "invnumber" if ($form->{bought} || $form->{sold});
  783. push @a, "ordnumber" if ($form->{onorder} || $form->{ordered});
  784. push @a, "quonumber" if ($form->{rfq} || $form->{quoted});
  785. %ordinal = ( 'partnumber' => 2,
  786. 'description' => 3,
  787. 'serialnumber' => 4,
  788. 'bin' => 7,
  789. 'priceupdate' => 14,
  790. 'partsgroup' => 19,
  791. 'invnumber' => 20,
  792. 'ordnumber' => 21,
  793. 'quonumber' => 22,
  794. 'name' => 24,
  795. 'employee' => 25,
  796. 'curr' => 26,
  797. 'make' => 29,
  798. 'model' => 30
  799. );
  800. $sortorder = $form->sort_order(\@a, \%ordinal);
  801. my $union = "";
  802. $query = "";
  803. if ($form->{bought} || $form->{sold}) {
  804. my $invwhere = "$where";
  805. my $transdate = ($form->{method} eq 'accrual') ? "transdate" : "datepaid";
  806. $invwhere .= " AND i.assemblyitem = '0'";
  807. $invwhere .= " AND a.$transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
  808. $invwhere .= " AND a.$transdate <= '$form->{transdateto}'" if $form->{transdateto};
  809. if ($form->{description} ne "") {
  810. $var = $form->like(lc $form->{description});
  811. $invwhere .= " AND lower(i.description) LIKE '$var'";
  812. }
  813. if ($form->{open} || $form->{closed}) {
  814. if ($form->{open} && $form->{closed}) {
  815. if ($form->{method} eq 'cash') {
  816. $invwhere .= " AND a.amount = a.paid";
  817. }
  818. } else {
  819. if ($form->{open}) {
  820. if ($form->{method} eq 'cash') {
  821. $invwhere .= " AND a.id = 0";
  822. } else {
  823. $invwhere .= " AND NOT a.amount = a.paid";
  824. }
  825. } else {
  826. $invwhere .= " AND a.amount = a.paid";
  827. }
  828. }
  829. } else {
  830. $invwhere .= " AND a.id = 0";
  831. }
  832. my $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
  833. i.qty AS onhand, i.unit, p.bin, i.sellprice,
  834. p.listprice, p.lastcost, p.rop, p.weight,
  835. p.avgcost,
  836. p.priceupdate, p.image, p.drawing, p.microfiche,
  837. p.assembly,
  838. pg.partsgroup, a.invnumber, a.ordnumber, a.quonumber,
  839. i.trans_id, ct.name, e.name AS employee, a.curr, a.till,
  840. p.notes
  841. $makemodelfld|;
  842. if ($form->{bought}) {
  843. my $rflds = $flds;
  844. $rflds =~ s/i.qty AS onhand/i.qty * -1 AS onhand/;
  845. $query = qq|
  846. SELECT $rflds, 'ir' AS module, '' AS type,
  847. (SELECT sell FROM exchangerate ex
  848. WHERE ex.curr = a.curr
  849. AND ex.transdate = a.$transdate) AS exchangerate,
  850. i.discount
  851. FROM invoice i
  852. JOIN parts p ON (p.id = i.parts_id)
  853. JOIN ap a ON (a.id = i.trans_id)
  854. JOIN vendor ct ON (a.vendor_id = ct.id)
  855. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  856. LEFT JOIN employee e ON (a.employee_id = e.id)
  857. $makemodeljoin
  858. WHERE $invwhere|;
  859. $union = "
  860. UNION ALL";
  861. }
  862. if ($form->{sold}) {
  863. $query .= qq|$union
  864. SELECT $flds, 'is' AS module, '' AS type,
  865. (SELECT buy FROM exchangerate ex
  866. WHERE ex.curr = a.curr
  867. AND ex.transdate = a.$transdate) AS exchangerate,
  868. i.discount
  869. FROM invoice i
  870. JOIN parts p ON (p.id = i.parts_id)
  871. JOIN ar a ON (a.id = i.trans_id)
  872. JOIN customer ct ON (a.customer_id = ct.id)
  873. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  874. LEFT JOIN employee e ON (a.employee_id = e.id)
  875. $makemodeljoin
  876. WHERE $invwhere|;
  877. $union = "
  878. UNION ALL";
  879. }
  880. }
  881. if ($form->{onorder} || $form->{ordered}) {
  882. my $ordwhere = "$where
  883. AND a.quotation = '0'";
  884. $ordwhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
  885. $ordwhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
  886. if ($form->{description} ne "") {
  887. $var = $form->like(lc $form->{description});
  888. $ordwhere .= " AND lower(i.description) LIKE '$var'";
  889. }
  890. if ($form->{open} || $form->{closed}) {
  891. unless ($form->{open} && $form->{closed}) {
  892. $ordwhere .= " AND a.closed = '0'" if $form->{open};
  893. $ordwhere .= " AND a.closed = '1'" if $form->{closed};
  894. }
  895. } else {
  896. $ordwhere .= " AND a.id = 0";
  897. }
  898. $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
  899. i.qty AS onhand, i.unit, p.bin, i.sellprice,
  900. p.listprice, p.lastcost, p.rop, p.weight,
  901. p.avgcost,
  902. p.priceupdate, p.image, p.drawing, p.microfiche,
  903. p.assembly,
  904. pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
  905. i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
  906. p.notes
  907. $makemodelfld|;
  908. if ($form->{ordered}) {
  909. $query .= qq|$union
  910. SELECT $flds, 'oe' AS module, 'sales_order' AS type,
  911. (SELECT buy FROM exchangerate ex
  912. WHERE ex.curr = a.curr
  913. AND ex.transdate = a.transdate) AS exchangerate,
  914. i.discount
  915. FROM orderitems i
  916. JOIN parts p ON (i.parts_id = p.id)
  917. JOIN oe a ON (i.trans_id = a.id)
  918. JOIN customer ct ON (a.customer_id = ct.id)
  919. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  920. LEFT JOIN employee e ON (a.employee_id = e.id)
  921. $makemodeljoin
  922. WHERE $ordwhere
  923. AND a.customer_id > 0|;
  924. $union = "
  925. UNION ALL";
  926. }
  927. if ($form->{onorder}) {
  928. $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
  929. i.qty AS onhand, i.unit, p.bin, i.sellprice,
  930. p.listprice, p.lastcost, p.rop, p.weight,
  931. p.avgcost,
  932. p.priceupdate, p.image, p.drawing, p.microfiche,
  933. p.assembly,
  934. pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
  935. i.trans_id, ct.name,e.name AS employee, a.curr, '0' AS till,
  936. p.notes
  937. $makemodelfld|;
  938. $query .= qq|$union
  939. SELECT $flds, 'oe' AS module, 'purchase_order' AS type,
  940. (SELECT sell FROM exchangerate ex
  941. WHERE ex.curr = a.curr
  942. AND ex.transdate = a.transdate) AS exchangerate,
  943. i.discount
  944. FROM orderitems i
  945. JOIN parts p ON (i.parts_id = p.id)
  946. JOIN oe a ON (i.trans_id = a.id)
  947. JOIN vendor ct ON (a.vendor_id = ct.id)
  948. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  949. LEFT JOIN employee e ON (a.employee_id = e.id)
  950. $makemodeljoin
  951. WHERE $ordwhere
  952. AND a.vendor_id > 0|;
  953. }
  954. }
  955. if ($form->{rfq} || $form->{quoted}) {
  956. my $quowhere = "$where
  957. AND a.quotation = '1'";
  958. $quowhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
  959. $quowhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
  960. if ($form->{description} ne "") {
  961. $var = $form->like(lc $form->{description});
  962. $quowhere .= " AND lower(i.description) LIKE '$var'";
  963. }
  964. if ($form->{open} || $form->{closed}) {
  965. unless ($form->{open} && $form->{closed}) {
  966. $ordwhere .= " AND a.closed = '0'" if $form->{open};
  967. $ordwhere .= " AND a.closed = '1'" if $form->{closed};
  968. }
  969. } else {
  970. $ordwhere .= " AND a.id = 0";
  971. }
  972. $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
  973. i.qty AS onhand, i.unit, p.bin, i.sellprice,
  974. p.listprice, p.lastcost, p.rop, p.weight,
  975. p.avgcost,
  976. p.priceupdate, p.image, p.drawing, p.microfiche,
  977. p.assembly,
  978. pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
  979. i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
  980. p.notes
  981. $makemodelfld|;
  982. if ($form->{quoted}) {
  983. $query .= qq|$union
  984. SELECT $flds, 'oe' AS module, 'sales_quotation' AS type,
  985. (SELECT buy FROM exchangerate ex
  986. WHERE ex.curr = a.curr
  987. AND ex.transdate = a.transdate) AS exchangerate,
  988. i.discount
  989. FROM orderitems i
  990. JOIN parts p ON (i.parts_id = p.id)
  991. JOIN oe a ON (i.trans_id = a.id)
  992. JOIN customer ct ON (a.customer_id = ct.id)
  993. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  994. LEFT JOIN employee e ON (a.employee_id = e.id)
  995. $makemodeljoin
  996. WHERE $quowhere
  997. AND a.customer_id > 0|;
  998. $union = "
  999. UNION ALL";
  1000. }
  1001. if ($form->{rfq}) {
  1002. $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
  1003. i.qty AS onhand, i.unit, p.bin, i.sellprice,
  1004. p.listprice, p.lastcost, p.rop, p.weight,
  1005. p.avgcost,
  1006. p.priceupdate, p.image, p.drawing, p.microfiche,
  1007. p.assembly,
  1008. pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
  1009. i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
  1010. p.notes
  1011. $makemodelfld|;
  1012. $query .= qq|$union
  1013. SELECT $flds, 'oe' AS module, 'request_quotation' AS type,
  1014. (SELECT sell FROM exchangerate ex
  1015. WHERE ex.curr = a.curr
  1016. AND ex.transdate = a.transdate) AS exchangerate,
  1017. i.discount
  1018. FROM orderitems i
  1019. JOIN parts p ON (i.parts_id = p.id)
  1020. JOIN oe a ON (i.trans_id = a.id)
  1021. JOIN vendor ct ON (a.vendor_id = ct.id)
  1022. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  1023. LEFT JOIN employee e ON (a.employee_id = e.id)
  1024. $makemodeljoin
  1025. WHERE $quowhere
  1026. AND a.vendor_id > 0|;
  1027. }
  1028. }
  1029. $query .= qq|
  1030. ORDER BY $sortorder|;
  1031. }
  1032. my $sth = $dbh->prepare($query);
  1033. $sth->execute || $form->dberror($query);
  1034. $query = qq|SELECT c.accno
  1035. FROM chart c
  1036. JOIN partstax pt ON (pt.chart_id = c.id)
  1037. WHERE pt.parts_id = ?
  1038. ORDER BY accno|;
  1039. my $pth = $dbh->prepare($query) || $form->dberror($query);
  1040. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1041. $pth->execute($ref->{id});
  1042. while (($accno) = $pth->fetchrow_array) {
  1043. $ref->{tax} .= "$accno ";
  1044. }
  1045. $pth->finish;
  1046. push @{ $form->{parts} }, $ref;
  1047. }
  1048. $sth->finish;
  1049. @a = ();
  1050. # include individual items for assembly
  1051. if (($form->{searchitems} eq 'assembly') && $form->{individual}) {
  1052. if ($form->{sold} || $form->{ordered} || $form->{quoted}) {
  1053. $flds = qq|p.id, p.partnumber, p.description, p.onhand AS perassembly, p.unit,
  1054. p.bin, p.sellprice, p.listprice, p.lastcost, p.rop,
  1055. p.avgcost,
  1056. p.weight, p.priceupdate, p.image, p.drawing, p.microfiche,
  1057. p.assembly, pg.partsgroup, p.notes
  1058. $makemodelflds $assemblyflds
  1059. |;
  1060. } else {
  1061. # replace p.onhand with a.qty AS onhand
  1062. $flds =~ s/p.onhand/a.qty AS perassembly/;
  1063. }
  1064. for (@{ $form->{parts} }) {
  1065. push @a, $_;
  1066. $_->{perassembly} = 1;
  1067. $flds =~ s/p\.onhand*AS perassembly/p\.onhand, a\.qty AS perassembly/;
  1068. push @a, &include_assembly($dbh, $myconfig, $form, $_->{id}, $flds, $makemodeljoin);
  1069. push @a, {id => $_->{id}, assemblyitem => 1}; # this is just for
  1070. # adding a blank line
  1071. }
  1072. # copy assemblies to $form->{parts}
  1073. @{ $form->{parts} } = @a;
  1074. }
  1075. @a = ();
  1076. if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
  1077. if ($form->{warehouse} ne "") {
  1078. ($null, $var) = split /--/, $form->{warehouse};
  1079. $var *= 1;
  1080. $query = qq|SELECT SUM(qty) AS onhand, '$null' AS description
  1081. FROM inventory
  1082. WHERE warehouse_id = $var
  1083. AND parts_id = ?|;
  1084. } else {
  1085. $query = qq|SELECT SUM(i.qty) AS onhand, w.description AS warehouse
  1086. FROM inventory i
  1087. JOIN warehouse w ON (w.id = i.warehouse_id)
  1088. WHERE i.parts_id = ?
  1089. GROUP BY w.description|;
  1090. }
  1091. $sth = $dbh->prepare($query) || $form->dberror($query);
  1092. for (@{ $form->{parts} }) {
  1093. $sth->execute($_->{id}) || $form->dberror($query);
  1094. if ($form->{warehouse} ne "") {
  1095. $ref = $sth->fetchrow_hashref(NAME_lc);
  1096. if ($ref->{onhand} != 0) {
  1097. $_->{onhand} = $ref->{onhand};
  1098. push @a, $_;
  1099. }
  1100. } else {
  1101. push @a, $_;
  1102. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1103. if ($ref->{onhand} > 0) {
  1104. push @a, $ref;
  1105. }
  1106. }
  1107. }
  1108. $sth->finish;
  1109. }
  1110. @{ $form->{parts} } = @a;
  1111. }
  1112. $dbh->disconnect;
  1113. }
  1114. sub include_assembly {
  1115. my ($dbh, $myconfig, $form, $id, $flds, $makemodeljoin) = @_;
  1116. $form->{stagger}++;
  1117. if ($form->{stagger} > $form->{pncol}) {
  1118. $form->{pncol} = $form->{stagger};
  1119. }
  1120. $form->{$id} = 1;
  1121. my %oid = ('Pg' => 'a.oid',
  1122. 'PgPP' => 'a.oid',
  1123. 'Oracle' => 'a.rowid',
  1124. 'DB2' => '1=1'
  1125. );
  1126. my @a = qw(partnumber description bin);
  1127. if ($form->{sort} eq 'partnumber') {
  1128. $sortorder = "$oid{$myconfig->{dbdriver}}";
  1129. } else {
  1130. @a = grep !/$form->{sort}/, @a;
  1131. $sortorder = "$form->{sort} $form->{direction}, ". join ',', @a;
  1132. }
  1133. @a = ();
  1134. my $query = qq|SELECT $flds
  1135. FROM parts p
  1136. JOIN assembly a ON (a.parts_id = p.id)
  1137. LEFT JOIN partsgroup pg ON (pg.id = p.id)
  1138. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  1139. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  1140. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  1141. $makemodeljoin
  1142. WHERE a.id = $id
  1143. ORDER BY $sortorder|;
  1144. my $sth = $dbh->prepare($query);
  1145. $sth->execute || $form->dberror($query);
  1146. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1147. $ref->{assemblyitem} = 1;
  1148. $ref->{stagger} = $form->{stagger};
  1149. push @a, $ref;
  1150. if ($ref->{assembly} && !$form->{$ref->{id}}) {
  1151. push @a, &include_assembly($dbh, $myconfig, $form, $ref->{id}, $flds, $makemodeljoin);
  1152. if ($form->{stagger} > $form->{pncol}) {
  1153. $form->{pncol} = $form->{stagger};
  1154. }
  1155. }
  1156. }
  1157. $sth->finish;
  1158. $form->{$id} = 0;
  1159. $form->{stagger}--;
  1160. @a;
  1161. }
  1162. sub requirements {
  1163. my ($self, $myconfig, $form) = @_;
  1164. my $null;
  1165. my $var;
  1166. my $ref;
  1167. my $where = qq|p.obsolete = '0'|;
  1168. my $dwhere;
  1169. for (qw(partnumber description)) {
  1170. if ($form->{$_} ne "") {
  1171. $var = $form->like(lc $form->{$_});
  1172. $where .= qq| AND lower(p.$_) LIKE '$var'|;
  1173. }
  1174. }
  1175. if ($form->{partsgroup} ne "") {
  1176. ($null, $var) = split /--/, $form->{partsgroup};
  1177. $where .= qq| AND p.partsgroup_id = $var|;
  1178. }
  1179. # connect to database
  1180. my $dbh = $form->dbconnect($myconfig);
  1181. my ($transdatefrom, $transdateto);
  1182. if ($form->{year}) {
  1183. ($transdatefrom, $transdateto) = $form->from_to($form->{year}, '01', 12);
  1184. $dwhere = qq| AND a.transdate >= '$transdatefrom'
  1185. AND a.transdate <= '$transdateto'|;
  1186. }
  1187. $query = qq|SELECT p.id, p.partnumber, p.description,
  1188. sum(i.qty) AS qty, p.onhand,
  1189. extract(MONTH FROM a.transdate) AS month,
  1190. '0' AS so, '0' AS po
  1191. FROM invoice i
  1192. JOIN parts p ON (p.id = i.parts_id)
  1193. JOIN ar a ON (a.id = i.trans_id)
  1194. WHERE $where
  1195. $dwhere
  1196. AND p.inventory_accno_id > 0
  1197. GROUP BY p.id, p.partnumber, p.description, p.onhand,
  1198. extract(MONTH FROM a.transdate)|;
  1199. my $sth = $dbh->prepare($query);
  1200. $sth->execute || $form->dberror($query);
  1201. my %parts;
  1202. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1203. $parts{$ref->{id}} = $ref;
  1204. }
  1205. $sth->finish;
  1206. my %ofld = ( customer => so,
  1207. vendor => po );
  1208. for (qw(customer vendor)) {
  1209. $query = qq|SELECT p.id, p.partnumber, p.description,
  1210. sum(qty) - sum(ship) AS $ofld{$_}, p.onhand,
  1211. 0 AS month
  1212. FROM orderitems i
  1213. JOIN parts p ON (p.id = i.parts_id)
  1214. JOIN oe a ON (a.id = i.trans_id)
  1215. WHERE $where
  1216. AND p.inventory_accno_id > 0
  1217. AND p.assembly = '0'
  1218. AND a.closed = '0'
  1219. AND a.${_}_id > 0
  1220. GROUP BY p.id, p.partnumber, p.description, p.onhand,
  1221. month|;
  1222. $sth = $dbh->prepare($query);
  1223. $sth->execute || $form->dberror($query);
  1224. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1225. if (exists $parts{$ref->{id}}->{$ofld{$_}}) {
  1226. $parts{$ref->{id}}->{$ofld{$_}} += $ref->{$ofld{$_}};
  1227. } else {
  1228. $parts{$ref->{id}} = $ref;
  1229. }
  1230. }
  1231. $sth->finish;
  1232. }
  1233. # add assemblies from open sales orders
  1234. $query = qq|SELECT DISTINCT a.id AS orderid, b.id, i.qty - i.ship AS qty
  1235. FROM parts p
  1236. JOIN assembly b ON (b.parts_id = p.id)
  1237. JOIN orderitems i ON (i.parts_id = b.id)
  1238. JOIN oe a ON (a.id = i.trans_id)
  1239. WHERE $where
  1240. AND (p.inventory_accno_id > 0 OR p.assembly = '1')
  1241. AND a.closed = '0'|;
  1242. $sth = $dbh->prepare($query);
  1243. $sth->execute || $form->dberror($query);
  1244. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1245. &requirements_assembly($dbh, $form, \%parts, $ref->{id}, $ref->{qty}, $where) if $ref->{qty};
  1246. }
  1247. $sth->finish;
  1248. $dbh->disconnect;
  1249. for (sort { $parts{$a}->{$form->{sort}} cmp $parts{$b}->{$form->{sort}} } keys %parts) {
  1250. push @{ $form->{parts} }, $parts{$_};
  1251. }
  1252. }
  1253. sub requirements_assembly {
  1254. my ($dbh, $form, $parts, $id, $qty, $where) = @_;
  1255. # assemblies
  1256. my $query = qq|SELECT p.id, p.partnumber, p.description,
  1257. a.qty * $qty AS so, p.onhand, p.assembly,
  1258. p.partsgroup_id
  1259. FROM assembly a
  1260. JOIN parts p ON (p.id = a.parts_id)
  1261. WHERE $where
  1262. AND a.id = $id
  1263. AND p.inventory_accno_id > 0
  1264. UNION
  1265. SELECT p.id, p.partnumber, p.description,
  1266. a.qty * $qty AS so, p.onhand, p.assembly,
  1267. p.partsgroup_id
  1268. FROM assembly a
  1269. JOIN parts p ON (p.id = a.parts_id)
  1270. WHERE a.id = $id
  1271. AND p.assembly = '1'|;
  1272. my $sth = $dbh->prepare($query);
  1273. $sth->execute || $form->dberror($query);
  1274. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  1275. if ($ref->{assembly}) {
  1276. &requirements_assembly($dbh, $form, $parts, $ref->{id}, $ref->{so}, $where);
  1277. next;
  1278. }
  1279. if (exists $parts->{$ref->{id}}{so}) {
  1280. $parts->{$ref->{id}}{so} += $ref->{so};
  1281. } else {
  1282. $parts->{$ref->{id}} = $ref;
  1283. }
  1284. }
  1285. $sth->finish;
  1286. }
  1287. sub create_links {
  1288. my ($self, $module, $myconfig, $form) = @_;
  1289. # connect to database
  1290. my $dbh = $form->dbconnect($myconfig);
  1291. my $ref;
  1292. my $query = qq|SELECT accno, description, link
  1293. FROM chart
  1294. WHERE link LIKE '%$module%'
  1295. ORDER BY accno|;
  1296. my $sth = $dbh->prepare($query);
  1297. $sth->execute || $form->dberror($query);
  1298. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1299. foreach my $key (split /:/, $ref->{link}) {
  1300. if ($key =~ /$module/) {
  1301. push @{ $form->{"${module}_links"}{$key} }, { accno => $ref->{accno},
  1302. description => $ref->{description} };
  1303. }
  1304. }
  1305. }
  1306. $sth->finish;
  1307. if ($form->{item} ne 'assembly') {
  1308. $query = qq|SELECT count(*) FROM vendor|;
  1309. my ($count) = $dbh->selectrow_array($query);
  1310. if ($count < $myconfig->{vclimit}) {
  1311. $query = qq|SELECT id, name
  1312. FROM vendor
  1313. ORDER BY name|;
  1314. $sth = $dbh->prepare($query);
  1315. $sth->execute || $form->dberror($query);
  1316. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1317. push @{ $form->{all_vendor} }, $ref;
  1318. }
  1319. $sth->finish;
  1320. }
  1321. }
  1322. # pricegroups, customers
  1323. $query = qq|SELECT count(*) FROM customer|;
  1324. ($count) = $dbh->selectrow_array($query);
  1325. if ($count < $myconfig->{vclimit}) {
  1326. $query = qq|SELECT id, name
  1327. FROM customer
  1328. ORDER BY name|;
  1329. $sth = $dbh->prepare($query);
  1330. $sth->execute || $form->dberror($query);
  1331. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1332. push @{ $form->{all_customer} }, $ref;
  1333. }
  1334. $sth->finish;
  1335. }
  1336. $query = qq|SELECT id, pricegroup
  1337. FROM pricegroup
  1338. ORDER BY pricegroup|;
  1339. $sth = $dbh->prepare($query);
  1340. $sth->execute || $form->dberror($query);
  1341. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1342. push @{ $form->{all_pricegroup} }, $ref;
  1343. }
  1344. $sth->finish;
  1345. if ($form->{id}) {
  1346. $query = qq|SELECT weightunit, curr AS currencies
  1347. FROM defaults|;
  1348. ($form->{weightunit}, $form->{currencies}) = $dbh->selectrow_array($query);
  1349. } else {
  1350. $query = qq|SELECT d.weightunit, current_date AS priceupdate,
  1351. d.curr AS currencies,
  1352. c1.accno AS inventory_accno, c1.description AS inventory_description,
  1353. c2.accno AS income_accno, c2.description AS income_description,
  1354. c3.accno AS expense_accno, c3.description AS expense_description
  1355. FROM defaults d
  1356. LEFT JOIN chart c1 ON (d.inventory_accno_id = c1.id)
  1357. LEFT JOIN chart c2 ON (d.income_accno_id = c2.id)
  1358. LEFT JOIN chart c3 ON (d.expense_accno_id = c3.id)|;
  1359. $sth = $dbh->prepare($query);
  1360. $sth->execute || $form->dberror($query);
  1361. $ref = $sth->fetchrow_hashref(NAME_lc);
  1362. for (qw(weightunit priceupdate currencies)) { $form->{$_} = $ref->{$_} }
  1363. # setup accno hash, {amount} is used in create_links
  1364. for (qw(inventory income expense)) { $form->{amount}{"IC_$_"} = { accno => $ref->{"${_}_accno"}, description => $ref->{"${_}_description"} } }
  1365. $sth->finish;
  1366. }
  1367. $dbh->disconnect;
  1368. }
  1369. sub get_warehouses {
  1370. my ($self, $myconfig, $form) = @_;
  1371. my $dbh = $form->dbconnect($myconfig);
  1372. my $query = qq|SELECT id, description
  1373. FROM warehouse|;
  1374. my $sth = $dbh->prepare($query);
  1375. $sth->execute || $form->dberror($query);
  1376. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  1377. push @{ $form->{all_warehouse} }, $ref;
  1378. }
  1379. $sth->finish;
  1380. $dbh->disconnect;
  1381. }
  1382. 1;