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