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