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