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