summaryrefslogtreecommitdiff
path: root/LedgerSMB/JC.pm
blob: 088fad9708255a89d6c56aa9ffee37f74f24691b (plain)
  1. #=====================================================================
  2. # LedgerSMB
  3. # Small Medium Business Accounting software
  4. #
  5. # Copyright (C) 2006
  6. # This work contains copyrighted information from a number of sources all used
  7. # with permission.
  8. #
  9. # This file contains source code included with or based on SQL-Ledger which
  10. # is Copyright Dieter Simader and DWS Systems Inc. 2000-2005 and licensed
  11. # under the GNU General Public License version 2 or, at your option, any later
  12. # version. For a full list including contact information of contributors,
  13. # maintainers, and copyright holders, see the CONTRIBUTORS file.
  14. #
  15. # Original Copyright Notice from SQL-Ledger 2.6.17 (before the fork):
  16. # Copyright (C) 2005
  17. #
  18. # Author: DWS Systems Inc.
  19. # Web: http://www.sql-ledger.org
  20. #
  21. # Contributors:
  22. #
  23. #======================================================================
  24. #
  25. # This file has NOT undergone whitespace cleanup.
  26. #
  27. #======================================================================
  28. #
  29. # Job Costing
  30. #
  31. #======================================================================
  32. package JC;
  33. use LedgerSMB::IS;
  34. sub get_jcitems {
  35. my ($self, $myconfig, $form) = @_;
  36. # connect to database
  37. my $dbh = $form->dbconnect($myconfig);
  38. my $query = qq|SELECT current_date FROM defaults|;
  39. ($form->{transdate}) = $dbh->selectrow_array($query);
  40. ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
  41. my $dateformat = $myconfig->{dateformat};
  42. $dateformat =~ s/yy/yyyy/;
  43. $dateformat =~ s/yyyyyy/yyyy/;
  44. if ($form->{id}) {
  45. # retrieve timecard/storescard
  46. $query = qq|SELECT j.*, to_char(j.checkedin, 'HH24:MI:SS') AS checkedina,
  47. to_char(j.checkedout, 'HH24:MI:SS') AS checkedouta,
  48. to_char(j.checkedin, '$dateformat') AS transdate,
  49. e.name AS employee, p.partnumber,
  50. pr.projectnumber, pr.description AS projectdescription,
  51. pr.production, pr.completed, pr.parts_id AS project
  52. FROM jcitems j
  53. JOIN employee e ON (e.id = j.employee_id)
  54. JOIN parts p ON (p.id = j.parts_id)
  55. JOIN project pr ON (pr.id = j.project_id)
  56. WHERE j.id = $form->{id}|;
  57. $sth = $dbh->prepare($query);
  58. $sth->execute || $form->dberror($query);
  59. $ref = $sth->fetchrow_hashref(NAME_lc);
  60. for (keys %$ref) { $form->{$_} = $ref->{$_} }
  61. $sth->finish;
  62. $form->{project} = ($form->{project}) ? "job" : "project";
  63. for (qw(checkedin checkedout)) {
  64. $form->{$_} = $form->{"${_}a"};
  65. delete $form->{"${_}a"};
  66. }
  67. $query = qq|SELECT s.printed, s.spoolfile, s.formname
  68. FROM status s
  69. WHERE s.formname = '$form->{type}'
  70. AND s.trans_id = $form->{id}|;
  71. $sth = $dbh->prepare($query);
  72. $sth->execute || $form->dberror($query);
  73. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  74. $form->{printed} .= "$ref->{formname} " if $ref->{printed};
  75. $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
  76. }
  77. $sth->finish;
  78. for (qw(printed queued)) { $form->{$_} =~ s/ +$//g }
  79. }
  80. JC->jcitems_links($myconfig, $form, $dbh);
  81. # get language codes
  82. $query = qq|SELECT *
  83. FROM language
  84. ORDER BY 2|;
  85. $sth = $dbh->prepare($query);
  86. $sth->execute || $form->dberror($query);
  87. $form->{all_language} = ();
  88. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  89. push @{ $form->{all_language} }, $ref;
  90. }
  91. $sth->finish;
  92. $dbh->disconnect;
  93. }
  94. sub jcitems_links {
  95. my ($self, $myconfig, $form, $dbh) = @_;
  96. my $disconnect = 0;
  97. if (! $dbh) {
  98. $dbh = $form->dbconnect($myconfig);
  99. $disconnect = 1;
  100. }
  101. my $query;
  102. if ($form->{project_id}) {
  103. $form->{orphaned} = 1;
  104. $query = qq|SELECT parts_id
  105. FROM project
  106. WHERE id = $form->{project_id}|;
  107. if ($dbh->selectrow_array($query)) {
  108. $form->{project} = 'job';
  109. $query = qq|SELECT id
  110. FROM project
  111. WHERE parts_id > 0
  112. AND production > completed
  113. AND id = $form->{project_id}|;
  114. ($form->{orphaned}) = $dbh->selectrow_array($q);
  115. } else {
  116. $form->{project} = 'project';
  117. }
  118. }
  119. JC->jcparts($myconfig, $form, $dbh);
  120. $form->all_employees($myconfig, $dbh, $form->{transdate});
  121. my $where;
  122. if ($form->{transdate}) {
  123. $where .= qq| AND (enddate IS NULL
  124. OR enddate >= '$form->{transdate}')
  125. AND (startdate <= '$form->{transdate}'
  126. OR startdate IS NULL)|;
  127. }
  128. if ($form->{project} eq 'job') {
  129. $query = qq|
  130. SELECT pr.*
  131. FROM project pr
  132. WHERE pr.parts_id > 0
  133. AND pr.production > pr.completed
  134. $where|;
  135. } elsif ($form->{project} eq 'project') {
  136. $query = qq|
  137. SELECT pr.*
  138. FROM project pr
  139. WHERE pr.parts_id IS NULL
  140. $where|;
  141. } else {
  142. $query = qq|
  143. SELECT pr.*
  144. FROM project pr
  145. WHERE 1=1
  146. $where
  147. EXCEPT
  148. SELECT pr.*
  149. FROM project pr
  150. WHERE pr.parts_id > 0
  151. AND pr.production = pr.completed|;
  152. }
  153. if ($form->{project_id}) {
  154. $query .= qq|
  155. UNION
  156. SELECT *
  157. FROM project
  158. WHERE id = $form->{project_id}|;
  159. }
  160. $query .= qq|
  161. ORDER BY projectnumber|;
  162. $sth = $dbh->prepare($query);
  163. $sth->execute || $form->dberror($query);
  164. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  165. push @{ $form->{all_project} }, $ref;
  166. }
  167. $sth->finish;
  168. $dbh->disconnect if $disconnect;
  169. }
  170. sub jcparts {
  171. my ($self, $myconfig, $form, $dbh) = @_;
  172. my ($null, $project_id) = split /--/, $form->{projectnumber};
  173. $project_id *= 1;
  174. my $query = qq|SELECT customer_id
  175. FROM project
  176. WHERE id = $project_id|;
  177. my ($customer_id) = $dbh->selectrow_array($query);
  178. $customer_id *= 1;
  179. my $where;
  180. if ($form->{project} eq 'job') {
  181. $where = " AND p.income_accno_id IS NULL";
  182. if ($form->{type} eq 'storescard') {
  183. $where = " AND p.inventory_accno_id > 0
  184. AND p.income_accno_id > 0";
  185. }
  186. $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
  187. p.unit, t.description AS translation
  188. FROM parts p
  189. LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
  190. WHERE p.obsolete = '0'
  191. $where|;
  192. } elsif ($form->{project} eq 'project') {
  193. $where = " AND p.inventory_accno_id IS NULL";
  194. if ($form->{type} eq 'storescard') {
  195. $where = " AND p.inventory_accno_id > 0";
  196. }
  197. $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
  198. p.unit, t.description AS translation
  199. FROM parts p
  200. LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
  201. WHERE p.obsolete = '0'
  202. AND p.assembly = '0'
  203. $where|;
  204. } else {
  205. $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
  206. p.unit, t.description AS translation
  207. FROM parts p
  208. LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
  209. WHERE p.obsolete = '0'
  210. AND p.income_accno_id IS NULL
  211. UNION
  212. SELECT p.id, p.partnumber, p.description, p.sellprice,
  213. p.unit, t.description AS translation
  214. FROM parts p
  215. LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
  216. WHERE p.obsolete = '0'
  217. AND p.assembly = '0'
  218. AND p.inventory_accno_id IS NULL|;
  219. }
  220. $query .= qq|
  221. ORDER BY 2|;
  222. my $sth = $dbh->prepare($query);
  223. $sth->execute || $form->dberror($query);
  224. my $pmh = price_matrix_query($dbh, $project_id, $customer_id);
  225. IS::exchangerate_defaults($dbh, $form);
  226. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  227. $ref->{description} = $ref->{translation} if $ref->{translation};
  228. IS::price_matrix($pmh, $ref, $form->datetonum($form->{transdate}), 4, $form, $myconfig);
  229. push @{ $form->{all_parts} }, $ref;
  230. }
  231. $sth->finish;
  232. }
  233. sub delete_timecard {
  234. my ($self, $myconfig, $form) = @_;
  235. # connect to database
  236. my $dbh = $form->dbconnect_noauto($myconfig);
  237. my %audittrail = ( tablename => 'jcitems',
  238. reference => $form->{id},
  239. formname => $form->{type},
  240. action => 'deleted',
  241. id => $form->{id} );
  242. $form->audittrail($dbh, "", \%audittrail);
  243. my $query = qq|DELETE FROM jcitems
  244. WHERE id = $form->{id}|;
  245. $dbh->do($query) || $form->dberror($query);
  246. # delete spool files
  247. $query = qq|SELECT spoolfile FROM status
  248. WHERE formname = '$form->{type}'
  249. AND trans_id = $form->{id}
  250. AND spoolfile IS NOT NULL|;
  251. my $sth = $dbh->prepare($query);
  252. $sth->execute || $form->dberror($query);
  253. my $spoolfile;
  254. my @spoolfiles = ();
  255. while (($spoolfile) = $sth->fetchrow_array) {
  256. push @spoolfiles, $spoolfile;
  257. }
  258. $sth->finish;
  259. # delete status entries
  260. $query = qq|DELETE FROM status
  261. WHERE formname = '$form->{type}'
  262. AND trans_id = $form->{id}|;
  263. $dbh->do($query) || $form->dberror($query);
  264. my $rc = $dbh->commit;
  265. if ($rc) {
  266. foreach $spoolfile (@spoolfiles) {
  267. unlink "$spool/$spoolfile" if $spoolfile;
  268. }
  269. }
  270. $dbh->disconnect;
  271. $rc;
  272. }
  273. sub jcitems {
  274. my ($self, $myconfig, $form) = @_;
  275. # connect to database
  276. my $dbh = $form->dbconnect($myconfig);
  277. my $query;
  278. my $where = "1 = 1";
  279. my $null;
  280. my $var;
  281. if ($form->{projectnumber}) {
  282. ($null, $var) = split /--/, $form->{projectnumber};
  283. $where .= " AND j.project_id = $var";
  284. $query = qq|SELECT parts_id
  285. FROM project
  286. WHERE id = $var|;
  287. my ($job) = $dbh->selectrow_array($query);
  288. $form->{project} = ($job) ? "job" : "project";
  289. }
  290. if ($form->{partnumber}) {
  291. ($null, $var) = split /--/, $form->{partnumber};
  292. $where .= " AND j.parts_id = $var";
  293. $query = qq|SELECT inventory_accno_id
  294. FROM parts
  295. WHERE id = $var|;
  296. my ($job) = $dbh->selectrow_array($query);
  297. $form->{project} = ($job) ? "job" : "project";
  298. }
  299. if ($form->{employee}) {
  300. ($null, $var) = split /--/, $form->{employee};
  301. $where .= " AND j.employee_id = $var";
  302. }
  303. if ($form->{open} || $form->{closed}) {
  304. unless ($form->{open} && $form->{closed}) {
  305. $where .= " AND j.qty != j.allocated" if $form->{open};
  306. $where .= " AND j.qty = j.allocated" if $form->{closed};
  307. }
  308. }
  309. ($form->{startdatefrom}, $form->{startdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
  310. $where .= " AND j.checkedin >= '$form->{startdatefrom}'" if $form->{startdatefrom};
  311. $where .= " AND j.checkedout < date '$form->{startdateto}' + 1" if $form->{startdateto};
  312. my %ordinal = ( id => 1,
  313. description => 2,
  314. transdate => 7,
  315. partnumber => 9,
  316. projectnumber => 10,
  317. projectdescription => 11,
  318. );
  319. my @a = (transdate, projectnumber);
  320. my $sortorder = $form->sort_order(\@a, \%ordinal);
  321. my $dateformat = $myconfig->{dateformat};
  322. $dateformat =~ s/yy$/yyyy/;
  323. $dateformat =~ s/yyyyyy/yyyy/;
  324. if ($form->{project} eq 'job') {
  325. if ($form->{type} eq 'timecard') {
  326. $where .= " AND pr.parts_id > 0
  327. AND p.income_accno_id IS NULL";
  328. }
  329. if ($form->{type} eq 'storescard') {
  330. $where .= " AND pr.parts_id > 0
  331. AND p.income_accno_id > 0";
  332. }
  333. }
  334. if ($form->{project} eq 'project') {
  335. $where .= " AND pr.parts_id IS NULL";
  336. }
  337. $query = qq|SELECT j.id, j.description, j.qty, j.allocated,
  338. to_char(j.checkedin, 'HH24:MI') AS checkedin,
  339. to_char(j.checkedout, 'HH24:MI') AS checkedout,
  340. to_char(j.checkedin, 'yyyymmdd') AS transdate,
  341. to_char(j.checkedin, '$dateformat') AS transdatea,
  342. to_char(j.checkedin, 'D') AS weekday,
  343. p.partnumber,
  344. pr.projectnumber, pr.description AS projectdescription,
  345. e.employeenumber, e.name AS employee,
  346. to_char(j.checkedin, 'WW') AS workweek, pr.parts_id,
  347. j.sellprice
  348. FROM jcitems j
  349. JOIN parts p ON (p.id = j.parts_id)
  350. JOIN project pr ON (pr.id = j.project_id)
  351. JOIN employee e ON (e.id = j.employee_id)
  352. WHERE $where
  353. ORDER BY employee, employeenumber, $sortorder|;
  354. $sth = $dbh->prepare($query);
  355. $sth->execute || $form->dberror($query);
  356. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  357. $ref->{project} = ($ref->{parts_id}) ? "job" : "project";
  358. $ref->{transdate} = $ref->{transdatea};
  359. delete $ref->{transdatea};
  360. push @{ $form->{transactions} }, $ref;
  361. }
  362. $sth->finish;
  363. $dbh->disconnect;
  364. }
  365. sub save {
  366. my ($self, $myconfig, $form) = @_;
  367. # connect to database
  368. my $dbh = $form->dbconnect_noauto($myconfig);
  369. my $query;
  370. my $sth;
  371. my ($null, $project_id) = split /--/, $form->{projectnumber};
  372. if ($form->{id}) {
  373. # check if it was a job
  374. $query = qq|SELECT pr.parts_id, pr.production - pr.completed
  375. FROM project pr
  376. JOIN jcitems j ON (j.project_id = pr.id)
  377. WHERE j.id = $form->{id}|;
  378. my ($job_id, $qty) = $dbh->selectrow_array($query);
  379. if ($job_id && $qty == 0) {
  380. $dbh->disconnect;
  381. return -1;
  382. }
  383. # check if new one belongs to a job
  384. if ($project_id) {
  385. $query = qq|SELECT pr.parts_id, pr.production - pr.completed
  386. FROM project pr
  387. WHERE pr.id = $project_id|;
  388. my ($job_id, $qty) = $dbh->selectrow_array($query);
  389. if ($job_id && $qty == 0) {
  390. $dbh->disconnect;
  391. return -2;
  392. }
  393. }
  394. } else {
  395. my $uid = localtime;
  396. $uid .= "$$";
  397. $query = qq|INSERT INTO jcitems (description)
  398. VALUES ('$uid')|;
  399. $dbh->do($query) || $form->dberror($query);
  400. $query = qq|SELECT id FROM jcitems
  401. WHERE description = '$uid'|;
  402. ($form->{id}) = $dbh->selectrow_array($query);
  403. }
  404. for (qw(inhour inmin insec outhour outmin outsec)) { $form->{$_} = substr("00$form->{$_}", -2) }
  405. for (qw(qty sellprice allocated)) { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
  406. my $checkedin = "$form->{inhour}$form->{inmin}$form->{insec}";
  407. my $checkedout = "$form->{outhour}$form->{outmin}$form->{outsec}";
  408. my $outdate = $form->{transdate};
  409. if ($checkedout < $checkedin) {
  410. $outdate = $form->add_date($myconfig, $form->{transdate}, 1, 'days');
  411. }
  412. ($null, $form->{employee_id}) = split /--/, $form->{employee};
  413. unless ($form->{employee_id}) {
  414. ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
  415. }
  416. my $parts_id;
  417. ($null, $parts_id) = split /--/, $form->{partnumber};
  418. $query = qq|UPDATE jcitems SET
  419. project_id = $project_id,
  420. parts_id = $parts_id,
  421. description = |.$dbh->quote($form->{description}).qq|,
  422. qty = $form->{qty},
  423. allocated = $form->{allocated},
  424. sellprice = $form->{sellprice},
  425. fxsellprice = $form->{sellprice},
  426. serialnumber = |.$dbh->quote($form->{serialnumber}).qq|,
  427. checkedin = timestamp '$form->{transdate} $form->{inhour}:$form->{inmin}:$form->{insec}',
  428. checkedout = timestamp '$outdate $form->{outhour}:$form->{outmin}:$form->{outsec}',
  429. employee_id = $form->{employee_id},
  430. notes = |.$dbh->quote($form->{notes}).qq|
  431. WHERE id = $form->{id}|;
  432. $dbh->do($query) || $form->dberror($query);
  433. # save printed, queued
  434. $form->save_status($dbh);
  435. my %audittrail = ( tablename => 'jcitems',
  436. reference => $form->{id},
  437. formname => $form->{type},
  438. action => 'saved',
  439. id => $form->{id} );
  440. $form->audittrail($dbh, "", \%audittrail);
  441. my $rc = $dbh->commit;
  442. $rc;
  443. }
  444. sub price_matrix_query {
  445. my ($dbh, $project_id, $customer_id) = @_;
  446. my $query = qq|SELECT p.id AS parts_id, 0 AS customer_id, 0 AS pricegroup_id,
  447. 0 AS pricebreak, p.sellprice, NULL AS validfrom, NULL AS validto,
  448. (SELECT substr(curr,1,3) FROM defaults) AS curr, '' AS pricegroup
  449. FROM parts p
  450. WHERE p.id = ?
  451. UNION
  452. SELECT p.*, g.pricegroup
  453. FROM partscustomer p
  454. LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
  455. WHERE p.parts_id = ?
  456. AND p.customer_id = $customer_id
  457. UNION
  458. SELECT p.*, g.pricegroup
  459. FROM partscustomer p
  460. LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
  461. JOIN customer c ON (c.pricegroup_id = g.id)
  462. WHERE p.parts_id = ?
  463. AND c.id = $customer_id
  464. UNION
  465. SELECT p.*, '' AS pricegroup
  466. FROM partscustomer p
  467. WHERE p.customer_id = 0
  468. AND p.pricegroup_id = 0
  469. AND p.parts_id = ?
  470. ORDER BY customer_id DESC, pricegroup_id DESC, pricebreak
  471. |;
  472. my $sth = $dbh->prepare($query) || $form->dberror($query);
  473. $sth;
  474. }
  475. 1;