summaryrefslogtreecommitdiff
path: root/LedgerSMB/IC.pm
blob: e397489109a2c60ed7b9a7b0b8b0972e40cff033 (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->{dbh};
  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. }
  758. sub all_parts {
  759. my ($self, $myconfig, $form) = @_;
  760. $dbh = $form->{dbh};
  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 = $dbh->quote($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}
  774. || $form->{ordered} || $form->{rfq} || $form->{quoted}
  775. ) {
  776. $var = $dbh->quote($form->like(
  777. lc $form->{description}));
  778. $where .= " AND lower(p.description) LIKE $var";
  779. }
  780. }
  781. # assembly components
  782. my $assemblyflds;
  783. if ($form->{searchitems} eq 'component') {
  784. $assemblyflds = qq|, p1.partnumber AS assemblypartnumber,
  785. a.id AS assembly_id|;
  786. }
  787. # special case for serialnumber
  788. if ($form->{l_serialnumber}) {
  789. if ($form->{serialnumber} ne "") {
  790. $var = $dbh->quote(
  791. $form->like(lc $form->{serialnumber}));
  792. $where .= " AND lower(i.serialnumber) LIKE $var";
  793. }
  794. }
  795. if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
  796. $form->{l_warehouse} = 1;
  797. }
  798. if ($form->{searchitems} eq 'part') {
  799. $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id > 0";
  800. }
  801. if ($form->{searchitems} eq 'assembly') {
  802. $form->{bought} = "";
  803. $where .= " AND p.assembly = '1'";
  804. }
  805. if ($form->{searchitems} eq 'service') {
  806. $where .= " AND p.assembly = '0' AND p.inventory_accno_id IS NULL";
  807. }
  808. if ($form->{searchitems} eq 'labor') {
  809. $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id IS NULL";
  810. }
  811. # items which were never bought, sold or on an order
  812. if ($form->{itemstatus} eq 'orphaned') {
  813. $where .= qq|
  814. AND p.onhand = 0
  815. AND p.id NOT IN (SELECT p.id FROM parts p
  816. JOIN invoice i
  817. ON (p.id = i.parts_id))
  818. AND p.id NOT IN (SELECT p.id FROM parts p
  819. JOIN assembly a
  820. ON (p.id = a.parts_id))
  821. AND p.id NOT IN (SELECT p.id FROM parts p
  822. JOIN orderitems o
  823. ON (p.id = o.parts_id))
  824. AND p.id NOT IN (SELECT p.id FROM parts p
  825. JOIN jcitems j
  826. ON (p.id = j.parts_id))|;
  827. }
  828. if ($form->{itemstatus} eq 'active') {
  829. $where .= " AND p.obsolete = '0'";
  830. }
  831. if ($form->{itemstatus} eq 'obsolete') {
  832. $where .= " AND p.obsolete = '1'";
  833. }
  834. if ($form->{itemstatus} eq 'onhand') {
  835. $where .= " AND p.onhand > 0";
  836. }
  837. if ($form->{itemstatus} eq 'short') {
  838. $where .= " AND p.onhand < p.rop";
  839. }
  840. my $makemodelflds = qq|, '', ''|;;
  841. my $makemodeljoin;
  842. if (($form->{make} ne "") || $form->{l_make} || ($form->{model} ne "")
  843. || $form->{l_model}) {
  844. $makemodelflds = qq|, m.make, m.model|;
  845. $makemodeljoin = qq|LEFT JOIN makemodel m ON (m.parts_id = p.id)|;
  846. if ($form->{make} ne "") {
  847. $var = $dbh->quote($form->like(lc $form->{make}));
  848. $where .= " AND lower(m.make) LIKE $var";
  849. }
  850. if ($form->{model} ne "") {
  851. $var = $dbh->quote($form->like(lc $form->{model}));
  852. $where .= " AND lower(m.model) LIKE $var";
  853. }
  854. }
  855. if ($form->{partsgroup} ne "") {
  856. ($null, $var) = split /--/, $form->{partsgroup};
  857. $where .= qq| AND p.partsgroup_id = | . $dbh->quote($var);
  858. }
  859. my %ordinal = (
  860. 'partnumber' => 2,
  861. 'description' => 3,
  862. 'bin' => 6,
  863. 'priceupdate' => 13,
  864. 'drawing' => 15,
  865. 'microfiche' => 16,
  866. 'partsgroup' => 18,
  867. 'make' => 21,
  868. 'model' => 22,
  869. 'assemblypartnumber' => 23
  870. );
  871. my @a = qw(partnumber description);
  872. my $sortorder = $form->sort_order(\@a, \%ordinal);
  873. my $query = qq|SELECT value FROM defaults WHERE setting_key = 'curr'|;
  874. my ($curr) = $dbh->selectrow_array($query);
  875. $curr =~ s/:.*//;
  876. $curr = $dbh->quote($curr);
  877. my $flds = qq|
  878. p.id, p.partnumber, p.description, p.onhand, p.unit,
  879. p.bin, p.sellprice, p.listprice, p.lastcost, p.rop,
  880. p.avgcost,
  881. p.weight, p.priceupdate, p.image, p.drawing, p.microfiche,
  882. p.assembly, pg.partsgroup, $curr AS curr,
  883. c1.accno AS inventory, c2.accno AS income, c3.accno AS expense,
  884. p.notes
  885. $makemodelflds $assemblyflds
  886. |;
  887. $query = qq|
  888. SELECT $flds
  889. FROM parts p
  890. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  891. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  892. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  893. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  894. $makemodeljoin
  895. WHERE $where
  896. ORDER BY $sortorder|;
  897. # redo query for components report
  898. if ($form->{searchitems} eq 'component') {
  899. $flds =~ s/p.onhand/a.qty AS onhand/;
  900. $query = qq|
  901. SELECT $flds
  902. FROM assembly a
  903. JOIN parts p ON (a.parts_id = p.id)
  904. JOIN parts p1 ON (a.id = p1.id)
  905. LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
  906. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  907. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  908. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  909. $makemodeljoin
  910. WHERE $where
  911. ORDER BY $sortorder|;
  912. }
  913. # rebuild query for bought and sold items
  914. if ($form->{bought} || $form->{sold} || $form->{onorder}
  915. || $form->{ordered} || $form->{rfq} || $form->{quoted}
  916. ) {
  917. $form->sort_order();
  918. @a = qw(partnumber description curr employee name
  919. serialnumber id);
  920. push @a, "invnumber" if ($form->{bought} || $form->{sold});
  921. push @a, "ordnumber" if ($form->{onorder} || $form->{ordered});
  922. push @a, "quonumber" if ($form->{rfq} || $form->{quoted});
  923. %ordinal = (
  924. 'partnumber' => 2,
  925. 'description' => 3,
  926. 'serialnumber' => 4,
  927. 'bin' => 7,
  928. 'priceupdate' => 14,
  929. 'partsgroup' => 19,
  930. 'invnumber' => 20,
  931. 'ordnumber' => 21,
  932. 'quonumber' => 22,
  933. 'name' => 24,
  934. 'employee' => 25,
  935. 'curr' => 26,
  936. 'make' => 29,
  937. 'model' => 30
  938. );
  939. $sortorder = $form->sort_order(\@a, \%ordinal);
  940. my $union = "";
  941. $query = "";
  942. if ($form->{bought} || $form->{sold}) {
  943. my $invwhere = "$where";
  944. my $transdate =
  945. ($form->{method} eq 'accrual')
  946. ? "transdate"
  947. : "datepaid";
  948. $invwhere .= " AND i.assemblyitem = '0'";
  949. $invwhere .= " AND a.$transdate >= " .
  950. $dbh->quote($form->{transdatefrom})
  951. if $form->{transdatefrom};
  952. $invwhere .= " AND a.$transdate <= " .
  953. $dbh->quote($form->{transdateto})
  954. if $form->{transdateto};
  955. if ($form->{description} ne "") {
  956. $var = dbh->quote(
  957. $form->like(lc $form->{description}));
  958. $invwhere .=
  959. " AND lower(i.description) LIKE $var";
  960. }
  961. if ($form->{open} || $form->{closed}) {
  962. if ($form->{open} && $form->{closed}) {
  963. if ($form->{method} eq 'cash') {
  964. $invwhere .=
  965. " AND a.amount = a.paid";
  966. }
  967. } else {
  968. if ($form->{open}) {
  969. if ($form->{method} eq 'cash') {
  970. $invwhere .=
  971. " AND a.id = 0";
  972. } else {
  973. $invwhere .=
  974. " AND NOT a.amount = a.paid";
  975. }
  976. } else {
  977. $invwhere .=
  978. " AND a.amount = a.paid";
  979. }
  980. }
  981. } else {
  982. $invwhere .= " AND a.id = 0";
  983. }
  984. my $flds = qq|
  985. p.id, p.partnumber, i.description,
  986. i.serialnumber, i.qty AS onhand, i.unit, p.bin,
  987. i.sellprice, p.listprice, p.lastcost, p.rop,
  988. p.weight, p.avgcost, p.priceupdate, p.image,
  989. p.drawing, p.microfiche, p.assembly,
  990. pg.partsgroup, a.invnumber, a.ordnumber,
  991. a.quonumber, i.trans_id, ct.name,
  992. e.name AS employee, a.curr, a.till, p.notes
  993. $makemodelfld|;
  994. if ($form->{bought}) {
  995. my $rflds = $flds;
  996. $rflds =~
  997. s/i.qty AS onhand/i.qty * -1 AS onhand/;
  998. $query = qq|
  999. SELECT $rflds, 'ir' AS module,
  1000. '' AS type,
  1001. (SELECT sell
  1002. FROM exchangerate ex
  1003. WHERE ex.curr = a.curr
  1004. AND ex.transdate
  1005. = a.$transdate)
  1006. AS exchangerate, i.discount
  1007. FROM invoice i
  1008. JOIN parts p
  1009. ON (p.id = i.parts_id)
  1010. JOIN ap a ON (a.id = i.trans_id)
  1011. JOIN vendor ct
  1012. ON (a.vendor_id = ct.id)
  1013. LEFT JOIN partsgroup pg
  1014. ON (p.partsgroup_id = pg.id)
  1015. LEFT JOIN employee e
  1016. ON (a.employee_id = e.id)
  1017. $makemodeljoin
  1018. WHERE $invwhere|;
  1019. $union = "
  1020. UNION ALL";
  1021. }
  1022. if ($form->{sold}) {
  1023. $query .= qq|
  1024. $union
  1025. SELECT $flds, 'is' AS module,
  1026. '' AS type,
  1027. (SELECT buy
  1028. FROM exchangerate ex
  1029. WHERE ex.curr = a.curr
  1030. AND ex.transdate
  1031. = a.$transdate)
  1032. AS exchangerate, i.discount
  1033. FROM invoice i
  1034. JOIN parts p
  1035. ON (p.id = i.parts_id)
  1036. JOIN ar a ON (a.id = i.trans_id)
  1037. JOIN customer ct
  1038. ON (a.customer_id = ct.id)
  1039. LEFT JOIN partsgroup pg
  1040. ON (p.partsgroup_id = pg.id)
  1041. LEFT JOIN employee e
  1042. ON (a.employee_id = e.id)
  1043. $makemodeljoin
  1044. WHERE $invwhere|;
  1045. $union = "
  1046. UNION ALL";
  1047. }
  1048. }
  1049. if ($form->{onorder} || $form->{ordered}) {
  1050. my $ordwhere = "$where
  1051. AND a.quotation = '0'";
  1052. $ordwhere .= " AND a.transdate >= ".
  1053. $dbh->quote($form->{transdatefrom})
  1054. if $form->{transdatefrom};
  1055. $ordwhere .= " AND a.transdate <= ".
  1056. $dbh->quote($form->{transdateto})
  1057. if $form->{transdateto};
  1058. if ($form->{description} ne "") {
  1059. $var = $dbh->quote(
  1060. $form->like(lc $form->{description}));
  1061. $ordwhere .= " AND lower(i.description) LIKE $var";
  1062. }
  1063. if ($form->{open} || $form->{closed}) {
  1064. unless ($form->{open} && $form->{closed}) {
  1065. $ordwhere .= " AND a.closed = '0'"
  1066. if $form->{open};
  1067. $ordwhere .= " AND a.closed = '1'"
  1068. if $form->{closed};
  1069. }
  1070. } else {
  1071. $ordwhere .= " AND a.id = 0";
  1072. }
  1073. $flds = qq|
  1074. p.id, p.partnumber, i.description,
  1075. i.serialnumber, i.qty AS onhand, i.unit, p.bin,
  1076. i.sellprice, p.listprice, p.lastcost, p.rop,
  1077. p.weight, p.avgcost, p.priceupdate, p.image,
  1078. p.drawing, p.microfiche, p.assembly,
  1079. pg.partsgroup, '' AS invnumber, a.ordnumber,
  1080. a.quonumber, i.trans_id, ct.name,
  1081. e.name AS employee, a.curr, '0' AS till,
  1082. p.notes
  1083. $makemodelfld|;
  1084. if ($form->{ordered}) {
  1085. $query .= qq|
  1086. $union
  1087. SELECT $flds, 'oe' AS module,
  1088. 'sales_order' AS type,
  1089. (SELECT buy
  1090. FROM exchangerate ex
  1091. WHERE ex.curr = a.curr
  1092. AND ex.transdate
  1093. = a.transdate)
  1094. AS exchangerate, i.discount
  1095. FROM orderitems i
  1096. JOIN parts p ON (i.parts_id = p.id)
  1097. JOIN oe a ON (i.trans_id = a.id)
  1098. JOIN customer ct
  1099. ON (a.customer_id = ct.id)
  1100. LEFT JOIN partsgroup pg
  1101. ON (p.partsgroup_id = pg.id)
  1102. LEFT JOIN employee e
  1103. ON (a.employee_id = e.id)
  1104. $makemodeljoin
  1105. WHERE $ordwhere
  1106. AND a.customer_id > 0|;
  1107. $union = "
  1108. UNION ALL";
  1109. }
  1110. if ($form->{onorder}) {
  1111. $flds = qq|
  1112. p.id, p.partnumber, i.description,
  1113. i.serialnumber, i.qty AS onhand, i.unit,
  1114. p.bin, i.sellprice, p.listprice,
  1115. p.lastcost, p.rop, p.weight, p.avgcost,
  1116. p.priceupdate, p.image, p.drawing,
  1117. p.microfiche, p.assembly,
  1118. pg.partsgroup, '' AS invnumber,
  1119. a.ordnumber, a.quonumber,
  1120. i.trans_id, ct.name,e.name AS employee,
  1121. a.curr, '0' AS till, p.notes
  1122. $makemodelfld|;
  1123. $query .= qq|
  1124. $union
  1125. SELECT $flds, 'oe' AS module,
  1126. 'purchase_order' AS type,
  1127. (SELECT sell
  1128. FROM exchangerate ex
  1129. WHERE ex.curr = a.curr
  1130. AND ex.transdate
  1131. = a.transdate)
  1132. AS exchangerate, i.discount
  1133. FROM orderitems i
  1134. JOIN parts p ON (i.parts_id = p.id)
  1135. JOIN oe a ON (i.trans_id = a.id)
  1136. JOIN vendor ct
  1137. ON (a.vendor_id = ct.id)
  1138. LEFT JOIN partsgroup pg
  1139. ON (p.partsgroup_id = pg.id)
  1140. LEFT JOIN employee e
  1141. ON (a.employee_id = e.id)
  1142. $makemodeljoin
  1143. WHERE $ordwhere
  1144. AND a.vendor_id > 0|;
  1145. }
  1146. }
  1147. if ($form->{rfq} || $form->{quoted}) {
  1148. my $quowhere = "$where
  1149. AND a.quotation = '1'";
  1150. $quowhere .= " AND a.transdate >= ".
  1151. $dbh->quote($form->{transdatefrom})
  1152. if $form->{transdatefrom};
  1153. $quowhere .= " AND a.transdate <= ".
  1154. $dbh->quote($form->{transdateto})
  1155. if $form->{transdateto};
  1156. if ($form->{description} ne "") {
  1157. $var = $dbh->quote(
  1158. $form->like(lc $form->{description}));
  1159. $quowhere .= " AND lower(i.description) LIKE $var";
  1160. }
  1161. if ($form->{open} || $form->{closed}) {
  1162. unless ($form->{open} && $form->{closed}) {
  1163. $ordwhere .= " AND a.closed = '0'"
  1164. if $form->{open};
  1165. $ordwhere .= " AND a.closed = '1'"
  1166. if $form->{closed};
  1167. }
  1168. } else {
  1169. $ordwhere .= " AND a.id = 0";
  1170. }
  1171. $flds = qq|
  1172. p.id, p.partnumber, i.description,
  1173. i.serialnumber, i.qty AS onhand, i.unit, p.bin,
  1174. i.sellprice, p.listprice, p.lastcost, p.rop,
  1175. p.weight, p.avgcost, p.priceupdate, p.image,
  1176. p.drawing, p.microfiche, p.assembly,
  1177. pg.partsgroup, '' AS invnumber, a.ordnumber,
  1178. a.quonumber, i.trans_id, ct.name,
  1179. e.name AS employee, a.curr, '0' AS till, p.notes
  1180. $makemodelfld|;
  1181. if ($form->{quoted}) {
  1182. $query .= qq|
  1183. $union
  1184. SELECT $flds, 'oe' AS module,
  1185. 'sales_quotation' AS type,
  1186. (SELECT buy
  1187. FROM exchangerate ex
  1188. WHERE ex.curr = a.curr
  1189. AND ex.transdate
  1190. = a.transdate)
  1191. AS exchangerate,
  1192. i.discount
  1193. FROM orderitems i
  1194. JOIN parts p ON (i.parts_id = p.id)
  1195. JOIN oe a ON (i.trans_id = a.id)
  1196. JOIN customer ct
  1197. ON (a.customer_id = ct.id)
  1198. LEFT JOIN partsgroup pg
  1199. ON (p.partsgroup_id = pg.id)
  1200. LEFT JOIN employee e
  1201. ON (a.employee_id = e.id)
  1202. $makemodeljoin
  1203. WHERE $quowhere
  1204. AND a.customer_id > 0|;
  1205. $union = "
  1206. UNION ALL";
  1207. }
  1208. if ($form->{rfq}) {
  1209. $flds = qq|
  1210. p.id, p.partnumber, i.description,
  1211. i.serialnumber, i.qty AS onhand,
  1212. i.unit, p.bin, i.sellprice, p.listprice,
  1213. p.lastcost, p.rop, p.weight, p.avgcost,
  1214. p.priceupdate, p.image, p.drawing,
  1215. p.microfiche, p.assembly,
  1216. pg.partsgroup, '' AS invnumber,
  1217. a.ordnumber, a.quonumber,
  1218. i.trans_id, ct.name, e.name AS employee,
  1219. a.curr, '0' AS till, p.notes
  1220. $makemodelfld|;
  1221. $query .= qq|
  1222. $union
  1223. SELECT $flds, 'oe' AS module,
  1224. 'request_quotation' AS type,
  1225. (SELECT sell
  1226. FROM exchangerate ex
  1227. WHERE ex.curr = a.curr
  1228. AND ex.transdate
  1229. = a.transdate)
  1230. AS exchangerate, i.discount
  1231. FROM orderitems i
  1232. JOIN parts p ON (i.parts_id = p.id)
  1233. JOIN oe a ON (i.trans_id = a.id)
  1234. JOIN vendor ct
  1235. ON (a.vendor_id = ct.id)
  1236. LEFT JOIN partsgroup pg
  1237. ON (p.partsgroup_id = pg.id)
  1238. LEFT JOIN employee e
  1239. ON (a.employee_id = e.id)
  1240. $makemodeljoin
  1241. WHERE $quowhere
  1242. AND a.vendor_id > 0|;
  1243. }
  1244. }
  1245. $query .= qq|
  1246. ORDER BY $sortorder|;
  1247. }
  1248. my $sth = $dbh->prepare($query);
  1249. $sth->execute || $form->dberror($query);
  1250. $query = qq|
  1251. SELECT c.accno
  1252. FROM chart c
  1253. JOIN partstax pt ON (pt.chart_id = c.id)
  1254. WHERE pt.parts_id = ?
  1255. ORDER BY accno|;
  1256. my $pth = $dbh->prepare($query) || $form->dberror($query);
  1257. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1258. $pth->execute($ref->{id});
  1259. while (($accno) = $pth->fetchrow_array) {
  1260. $ref->{tax} .= "$accno ";
  1261. }
  1262. $pth->finish;
  1263. push @{ $form->{parts} }, $ref;
  1264. }
  1265. $sth->finish;
  1266. @a = ();
  1267. # include individual items for assembly
  1268. if (($form->{searchitems} eq 'assembly') && $form->{individual}) {
  1269. if ($form->{sold} || $form->{ordered} || $form->{quoted}) {
  1270. $flds = qq|
  1271. p.id, p.partnumber, p.description,
  1272. p.onhand AS perassembly, p.unit, p.bin,
  1273. p.sellprice, p.listprice, p.lastcost, p.rop,
  1274. p.avgcost, p.weight, p.priceupdate, p.image,
  1275. p.drawing, p.microfiche, p.assembly,
  1276. pg.partsgroup, p.notes
  1277. $makemodelflds $assemblyflds |;
  1278. } else {
  1279. # replace p.onhand with a.qty AS onhand
  1280. $flds =~ s/p\.onhand/a.qty AS perassembly/;
  1281. }
  1282. for (@{ $form->{parts} }) {
  1283. push @a, $_;
  1284. $_->{perassembly} = 1;
  1285. $flds =~ s/p\.onhand*AS perassembly/p\.onhand, a\.qty AS perassembly/;
  1286. push @a, &include_assembly(
  1287. $dbh, $myconfig, $form, $_->{id}, $flds,
  1288. $makemodeljoin);
  1289. push @a, {id => $_->{id}, assemblyitem => 1};
  1290. }
  1291. # copy assemblies to $form->{parts}
  1292. @{ $form->{parts} } = @a;
  1293. }
  1294. @a = ();
  1295. if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
  1296. if ($form->{warehouse} ne "") {
  1297. my ($desc, $var) = split /--/, $form->{warehouse};
  1298. $var = $dbh->quote($var);
  1299. $desc = $dbh->quote($desc);
  1300. $query = qq|
  1301. SELECT SUM(qty) AS onhand,
  1302. $desc AS description
  1303. FROM inventory
  1304. WHERE warehouse_id = $var
  1305. AND parts_id = ?|;
  1306. } else {
  1307. $query = qq|
  1308. SELECT SUM(i.qty) AS onhand,
  1309. w.description AS warehouse
  1310. FROM inventory i
  1311. JOIN warehouse w ON (w.id = i.warehouse_id)
  1312. WHERE i.parts_id = ?
  1313. GROUP BY w.description|;
  1314. }
  1315. $sth = $dbh->prepare($query) || $form->dberror($query);
  1316. for (@{ $form->{parts} }) {
  1317. $sth->execute($_->{id}) || $form->dberror($query);
  1318. if ($form->{warehouse} ne "") {
  1319. $ref = $sth->fetchrow_hashref(NAME_lc);
  1320. if ($ref->{onhand} != 0) {
  1321. $_->{onhand} = $ref->{onhand};
  1322. push @a, $_;
  1323. }
  1324. } else {
  1325. push @a, $_;
  1326. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1327. if ($ref->{onhand} > 0) {
  1328. push @a, $ref;
  1329. }
  1330. }
  1331. }
  1332. $sth->finish;
  1333. }
  1334. @{ $form->{parts} } = @a;
  1335. }
  1336. $dbh->commit;
  1337. }
  1338. sub include_assembly {
  1339. my ($dbh, $myconfig, $form, $id, $flds, $makemodeljoin) = @_;
  1340. $form->{stagger}++;
  1341. if ($form->{stagger} > $form->{pncol}) {
  1342. $form->{pncol} = $form->{stagger};
  1343. }
  1344. $form->{$id} = 1;
  1345. my @a = qw(partnumber description bin);
  1346. if ($form->{sort} eq 'partnumber') {
  1347. $sortorder = "TRUE";
  1348. } else {
  1349. @a = grep !/$form->{sort}/, @a;
  1350. $sortorder = "$form->{sort} $form->{direction}, ". join ',', @a;
  1351. }
  1352. @a = ();
  1353. my $query = qq|
  1354. SELECT $flds
  1355. FROM parts p
  1356. JOIN assembly a ON (a.parts_id = p.id)
  1357. LEFT JOIN partsgroup pg ON (pg.id = p.id)
  1358. LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
  1359. LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
  1360. LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
  1361. $makemodeljoin
  1362. WHERE a.id = ?
  1363. ORDER BY $sortorder|;
  1364. my $sth = $dbh->prepare($query);
  1365. $sth->execute($id) || $form->dberror($query);
  1366. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1367. $ref->{assemblyitem} = 1;
  1368. $ref->{stagger} = $form->{stagger};
  1369. push @a, $ref;
  1370. if ($ref->{assembly} && !$form->{$ref->{id}}) {
  1371. push @a, &include_assembly(
  1372. $dbh, $myconfig, $form, $ref->{id}, $flds,
  1373. $makemodeljoin);
  1374. if ($form->{stagger} > $form->{pncol}) {
  1375. $form->{pncol} = $form->{stagger};
  1376. }
  1377. }
  1378. }
  1379. $sth->finish;
  1380. $form->{$id} = 0;
  1381. $form->{stagger}--;
  1382. @a;
  1383. }
  1384. sub requirements {
  1385. my ($self, $myconfig, $form) = @_;
  1386. my $dbh = $form->{dbh};
  1387. my $null;
  1388. my $var;
  1389. my $ref;
  1390. my $where = qq|p.obsolete = '0'|;
  1391. my $dwhere;
  1392. for (qw(partnumber description)) {
  1393. if ($form->{$_} ne "") {
  1394. $var = $dbh->quote($form->like(lc $form->{$_}));
  1395. $where .= qq| AND lower(p.$_) LIKE $var|;
  1396. }
  1397. }
  1398. if ($form->{partsgroup} ne "") {
  1399. ($null, $var) = split /--/, $form->{partsgroup};
  1400. $var = $dbh->quote($var);
  1401. $where .= qq| AND p.partsgroup_id = $var|;
  1402. }
  1403. # connect to database
  1404. my ($transdatefrom, $transdateto);
  1405. if ($form->{year}) {
  1406. ($transdatefrom, $transdateto)
  1407. = $form->from_to($form->{year}, '01', 12);
  1408. $dwhere = qq| AND a.transdate >= '$transdatefrom'
  1409. AND a.transdate <= '$transdateto'|;
  1410. }
  1411. $query = qq|
  1412. SELECT p.id, p.partnumber, p.description, sum(i.qty) AS qty,
  1413. p.onhand, extract(MONTH FROM a.transdate) AS month,
  1414. '0' AS so, '0' AS po
  1415. FROM invoice i
  1416. JOIN parts p ON (p.id = i.parts_id)
  1417. JOIN ar a ON (a.id = i.trans_id)
  1418. WHERE $where $dwhere AND p.inventory_accno_id > 0
  1419. GROUP BY p.id, p.partnumber, p.description, p.onhand,
  1420. extract(MONTH FROM a.transdate)|;
  1421. my $sth = $dbh->prepare($query);
  1422. $sth->execute || $form->dberror($query);
  1423. my %parts;
  1424. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1425. $parts{$ref->{id}} = $ref;
  1426. }
  1427. $sth->finish;
  1428. my %ofld = ( customer => so, vendor => po );
  1429. for (qw(customer vendor)) {
  1430. $query = qq|
  1431. SELECT p.id, p.partnumber, p.description,
  1432. sum(qty) - sum(ship) AS $ofld{$_}, p.onhand,
  1433. 0 AS month
  1434. FROM orderitems i
  1435. JOIN parts p ON (p.id = i.parts_id)
  1436. JOIN oe a ON (a.id = i.trans_id)
  1437. WHERE $where AND p.inventory_accno_id > 0
  1438. AND p.assembly = '0' AND a.closed = '0'
  1439. AND a.${_}_id > 0
  1440. GROUP BY p.id, p.partnumber, p.description, p.onhand,
  1441. month|;
  1442. $sth = $dbh->prepare($query);
  1443. $sth->execute || $form->dberror($query);
  1444. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1445. if (exists $parts{$ref->{id}}->{$ofld{$_}}) {
  1446. $parts{$ref->{id}}->{$ofld{$_}}
  1447. += $ref->{$ofld{$_}};
  1448. } else {
  1449. $parts{$ref->{id}} = $ref;
  1450. }
  1451. }
  1452. $sth->finish;
  1453. }
  1454. # add assemblies from open sales orders
  1455. $query = qq|
  1456. SELECT DISTINCT a.id AS orderid, b.id, i.qty - i.ship AS qty
  1457. FROM parts p
  1458. JOIN assembly b ON (b.parts_id = p.id)
  1459. JOIN orderitems i ON (i.parts_id = b.id)
  1460. JOIN oe a ON (a.id = i.trans_id)
  1461. WHERE $where
  1462. AND (p.inventory_accno_id > 0 OR p.assembly = '1')
  1463. AND a.closed = '0'|;
  1464. $sth = $dbh->prepare($query);
  1465. $sth->execute || $form->dberror($query);
  1466. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1467. &requirements_assembly(
  1468. $dbh, $form, \%parts, $ref->{id}, $ref->{qty}, $where)
  1469. if $ref->{qty};
  1470. }
  1471. $sth->finish;
  1472. $dbh->commit;
  1473. for (sort { $parts{$a}->{$form->{sort}} cmp $parts{$b}->{$form->{sort}}} keys %parts) {
  1474. push @{ $form->{parts} }, $parts{$_};
  1475. }
  1476. }
  1477. sub requirements_assembly {
  1478. my ($dbh, $form, $parts, $id, $qty, $where) = @_;
  1479. # assemblies
  1480. my $query = qq|
  1481. SELECT p.id, p.partnumber, p.description, a.qty * ? AS so,
  1482. p.onhand, p.assembly, p.partsgroup_id
  1483. FROM assembly a
  1484. JOIN parts p ON (p.id = a.parts_id)
  1485. WHERE $where AND a.id = ? AND p.inventory_accno_id > 0
  1486. UNION
  1487. SELECT p.id, p.partnumber, p.description, a.qty * ? AS so,
  1488. p.onhand, p.assembly, p.partsgroup_id
  1489. FROM assembly a
  1490. JOIN parts p ON (p.id = a.parts_id)
  1491. WHERE a.id = ? AND p.assembly = '1'|;
  1492. my $sth = $dbh->prepare($query);
  1493. $sth->execute($qty, $id, $qty, $id) || $form->dberror($query);
  1494. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  1495. if ($ref->{assembly}) {
  1496. &requirements_assembly(
  1497. $dbh, $form, $parts, $ref->{id}, $ref->{so},
  1498. $where);
  1499. next;
  1500. }
  1501. if (exists $parts->{$ref->{id}}{so}) {
  1502. $parts->{$ref->{id}}{so} += $ref->{so};
  1503. } else {
  1504. $parts->{$ref->{id}} = $ref;
  1505. }
  1506. }
  1507. $sth->finish;
  1508. }
  1509. sub create_links {
  1510. my ($self, $module, $myconfig, $form) = @_;
  1511. # connect to database
  1512. my $dbh = $form->{dbh};
  1513. my $ref;
  1514. my $query = qq|
  1515. SELECT accno, description, link
  1516. FROM chart
  1517. WHERE link LIKE ?
  1518. ORDER BY accno|;
  1519. my $sth = $dbh->prepare($query);
  1520. $sth->execute("%$module%") || $form->dberror($query);
  1521. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1522. foreach my $key (split /:/, $ref->{link}) {
  1523. if ($key =~ /$module/) {
  1524. push @{ $form->{"${module}_links"}{$key} },
  1525. { accno => $ref->{accno},
  1526. description => $ref->{description} };
  1527. }
  1528. }
  1529. }
  1530. $sth->finish;
  1531. if ($form->{item} ne 'assembly') {
  1532. $query = qq|SELECT count(*) FROM vendor|;
  1533. my ($count) = $dbh->selectrow_array($query);
  1534. if ($count < $myconfig->{vclimit}) {
  1535. $query = qq|SELECT id, name FROM vendor ORDER BY name|;
  1536. $sth = $dbh->prepare($query);
  1537. $sth->execute || $form->dberror($query);
  1538. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1539. push @{ $form->{all_vendor} }, $ref;
  1540. }
  1541. $sth->finish;
  1542. }
  1543. }
  1544. # pricegroups, customers
  1545. $query = qq|SELECT count(*) FROM customer|;
  1546. ($count) = $dbh->selectrow_array($query);
  1547. if ($count < $myconfig->{vclimit}) {
  1548. $query = qq|SELECT id, name FROM customer ORDER BY name|;
  1549. $sth = $dbh->prepare($query);
  1550. $sth->execute || $form->dberror($query);
  1551. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1552. push @{ $form->{all_customer} }, $ref;
  1553. }
  1554. $sth->finish;
  1555. }
  1556. $query = qq|SELECT id, pricegroup FROM pricegroup ORDER BY pricegroup|;
  1557. $sth = $dbh->prepare($query);
  1558. $sth->execute || $form->dberror($query);
  1559. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  1560. push @{ $form->{all_pricegroup} }, $ref;
  1561. }
  1562. $sth->finish;
  1563. if ($form->{id}) {
  1564. $query = qq|
  1565. SELECT value FROM defaults
  1566. WHERE setting_key = 'weightunit'|;
  1567. ($form->{weightunit}) = $dbh->selectrow_array($query);
  1568. $query = qq|
  1569. SELECT value FROM defaults
  1570. WHERE setting_key = 'curr'|;
  1571. ($form->{currencies}) = $dbh->selectrow_array($query);
  1572. } else {
  1573. # Dieter: FIXME left joins not working
  1574. $query = qq|
  1575. SELECT (SELECT value FROM defaults
  1576. WHERE setting_key = 'weightunit')
  1577. AS weightunit, current_date AS priceupdate,
  1578. (SELECT value FROM defaults
  1579. WHERE setting_key = 'curr') AS currencies,
  1580. c1.accno AS inventory_accno,
  1581. c1.description AS inventory_description,
  1582. c2.accno AS income_accno,
  1583. c2.description AS income_description,
  1584. c3.accno AS expense_accno,
  1585. c3.description AS expense_description
  1586. FROM chart c1, chart c2, chart c3
  1587. WHERE c1.id IN (SELECT value FROM defaults
  1588. WHERE setting_key = 'inventory_accno_id')
  1589. AND c2.id IN (SELECT value FROM defaults
  1590. WHERE setting_key = 'income_accno_id')
  1591. AND c3.id IN (SELECT value FROM defaults
  1592. WHERE setting_key
  1593. = 'expense_accno_id')|;
  1594. $sth = $dbh->prepare($query);
  1595. $sth->execute || $form->dberror($query);
  1596. $ref = $sth->fetchrow_hashref(NAME_lc);
  1597. for (qw(weightunit priceupdate currencies)) {
  1598. $form->{$_} = $ref->{$_};
  1599. }
  1600. # setup accno hash, {amount} is used in create_links
  1601. for (qw(inventory income expense)) {
  1602. $form->{amount}{"IC_$_"}
  1603. = {
  1604. accno => $ref->{"${_}_accno"},
  1605. description => $ref->{"${_}_description"}
  1606. };
  1607. }
  1608. $sth->finish;
  1609. }
  1610. $dbh->commit;
  1611. }
  1612. sub get_warehouses {
  1613. my ($self, $myconfig, $form) = @_;
  1614. my $dbh = $form->{dbh};
  1615. my $query = qq|SELECT id, description FROM warehouse|;
  1616. my $sth = $dbh->prepare($query);
  1617. $sth->execute || $form->dberror($query);
  1618. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  1619. push @{ $form->{all_warehouse} }, $ref;
  1620. }
  1621. $sth->finish;
  1622. $dbh->commit;
  1623. }
  1624. 1;