tle>
summaryrefslogtreecommitdiff
path: root/bin/jc.pl
blob: 6af0f84ac26b04b41edfd4ffb3360526b5dfe496 (plain)
  1. #=====================================================================
  2. # LedgerSMB Small Medium Business Accounting
  3. # http://www.ledgersmb.org/
  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. #
  22. #
  23. # This program is free software; you can redistribute it and/or modify
  24. # it under the terms of the GNU General Public License as published by
  25. # the Free Software Foundation; either version 2 of the License, or
  26. # (at your option) any later version.
  27. #
  28. # This program is distributed in the hope that it will be useful,
  29. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  31. # GNU General Public License for more details.
  32. # You should have received a copy of the GNU General Public License
  33. # along with this program; if not, write to the Free Software
  34. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35. #======================================================================
  36. #
  37. # Job Costing module
  38. #
  39. #======================================================================
  40. use LedgerSMB::JC;
  41. 1;
  42. # end of main
  43. sub add {
  44. if ($form->{type} eq 'timecard') {
  45. $form->{title} = $locale->text('Add Time Card');
  46. }
  47. if ($form->{type} eq 'storescard') {
  48. $form->{title} = $locale->text('Add Stores Card');
  49. }
  50. $form->{callback} = "$form->{script}?action=add&type=$form->{type}&login=$form->{login}&path=$form->{path}&sessionid=$form->{sessionid}&project=$form->{project}" unless $form->{callback};
  51. &{ "prepare_$form->{type}" };
  52. $form->{orphaned} = 1;
  53. &display_form;
  54. }
  55. sub edit {
  56. if ($form->{type} eq 'timecard') {
  57. $form->{title} = $locale->text('Edit Time Card');
  58. }
  59. if ($form->{type} eq 'storescard') {
  60. $form->{title} = $locale->text('Add Stores Card');
  61. }
  62. &{ "prepare_$form->{type}" };
  63. &display_form;
  64. }
  65. sub jcitems_links {
  66. if (@{ $form->{all_project} }) {
  67. $form->{selectprojectnumber} = "<option>\n";
  68. foreach $ref (@{ $form->{all_project} }) {
  69. $form->{selectprojectnumber} .= qq|<option value="$ref->{projectnumber}--$ref->{id}">$ref->{description} ($ref->{description})</option>\n|;
  70. if ($form->{projectnumber} eq "$ref->{projectnumber}--$ref->{id}") {
  71. $form->{projectdescription} = $ref->{description};
  72. }
  73. }
  74. } else {
  75. if ($form->{project} eq 'job') {
  76. $form->error($locale->text('No open Jobs!'));
  77. } else {
  78. $form->error($locale->text('No open Projects!'));
  79. }
  80. }
  81. if (@{ $form->{all_parts} }) {
  82. $form->{selectpartnumber} = "<option>\n";
  83. foreach $ref (@{ $form->{all_parts} }) {
  84. $form->{selectpartnumber} .= qq|<option value="$ref->{partnumber}--$ref->{id}">$ref->{partnumber}\n|;
  85. if ($form->{partnumber} eq "$ref->{partnumber}--$ref->{id}") {
  86. if ($form->{partnumber} ne $form->{oldpartnumber}) {
  87. for (qw(description unit sellprice pricematrix)) { $form->{$_} = $ref->{$_} }
  88. }
  89. }
  90. }
  91. } else {
  92. if ($form->{type} eq 'timecard') {
  93. if ($form->{project} eq 'job') {
  94. $form->error($locale->text('No Labor codes on file!'));
  95. } else {
  96. $form->error($locale->text('No Services on file!'));
  97. }
  98. } else {
  99. $form->error($locale->text('No Parts on file!'));
  100. }
  101. }
  102. # employees
  103. if (@{ $form->{all_employee} }) {
  104. $form->{selectemployee} = "<option>\n";
  105. for (@{ $form->{all_employee} }) { $form->{selectemployee} .= qq|<option value="$_->{name}--$_->{id}">$_->{name}\n| }
  106. } else {
  107. $form->error($locale->text('No Employees on file!'));
  108. }
  109. }
  110. sub search {
  111. # accounting years
  112. $form->all_years(\%myconfig);
  113. if (@{ $form->{all_years} }) {
  114. $form->{selectaccountingyear} = "<option>\n";
  115. for (@{ $form->{all_years} }) { $form->{selectaccountingyear} .= qq|<option>$_\n| }
  116. $form->{selectaccountingmonth} = "<option>\n";
  117. for (sort keys %{ $form->{all_month} }) { $form->{selectaccountingmonth} .= qq|<option value=$_>|.$locale->text($form->{all_month}{$_}).qq|\n| }
  118. $selectfrom = qq|
  119. <tr>
  120. <th align=right>|.$locale->text('Period').qq|</th>
  121. <td colspan=3>
  122. <select name=month>$form->{selectaccountingmonth}</select>
  123. <select name=year>$form->{selectaccountingyear}</select>
  124. <input name=interval class=radio type=radio value=0 checked>&nbsp;|.$locale->text('Current').qq|
  125. <input name=interval class=radio type=radio value=1>&nbsp;|.$locale->text('Month').qq|
  126. <input name=interval class=radio type=radio value=3>&nbsp;|.$locale->text('Quarter').qq|
  127. <input name=interval class=radio type=radio value=12>&nbsp;|.$locale->text('Year').qq|
  128. </td>
  129. </tr>
  130. |;
  131. }
  132. $fromto = qq|
  133. <tr>
  134. <th align=right nowrap>|.$locale->text('Startdate').qq|</th>
  135. <td>|.$locale->text('From').qq| <input name=startdatefrom size=11 title="$myconfig{dateformat}">
  136. |.$locale->text('To').qq| <input name=startdateto size=11 title="$myconfig{dateformat}"></td>
  137. </tr>
  138. $selectfrom
  139. |;
  140. if ($form->{type} eq 'timecard') {
  141. $form->{title} = $locale->text('Time Cards');
  142. JC->jcitems_links(\%myconfig, \%$form);
  143. }
  144. if ($form->{type} eq 'storescard') {
  145. $form->{title} = $locale->text('Stores Cards');
  146. JC->jcitems_links(\%myconfig, \%$form);
  147. }
  148. if (@{ $form->{all_project} }) {
  149. $form->{selectprojectnumber} = "<option>\n";
  150. for (@{ $form->{all_project} }) { $form->{selectprojectnumber} .= qq|<option value="$_->{projectnumber}--$_->{id}">$_->{projectnumber}\n| }
  151. }
  152. if (@{ $form->{all_parts} }) {
  153. $form->{selectpartnumber} = "<option>\n";
  154. foreach $ref (@{ $form->{all_parts} }) {
  155. $form->{selectpartnumber} .= qq|<option value="$ref->{partnumber}--$ref->{id}">$ref->{partnumber}\n|;
  156. }
  157. }
  158. if ($form->{project} eq 'job') {
  159. $joblabel = $locale->text('Job Number');
  160. $laborlabel = $locale->text('Labor Code');
  161. } elsif ($form->{project} eq 'project') {
  162. $joblabel = $locale->text('Project Number');
  163. $laborlabel = $locale->text('Service Code');
  164. } else {
  165. $joblabel = $locale->text('Project/Job Number');
  166. $laborlabel = $locale->text('Service/Labor Code');
  167. }
  168. if ($form->{selectprojectnumber}) {
  169. $jobnumber = qq|
  170. <tr>
  171. <th align=right nowrap>$joblabel</th>
  172. <td colspan=3><select name=projectnumber>$form->{selectprojectnumber}</select></td>
  173. </tr>
  174. |;
  175. }
  176. if ($form->{type} eq 'timecard') {
  177. # employees
  178. if (@{ $form->{all_employee} }) {
  179. $form->{selectemployee} = "<option>\n";
  180. for (@{ $form->{all_employee} }) { $form->{selectemployee} .= qq|<option value="$_->{name}--$_->{id}">$_->{name}\n| }
  181. } else {
  182. $form->error($locale->text('No Employees on file!'));
  183. }
  184. if ($form->{selectpartnumber}) {
  185. $partnumber = qq|
  186. <tr>
  187. <th align=right nowrap>$laborlabel</th>
  188. <td colspan=3><select name=partnumber>$form->{selectpartnumber}</select></td>
  189. </tr>
  190. |;
  191. }
  192. $employee = qq|
  193. <tr>
  194. <th align=right nowrap>|.$locale->text('Employee').qq|</th>
  195. <td colspan=3><select name=employee>$form->{selectemployee}</select></td>
  196. </tr>
  197. |;
  198. $l_time = qq|<td nowrap><input name=l_time class=checkbox type=checkbox value=Y>&nbsp;|.$locale->text('Time').qq|</td>|;
  199. }
  200. $form->header;
  201. print qq|
  202. <body>
  203. <form method=post action=$form->{script}>
  204. <table width=100%>
  205. <tr>
  206. <th class=listtop>$form->{title}</th>
  207. </tr>
  208. <tr height="5"></tr>
  209. <tr valign=top>
  210. <td>
  211. <table>
  212. $jobnumber
  213. $partnumber
  214. $employee
  215. $fromto
  216. <tr>
  217. <th align=right nowrap>|.$locale->text('Include in Report').qq|</th>
  218. <td>
  219. <table>
  220. <tr>
  221. <td nowrap><input name=open class=checkbox type=checkbox value=Y checked> |.$locale->text('Open').qq|</td>
  222. <td nowrap><input name=closed class=checkbox type=checkbox value=Y> |.$locale->text('Closed').qq|</td>
  223. </tr>
  224. <tr>
  225. $l_time
  226. <td nowrap><input name=l_allocated class=checkbox type=checkbox value=Y> |.$locale->text('Allocated').qq|</td>
  227. </tr>
  228. <tr>
  229. <td><input name=l_subtotal class=checkbox type=checkbox value=Y>&nbsp;|.$locale->text('Subtotal').qq|</td>
  230. </tr>
  231. </table>
  232. </td>
  233. </tr>
  234. </table>
  235. </td>
  236. </tr>
  237. <tr>
  238. <td><hr size=3 noshade></td>
  239. </tr>
  240. </table>
  241. <input type=hidden name=nextsub value="list_$form->{type}">
  242. <input type=hidden name=sort value="transdate">
  243. |;
  244. $form->hide_form(qw(db path login sessionid project type));
  245. print qq|
  246. <br>
  247. <button type="submit" class="submit" name="action" value="continue">|.$locale->text('Continue').qq|</button>
  248. </form>
  249. |;
  250. if ($form->{lynx}) {
  251. require "bin/menu.pl";
  252. &menubar;
  253. }
  254. print qq|
  255. </body>
  256. </html>
  257. |;
  258. }
  259. sub display_form {
  260. &{ "$form->{type}_header" };
  261. &{ "$form->{type}_footer" };
  262. }
  263. sub form_header {
  264. &{ "$form->{type}_header" };
  265. }
  266. sub form_footer {
  267. &{ "form->{type}_footer" };
  268. }
  269. sub prepare_timecard {
  270. $form->{formname} = "timecard";
  271. $form->{format} = "postscript" if $myconfig{printer};
  272. $form->{media} = $myconfig{printer};
  273. JC->get_jcitems(\%myconfig, \%$form);
  274. $form->{selectformname} = qq|<option value="timecard">|.$locale->text('Time Card');
  275. foreach $item (qw(in out)) {
  276. ($form->{"${item}hour"}, $form->{"${item}min"}, $form->{"${item}sec"}) = split /:/, $form->{"checked$item"};
  277. for (qw(hour min sec)) {
  278. if (($form->{"$item$_"} *= 1) > 0) {
  279. $form->{"$item$_"} = substr(qq|0$form->{"$item$_"}|,-2);
  280. } else {
  281. $form->{"$item$_"} ||= "";
  282. }
  283. }
  284. }
  285. $form->{checkedin} = $form->{inhour} * 3600 + $form->{inmin} * 60 + $form->{insec};
  286. $form->{checkedout} = $form->{outhour} * 3600 + $form->{outmin} * 60 + $form->{outsec};
  287. if ($form->{checkedin} > $form->{checkedout}) {
  288. $form->{checkedout} = 86400 - ($form->{checkedin} - $form->{checkedout});
  289. $form->{checkedin} = 0;
  290. }
  291. $form->{clocked} = ($form->{checkedout} - $form->{checkedin}) / 3600;
  292. if ($form->{clocked}) {
  293. $form->{oldnoncharge} = $form->{clocked} - $form->{qty};
  294. }
  295. $form->{oldqty} = $form->{qty};
  296. $form->{noncharge} = $form->format_amount(\%myconfig, $form->{clocked} - $form->{qty}, 4) if $form->{checkedin} != $form->{checkedout};
  297. $form->{clocked} = $form->format_amount(\%myconfig, $form->{clocked}, 4);
  298. $form->{amount} = $form->{sellprice} * $form->{qty};
  299. for (qw(sellprice amount)) { $form->{$_} = $form->format_amount(\%myconfig, $form->{$_}, 2) }
  300. $form->{qty} = $form->format_amount(\%myconfig, $form->{qty}, 4);
  301. $form->{allocated} = $form->format_amount(\%myconfig, $form->{allocated});
  302. $form->{employee} .= "--$form->{employee_id}";
  303. $form->{projectnumber} .= "--$form->{project_id}";
  304. $form->{partnumber} .= "--$form->{parts_id}";
  305. $form->{oldpartnumber} = $form->{partnumber};
  306. if (@{ $form->{all_language} }) {
  307. $form->{selectlanguage} = "<option>\n";
  308. for (@{ $form->{all_language} }) { $form->{selectlanguage} .= qq|<option value="$_->{code}">$_->{description}\n| }
  309. }
  310. &jcitems_links;
  311. $form->{locked} = ($form->{revtrans}) ? '1' : ($form->datetonum(\%myconfig, $form->{transdate}) <= $form->datetonum(\%myconfig, $form->{closedto}));
  312. $form->{readonly} = 1 if $myconfig{acs} =~ /Production--Add Time Card/;
  313. if ($form->{income_accno_id}) {
  314. $form->{locked} = 1 if $form->{production} == $form->{completed};
  315. }
  316. }
  317. sub timecard_header {
  318. # set option selected
  319. for (qw(employee projectnumber partnumber)) {
  320. $form->{"select$_"} =~ s/ selected//;
  321. $form->{"select$_"} =~ s/(<option value="\Q$form->{$_}\E")/$1 selected/;
  322. }
  323. $rows = $form->numtextrows($form->{description}, 50, 8);
  324. for (qw(transdate checkedin checkedout partnumber)) { $form->{"old$_"} = $form->{$_} }
  325. for (qw(partnumber description)) { $form->{$_} = $form->quote($form->{$_}) }
  326. if ($rows > 1) {
  327. $description = qq|<textarea name=description rows=$rows cols=46 wrap=soft>$form->{description}</textarea>|;
  328. } else {
  329. $description = qq|<input name=description size=48 value="$form->{description}">|;
  330. }
  331. if ($form->{project} eq 'job') {
  332. $projectlabel = $locale->text('Job Number');
  333. $laborlabel = $locale->text('Labor Code');
  334. $rate = qq|<input type=hidden name=sellprice value=$form->{sellprice}>|;
  335. } else {
  336. if ($form->{project} eq 'project') {
  337. $projectlabel = $locale->text('Project Number');
  338. $laborlabel = $locale->text('Service Code');
  339. } else {
  340. $projectlabel = $locale->text('Project/Job Number');
  341. $laborlabel = $locale->text('Service/Labor Code');
  342. }
  343. if ($myconfig{role} ne 'user') {
  344. $rate = qq|
  345. <tr>
  346. <th align=right nowrap>|.$locale->text('Chargeout Rate').qq|</th>
  347. <td><input name=sellprice value=$form->{sellprice}></td>
  348. <th align=right nowrap>|.$locale->text('Total').qq|</th>
  349. <td>$form->{amount}</td>
  350. </tr>
  351. <tr>
  352. <th align=right nowrap>|.$locale->text('Allocated').qq|</th>
  353. <td><input name=allocated value=$form->{allocated}></td>
  354. </tr>
  355. |;
  356. } else {
  357. $rate = qq|
  358. <tr>
  359. <th align=right nowrap>|.$locale->text('Chargeout Rate').qq|</th>
  360. <td>$form->{sellprice}</td>
  361. <th align=right nowrap>|.$locale->text('Total').qq|</th>
  362. <td>$form->{amount}</td>
  363. </tr>
  364. <tr>
  365. <th align=right nowrap>|.$locale->text('Allocated').qq|</th>
  366. <td>$form->{allocated}</td>
  367. </tr>
  368. <input type=hidden name=sellprice value=$form->{sellprice}>
  369. <input type=hidden name=allocated value=$form->{allocated}>
  370. |;
  371. }
  372. }
  373. if ($myconfig{role} eq 'user') {
  374. $charge = qq|<input type=hidden name=qty value=$form->{qty}>$form->{qty}|;
  375. } else {
  376. $charge = qq|<input name=qty value=$form->{qty}>|;
  377. }
  378. if (($rows = $form->numtextrows($form->{notes}, 40, 6)) < 2) {
  379. $rows = 2;
  380. }
  381. $notes = qq|<tr>
  382. <th align=right>|.$locale->text('Notes').qq|</th>
  383. <td colspan=3><textarea name="notes" rows=$rows cols=46 wrap=soft>$form->{notes}</textarea>
  384. </td>
  385. </tr>
  386. |;
  387. ##################
  388. ($null, $form->{oldproject_id}) = split /--/, $form->{projectnumber};
  389. $form->header;
  390. print qq|
  391. <body>
  392. <form method=post action="$form->{script}">
  393. |;
  394. $form->hide_form(qw(id type media format printed queued title closedto locked oldtransdate oldcheckedin oldcheckedout oldpartnumber project oldqty oldnoncharge pricematrix oldproject_id));
  395. print qq|
  396. <table width=100%>
  397. <tr class=listtop>
  398. <th class=listtop>$form->{title}</th>
  399. </tr>
  400. <tr height="5"></tr>
  401. <tr>
  402. <td>
  403. <table>
  404. <tr>
  405. <td>
  406. <table>
  407. <tr>
  408. <th align=right nowrap>|.$locale->text('Employee').qq|</th>
  409. <td><select name=employee>$form->{selectemployee}</select></td>
  410. </tr>
  411. <tr>
  412. <th align=right nowrap>$projectlabel</th>
  413. <td><select name=projectnumber>$form->{selectprojectnumber}</select>
  414. </td>
  415. <td></td>
  416. <td>$form->{projectdescription}</td>
  417. <input type=hidden name=projectdescription value="|.$form->quote($form->{projectdescription}).qq|">
  418. </tr>
  419. <tr>
  420. <th align=right nowrap>|.$locale->text('Date worked').qq|</th>
  421. <td><input name=transdate size=11 title="$myconfig{dateformat}" value=$form->{transdate}></td>
  422. </tr>
  423. <tr>
  424. <th align=right nowrap>$laborlabel</th>
  425. <td><select name=partnumber>$form->{selectpartnumber}</select></td>
  426. </tr>
  427. <tr valign=top>
  428. <th align=right nowrap>|.$locale->text('Description').qq|</th>
  429. <td colspan=3>$description</td>
  430. </tr>
  431. <tr>
  432. <th align=right nowrap>|.$locale->text('Time In').qq|</th>
  433. <td>
  434. <table>
  435. <tr>
  436. <td><input name=inhour title="hh" size=3 maxlength=2 value=$form->{inhour}></td>
  437. <td><input name=inmin title="mm" size=3 maxlength=2 value=$form->{inmin}></td>
  438. <td><input name=insec title="ss" size=3 maxlength=2 value=$form->{insec}></td>
  439. </tr>
  440. </table>
  441. </td>
  442. <th align=right nowrap>|.$locale->text('Time Out').qq|</th>
  443. <td>
  444. <table>
  445. <tr>
  446. <td><input name=outhour title="hh" size=3 maxlength=2 value=$form->{outhour}></td>
  447. <td><input name=outmin title="mm" size=3 maxlength=2 value=$form->{outmin}></td>
  448. <td><input name=outsec title="ss" size=3 maxlength=2 value=$form->{outsec}></td>
  449. </tr>
  450. </table>
  451. </td>
  452. </tr>
  453. <tr>
  454. <th align=right nowrap>|.$locale->text('Clocked').qq|</th>
  455. <td>$form->{clocked}</td>
  456. </tr>
  457. <tr>
  458. <th align=right nowrap>|.$locale->text('Non-chargeable').qq|</th>
  459. <td><input name=noncharge value=$form->{noncharge}></td>
  460. </tr>
  461. <tr>
  462. <th align=right nowrap>|.$locale->text('Chargeable').qq|</th>
  463. <td>$charge</td>
  464. </tr>
  465. $rate
  466. $notes
  467. </table>
  468. </td>
  469. </tr>
  470. |;
  471. }
  472. sub timecard_footer {
  473. print qq|
  474. </table>
  475. </td>
  476. </tr>
  477. <tr>
  478. <td><hr size=3 noshade></td>
  479. </tr>
  480. <tr>
  481. <td>
  482. |;
  483. &print_options;
  484. print qq|
  485. </td>
  486. </tr>
  487. </table>
  488. <br>
  489. |;
  490. $transdate = $form->datetonum(\%myconfig, $form->{transdate});
  491. $closedto = $form->datetonum(\%myconfig, $form->{closedto});
  492. if (! $form->{readonly}) {
  493. # type=submit $locale->text('Update')
  494. # type=submit $locale->text('Print')
  495. # type=submit $locale->text('Save')
  496. # type=submit $locale->text('Print and Save')
  497. # type=submit $locale->text('Save as new')
  498. # type=submit $locale->text('Print and Save as new')
  499. # type=submit $locale->text('Delete')
  500. %button = ('update' => { ndx => 1, key => 'U', value => $locale->text('Update') },
  501. 'print' => { ndx => 2, key => 'P', value => $locale->text('Print') },
  502. 'save' => { ndx => 3, key => 'S', value => $locale->text('Save') },
  503. 'print_and_save' => { ndx => 6, key => 'R', value => $locale->text('Print and Save') },
  504. 'save_as_new' => { ndx => 7, key => 'N', value => $locale->text('Save as new') },
  505. 'print_and_save_as_new' => { ndx => 8, key => 'W', value => $locale->text('Print and Save as new') },
  506. 'delete' => { ndx => 16, key => 'D', value => $locale->text('Delete') },
  507. );
  508. %a = ();
  509. if ($form->{id}) {
  510. if (!$form->{locked}) {
  511. for ('update', 'print', 'save', 'save_as_new') { $a{$_} = 1 }
  512. if (${LedgerSMB::Sysconfig::latex}) {
  513. for ('print_and_save', 'print_and_save_as_new') { $a{$_} = 1 }
  514. }
  515. if ($form->{orphaned}) {
  516. $a{'delete'} = 1;
  517. }
  518. }
  519. } else {
  520. if ($transdate > $closedto) {
  521. for ('update', 'print', 'save') { $a{$_} = 1 }
  522. if (${LedgerSMB::Sysconfig::latex}) {
  523. $a{'print_and_save'} = 1;
  524. }
  525. }
  526. }
  527. }
  528. for (keys %button) { delete $button{$_} if ! $a{$_} }
  529. for (sort { $button{$a}->{ndx} <=> $button{$b}->{ndx} } keys %button) { $form->print_button(\%button, $_) }
  530. if ($form->{lynx}) {
  531. require "bin/menu.pl";
  532. &menubar;
  533. }
  534. $form->hide_form(qw(callback path login sessionid));
  535. print qq|
  536. </form>
  537. </body>
  538. </html>
  539. |;
  540. }
  541. sub prepare_storescard {
  542. $form->{formname} = "storescard";
  543. $form->{format} = "postscript" if $myconfig{printer};
  544. $form->{media} = $myconfig{printer};
  545. JC->get_jcitems(\%myconfig, \%$form);